如何使用 scikit-learn 和 matplotlib 为不平衡数据集绘制 SVC 分类?

Posted

技术标签:

【中文标题】如何使用 scikit-learn 和 matplotlib 为不平衡数据集绘制 SVC 分类?【英文标题】:How to plot SVC classification for an unbalanced dataset with scikit-learn and matplotlib? 【发布时间】:2015-04-12 18:10:49 【问题描述】:

我有一个文本分类任务,包含 2599 个文档和从 1 到 5 的五个标签。文档是

label | texts
----------
5     |1190
4     |839
3     |239
1     |204
2     |127

All ready 以非常低的性能对这些文本数据进行分类,并且还会收到有关定义不明确的指标的警告:

Accuracy: 0.461057692308

score: 0.461057692308

precision: 0.212574195636

recall: 0.461057692308

  'precision', 'predicted', average, warn_for)
 confussion matrix:
[[  0   0   0   0 153]
  'precision', 'predicted', average, warn_for)
 [  0   0   0   0  94]
 [  0   0   0   0 194]
 [  0   0   0   0 680]
 [  0   0   0   0 959]]

 clasification report:
             precision    recall  f1-score   support

          1       0.00      0.00      0.00       153
          2       0.00      0.00      0.00        94
          3       0.00      0.00      0.00       194
          4       0.00      0.00      0.00       680
          5       0.46      1.00      0.63       959

avg / total       0.21      0.46      0.29      2080

很明显,这是因为我有一个不平衡的数据集,所以我发现了这个paper,作者提出了几种解决这个问题的方法:

问题在于,对于不平衡的数据集,学习到的边界是 太接近正面的例子。我们需要以某种方式偏置 SVM 会将边界推离正面实例。维罗普洛斯等 al [14] 建议对正 (C +) 和 负 (C - ) 类

我知道这可能非常复杂,但 SVC 提供了几个超参数,所以我的问题是:有没有办法通过提供 SVC 分类器的超参数将边界推离正实例的方式来偏置 SVC? .我知道这可能是一个难题,但欢迎任何帮助,在此先感谢各位。

from sklearn.feature_extraction.text import TfidfVectorizer
import numpy as np
tfidf_vect= TfidfVectorizer(use_idf=True, smooth_idf=True, sublinear_tf=False, ngram_range=(2,2))
from sklearn.cross_validation import train_test_split, cross_val_score

import pandas as pd
df = pd.read_csv('/path/of/the/file.csv',
                     header=0, sep=',', names=['id', 'text', 'label'])



reduced_data = tfidf_vect.fit_transform(df['text'].values)
y = df['label'].values



from sklearn.decomposition.truncated_svd import TruncatedSVD
svd = TruncatedSVD(n_components=5)
reduced_data = svd.fit_transform(reduced_data)

from sklearn import cross_validation
X_train, X_test, y_train, y_test = cross_validation.train_test_split(reduced_data,
                                                    y, test_size=0.33)

#with no weights:

from sklearn.svm import SVC
clf = SVC(kernel='linear', class_weight=1: 10)
clf.fit(reduced_data, y)
prediction = clf.predict(X_test)

w = clf.coef_[0]
a = -w[0] / w[1]
xx = np.linspace(-5, 5)
yy = a * xx - clf.intercept_[0] / w[1]


# get the separating hyperplane using weighted classes
wclf = SVC(kernel='linear', class_weight=1: 10)
wclf.fit(reduced_data, y)

ww = wclf.coef_[0]
wa = -ww[0] / ww[1]
wyy = wa * xx - wclf.intercept_[0] / ww[1]

# plot separating hyperplanes and samples
import matplotlib.pyplot as plt
h0 = plt.plot(xx, yy, 'k-', label='no weights')
h1 = plt.plot(xx, wyy, 'k--', label='with weights')
plt.scatter(reduced_data[:, 0], reduced_data[:, 1], c=y, cmap=plt.cm.Paired)
plt.legend()

