実装関連事項

各種プログラミング言語の基本的な書き方やソフトウェア等の使用方法について.

決定木 (decision tree) は最も簡単な論理体系からなる機械学習法のひとつであり,入力ベクトルを学習することにより,その過程で自然に重要な特徴量の抽出および順位付けをすることができる手法である.決定木は多くの機械学習法において問題となる過学習を起こすことがあり得るが,これを集団学習を用いることで起こし難くしたのがランダムフォレスト (random forest) である.決定木をたくさん組み合わせることによって構築される予測器なので名称にフォレストが冠されている.ランダムフォレストでは,データをブートストラップサンプリングにより複数個サンプリングし,それらに対して決定木モデルを作成,それらの結果を多数決や平均ととる等の作業によって統合することで最終的な予測を行う.このような手法を集団学習 (アンサンブル学習) の中でも特に,バギングと呼ぶ.

学習させるデータの構造

以下のようなデータセットを考える.このデータセットでは,60次元 (アトリビュート) のインプットベクトルに対して0または1のスカラーの値が対応している.インプットベクトルの各アトリビュートはコンマ (,) によって分割されており,インプットベクトルとターゲットベクトル (スカラー) はタブ (\t) によって分割されている.

0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0	0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1	1
0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0	0
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0	1
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0	1
0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1	1
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0	1
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0	1
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0	0
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0	0
.
.
.

データセット

このような構造のインスタンスが400個含まれるデータをラーニングデータセット (classification_01_learning.txt),411個含まれるデータをテストデータセット (classification_01_test.txt) とする.以下からダウンロードできる.

classification_01_learning.txt

classification_01_test.txt

学習による予測器の生成

このラーニングデータセットを学習して予測器を作るには以下のようにする.これは,Scikit-learnによる決定木の学習方法とほぼ同じように書くことができる.決定木における,DecisionTreeClassifier() の代わりに RandomForestClassifier() を用いれば良い.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sklearn
from sklearn.ensemble import RandomForestClassifier
import numpy as np
np.random.seed(0)

def main():
	# 1. reading data
	xtrain,ttrain=[],[]
	fin=open("classification_01_learning.txt","r")
	for i,line in enumerate(fin):
		line=line.rstrip()
		if line:
			tmp=line.split("\t")
			tmpx=tmp[0].split(",")
			tmpx=[float(j) for j in tmpx]
			tmpt=int(tmp[1])
			xtrain.append(tmpx)
			ttrain.append(tmpt)
	fin.close()
	xtrain=np.asarray(xtrain,dtype=np.float32)
	ttrain=np.asarray(ttrain,dtype=np.int32)
	
	# 2. learning, cross-validation
	diparameter={"n_estimators":[i for i in range(10,50,10)],"criterion":["gini","entropy"],"max_depth":[i for i in range(1,6,1)],"random_state":[123],}
	licv=sklearn.model_selection.GridSearchCV(RandomForestClassifier(),param_grid=diparameter,cv=5,n_jobs=5)
	licv.fit(xtrain,ttrain)
	predictor=licv.best_estimator_
	sklearn.externals.joblib.dump(predictor,"predictor_rf.pkl",compress=True)
	
	# 3. evaluating the performance of the predictor
	liprediction=predictor.predict(xtrain)
	table=sklearn.metrics.confusion_matrix(ttrain,liprediction)
	tn,fp,fn,tp=table[0][0],table[0][1],table[1][0],table[1][1]
	print("TPR\t{0:.3f}".format(tp/(tp+fn)))
	print("SPC\t{0:.3f}".format(tn/(tn+fp)))
	print("PPV\t{0:.3f}".format(tp/(tp+fp)))
	print("ACC\t{0:.3f}".format((tp+tn)/(tp+fp+fn+tn)))
	print("MCC\t{0:.3f}".format((tp*tn-fp*fn)/((tp+fp)*(tp+fn)*(tn+fp)*(tn+fn))**(1/2)))
	print("F1\t{0:.3f}".format((2*tp)/(2*tp+fp+fn)))
	
	# 4. printing parameters of the predictor
	print(sorted(predictor.get_params(True).items()))
	
	# 5. printing importances of the predictor
	print(predictor.feature_importances_)
	
if __name__ == '__main__':
	main()

以上のプログラムにおいて学習は以下の5ステップからなる.

  1. データの読み込み.
  2. クロスバリデーションによる学習.
  3. 構築した予測器のラーニングデータセットにおける性能の評価結果の出力.
  4. 予測器のパラメーターの出力.
  5. フィーチャーの重要度の出力.

