読者です 読者をやめる 読者になる 読者になる

子育てしながらエンジニアしたい

現在 2 歳女の子の子育て中エンジニア。子育てとエンジニアを両立するにはどうすれば良いのか模索中。

Kaggle 初挑戦: タイタニック号の生存予測その 3 - ニューラルネットワークによる予測 -

Kaggle: タイタニック号生存予測シリーズ

ニューラルネットワークとは

ニューラルネットワークとは、脳神経系をモデルにした情報処理システムのことです。学習能力を持ち、必要とされる機能を、提示されるサンプルに基づき自動形成することができます。文字認識や、音声認識など、コンピュータが苦手とされている処理に対して有効です。

村上研究室 ニューラルネットワーク より

いわゆる人工知能の一つのアプローチです。
実は大学時代 (もう10年前か...)、人工知能の一分野である、強化学習というのを研究していました。
当時は実装が大変で、かなり挫折してました...
それが今は Python という言語に強力なライブラリがあるおかげで、とても簡単に実装できるようになっていました。
Python で作るニューラルネットワークの本「ゼロから作る Deep Learning」を買ったので、
せっかくなのでタイタニック号生存予測をニューラルネットワークでやることにしました。

ゼロから作る Deep Learning

Amazon でも大評判のこの本、読んでみました。
たしかに説明もわかりやすく、かつ Python の実装もちゃんと書かれているので、とても理解しやすいです。
また、素晴らしいのはコードが Github で公開されていることです。

Gitlab - ゼロから作るdeep learning

MITライセンスで自由に使えるとのことで、ありがたいです。
今回はこのソースからニューラルネットワークを使わせてもらうことにしました。

データの加工

前回の記事のように、まずはデータを加工します。

edosha.hatenablog.jp

前回の記事では試行錯誤しながらデータを加工しているので、これをまとめました。

# coding: utf-8
import pandas as pd
"""Titanic データの前処理をする

同じディレクトリに train.csv, test.csv を置いて使う。

Available functions:
    wrangle_data - データを加工する。返り値は train_df, test_df
                   (pandas のデータフレーム)
"""

def wrangle_data ():
    # Preprocess training data
    train_df = pd.read_csv ('train.csv')
    test_df = pd.read_csv ('test.csv')
    
    train_df['Title'] = train_df.Name.str.extract(' ([A-Za-z]+)\.', expand=False)
    test_df['Title'] = test_df.Name.str.extract(' ([A-Za-z]+)\.', expand=False)
    
    # train_df
    title_list = train_df['Title'].sort_values (inplace=False).unique ()
    pclass_list = train_df['Pclass'].sort_values (inplace=False).unique ()
    for title in title_list:
        for pclass in pclass_list:
            guess_df = train_df[(train_df['Title'] == title) & (train_df['Pclass'] == pclass)]['Age'].dropna ()
            train_df.loc[(train_df['Age'].isnull()) & (train_df['Title'] == title) & (train_df['Pclass'] == pclass), 'Age'] = guess_df.median ()
            
    # test_df
    title_list_t = test_df['Title'].sort_values (inplace=False).unique ()
    pclass_list_t = test_df['Pclass'].sort_values (inplace=False).unique ()
    for title in title_list_t:
        for pclass in pclass_list_t:
            guess_df = test_df[(test_df['Title'] == title) & (test_df['Pclass'] == pclass)]['Age'].dropna ()
            test_df.loc[(test_df['Age'].isnull()) & (test_df['Title'] == title) & (test_df['Pclass'] == pclass), 'Age'] = guess_df.median ()
            
    # test_df 'Title' == 'Ms' の補完
    guess_df = train_df[train_df['Title'] == 'Ms']['Age'].dropna ()
    test_df.loc[test_df['Age'].isnull(), 'Age'] = guess_df.median ()
       
    # train_df 'Embarked' の補完
    freq_port = train_df.Embarked.dropna().mode()[0]
    train_df['Embarked'] = train_df['Embarked'].fillna (freq_port)
    
    # test_df 'Fare' の補完
    guess_df = test_df[test_df['Pclass'] == 3].dropna()
    test_df.loc[test_df['Fare'].isnull(), 'Fare'] = guess_df['Fare'].median()
    
    df_list = [train_df, test_df]
    for df in df_list:
        df.loc[df['Age'] <= 8, 'Age'] = 0
        df.loc[(df['Age'] > 8) & (df['Age'] <= 16), 'Age'] = 1
        df.loc[(df['Age'] > 16) & (df['Age'] <= 32), 'Age'] = 2
        df.loc[(df['Age'] > 32) & (df['Age'] <= 48), 'Age'] = 3
        df.loc[(df['Age'] > 48) & (df['Age'] <= 64), 'Age'] = 4
        df.loc[(df['Age'] > 64) & (df['Age'] <= 80), 'Age'] = 5
        df['Fellow'] = df['SibSp'] + df['Parch']
        df['IsAlone'] = 0
        df.loc[df['Fellow'] == 0, 'IsAlone'] = 1
        df['Sex'] = df['Sex'].map ({'female': 0, 'male': 1}).astype (int)
        df['Embarked'] = df['Embarked'].map ({'C': 0, 'Q': 1, 'S': 2}).astype (int)
        df.loc[df['Fare'] <= 7.91, 'Fare'] = 0
        df.loc[(df['Fare'] > 7.91) & (df['Fare'] <= 14.454), 'Fare'] = 1
        df.loc[(df['Fare'] > 14.454) & (df['Fare'] <= 31), 'Fare']   = 2
        df.loc[df['Fare'] > 31, 'Fare'] = 3
        df['Fare'] = df['Fare'].astype(int)
        
    train_df = train_df.drop (['PassengerId', 'Ticket', 'Cabin', 'Name', 'Title', 'SibSp', 'Parch', 'Fellow'], axis=1)
    test_df = test_df.drop (['Ticket', 'Cabin', 'Name', 'Title', 'SibSp', 'Parch', 'Fellow'], axis=1)
    
    return train_df, test_df

