Explore "Full-Stack" in depth!

情報系の専門学校で、今は機械学習に的を絞って学習中。プログラミングを趣味でやりつつ、IT系のあらゆる知識と技術を身に付けるべく奮闘中。

SVMについて簡単に理解する。

概要

サラマンダー本は数式もガンガン出てきて、それなり難しい話が続きますが、

今回はとても人気で、且つ非常に強力なアルゴリズム
SVM( Supoort Vector Machine) を解説します。

この理解は データ分析系のコンペで好成績を残したいという方にも、
機械学習をより深く理解したいという方にも必須なので、一緒に頑張りましょう。

サラマンダー本自体には数理的に複雑な内部のことまで紹介していますが、
その内容についてはいつか触れることにしましょう。

本記事はサラマンダー本の内容を噛み砕きながら初心者に向けて説明する事を趣旨としています。
出来る限り自分の言葉と表現で説明しますが、
手軽さという意味で本書のコードを引用しています。
権利の問題などがあればいつでもご連絡ください。即消去します。

参考資料: SVM実践ガイド カーネル法


SVMの基本概念

さてこのSVMですが、基本的には線形回帰(線形識別モデル)を基礎として、
より汎化性能を高める為のアプローチを可能にしたアルゴリズムとなります。

線形回帰については

drumato.hatenablog.com

これらの記事を参考にしてください。

線形識別モデル(関数)とは、線形回帰のような線を用いて
分類問題に応用する手法のことですが、今回は詳しく説明しないこととします。

私自身はじパタを使って絶賛大勉強中なので、
各章ごとに 記事をあげようかなぁと思っています。

話を本題に戻しましょう。

SVMは線形or非線形の回帰/分類だけでなく、外れ値の検出まで扱える非常に強力なアルゴリズムです。
主に小規模~中規模のデータセットに適しています。

SVMで一番重要な概念は太い道です。
実例をお見せします。


線形識別モデルとSVMの比較

二値分類(2つのクラスに分ける)問題について考えます。
英語で言えばbinary classificationですかね。

機械学習に限らず、技術の専門用語やよく使われる表現については
日本語と英語セットで覚える事をおすすめします。
英語の記事を簡単に読めるようになるので。

f:id:orangebladdy:20181117191927j:plain

このデータセットに対して、 赤色でテストデータを加えて、
訓練セットで学習したモデルの予測した決定境界を見てみましょう。

f:id:orangebladdy:20181117191940j:plain

線形識別モデル の場合、訓練データに対しては正しい線を引けているものの、
インスタンスに近いせいで汎化性能が低く、
少しの位置の変化に対応しきれません。

f:id:orangebladdy:20181117191948j:plain

SVMの場合、 最も近いインスタンス(これをサポートベクトルという)との距離を最大化していて、
汎化性能が十分に高いと言えそうです。

このような分類をマージンの大きい分類といい、
それによって構築された決定境界は太い道を模しています。

これがSVMのわかりやすい利点です。


SVMの特徴

ここからはSVMの特徴についてより詳しく見ていきましょう。

まず、SVMには重要な指標があります。

マージンです。

正則化みたいなものと考えるとわかりやすいかもしれないですね。

正則化についてはこちらの記事を御覧ください。

drumato.hatenablog.com

正則化項が強すぎると過小適合の問題を、
正則化項が効かなすぎると過学習の問題をそれぞれ引き起こすのと同じ様に、

サポートベクトル間のマージンを絶対に確保する(サポートベクトル間にインスタンスが存在しないようにする)
という方式(ハードマージン分類)と、
多少のマージン違反を許容する方式(ソフトマージン分類)です。

一般には、ソフトマージン分類の方が優れていると言われています。
(ハードマージン分類の場合外れ値をサポートベクトルとすることで汎化性能が著しく低下する)

スケールの重要性

SVM特徴量間のスケーリングバイアスの影響をもろに受けます。

感覚的にわかりやすいとは思いますが、
各特徴量の規模感を揃える事でクラス同士の間隔が適切になり、
汎化性能の高いモデルを構築出来ます。

sklearn.preprocessing.StandardScaler()がよく用いられます。


実装、SVM

それではご存知irisを使って、モデルの構築をしてみましょう。

import numpy as np
from sklearn import datasets
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler

from sklearn.svm import LinearSVC

iris = datasets.load_iris()
X = iris['data'][:,(2,3)] #花弁の長さ,花弁の幅
y = (iris['target'] == 2).astype(np.float64) #バージニカ種

svc = Pipeline([
    ('scaler',StandardScaler()),
    ('linear_svc',LinearSVC(C=1,loss='hinge'))
])

svc.fit(X,y)

ここで気をつけるのは、Cについてです。

Cはソフトマージン分類とハードマージン分類を上手くバランシングするパラメータです。
Cが大きい程マージン違反に対して敏感に反応する反面、外れ値の影響を受けやすくなります。

つまり過学習している(=マージンが小さい)場合はCの値を小さくする必要があります。

続いて本書の記述を引用することにします。
理解しなければいけない内容ではないので、興味のある方のみ参考にしてください。

LinearSVCクラスはバイアス項を正則化するので、
まずバイアス項の訓練セットを中央に置こう。
StandardScaler()でスケーリングすれば、これは自動的に適応される。 loss='hinge'を損失関数に指定して、
訓練インスタンスが特徴量より少なければ、dual=Falseを適用しよう。

ここまで線形分離可能なデータについて考えてきましたが、
実際に目の当たりにするデータの殆どは線形分離不可能です。


非線形SVM分類器

ここからは、非線形データに対するSVMの分類を見ていきます。

