KaggleのTitanicを使っていろんなライブラリでいろんなアルゴリズムを試してみようかなと思います。今回はChainerでニューラルネットワークをやってみます。
Kaggleとは
Kaggleは、機械学習に興味のある人々のためのコミュニティサイトです。ここでは、データ分析のコンペティションが開催され、参加者は賞金を獲得したり、トップクラスのプログラムを学んだり、有名なデータサイエンティストとディスカッションを行ったりすることができます。世界中のデータサイエンティストが集まり、お互いに競い合って分析能力を高めています。参加者は一般的に「Kaggler」と呼ばれます。
Titanicとは
Titanic(タイタニック)号は、処女航海中の1912年に北大西洋上で氷山に接触、翌日未明にかけて沈没した。未曾有の人災で有名となってしまった20世紀初頭に建造された豪華客船です。何度も悲劇として映画化されたりしているので知っている方も多いかと思います。
KaggleにおけるTitanicとは
Titanic号に関連するKaggleのコンペティションは、「タイタニックの生存者予測」として、Kaggle初心者向けのチュートリアルとして行われています。
参加者は、与えられた学習用データ(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
データ確認
まずは、どんなデータが入っているのかを確認します。
# 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でもできるんだろうかということでやったので結果はそこまで気にしてないですけど、思ってた以上に低いスコアでした。
次は、もうちょっとスコアに注視して進めたものを書くことにします。
コメント