2017年11月9日 更新

KaggleチュートリアルTitanicで上位1%に入った話。(0.87081)

前回書いた「KaggleチュートリアルTitanicで上位3%以内に入るには。(0.82297)」 から久々にやり直した結果上位1%の0.87081を出せたのでどのようにしたのかを書いていきます。

52,159 view お気に入り 1

目次

1.前回まで

2.今回変わったこと

3.生存予測

4.データ分析

5.モデル検証

6.感想

7.実際の予測結果

参考文献

1. 前回まで

 前回の記事では、機械学習の勉強を初めて4ヶ月で右も左も分からない中で他の人のカーネルを参考にしてデータの前処理をし、RandomForestClassifierのパラメータチューニングを適化してこれ以上あげられない結果になったということでした。詳しくは記事を見ていただけると助かります。(https://lp-tech.net/articles/0QUUd)

2.今回変わったこと

 今回大きく変わった点は大きく2つです。
- 1.RandomForestClassifierではなくLightGBMを使った。
- 2.トレーニングデータを増やした。

以上です。具体的に見ていきましょう。

2-1.LightGBMについて

 Titanicの次に、House Pricesのチュートリアルを始めた時にRandomForestClassifierでは全く歯が立たなかったため、カーネルを見たところ、XGBoostかLightGBMを使っている人が多かったので私もそれをやってみようと思いました。ところが、インストールしようとした時に、何回やってもXGBoostがうまくいきませんでした。一方、LightGBMはすぐにできたのでLightGBMを使うことにしました。
LightGBMは、Microsoftが作ったboosting tree系のライブラリの一つらしいです。理論とかはそんなにわからないので詳しく知りたい方はGoogle先生に聞いてください。

2-2.トレーニングデータを増やす

 おそらくただLightGBMを使ってもデータの前処理を変えない限りそこまで生存予測を結果は変わらなかったでしょう。そこで今回トレーニングデータを増やしました。もともとKaggleで今回もらったトレーニングデータは891人分のデータだけでした。それを用いてテストデータである418人分のデータを予測するのです。
そこで前回、私は約82%のデータの予測を当てていたのですからこのデータを使うことにしました。
(元々のトレーニングデータ891人分のデータ+テストデータ418人分の予測データ)=1309人分のデータ
これを用いて元々のテストデータ418人分の生存予測をするのです。
これで結果的には良かったのですが、他の例では必ずしもいい結果になるとは限らないと思います。過学習になりそうです。また、418×0.18=75人分のデータは誤っているはずです。

3.生存予測

 ここからは実際にコードを交えて生存予測をして行いたいと思います。最初の方は前回と同じなのでさらっといきます。

import pandas as pd 
import numpy as np
from sklearn.ensemble import RandomForestClassifier 
train= pd.read_csv("train.csv").replace("male",0).replace("female",1).replace("S",0).replace("C",1).replace("Q",2)
test= pd.read_csv("test.csv").replace("male",0).replace("female",1).replace("S",0).replace("C",1).replace("Q",2) 
train["Age"].fillna(train.Age.mean(), inplace=True) 
train["Embarked"].fillna(train.Embarked.mean(), inplace=True) 
combine1 = [train]

for train in combine1: 
        train['Salutation'] = train.Name.str.extract(' ([A-Za-z]+).', expand=False) 
for train in combine1: 
        train['Salutation'] = train['Salutation'].replace(['Lady', 'Countess','Capt', 'Col','Don', 'Dr', 'Major', 'Rev', 'Sir', 'Jonkheer', 'Dona'], 'Rare')
        train['Salutation'] = train['Salutation'].replace('Mlle', 'Miss')
        train['Salutation'] = train['Salutation'].replace('Ms', 'Miss')
        train['Salutation'] = train['Salutation'].replace('Mme', 'Mrs')
        del train['Name']
Salutation_mapping = {"Mr": 1, "Miss": 2, "Mrs": 3, "Master": 4, "Rare": 5} 

for train in combine1: 
        train['Salutation'] = train['Salutation'].map(Salutation_mapping) 
        train['Salutation'] = train['Salutation'].fillna(0) 
    
for train in combine1: 
        train['Ticket_Lett'] = train['Ticket'].apply(lambda x: str(x)[0])
        train['Ticket_Lett'] = train['Ticket_Lett'].apply(lambda x: str(x)) 
        train['Ticket_Lett'] = np.where((train['Ticket_Lett']).isin(['1', '2', '3', 'S', 'P', 'C', 'A']), train['Ticket_Lett'], np.where((train['Ticket_Lett']).isin(['W', '4', '7', '6', 'L', '5', '8']), '0','0')) 
        train['Ticket_Len'] = train['Ticket'].apply(lambda x: len(x)) 
        del train['Ticket'] 
train['Ticket_Lett']=train['Ticket_Lett'].replace("1",1).replace("2",2).replace("3",3).replace("0",0).replace("S",3).replace("P",0).replace("C",3).replace("A",3) 

    
for train in combine1: 
    train['Cabin_Lett'] = train['Cabin'].apply(lambda x: str(x)[0]) 
    train['Cabin_Lett'] = train['Cabin_Lett'].apply(lambda x: str(x)) 
    train['Cabin_Lett'] = np.where((train['Cabin_Lett']).isin([ 'F', 'E', 'D', 'C', 'B', 'A']),train['Cabin_Lett'], np.where((train['Cabin_Lett']).isin(['W', '4', '7', '6', 'L', '5', '8']), '0','0'))
del train['Cabin'] 
train['Cabin_Lett']=train['Cabin_Lett'].replace("A",1).replace("B",2).replace("C",1).replace("0",0).replace("D",2).replace("E",2).replace("F",1) 
train["FamilySize"] = train["SibSp"] + train["Parch"] + 1
for train in combine1:
    train['IsAlone'] = 0
    train.loc[train['FamilySize'] == 1, 'IsAlone'] = 1
train_data = train.values
xs = train_data[:, 2:] # Pclass以降の変数
y  = train_data[:, 1]  # 正解データ
test["Age"].fillna(train.Age.mean(), inplace=True)
test["Fare"].fillna(train.Fare.mean(), inplace=True)

combine = [test]
for test in combine:
    test['Salutation'] = test.Name.str.extract(' ([A-Za-z]+)\.', expand=False)
for test in combine:
    test['Salutation'] = test['Salutation'].replace(['Lady', 'Countess','Capt', 'Col',\
         'Don', 'Dr', 'Major', 'Rev', 'Sir', 'Jonkheer', 'Dona'], 'Rare')

    test['Salutation'] = test['Salutation'].replace('Mlle', 'Miss')
    test['Salutation'] = test['Salutation'].replace('Ms', 'Miss')
    test['Salutation'] = test['Salutation'].replace('Mme', 'Mrs')
    del test['Name']
Salutation_mapping = {"Mr": 1, "Miss": 2, "Mrs": 3, "Master": 4, "Rare": 5}

for test in combine:
    test['Salutation'] = test['Salutation'].map(Salutation_mapping)
    test['Salutation'] = test['Salutation'].fillna(0)

for test in combine:
        test['Ticket_Lett'] = test['Ticket'].apply(lambda x: str(x)[0])
        test['Ticket_Lett'] = test['Ticket_Lett'].apply(lambda x: str(x))
        test['Ticket_Lett'] = np.where((test['Ticket_Lett']).isin(['1', '2', '3', 'S', 'P', 'C', 'A']), test['Ticket_Lett'],
                                   np.where((test['Ticket_Lett']).isin(['W', '4', '7', '6', 'L', '5', '8']),
                                            '0', '0'))
        test['Ticket_Len'] = test['Ticket'].apply(lambda x: len(x))
        del test['Ticket']
test['Ticket_Lett']=test['Ticket_Lett'].replace("1",1).replace("2",2).replace("3",3).replace("0",0).replace("S",3).replace("P",0).replace("C",3).replace("A",3) 

for test in combine:
        test['Cabin_Lett'] = test['Cabin'].apply(lambda x: str(x)[0])
        test['Cabin_Lett'] = test['Cabin_Lett'].apply(lambda x: str(x))
        test['Cabin_Lett'] = np.where((test['Cabin_Lett']).isin(['T', 'H', 'G', 'F', 'E', 'D', 'C', 'B', 'A']),test['Cabin_Lett'],
                                   np.where((test['Cabin_Lett']).isin(['W', '4', '7', '6', 'L', '5', '8']),
                                            '0','0'))        
        del test['Cabin']
test['Cabin_Lett']=test['Cabin_Lett'].replace("A",1).replace("B",2).replace("C",1).replace("0",0).replace("D",2).replace("E",2).replace("F",1).replace("G",1) 
test["FamilySize"] = train["SibSp"] + train["Parch"] + 1

for test in combine:
    test['IsAlone'] = 0
    test.loc[test['FamilySize'] == 1, 'IsAlone'] = 1
    
test_data = test.values
xs_test = test_data[:, 1:]

from sklearn.ensemble import RandomForestClassifier

random_forest=RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini',
            max_depth=25, max_features='auto', max_leaf_nodes=None,
            min_samples_leaf=1, min_samples_split=15,
            min_weight_fraction_leaf=0.0, n_estimators=51, n_jobs=4,
            oob_score=False, random_state=0, verbose=0, warm_start=False)