以下に抜粋した25行目からの部分では評価基準 (ジニ係数またはエントロピー) と決定木の深さ (1から5まで1刻み) とバギングに用いる決定木モデルの構築個数について5フォールドのクロスバリデーションで予測器を複数個作る.n_estimators というのがランダムフォレストで用いる木の数を設定するためのパラメーターであり,これは当然,決定木のための関数 DecisionTreeClassifier には存在しないパラメーターである.構築された予測器候補の中で最も良い性能の予測器を最終的な予測器とするのが,29行目の書き方で,構築した予測器を保存するための書き方が30行目.クロスバリデーションを用いたグリッドサーチは過学習を抑制するための重要な方法.特別な事情がない限り絶対に行うべき.ここで探索すべきパラメーターはページ最下部の表にあるものとなる.また,グリッドサーチにおいて使用するスレッドの数は n_jobs= に指定する.

	# 2. learning, cross-validation
	diparameter={"n_estimators":[i for i in range(10,50,10)],"criterion":["gini","entropy"],"max_depth":[i for i in range(1,6,1)],"random_state":[123],}
	licv=sklearn.model_selection.GridSearchCV(RandomForestClassifier(),param_grid=diparameter,cv=5,n_jobs=5)
	licv.fit(xtrain,ttrain)
	predictor=licv.best_estimator_
	sklearn.externals.joblib.dump(predictor,"predictor_rf.pkl",compress=True)

以上のプログラムを実行した結果は以下のようになる.この場合,正確度は0.855,MCC は0.718,F1スコアは0.865である.また,最終的な予測器において利用された決定木の数は20個,評価基準はジニ係数であり,決定木の深さは最大で4であることがわかる.変数の重要度はその下に示されれている値で,これの次元は入力ベクトルのアトリビュートの個数と一致する.この場合,60個の特徴量のうち,16番目の特徴量において値が0.1867でありその他と比べて最も値が高いため,重要な特徴量であると結論する.また,このプログラムを実行して出力されるファイルは予測器を再現するためのファイル predictor_rf.pkl である.

TPR     0.930
SPC     0.780
PPV     0.809
ACC     0.855
MCC     0.718
F1      0.865
[('bootstrap', True), ('class_weight', None), ('criterion', 'gini'), ('max_depth', 4), ('max_features', 'auto'), ('max_leaf_nodes', None), ('min_samples_leaf', 1), ('min_samples_split', 2), ('min_weight_fraction_leaf', 0.0), ('n_estimators', 20), ('n_jobs', 1), ('oob_score', False), ('random_state', 123), ('verbose', 0), ('warm_start', False)]
[  9.67877809e-02   0.00000000e+00   0.00000000e+00   0.00000000e+00
   0.00000000e+00   0.00000000e+00   0.00000000e+00   9.14845057e-02
   0.00000000e+00   0.00000000e+00   0.00000000e+00   0.00000000e+00
   0.00000000e+00   0.00000000e+00   1.17514613e-01   1.86619941e-01
   6.37839770e-03   0.00000000e+00   0.00000000e+00   9.07825909e-02
   3.73178101e-03   2.67772012e-02   3.57157989e-03   7.21612371e-02
   2.37955820e-05   4.36046509e-05   8.90056875e-02   8.74170409e-04
   3.81113734e-03   6.97794477e-04   2.61518370e-02   6.40931890e-03
   5.88009018e-03   2.19524686e-03   4.66903379e-02   1.19552496e-02
   8.63621345e-03   7.66083296e-03   1.24476174e-03   9.10492481e-04
   2.78773385e-03   5.18021531e-03   1.24358279e-04   1.51104381e-03
   7.50534359e-03   1.88922539e-03   1.75138451e-03   0.00000000e+00
   3.13814699e-03   5.79279066e-04   1.66175933e-03   3.13285244e-03
   4.18037780e-03   3.98815365e-03   0.00000000e+00   2.66529094e-02
   1.07391526e-02   5.79753667e-03   8.67067110e-03   2.70965626e-03]

構築した予測器を用いたテスト

以上で学習した予測器を読み込んで,学習データとは完全に独立なデータセット上で構築した予測器のテストを行うには以下のように書く.構築した予測器は,25行目のように書くことで読み込むことができる.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sklearn
import numpy as np
np.random.seed(0)

