MinMaxScaler

概要

sklearn.preprocessingモジュールのMinMaxScalerは、各特徴量が0~1の範囲に納まるように変換する。具体的には、特徴量Fiの最小値(mini)と最大値(maxi)から以下の式により各特徴量FiFi*に変換する。

(1)    \begin{equation*} {F_i}^* = \frac{F_i - min_i}{max_i - min_i} \end{equation*}

挙動

それぞれ異なる正規分布に従う2つの特徴量について、MinMaxScalerを適用したときの挙動を以下に示す。異なる大きさとレンジの特徴量が、変換後にはいずれも0~1の間に納まっているのが確認できる。

コードは以下の通りで、データに対してfit()メソッドでスケールパラメーターを決定し、transform()メソッドで変換を行うところを、これらを連続して実行するfit_transform()メソッドを使っている。

特徴

MinMaxScalerは簡明な方法だが、極端に値が離れた異常値が発生すると本来のデータがその影響を受ける場合がある。

scikit-learn – predict_proba

概要

decision_function()は各データが推測したクラスに属する確信度(confidence)を表すが、超平面のパラメータに依存し、そのレンジや値の大きさと確信度の関係が明確ではない。

これに対してpredict_probaは、それぞれのターゲットが予測されたクラスに属する確率を0~1の実数で表す。2クラス分類では、結果の配列の形状は(n_sumples, 2)となる。

predict_proba()の挙動

以下はmake_circles()で生成した2クラスのデータをGradient Boostingによって分類したときの確信度。各データに対応した2要素の配列の1つ目がクラス0(blue)、2つ目がクラス1(orange)に属する確率を表し、2つの和は1となる。なお16行目でsuppress=Trueとすることで、ndarrayの表示を常に固定小数点としている。

decision_function()との比較

先のコードに以下を続けて、predict_proba()による確率、予測されたクラス、decsion_function()の値と、各データの正解クラスを並べて表示する。予測されたクラスの方の確率が大きいこと、その予測結果とdecision_function()の符号が一致していることが確認できる。

このデータをクラス0(blue)に対する確率(prob0)でソートし、decision_function()との関係を見てみると、以下のことがわかる。

  • blueクラスの確率が高いとdecision_functionの確信度はマイナスで絶対値が大きくなり、orangeクラスの確率が高いと確信度はプラスで絶対値が大きくなる
  • blueクラスの確率とorangeクラスの確率が同程度の時、確信度の絶対値が同程度になり、符号が逆になる
  • 確率に対して確信度は線形ではない

クラス0(blue)に対する確率とdecision_function()の確信度の関係を図示すると以下のようになり、確率に対して確信度が必ずしも線形になっていないことがわかる。

コードはmatplotlib.pyplotをインポートした上で、以下を追加。

決定境界

以下は、predict_proba()で計算された確率を可視化したもので、decision_function()の場合に比べて、直感的にも分かりやすい分布となっている。

コンターに表す値として、30行目でpredict_proba()の結果の0列目、すなわちClass0の確率を取り出している。

3クラス以上の場合

3クラスのirisデータセットにGradientBoostingClassifierを適用し、predict_proba()の出力を見てみる。

このコードの出力結果は以下の通り。3つのクラスに対する確率が得られ、合計は1になる。こちらはdecision_function()が2クラスの時だけ配列が1次元となるのと違って、どのような場合でも行数×列数=データ数×クラス数の配列になる。

なお17行目で、argmaxを使って各データで確率が最大となるクラスを探している。

 

scikit-learn – decision_function

概要

decision_function()は、超平面によってクラス分類をするモデルにおける、各予測データの確信度を表す。

2クラス分類の場合は(n_samples, )の1次元配列、マルチクラスの場合は(n_samples, n_classes)の2次元配列になる。2クラス分類の場合、符号の正負がそれぞれのクラスに対応する。

decision_function()を持つモデルは、LogisticRegressionSVCGladientBoostClassifierなどで、RandomForestはこのメソッドを持っていない。

