将CLI LibSVM转换为Python:如何使用svm-scale

时间:2018-01-24 14:42:02

标签: python machine-learning scikit-learn svm libsvm

我正在将一些命令行工具命令转移到可重用的Python脚本。但是,我似乎无法理解LibSVM的Python实现。这是安装了LibSVM库的CLI命令(Linux):

svm-scale -l 0 -u 1 -r models/feats.range input.feats > feats.scaled
svm-predict feats.scaled models/feats.model feats.pred

示例输入文件(input.feats),其中UNK是要预测的标签,如下所示。 (旁注我发现在Windows上测试时,'UNK'作为任意值是不允许的,需要传递一个整数,所以libsvm/tools/checkdata.py告诉我。我不明白为什么。 Linux完全没有问题。)

UNK 1:4.458333333333333 2:24.0 3:0.20833333333333334 4:8.333333333333334 5:29.166666666666668 6:87.5 7:0.5 8:0.5 9:0.16666666666666666 10:0.16666666666666666 11:4.0 12:4.0 13:0.19047619047619047 14:0.041666666666666664 15:0.041666666666666664 16:1.0 17:1.0 18:0.047619047619047616 19:0.2916666666666667 20:0.25 21:7.0 22:6.0 23:0.2857142857142857 29:0.125 30:0.041666666666666664 31:3.0 32:1.0 33:0.047619047619047616

第一个问题是,我似乎无法找到一种方法来实现svm-scale具有较低(-l)和较高(-u)绑定的给定参数模型({{ 1}})和输入文件。官方LibSVM实现的Python branch微不足道。我not the only one with this questionThis answer建议使用-r,即使这适用于简单的sklearn.preprocessing-1,1缩放,我也希望根据以前的参数进行扩展 - 正如{ 0,1的CLI界面中的{1}}(恢复)参数。我还没有找到解决方案。 如何使用以前保存的参数来扩展数据?此类参数文件-r的示例如下所示:

svm-scale

即使这是成功的,我也不完全确定在加载模型和预测标签时如何进行。 以下内容是否正确?(改编自示例here。)

feats.range

下面给出了模型x 0 1 1 3.88936170212766 6.346938775510204 2 7.34375 32.625 3 0.1188118811881188 0.4538461538461538 4 3.3003300330033 34.61538461538461 5 18.13471502590674 67.34693877551021 6 43.38235294117647 78.46153846153847 7 0.4794117647058824 0.7286821705426356 8 0.2713178294573644 0.5205882352941176 9 0.1808873720136519 0.5045045045045045 10 0.1148936170212766 0.4144144144144144 11 1.875 12.83333333333333 12 0.84375 10.33333333333333 13 0.217948717948718 0.6125 14 0.02006688963210702 0.1769230769230769 15 0.02006688963210702 0.1538461538461539 16 0.1875 4 17 0.15625 2.857142857142857 18 0.04477611940298507 0.2264150943396226 19 0.0796812749003984 0.2603550295857988 20 0.04682274247491638 0.2 21 1.5 5.777777777777778 22 0.8125 5 23 0.08490566037735849 0.3459119496855346 24 0 0.101010101010101 25 0 0.08856088560885608 26 0 2.444444444444445 27 0 2.25 28 0 0.1437125748502994 29 0.06825938566552901 0.1923076923076923 30 0.03105590062111801 0.1203703703703704 31 0.59375 5.5 32 0.3125 3 33 0.05220883534136546 0.1857142857142857 34 0 0.01818181818181818 35 0 0.5833333333333334 36 0 0.01558441558441558 37 0 0.5 38 0 0.01481481481481482 39 0 0.25 42 0 0.007281553398058253 43 0 0.1818181818181818 的一个示例。

from libsvm.svm import *
from libsvm.svmutil import *

model = svm_load_model('models/feats.model')
# Given the scaled features 
pred = svm.libsvm.predict(feats_scaled, model)

3 个答案:

答案 0 :(得分:0)

