python - 在 SMOTETomek 之前和之后使用 train_test_split 时的不同分数
问题描述
我正在尝试将文本分类为 6 个不同的类。由于我有一个不平衡的数据集,我还使用 SMOTETomek 方法,该方法应该综合平衡数据集与额外的人工样本。
我注意到通过管道与“逐步”应用它时存在巨大的分数差异,唯一的区别是(我相信)我正在使用的地方train_test_split
这是我的功能和标签:
for curr_features, label in self.training_data:
features.append(curr_features)
labels.append(label)
algorithms = [
linear_model.SGDClassifier(loss='hinge', penalty='l2', alpha=1e-3, random_state=42, max_iter=5, tol=None),
naive_bayes.MultinomialNB(),
naive_bayes.BernoulliNB(),
tree.DecisionTreeClassifier(max_depth=1000),
tree.ExtraTreeClassifier(),
ensemble.ExtraTreesClassifier(),
svm.LinearSVC(),
neighbors.NearestCentroid(),
ensemble.RandomForestClassifier(),
linear_model.RidgeClassifier(),
]
使用管道:
X_train, X_test, y_train, y_test = train_test_split(features, labels, test_size=0.2, random_state=42)
# Provide Report for all algorithms
score_dict = {}
for algorithm in algorithms:
model = Pipeline([
('vect', CountVectorizer()),
('tfidf', TfidfTransformer()),
('smote', SMOTETomek()),
('classifier', algorithm)
])
model.fit(X_train, y_train)
# Score
score = model.score(X_test, y_test)
score_dict[model] = int(score * 100)
sorted_score_dict = {k: v for k, v in sorted(score_dict.items(), key=lambda item: item[1])}
for classifier, score in sorted_score_dict.items():
print(f'{classifier.__class__.__name__}: score is {score}%')
逐步使用:
vectorizer = CountVectorizer()
transformer = TfidfTransformer()
cv = vectorizer.fit_transform(features)
text_tf = transformer.fit_transform(cv).toarray()
smt = SMOTETomek()
X_smt, y_smt = smt.fit_resample(text_tf, labels)
X_train, X_test, y_train, y_test = train_test_split(X_smt, y_smt, test_size=0.2, random_state=0)
self.test_classifiers(X_train, X_test, y_train, y_test, algorithms)
def test_classifiers(self, X_train, X_test, y_train, y_test, classifiers_list):
score_dict = {}
for model in classifiers_list:
model.fit(X_train, y_train)
# Score
score = model.score(X_test, y_test)
score_dict[model] = int(score * 100)
print()
print("SCORE:")
sorted_score_dict = {k: v for k, v in sorted(score_dict.items(), key=lambda item: item[1])}
for model, score in sorted_score_dict.items():
print(f'{model.__class__.__name__}: score is {score}%')
我得到(对于最好的分类器模型)大约 65% 使用管道,而 90% 使用逐步。不知道我错过了什么。
解决方案
您的代码本身没有任何问题。但是您的分步方法是在机器学习理论中使用不良实践:
不要重新采样您的测试数据
在您的分步方法中,您首先重新采样所有数据,然后将它们拆分为训练集和测试集。这将导致对模型性能的高估,因为您已经更改了测试集中类的原始分布,并且它不再代表原始问题。
相反,您应该做的是将测试数据保留在其原始分布中,以便获得模型对原始数据的执行方式的有效近似值,这代表了生产中的情况。因此,您使用管道的方法是可行的方法。
作为旁注:您可以考虑将整个数据准备(矢量化和重新采样)从您的拟合和测试循环中移出,因为您可能希望将模型性能与相同的数据进行比较。然后,您只需运行一次这些步骤,您的代码就会执行得更快。
推荐阅读
- flutter - 如何在 NestedScrollView 中为 SliverAppBar 设置动画
- python - QMessageBox 不显示 - PyQt
- javascript - 我应该在 react-redux 应用程序中的哪里存储非 UI 数据?
- ruby - 用于重命名对象的实例方法和 setter 方法有什么区别?
- node.js - 如何将 json 文件夹加载到 Javascript 中?
- html - 使用引导网格如何使最后一个项目左对齐而不是分布在整个宽度上
- c# - 如何让 C# 推断我的泛型类型?
- c++ - 在使用链表实现队列时,为什么插入复杂度为 O(1)?
- c# - 使用 .ActiveInspector().CurrentItem 时,检索到的撰写电子邮件的“主题”有时为空
- python - 如何更正设置具有序列错误的数组元素