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

はじめに

この章のサンプルプログラムは、Jupyter NotebookのScikit-learnのバージョンが’0.20.0’及び、
Google Colab上でScikit-learnのバージョンが’0.20.2’にて動作確認ができています。
それ以前のバージョンでは動作しない可能性がありますのでご注意ください。

SVM(Support Vector Machine)

SVM(Support Vector Machine)は、機械学習モデルの一種で、非常に強力なアルゴリズムです。
教師あり学習で、分類と回帰を扱うことができますが、主に分類のタスクで使われます。

サポートベクターとは

まず、アルゴリズムの名前にも出てくるSupport Vectorとは、データを分割する直線に最も近いデータ点の事です。
SVMでは、このサポートベクターが大きな役割を果たします。
また、サポートベクターを定めてこのような分割線が決まれば、あとはコレより上にあるか下にあるかで、どのクラスに属しているかの予測が出来るようになるわけです。

SVMの仕組み

SVMの肝は、マージン最大化とカーネル法により非線形データを扱えるというところです。
ただ、SVMの仕組みをきちんと理解しようと思うとそれなりに骨が折れます。
ちょっと理論はまだ手をつける予定がないが、強力と噂のアルゴリズムを試してみたい。という方は、仕組みを気にせず読み進めてください。
というのも、SVMの実装はscikit-learnを使えば、とても簡単に行うことができるからです。
ここの理論について詳しく知りたいという方は、“アルゴリズム(理論編)”の”SVM”を参考にしてください。
(大学で学ぶ基礎数学と数理最適化の知識がある方はトライしてみてください。ちなみに、SVMのアルゴリズムは難しいので注意してください。)

SVMのメリットとデメリット

アルゴリズムを選択する上で、各アルゴリズムのメリットとデメリットを把握しておく事は重要です。
● SVMのメリット
データの次元が大きくなっても識別精度が良い
最適化すべきパラメータが少ない

● SVMのデメリット
学習データが増えると計算量が膨大になる
基本的に2クラス分類に特化している
スケーリングが必要
(SVMでは距離を測定するので、大きい範囲をとる特徴量に引きずられないようにする)

線形SVMと非線形SVM

SVMでは、線形と非線形を扱うことが出来ます。
非線形SVMとは次のようなSVMのことです。

Scikit-learnのLinearSVC()を使うことで、線形SVMを扱うことができます。
また、SVC()を使うことで非線形SVMを扱うことができます。

# 線形SVMの場合
from sklearn.svm import LinearSVC
model1 = LinearSVC()

# 非線形SVMの場合
from sklearn.svm import SVC
model2 = SVC()

SVMの実装例

実装例として、SVMで画像認識を行います。
まず、はじめにScikit-learnにあらかじめ用意されている画像データをダウンロードします。
(初めて行う際に、この作業は数分かかります。)

import sklearn as sk
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import fetch_olivetti_faces

faces = fetch_olivetti_faces()
print(faces.DESCR)

faces.DESCRによって、データセットの説明をみることができます。
実行してみると、
このデータは40人の顔を10枚ずつの計400枚の画像があることが分かります。
また、この画像データは64×64 pixelで構成されています。
どのようなデータがあるかは、以下のようにによって確認できます。

print(faces.keys())

次のように出力されます。

['target', 'data', 'DESCR', 'images']

shapeによって、そのデータの要素数が確認できるのでみてみましょう。

print(faces.images.shape)
print(faces.data.shape)
print(faces.target.shape)

上記を実行すると、次のように出力されます。

(400, 64, 64)
(400, 4096)
(400,)

imagesは400×64×64(64×64の画像400枚)、
dataは400×4096(64×64を一列にしたもの400個)
targetは400×1(正解データ400個)
ということが分かります。
では、実際にデータをplotして画像を出力してみましょう。
(下のプログラムは最初のうちは理解出来なくても構いません。)

def print_faces(images, target, top_n):
    #出力する画像のサイズを設定する
    fig = plt.figure(figsize=(12, 12))
    fig.subplots_adjust(left=0,right=1,bottom=0,top=1,hspace=0.05,wspace=0.05)
    for i in range(top_n):
        p = fig.add_subplot(20, 20,i+1,xticks=[],yticks=[])
        p.imshow(images[i],cmap=plt.cm.bone)

        #正解ラベルを表示
        p.text(0,14,str(target[i]))
        p.text(0,60,str(i))
    plt.show() # 実行環境によってはなくても描画されます。