我相信您可以使用sklearn的sklearn.preprocessing.MinMaxScaler来完成所需的行为,可以根据MinMaxScaler(feature_range=(0, 1), copy=True)扩展每个功能是所需的范围。这是通过设置feature_range来实现的。在计算方面,每个要素的最大值和最小值是在fit期间计算的,并在predict期间使用。

svm-scale的工作方式类似,在您建议的文件中保存了转换测试数据所需的缩放因子。在这里,首次调用svm-scale时,每个要素都将在范围集中进行变换。 Scikit的缩放器提供了更大的灵活性,因为您可以使用不同的比例缩放其功能,但这可能是直观的。

当首先拟合训练数据然后预测测试数据时,scikit学习的变换器最好用于整个管道。您可以根据已经包含文件的情况来修改它们,但对于后一种情况,最好编写自己的缩放功能以获得更清晰的代码。例如,假设有文件feats.range

x
0 1
1 -1 1
2 2 18

以下函数将产生所需的输出。

data = [[-1, 2], [-0.5, 6], [0, 10], [1, 18]]

def svm_scale(X, path="feats.range", new_range = (0,1)):
    f = open(path).read().splitlines()[2:] 
    f = np.array([x.split() for x in f]).astype(float)
    my_min, my_max = f[:,1], f[:,2] # second/third col is the feat. mins/max
    X_std = (X - my_min) / (my_max - my_min) # This brings at (0,1)
    X_scaled = X_std * (new_range[1] - new_range[0]) + new_range[0] # Modifies min/max
    return X_scaled

svm_scale(data) # returns array([[ 0., 0.],[ 0.25, 0.25],[ 0.5, 0.5 ], [1.,1.]])

答案 1 :(得分:0)

对不起,我在理解你在这里想要完成什么方面遇到了一些麻烦。

如果问题是"如何创建具有先前保存的特征权重的模型,那将更简单" OR "如何在此CLI中解决此特定的linux / Windows操作系统差异?"但你在评论中承认了这一点。

换句话说,如果我们使用scikit-learn,您已经拥有"每个功能相对缩放数据" ?在你的feat。范围例子中,第0列是特征索引,第1列是min,第2列是max?为什么不直接将缩放器的属性分配给已安装的分钟和最大值?

from sklearn.preprocessing import minMaxScaler
data = [[-1, 2], [-0.5, 6], [0, 10], [1, 18]]
scaler = MinMaxScaler()
scaler.transform(data)

scaler.scale_

返回array([ 0.5 , 0.0625])

但我们可以强迫它成为我们想要的任何东西,例如你的feats.range

from numpy import array
scaler.scale_ = array([12345, 54321]) #your values go here, I think?

scaler.scale_

返回array([12345, 54321])

如果我误解了道歉。

答案 2 :(得分:-1)

我对LibSVM的python API没有太多的经验,但是经过快速浏览后看起来似乎没有完整的命令行界面暴露给python。我在python API中找不到类似svm-scale命令的任何东西,所以如果你真的想直接从python中做到这一点,那么看起来并不容易。

但是,从你的问题我得到的印象是运行命令行界面不一定是个问题,对吗?您可以安装并运行它,但更喜欢通过python自动化它。在这种情况下,我建议一个替代解决方案:使用python脚本的命令行界面。

例如,如果您要运行的命令是:

svm-scale -l 0 -u 1 -r models/feats.range input.feats > feats.scaled

我认为您可以使用以下python代码执行此操作:

import subprocess
subprocess.Popen(["svm-scale", "-l", "0", "-u", "1", "r", "models/feats.range", "input.feats", ">", "feats.scaled"])

注意:我不是100%确定指定输出文件的最后两个参数,也许subprocess.Popen()调用的其他参数会更好(参见例如{{3 }})。我现在无法解决这个问题,但如果在我有时间进行实际测试之前你无法看到这个答案,可以稍后再进行编辑。

当然,此解决方案的另一个复杂之处在于,您的缩放功能最终会出现在文件中,而不是直接在python可访问的RAM中。那么,你将不得不写一些额外的代码来从文件中读取(或者svm_read_problem()可能有效吗?不确定)。这个代码可能比尝试在python中重新实现svm-scale功能要容易得多。