Support Vector Regressorの実装(sklearn)

概要

sklearnのSupport Vector Regressor(SVR)は主にSVR, NuSVR, LinearSVRの三種類がある。LinearSVRの方がSVRより計算が若干早く、NuSVRはSVRとLinearSVRと実装の仕方が若干違う。詳しくはこちら。今回は一般的なSVRを使用する。

注意点

今回の実装ではsklearnバージョン0.21.2を使用したが、参考文献としてはバージョン0.22のドキュメントを参考にしている点に注意してほしい。しかし、今回の実装では、バージョンによるデフォルト設定が少し違ったものの、それ以外はほとんど変わらない。

環境

sklearn 0.21.2

デフォルトパラメータ

from sklearn.svm import SVR

model = SVR()
SVR(C=1.0, cache_size=200, coef0=0.0, degree=3, epsilon=0.1,
    gamma='auto_deprecated', kernel='rbf', max_iter=-1, shrinking=True,
    tol=0.001, verbose=False)

パラメータ

  • kernel (rbf) : linear, poly, rbf, sigmoid, precomputedから選択可能
    • linear :  \left\lt x, x'\right\gt
    • poly :  (\gamma\left\lt x, x'\right\gt + r) ^ d
    • rbf :  \rm{exp}(-\gamma|x-x'| ^ 2)
    • sigmoid :  (\rm{tanh}(\gamma\left\lt x, x'\right\gt + r)
  • degree (3) : kernelがpolyのときのみ有効。 dのこと。
  • gamma (auto) : kernelがrbf, poly, sigmoidのときに有効。scale, auto, 数値から選択可能。 \gammaのこと。
    • scale : 1 / (n_features * X.var())
    • auto : 1 / n_features
  • coef0 (0) : kernelがpoly, sigmoidのときに有効。 rのこと。
  • tol (1e-3) :
  • C (1.0) : 正則化に使用。l2正則化を行う
  • epsilon (0.1) :

実装

今回はrbfカーネルを使用する。よって、gamma, C, epsilonの3つのパラメータを決める。

データ

ボストンデータを利用する。訓練、テストデータに分離した後、スケーリングする。

from sklearn.datasets import load_boston
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler

data = load_boston()
x_train, x_test, y_train, y_test = train_test_split(data['data'], data['target'], test_size=0.2, random_state=0)
scaler = StandardScaler()
scaler.fit(data['data'])
x_train = scaler.transform(x_train)
x_test = scaler.transform(x_test)

モデル

from sklearn.svm import SVR

model = SVR()

ハイパーパラメータチューニング

今回のハイパーパラメータはC, epsilon, gammaの3つ。グリッドサーチクロスバリデーションで最適化する。5-fold cross validationをする。

n_splits = 5
params = {'C' : [1], 'epsilon' : [1], 'gamma' : ['scale']}

gammaをまずscaleに固定して他のパラメータを調整する。グリッドサーチや1つのパラメータチューニングはRidge回帰の実装で定義されている関数(gscv, search_param1)を使う。GridsearchCV を利用しても内容はほとんど変わらない。今回は、二つのパラメータチューニングの結果をヒートマップで可視化してみる。

def ShowHeatMap(df, param1_name, param2_name):
    x = df.columns
    y = df.index
    score_max = df.max().max()
    score_min = df.min().min()
    fig = plt.figure(figsize=(10, 6))
    ax = fig.add_subplot(111)
    im = ax.imshow(df, interpolation='nearest', vmin=score_min, vmax=score_max, cmap='Reds')
    fig.colorbar(im)
    ax.set_xticks(range(0, len(x)))
    ax.set_yticks(range(0, len(y)))
    ax.set_xticklabels(x)
    ax.set_yticklabels(y)
    ax.set_ylabel(f'param_{param1_name}')
    ax.set_xlabel(f'param_{param2_name}')
    plt.show()

次に、ハイパーパラメータをチューニングするパイプラインを関数で定義する。ハイパーパラメータをチューニングし、グラフを表示し、もっともよかったパラメータを自動的に更新する。

def search_param2(param1_name, param1_values, param2_name, param2_values, params):
    new_params = params.copy()
    new_params[param1_name] = param1_values
    new_params[param2_name] = param2_values
    print(f'set new_params: {new_params}')
    results = gscv([x_train, y_train], new_params)
    cv_score_li = results['validation_score_mean']
    best_index = cv_score_li.index(max(cv_score_li))
    print(f"cv score: {results['validation_score_mean'][best_index]:.2f}+-{results['validation_score_std'][best_index]:.3f}")
    print(f"best params: {results['params'][best_index]}")
          
    scores = results['validation_score_mean']
    param1_li = results[f'param_{param1_name}']
    param2_li = results[f'param_{param2_name}']
    cv_data = np.stack([scores, param1_li, param2_li], axis=1)
    cv_data = sorted(cv_data, key=lambda x: (x[1], x[2]))
    scores = np.array(cv_data).T[0].astype(float)
    x = sorted(new_params[param1_name])
    y = sorted(new_params[param2_name])
    scores = scores.reshape((len(x),len(y)))
    df = pd.DataFrame(scores, columns=y, index=x)
    display(df )
    ShowHeatMap(df, param1_name, param2_name)
    params[param1_name] = [results['params'][best_index][param1_name]]
    params[param2_name] = [results['params'][best_index][param2_name]]

実行する。

search_param2('C', [0.01, 0.1, 1, 10, 100, 1000], 'epsilon', [0, 0.01, 0.1, 1, 10, 100], params)
set new_params: {'C': [0.01, 0.1, 1, 10, 100, 1000, 10000], 'epsilon': [0, 0.01, 0.1, 1, 10, 100], 'gamma': ['scale']}
cv score: 0.87+-0.041
best params: {'C': 100, 'epsilon': 1, 'gamma': 'scale'}

f:id:oosakik:20191216011040p:plain

Cが100、epsilonが1で最も良い結果が出た。次に、gammaをsearch_param1を使って検証してみる。

search_param1('gamma', [0, 0.001,  0.01, 0.1, 1, 10, 100, 1000], params )
set new_params: {'C': [100], 'epsilon': [1], 'gamma': [0, 0.001, 0.01, 0.1, 1, 10, 100, 1000]}
cv score: 0.86+-0.031
best params: {'C': 100, 'epsilon': 1, 'gamma': 0.1}

f:id:oosakik:20191216011113p:plain`

gamma`は0.01がベストであった。

訓練と予測

#train
for k, v in params.items():
    params[k] = v[0]
model.set_params(**params)
model.fit(x_train, y_train)

#predict
y_pred = model.predict(x_test)

結果と分析

決定係数は組み込み関数で計算できる。

r2 = model.score(x_test, y_test)
print(r2)
0.768769885637091

予測した値をプロットする。

import matplotlib.pyplot as plt
fig = plt.figure(figsize=(6,6))
ax = fig.add_subplot(111)
ax.scatter(y_test, y_pred)
ax.set_xlabel('y_test')
ax.set_ylabel('y_pred')
plt.show()

f:id:oosakik:20191216010553p:plain

参照文献

scikit-learn 0.22 sklearn.svm.SVR
scikit-learn 0.22 1.4.Support Vector Machines