如何使用 python scikit-learn 执行欠采样(正确的方法)?

Posted

技术标签:

【中文标题】如何使用 python scikit-learn 执行欠采样(正确的方法)?【英文标题】:How to perform undersampling (the right way) with python scikit-learn? 【发布时间】:2016-04-22 06:19:15 【问题描述】:

我正在尝试使用 python scikit learn 对多数类进行欠采样。目前我的代码寻找少数类的 N,然后尝试从多数类中对完全相同的 N 进行欠采样。结果,测试和训练数据都具有这种 1:1 的分布。但我真正想要的是仅在训练数据上进行这种 1:1 分布,但在测试数据中的原始分布上进行测试。

我不太确定如何做后者,因为两者之间有一些 dict 矢量化,这让我感到困惑。

# Perform undersampling majority group
minorityN = len(df[df.ethnicity_scan == 1]) # get the total count of low-frequency group
minority_indices = df[df.ethnicity_scan == 1].index
minority_sample = df.loc[minority_indices]

majority_indices = df[df.ethnicity_scan == 0].index
random_indices = np.random.choice(majority_indices, minorityN, replace=False) # use the low-frequency group count to randomly sample from high-frequency group
majority_sample = data.loc[random_indices]

merged_sample = pd.concat([minority_sample, majority_sample], ignore_index=True) # merging all the low-frequency group sample and the new (randomly selected) high-frequency sample together
df = merged_sample
print 'Total N after undersampling:', len(df)

# Declaring variables
X = df.raw_f1.values
X2 = df.f2.values
X3 = df.f3.values
X4 = df.f4.values
y = df.outcome.values

# Codes skipped ....
def feature_noNeighborLoc(locString):
    pass
my_dict16 = ['location': feature_noNeighborLoc(feature_full_name(i)) for i in X4]
# Codes skipped ....

# Dict vectorization
all_dict = []
for i in range(0, len(my_dict)):
    temp_dict = dict(
        my_dict[i].items() + my_dict2[i].items() + my_dict3[i].items() + my_dict4[i].items()
        + my_dict5[i].items() + my_dict6[i].items() + my_dict7[i].items() + my_dict8[i].items()
        + my_dict9[i].items() + my_dict10[i].items()
        + my_dict11[i].items() + my_dict12[i].items() + my_dict13[i].items() + my_dict14[i].items()
        + my_dict19[i].items()
        + my_dict16[i].items() # location feature
        )
all_dict.append(temp_dict)

newX = dv.fit_transform(all_dict)

X_train, X_test, y_train, y_test = cross_validation.train_test_split(newX, y, test_size=testTrainSplit)

# Fitting X and y into model, using training data
classifierUsed2.fit(X_train, y_train)

# Making predictions using trained data
y_train_predictions = classifierUsed2.predict(X_train)
y_test_predictions = classifierUsed2.predict(X_test)

【问题讨论】:

看来你的缩进是错误的。 Ad rem:不确定在最后一行使用您想要的测试数据有什么问题。 缩进已更正。原始分布是说 20:1 多数:少数类。我的代码使得测试和训练数据都是 1:1 多数:少数。一些 ML 顾问建议我在训练集中应采用 1:1 的比例,但在测试集中保留原来的 20:1 比例。 @您只是在重复您在问题中所说的话。是什么阻止您在测试阶段使用 20:1 比率的数据?顺便提一句。你的 for 循环没有任何意义。 循环工作得很好,我没有发布我的整个代码,因为它将是 600 行,所以我需要削减一些东西。我展示了这一点来说明 dict 矢量化过程。我不确定我是否理解你的第一个问题。我已经在另一个代码集中进行了 20:1 的测试和训练,但我没有包含欠采样代码。如果您问是什么阻止了我从上述代码中测试 20:1 测试数据,那么我不再拥有它了。 df = merge_sample 是在欠采样之后,以及以下各项:X_train、X_test、y_train、y_test 派生自此 df。 我在循环之前添加了一些示例代码来说明这里的逻辑。 【参考方案1】:

您希望对某一类别的训练样本进行二次抽样,因为您需要一个对所有标签都一视同仁的分类器。

如果您想这样做而不是二次抽样,您可以将分类器的“class_weight”参数的值更改为“平衡”(或某些分类器的“自动”),这样就可以完成您想做的工作。

您可以阅读 LogisticRegression 分类器的文档作为示例。注意'class_weight'参数here的描述。

通过将该参数更改为“平衡”,您将不再需要进行二次采样。

【讨论】:

我应该期望性能比没有 class_weight=balanced 时更好还是更差?我运行它,性能恶化。而且我认为通常欠采样应该会提高性能。 我的经验是准确性会变差。原因是您的测试集中也存在不平衡。当标签错误会产生非常糟糕的后果时,这种加权非常有用。例如,当一个人进行癌症检测时,您需要识别可疑病例,即使他们没有。所以我建议不要进行二次采样并将class_weight设置为None。这是我通常做的。 所以在使用调整后的类权重后准确性变差的情况并不少见?我不完全清楚,您是否建议将 class_weight 设置为“平衡”或“无”?谢谢。 是的,使用 class_weight='balanced' 之后,准确性通常会变差,所以我建议将 class_weight 设置为 'none'。 我以为你建议使用类权重来处理不平衡数据而不进行欠采样,如果我设置 class_weight="none",那不只是默认值而且仍然不需要考虑到数据不平衡的问题?

以上是关于如何使用 python scikit-learn 执行欠采样(正确的方法)?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 python 虚拟环境中导入 scikit-learn?

如何在 Python 中使用带有 Keras 的 scikit-learn 评估指标函数?

如何使用 python scikit-learn 执行欠采样(正确的方法)?

python - 如何在python scikit-learn中找到逻辑回归中的正则化参数?

在 scikit-learn 中使用 python 生成器 [关闭]

如何使用 scikit-learn for python 分析和预测(机器学习)时间序列数据集