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

 

ndarray.min/max – 配列の最小値と最大値

ndarray.min()/max()は、配列の最小値/最大値を返すメソッド。また、ndarray.argmin()/argmax()は、最小/最大の要素のインデックスを配列で返す。

なお、numpy.amin()/amax()numpy.argmin()/argmax()もほぼ同じ動作をする。

以下、次の配列で動作を確認する。

引数に何も指定しない場合、配列の全要素の中の最小値と最大値を返す。このとき、argmin/argmaxでは、配列をreshape(-1)で1次元化したときのインデックスが返される。

引数にaxis=0を指定すると、各列ベクトルの行方向の中での最小値/最大値を返す。以下の例では、各列ごとの最小値/最大値とそれらに対する行インデックスが配列で返されている。

axis=0の0を2次元配列の引数の位置と考えると0番目の引数で、各列における行の位置を表す。これはargmin/argmaxの意味合いと符合する。

引数にaxis=1を指定すると、各行ベクトルの列方向の中での最小値/最大値を返す。以下の例では、各行ごとの最小値/最大値とそれらに対する列インデックスが配列で返されている。

axis=1の1を2次元配列の引数の位置と考えると1番目の引数で、各行における列の位置を表す。これはargmin/argmaxの意味合いと符合する。

 

pyplot – グラフ要素のフォントサイズ

グラフ全体のフォントサイズ

pyplot.rcParams()で基準のフォントサイズを変更。デフォルトはfont.size=12。以下は全体のフォントサイズを大きくした例。

個別要素のフォントサイズ

タイトル、軸ラベル、軸目盛、凡例について個別にフォントサイズを指定した例。

 

Python – 多重ループの一重化

概要

以下のような二重ループを、一重ループで実現する方法。

内包表記による方法

内包表記の中で二重ループを回し、1つのリストを生成する。

itertools.productによる方法

itertoolsライブラリーにあるproduct()は、引数のリストの各要素の直積を要素とするリストを返す。

 

numpy – r_とc_

概要

numpy.r_ / numpy.c_は配列を結合するオブジェクト。r_は縦方向に配列を結合し、c_は横方向に配列を結合する。vstack() / hstack()linspace()と似たような使い方ができるが、少し癖がある。

  • 配列と数値を混在させて結合できる
  • スライスでステップ数やか分割数を指定して数列をつくれる
  • vstack()hstack()の代わりに使える

vstack()hstack()と同じように使う。

r_について

numpy.r_で2次元配列に1行だけ追加するとき、1次元配列のままだ”次元が異なる”とエラー。素直にvstack()を使った方がよい。

r_のデフォルトで1次元配列同士を結合すると、単に横方向に結合される。配列と要素が混在していてもok。文字列の配列も結合できるが、文字列要素が混在するとエラーになる。

スライスを使って数列を生成。

3つ目の引数に'j'をつけてnumpy.linspace()と同様の動作。このときは終了値が含まれる。

c_について

numpy.c_で2次元配列にその行数と同じ要素数の1次元配列を結合すると、列ベクトルとみなされて1列追加される。hstack()が1次元配列を列ベクトル化する必要があるのに比べると手軽。

さらに要素数が同じ1次元配列同士を結合すると、それらが列ベクトルとみなされて結合される。

空の配列に対して順次列ベクトルを追加する場合には、empty(n, 0, dtype=type)を準備する。

 

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*}

今後の課題

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

 

 

点と直線の距離~媒介変数による愚直な解法

Pと直線lの距離を、媒介変数を通して愚直に求める。具体的には点Pから直線lへの垂線の足Hと点Pの距離となる。

直点をパラメーター表示し、点H(xH, yH)に対応するパラメーターをtHとする。

