見出し画像

データ分析奮闘記(Day9)[Titanic]その8〜過学習の改善と特徴量エンジニアリング〜

こんばんは!今年は暦が良くて、年末年始の休日が多いですね!時間があるので、自己研鑽として僕はデータ分析のお勉強頑張ります!(あと情報発信もね!)

前回まで

データ整形を行い、カテゴリ変数のダミー変数化などをしたあと、ベースモデルとしてランダムフォレストを実施しました。結果は以下の通り。

スクリーンショット 2019-12-28 17.25.56

訓練データの精度が95%な一方、テストデータが79%です。訓練データではかなりフィットしているように見えて、他のデータだとうまく予測できていない、つまり「過学習」状態になっています。

やりたいこと

今回やりたいことは2つ!
1、過学習を改善する!(できるかわからんけど)
2、説明変数をいじって精度をあげる(特徴量エンジニアリング)

1、過学習を改善する!(できるかわからんけど)

過学習を解決するにはハイパーパラメータを修正することが必要らしい。
、、、ハイパーパラメータってなに??と思った僕はググって見ました。

ハイパーパラメータとは、機械学習モデルを構築する際に、あらかじめ指定する必要のあるパラメータのことらしいです。これはデータをどれだけインプットしたとしても、更新されないものなので、これを調整する必要があるとのこと。今回実行したランダムフォレストのコードをもう一度見てみます。

#ランダムフォレストのインスタンス生成
rfc = RandomForestClassifier(max_depth=10, min_samples_leaf=1, n_estimators=100, n_jobs=-1, random_state=42)
#擬似訓練データでモデル構築
rfc.fit(X_train, y_train)

今回の場合のハイパーパラメータは以下ですね!
・max_depth=10,
・min_samples_leaf=1,
・n_estimators=100,
・n_jobs=-1,
・random_state=42

例えばmax_depthを大きくすれば、分岐が増えるので、与えられたデータに対しては詳細に分類できるようになり、精度は上がりますが、別のデータを与えた時にはうまくフィットしない可能性が高まるので、過学習が起こりやすくなりますね。ただ、それを小さくすると、そもそもうまく分類できないので、精度が落ちることになります(未学習というらしい)

どうやってそれを見つければいいのー??と思ってググってみたところ、ここにもいくつか方法があるようですね。たとえば
・グリッドサーチ
・ベイズ最適化
などがあるとのことです。まずはグリッドサーチを使ってみます!

グリッドサーチとは、パラメータの組み合わせを色々試して、一番過学習と未学習のバランスが取れているパラメータを探すことのようですね、つまり勝手に「しらみつぶし作戦」と命名しましょう!

#パラメータの候補を設定
param_grid = {'max_depth': [3, 5, 7],
             'min_samples_leaf': [1, 2, 4]}
#max_depthとmin_samples_leafの組み合わせをループで回し、それぞれの結果を出力する
for max_depth in param_grid['max_depth']:
   for min_samples_leaf in param_grid['min_samples_leaf']:
       rfc_grid = RandomForestClassifier(max_depth=max_depth, min_samples_leaf=min_samples_leaf, 
                                           n_estimators=100, n_jobs=-1, random_state=42)
       rfc_grid.fit(X_train, y_train)
       print('max_depth: {}, min_samples_leaf: {}'.format(max_depth, min_samples_leaf))
       print('    Train Score: {}, Test Score: {}'.format(round(rfc_grid.score(X_train, y_train), 3),
                                                          round(rfc_grid.score(X_valid, y_valid), 3)))

えええ、面倒くさすぎないか、、、?と思ったんですが、必死に調べたら

ありました。

#グリッドサーチクロスバリデーションのインポート
from sklearn.model_selection import train_test_split, GridSearchCV
#グリッドサーチクロスバリデーションの利用
rfc_gs = GridSearchCV(RandomForestClassifier(n_estimators=100, n_jobs=-1, random_state=42), param_grid, cv=5)
rfc_gs.fit(X, y)

