タイタニックの生存者予測

はじめに

今回初めて記事を書きます。

Kaggleの課題の1つ "Titanic: Machine Learning from Disaster" に挑戦します。

https://www.kaggle.com/c/titanic

あくまで、自身が学習したことを整理することを目的とします。

 

お題

タイタニックの乗客の情報をもとに、各人の生存、死亡を予測しろというものです。

KaggleのDescriptionによると、乗客は2224名おり、1502名が死亡しています。多くの人が亡くなった原因として、ライフボートが十分に用意されていなかったというのがあります。そのため(?)、女性、子供、金持ち(?)が生存した傾向にあるとのこと。

性別・年齢・号車(?)が予測する際の重要な要素になりそうですね!

 

評価スコアは、正解率で求めます。

予測して、10人中9人合っていたら "0.9" となります。

全員死亡と予測した場合、1502/2224 = "0.68" となるので、最低限このスコアは超えなければなりません。

 

データについて

データはトレーニングセットとテストセットに分かれます。

レーニングセットは、891レコード

テストセットは、418レコード

合計で、1309レコード

あります。

2224レコードあるわけではないのですね。

また、トレーニング対テストのレコード数は、約 2 対 1 です。

こういうお題はこんな感じにレコードセットが用意されるのですかね。

 

各レコードに与えられた情報は以下です。

Variable Definition Key
survival Survival 0 = No, 1 = Yes
pclass Ticket class 1 = 1st, 2 = 2nd, 3 = 3rd
sex Sex  
Age Age in years  
sibsp # of siblings / spouses aboard the Titanic  
parch # of parents / children aboard the Titanic  
ticket Ticket number  
fare Passenger fare  
cabin Cabin number  
embarked Port of Embarkation

C = Cherbourg,

Q = Queenstown,

S = Southampton

sibsp: The dataset defines family relations in this way...
Sibling = brother, sister, stepbrother, stepsister
Spouse = husband, wife (mistresses and fiancés were ignored)

parch: The dataset defines family relations in this way...
Parent = mother, father
Child = daughter, son, stepdaughter, stepson
Some children travelled only with a nanny, therefore parch=0 for them.

 

sibspやparchは役立つんですかねこれ。

まあ、なるべく全部モデルに投げる方針としますかね...

ticketは "693" や "WE/P 5735" といった文字列が入っています。ここから法則取れそうにないのでカラムを消します。同一チケットの場合、生存率が似そうですが、モデルにそういうの学習させることできる?

fareとembarked (乗船港) についてですが、おそらくembarkedが近いと料金が安くなり、embarkedが遠いと料金が高くなるはず。

組み合わせての分析をしても良いですが、pclassがあるのなら、それ使えば良いじゃんということで、fareとembarkedのカラムも消します。

cabinについてですが、そもそもcabinが何を表すか不明だったので、Discussionを見てみました。

https://www.kaggle.com/c/titanic/discussion/4693#latest-31689

うーん... Pclassと相関しそうな情報ですね。

ただ、600レコードほどがブランクとなっていて、補完するのが難しそうなので、このカラムも消してしまいますか。

 

というわけで、以下の情報のみを使います。

pclass、sex、Age、sibsp、parch

データ削り過ぎなのでしょうか? 

 

さて、次にデータの補完について考えます。

train.info()

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 12 columns):
PassengerId    891 non-null int64
Survived       891 non-null int64
Pclass         891 non-null int64
Sex            891 non-null object
Age            714 non-null float64
SibSp          891 non-null int64
Parch          891 non-null int64
dtypes: float64(2), int64(5), object(5)
memory usage: 83.6+ KB

Ageにnullがあるので、中央値で補完します。

from sklearn.impute import SimpleImputer
imputer = SimpleImputer(strategy="median")
titanic_train_num = titanic_train_set_drop.drop(["Sex"], axis=1)
imputer.fit(titanic_train_num)

X = imputer.transform(titanic_train_num)

titanic_tr_num = pd.DataFrame(X, columns=titanic_train_num.columns

 また、オブジェクトであるSexを数値化&ワンホットエンコードします。

titanic_train_sex = titanic_train_set["Sex"]
titanic_sex_encoded, titanic_sex_categories = titanic_train_sex.factorize()

from sklearn.preprocessing import OneHotEncoder
encoder_sex = OneHotEncoder(categories="auto")
titanic_sex_1hot = encoder_sex.fit_transform(titanic_sex_encoded.reshape(-1,1))
titanic_tr_ob = pd.DataFrame(titanic_sex_1hot.toarray(), columns=titanic_sex_categories)

ふたつをくっつけて、データの準備完了。

titanic_prepared = pd.concat([titanic_tr_num, titanic_tr_ob], axis=1) 

 

モデルについて

今回は、

・回帰ではなく分類

・サンプル数が100K以下

のため、チートシートに従い、SGDClassifierを使用します。

f:id:konykun:20190616145411p:plain

アルゴリズムチートシート

 

とりあえず色々モデルに突っ込んでみて、成績が良いやつを採用するというやり方でも良いと思うのですが、色々なモデルの使い方がわからないので、ひとつずつ試していく方式を取ります。

 

from sklearn.linear_model import SGDClassifier
sgd_clf = SGDClassifier(random_state = 42 ,max_iter=1000, tol=1e-3)
sgd_clf.fit(titanic_prepared, titanic_labels) 

titanic_predict = sgd_clf.predict(titanic_prepared)

 SGDClassifierを用いた結果が以下の通り。

from sklearn.metrics import confusion_matrix
confusion_matrix(titanic_labels, titanic_predict)
array([[436, 8], [217, 51]])
正解率は、68%です(笑)
(...ちなみに、訓練データでモデルをフィットして、訓練データの予測をしたので、テストデータを使ったらもっと成績が悪くなるはずです。)

予測結果について

ということで、全員死亡と予測した場合と同じ正解率になりました。

 なんの為にモデルを作ったのでしょうか。。。

 

さて、やるべきことは、ここからです。

正解率を上げる為に何をするべきか。

① SGDClassifierのパラメーターを修正する

アルゴリズムチートシートに従い、Kernel approximationを用いる 

③ 訓練データを一部削ったのを、削らず用いるようにする

などですかね...

 

①は何をして良いのかよくわからず、③はメンドくさそう。

ということで、②だけトライしてみましょう。

 

from sklearn.kernel_approximation import RBFSampler
rbf_feature = RBFSampler(gamma=1, n_components=100, random_state=1)
X_std = rbf_feature.fit_transform(titanic_prepared)
from sklearn import linear_model, model_selection
clf_result=linear_model.SGDClassifier(loss="hinge", random_state = 42 ,max_iter=1000, tol=1e-3)
scores=model_selection.cross_val_score(clf_result, X_std, titanic_labels, cv=10)
print("平均正解率 = ", scores.mean())

結果は、

平均正解率 = 0.7289503688799464

72%に上がりました!

テストセットでのテストはしない!メンドくさい。

 

感想

・人のブログ見ていて、わかりにくいなーと思うことが多々あるのですが、このブログがその最たるものであることがわかりました。ブログに丁寧に書くのって難しいんですね。

 

メモ

・よく正規化って言葉が出てきますが、今回のレコードに対して正規化をする必要があるのでしょうか。

・DeepLearningがこのチートシートにでてこないですが、どのタイミングで使うんですかね。。。

・Kaggleのカーネルに良さげな記事あり。さらにそれを日本語化している人がいたので、それを見てみる。

https://qiita.com/hokuto_HIRANO/items/2c35a81fbc95f0e4b7c1