decision_function()の挙動

decision_function()の挙動をGradientBoostingClassifierで確認する。

まずmake_circles()で2クラスのデータを生成し、外側のクラス0をblue、内側のクラス1をorangeとして再定義する。

次に、データを訓練データとテストデータに分割し、訓練データによって学習する。データ分割にあたって、Xyに加えて文字列に置き換えたy_namedを分割している。学習の際にはXy_namedの訓練データとテストデータのみを用いるのでyについては特に含める必要ないが、ここではtrain_test_split()が3つ以上のデータでも分割可能なことを示している。

学習後の分類器のclasses_プロパティーを参照すると、クラスがどのように表現されるかを確認できる。上のfit()メソッドでy_train_namedを与えたのでクラスの表現が文字列になっているが、代わりにy_trainを用いると[0, 1]のように元のyに対応したクラス表現が返される。

次に、学習済みモデルにテストデータを与えて、decision_function()の結果とpredict()の結果を並べてみる。decision_function()fit()で与えたテストデータ数の1次元配列を返し、各要素の負の値に対してクラス0のblueが、正の値に対してはクラス1のorangeがpredict()で予測されていることがわかる。

decision_function()の各要素の符号に応じてpredict()と同じ結果を得たいなら、次のように処理していくとよい。

最後に、上記のデータと正解であるy_test_namedのデータを先ほどのデータフレームに追加して全体を確認する。predit()メソッドの結果とdecision_function()の符号による判定結果は等しく、y_testと異なるデータがあることがわかる。

decision_function()の意味

decusuib_function()のレベルは超平面上の高さになるが、これはデータ、モデルパラメーターにより変化し、このスケールの解釈は難しい。それはpredict_proba()で得られる予測確率とdecision_function()で計算される確信度の非線形性からも予想される。

circlesデータに対するGradientBoostingClassifierの決定境界とdecision_function()の値の分布を表示したのが以下の図。コンターが交錯していてわかりにくく、直感的にはpredict_proba()の方がわかりやすい

3クラス以上の場合

3クラスのirisデータセットにGradientBoostingClassifierを適用して、decision_function()の出力を見てみる。

このコードの出力結果は以下の通り。2クラスの場合は1次元配列だったが、3クラスになると行数×列数がデータ数×クラス数の配列になる。predict_proba()は2クラスでも2列の配列になるので、decision_function()の2クラスの場合だけ特に1次元配列になると言える。

なお、19行目で各データごとに最大の値をとる列をargmaxで探して、そのサフィックスを”decision”のクラス番号として表示している。

 

scikit-learn – make_circles

概要

sklearn.datasets.make_circles()はクラス分類のためのデータを生成する。2つのクラスのデータが同心円状に分布し、各クラスの半径の差異、データのばらつきを指定できる。

得られるデータの形式

2つの配列X, yが返され、配列Xは列が特徴量、行がレコードの2次元配列。ターゲットyはレコード数分のクラス属性値の整数で0か1の値をとる。

パラメーターの指定

n_samples
総データ数で、奇数の場合は内側のデータが1つ多くなる。2要素のタプルで指定した場合、1つ目が外側、2つ目が内側のデータ数となる。デフォルトは100
shuffle
データをシャッフルするかどうかを指定。Falseにすると、前半がクラス0、後半がクラス1となるように並ぶ。デフォルトはTrue。
noise
ガウス分布のノイズを標準偏差で指定。デフォルトはNoneでノイズなし。
random_state
データを生成する乱数系列を指定。デフォルトはNone
factor
外側に対する内側のデータのスケールファクター。デフォルトは0.8。

利用例

以下はスケールファクターを0.5、ノイズを0.15としてデフォルトの100個のデータを生成した例。

以下はノイズの程度を変化させた例。

以下はスケールファクターを変化させた例。

 

MLP – 多層パーセプトロン

線形モデルの多層化

