Vet Analytics技術ブログ

機械学習・フロントエンド・獣医関連のことについて更新していきます

SVMの理論とPythonを使った実装方法について

こんにちは、Vet Analyticsでデータアナリストをしている富永です。

Vet Analyticsは獣医学機械学習の知見を導入しようと日々奮闘しているデータサイエンス・エンジニアチームです! 東京大学本郷キャンパス付近でいつも活動しているので興味がありましたらtwitterアカウントから連絡してください。

前回はk-means法についての理論とpythonを使った実装方法について解説しました。 vetanalytics.hatenablog.com

今回はSVM(サポートベクトルマシン)の理論と実装方法について説明していきたいと思います!

SVM(サポートベクトルマシン)とは

SVM(サポートベクトルマシン)はかなりよく見る機械学習アルゴリズムの1つで機械学習初心者ならば必ずおさえておきたい アルゴリズムの1つです。 SVMは乱暴にいうとクラスター同士の真ん中に境界を引くアルゴリズムのことです。

f:id:vetanalytics:20200315111805j:plain
分類できそうなクラスタ

上のように2つのクラスターがある時直感的には一本の境界が引けそうですよね? でもその一本はどのようにして決めますか?? f:id:vetanalytics:20200315112049j:plain

明確な定義がないと境界線が定まらないのです!

そこでSVMではこの境界の定義を 「その境界に一番近い点との距離の最大化」としました。 f:id:vetanalytics:20200315112938j:plain

この境界線と直交するようなベクトル をwとすると この2つの点を通るような直線は

 w_{0} + w^{T}x_p=1
 w_{0} + w^{T}x_n= -1

となるので、この2つの式から

 w^{T}(x_p-x_n)=2

これの両辺をベクトル の長さで割ると

 \displaystyle\frac{w^{T}(x_p-x_n)}{\|w\|} = \frac{2}{\|w\|}

となり、これは左辺が2つの境界線の距離を表しているので右辺を最大化すればいいことがわかりますね。 これは、

\frac{1}{2}\|w\|^2

を最小化する問題と同値なので、 誤分類によるペナルティをpとすると 最小化すべき問題は

\frac{1}{2}\|w\|^2+Cp

となります。(ここでCは定数) これを最小化することがSVMの1番の目標となるわけです。この問題をPythonを使って実装していきたいと思います。

SVMpythonでの実装方法

今回はSVMPythonモジュールのScikit-learnを用いて実装していきたいと思います。 まずは例によってサンプルデータの作成をしていきます。

%matplotlib inline
from sklearn.datasets import make_blobs
import matplotlib.pyplot as plt
from sklearn.svm import SVC

x_train,y_train = make_blobs(n_samples=200, #plotするサンプル数
                n_features=2, #特徴量(2次元)
                centers=4, #クラスターの数
                cluster_std=0.4, #クラスターごとの標準偏差
                shuffle=True, 
                random_state=0)
x_test,y_test = make_blobs(n_samples=300, #plotするサンプル数
                n_features=2, #特徴量(2次元)
                centers=4, #クラスターの数
                cluster_std=0.8, #クラスターごとの標準偏差
                shuffle=True, 
                random_state=0)

この時にrandom_stateの値をtrainとtestで変えると予測モデルが使えなくなるので注意してください。 このコードによって生成されたサンプルデータをplotしていきます。

plt.scatter(x_train[:,0], x_train[:,1], c="red", marker="o", s=40)
plt.tight_layout()
plt.show()
plt.scatter(x_test[:,0], x_test[:,1], c="green", marker="o", s=40)
plt.tight_layout()
plt.show()

f:id:vetanalytics:20200314213546p:plain
trainデータの分布
f:id:vetanalytics:20200314213615p:plain
testデータの分布

次にtrainデータを用いて予測モデルの訓練と予測の実行を行っていきたいと思います。

svm = SVC(kernel="linear", C=1.0, random_state=0)
svm.fit(x_train,y_train)
predict = svm.predict(x_test)

この時Cがペナルティの許容度です。(前章参照) これで予測完了です。予測モデルの分類がうまくいっているかどうかを確かめてみましょう

plt.scatter(x_test[predict==0,0],
           x_test[predict==0,1],
           s=40,
           c="red",
           marker="o",
           label="1")
plt.scatter(x_test[predict==1,0],
           x_test[predict==1,1],
           s=40,
           c="green",
           marker="o",
           label="2")
plt.scatter(x_test[predict==2,0],
           x_test[predict==2,1],
           s=40,
           c="blue",
           marker="o",
           label="3")
plt.scatter(x_test[predict==3,0],
           x_test[predict==3,1],
           s=40,
           c="yellow",
           marker="o",
           label="4")
plt.legend(scatterpoints=1)
plt.tight_layout()
plt.show()

f:id:vetanalytics:20200314215238p:plain
予測結果
予測モデルが正しくクラスターを分類できていることがわかります。

この予測はデータセットの形状からも納得できる結果だと思います。 しかし、訓練データと予測データの形状が異なる場合はどうなるのでしょうか?

SVMの予測失敗例

例えば、

f:id:vetanalytics:20200314214131p:plain
失敗訓練データ
f:id:vetanalytics:20200314214217p:plain
失敗テストデータ

のような直感的にもデータの分布が異なる場合について予測モデルを組み立てるとどうなるのでしょうか?

これに対して先ほどのように予測モデルを立てると

f:id:vetanalytics:20200314214414p:plain
予測結果
全く予測できていないことがわかります

このように訓練データに対して予測データが異なると対応できないのが教師あり学習の弱いところです。 テストデータが学習時と似通っているほど精度が高くなります。

これは多くの機械学習プロジェクトで障壁となっている問題の1つです。 数多くのプロジェクトがこの障壁にぶつかり頓挫しました(笑) 次回以降にそれらのプロジェクトにも触れられたらと思います!

次回はロジスティック回帰について書きます。

Vet Analyticsは様々な方のコンタクトを歓迎しています!DMへのご連絡をお待ちしております!