def main():
	# 1. reading data
	xtest,ttest=[],[]
	fin=open("classification_01_test.txt","r")
	for i,line in enumerate(fin):
		line=line.rstrip()
		if line:
			tmp=line.split("\t")
			tmpx=tmp[0].split(",")
			tmpx=[float(j) for j in tmpx]
			tmpt=int(tmp[1])
			xtest.append(tmpx)
			ttest.append(tmpt)
	fin.close()
	xtest=np.asarray(xtest,dtype=np.float32)
	ttest=np.asarray(ttest,dtype=np.int32)
	
	# 2. reading predictor
	predictor=sklearn.externals.joblib.load("predictor_rf.pkl")
	
	# 3. evaluating the performance of the predictor on the test dataset
	liprediction=predictor.predict(xtest)
	table=sklearn.metrics.confusion_matrix(ttest,liprediction)
	tn,fp,fn,tp=table[0][0],table[0][1],table[1][0],table[1][1]
	print("TPR\t{0:.3f}".format(tp/(tp+fn)))
	print("SPC\t{0:.3f}".format(tn/(tn+fp)))
	print("PPV\t{0:.3f}".format(tp/(tp+fp)))
	print("ACC\t{0:.3f}".format((tp+tn)/(tp+fp+fn+tn)))
	print("MCC\t{0:.3f}".format((tp*tn-fp*fn)/((tp+fp)*(tp+fn)*(tn+fp)*(tn+fn))**(1/2)))
	print("F1\t{0:.3f}".format((2*tp)/(2*tp+fp+fn)))

if __name__ == '__main__':
	main()

これを実行した結果は以下のようになる.独立なデータセットでも良い性能が出ている.これらの評価尺度の中でより頑健なものは,ACC,MCC,F1スコアである.

TPR     0.886
SPC     0.840
PPV     0.854
ACC     0.864
MCC     0.728
F1      0.870

学習パラメーター一覧

ランダムフォレストでコントロールすべきパラメーターには以下のものがある.これを上述のグリッドサーチの部分で変化させることで最も良い予測器を構築する.

パラメーター詳細
n_estimators整数を指定.デフォルトの値は10.バギングに用いる決定木の個数を指定.
criterion文字列を指定.デフォルトは gini.その他に entropy を指定できる.決定木はこの指標を基準にデータを分割する.
max_features整数,小数,文字列または None を指定.デフォルトは None.最適な分割をするために考慮するフィーチャーの数を指定.整数を指定した場合,その個数,小数の場合全フィーチャーに対する割合個,auto を指定した場合,フィーチャー数のルート個,log2 を指定した場合,log2(フィーチャー数) 個.基本は None を使うべき.
max_depth整数または None を指定.決定木の深さの最大値を指定.過学習を避けるためにはこれを調節するのが最も重要.
min_samples_split整数または小数を指定.デフォルトは None.ノードを分割するために必要な最小サンプルサイズ.整数を指定した場合,その数,小数を指定した場合,全サンプルサイズに対する割合個.
min_samples_leaf整数か小数で指定.デフォルトは1.葉を構成するのに必要な最小限のサンプルの数.整数を指定した場合,その数.小数を指定した場合,元々のサンプルサイズに占める割合と解釈される.
min_weight_fraction_leaf数値で指定.デフォルトは0.葉における重みの総和の最小加重率を指定.何のためのパラメーターなのか不明瞭.
max_leaf_nodes整数または None を指定.デフォルトは None.生成される決定木における最大の葉の数を指定.
min_impurity_split数値を指定.デフォルトは 1e-7.決定木の成長の早期停止の閾値.不純度がこの値より大きいとき,ノードは分割される.
bootstrapTrue または False を指定.決定木モデルの構築の際にブートストラップサンプリングを行うかどうかの指定.
oob_score学習における正確度の計算に OOB (out-of-bag),すなわち,各ブートストラップサンプリングでサンプリングされなかったサンプルを利用するかどうかの指定.デフォルトは False.
n_jobs整数を指定.デフォルトは1.フィットおよび予測の際に用いるスレッドの数を指定.-1 を指定した場合は計算機に載っている全スレッド分確保される.
random_state乱数のタネの指定.何かしらの整数を指定すべき.
verbose整数を指定.モデル構築の過程のメッセージを出すかどうか.デフォルトは0.
warm_startTrue または False を指定.デフォルトは False.ここに True を設定すると既にフィットしたモデルに学習を追加することができる.
class_weightディクショナリ,ディクショナリのリスト,balanced または None を指定.デフォルトは None.ディクショナリを指定する場合,{class_label:weight} の形式で,各クラスに重みを設定できる.指定しない場合は全てのクラスに1が設定されている.balanced を指定すると,y の値により n_samples / (n_classes * np.bincount(y)) を計算することで自動的に重みを調整する.
Hatena Google+