“Pythonではじめる機械学習”の写経。多層パーセプトロン(Multilayer perceptron : MLP)はフィードフォワード・ニューラルネットワークとも呼ばれる。

まず、線形モデルを以下の式で表す。

(1)    \begin{equation*} b + w_0 x_0 + \cdots + w_n x_n \end{equation*}

n = 3の場合について図示すると、以下のように表せる。左側のノードの特徴量xiに対して、wiによる重み付き和を計算している。

MLPは、この構造に中間層を導入し、中間層に隠れユニット(hidden units)を配置する。特徴量入力はまず隠れユニットに対して重み付き線形和を計算し、その後に隠れユニットの出力の重み付き線形和を出力とする。

特徴量xi (i = 0~n)の隠れユニットhj (j = 0~m)に対する重みをwij、切片をbjとすると、hjへの入力となる重み付き線形和は以下のようになる。

(2)    \begin{equation*} h_j = \sum_{i=0}^n (b_j + w_{ij} x_i) \end{equation*}

また、隠れユニットhjの出力\hat{y}に対する重みをvj、切片をcとすると、出力への重み付き線形和は以下のようになる。

(3)    \begin{equation*} \hat{y} = c + \sum_{j=0}^m v_j h_j = c + \sum_{j=0}^m v_j \sum_{i=0}^n (b_{ij} + w_{0ij} x_i) \end{equation*}

これは結局、xiに対する重み付き線形和となる。たとえば特徴量0~3、隠れユニット0~2の場合は以下のとおり。

(4)    \begin{align*} \hat{y} &= c + v_0 h_0 + v_1 h_1 + v_2 h_2 \\ &= c + v_0 (b_0 + w_{00} x_0 + w_{10} x_1 + w_{20} x_2) \\ &\phantom{=c+}v_1 (b_1 + w_{01} x_0 + w_{11} x_1 + w_{21} x_2) \\ &\phantom{=c+}v_2 (b_2 + w_{02} x_0 + w_{12} x_1 + w_{22} x_2) \\ &= c + v_0 b_0 + v_1 b_1 + v_2 b_2 \\ &\phantom{=}+ (v_0 w_{00} + v_1 w_{01} + v_2 w_{02}) x_0 \\ &\phantom{=}+ (v_0 w_{10} + v_1 w_{11} + v_2 w_{12}) x_1 \\ &\phantom{=}+ (v_0 w_{20} + v_1 w_{21} + v_2 w_{22}) x_2 \end{align*}

非線形活性化関数

単純な線形和をいくら多層化しても、結果は特徴量の線形和にしかならない。そこで、隠れユニットの入力に対して非線形関数を適用して出力とし、複雑・柔軟な動作を可能とする。

このような関数を活性化関数(activation function)あるいは伝達関数(transfer function)と呼び、様々な種類がある。書籍では、このうちReLU (Rectified linear unit)とtanh (hyperbolic tangent)が紹介されている。ReLUは以下の式で表され、負の値が採用しえない(計算過程での)ノイズであるような場合に好都合らしい。tanhは(−∞, +∞)の入力に対して(−1, +1)の出力を返す。

