管理人
ちょっと今回は茶番的なのはなしにして、KaggleのTitanicを使っていろんなライブラリでいろんなアルゴリズムを試してみようかなと思います。今回はChainerニューラルネットワークをやってみます。

Kaggleとは

Kaggle(カグル)は、端的に言うと機械学習をやる人たちのコミュニティサイトです。それだけではなくデータ分析のコンペが開催されているので、参加して賞金を得たり、世の中にいるトップクラスの人のプログラムを見て勉強することができたりトップサイエンティストとディスカッションをしたりできるサイトです。世界中のデータサイエンティストが切磋琢磨して分析力を競っています。ちなみに参加している人のことをKaggler(カグラー)と呼びます。

Titanicとは

そもそもTitanicとは

Titanic(タイタニック)号は、処女航海中の1912年に北大西洋上で氷山に接触、翌日未明にかけて沈没した。未曾有の人災で有名となってしまった20世紀初頭に建造された豪華客船です。何度も悲劇として映画化されたりしているので知っている方も多いかと思います。

管理人
レオナルドディカプリオが主演した映画が1997年で20年以上経ってて驚愕してます。

KaggleにおけるTitanicとは

先のTitanic号を題材にしたKaggle初心者向けにチュートリアルも兼ねて行われている「タイタニックの生存者予測」を行うコンペをKagglerたちはTitanicと呼んでます。

いきなりコンペに参加するのもいいですが、まずは肩慣らしとしてやってみようというものですね。Kaggleと言ったらまずはTitanicというぐらい有名な題材みたいです。

何をする?

実際のTitanicの乗客リストに生死についての情報が追記されている学習用データ(train.csv)を与えられます。このデータで機械学習を行い、コンペ提出用の生死がわからない(実際には答えがあるが提供されていない)テストデータ(test.csv)について生存者予測を行いどれぐらい正答率が高いかを競うコンペです。

Competitionへの参加

コンペへの参加方法からデータセットの設定までを知りたい方はリンクを参照してください。

やってみた

事前準備

事前準備としてライブラリのインポートを行います。ちなみにKaggleのKernelでJupyterを使ってます。

import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import os

from chainer import Function, gradient_check, report, training, utils, Variable
from chainer import datasets, iterators, optimizers, serializers
from chainer import Link, Chain, ChainList
import chainer.functions as F
import chainer.links as L
from chainer.training import extensions
%matplotlib inline

データを確認する

まずは、どんなデータが入っているのかを確認します。

# Reading file
train_base = pd.read_csv("../input/train.csv")
test_base = pd.read_csv("../input/test.csv")

train.head()

次に欠損値がないかどうかを確認します。

# check missing rate for Train data
null_count = train_base.isnull().sum()
null_rate = null_count / len(train_base)

null_table = pd.DataFrame({
    'null_couunt': null_count,
    'null_rate': null_rate
})

print(null_table.sort_values(by='null_rate', ascending=False))

データをクレンジングする

データを確認したところ、結構データの欠損があることがわかりました。さらに、文字列データが散見されることがわかります。よって、これからデータを機械学習できる形にもっていかなければなりません。

ここでやりたいこと
  • 欠損値をどうにかする。
  • 文字列データをどうにかする。
 

まずは欠損を補完する作業を行いました。実際にやった補完方法を全て載せると長くなってしまうので省きます。代わりにシンプルに平均値で補完する方法を載せておきます。

train["Age"] = train["Age"].fillna(train["Age"].mean())

なお、Cabinの補間はしないことにしました。データの77%が欠損しているので、信頼性の高い補間は無理だと判断したためです。

次に、Chainerが文字列データを受け付けてくれないので文字データになっているフィールドを数値に変換していきます。ここでも、どれに対してどういう処理をしたかの詳細は長くなるので省きますが、それぞれのフィールドに対して以下に挙げる例のどちらかを行いました。

One-Hot-Encoding

文字列データなどでいくつかのグループに分けられる場合かつそれぞれのデータ自身に優劣をつけたくない場合などに利用されるのがOne-Hot-Encodingと呼ばれる方法です。Pandasがあれば非常に簡単にできます。

train = pd.get_dummies(train, columns=['Age'])

データ置き換え

文字列データなどでいくつかのグループに分けられる場合かつそれぞれのデータ自身に優劣をつけてもいい場合などは単純にデータの条件に合わせて置き換えることもできます。

data_f.loc[ data_f['Age'] <= 30, 'Age'] = 0

ここでは、data_f['Age'] <= 30という条件下で'Age'フィールドを=以降の数字(今回は0)で置き換えるという感じです。

これでデータのクレンジングを行うことができました。

必要なデータのみを抽出する

次に、読み込んだデータの中には機械学習に利用しない(すべきではない)データがいくつかあるのでそれを省きたいと思います。例えば、PassengerIdとか、欠損値補間をしなかったCabinとかですね。