random_forest.fit(xs, y)
Y_pred = random_forest.predict(xs_test)


import csv
with open("Predict_result_data.csv", "w") as f:
    writer = csv.writer(f, lineterminator='\n')
    writer.writerow(["PassengerId", "Survived"])
    for pid, survived in zip(test_data[:,0].astype(int), Y_pred.astype(int)):
        writer.writerow([pid, survived])
python.py

ここまでが前回までのコードでした。この、Predict_result_data.csvを提出すると0.82296という結果になりました。 次からが今回やったことです。はじめに結果の良かった生存予測の結果(087081.csv)をテストデータのSurvivedに入れ、その後そのテストデータをトレーニングデータに入れて、1309人分のトレーニングデータを完成させます。

#test["Survived"]=Y_pred.astype(int)
#ex= pd.read_csv("Predict_result_data.csv")
ex= pd.read_csv("087081.csv")
test["Survived"]=ex["Survived"]
train=train.append(test, ignore_index=True)
python.py

ここでtrainを見てみるとカラムの順番が変わってしまっているので、それを直すコードが下のものとなります。きっと上で入れた時に崩れたのだと思います。入れ方がもっといいものがあるはずですがここはこれでいきます。この順番を直すのももっと楽な方法があると思いますが… 順番を直した後はLightGBMに学習させるために値だけ取り出します。