(5)    \begin{equation*} h(x) = \max (0, x) = \left\{ \begin{align} x \quad (x \ge 0) \\ 0 \quad(x < 0) \end{aling} \right. \end{equation*}

ニューラルネットワークのチューニング

two moonsデータでの確認

two moonsデータセットに対してMLPを適用する。隠れユニットの数はデフォルトの100としている。

隠れユニット数と決定境界

隠れユニット数を10とした場合の結果は数の通り。先のユニット数100の場合に比べて、決定境界が折れ線になっている。

隠れユニット数の指定はhidden_layer_sizes=[10]のように指定する。複数の隠れ層を表現するためにリストとなっていて、1層の場合でも1要素のリストとする。また、収束計算回数の最大値がデフォルトのmax_iter=200では収束しきれないという警告が出るため、この値を1000に引き上げている。

結果は書籍のものと少し異なっていて、上方の▲の点より上に鋭く境界が突き抜けている。いくつかパラメーターを変えてみたが、書籍のような境界の形状は再現できなかった。

隠れユニットの数を[1]~[4]と変化させたときの決定境界の様子は以下の通りで、ユニット数が増えるにしたがって決定境界を構成する線分の数が増えている。

隠れ層の数

隠れユニット数が10程度でも、隠れ層の数を増やすと決定境界は滑らかになる。

隠れ層が2層の場合に、各層のユニット数を変化させたときの決定境界の変化を見てみる。1層目のユニット数が大まかな形に影響し、2層目のユニットは決定境界の滑らかさに影響していると言えそうだ。

活性化関数tanh

デフォルトでは非線形活性化関数にReLUが用いられるが、これをtanhとすることで下図のように決定境界が滑らかになる。デフォルトのまま(右)だと書籍のような形にならないが、最大計算回数max_iter=115と制限すると大体似たような形になる。

ここでも2つの隠れ層のユニット数を変化させてみると、第1層が大まかな形、第2層が細部の表現に影響していると言えそうだ。

正則化

MLPClassifierはL2正則化が可能で、パラメーターalphaに大きな値を設定すると正則化を強くできる。デフォルトはalpha=0.0001で正則化が効いていない状態。

以下に、2層のユニット数[10, 10]と[100, 100]に対してalphaをデフォルトの0.0001から1.0まで変化させたときの様子を示す。ただしmax_iter=500として未収束の警告が出ないようにしている。alphaを大きくするにしたがって正則化が強くなり、決定境界がシンプルなものになっていく様子が見られる。

ランダムな重みづけの影響

ニューラルネットワークでは、学習開始前に各重み係数がランダムに割り当てられるため、その初期値がモデルに影響を与える。以下は同じパラメーター設定に対してrandom_stateのみを変化させたもので、決定境界の形が異なっている。

データの前処理等

MLPのBreast cancerデータセットへの適用例で、データの標準化や重み係数の分布の確認等を行っている。

今後の課題

  • 総数・ユニット数と計算量の関係
  • パラメーター調整のパターン
  • scikit-learn以外のライブラリー(keras, lasagna, tensor-flow)
  • GPUのサポート
  • 収束計算のアルゴリズム(lbfgs, adam, sgd)

 

Breast cancerデータセット – MLP

精度不足

書籍”Pythonではじめる機械学習”の”2.3.8.2 ニューラルネットワークのチューニング”で、scikit-learnのMLPBreast Cancerデータセットに適用した例が示されている。

デフォルトのパラメーターのままで実行した例は以下の通りだが、訓練スコアとテストスコアは、書籍ではそれぞれ0.92と0.90となっていて、下の結果とは異なる。

データの標準化

これに対して書籍では、特徴量データを標準化(standardize)する例を示している。同じコードで計算したのが以下の結果で、この場合は書籍と同じ値となっている。

ここで未収束の警告が出て、これも書籍と同じ。

書籍に倣ってmax_iter=1000とすると正常終了するが、今度は書籍の結果(0.995/0.965)と異なる結果となってしまう。

random_stateが違う?

よく見ると、最初のコードではMPLClassifierのパラメーターでrandom_state=42とそれ以前と同じ値を使っているが、その後の2つの計算ではrandom_state=0と異なる値を使っている。MLPの解説で重みの初期値に影響するrandom_stateの値によってモデルが異なることを注意しているにもかかわらず、このパラメーターを変更している理由がよくわからない(値を42に揃えてみたところ、ドラスティックな変化はなかったが)。

重み係数の分布

最後に、書籍に掲載されているimshowを使った重み係数の分布を再現してみる。imshowは画像ファイルを表示するほかに、配列を与えてその内容に応じたイメージを表示できる。colorbarは扱いがややこしそうで、Axesに対して適当なメソッドが見当たらなかったので、ここではpyplotに直接描画している。

 

Python/pyplot – 決定境界の描き方

決定境界の描き方として以前ループを使った泥臭い方法を考えたが、meshgridを使って数行で書けることを知ったのでまとめ。

結論としては以下の19~25行目の8行で、以下の手順で決定境界を書いている。

  1. 2つの特徴量の全領域をカバーする値をnumpy.linspace()で生成
  2. numpy.meshgrid()で2次元のグリッドに変換
  3. 各特徴量のメッシュグリッドを1次元に変形し、縦2列の配列化
  4. prediction()メソッドでその配列の各座標に対応する予測値を計算(結果は1次元配列)
  5. 結果の配列をmeshgridと同じ形状の2次元配列に変形
  6. contour/contourf()で決定境界を描画

具体的な変数の変形状況を要素数4の少ない例で示すと以下の通り。

まず、2つの特徴量の範囲の数列を生成する。

それらの数列を、meshgridで2次元配列に変形する。

予測モデルに与える変数は各特徴量を列とする2次元配列とする必要があるので、まず上の2次元配列をそれぞれ1次元に変形。この変形では、2次元配列の各行を連ねていった1行の配列を列ベクトルにした形になる。

次に2つの列ベクトルを横方向に並べて、総計算データ数×特徴量数(2)の2次元配列とする。

この配列の各座標に対する予測値を、predict()メソッドで予測。この結果は、1次元化されたf0やf1と同じく、2次元のmeshgridの各行を横に連ねたものになっている。

この結果を、meshgrid化されたf0(またはf1)と同じ形に変形。これで予測結果がf0×f1平面の各座標に対応した予測値の2次元配列となっている。

この結果を使い、contour()/contourf()で決定境界あるいは決定領域を描画。

ここでlevelsの指定は以下のようにしている。

まずcontour()の場合、ドキュメンテーションには“If an int n, use n data intervals; i.e. draw n+1 contour lines. The level heights are automatically chosen.”と書かれているので、levels=0と指定すると0+1本の線が描かれると考えたが以下のような警告が出て線の位置がずれた。

そこでlevels=[0.5]と2つのクラス値0と1の間をとると適切に表示される。

なおcontourf()のときは、levels=1として2つの領域が描かれる。

 

 

Breast Cancerデータセット – SVM

過学習?

書籍”Pythonではじめる機械学習”の”2.3.7.4 SVMパラメータの調整”の最後の方で、scikit-learnのSVMをBreast Cancerデータセットに適用した例が示されている(カーネル法によるSVMについてはこちらにまとめている)。

ここで、原典ではSVC()の引数を指定せずデフォルトのままとしているが、そのまま実行すると以下のような結果になった。

scikit-learnのドキュメンテーションによると、

Kernel coefficient for ‘rbf’, ‘poly’ and ‘sigmoid’.

  • if gamma='scale' (default) is passed then it uses 1 / (n_features * X.var()) as value of gamma,
  • if ‘auto’, uses 1 / n_features.

Changed in version 0.22: The default value of gamma changed from ‘auto’ to ‘scale’.

とされていて、gammaのデフォルト設定が変わったようである。新しい仕様ではデフォルトでデータのスケーリングが行われるため、どちらかといえば適合不足の状態になる。先のコードでは明示的にgamma=autoを設定し、書籍と同じ結果を得ている。

特徴量データのサイズの違い

Breast Cancerデータの30の特徴量について、各々の分布状況を箱髭図で描いてみた。縦軸の対数スケールに対してでも、各特徴量がかなりばらついており、1万倍~100万倍ほどの違いがあることがわかる。

データの前処理

データのスケールを揃えるために使われるMiniMaxScalorでは、各特徴量の訓練データを最小値と最大値でスケーリングし、0~1に納まるようにする。具体的には、特徴量ごとに最小値を引いて、最大値-最小値のレンジで除する。

この結果、訓練データの各特徴量の最小値はすべて0となり、最大値はすべて1となる。

テストデータに対してもスケーリングを行うが、ここで使う最小値とレンジは訓練データのものとし、訓練データとテストデータでスケーリングに歪がでないようにする。その結果、スケーリング後のテストデータには、最小値が0より小さい値や最大値が1より大きい値が出ている。

スケーリングされた訓練データとテストデータについてスコアを計算すると以下のようになり、先ほどの過学習の状態から適合不足の状態となった。尚この結果は、新しいSVCクラスにおいてデフォルトのgamma='auto'を指定したときの傾向と似ていて、若干の適合不足となっている。

パラメーター調整

上記の適合不足の結果に対して、パラメーターを変化させてみる。デフォルトのC=1からC=1000としてみると、訓練スコア、テストスコアとも改善された。テストスコアはランダムフォレスト決定木の勾配ブースティングの結果と同じになっている。

さらにいくつかのCとgammaで試してみると、特にスコアがいいのは以下のケースだった。なおgamma=1, 10の場合、C=100, 1000, 10000に対して訓練スコアが1.000、テストスコアが0.95程度で全て過学習となった。

C gamma 訓練スコア テストスコア
1000 auto 0.988 0.972
1000 0.01 0.986 0.979
100 0.1 0.986 0.972

 

SVM~カーネル法

概要

書籍”Pythonではじめる機械学習”の2.3.7 カーネル法を用いた”サポートベクタマシン”の写経

線形特徴量の非線形化

線形モデルでは分離不可能なデータ

以下は、scikit-learnのmake_blobs()により生成した2特徴量、2クラス分類のデータに線形サポートベクターマシンを適用した例。このとき、決定境界は以下のように得られる。

(1)    \begin{gather*} b + w_0 f_0 + w_1 f_1 = 0\\ b \approx -0.2817,\; w_0 \approx 0.1261,\; w_1 \approx -0.0918 \end{gather*}

決定境界より上側では多項式の値は負となり、下側では正となるが、この境界は明らかに2つのクラスを分割していない。このように単純な例でも、線形モデルでは的確なクラス分類はできない。

以下のコードでは、原典と以下が異なっている。

  • 収束しないという警告を受けて、LinearSVCmax_iterをデフォルトの1000より大きな値としている

非線形特徴量の追加

ここで、特徴量1の2乗を新たな特徴量として加える。この場合、3つの特徴量に対して3次元空間内に各点が位置し、それぞれがクラス0/1に属している。新たな特徴量の追加によって、その軸の方向に各点が立ち上がり、真ん中の三角形の点群と両側の丸印の点群が平面でうまく分割できそうである。

このデータセットに対して、線形SVMを適用し、決定境界を描いたのが以下の画像。特徴量が2つの場合の決定境界は直線だったが、特徴が3つになると決定境界は平面となる。予想通り、単純な平面で2つのクラスが分けられている。この決定境界の式は以下のようになる。

(2)    \begin{gather*} b + w_0 f_0 + w_1 f_1 + w_2 {f_1}^2 = 0\\ b \approx 1.1734,\; w_0 \approx 0.1301,\; w_1 \approx -0.2203,\; w_2 = -0.0597 \end{gather*}

この平面に対して上側(f12が小さい側)では多項式の値は正となり、その反対側では負となる。

以下のコードは、原典と以下が異なっている。

  • 収束しないという警告を受けて、LinearSVCmax_iterをデフォルトの1000より大きな値としている
  • Axes3Dの生成の仕方を最新のバージョンに合ったものとしている
    • 原典ではFigureオブジェクトを生成し、それをビューに関する引数とともにAxes3Dコンストラクターに渡している
    • 本コードでは、subplotsの引数でprojectionを指定してFigureAxes3Dを同時に生成し、veiw_init()でビューに関する引数を指定

元の特徴量に対する決定境界

上の例では特徴量は3つだが、最後の特徴量は2つ目の特徴量f1から計算される量であり、実質は2つの特徴量が決まれば決定境界が決まる。3次元空間内の平面の決定境界を以下のように書きなおすと、このことが確認できる。

(3)    \begin{align*} f_0 &= \frac{-b - w_1 f_1 - w_2 {f_1}^2}{w_0} \\ &= -9.02 +1.69 f_1 + 0.46 {f_1}^2 \\ &= 0.46(f_1 +1.83)^2 - 10.56 \end{align*}

これを2つの特徴量に対する決定境界として描画したのが以下の図で、境界が2次関数となっているのが確認できる。

SVMそのものは線形の決定境界しか得られないが、非線形化した特徴量を追加することによって、より複雑な決定境界とすることができる。

カーネルトリック

概要

上記の例では特徴量の1つを2次として新たな特徴量とした。特徴量の非線形化としては、このように特徴量の累乗とするほか、異なる特徴量同士の積を交互作用として導入することが考えられる。ただし、特徴量の数が多くなった時に、それらの全ての組み合わせに対する積を考えると、計算量が膨れ上がる。カーネルトリック(kernel trick)とは、拡張された特徴量空間でのデータ間の距離を、実際の拡張計算をせずに行う方法らしい。

受け売りをそのまま書いておくと、SVMで広く用いられているカーネルトリックのマッピング方法は以下の2つとのこと。

  • 多項式カーネル(polynomial kernel):もとの特徴量の特定の次数までの全ての多項式を計算
  • 放射既定関数(radial basis function: RBF)カーネルとも呼ばれるガウシアンカーネル:直感的には全次数の全ての多項式を考えるが、次数が高くなるにつれて特徴量の重要性を小さくする

以下はforgeデータセットに対して、カーネルトリックを用いたSVCを適用した例。直線はLinearSVCによる決定境界で、曲線はガウシアンカーネル(RBF)によるSVCの決定境界で、カーネル関数は以下のような形。

(4)    \begin{equation*} k_{\rm rbf}(x_1, x_2) = \exp \left( -\gamma || x_1 - x_2 ||^2 \right) \end{equation*}

scikit-learnのSVCの引数で、kernel='rbf'C=10gamma=0.1と指定している。

線形モデルの決定境界が直線なのに対して、カーネルトリックによる決定境界は、非線形化した特徴量を導入していることから曲線となっている。

scikit-learnのSVCクラスには、サポートベクターに関する以下のパラメーターがある。

  • support_:データセットにおけるサポートベクターのインデックス(1次元配列)
  • support_vector_:サポートベクターの配列(2次元配列)

38~44行目で、これらのパラメーターを使ってサポートベクターを強調表示している。

パラメータ調整

SVCモデルでパラメーターCとgammaの値を変化させたときの決定境界は以下の通り。

gammaはガウシアンカーネルの直径(σ2に相当)の逆数で、この値が小さいと直径が大きくなり、より多くの点を近いと判断するようになる。左の方はgammaが小さく広域のデータをまとめようとするため、決定境界は大まかとなり、右の方はgammaが大きく近いもの同士をまとめようとする傾向となる。

Cは正則化の強さの逆数で、上の方ほどCの値が小さく正則化が強く効くため、決定境界はよりまっすぐとなり、下の方ほど正則化が弱く個々のデータの影響を受ける。

Breast cancerデータへの適用例

Breast cancerデータへの適用例で、特徴量データの大きさやレンジが大きくばらついていること、特徴量データをそのまま使った場合に過学習となること、特徴量データに前処理を施して正規化(normalize)した場合に精度が向上することを示している。

SVMの特徴

SVMの特徴量を受け売りのまままとめておく。

  • データにわずかな特徴量しかない場合も複雑な決定境界を生成可能(低次元でも高次元でもうまく機能)
  • サンプルの個数が大きくなるとうまく機能しない(10万サンプルくらいになると、実行時間やメモリ使用量の面で難しくなる
  • 注意深いデータの前処理とパラメーター調整が必要
  • 検証が難しい(予測に対する理由を理解することが難しい)
  • RBFの場合、Cやgammaを大きくするとより複雑なモデルになる(2つのパラメーターは強く相関するため、同時に調整する必要がある)

今後の課題~覚え書き

カーネル関数

(5)    \begin{equation*} K(\boldsymbol{x}_1, \boldsymbol{x}_2) = \sum \phi(\boldsymbol{x}_1) \phi(\boldsymbol{x}_2) \end{equation*}

多項式カーネル

(6)    \begin{equation*} K(\boldsymbol{x}_1, \boldsymbol{x}_2) = (\boldsymbol{x}_1 \cdot \boldsymbol{x}_2 + 1 )^d \end{equation*}

ガウシアンカーネル

(7)    \begin{equation*} K(\boldsymbol{x}_1, \boldsymbol{x}_2) = \exp \left(- \frac{||(\boldsymbol{x}_1 - \boldsymbol{x}_2 ||^2}{2\sigma^2} \right) \end{equation*}

 

SVMの定式化

SVMの定式化

SVMのクラス分類の条件

2つの特徴量を持つデータが2つのクラスに分かれているとする。ここで下図のように、1つの直線によって、2つのクラスを完全に分離できるとする。

このとき、直線lによって分割したとして、以下の符号によってクラスを分離する。

(1)    \begin{equation*} \left\{ \begin{align} a x_1 + b x_2 + c > 0 &\rightarrow \rm{Class1} \\ a x_1 + b x_2 + c < 0 & \rightarrow \rm{Class2} \end{align} \right. \end{equation*}

ここでラベル変数tiを導入する。tiはデータiがClass1/2のいずれに属するかを示す変数で、Class1ならti > 0、Class2ならti < 0と定義する。

(2)    \begin{equation*} \left\{ \begin{array}{lll} t_i = 1 & x_i \in \rm{Class1} & (a x_{i1} + b x_{i2} + c > 0) \\ t_i = -1 & x_i \in \rm{Class2} & (a x_{i1} + b x_{i2} + c < 0) \\ \end{array} \right. \end{equation*}

このラベル変数を用いて、クラスの条件式は以下のように統一される。

(3)    \begin{equation*} t_i (a x_{i1} + b x_{i2} + c) > 0 \end{equation*}

SVMにおいては、すべてのデータについてこの式が満足されるようにa, b, cを決定する。これらはすべてa, b, cに対する制約条件だが、どのようにこれらの値を求めるべきか、その目的関数が必要になる。SVMでは、これをマージン最大化により行う。

マージン最大化

ある直線l1によって、下図のようにデータセットがClass1/2に分類できるとする。このときl1に対してClass1/2の最も直線に近いデータを”サポートベクター”と呼ぶ。また、これらのサポートベクターに対応するl1と平行な直線間の距離を”マージン”と呼ぶ。

ところで、l1とは異なる別の直線l2を選ぶと、異なるサポートベクターに対してより大きなマージンを得ることができる。SVMでは、式(3)のもとでこのマージンを最大化するような直線lを探すこととなる。

直線lに対するサポートベクターの対を(x+, x)とすると、それぞれからlへの距離dは以下のように表現される。

(4)    \begin{equation*} d = \frac{|a x^+_1 + b x^+_2 + c|}{a^2 + b^2} = \frac{|a x^-_1 + b x^-_2 + c|}{a^2 + b^2} \end{equation*}

ここで直線lはマージンの端にある平行な2つの直線の中央にあることから、上式の分子は同じ値となる。この値でdを除したものを改めて\tilde{d}と置くと、dの最大化問題は1/\tilde{d}=\sqrt{a^2+b^2}の最小化問題となる。これに式(3)の制約条件を加味して、問題は以下の制約条件付き最小化問題となる。

(5)    \begin{align*} \min a^2 + b^2 \quad {\rm s.t.} \; t_i (a x_{i1} + b x_{i2} + c) > 0 \; (i=1~n) \end{align*}

今後の課題

  • ここから先の定式化
  • ソフトマージンの導出