sklearn SVM 表现非常糟糕

Posted

技术标签:

【中文标题】sklearn SVM 表现非常糟糕【英文标题】:sklearn SVM performing awfully poor 【发布时间】:2017-01-25 20:24:14 【问题描述】:

我有 9164 个点,其中 4303 被标记为我要预测的类,而 4861 被标记为不是该类。它们不是重复的点。

在How to split into train, test and evaluation sets in sklearn? 之后,由于我的dataset 是3 个项目(id、向量、标签)的元组,我这样做:

df = pd.DataFrame(dataset)
train, validate, test = np.split(df.sample(frac=1), [int(.6*len(df)), int(.8*len(df))])

train_labels = construct_labels(train)
train_data = construct_data(train)

test_labels = construct_labels(test)
test_data = construct_data(test)

def predict_labels(test_data, classifier):
    labels = []
    for test_d in test_data:
        labels.append(classifier.predict([test_d]))
    return np.array(labels)

def construct_labels(df):
    labels = []
    for index, row in df.iterrows():
        if row[2] == 'Trump':
            labels.append('Atomium')
        else:
            labels.append('Not Trump')
    return np.array(labels)

def construct_data(df):
    first_row = df.iloc[0]
    data = np.array([first_row[1]])
    for index, row in df.iterrows():
        if first_row[0] != row[0]:
            data = np.concatenate((data, np.array([row[1]])), axis=0)
    return data

然后:

>>> classifier = SVC(verbose=True)
>>> classifier.fit(train_data, train_labels)
[LibSVM].......*..*
optimization finished, #iter = 9565
obj = -2718.376533, rho = 0.132062
nSV = 5497, nBSV = 2550
Total nSV = 5497
SVC(C=1.0, cache_size=200, class_weight=None, coef0=0.0,
  decision_function_shape=None, degree=3, gamma='auto', kernel='rbf',
  max_iter=-1, probability=False, random_state=None, shrinking=True,
  tol=0.001, verbose=True)
>>> predicted_labels = predict_labels(test_data, classifier)
>>> for p, t in zip(predicted_labels, test_labels):
...     if p == t:
...             correct = correct + 1

我在 1833 个标签中只得到正确的 943 个标签 (=len(test_labels)) -> (943*100/1843 = 51.4%)


我怀疑我在这里错过了一些重要的时间,也许我应该为分类器设置一个parameter 以做更精细的工作或其他什么?

注意:第一次在这里使用 SVM,所以任何你认为理所当然的事情,我可能都没有想到......


尝试:

我同意并将负面示例的数量减少到 4303(与正面示例的数量相同)。这略微提高了准确性。


回答后编辑:

>>> print(clf.best_estimator_)
SVC(C=1000.0, cache_size=200, class_weight='balanced', coef0=0.0,
  decision_function_shape=None, degree=3, gamma=0.0001, kernel='rbf',
  max_iter=-1, probability=False, random_state=None, shrinking=True,
  tol=0.001, verbose=False)
>>> classifier = SVC(C=1000.0, cache_size=200, class_weight='balanced', coef0=0.0,
...   decision_function_shape=None, degree=3, gamma=0.0001, kernel='rbf',
...   max_iter=-1, probability=False, random_state=None, shrinking=True,
...   tol=0.001, verbose=False)
>>> classifier.fit(train_data, train_labels)
SVC(C=1000.0, cache_size=200, class_weight='balanced', coef0=0.0,
  decision_function_shape=None, degree=3, gamma=0.0001, kernel='rbf',
  max_iter=-1, probability=False, random_state=None, shrinking=True,
  tol=0.001, verbose=False)

我也尝试了clf.fit(train_data, train_labels),效果相同。


用数据编辑(数据不是随机的):

>>> train_data[0]
array([  20.21062112,   27.924016  ,  137.13815308,  130.97432804,
        ... # there are 256 coordinates in total
         67.76352596,   56.67798138,  104.89566517,   10.02616417])
>>> train_labels[0]
'Not Trump'
>>> train_labels[1]
'Trump'

【问题讨论】:

SVM 需要参数调整,这非常重要(尤其是非线性内核)。你似乎没有调整这些。标准化数据(均值和方差)也非常重要。使用 scikit-learns GridSearchCV 通过交叉验证自动调整这些。 @sascha 您能否提供一个示例或更多内容?我真的是这里的新手!而且你说的很对! 阅读 scikit-learns user-guide。这些是非常基本的步骤,我很困惑为什么人们在没有阅读基本使用规则的情况下使用像 SVM 这样理论上复杂的概念。 Heres a GridSearch example 这也显示了参数调整的重要性 -> 精度在 ~ [0.2, 0.95] 之间 @sascha 我在开始实验之前阅读了 SVM 的文档,但是我没有找到你说的,对不起!无论如何,我刚刚应用了答案,并获得了 49.8% 的准确率,但我的尝试并没有更好……如果您对此有任何意见,请告诉我。 如果你这样做了,然后显示代码。尝试其他参数很有可能(~100%)会提高分数。您还应该阅读一些基本的 ML 课程。 【参考方案1】:

