Kaggleを制するにはKernelを制すべし。って誰かが言っていたので、適当なKernelを動かしてみることにしました。
の続きです。
前回までのあらすじ。
データの前処理(Imputation)を行った後、
様々な側面からデータを可視化した結果、性別(Sex)、階級 (PClass)、年齢 (Age)などが生存率に影響を与え得ることを明らかにしたのでした。
今回は、
- 特徴エンジニアリング
- モデルの作成と予測
- 各モデルパフォーマンスの評価
- GridSearchCVを用いたハイパーパラメータの調整
- 予測結果のSubmission
という一連の流れを見ていきます。
目指せKaggleマスター!
特徴エンジニアリング
特徴エンジニアリングとは、
データ内の既存の生の特徴から、関連する特徴を作成し、学習アルゴリズムの予測力を高めようとします。 データ サイエンスにおける特徴エンジニアリング - Team Data Science Process | Microsoft Docs
データの変換や追加により、新たなカラム(変数)を作成することで機械学習による予測に役立てることを指します。
ではでは、早速データを見てみましょう。
training.head() # PassengerId Survived Pclass Name Sex Age SibSp Parch Fare Embarked #0 1 0 3 Braund, Mr. Owen Harris male 22.0 1 0 7.2500 S #1 2 1 1 Cumings, Mrs. John Bradley (Florence Briggs Th... female 38.0 1 0 71.2833 C #2 3 1 3 Heikkinen, Miss. Laina female 26.0 0 0 7.9250 S #3 4 1 1 Futrelle, Mrs. Jacques Heath (Lily May Peel) female 35.0 1 0 53.1000 S #4 5 0 3 Allen, Mr. William Henry male 35.0 0 0 8.0500 S
One-Hot-Encodingへの変換
male
やS
、C
といったカテゴリー変数が存在するのがわかります。
カテゴリー変数はデータとして扱いづらいため、
まず、カテゴリー変数をOne-Hot-Encodingに変換します。
One-hot-Endcodingについては以下を参照。
参考:Why One-Hot Encode Data in Machine Learning?
training.loc[training["Sex"] == "male", "Sex"] = 0 training.loc[training["Sex"] == "female", "Sex"] = 1 training.loc[training["Embarked"] == "S", "Embarked"] = 0 training.loc[training["Embarked"] == "C", "Embarked"] = 1 training.loc[training["Embarked"] == "Q", "Embarked"] = 2 testing.loc[testing["Sex"] == "male", "Sex"] = 0 testing.loc[testing["Sex"] == "female", "Sex"] = 1 testing.loc[testing["Embarked"] == "S", "Embarked"] = 0 testing.loc[testing["Embarked"] == "C", "Embarked"] = 1 testing.loc[testing["Embarked"] == "Q", "Embarked"] = 2
One-Hot-Encodingに変換されていることを確認します。
sample
メソッドを使うと、指定した行数ランダムに取得することができます。
training.sample(5) # PassengerId Survived Pclass Name Sex Age SibSp Parch Fare Embarked #229 230 0 3 Lefebre, Miss. Mathilde 1 28.0 3 1 25.4667 0 #488 489 0 3 Somerton, Mr. Francis William 0 30.0 0 0 8.0500 0 #202 203 0 3 Johanson, Mr. Jakob Alfred 0 34.0 0 0 6.4958 0 #425 426 0 3 Wiseman, Mr. Phillippe 0 28.0 0 0 7.2500 0 #724 725 1 1 Chambers, Mr. Norman Campbell 0 27.0 1 0 53.1000 0
新たなカラム(変数)の追加
次に、FamSize
という変数名で新たにカラムを追加します。
ここで、
FamSize
(家族の数) = SibSp
(兄弟の数) + Parch
(両親、子供の数) + 1 (自分)
となります。
家族の数をふつーに求めているだけですね。
training["FamSize"] = training["SibSp"] + training["Parch"] + 1 testing["FamSize"] = testing["SibSp"] + testing["Parch"] + 1
さらに、IsAlone
という変数名で、独身か、否かのデータを追加します。
独身か否か、というデータも、生存者率に影響を与える可能性がありますからね。
training["IsAlone"] = training.FamSize.apply(lambda x: 1 if x == 1 else 0) testing["IsAlone"] = testing.FamSize.apply(lambda x: 1 if x == 1 else 0)
次に、Name
カラムから敬称部分を抜き出し、新たなデータとして追加します。
ここでは拡張正規表現を用いて抜き出すことにします。
([A-Za-z]+)\.
で、任意の英字文に続く.
までの文字列を抜き出すことができます。
for name in training["Name"]: training["Title"] = training["Name"].str.extract("([A-Za-z]+)\.",expand=True) for name in testing["Name"]: testing["Title"] = testing["Name"].str.extract("([A-Za-z]+)\.",expand=True)
では、どのような敬称が使われているのか、確認しましょう。
set()
関数で重複を除いて出力します。
titles = set(training["Title"]) #making it a set gets rid of all duplicates set(titles) #{'Capt', 'Col', 'Countess', 'Don', 'Dr', 'Jonkheer', 'Lady', 'Major', 'Master', 'Miss', 'Mlle', 'Mme', 'Mr', 'Mrs', 'Ms', 'Rev', 'Sir'}
結構多いですね。。
pandas.DataFrame
モジュールでは、リストとカラム名を与えることで、データフレームが作成できます。
データフレームを作成することでより見やすく敬称のリストを見てみましょう。
titles = list(titles) title_dataframe = pd.DataFrame({ "Titles" : titles, "Frequency" : frequency_titles }) print(title_dataframe) # Titles Frequency #0 Rev 6 #1 Miss 182 #2 Mr 517 #3 Countess 1 #4 Master 40 #5 Mlle 2 #6 Lady 1 #7 Dr 7 #8 Col 2 #9 Don 1 #10 Mrs 125 #11 Capt 1 #12 Major 2 #13 Mme 1 #14 Ms 1 #15 Jonkheer 1 #16 Sir 1
データ数が1個とか2個とかしかないものは、予測結果に悪影響を与え得るため、まとめてOthers
というカラムに格納しましょう。
特定のデータを置き換える際には、keyを置き換え元、valueを置き換え先とした辞書を作成し (title_replacements
)、replace
メソッドを使用します。
title_replacements = {"Mlle": "Other", "Major": "Other", "Col": "Other", "Sir": "Other", "Don": "Other", "Mme": "Other", "Jonkheer": "Other", "Lady": "Other", "Capt": "Other", "Countess": "Other", "Ms": "Other", "Dona": "Other"} training.replace({"Title": title_replacements}, inplace=True) testing.replace({"Title": title_replacements}, inplace=True)
そのままでは扱いづらいので、その他の変数と同様にOne-Hot-Encodingに変換しましょう。
training.loc[training["Title"] == "Miss", "Title"] = 0 training.loc[training["Title"] == "Mr", "Title"] = 1 training.loc[training["Title"] == "Mrs", "Title"] = 2 training.loc[training["Title"] == "Master", "Title"] = 3 training.loc[training["Title"] == "Dr", "Title"] = 4 training.loc[training["Title"] == "Rev", "Title"] = 5 training.loc[training["Title"] == "Other", "Title"] = 6 testing.loc[testing["Title"] == "Miss", "Title"] = 0 testing.loc[testing["Title"] == "Mr", "Title"] = 1 testing.loc[testing["Title"] == "Mrs", "Title"] = 2 testing.loc[testing["Title"] == "Master", "Title"] = 3 testing.loc[testing["Title"] == "Dr", "Title"] = 4 testing.loc[testing["Title"] == "Rev", "Title"] = 5 testing.loc[testing["Title"] == "Other", "Title"] = 6
データを確認してみましょう。
training.sample(5) # PassengerId Survived Pclass Name Sex Age SibSp Parch Fare Embarked Title FamSize IsAlone #208 209 1 3 Carr, Miss. Helen "Ellen" 1 16.0 0 0 7.7500 2 0 1 1 #629 630 0 3 O'Connell, Mr. Patrick D 0 28.0 0 0 7.7333 2 1 1 1 #768 769 0 3 Moran, Mr. Daniel J 0 28.0 1 0 24.1500 2 1 2 0 #889 890 1 1 Behr, Mr. Karl Howell 0 26.0 0 0 30.0000 1 1 1 1 #615 616 1 2 Herman, Miss. Alice 1 24.0 1 2 65.0000 0 0 4 0
ここまで終わると上記のようにName
以外が数値データとなります。
モデルの作成と予測
データのフォーマッティングが終わったら、
いよいよscikit-learnを使って、生存者率の予測を行っていきます。
以下では
- SVC
- Linear SVC
- Random Forest
- Logistic Regression
- K Nearest Neighbors
- Gaussian Naive Bayes
- Decision Tree
- XGBClassifier
の8つのモデルについて評価を行います。
ライブラリの読み込み
まずは、モデルの構築と予測に使用するライブラリを読み込みます。
from sklearn.svm import SVC, LinearSVC from sklearn.ensemble import RandomForestClassifier from sklearn.linear_model import LogisticRegression from sklearn.neighbors import KNeighborsClassifier from sklearn.naive_bayes import GaussianNB from sklearn.tree import DecisionTreeClassifier
さらに、モデルの評価に使用するmake_scorere
関数とaccuracy_score
関数を読み込みます。
from sklearn.metrics import make_scorer, accuracy_score
ハイパーパラメータを調整する際には、GridSearchを用いた交差検証(Cross-validation)を行います。
これは指定したパラメータの全ての組み合わせに対して学習を行い,もっとも良い精度を示したパラメータを採用する方法です。
Cross-validationに使用するGridSearchCV
関数を読み込みます。
from sklearn.model_selection import GridSearchCV
データの定義
次に、使用するデータを定義します。
生存者率の推定には"Pclass", "Sex", "Age", "Embarked", "Fare", "FamSize", "IsAlone", "Title"の8つの変数を使用します。
ここで、y_test
は定義していません。これから予測するのがy_test
だからです。
features = ["Pclass", "Sex", "Age", "Embarked", "Fare", "FamSize", "IsAlone", "Title"] X_train = training[features] #define training features set y_train = training["Survived"] #define training label set X_test = testing[features] #define testing features set
ハイパーパラメータを調整する際には、検証用の第3のデータセットを用意することが一般的です。
X_valid
、y_valid
という変数名で、検証用データを定義します。
from sklearn.model_selection import train_test_split #to create validation data set X_training, X_valid, y_training, y_valid = train_test_split(X_train, y_train, test_size=0.2, random_state=0) #X_valid and y_valid are the validation sets
SVC Modelの構築
scikit-learnでは、モデルの種類によらず、以下のようにモデルの構築、予測、スコアの算出まで行うことができます。
{モデル名}.fit
:モデルの構築
{モデル名}.predict
:モデルを使用した予測
accuracy_score
:スコアの算出
svc_clf = SVC() svc_clf.fit(X_training, y_training) pred_svc = svc_clf.predict(X_valid) acc_svc = accuracy_score(y_valid, pred_svc) print(acc_svc) #0.7150837988826816
LinearSVC Modelを使用した推定
与えるモデル名を置き換えるだけでその実装が完了できます。
SVC ModelからLinearSVC Modelに変更する際の例を見てみましょう。
linsvc_clf = LinearSVC() linsvc_clf.fit(X_training, y_training) pred_linsvc = linsvc_clf.predict(X_valid) acc_linsvc = accuracy_score(y_valid, pred_linsvc) print(acc_linsvc) #0.7541899441340782
ここで変わったのは最初に与えている関数のみ( + 定義する変数名)です。
以下、同様にその他のモデルについてスコアの算出まで行いましょう。
RandomForest Model
rf_clf = RandomForestClassifier() rf_clf.fit(X_training, y_training) pred_rf = rf_clf.predict(X_valid) acc_rf = accuracy_score(y_valid, pred_rf) print(acc_rf) #0.8156424581005587
LogisiticRegression Model
logreg_clf = LogisticRegression() logreg_clf.fit(X_training, y_training) pred_logreg = logreg_clf.predict(X_valid) acc_logreg = accuracy_score(y_valid, pred_logreg) print(acc_logreg) #0.8044692737430168
kNN
knn_clf = KNeighborsClassifier() knn_clf.fit(X_training, y_training) pred_knn = knn_clf.predict(X_valid) acc_knn = accuracy_score(y_valid, pred_knn) print(acc_knn) #0.7430167597765364
GaussianNB Model
gnb_clf = GaussianNB() gnb_clf.fit(X_training, y_training) pred_gnb = gnb_clf.predict(X_valid) acc_gnb = accuracy_score(y_valid, pred_gnb) print(acc_gnb) #0.7821229050279329
DecisionTree Model
dt_clf = DecisionTreeClassifier() dt_clf.fit(X_training, y_training) pred_dt = dt_clf.predict(X_valid) acc_dt = accuracy_score(y_valid, pred_dt) print(acc_dt) #0.776536312849162
XGBoost Model
from xgboost import XGBClassifier xg_clf = XGBClassifier(objective="binary:logistic", n_estimators=10, seed=123) xg_clf.fit(X_training, y_training) pred_xg = xg_clf.predict(X_valid) acc_xg = accuracy_score(y_valid, pred_xg) print(acc_xg) #0.7988826815642458
各モデルパフォーマンスの評価
モデル名と、その精度をデータフレームに格納し、精度順にソートしましょう。
model_performance = pd.DataFrame({ "Model": ["SVC", "Linear SVC", "Random Forest", "Logistic Regression", "K Nearest Neighbors", "Gaussian Naive Bayes", "Decision Tree", "XGBClassifier"], "Accuracy": [acc_svc, acc_linsvc, acc_rf, acc_logreg, acc_knn, acc_gnb, acc_dt, acc_xg] }) model_performance.sort_values(by="Accuracy", ascending=False) # Model Accuracy #2 Random Forest 0.815642 #3 Logistic Regression 0.804469 #7 XGBClassifier 0.798883 #5 Gaussian Naive Bayes 0.782123 #6 Decision Tree 0.776536 #1 Linear SVC 0.754190 #4 K Nearest Neighbors 0.743017 #0 SVC 0.715084
Random Forestモデルの精度が最も良いようです。テストデータにはこのモデルを使用して推定を行いましょう。
GridSearchCVを用いたハイパーパラメータの調整
rf_clf = RandomForestClassifier() parameters = {"n_estimators": [4, 5, 6, 7, 8, 9, 10, 15], "criterion": ["gini", "entropy"], "max_features": ["auto", "sqrt", "log2"], "max_depth": [2, 3, 5, 10], "min_samples_split": [2, 3, 5, 10], "min_samples_leaf": [1, 5, 8, 10] } grid_cv = GridSearchCV(rf_clf, parameters, scoring = make_scorer(accuracy_score)) grid_cv = grid_cv.fit(X_train, y_train) print("Our optimized Random Forest model is:") grid_cv.best_estimator_ #RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini', # max_depth=10, max_features='log2', max_leaf_nodes=None, # min_impurity_decrease=0.0, min_impurity_split=None, # min_samples_leaf=5, min_samples_split=10, # min_weight_fraction_leaf=0.0, n_estimators=8, n_jobs=1, # oob_score=False, random_state=None, verbose=0, # warm_start=False)
調整したハイパーパラメータを使ったモデルを使用して、訓練データによる学習を行います。
rf_clf = grid_cv.best_estimator_ rf_clf.fit(X_train, y_train) #RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini', # max_depth=10, max_features='log2', max_leaf_nodes=None, # min_impurity_decrease=0.0, min_impurity_split=None, # min_samples_leaf=5, min_samples_split=10, # min_weight_fraction_leaf=0.0, n_estimators=7, n_jobs=1, # oob_score=False, random_state=None, verbose=0, # warm_start=False)
予測結果のSubmission
はい、お疲れさまでした。
予測が完了したら、KaggleのCompetitionにsubmitするためのデータを作成しましょう。
pandasのデータフレームは、to_csv
メソッドでcsv形式での出力が可能です。
最後のsubmission.shape
では、submitするデータが正しい次元数になっているかを確認しています。
submission_predictions = rf_clf.predict(X_test) submission = pd.DataFrame({ "PassengerId": testing["PassengerId"], "Survived": submission_predictions }) submission.to_csv("titanic.csv", index=False) print(submission.shape) #(418, 2)
おわりに
今回はTutrial的な内容のKernelをざーっと見ていきましたが、そんな難しいことはやっていない印象でした。
一連の流れとしては、
Imputation
前処理
モデル構築とその評価
学習
推定
これらの流れの中でデータの特徴を見るためにところどころ可視化が入ってくる、といった感じでしょうか。
次回はもう少し複雑なKernelにも挑戦してみます!