線形回帰を勉強した私達が真っ先に考えるのは、
多項式回帰のように既存の特徴量から新たな特徴量を作る方法です。
具体的には二乗したりしますよね。

これは線形回帰と同じ様に、
sklearn.preprocessing.PolynomialFeaturesを使って
特徴量の次元を引き上げる事で実現可能です。

from sklearn.datasets import make_moons
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import PolynomialFeatures

svmpipe = Pipeline([
    ('poly_features',PolynomialFeatures(degrees=3)),
    ('scaler',StandardScaler()),
    ('svm',LinearSVC(C=10,loss='hinge'))
])

svmpipe.fit(X,y)  

多項式カーネル

非線形SVMをさらりと見てみて、あなたが思ったことは次のうちのどれか、
またはその全てでしょう。

  • 次数が低いと複雑なデータセットに対して過小適合してしまうのでは…?
  • 逆に次数を高くすると特徴量数が膨大になるのでは?というか組み合わせ爆発による訓練スピードの変化は?

一般に、多項式特徴量を用いた訓練手法は
SVMに限らず多くのアルゴリズムで効果的に機能します。 非線形SVMは、PolynomialFeaturesを用いて簡単に実装出来る反面、
その弱点には上記のものがあげられます。

多項式SVMのような、
組み合わせ爆発や過小適合のデメリットを回避するためにはどうすればいいのでしょうか?

その答えは多項式カーネルが鍵を握っています。

カーネル関数とは

多項式カーネルを理解する上で知っておくべきなのは、
カーネル関数という存在です。

厳密な定義で話せば、
写像された高次元空間における内積を計算」する関数というのでしょうが、
これではあまりに抽象的で初心者の私達に馴染みの無い言い回しです。

順を追って説明していきましょう。

一番馴染みやすいのが、特徴量を

f:id:orangebladdy:20181116195707j:plain

のように表す方法です。

これはn個の特徴量を"行ベクトル"として(行列として)示したもので、

f:id:orangebladdy:20181101233322j:plain

のように(線形回帰モデルの典型例)用いられます。

そして、PolynomialFeaturesを用いて多項式回帰モデルを構築すると、

f:id:orangebladdy:20181116195558j:plain

のような特徴量が得られます。

これは元の特徴量を様々に組み合わせたもので、
一般的に元の特徴量より多くなります。

この、元の特徴量多項式特徴量の違いが、
先程で言う所の線形SVM非線形SVMの違いになるわけです。

多項式特徴量は、多くのデータを説明できる説得力を持つ代わりに、
先述したデメリットがつきまといます

特に組み合わせ爆発の影響は顕著に現れます。

ガウスRBFカーネル

ここでカーネル関数(ex.ガウスRBFカーネル)を導入します。

(類似性特徴量について記述するべきか迷いましたが、
混乱を招く為割愛します。興味のある方は調べてください。)

f:id:orangebladdy:20181116200338j:plain

これがカーネル関数です。

カーネル関数を用いる事で、

  • 多項式特徴量のように、複雑なデータセットに対応可能
  • 実際には特徴量を追加しないので、モデルのスピードが早い

というメリットを享受出来ます。

今は多項式特徴量の欠点を補うものとおぼえておいてください。

rbf_kernel_svm = Pipeline([
    ('scaler',StandardScaler()),
    ('svm_clf',SVC(kernel="rbf",gamma=5,C=0.001))
])

rbf_kernel_svm.fit(X,y)

γは正則化パラメータのようなものです。
過小適合している際には大きくし、
過学習している際には小さくする事を覚えておきましょう。


ここに至るまで少し難しい話が続いたので、ここで整理しておきます。

  • 回帰問題・分類問題どちらについても強力なパフォーマンスを見せるアルゴリズムSVMがある。
    • SVMは決定境界の選定を最適化し、非常に汎化性能が高い。
  • SVM は小規模~中規模のデータセットに対して強力に作用する。
  • 線形分離可能でない場合は、PolynomialFeaturesを用いて多項式特徴量を追加する。
  • 多項式特徴量 にはいくつかのデメリットがあるが、カーネル関数を用いる事で補える

全体像は掴めてきたでしょうか。

一般に、SVMモデルを構築する時には、

データの大きさは?特徴量の数は?」

ということに注意しましょう。

非常に大きいデータセットを扱う時はLinearSVC
中小規模のデータに対してはSVC(kernel='rbf')が効果的です。

基本的にはこの2つが使われますが、
カーネル関数の中には特定のデータセットに最適化されたものがあります。
その場合はそちらを使う事を検討すべきです。

SVM回帰

先程のSVM(分類問題に対して使われるアルゴリズム)では、
サポートベクトル間の太い道に入るインスタンスマージン違反として、
アルゴリズムの汎化性能を調整するという特徴がありました。

この考え方を応用することで、回帰問題を解くことが出来ます。

f:id:orangebladdy:20181117195757j:plain

マージン違反=道の外に出ているインスタンスとして、一本の帯を描きます。

from sklearn.svm import LinearSVR

svmreg = LinearSVR(epsilon=1.5)
svmreg.fit(X,y)

εはマージンの太さを示すパラメータです。

これは線形回帰にしか適合しませんが、
カーネル化されたモデルを使う事で非線形データにも対応可能です。

from sklearn.svm import SVR

svmreg = SVR(kernel='poly',degree=2,C=100,epsilon=0.1)
svmreg.fit(X,y)

これで二次元のデータに対応できる回帰モデルが構築されます。


終わりに

何かサンプルデータで実際に予測モデルを組み立てたノートブックを作成して
公開しようと思っています。

また私自身数理的なアプローチは学習している途中なので、
自分の中で納得出来る解説が出来るようであれば別途記事を作ります。