train=train.ix[:,[7,12,8,10,0,11,6,4,2,9,14,13,1,3,5]]
train_data = train.values
xs = train_data[:, 2:] # Pclass以降の変数
y  = train_data[:, 1]  # 正解データ
test_data = test.values
xs_test = test_data[:, 1:]
python.py

ここからLightGBMに学習させていきます。正直に書きますとあまりLightGBMの使い方はうまくありません。コードとして他の書き方がありそうです。
わかったことを書きますと、今、test_size=0.008となっていますがここを大きく変えると結果がかなり変わります。’learning_rate’: 0.003も変えると結果が大きく変わります。’feature_fraction’: 0.52,’bagging_fraction’: 0.79この二つは値を大きくすると0付近と1付近に値が集まりやすくなります。0は死亡、1は生存です。num_boost_round=5000は試行回数の最大値みたいなものです。early_stopping_rounds=1000これは小さくすると今回の場合は試行回数が400とかで止まったりします。大きくすると試行回数は5000まで行くようになります。試行回数が多くなればなるほどはっきりと生存したかどうかを分けられます。

import lightgbm as lgb
from sklearn.cross_validation import train_test_split
X_train,X_test,y_train,y_test = train_test_split(xs,y,test_size=0.008,random_state=0)

lgb_train = lgb.Dataset(np.array(X_train),np.array(y_train))
lgb_eval = lgb.Dataset(np.array(X_test),np.array(y_test),reference=lgb_train)
 