print('Best Parameters: {}'.format(rfc_gs.best_params_))
print('CV Score: {}'.format(round(rfc_gs.best_score_, 3)))

このクロスバリデーションってなに??ってなったので調べて見たところ、これまでは訓練データを擬似訓練データと擬似テストデータに分割するとき、自分でどれくらいの割合で分けるのかを決めていましたが、このクロスバリデーションはN分割して、それをN回別の場所でN分割しつつ分析を繰り返し、その平均値をモデルのスコアとするというものです!なんだか良い感じ。

これをやると、以下のようにベストパラメータが得られたので、これでいきましょう!

スクリーンショット 2019-12-28 21.50.23

2、説明変数をいじって精度をあげる(特徴量エンジニアリング)

次は特徴量エンジニアリングやります!(一言で分析といっても事前作業含めてやることいっぱいあって心折れそうですが、頑張ります!)

特徴量エンジニアリングとは、元々存在している説明変数に手を加えることで、効果的な説明変数(特徴量)を作り出そうという試みだそうです!

方向性は2つあって、1つはどんな問題だろうと適用できる汎用的な方法(例えば前回やったカテゴリカルデータのエンコーディングなど)と、もう1つは問題設定の特徴に応じて行う特徴量エンジニアリング。(例えばTitanicだと船が難破して、その生存者を推定するわけだから、、、と考える方法)後者はEDAなどから手がかりを得ていくらしい。

この特徴量エンジニアリングで大きな差がつくらしいので、詳しくもっと勉強しなきゃなあ。。。!

前回のEDAの中で、 
・SibSpは同乗していた兄弟姉妹・配偶者の数
・Parchは同乗していた親・子供の数
だとわかっているので、SibSpの値とParchの値の和をとって「同乗していた家族の人数」という新しい変数を加えて、特徴量としてみたいと思います!

#dfとdf_testをコピーして新しいDataFrameオブジェクトを生成する
df_fe = df.copy()
df_fe_test = df_test.copy()
#SibSpの値とParchの値の和をとって、Family(同乗していた家族の人数)という新しい変数を加える
df_fe['Family'] = df_fe['SibSp'] + df_fe['Parch']
df_fe_test['Family'] = df_fe_test['SibSp'] + df_fe_test['Parch']

このデータでランダムフォレストを学習させてみたところ、、、!

#説明変数と目的変数に分割する
X_list = ['Pclass', 'Sex', 'Age', 'SibSp', 'Parch', 'Fare', 'Embarked_C', 'Embarked_Q', 'Embarked_S', 'Family']
X_test_list = ['Pclass', 'Sex', 'Age', 'SibSp', 'Parch', 'Fare','Embarked_C', 'Embarked_Q', 'Embarked_S', 'Family']
X = df_fe[X_list]
y = df_fe[['Survived']]
X_test = df_fe_test[X_test_list]
#擬似訓練データと擬似テストデータを生成
X_train, X_valid, y_train, y_valid = train_test_split(X, y, test_size=0.3, random_state=42)
#ランダムフォレストのインスタンス生成(グリッドサーチクロスバリデーションの結果から、パラメータを調整)
rfc = RandomForestClassifier(max_depth=7, min_samples_leaf=1, n_estimators=100, n_jobs=-1, random_state=42)
#擬似訓練データでモデル構築
rfc.fit(X_train, y_train)
#過学習が起きていないか確認
print('Train Score: {}'.format(round(rfc.score(X_train, y_train), 3)))
print(' Test Score: {}'.format(round(rfc.score(X_valid, y_valid), 3)))

スクリーンショット 2019-12-29 14.14.58

元々のFamilyを含まなかった場合には0.827くらいだったので精度が落ちましたー。ということで非採用!(ちーん)

一旦次に進もうっと!笑

今回はここまでにします!次回はアンサンブリング(色々なモデルを組み合わせる)をやってみます!うまくいくといいなあ


Twitterもやっています!発信数ももっと増やして有意義なものにしていこうと思うので、ぜひ応援お願いします!


この記事が気に入ったらサポートをしてみませんか?