我一直在尝试在我拥有的数据集上使用一些监督学习的基本技术,并且我对整个过程有几个问题(即数据预处理、模型评估等)。
在我开始提出问题之前,让我先让您了解一下我的数据集的样子。数据集来自开放的 ML 存储库,它由 22 种不同类型的文章(目标或类)和 1079 个不同的词(特征)组成。目的是根据这 1079 个单词对这些文章即将发布的简介进行分类。下面你可以看到我的数据集的前 5 行。
正如您在前 22 列中的上述片段中看到的那样,我有我的目标(即文章类型),其余列属于我的单词预测器。我的特征值是二进制的,即如果单词出现在简介中,则为“1”,否则为“0”。首先,我做了一些预处理,将目标与特征分开,将与目标对应的布尔值更改为 0 和 1,并为我的文章添加标签(即 0:«娱乐»,1:«采访“ ETC)。在下面的代码片段中,您可以看到我的样本根据每种不同类型的文章的分布情况。
从上图中可以看出,我的数据集是不平衡的。我的目标是尝试以下分类算法并最终选择最好的一个:1)GaussianNB(GNB),2)KNearestNeighbors(KNN),3)LogisticRegression(LR),4)多层感知器(MLP)和5)支持向量机 (SVM)。在我开始更详细地预处理我的数据集之前,我将我的数据集拆分为 70% 的训练和 30% 的测试,并对这些算法进行了开箱即用测试,您可以在下表中看到我的结果
分类器 | f1score(火车) | 会计(火车) | f1score(测试) | ACC(测试) | 时间 - f1score(train) |
---|---|---|---|---|---|
高斯NB | 23 % | 41 % | 24 % | 40 % | 0.54(秒) |
假的 | 1% | 16 % | 2% | 19 % | 0.01(秒) |
1NN | 4 % | 18 % | 8 % | 24 % | 0.22(秒) |
物流 | 34 % | 56 % | 40 % | 62 % | 1.04(秒) |
MLP | 33 % | 54 % | 39 % | 59 % | 10.5(秒) |
支持向量机 | 19 % | 46 % | 32 % | 57 % | 3.7(秒) |
前两列是根据准确性和 f1(average = macro) 指标在我的训练集上使用 2 折交叉验证的分类器的结果,最后两列是根据测试集上的分类器的结果上述指标。从上表中您可以观察到其中一些分类器的性能较低。在我的下一个任务中,我将尝试使用 StandarScaler、VarianceThreshold、PCA 或 SelectBestK、RandomOverSampling 等技术更详细地预处理我的数据,并使用 GridSearch 优化我的分类器的参数。目前我可以提出我的第一个问题。
Q1上述技术(除了我完全理解的 GridSearch)是否肯定会提高分类器的性能?我的意思是有什么理由证明这些技术通常效果更好,或者它只是一个试验和观察程序?
在下一个代码中,我创建了一个管道,该管道执行以下操作,过采样,在训练模型之前依次删除具有低方差的特征(在这种情况下,我只使用 GaussianNB。请注意,我首先将数据集拆分为训练并测试然后使用过采样。
# Case 1 splitting first and then oversampling on training set
from imblearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from imblearn.over_sampling import RandomOverSampler
from sklearn.feature_selection import VarianceThreshold
selector = VarianceThreshold()
scaler = StandardScaler()
over = RandomOverSampler()
clf = GaussianNB()
pipe = Pipeline(steps = [('over', over),('selector', selector), ('scaler', scaler),\
('GNB', clf)])
train_new, test_new, train_new_labels,\
test_labels_new = train_test_split(features, labels, test_size = 0.3)
pipe.fit(train_new, train_new_labels)
pipe.score(test_new, test_labels_new)
以下管道的测试集的准确率为 38%,比没有此预处理程序的 GaussianNB 的分数低 2%,所以我的第二个问题如下。
Q2为什么这些修改会降低分类器的性能?我的数据集的值或其结构是否有任何迹象可以预测此结果?
现在,如果我更改拆分顺序和过采样,我会得到完全不同的结果,例如,如果我运行以下代码块。
# Case 2 oversampling before splitting
clf = GaussianNB()
features2, labels2 = over.fit_resample(features, labels) # First do the oversampling
train_new, test_new, train_new_labels, test_labels_new \
= train_test_split(features2, labels2, test_size = 0.3) # Then do the splitting
train_new = selector.fit_transform(train_new)
test_new = selector.transform(test_new) # This block of code does all
train_new = scaler.fit_transform(train_new) # all the preprocessing.
test_new = scaler.transform(test_new)
train_new = selector2.fit_transform(train_new, train_new_labels)
test_new = selector2.transform(test_new)
clf.fit(train_new, train_new_labels) # Fit on train
clf.score(test_new, test_labels_new) # Evaluate on test
我在测试集上获得了 74% 的准确率,比以前好得多。所以我的问题是:
Q3为什么用过采样改变分割的顺序会改变结果呢?一般来说,我必须先进行拆分,然后只对我的训练集进行预处理?例如,我知道如果首先进行一些预处理,如缩放、PCA,然后拆分我的集合,那么我的结果将会有偏差,因为我也对测试集进行了预处理,但我不明白为什么过采样也会发生这种情况(如果就是这种情况)。
为了让您对下面的上述结果有另一种看法,我可以在第二种情况下向您展示 GaussianNB 在 10 倍交叉验证中的学习曲线,在这种情况下,我首先进行过采样,然后进行拆分。
从上面的代码片段可以看出,验证分数和训练分数收敛在同一个数字上,这很好地表明该模型可以实现良好的泛化性能。
Q4上述两种情况中哪一种更可靠,可以在未来未见的样本上给我带来好的结果?此外,您会建议对上述数据集进行什么样的预处理?例如,在这两次运行中,我从数据集中删除了属于 2 个或更多类的所有样本,这种修改让我恢复了初始数据集的 84%。如果我创建了这些样本的副本以尽可能多地防止数据集的不平衡,会更好吗?
PS:对不起,我的帖子很长,我不希望得到以上所有问题的答案,如果您对以上任何问题有任何有见地的答案,我将很高兴分享您的意见!提前致谢!