plt.axis('tight')
plt.show()

但我什么也没得到,我无法理解发生了什么,这就是情节:

然后:

#Let's show some metrics[unweighted]:
from sklearn.metrics.metrics import precision_score, \
    recall_score, confusion_matrix, classification_report, accuracy_score
print '\nAccuracy:', accuracy_score(y_test, prediction)
print '\nscore:', clf.score(X_train, y_train)
print '\nrecall:', recall_score(y_test, prediction)
print '\nprecision:', precision_score(y_test, prediction)
print '\n clasification report:\n', classification_report(y_test, prediction)
print '\n confussion matrix:\n',confusion_matrix(y_test, prediction)

#Let's show some metrics[weighted]:
print 'weighted:\n'

from sklearn.metrics.metrics import precision_score, \
    recall_score, confusion_matrix, classification_report, accuracy_score
print '\nAccuracy:', accuracy_score(y_test, prediction)
print '\nscore:', wclf.score(X_train, y_train)
print '\nrecall:', recall_score(y_test, prediction)
print '\nprecision:', precision_score(y_test, prediction)
print '\n clasification report:\n', classification_report(y_test, prediction)
print '\n confussion matrix:\n',confusion_matrix(y_test, prediction)

这是我使用的data。我该如何解决这个问题并以正确的方式绘制这个问题?在此先感谢各位!

从这个问题的答案中,我删除了以下几行:

#
# from sklearn.decomposition.truncated_svd import TruncatedSVD
# svd = TruncatedSVD(n_components=5)
# reduced_data = svd.fit_transform(reduced_data)


#
# w = clf.coef_[0]
# a = -w[0] / w[1]
# xx = np.linspace(-10, 10)
# yy = a * xx - clf.intercept_[0] / w[1]

# ww = wclf.coef_[0]
# wa = -ww[0] / ww[1]
# wyy = wa * xx - wclf.intercept_[0] / ww[1]
#
# # plot separating hyperplanes and samples
# import matplotlib.pyplot as plt
# h0 = plt.plot(xx, yy, 'k-', label='no weights')
# h1 = plt.plot(xx, wyy, 'k--', label='with weights')
# plt.scatter(reduced_data[:, 0], reduced_data[:, 1], c=y, cmap=plt.cm.Paired)
# plt.legend()
#
# plt.axis('tight')
# plt.show()

This where the results:

Accuracy: 0.787878787879

score: 0.779437105112

recall: 0.787878787879

precision: 0.827705441238

此指标有所改善。 我怎样才能绘制这个结果,以便有一个像文档一样的好例子。我想看看这两个超平面的行为?。谢谢大家!

【问题讨论】:

Clearly this is happening by the fact that I have an unbalanced dataset - 根据你所说的,我根本不明白这一点。您能否向我们展示您的代码甚至数据? 如果没有 SVD 并且不触及 class_weight 参数,你会得到什么?尝试先关注性能,然后再关注绘图。 @Ivlad 不使用不平衡数据集文档中的示例,这是我得到的性能:Accuracy: 0.461057692308 score: 0.461057692308 precision: 0.212574195636 recall: 0.461057692308 这是我可以用网格搜索做的最好的。 【参考方案1】:

通过使用SVD 将您的数据减少到5 功能:

svd = TruncatedSVD(n_components=5)
reduced_data = svd.fit_transform(reduced_data)

你会丢失很多信息。只需删除这些行,我就能得到78% 的准确性。

在设置时保留class_weight 参数似乎比删除它做得更好。我没有尝试给它其他值。

考虑使用k-fold cross validation 和grid search 来调整模型的参数。如果您想减少数据的维度,您也可以使用pipeline,以便在不影响性能的情况下确定要减少多少。 Here 是一个示例,展示了如何使用网格搜索调整整个管道。

