调整 SVM 回归的参数

Posted

技术标签:

【中文标题】调整 SVM 回归的参数【英文标题】:Tuning parameters for SVM Regression 【发布时间】:2018-06-10 12:55:09 【问题描述】:

我正在尝试创建 SV 回归。我正在从带有一些高斯噪声的 sinc 函数生成数据。

现在,为了找到 RBF 内核的最佳参数,我通过运行 5 折交叉验证来使用 GridSearchCV。

P.S - 我是 python 和机器学习的新手,所以可能代码在某些方面不是很优化或正确。

我的代码:

import numpy as np
import matplotlib.pyplot as plt
from sklearn.svm import SVR
from sklearn.model_selection import GridSearchCV
from sklearn.metrics import mean_squared_error


def generateData(N, sigmaT):  
    # Input datapoints 
    data = np.reshape(np.linspace(-10, 10, N), (N,1))
    # Noise in target with zero mean and variance sigmaT
    epi = np.random.normal(0 , sigmaT, N)

    # Target
    t1 = np.sinc(data).ravel()              # target without noise
    t2 = np.sinc(data).ravel() + epi        # target with noise
    t1 = np.reshape(t1, (N, 1))
    t2 = np.reshape(t2, (N, 1))

    # Plot the generated data
    plt.plot(data, t1, '--r', label = 'Original Curve')
    plt.scatter(data, t2, c = 'orange', label = 'Data')
    plt.title("Generated data")

    return data, t2, t1


# Generate data from sin funtion
N = 100                         # Number of data points
sigmaT = 0.1                    # Noise in the data 
plt.figure(1)
X, y, true = generateData(N, sigmaT)
y = y.ravel()

# Tuning of parameters for regression by cross-validation
K = 5               # Number of cross valiations

# Parameters for tuning
parameters = ['kernel': ['rbf'], 'gamma': [1e-4, 1e-3, 0.01, 0.1, 0.2, 0.5, 0.6, 0.9],'C': [1, 10, 100, 1000, 10000]]
print("Tuning hyper-parameters")
svr = GridSearchCV(SVR(epsilon = 0.01), parameters, cv = K)
svr.fit(X, y)

# Checking the score for all parameters
print("Grid scores on training set:")
means = svr.cv_results_['mean_test_score']
stds = svr.cv_results_['std_test_score']
for mean, std, params in zip(means, stds, svr.cv_results_['params']):
    print("%0.3f (+/-%0.03f) for %r"% (mean, std * 2, params))

结果是

Best parameters set found on development set:  'gamma': 0.0001, 'kernel': 'rbf', 'C': 1
Grid scores on training set:
-0.240 (+/-0.366) for 'gamma': 0.0001, 'kernel': 'rbf', 'C': 1
-0.535 (+/-1.076) for 'gamma': 0.001, 'kernel': 'rbf', 'C': 1
-0.863 (+/-1.379) for 'gamma': 0.01, 'kernel': 'rbf', 'C': 1
-3.057 (+/-4.954) for 'gamma': 0.1, 'kernel': 'rbf', 'C': 1
-1.576 (+/-3.185) for 'gamma': 0.2, 'kernel': 'rbf', 'C': 1
-0.439 (+/-0.048) for 'gamma': 0.5, 'kernel': 'rbf', 'C': 1
-0.417 (+/-0.110) for 'gamma': 0.6, 'kernel': 'rbf', 'C': 1
-0.370 (+/-0.248) for 'gamma': 0.9, 'kernel': 'rbf', 'C': 1
-0.514 (+/-0.724) for 'gamma': 0.0001, 'kernel': 'rbf', 'C': 10
-1.308 (+/-3.002) for 'gamma': 0.001, 'kernel': 'rbf', 'C': 10
-4.717 (+/-10.886) for 'gamma': 0.01, 'kernel': 'rbf', 'C': 10
-14.247 (+/-27.218) for 'gamma': 0.1, 'kernel': 'rbf', 'C': 10
-15.241 (+/-19.086) for 'gamma': 0.2, 'kernel': 'rbf', 'C': 10
-0.533 (+/-0.571) for 'gamma': 0.5, 'kernel': 'rbf', 'C': 10
-0.566 (+/-0.527) for 'gamma': 0.6, 'kernel': 'rbf', 'C': 10
-1.087 (+/-1.828) for 'gamma': 0.9, 'kernel': 'rbf', 'C': 10
-0.591 (+/-1.218) for 'gamma': 0.0001, 'kernel': 'rbf', 'C': 100
-2.111 (+/-2.940) for 'gamma': 0.001, 'kernel': 'rbf', 'C': 100
-19.591 (+/-29.731) for 'gamma': 0.01, 'kernel': 'rbf', 'C': 100
-96.461 (+/-96.744) for 'gamma': 0.1, 'kernel': 'rbf', 'C': 100
-14.430 (+/-10.858) for 'gamma': 0.2, 'kernel': 'rbf', 'C': 100
-14.742 (+/-37.705) for 'gamma': 0.5, 'kernel': 'rbf', 'C': 100
-7.915 (+/-10.308) for 'gamma': 0.6, 'kernel': 'rbf', 'C': 100
-1.592 (+/-1.513) for 'gamma': 0.9, 'kernel': 'rbf', 'C': 100
-1.543 (+/-3.654) for 'gamma': 0.0001, 'kernel': 'rbf', 'C': 1000
-4.629 (+/-10.477) for 'gamma': 0.001, 'kernel': 'rbf', 'C': 1000
-65.690 (+/-92.825) for 'gamma': 0.01, 'kernel': 'rbf', 'C': 1000
-2745.336 (+/-4173.978) for 'gamma': 0.1, 'kernel': 'rbf', 'C': 1000
-248.269 (+/-312.776) for 'gamma': 0.2, 'kernel': 'rbf', 'C': 1000
-65.826 (+/-132.946) for 'gamma': 0.5, 'kernel': 'rbf', 'C': 1000
-28.569 (+/-64.979) for 'gamma': 0.6, 'kernel': 'rbf', 'C': 1000
-6.955 (+/-8.647) for 'gamma': 0.9, 'kernel': 'rbf', 'C': 1000
-3.647 (+/-7.858) for 'gamma': 0.0001, 'kernel': 'rbf', 'C': 10000
-12.712 (+/-29.380) for 'gamma': 0.001, 'kernel': 'rbf', 'C': 10000
-1094.270 (+/-2262.303) for 'gamma': 0.01, 'kernel': 'rbf', 'C': 10000
-3698.268 (+/-8085.389) for 'gamma': 0.1, 'kernel': 'rbf', 'C': 10000
-2079.620 (+/-3651.872) for 'gamma': 0.2, 'kernel': 'rbf', 'C': 10000
-70.982 (+/-159.707) for 'gamma': 0.5, 'kernel': 'rbf', 'C': 10000
-89.859 (+/-180.071) for 'gamma': 0.6, 'kernel': 'rbf', 'C': 10000
-661.291 (+/-1636.522) for 'gamma': 0.9, 'kernel': 'rbf', 'C': 10000