print_faces(faces.images, faces.target,20)

実行すると、顔の画像が出力されます。

データの中身が確認できたので、分類を行なってみましょう。
今回は、画像データからそれが誰なのかを分類します。
scikit-learnのSVC(Support Vector Classifier)を使います。

from sklearn.svm import SVC
svc_1 = SVC(kernel='linear')

SVCには、様々なパラメータがありますが一番重要なパラメータはkernelというものです。
ここでは詳しく説明しませんがどのような空間を作成するというものです。
今回は一番単純で計算速度がはやいlinear kernelを使用します
(他に定番のものとしてrbf kernelがよく使われます。)
まず、データを学習データとテストデータに分割します。

from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(faces.data, faces.target,test_size=0.25,random_state=0)

そして、学習データを使って学習させ交差検定を行います。

from sklearn.model_selection import cross_val_score, KFold
from scipy.stats import sem

def evaluate_cross_validation(clf, x, y, K):                        
    cv = KFold(n_splits=K, random_state=0,shuffle=True)
    scores = cross_val_score(clf,x,y,cv=cv)
    print(scores)
    print ("Mean score: {} (+/-{})".format( np.mean (scores), sem(scores)))

evaluate_cross_validation(svc_1,x_train,y_train,5)

実行すると、次のような出力になります。

[ 0.93333333  0.86666667  0.91666667  0.93333333  0.91666667]
Mean score: 0.9133333333333334 (+/-0.012247448713915886)

上記のスコアを見てみると、91%のスコアが出ているのでかなり良い結果となっていることがわかります。
学習データとテストデータの両方で性能を評価してみましょう。

def train_and_evaluate(clf,x_train,x_test,y_train,y_test):
    clf.fit(x_train,y_train)

    print("Accuracy(学習データ):",clf.score(x_train,y_train))
    print("Accuracy(テストデータ):",clf.score(x_test,y_test))

train_and_evaluate(svc_1, x_train,x_test,y_train,y_test)

関数train_and_evaluateを実行すると、
Accuracy(学習データ): 1.0
Accuracy(テストデータ): 0.99
と出力され良いモデルが作れています。

今回作成したプログラム

import sklearn as sk
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import fetch_olivetti_faces

faces = fetch_olivetti_faces()
print(faces.DESCR)

print(faces.keys())

# 要素数の確認
print(faces.images.shape)
print(faces.data.shape)
print(faces.target.shape)


def print_faces(images, target, top_n):
    #出力する画像のサイズを設定する
    fig = plt.figure(figsize=(12, 12))
    fig.subplots_adjust(left=0,right=1,bottom=0,top=1,hspace=0.05,wspace=0.05)
    for i in range(top_n):
        p = fig.add_subplot(20, 20,i+1,xticks=[],yticks=[])
        p.imshow(images[i],cmap=plt.cm.bone)

        # 正解ラベルを表示
        p.text(0,14,str(target[i]))
        p.text(0,60,str(i))

print_faces(faces.images, faces.target,20)


from sklearn.svm import SVC
svc_1 = SVC(kernel='linear')


from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(faces.data, faces.target,test_size=0.25,random_state=0)


from sklearn.model_selection import cross_val_score, KFold
from scipy.stats import sem

# 交差検定
def evaluate_cross_validation(clf, x, y, K):

    cv = KFold(n_splits=K, random_state=0,shuffle=True)
    scores = cross_val_score(clf,x,y,cv=cv)
    print(scores)
    print ("Mean score: {} (+/-{})".format( np.mean (scores), sem(scores)))

evaluate_cross_validation(svc_1,x_train,y_train,5)


def train_and_evaluate(clf,x_train,x_test,y_train,y_test):
    clf.fit(x_train,y_train)

    print("Accuracy(学習データ):",clf.score(x_train,y_train))
    print("Accuracy(テストデータ):",clf.score(x_test,y_test))

train_and_evaluate(svc_1, x_train,x_test,y_train,y_test)

まとめ

この章では、SVMに関して学び、PythonでSVMのプログラムを書き、画像の分類を行いました。
SVMとは何か、メリットやデメリットをしっかりとおさえましょう。

Leave a Reply