kaggleに挑戦その5 過学習、学習不足の可視化
学習曲線
データセットに対してモデルが複雑すぎる場合、過学習する傾向があり汎化性能が落ちてしまう。
トレーニングデータをさらに集めると、過学習は抑えることができるが、データをさらに集めることはコストが非常に高い。
また、実はこれ以上データを集めても無駄ということもある。 そんなとき、学習曲線を使うことでデータを集めることに価値があるか、検証できる。
有名な過学習、学習不足の絵ですね。
過学習と、学習不足が丁度良いバランスの場合、
トレーニングデータセットから作成したモデルの精度と、
そのデータを(例えば)K分割交差検証で評価したモデルの精度が同程度になります。
逆にすでに同程度の場合、これ以上サンプル数を増やしても精度は上がらないですね。
精度を上げたい場合は、モデル自体を変えた方が良いことになります。
今回作成したモデルはどうなんでしょう、、、
import matplotlib.pyplot as plt from sklearn.learning_curve import learning_curve train_sizes,train_scores,valid_scores = learning_curve(estimator=forest, X=X_train, y=y_train, train_sizes=np.linspace(0.1,1.0,10), cv=10, n_jobs=1) train_mean = np.mean(train_scores,axis = 1) test_mean = np.mean(valid_scores,axis=1) plt.plot(train_sizes,train_mean) plt.plot(train_sizes,test_mean) plt.show()
過学習してますねー。
データ数増やしていけば、過学習を抑えることができるってことがわかります。
実際は、データ数を増やすことはできないので、
モデルの複雑さを抑えて汎化性能を上げる必要があるかと思います。
kaggleに挑戦その4 k分割交差検証(k-fold cross-validation)及びグリッドサーチ
k分割交差検証
前回までで、ランダムフォレストでモデル予測を実施しました。
今回は、k分割交差検証、グリッドサーチを用いてモデル性能の評価及びハイパーパラメータの修正を行います。
これにより、バイアス高、バリアンス高のバランスをとることができます。
ランダムフォレストで、モデル化してみましたが、これは汎化性能がどの程度あるのか、
k分割交差検証で調べます。
k分割交差検証とは、トレーニングデータをランダムにk個分割して、
そのうちk-1個をモデル予想に使い、残りの1個でそのモデルを予想し、
この手順をk回繰り返すことで、そのモデルの平均性能を評価するものです。
kをどの程度の値にすべきかは、もとのデータセットに依存します。
kの値でどう変わるかというと、、、
・kが大きいと、各トレーニングサブセットの値が似てきて、評価のバリアンス高(性能評価の結果が、テストデータに依存した形となりやすい。)となりやすい。
・kが小さいと、各トレーニングサブセットの値が違いすぎて、バイアス高となりモデルの正確な評価ができない(毎回違う評価結果となる)。
ということです。
今回は、k=5として実施していきます。
from sklearn.model_selection import KFold from sklearn import metrics from sklearn.metrics import accuracy_score K = 5 kf = KFold(n_splits=K, shuffle=True, random_state=1 ) score_train_tmp = 0 score_test_tmp = 0 X = x_train y = np.array(titanic_df_main_random_forest["Survived"]) for train_index,test_index in kf.split(X): X_train, X_test = X[train_index], X[test_index] y_train, y_test = y[train_index], y[test_index] # トレーニングサブセットでモデル構築 forest.fit(X_train, y_train) # トレーニングサブセットでの予測値 pred_train = forest.predict(X_train) # トレーニングサブセットのaccuracy auccuracy = accuracy_score(pred_train, y_train) #トレーニングサブセットのaccuracyを足していく score_train_tmp+=auccuracy #テストサブセットの予測値 pred_test = forest.predict(X_test) #テストサブセットのaccuracy auccuracy = accuracy_score(pred_test, y_test) #テストサブセットのaccuracyを足していく score_test_tmp+=auccuracy
これで、ランダムフォレストモデル(n_estimators = 100)の平均性能を評価することができます。
続いて、nn_estimators を変えたら、モデル性能はどのように変わるか、グリッドサーチ及びk分割交差検証を実施していきます。
グリッドサーチ
事前に決めるべきパラメータのことをハイパーパラメーターといいます。
今回は、2分木の数(n_estimators)がそれに当たります。
これはいくつにすれば良いのか???ということで今回はグリッドサーチを実施します。
グリッドサーチとは、パラメータを網羅的に変えながら、モデルの良し悪しを評価(例えばk分割交差検証)していき、
何が一番良いのか決める方法のことです。
scikit-learnには、もともとグリッドサーチ用のクラスが準備されているのでそれを使います。
(for文でループ!とかやらなくてもOK)
from sklearn.model_selection import GridSearchCV param_grid = {"n_estimators":[10,20,30,40,50,60,70,80,90,100]} tree_grid = GridSearchCV(estimator=forest, param_grid = param_grid, scoring="accuracy", #metrics cv = K, #cross-validation n_jobs =-1) #number of core tree_grid.fit(X,y) #fit tree_grid_best = tree_grid.best_estimator_ #best estimator print("Best Model Parameter: ",tree_grid.best_params_) print("Best Model Score : ",tree_grid.best_score_)
実行した結果が以下です。n_estimatorsがいくつだと性能が良いかわかりますね。
Best Model Parameter: {'n_estimators': 90} Best Model Score : 0.805836139169
kaggleに挑戦その3 ランダムフォレストを可視化
決定木の可視化
決定木の利点としては、意味解釈可能性があります。
ランダムフォレストで作成された決定木を可視化して、その意味を解釈しようと思います。
scikit-learnには、決定木を.dotファイルとしてエクスポートする機能があります。
その前に、.dotファイルを使うために、自分のパソコンにGraphVizをインストールします。
pip install pydotplus brew install Graphviz
ランダムフォレストで作成された決定木を.dotファイルにエクスポートします。
#ランダムフォレストを作成 from sklearn.ensemble import RandomForestClassifier forest = RandomForestClassifier(criterion="entropy",n_estimators = 100) forest = forest.fit(x_train,y_train) #dotファイルを出力 from sklearn import tree for i,val in enumerate(forest.estimators_): tree.export_graphviz(forest.estimators_[i], out_file='tree_%d.dot'%i)
ファイルが出力されたら、コマンドライン上で.dotファイルを.pngファイルに変換します。
例として、最初の決定木をpngファイルにしてみます。
>dot -Tpng tree_0.dot -o tree.png
実際のファイルは以下です。どういうロジックでTrue or Falseが分岐しているか、一目瞭然ですね。
一番大事な変数は?
scikit-learnのfeature_importancesを使うことにより、重要は変数がわかります。
重要変数は各変数での分岐の際に、情報エントロピーの差分を各決定木で平均させて、求められます。
titanic_df_main_random_forest_df = titanic_df_main_random_forest.drop("Survived",axis = 1).columns importances_df = forest.feature_importances_ random_forest_df = pd.DataFrame({"変数":titanic_df_main_random_forest_df,"corr":importances_df}) random_forest_df= random_forest_df.sort_values(by = "corr",ascending=False) sns.barplot(x = "変数",y = "corr",data = random_forest_df)
一番重要な変数は、Ageのようですね。
kaggleに挑戦 その2 ランダムフォレスト
ランダムフォレストって何?
ランダムフォレストは、複数の決定木を組み合わせて、
各決定木における予想結果の多数決により、結果を得ます。
アルゴリズムとしては、
1.ランダムにn個のデータを抽出、
2.1で取得したデータから決定木を成長、
3.1と2を複数回繰り返す、
4.予想結果を多数決する、
タイタニック号のデータに適用する。
まずは、簡単にするため、
説明変数を"Age"、"Pclass"、"Sex"に絞ろうと思います。
目的変数"Survived"と相関があるか調べます。
#必要なモジュールインポート import pandas as pd import matplotlib.pyplot as plt import seaborn as sns %matplotlib inline #データの読み込み titanic_df = pd.read_csv("train.csv") titanic_df.info() sns.factorplot("Pclass","Survived",data = titanic_df_main, hue = "Sex") sns.lmplot('Age','Survived',data=titanic_df)
データの可視化には、seabornというライブラリを使ってます。
factorplot した歳の、データ点は平均値、誤差棒は標準誤差分です。
男性、3等客室は亡くなった方が多いことがわかります。
近似直線を引くと、右肩下がりになっています。
年齢が高い方が、亡くなった方が多いことがわかります。
"Age"、"Pclass"、"Sex"は説明変数として使えそうですね。
続いて、解析する前にデータを整形します。
"Age"には欠損値が多く、そのままではデータ解析できません。
欠損値を中央値で置き換えることにします。
titanic_df_main["Age"].fillna(titanic_df_main["Age"].median(),inplace = True)
続いて、"Sex"をダミー変数に変えます。
dummy = pd.get_dummies(data = titanic_df_main["Sex"],drop_first=True) titanic_df_main = titanic_df_main.combine_first(dummy)
ダミー変数は、オプションとして、「drop_fisrt = True 」とすると第1項を落としてくれます。
説明変数としてダミー変数を使う場合、多重共線性から上記を実施する必要があります。
続いてscikit-learnのランダムフォレストを利用して、実装していきます。
ランダムフォレストを実装
ランダムフォレストの代表的パラメーターとしては、以下3種類です。
1.ランダムに選ぶデータの数
2.説明変数の数(木の深さ)
3.決定木の数
1と2に関しては、デフォルト値を採用します。
尚、1のデフォルト値はデータ数になります。
データは、復元抽出で選ばれるので、すべてのデータが使われるわけではありません。
2のデフォルト値は、説明変数のルートをとったものです。
3はとりあえずn=100とします。
titanic_df_main_random_forest = titanic_df_main.drop(["PassengerId","Sex"],axis = 1 ) train_data = titanic_df_main_random_forest.values x_train = train_data[:,[0,1,3]] y_train = train_data[:,2] from sklearn.ensemble import RandomForestClassifier forest = RandomForestClassifier(criterion = "entropy", n_estimators = 100)
これでランダムフォレストを実装できました。
決定木の数に関しては、当然数が多いと性能はよくなりますが、計算コストが増えます。
これはグリッドサーチにより決めれば良いと思います。
次はグリッドサーチとランダムフォレストの結果について書こうと思います。
kaggleに挑戦 その1 決定木を使う
はじめに
機会学習のスキルを身に付けたく、kaggleに挑戦中です。
タイタニック号のデータを、ランダムフォレストでモデル化しました。
今回は、ランダムフォレストの基本となる決定木について書こうと思います。
簡単に自己紹介すると、僕は材料系メーカーのエンジニアです。
学生時代に統計分析はそこそこ経験していますが、
機会学習は全くの素人でした。
社会人になって、機会学習に興味を持ち、
色々と勉強していくうちにkaggleを知った次第です。
タイタニック号のデータ
初心者向けのコンペとして、
「Titanic:Machine Learning From Disaster 」
があります。
タイタニック号の事故について、
どのような要因(年齢、性別、階級等)が乗客の生死を分けたか?
という物を分析するものです。
初心者は初心者らしく、初心者向けのコンペに挑戦します。
データのダウンロード
まずはkaggleのサイトから、以下二つのデータをダウンロードします。
kaggleのアカウントを持っていない人は、先に作っておきましょう。
上記二つのデータは、それぞれ訓練用(train)データと評価用(test)データです。
訓練用データで、モデルを作成し、評価用データでそのモデルの妥当性を検証します。
「訓練用データでどのようなモデルを作成するか」
これがデータ分析者のキモですね。
モデルを作るときは汎化性能を意識する必要があります。
このあたりが、学生の時にやっていた統計分析と違うのかなーと思っています。
(あくまで個人的な意見です。統計分析という単語も個人的経験を指しています。)
今回はscikit-learnのランダムフォレストを使ってモデルを作成していきます。
ランダムフォレストと決定木分類器
ランダムフォレストってなんぞや?となりますが、
その前に決定木分類器の説明をします。
決定木分類器とは、いくつか質問に基づき決断を下すという分類器。
言葉で書いてもよくわからないと思うので、こちらのHPから下記図を引用させて頂きました。
上図では、性別、年齢等に関する問を組み合わせて生存可否の決断をします。
親nodeから子nodeに分岐するTree構造になっているので、決定木と呼ばれています。
上記図の年齢に関する質問において、10歳を基準に区切っています。
このとき15歳ではダメなのか?5歳ではダメなのか?という疑問を持ちます。
質問の目的は生存、死亡を分岐することなので、一番正確に分岐できる年齢を設定したいですよね。
このモチベーションを定量化するために、「情報エントロピー」「情報利得」を用います。
上記質問で例えると、
10歳以下の人がすべて生存して、それ以外は亡くなったとしたら、情報エントロピーは0となります。
0に近いと正確(理想的)に分岐できていて、1に近いとその質問では全く分岐できていないことになります。
ここで情報利得とは、親nodeと子nodeの情報エントロピーの差です。
子nodeの情報エントロピーが0に近い程(その質問で正確に分岐できている程)、情報利得は大きくなります。
つまり、情報利得が最大になるように質問を設定すれば良いのです。
決定木は、質問が多い程、過学習に陥りやすくなります。
そこでランダムフォレストは、上記対策がなされています。
次回はランダムフォレストについて書こうと思います。
pycharm インストール
学生の時は、Fortran,Cとか使ってた。
流行りだからpythonのスキルを上げたい。
IDEはvisual studioを使ってたけど、
pycharmに乗り換えて(python限定だけど)みる。
visual studioは重い気がして好きになれない・・・。
windows とmac、両方で使えるって理由のみでこれにpycharmを選びました。
インストールしてみる。
インタプリタは、anacondaを使用。
下記サイトからDLできます。
https://www.continuum.io/downloads
ver3.6をインストール。
pycharmを起動すると下記の画面。
「Create New Project」を選択。
次の画面で、Interpreterを選びます。
今回はanacondaを選択。
以上