if __name__ == '__main__':
    
    train_df, test_df = wrangle_data ()
    
    print (train_df.describe())
    print (test_df.describe())

ニューラルネットワークによる予測

いよいよ予測をします!
まずは使用するニューラルネットワークのライブラリを Github からダウンロードしてください。
common ディレクトリだけ使うので、それだけダウンロードしても大丈夫です。
そして、以下のようにスクリプトを配置してください。

dir
│   data_wrangle.py
│   test.csv
│   train.csv
│   train_neuralnet.py
│
├───common
│   │   functions.py
│   │   gradient.py
│   │   layers.py
│   │   multi_layer_net.py
│   │   multi_layer_net_extend.py
│   │   optimizer.py
│   │   trainer.py
│   │   util.py

train_neuralnet.py は以下のソースコードになってます。
自分の Github からもダウンロード可能です。

# coding: utf-8

"""titanic の生存予測を、ニューラルネットワークで行う

ニューラルネットワークのサイズは MultiLayerNet の引数で決まる。
"""

import numpy as np
from common.multi_layer_net import MultiLayerNet
import pandas as pd
import matplotlib.pyplot as plt
from common.optimizer import *
import data_wrangle

def change_one_hot_label(X):
    """書籍を参考。正解データを one_hot_label 形式にする"""
    T = np.zeros((X.size, 2))
    for idx, row in enumerate(T):
        row[X[idx]] = 1
        
    return T

# 前処理されたデータの取得
train_df, test_df = data_wrangle.wrangle_data ()

# データを学習物と答えに分ける
x_train = train_df.drop (['Survived'], axis=1).values
t_train = train_df['Survived'].values
t_train = change_one_hot_label (t_train)

# ニューラルネットワークの生成。
# 隠れ層は適当に 100, 100, 100 とした。
network = MultiLayerNet(input_size=6, hidden_size_list=[100, 100, 100], output_size=2, weight_decay_lambda=0.01)
# Optimizer の選択。AdaGrad() は common.optimizer 以下にある。
optimizer = AdaGrad()

iters_num = 1000
train_size = x_train.shape[0]
batch_size = 99

train_loss_list = []
train_acc_list = []

iter_per_epoch = max(train_size / batch_size, 1)

# 学習
for i in range(iters_num):
    batch_mask = np.random.choice(train_size, batch_size)
    x_batch = x_train[batch_mask]
    t_batch = t_train[batch_mask]
    
    # 勾配
    #grad = network.numerical_gradient(x_batch, t_batch)
    grad = network.gradient(x_batch, t_batch)
    
    # 更新
    optimizer.update(network.params, grad)
    
    loss = network.loss(x_batch, t_batch)
    train_loss_list.append(loss)
    
    if i % iter_per_epoch == 0:
        train_acc = network.accuracy(x_train, t_train)
        train_acc_list.append(train_acc)

# train データでどのように学習されたか図示
plt.plot (train_acc_list)

# テストデータの PassengerId を退避させる
test_df_id = test_df['PassengerId']
test_df = test_df.drop ('PassengerId', axis=1)
x_test = test_df.values

# テストデータの生存予測
output = network.predict (x_test)
output = np.argmax (output, axis=1)

# 生存予測を Kaggle に投稿する形に修正する
output_df = pd.DataFrame (output, columns=['Survived'])
output_df['PassengerId'] = test_df_id
output_df = output_df.ix[:, ['PassengerId', 'Survived']]
output_df.to_csv('test_ans.csv', index=False, encoding='utf-8')

さあ、ついにこれで test_ans.csv を得ました!
これを Kaggle に投稿してみましょう!

結果は....


f:id:edosha:20170429215836p:plain

0.7799!!

いまいち!!(笑)


まだまだ全然修行がたりないようです。
でも、こうやってデータ処理を競うっておもしろいですね。
これからも頑張ります。