scikit-learn 中的大多数估计器(例如 SVC)都是使用许多输入参数(也称为超参数)启动的。根据您的数据,您必须弄清楚在初始化期间将什么作为输入传递给估计器。如果您查看 scikit-learn 中的 SVC 文档,您会发现它可以使用几个不同的输入参数进行初始化。

为简单起见,让我们考虑可以是“rbf”或“线性”的内核(以及其他一些选择);和 C,这是一个惩罚参数,您想尝试 C 的值 0.01、0.1、1、10、100。这将导致创建和评估 10 种不同的可能模型。

一个简单的解决方案是编写两个嵌套的 for 循环,一个用于内核,另一个用于 C,并创建 10 个可能的模型,看看哪一个是最好的模型。但是,如果要调整多个超参数,则必须编写多个嵌套的 for 循环,这可能很乏味。

幸运的是,scikit learn 有一种更好的方法,可以根据您的超模型的不同值组合创建不同的模型并选择最佳模型。为此,您使用 GridSearchCV。 GridSearchCV 使用两件事进行初始化:估计器的实例,以及超参数字典和要检查的所需值。然后它将运行并根据超参数的选择创建所有可能的模型并找到最佳模型,因此您无需编写任何嵌套的 for 循环。这是一个例子:

from sklearn.grid_search import GridSearchCV
print("Fitting the classifier to the training set")
param_grid = 'C': [0.01, 0.1, 1, 10, 100], 'kernel': ['rbf', 'linear']
clf = GridSearchCV(SVC(class_weight='balanced'), param_grid)
clf = clf.fit(train_data, train_labels)
print("Best estimator found by grid search:")
print(clf.best_estimator_)

您将需要使用与此示例类似的东西,并使用不同的超参数。如果您的超参数有多种不同的值,那么您很有可能会通过这种方式找到更好的模型。

然而,GridSearchCV 可能需要很长时间才能创建所有这些模型以找到最佳模型。一种更实用的方法是改用 RandomizedSearchCV,它随机创建所有可能模型的子集(使用超参数)。如果你有很多超参数,它应该会运行得更快,而且它的最佳模型通常非常好。

【讨论】:

答案缺乏一些解释,但它应该有助于 OP。只是一些评论:GridSearchCV 不会尝试所有可能的模型,但参数候选的笛卡尔积(有序组合)由 shahins 定义。另外:当使用 SVM + rbf-kernel 时,C 和 gamma 是最重要的参数,因此在此处进行了调整(还有更多)。另一个说明:交叉验证的概念独立于 SVM。这也是应该阅读的内容(还有很多参数/概念)。 shashins,你在那里装什么?你不是说clf.fit(train_data, train_labels)吗?我刚刚应用了答案,并得到了 49.8% 的准确率,我的尝试,这根本不是更好.. :/ 还有其他想法吗?我也认为我可能还在做一些愚蠢的事情,请检查我更新的问题! :) 我在回答中添加了更多解释。我希望它有所帮助。【参考方案2】:

在 sascha 的 cmets 和 shahins 的回答之后,我最终做到了:

df = pd.DataFrame(dataset)
train, validate, test = np.split(df.sample(frac=1), [int(.6*len(df)), int(.8*len(df))])

train_labels = construct_labels(train)
train_data = construct_data(train)

test_labels = construct_labels(test)
test_data = construct_data(test)

from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
train_data = scaler.fit_transform(train_data)

from sklearn.svm import SVC
# Classifier found with shahins' answer
classifier = SVC(C=10, cache_size=200, class_weight='balanced', coef0=0.0,
  decision_function_shape=None, degree=3, gamma='auto', kernel='rbf',
  max_iter=-1, probability=False, random_state=None, shrinking=True,
  tol=0.001, verbose=False)
classifier = classifier.fit(train_data, train_labels)

test_data = scaler.fit_transform(test_data)
predicted_labels = predict_labels(test_data, classifier)

得到:

>>> correct_labels = count_correct_labels(predicted_labels, test_labels)
>>> print_stats(correct_labels, len(test_labels))
Correct labels = 1624
Accuracy = 88.5979268958

使用这些方法:

def count_correct_labels(predicted_labels, test_labels):
    correct = 0
    for p, t in zip(predicted_labels, test_labels):
        if p[0] == t:
            correct = correct + 1
    return correct

def print_stats(correct_labels, len_test_labels):
    print "Correct labels = " + str(correct_labels)
    print "Accuracy = " + str((correct_labels * 100 / float(len_test_labels)))

我能够通过更多的超参数调整来进行更多优化!

有用的链接:RBF SVM parameters


注意:如果我不转换test_data,准确率是52.7%。

【讨论】:

谢谢@shahins,您的帮助非常宝贵!我还绘制了confusion matrix。希望我的回答对未来的用户也有帮助,尽管它的 0 分数! :)

以上是关于sklearn SVM 表现非常糟糕的主要内容,如果未能解决你的问题,请参考以下文章

sklearn svm 不合适

具有不平衡数据的 SKlearn SVM RBF

机器学习SVM多分类问题及基于sklearn的Python代码实现

sklearn.svm在建立好模型后怎么使用

sklearn.svm在建立好模型后怎么使用

sklearn系列之 sklearn.svm.SVC详解