至于绘图,您只能绘制 2d 或 3d 数据。在使用更多维度进行训练后,您可以将数据减少到 2 或 3 维并绘制它。有关绘图示例,请参阅here。该代码看起来与您正在绘制的相似,我得到了与您相似的结果。问题是您的数据有很多特征,您只能将事物绘制到 2d 或 3d 表面。这通常会让它看起来很奇怪,很难判断发生了什么。

我建议您不要为绘图而烦恼,因为它不会告诉您太多关于高维数据的信息。使用带有网格搜索的 k 折交叉验证以获得最佳参数,如果您想更仔细地研究过拟合,请改为绘制 learning curves。

所有这些结合起来会告诉您更多关于模型行为的信息,而不是绘制超平面。

【讨论】:

感谢您的帮助。我删除了这些行,我得到了这个异常:Traceback (most recent call last): File "/Users/user/test.py", line 35, in <module> a = -w[0] / w[1] File "/usr/local/lib/python2.7/site-packages/scipy/sparse/csr.py", line 253, in __getitem__ return self._get_row_slice(row, col) File "/usr/local/lib/python2.7/site-packages/scipy/sparse/csr.py", line 320, in _get_row_slice raise IndexError('index (%d) out of range' % i) IndexError: index (1) out of range 。知道如何解决这个问题吗?。 @ml_guy - 我还评论了w = clf.coef_[0] a = -w[0] / w[1] xx = np.linspace(-5, 5) yy = a * xx - clf.intercept_[0] / w[1] 以解决这个问题。我还评论了绘图代码。 @ml_guy - 您可以选择在 2 维或 3 维中绘制它,您不能在更高维中绘制它。为此,您需要使用 PCA 或其他降维算法来减少数据。交叉验证方法不会防止过度拟合,但它会帮助您识别它:如果使用交叉验证获得良好的结果,您可以假设您的算法是好的。否则你可以假设有问题。稍后我将在我的答案中添加绘图代码。 @ml_guy - 你没有做错什么,如果你把尺寸缩小太多,你会得到更差的性能。这是可以预料的。你不应该减少它们,除非你有充分的理由,例如提高执行时间或准确性,后者不是这里的情况。我在帖子中添加了有关如何诊断模型问题的更多信息。 @ml_guy - 除了它们在我链接到的 scikit-learn 网站上显示之外,我不知道该怎么做。如果这不能产生良好的结果,那么您的数据就无法通过减小尺寸来令人满意地绘制。我想您也可以尝试 scikit 的主成分分析 (PCA),看看是否有帮助。【参考方案2】:

如果我正确理解了您的输入,您有:

1190 个标记文本,共 5 个 1-4 个标记文本中的 1409 个

您可以尝试进行顺序分类。首先威胁所有 5 个标签为 1,所有其他标签为 0。为此任务训练分类器

其次,从数据集中删除所有 5 个示例。训练分类器对 1-4 个标签进行分类。

分类时运行第一个分类器,如果返回0 - 运行第二个分类器得到最终标签。

虽然我不认为这种分布真的是偏斜和不平衡的(它应该是 5 的 90% 和 10% - 所有其余部分,都是非常偏斜的,因此向 SVC 引入偏差可能会很有趣)。因此,我认为您可能想尝试其他一些分类算法,因为您的选择似乎不适合此任务。或者您可能需要在 SVC 中使用不同的内核(我假设您使用线性内核,尝试不同的方法 - RBF 或多项式)。

【讨论】:

我使用的是 rbf,我尝试了多项式、RF、LR 和 svc,它的性能最好。 感谢您的反馈。关于如何偏置 SVC 分类器的任何想法? 我相信 klubow 在下面提供了一些有用的信息。在 scikit-learn 中,您可以在此处阅读有关使用不平衡分类的正确方法:scikit-learn.org/stable/auto_examples/svm/… 请记住,您需要手动调整类权重以获得最佳性能,或者尝试使用 GridSearchCV 自动调整它们(尽管您会需要使用一些具体的评分指标,否则不会得到好的结果)。 感谢您的参考。我不明白什么意思:class_weight=1: 10(即1: 10)。就我而言,它将是:1:5? 为什么,这是你的班级标签,然后是你给他们的重量。在您的情况下,例如,它将是 5:10(如果您的“5”类具有此标签),如果您想为第 5 类赋予 C 参数更大的值。【参考方案3】:

作为一个简单的解决方案,只需在较小的类中增加实例并平衡实例的数量。即使它看起来很愚蠢,也可以使用,并且在钻机配置中不需要。

使用这种方法的想法是模仿每个班级的规模学习率行为,以了解其班级规模。也就是说,在基于梯度的优化方法中,您应该与每个类的类大小成反比地缩放学习率,以便您可以防止模型过度学习某些类而不是其他类。

如果您的问题非常大并且您正在使用批量更新,那么不要预订所有数据集和计数类,而只考虑小批量并根据小批量中每个类的实例数动态调整学习率.

这意味着如果您的主学习率为 0.01,并且在一批实例中,您有 0.4 个 A 类和 0.6 个 B 类,那么对于每个类,您需要将最终学习率调整为 A 类的 master_learning rate (这意味着保持不变),B 类的 2/3*master_learning 率。因此,A 类的步幅更大,B 类的步幅相反。

我的选择是,特别是对于大型问题和通过复制实例为较小的类增加数据,或者作为更稳健的选择,为复制的实例添加一些噪声和方差。通过这种方式,(取决于您的问题)您还可以训练一个对小变化更稳健的模型(这对于特别是图像分类问题非常常见。)。

【讨论】:

你有任何关于这行得通的陈述的参考资料吗?我认为它不适用于 SVM。 您只需平衡每个类的模型更新次数。从理论上讲,将学习率设置为与每个类的实例数成反比更有意义。通过设置实例的数量,您只是以幼稚的方式模仿相同的行为。 @ml_guy 因为 SVM 会根据您拥有的点找到一个大边距分离超平面。拥有更多相同点不会改变找到的超平面的样子。 在投反对票之前,请尝试解决玩具问题。然后我们可以再次讨论。 1.一个玩具问题没有任何意义。 2.您没有为您的建议提供任何论据或参考; 3. 好像你在建议一些 scikit-learn 可以让你以击球方式做的事情; 4. 真的,SVC 的学习率是多少?没有你可以调整的东西。所以真的,不管这是否有效,这都是一个糟糕的答案。【参考方案4】:

您可能已经尝试将class-weight 设置为auto,但我想确认一下。

也许平衡实验 (oversampling or undersampling) 会有所帮助,klubow 已经建议了一些 lib。

【讨论】:

使用 class-weight auto 这是结果:Accuracy: 0.453846153846 score:0.458315519453 recall: 0.453846153846 precision: 0.205976331361 指标下降..【参考方案5】:

您可能需要检查 SVM 分类器的 class_weight 参数 (http://scikit-learn.org/stable/modules/generated/sklearn.svm.SVC.html) 要么 平衡您的数据 (https://github.com/fmfn/UnbalancedDataset/blob/master/UnbalancedDataset.py)

【讨论】:

对于这个问题,我需要设置什么样的 class_weight 才能偏置 SVM?。感谢您的帮助! 这对我帮助很大,知道如何绘制这个吗?我只想对赏金公平。

以上是关于如何使用 scikit-learn 和 matplotlib 为不平衡数据集绘制 SVC 分类?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 scikit-learn 和 matplotlib 为不平衡数据集绘制 SVC 分类?

如何使用 scikit-learn 计算用于情感分析的分类报告

Scikit-learn:如何获得真阳性、真阴性、假阳性和假阴性

如何在 scikit-learn 中继续训练 svm 和 knn?

如何使用 scikit-learn 组合具有不同维度输出的特征

如何使用 scikit-learn 执行非正则化逻辑回归?