使用 Sci-kit Learn SVM 时预测始终相同

Posted

技术标签:

【中文标题】使用 Sci-kit Learn SVM 时预测始终相同【英文标题】:Prediction always the same while using Sci-kit Learn SVM 【发布时间】:2017-06-20 14:22:35 【问题描述】:

我有一个数据集,我试图从 DNA 组成中预测数据条目的 DNA 类型。例如,字符串ATTAG...ACGAT 可能会转换为EI。可能的输出是EIIEN。可以进一步调查数据集here。我尝试将内核从linear 切换到rbf,但结果是一样的。 SVM 分类器似乎每次都输出N。任何想法为什么?我是 Sci-kit Learn 的初学者。

import pandas as pd
# 3190 total
training_data = pd.read_csv('new_training.csv')
test_data = pd.read_csv('new_test.csv')
frames = [training_data, test_data]
data = pd.concat(frames)
x = data.iloc[:, 0:59]
y = data.iloc[:, 60]

x = pd.get_dummies(x)
train_x = x.iloc[0:3000, :]
train_y = y.iloc[0:3000]
test_x = x.iloc[3000:3190, :]
test_y = y.iloc[3000:3190]

from sklearn import svm
from sklearn import preprocessing

clf = svm.SVC(kernel="rbf")
label_encoder = preprocessing.LabelEncoder()
label_encoder.fit(y)

print(label_encoder.transform(train_y))
clf.fit(train_x, label_encoder.transform(train_y))

for u in train_y.unique():
    print(u)

predictions = clf.predict(test_x)

correct = 0
total = len(predictions)
for i in range(total):
    prediction = label_encoder.inverse_transform(predictions[i])
    print('predicted %s and actual %s' % (prediction, test_y[i]))
    print(len(prediction))
    if prediction == test_y[i]:
        correct += 1

print('correct %d out of %d' % (correct, total))

首先,我导入训练和测试数据,将其合并并拆分为 x(输入)或 y(输出标签)。然后我将 x 转换为虚拟变量版本,从原来的 60 列变为 300~ 列,因为每个 DNA 点可以是ATGC,有时是N。基本上每个输入的所有可能输入都为 0 或 1。 (有没有更好的方法来做到这一点?Sci-kit learn 不支持分类编码,我尽我所能从this。)然后我再次分离数据(我必须合并以便我可以在整个数据空间)。

从这里开始,我只是运行 svm 来适应 xy 标签,然后预测 test_x。我还必须编码/标记y,从字符串版本到数字版本。但是,是的,它总是产生N,我觉得这是错误的。我该如何解决?谢谢!

【问题讨论】:

尝试一些其他分类器,如 LinearSVC() 或 KNeighborsClassifer() 并检查它们是否都产生相同的答案。如果是,那么您可能需要检查您的数据。 您是按原样使用数据集还是在导入之前进行了修改? 我修改了数据集,使每个 DNA 点都是自己的变量,因此我应该有 60 个特征变量和 1 个输出标签。 我还使用了 Orange 库来随机化数据集并保存到不同的文件中,这就是为什么我不得不在上述脚本的开头再次组合它们。 【参考方案1】:

我认为问题在于数据被拆分为训练和测试的方式。您已将前 3000 个样本用于训练,其余 190 个样本用于测试。我发现通过这样的训练,分类器会为所有测试样本生成真正的类标签(score = 1.0)。我还注意到数据集的最后 190 个样本具有相同的类标签,即'N'。所以你得到的结果是正确的。

我建议您将数据集拆分为训练并通过ShuffleSplittest_size=.06 进行测试(这大约对应于190/3190,尽管为了更容易地可视化结果,我在下面的示例运行中使用了test_size=.01)。为简单起见,我还建议您使用OneHotEncoder 对特征的分类值进行编码。

这是完整的代码(我冒昧地进行了一些重构):

import numpy as np
from sklearn.preprocessing import OneHotEncoder, LabelEncoder
from sklearn.model_selection import ShuffleSplit
from sklearn import svm

data = np.loadtxt(r'splice.data', delimiter=',', dtype='string')

bases = 'A': 0, 'C': 1, 'D': 2, 'G': 3, 'N': 4, 'R': 5, 'S': 6, 'T': 7

X_base = np.asarray([[bases[c] for c in seq.strip()] for seq in data[:, 2]])
y_class = data[:, 0]

enc = OneHotEncoder(n_values=len(bases))
lb = LabelEncoder()

enc.fit(X_base)  
lb.fit(y_class)

X = enc.transform(X_base).toarray()
y = lb.transform(y_class)

rs = ShuffleSplit(n_splits=1, test_size=.01, random_state=0)
train_index, test_index = rs.split(X).next()
train_X, train_y = X[train_index], y[train_index]
test_X, test_y = X[test_index], y[test_index]

clf = svm.SVC(kernel="rbf")
clf.fit(train_X, train_y)

predictions = clf.predict(test_X)

演示:

Out[2]: 
array(['IE', 'EI', 'EI', 'EI', 'EI', 'IE', 'N', 'N', 'EI', 'N', 'N', 'IE',
       'IE', 'N', 'N', 'IE', 'EI', 'N', 'N', 'EI', 'IE', 'EI', 'IE', 'N',
       'EI', 'N', 'IE', 'N', 'EI', 'N', 'N', 'EI'], 
      dtype='|S79')

In [3]: y_class[test_index]
Out[3]: 
array(['IE', 'EI', 'EI', 'EI', 'EI', 'IE', 'N', 'N', 'EI', 'N', 'N', 'IE',
       'IE', 'N', 'N', 'IE', 'EI', 'N', 'N', 'EI', 'IE', 'EI', 'IE', 'N',
       'IE', 'N', 'IE', 'N', 'EI', 'N', 'N', 'EI'], 
      dtype='|S79')

In [4]: clf.score(test_X, test_y)
Out[4]: 0.96875

注意:确保您的 sklearn 版本是 0.18.1,否则上面的代码可能无法正常工作。

【讨论】:

不错!请注意,该代码仅适用于 Python 2,而 Python 3 则为 data = numpy.loadtxt(r'splice.data', delimiter=',', dtype=bytes).astype(str)train_index, test_index = rs.split(X).__next__() 太棒了!谢谢 :) 我会试试这个 可以确认作品!我将更多地查看文档以获得更好的理解。再次感谢! @Tonechas @Maximilian Peters 为什么是n_values=[60]*X_base.shape[1]?我认为n_values 作为数组意味着每个特征的值数。为什么可能的碱基数不是 8?来源:scikit-learn.org/stable/modules/generated/… 感谢您发现这一点。接得好!我的意思是n_values=[8]*60,但假设所有 60 个功能都可以采用 8 个分类值,您应该简单地写成enc = OneHotEncoder(n_values=len(bases))。我将编辑我的答案以修复此错误。

以上是关于使用 Sci-kit Learn SVM 时预测始终相同的主要内容,如果未能解决你的问题,请参考以下文章

sci-kit learn 库中算法的参数是如何优化的?

Svm 预测 Scikit Learn 中的默认值

使用 SVM 模型和 scikit-learn 进行预测的 AttributeError

Sci-kit Learn Confusion Matrix:发现样本数量不一致的输入变量

使用 scikit-learn SVM 将预测标记为概率分数预测/AUC

Scikit Learn:如何在回归中设置 SVM 输出范围?