前回記事、今日からはじめるPython機械学習プログラミング基礎 教師あり学習その1 に引き続き、教師あり学習です。今回は様々な決定木のモデルで分類問題を解きます。
##データセットの読み込み
Kaggleのタイタニック号から生存者のデータセットを使います。前回と異なりデータは直接ダウンロードする必要があるので注意しましょう。下記は作業ディレクトリの直下にデータを置いた場合のコードです。
#モジュールのインポート
import pandas as pd
from sklearn.model_selection import GridSearchCV
#ファイルの読み込み
train = pd.read_csv("train.csv")
test = pd.read_csv("test.csv")
train.head()
説明変数と目的変数の説明を下に記します。
変数 | 意味 | 補足 |
---|---|---|
survival | 生存 | 0 = No, 1 = Yes |
pclass | チケットクラス | 1 = 1st, 2 = 2nd, 3 = 3rd |
sex | 性別 | |
age | 年齢 | |
sibsp | 兄弟・配偶者の数 | |
parch | 両親・子供の数 | |
ticket | チケット番号 | |
fare | 運賃 | |
cabin | キャビン番号 | |
embarked | 搭乗した港 | C = Cherbourg, Q = Queenstown, S = Southampton |
色々な情報がありますが、すべてが乗客の安否を予測するのに必要ではなさそうです。そのため"PassengerId","Name","Ticket","Cabin"の四つをあらかじめ説明変数から削除します。
#不要なカラムの削除
train = train.drop(["PassengerId","Name","Ticket","Cabin"],axis=1)
test = test.drop(["PassengerId","Name","Ticket","Cabin"],axis=1)
また、データには欠損値が存在します。そこで、運賃と年齢はメジアン値、搭乗港には"Southampton"を代入することとします。
#欠損値埋め
train["Fare"] = train["Fare"].fillna(train["Fare"].median())
train["Age"] = train["Age"].fillna(train["Age"].median())
train["Embarked"] = train["Embarked"].fillna("S")
#搭乗港を番号へ
train["Embarked"] = train["Embarked"].map({"Q":0,"S":1,"C":2})
#説明変数と目的変数の分離
X_train = train.iloc[:,1:]
Y_train = train.iloc[:,0]
#学習データと検証データを分割
train_x, valid_x, train_y, valid_y = train_test_split(X_train, Y_train, test_size=0.33, random_state=0)
これで学習の準備が整いました。Kaggleは学習データからモデルを作成し、テストデータの予測値を提出するコンペティションのためテストデータの目的変数は与えられていません。そのため今回は学習データの2/3をtrain、残りの1/3をvalidとして扱い、validの正解率を比較することとします。
##機械学習のモデル
今回は
・RandomForest
・XGBoost
・LightGBM
・ロジスティック回帰
の4種類を比較してみます。
それぞれのモデルには様々なパラメータがあり、指定しなければデフォルトの値が使われます。
こちらから指定する場合にはGridSearchという手法が知られています。下記のコード内、paramsの中でそれぞれのパラメータを変化させ、最終的に最も精度が高かったものについてスコアを表示しています。
###Randam Forest
from sklearn.ensemble import RandomForestClassifier
rf = RandomForestClassifier(random_state=1)
params = {
"n_estimators":[10,20,30,40,50,60,70],
"max_depth":[2,3,4,5],
}
grid_search = GridSearchCV(rf, param_grid=params, cv=3)
grid_search.fit(train_x, train_y)
print(grid_search.best_score_) # ベストスコアを表示
print(grid_search.best_params_) # ベストスコアのパラメータを表示
print(grid_search.score(valid_x,valid_y))
0.8271812080536913
{'max_depth': 5, 'n_estimators': 30}
0.8203389830508474
###XGBoost
import xgboost as xgb
xg = xgb.XGBClassifier(random_state=1)
params = {
'n_estimators':[10,20,30,40,50,60,70],
'max_depth': [2, 3, 4, 5],
'reg_alpha': [0, 1, 10, 100],
'reg_lambda': [0, 1, 10, 100],
}
grid_search = GridSearchCV(xg, param_grid=params, cv=3)
grid_search.fit(train_x, train_y)
print(grid_search.best_score_) # ベストスコアを表示
print(grid_search.best_params_) # ベストスコアのパラメータを表示
print(grid_search.score(valid_x,valid_y))
0.8271812080536913
{'max_depth': 2, 'n_estimators': 60, 'reg_alpha': 0, 'reg_lambda': 10}
0.823728813559322
###LightGBM
import lightgbm as lgb
gbm = lgb.LGBMClassifier(random_state=1)
params = {
'n_estimators':[10,20,30,40,50,60,70],
'max_depth': [2, 3, 4, 5],
'reg_alpha': [0, 1, 10, 100],
'reg_lambda': [0, 1, 10, 100],
}
grid_search = GridSearchCV(gbm, param_grid=params, cv=3)
grid_search.fit(train_x, train_y)
print(grid_search.best_score_) # ベストスコアを表示
print(grid_search.best_params_) # ベストスコアのパラメータを表示
print(grid_search.score(valid_x,valid_y))
0.8271812080536913
{'max_depth': 3, 'n_estimators': 60, 'reg_alpha': 0, 'reg_lambda': 0}
0.8101694915254237
###ロジスティック回帰
from sklearn.linear_model import LogisticRegression
param_grid = {'C' : [0.001, 0.01, 0.1, 1, 10, 100]}
lr = LogisticRegression(solver='liblinear',random_state=1)
grid_search = GridSearchCV(lr, param_grid, cv=3)
grid_search.fit(train_x,train_y)
print(grid_search.best_score_) # ベストスコアを表示
print(grid_search.best_params_) # ベストスコアのパラメータを表示
print(grid_search.score(valid_x,valid_y))
0.8070469798657718
{'C': 1}
0.7932203389830509
それぞれの結果をまとめます。
モデル | 正解率(valid) |
---|---|
Random Forest | 0.820 |
XGBoost | 0.824 |
LightGBM | 0.811 |
ロジスティック回帰 | 0.793 |
タイタニックの問題については、少なくとも特徴量をそのまま使った場合にはXGBoostの正解率が最も高くなりました。次回はパラメータチューニングについてもう少し詳しくまとめてみたいと思います。