2017年11月9日 更新

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

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

248 view お気に入り 0

目次

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 | 2,036 view
pythonによるtensorflow〜deepdreamによる画像変換〜

pythonによるtensorflow〜deepdreamによる画像変換〜

今回は前回のtensorflowの記事に引き続き、deepdreamによる画像変換についてご紹介します。
三好 裕之 | 1,605 view
pythonによるtensorflow〜インストール、サンプルの実行〜

pythonによるtensorflow〜インストール、サンプルの実行〜

今回は、いま注目されている「tensorflow」についてご紹介します。
三好 裕之 | 1,630 view
Neural Network with Julia 〜Kaggleの文字認識(DeepLearning)の前処理〜

Neural Network with Julia 〜Kaggleの文字認識(DeepLearning)の前処理〜

今回もNeural NetworkをJuliaで実装する方法についてご紹介します。
LP-tech2周年記念#人気記事のまとめ#第5位〜第1位

LP-tech2周年記念#人気記事のまとめ#第5位〜第1位

LP-techが始まってから2周年を迎えました。ここまでLP-techを続けることができたのも読者の皆様のおかげだと思っています。そこで、LP-techの感謝祭ということで、人気の記事を第20位から第1位までをご紹介します。今回は第5位〜第1位までです。

この記事のキーワード

この記事のキュレーター

Takumi Ihara Takumi Ihara