现在 GridSearchCV 给了我最好的参数 C:1, gamma:0.0001 但我检查了参数应该是 C:1000, gamma:0.5

现在我的问题是

我在 GridSearchCV 中哪里出错了? 你能解释一下 GridSearchCV 结果吗?为什么是负分和方差?它使用什么样的评分标准,例如均方误差、r2 等?

编辑:我还添加了关于如何找到正确参数的代码。我只是尝试将所有参数放入 SVR 和均方误差中。

# Working parameters
svr = SVR(kernel='rbf', C=1e3, gamma = 0.5, epsilon = 0.01)
y_rbf = svr.fit(X, y).predict(X)

# Plotting
plt.figure(1)
plt.plot(X, y_rbf, c = 'navy', label = 'Predicted')
plt.legend()

# Checking prediction error
print("Mean squared error: %.2f" % mean_squared_error(true, y_rbf))

以上参数的图在链接中, https://imgur.com/a/cmwPz

来自 GridSearchCV 的绘图选择参数 https://imgur.com/a/R1OAs

【问题讨论】:

如何检查参数应该是 C = 1000, gamma = 0.5? 我只是随机尝试放置这些值,然后发现这些值实际上非常适合真实值。 GridSearchCV 使用提供的估计器的评分方法。所以对于 SVR,它的 r2 分数。并且对于不合适的情况可能是负面的。 现在至于你所说的最佳参数是 C=1000 和 gamma 0.5,我们需要你估计的完整代码。 我现在编辑了原始帖子,添加了我如何获得正确的参数以及我使用这些参数获得的情节。 【参考方案1】:

这里有几件事很重要:

1) GridSearch 用于查找最佳参数的评分标准。由于您没有为 GridSearchCV 的评分参数提供任何值,因此将使用 SVR 的评分方法,即 R 平方值,而不是您所做的 mean_squared_error。

这可以通过这样做来解决:

from sklearn.metrics import make_scorer
scorer = make_scorer(mean_squared_error, greater_is_better=False)
svr_gs = GridSearchCV(SVR(epsilon = 0.01), parameters, cv = K, scoring=scorer)

2) GridSearch 用于训练的数据量。网格搜索将使用提供的 cv 将数据拆分为训练和测试(在您的情况下 K=5,因此将使用 5 折方法)。这意味着网格搜索将在训练数据上训练 SVR 并计算测试数据的分数,而不是像你正在做的整个数据。这将导致答案的变化。对于 K=5,一次只有 80% 的数据用于训练。这意味着数据比您正在做的要少。

这可以通过将 K 的值增加到 15 或 20 或 25 来解决。

做了这两个改变后,我得到了:

【讨论】:

谢谢!这现在完美无缺。只是一个问题,GridSearchCV 的分数仍然是负数。这会以相反的方式得分吗?比如 mean_squared_error(prediction, true) 这就是为什么我们得到负分? @TanayRastogi 不,这不是您建议的方式。请查看上面的 make_scorer 行以及我如何提供 Greater_IS_Better = False 那里。由于 MSE 是一种损失,因此最低更好,因此为了对它们进行排名(而不是在通过诸如准确度之类的实际分数时更改 python 逻辑,其中越高越好)gridSearch 只是反转符号。然后,您应该再次反转符号以获得实际分数。因此,如果 gridSearch syas 得分为 -0.03,则实际为 0.03。

以上是关于调整 SVM 回归的参数的主要内容,如果未能解决你的问题,请参考以下文章

译:支持向量机(SVM)及其参数调整的简单教程(Python和R)

在 R 中调整 svm 参数(线性 SVM 内核)

调整参数 SVM

R:调整 SVM 参数 - e1071 包中的 class.weights

使用 GridSearchCv 优化 SVR() 参数

在调整参数时识别交叉验证的 SVM 中的过拟合