(1)    \begin{equation*} \left\{ \begin{align} x_H &= u t_H + x_0\\ y_H &= v t_H + y_0 \end{align} \right. \end{equation*}

このとき、直線に沿う方向のベクトルと直線に直角なベクトルを与えられた変数で表示すると以下の通り。

(2)    \begin{equation*} \boldsymbol{d} = \left[\begin{array}{c} u \\ v \end{array}\right] \quad , \quad \boldsymbol{h} = \left[\begin{array}{c} x_H - x_P \\ y_H - y_P \end{array}\right] \end{equation*}

これらのベクトルが直交する条件として内積をゼロとする。

(3)    \begin{gather*} \boldsymbol{d} \cdot \boldsymbol{h} = 0 \\ u(x_H - x_P) + v(y_H - y_P) = 0 \\ u(u t_H + x_0 - x_P) + v(v t_H + y_0 - y_P) = 0 \\ (u^2 + v^2)t_H + ux_0 + vy_0 - ux_P - vy_P = 0 \end{gather*}

これから点Hに対する直線のパラメーターが得られる。

(4)    \begin{equation*} t_H = \frac{u(x_P - x_0) + v(y_P - y_0)}{u^2 + v^2} \end{equation*}

このパラメーターを使ってHの座標を表示すると以下の通り。

(5)    \begin{equation*} \left\{ \begin{align} x_H &= u t_H + x_0 = \frac{u(u x_P + v y_P) + v(v x_0 - u y_0)}{u^2 + v^2} \\ y_H &= v t_H + y_0 = \frac{v(u x_P + v y_P) - u(v x_0 - u y_0)}{u^2 + v^2} \\ \end{align} \right. \end{equation*}

Pと直線lの距離はベクトルhの大きさとなる。

(6)    \begin{equation*} |\boldsymbol{h}|^2 = (x_H - x_P)^2 + (y_H- y_P)^2 \end{equation*}

以下、それぞれの項を計算していく。

(7)    \begin{equation*} \left\{ \begin{align} x_H - x_P &= \frac{v^2(x_0 - x_P) + uv(y_P-y_0)}{u^2 + v^2}\\ y_H - y_P &= \frac{u^2(y_0 - y_P) + uv(x_P-x_0)}{u^2 + v^2}\\ \end{align} \reigh. \end{equation*}

(8)    \begin{equation*} \left\{ \begin{align} (x_H - x_P)^2 &= \frac{v^4(x_0 - x_P)^2 + u^2v^2(y_P-y_0)^2 + 2uv^3(x_0 - x_P)(y_P - y_0)}{(u^2 + v^2)^2}\\ (y_H - y_P)^2 &= \frac{u^4(y_0 - y_P)^2 + u^2v^2(x_P-x_0)^2 + 2u^3v(y_0 - y_P)(x_P - x_0)}{(u^2 + v^2)^2}\\ \end{align} \reigh. \end{equation*}

これらの結果から以下を導く。

(9)    \begin{align*} |\boldsymbol{h}|^2 &= (x_H - x_P)^2 + (y_H - y_P)^2 \\ &= \frac{ \begin{array}{l} \phantom{+}v^2(u^2+v^2)(x_0 - x_P)^2 \\ + u^2(u^2+v^2)(y_0 - y_P)^2 \\ - 2uv(u^2+v^2)(x_0 - x_P)(y_0 - y_P) \end{array} }{(u^2 + v^2)^2} \\ &= \frac{v^2(x_0 - x_P)^2 + u^2(y_0 - y_P)^2 - 2uv(x_0 - x_P)(y_0 - y_P)} {u^2 + v^2} \\ &= \frac{[v(x_0 - x_P) - u(y_0 - y_P)]^2}{u^2 + v^2} \\ &= \frac{(v x_0 - u y_0 - v x_P + v y_P)^2}{u^2 + v^2} \end{align*}

ここで直線の一般式とパラメーター表示を比較すると

(10)    \begin{gather*} \frac{x - x_0}{u} = \frac{y - y_0}{v} \\ vx - uy - v x_0 + u y_0 = 0 \\ ax + by + c = 0 \\ \therefore v = a , -u = b , -v x_0 + u y_0 = c \end{gather*}

この結果を先の式に代入して以下を得る。

(11)    \begin{gather*} |\boldsymbol{h}|^2 = \frac{(a x_P + b x_P + c)^2}{a^2 + b^2} \\ |\boldsymbol{h}| = \frac{|a x_P + b x_P + c|}{\sqrt{a^2 + b^2}} \end{gather*}

 

 

勾配ブースティング

概要

勾配ブースティング(gradient boosthing)は、ランダムフォレストと同じく複数の決定木を組み合わせてモデルを強化する手法。ランダムフォレストと異なる点は、最初から複数の決定木を使うのではなく、1つずつ順番に決定木を増やしていく。その際に追加される決定木はそれぞれ深さ1~5くらいの浅い木(弱学習機:weak learner)で、直前の適合不足を補うように学習する。

勾配ブースティングの主なパラメーターは弱学習機の数(n_estimators)と学習率(learning_rate)で、学習率を大きくすると個々の弱学習機の補正を強化しモデルは複雑になる。

cancerデータへの適用

Pythonのscikit-learnにあるGradienBoostingClassifierをbreast_cancerデータに適用する例。”Pythonではじめる機械学習”の”2.3.6.2 勾配ブースティング回帰木”掲載のコードに沿って確認するが、バージョンの違いのためか、結果が異なる。いくつかのデフォルトのパラメーターを明示的に設定/変更してみたが、書籍に掲載されている結果には至っていない。

最初に試したのが以下のコード。ここでテストスコアが書籍にある0.958にならない。min_samples_split=5とすると書籍と同じ結果になるが、以降の特徴量重要度やlearning_rateの変更結果は再現されない。

過剰適合に対してmax_depth=1と強力な枝刈りをした場合。この結果は小数点以下3桁の表示で書籍と一致している。

learning_rateをデフォルトの0.1から0.01に変更した場合の結果も書籍と一致する。今回の再現結果では、デフォルト状態からテストスコアは改善されていない。

なお、事前剪定を強化したケースのグラフが、書籍と大きく異なる。横軸の値が倍ほどになっており、worst concave points、worst perimeter、mean concave pointsが重要度の大半を占めている。書籍では他の多くの特徴量も重要度がある程度高い点と異なっている。

今後確認したい点

  • 勾配ブースティングの基本的な考え方の整理
  • 簡単な事例での勾配ブースティングの挙動確認
  • 回帰への適用
  • 異なるモデルの組み合わせ

 

ランダムフォレストの概要

概要

“Pythonではじめる機械学習”のランダムフォレストの写経。

ランダムフォレストは決定木のアンサンブル法の1つ。異なる複数の決定木をランダムに発生させて平均をとることで、個々の決定木の過剰適合を打ち消すという考え方。

実行例

以下は、scikit-learnのランダムフォレストのモデルRandomForestClassifiermoonsデータセットをクラス分類した例で、ランダムに生成された5つの木とそれらを平均したランダムフォレストの決定領域を示している。

以下は実装コードで、draw_decision_field()draw_tree_boundary()はコードを省略。

上の例でn_estimatorsの数を1から9まで増やしていったときの様子を示す。徐々に細かい枝が取り払われて、境界が滑らかになっていく様子がわかる。

ランダムフォレストの考え方

概要

ランダムフォレストの大まかな手順は以下の通り。

  1. 訓練/ランダムフォレストの構築
    1. 決定木の数を指定する
    2. 決定木の数の分だけ、訓練データからランダムにデータを生成する
    3. 各決定木を学習させる
  2. 予測段階
    1. 予測したいデータの特徴量を与える
    2. その特徴量に対して各決定木がクラス分類
    3. 各決定木の分類結果の多数決でクラスを決定

ランダムフォレストの構築

決定木の数

決定木の数をRandomForestClassifiern_estimatorsパラメーターで、構築する決定木の数を指定する。

決定木に与えるデータの生成

各決定木に与えるデータセットをランダムに設定。特徴量の一部または全部を選び、各特徴量のデータをブートストラップサンプリングによってランダムに選び出す。

RandomForestClassifierでは、選ぶ特徴量の数をmax_featuresで指定し、各特徴量のデータをブートストラップサンプリングで選ぶかどうかをbootstrap(=True/False)で設定する。これらの乱数系列をrandom_stateパラメーターで指定する。

冒頭の事例の場合、特徴量は2つなので常にいずれも選び、bootstrapはデフォルトのTrue、決定木の数を3つ、乱数系列を3と指定している。

ランダムフォレストによる予測

ある特徴量セットを持つデータのクラスを予測する。与えられたデータに対して、各決定木がクラスを判定し、その結果の多数決でそのデータのクラスを決定する。

クラス分類でランダムフォレストが多数決で任意の点のクラスを決定する様子を確認する。以下は10個のmoonsデータセットに対して3つの分類木を適用した例。

訓練後のランダムフォレストにおいて、各点のクラスが多数決で決められていることが、以下の図で確認できる。

cancerデータによる確認

精度

breast_cancerデータに対してランダムフォレストを適用する。100個の決定木を準備し、他のパラメーターはデフォルトのままで実行する。

このコードの前半の実行結果は以下の通りで、訓練データに対して完全適合し、テストデータに対しても97.2%の精度を示している。

【注】決定木の数による違い

n_estimatorの数によって、テストスコアが違ってくるが、このケースでは100以上決定木の数を多くしてもテストスコアは向上しない。

  • n_estimators = 10 → 0.951
  • n_estimators = 50 → 0.965
  • n_estimators ≥ 100 → 0.972

特徴量重要度

後半の実行結果は以下のように表示される。単独の決定木による特徴量重要度と比べて全ての特徴量が0以上の重要度となっている。決定木の時に最重要であったworst radiusも重要度が高いが、worst perimeterが最も重要度が高く、worst concave pointsやconcave pointsも重要度が高い。

今後の課題

  • ランダムフォレストにおける特徴量重要度の意義
  • n_jobsの効果
  • RandomForestClassifierrandom_stateによる違い
  • max_featuresmax_depthmin_samples_leafなどの影響
    • デフォルトはmax_features=sqrt(n_features)、max_depth=None(all)、min_samples_leaf=1