params = {
    'task': 'train',
    'boosting_type': 'gbdt',
    'objective': 'regression',
    'metric': {'l2'},
    'num_leaves': 200,
    'learning_rate': 0.003,
    'num_iterations':100,
    'feature_fraction': 0.52,
    'bagging_fraction': 0.79,
    'bagging_freq': 7,
    'verbose': 0
}

gbm = lgb.train(params,
                lgb_train,
                num_boost_round=5000,
                valid_sets=lgb_eval,
                early_stopping_rounds=1000)
X_pred= gbm.predict(np.array(xs_test), num_iteration=gbm.best_iteration)
python.py

今回の結果を見てみましょう。
LightGBMのいいところはRandomForestClassifierと違って0.5391146708などと細かい値までわかることです。RandomForestClassifierだと0か1にしか分けられません。次にやることは閾値を決めることです。普通は0.5だと思いますが、提出して見て結果が一番高くなるものにしましょう。今回は0.51でした。

X_pred= gbm.predict(np.array(xs_test), num_iteration=gbm.best_iteration)
X_pred
python.py

array([ -1.79429443e-02, 7.08639118e-01, 4.17361704e-02, 1.18435005e-01, 9.46453072e-01, 1.06590832e-01, 7.37074399e-01, 3.98188634e-02, 8.20541267e-01, -3.63965273e-02, -9.63186239e-02, 6.24408421e-02, 1.09983865e+00, 8.28692270e-02, 9.18589512e-01, 1.02486356e+00, -8.39167873e-02, 2.06530570e-01, 3.16564821e-01, 7.59492717e-01, 1.43219001e-02, 8.91956837e-01, 9.01874052e-01, 1.49049070e-01, 9.34034993e-01, -5.36973212e-02, 1.03309890e+00, 6.12748015e-02, 3.53904706e-01, 1.14633129e-01, -4.37703581e-02, 5.91412072e-02, 8.36805600e-01, 1.72394220e-01, 7.96334526e-01, 9.70226417e-02, 8.25331616e-02, 5.85123409e-02, -2.74411167e-02, 3.10591766e-01, 1.75722573e-02, 2.98653082e-01, 2.38522000e-01, 9.64132214e-01, 1.01372043e+00,…

for i in range(418):
    if X_pred[i]>=0.51:
        X_pred[i]=1
    else:
        X_pred[i]=0

import csv
with open("Predict_result_data.csv", "w") as f:
    writer = csv.writer(f, lineterminator='\n')
    writer.writerow(["PassengerId", "Survived"])
    for pid, survived in zip(test_data[:,0].astype(int), X_pred.astype(int)):
    #for pid, survived in zip(test_data[:,0].astype(int), X_pred):
        writer.writerow([pid, survived])
python.py
23 件

関連する記事 こんな記事も人気です♪

KaggleチュートリアルTitanicで上位3%以内に入るには。(0.82297)

KaggleチュートリアルTitanicで上位3%以内に入るには。(0.82297)

まだ機械学習の勉強を初めて4ヶ月ですが、色々やってみた結果、約7000人のうち200位ぐらいの0.82297という記録を出せたので、色々振り返りながら書いていきます。
Takumi Ihara | 190,302 view
Python × TensorFlow ② ~TensorFlow を扱う上で必要な知識「定数・変数」~

Python × TensorFlow ② ~TensorFlow を扱う上で必要な知識「定数・変数」~

TensorFlow を扱う上で必要な以下3つの知識のうち「定数と変数」について解説していきたいと思います.
井上 大輝 | 9,091 view

この記事のキーワード

この記事のキュレーター

Takumi Ihara Takumi Ihara