ここでやりたいこと
  • 使わないデータを省く。
 

ここはもっとスマートなやり方があるだろうなと思いながらも以下の方法でやりました。スマートに書く方法を考えるよりとにかく進めたかったという思いが強かったので許してください...

data_f = train[["Pclass", "Age", "SibSp", "Parch", "Fare",
                "female", "male", "Survived"]]

data_array = data_f.as_matrix()

X = []
Y = []
for x in data_array:
    x_split = np.hsplit(x, [-1])
    X.append(x_split[0].astype(np.float32))
    Y.append(x_split[1].astype(np.int32))

これで、学習用のデータ及びラベルの作成が完了しました。

学習する

ここまででデータの準備が整いました。テスト(提出用)データも同様の準備をしますが同じ記述の繰り返しになるので省略します。あとは、学習するプログラムとニューラルネットワークのモデルを準備して動作させれば機械学習ができます。

ここでやりたいこと
  • モデルを設計する。
  • 学習するプログラムを書く。
 

モデルの準備

まずは、モデルを準備します。最初なので、精度とかはあまり考えず適当に作りたいと思います。

モデルの内容
  • 隠れ層は2層
  • 各層のノード数の初期値は200(ただし、インスタンス化の際に設定可能)
  • 隠れ層の活性化関数はrelu関数
 
class DNN(Chain):
    def __init__(self, n_mid_units=200, n_out=2):
        super(DNN, self).__init__()
        with self.init_scope():
            self.l1 = L.Linear(None, n_mid_units)
            self.l2 = L.Linear(None, n_mid_units)
            self.l3 = L.Linear(None, n_out)

    def __call__(self, x):
        h1 = F.selu(self.l1(x))
        h2 = F.selu(self.l2(h1))
        y = self.l3(h2)
        return y

プログラムにするとこんな感じですかね。

学習プログラムを書く

次に、学習プログラムを書いていきます。trainerを使うことで簡単に書けるようになったので、非常に楽になったのがいいですね。

ここでやりたいこと
  • 自己検証用テストデータの準備。
  • trainerの準備。
 

提出用とは別に自分でどれぐらいの精度を持っているか検証するためのデータを学習用データから拝借します。

train_data_items = 800
batch_size = 100

train_data, test_data = datasets.split_dataset_random(
    datasets.tuple_dataset.TupleDataset(X, Y),
    train_data_items
)

train_iter = iterators.SerialIterator(train_data,
                                      batch_size,
                                      shuffle=True)

test_iter = iterators.SerialIterator(test_data,
                                     batch_size,
                                     repeat=False,
                                     shuffle=False)

次に、実際に学習を行う部分を記述します。

model = L.Classifier(DNN())

optimizer = optimizers.Adam()
optimizer.setup(model)

updater = training.StandardUpdater(train_iter, optimizer)
trainer = training.Trainer(updater, (100, 'epoch'), out="result")
trainer.extend(extensions.Evaluator(test_iter, model))
trainer.extend(extensions.LogReport())
trainer.extend(extensions.PrintReport(['epoch', 'main/accuracy', 'validation/main/accuracy']))
trainer.extend(extensions.ProgressBar())
trainer.run()

実際に動かすと、こんな感じで学習の進み具合が表示されます。

後は学習が終わった学習済みのモデルを利用して提出用のデータを作成するだけですね。この先は、予測してデータを整形してCSVファイルにするだけなので省いて結果にいきたいと思います。

提出する

提出用のCSVを作成したら採点をしてもらうためにCSVを提出します。Kernelを利用している場合は作ったプログラムをコミットした上でデータをサブミットする必要があります。その方法を示します。

まずは、コミットです。編集画面の右上にあるCommitを押します。

そうすると、今まで書いているすべてのプログラムをサーバにアップロードすることができます。次に、CSVファイルの提出ですがコミットした後の画面にあるOpen Versionを押して、自分のKernelのページに移動します。

OutputのところまでスクロールしてそこにあるSubmit to Competitionを押すことで提出できます。

提出した段階で、Kaggleが正解と照らし合わせて正解率を算出し、スコアと順位を表示してくれます。

管理人
既にいろいろやってるのでここのデータはこのプログラムで出したスコアではないです。

結果

ということで無事に提出したスコアですが、0.55023という何とも情けない結果になりました。勘でやっても同じぐらいのスコア出せそう...

データ数少なすぎるし、データビジュアライズしたらランダムフォレストとかでやったほうがよさそうだなと思っていて、きっとニューラルネットワークはあまり向いてないだろうなと感じてましたがやってみたらどうなるかなという興味とKaggle見てるとScikit-learnがよくつかわれていたのでChainerでもできるんだろうかということでやったので結果はそこまで気にしてないですけど、思ってた以上に低いスコアですね。

次は、もうちょっとスコアに注視して進めたものを書くことにします。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です