KaggleのTitanicで上位10%に入った手法のまとめ

初心者向けですが深層学習の講師を最近やりました。
講義の中で実際にKaggleのコンペで腕試しをするということをしたかったので講義をやる前にチャレンジした。

今回腕試しをするコンペはkaggleのチュートリアルで有名なtitanicを選んだ。

Titanic: Machine Learning from Disaster | Kaggle

どういった内容かというと、タイタニック号の乗客のデータからある乗客は生存したか否かを判定し、その正解率を競う感じ。
試行錯誤して自分の最高スコアは 0.81339 になった。上位10%だしそこそこ頑張れたはず。
f:id:Owatank:20171023141813p:plain

もう少し上を目指したいとこだが授業も始まったので一旦打ち切り
コードはここ

github.com

データ分析にはpandasを用いた。最初はdescribe()df.isnull().sum()とかで統計や欠損値の確認をする。
f:id:Owatank:20171023142710p:plain
訓練データのAge、Cabinなどに欠損値があるのがわかる。

次に仮説をいくつか立ててその条件でデータを操作し、その時の生存者の割合を確認していった。仮説は主に

  • 子供や赤ん坊など年齢が若い人の方が優先して救助されるのではないか

  • 女性のが優先して救助されるのではないか

  • Pclass1(だいたいお金持ちの乗客)の人たちは優先して救助されるのではないか

などなど

年齢はやっぱりというか子供のが生存率が高いのは確認できた
f:id:Owatank:20171023144512p:plain

生存率に関しては性別の違いが一番別れていた。
f:id:Owatank:20171023144812p:plain

後は名前の属性を何かしら使いたい(文字列の操作を練習したかった)ので名前からMrMissなどを抽出してそれらの生存率を確認したりした。

f:id:Owatank:20171023143804p:plain

生存の判別モデル構築の問題として年齢を特徴として扱いたいけど欠損している乗客がいる。
この問題に対して訓練データの年齢における欠損値を除いた年齢の中央値で穴埋めか年齢自体を予測するモデルを作り、そのモデルの予測結果で穴埋めするといった2つの手法をとった。

良い精度が出たのは後者の方だった。年齢予測モデルはXGBoostで構築した。

最終的に使う特徴はこれらに絞った
f:id:Owatank:20171023150053p:plain

one_hot系は文字列カテゴリを数値に変換したもの
SibParchSibSpParchを合わせたもの
Fare_rounddown_splitFareの小数点を打ち切り、10ドルごとにカテゴリ分けしたもの
By_Age_classAgeを10歳ごとにカテゴリ分けしたもの

生存者の判別モデルとしてTensorflowでロジスティック回帰モデルとXGBoostモデルの2つを構築し、比較した。

Tensorflowのロジスティック回帰モデルは以下のように構築した

# 入力層
X = tf.placeholder(tf.float32, shape=[None, 7], name="input")
t = tf.placeholder(tf.float32, shape=[None, 1])
# パラメータ1
stddev = np.sqrt(2.0 / 7)
W1 = tf.Variable(tf.truncated_normal([7,24], stddev=stddev))
b1 = tf.Variable(tf.constant(0.1, shape=[24]))

# パラメータ2
stddev = np.sqrt(2.0 / 24)
W2 = tf.Variable(tf.truncated_normal([24,48], stddev=stddev))
b2 = tf.Variable(tf.constant(0.1, shape=[48]))
keep_prob = tf.placeholder(tf.float32) # ドロップアウトする割合

# パラメータ3
stddev = np.sqrt(2.0 / 48)
W3 = tf.Variable(tf.truncated_normal([48,1], stddev=stddev))
b3 = tf.Variable(tf.constant(0.1, shape=[1]))

layer1 = tf.nn.relu(tf.matmul(X,W1) + b1)

layer2 = tf.nn.relu(tf.matmul(layer1,W2) + b2)
layer2_drop = tf.nn.dropout(layer2, keep_prob)

layer3 = tf.matmul(layer2_drop,W3)+b3

p = tf.nn.sigmoid(layer3,name="output")
# 荷重減衰
norm_term = tf.nn.l2_loss(layer1) + tf.nn.l2_loss(layer2_drop)
# 正則項
lambda_ = 0.001
# 損失関数
loss = tf.reduce_mean(tf.square(p - t))+ lambda_*norm_term
# 学習アルゴリズム
optimizer = tf.train.AdamOptimizer()
train_step = optimizer.minimize(loss)
# 精度
correct_prediction = tf.equal(tf.sign(p-0.5), tf.sign(t-0.5))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

XGboostとlog_lossで誤差の値を比べたがXGboostの勝利だった
NNと決定木における表現力の問題だろうか・・・

ハイパーパラメータをいじって提出していたら 0.81339 という結果が出た。

終わった後に他の人と話してタイタニックは実際にあった話、つまり背景があるから背景から得られる情報を使えばよかったとか元のタイタニック号の船構造を見つけて救命艇がどこにあるかや部屋番号の割り当て(Cabin)を確認するのもアリだったよねーとか灯台下暗しって感じに知見を得た。

他にもstackingや交差検証など学習方法の見直しも大事