了解机器学习模型可能失败的场景
如果我们将苹果和橘子混合与另一组苹果和橘子的混合物进行比较,那么分配情况会有所不同。你还能比较吗?你将如何去做呢?
在现实世界中的大多数情况下,你会遇到如何去做的问题。
这在数据科学中经常发生。在开发机器学习模型的同时,我们遇到了一种情况,即我们的模型在我们的培训数据上表现良好,但未能达到与测试数据相同的性能。
我不是指这里过度拟合。即使我选择了基于交叉验证的最佳模型,并且它仍然在测试数据上表现不佳,但测试数据中还存在一些我们未捕获的固有模式。
想象一下,我试图模拟客户的购物行为。现在,如果我的训练和测试数据如下所示,那么您可以在这里清楚地看到问题。
Covariate Shift
我们可以更正式地定义这种情况。Covariate变量是指我们模型中的预测变量。Covariate Shift是指预测变量在训练和测试数据中具有不同特征(分布)的情况。
在具有许多变量的现实世界问题中,Covariate Shift很难被发现。在这篇文章中,我试图讨论一种识别这种方法的方法,以及如何解释列车和测试之间的这种转换。
基本理念
如果存在Covariate Shift,那么在混合训练和测试中,我们仍然能够对每个数据点(无论是来自测试还是训练)的起源进行分类,并具有较好的准确性。
考虑上面的例子,年龄是测试和训练之间的drifting feature。如果我们采用像随机森林这样的分类器,并尝试将行划分为测试和训练,那么年龄将是分割数据的一个非常重要的特征。
现在让我们尝试将这个想法应用于真实的数据集。我使用这个kaggle比赛的数据集:https://www.kaggle.com/c/porto-seguro-safe-driver-prediction/data
第一步:数据预处理
我们必须首先清理我们的数据,对所有缺失值进行归一化处理,并对所有分类变量进行标记编码。对于这个数据集,这一步不是必需的,所以我跳过了这一步
#loading test and train data
train = pd.read_csv('train.csv',low_memory = True)
test = pd.read_csv('test.csv',low_memory = True)
第二步: source of origin指标
我们必须在训练和测试数据中添加一个“is_train”特性。这个特性的值将是0用于测试,1用于训练。
#adding a column to identify whether a row comes from train or not
test[‘is_train’] = 0
train[‘is_train’] = 1
第三步: 结合训练和测试
然后,我们必须将这两个数据集组合起来。此外,由于训练数据具有原来的“target”变量,这在测试中不存在,我们也必须删除该变量。
#combining test and train data
df_combine = pd.concat([train, test], axis=0, ignore_index=True)
#dropping ‘target’ column as it is not present in the test
df_combine = df_combine.drop(‘target’, axis =1)
y = df_combine['is_train'].values #labels
x = df_combine.drop('is_train', axis=1).values #covariates or our independent variables
tst, trn = test.values, train.values
第4步: 构建和测试分类器
为了分类目的,我使用随机森林分类器来预测组合数据集中每一行的标签。您也可以使用任何其他分类器。
m = RandomForestClassifier(n_jobs=-1, max_depth=5, min_samples_leaf = 5)
predictions = np.zeros(y.shape) #creating an empty prediction array
我们使用stratified 4 fold来确保每个类的百分比被保留,并且我们覆盖整个数据一次。对于每一行,分类器将计算它属于训练的概率。
skf = SKF(n_splits=20, shuffle=True, random_state=100)
for fold, (train_idx, test_idx) in enumerate(skf.split(x, y)):
X_train, X_test = x[train_idx], x[test_idx]
y_train, y_test = y[train_idx], y[test_idx]
m.fit(X_train, y_train)
probs = m.predict_proba(X_test)[:, 1] #calculating the probability
predictions[test_idx] = probs
第5步: 解释结果
我们将输出用于分类器的ROC-AUC度量作为估计这些数据有多少covariate shift。
如果分类器能够将行分类为训练并且准确地进行测试,那么我们的AUC分数应该在较高的一侧(大于0.8)。这意味着训练和测试之间有很强的covariate shift。
print('ROC-AUC for train and test distributions:',AUC(y,predictions))
# ROC-AUC for train and test distributions: 0.49944573868
AUC值为0.49意味着没有strong covariate shift的证据。这意味着大多数观察来自不特定于测试或训练的特征空间。
由于这个数据集来自Kaggle,这个结果是相当期待的。正如在这种竞争数据集中精心策划的,以确保这种转变不存在。
在我们开始建模之前,这个过程可以被复制用于任何数据科学问题以检查covariate shift。
超越
在这一点上,我们要么观察covariate shift 与否。那么,我们可以做些什么来提高我们在测试数据上的表现?
Dropping of drifting features
Importance weight using Density Ratio Estimation
drifting features下降
注意:此方法适用于您见证covariate shift的情况。
从我们构建的随机森林分类器对象中提取特征重要性
顶部的特征是drifting和引起shift的特征
从顶部的功能一次下降一个变量,并建立您的模型,并检查其性能。收集性能不降低的所有功能
现在放弃所有这些功能,同时构建最终模型
使用密度比估计的重要权重(Importance weight using Density Ratio Estimation)
注意:不管是否存在covariate shift,该方法都适用。
让我们看看我们在前一节中计算出的预测。对于每个观察,这个预测告诉我们根据我们的分类器它属于训练数据的概率。
predictions[:10]
----output-----
array([ 0.34593171])
所以对于第一行我们的分类器来说,它属于以.34概率训练数据。我们称之为P(train)。或者我们也可以说,它来自测试数据的概率是.66。我们称之为P(test)。现在这是魔术:
对于每一行训练数据,我们计算一个系数w = P(test)/ P(train)。
这告诉我们从训练数据到我们的测试数据有多接近。
这些权重可以使用下面的代码来计算
plt.figure(figsize=(20,5))
predictions_train = predictions[len(tst):] #filtering the actual training rows
weights = (1./predictions_train) — 1.
weights /= np.mean(weights) # Normalizing the weights
plt.xlabel(‘Computed sample weight’)
plt.ylabel(‘# Samples’)
sns.distplot(weights, kde=False)
你可以像这样传递在模型拟合方法中计算的权重
m = RandomForestClassifier(n_jobs=-1,max_depth=5)
m.fit(X_train, y_train, sample_weight=weights)
上述图形中需要注意的一些事项:
观察的重量越高,它与测试数据的相似程度越高
接近70%的训练样本具有接近1的样本权重,因此来自特征空间,该特征空间对于训练或测试高密度区域不是特定的。这符合我们计算的AUC值