タイタニックの生存者予測
はじめに
今回初めて記事を書きます。
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を使用します。
とりあえず色々モデルに突っ込んでみて、成績が良いやつを採用するというやり方でも良いと思うのですが、色々なモデルの使い方がわからないので、ひとつずつ試していく方式を取ります。
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