KaggleのTitanicのチュートリアルをランダムフォレストで解く

machinelearningpythonscikit-learn

ランダムフォレストはデータや特徴量をランダムにサンプリングして決定木を複数生成し並列に学習するアンサンブル学習のBaggingという種類の手法。 決定木なので特徴量の影響が分かりやすく、値の大小で分岐するので標準化の必要がないが、複数の変数で表現される特徴を学習しづらい。 また、単一の決定木と比べて過学習を防ぐことができる。

KaggleのTitanicのチュートリアルをXGBoostで解く - sambaiz-net

train.csv と test.csv をKaggleからダウンロードする。 csvにはタイタニックの乗客者リストが含まれ、test.csv は生還したかを表すSurvivedが抜けている。 これを予測するのがこのコンペティションの目的。

データのうちFareやAge、Embarkedは入っていないものがあって、これらの欠損値をどう扱うという問題がある。

df = pd.read_csv('./train.csv')
print(len(df))
print(df.isnull().sum())
891
PassengerId      0
Survived         0
Pclass           0
Name             0
Sex              0
Age            177
SibSp            0
Parch            0
Ticket           0
Fare             0
Cabin          687
Embarked         2
dtype: int64

欠損値については連続値をとるFareとAgeは平均を取り、Embarkedは専用のカテゴリーにしてみた。決定木は分岐を繰り返すためlabel encodingで学習できる。 数値化できないものについては除いている。

def preprocess(df):
    df['Fare'] = df['Fare'].fillna(df['Fare'].mean())
    df['Age'] = df['Age'].fillna(df['Age'].mean())
    df['Embarked'] = df['Embarked'].fillna('Unknown')
    df['Sex'] = df['Sex'].apply(lambda x: 1 if x == 'male' else 0)
    df['Embarked'] = df['Embarked'].map( {'S': 0, 'C': 1, 'Q': 2, 'Unknown': 3} ).astype(int)
    df = df.drop(['Cabin','Name','PassengerId','Ticket'],axis=1)
    return df

scikit-learnののRandomForestClassifierで学習させる。 デフォルトの木の数は10で、特徴量の数はsqrt(n_features)となっている。 feature_importances_ で各特徴量の重要度を出すことかできる。

from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.ensemble import RandomForestClassifier
import numpy as np
import csv

def train(df):
    train_x = df.drop('Survived', axis=1)
    train_y = df.Survived
    (train_x, test_x ,train_y, test_y) = train_test_split(train_x, train_y, test_size = 0.33, random_state = 42)

    clf = RandomForestClassifier(random_state=0)
    clf = clf.fit(train_x, train_y)
    pred = clf.predict(test_x)
    print(accuracy_score(pred, test_y))
    
    features = train_x.columns
    importances = clf.feature_importances_
    indices = np.argsort(importances)
    for i in indices[::-1]:
        print("{:<15} {:f}".format(features[i], importances[i]))
    return clf

import pandas as pd
df = pd.read_csv('./train.csv')
df = preprocess(df)
clf = train(df)

精度は8割ほどで、AgeとFareの影響が大きいようだ。

0.803389830508
Age             0.267060
Fare            0.262733
Sex             0.224451
Pclass          0.097958
SibSp           0.058860
Parch           0.048173
Embarked        0.040765

このモデルで予測したSurvivedのcsvを出力してKaggleにアップロードするとスコアと順位が出る。

df_test_origin = pd.read_csv('./test.csv')
df_test = preprocess(df_test_origin)
submit_data =  pd.Series(clf.predict(df_test), name='Survived', index=df_test_origin['PassengerId'])
submit_data.to_csv('submit.csv', header=True)

スコアと順位

参考

機械学習の実践入門ーRandom Forestの要約 | GMOアドパートナーズグループ TECH BLOG byGMO

【Pythonで決定木 & Random Forest】タイタニックの生存者データを分析してみた - これで無理なら諦めて!世界一やさしいデータ分析教室