機械学習初心者がKaggleのTitanic課題でモデルを作る

機械学習の挑戦者が集まるKaggle

機械学習初心者だったら、「Pythonでモデリングの勉強はして機械学習を実際にやってみたいけど、データがない」と悩んだことはありませんか?

そんなあなたにおすすめしたいのがKaggleです。 Kaggleは大きく分けて2つの特徴があります。1つは企業や政府とデータサイエンティスト/機械学習エンジニアを繋げる「マッチング」。

そして、もう1つが「Competetion(コンペ)」という、 企業や政府がコンペ形式(競争形式)で課題を提示し、参加者が作成した分析モデルのうち、最も制度の高い分析モデルを買い取る仕組みです。

Kaggleは無料で初心者に優しい

Kaggleは無料でコンペに参加が可能です。しかも、企業から提供されているトレーニング用のデータを利用して、モデルの訓練やテストデータで評価も可能なので、コンペ自体に参加する自信がない初心者でも、これらのデータを使ってモデル作成のトレーニングをすることができます。

Kaggleの会員登録については特に難しくないので、ここでは省略します。

PythonでTitanicの生存者を予測する

今回は最も有名な課題の1つである「Titanic: Machine Learning from Disaster」(タイタニック:災害からの機械学習)を実際に行ってみます。これはあのタイタニック号の乗船リストから生還者を予測します。

手順

予測モデルの作成と結果の検証を行うまでの手順になります。

  1. データセットのダウンロード
  2. データセットの内容把握
  3. 前処理
  4. 予測モデルの作成
  5. テストデータを使って予測
  6. 予測結果をKaggle上で検証

データセットのダウンロード

まずはKaggleのサイトから下記の2つのCSVファイルをダウンロードします。各課題では「Data」ページに、データセットの説明が記載されていますので熟読することをおすすめします。

  • train.csv (学習用データ)
  • test.csv (テスト用データ)

データセットの内容把握

それではいよいよPythonで予測モデルの作成に取り掛かります。最初にCSVファイルをインポートしてデータセットの中身を確認しましょう。

※私はJupyter notebookを使用していますので、通常のPythonと若干違う箇所があるかもしれません。

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
# CSVファイルを同じフォルダに格納していますが、別のフォルダに格納する場合はフォルダの指定をしてください。
train = pd.read_csv(\"train.csv\")
test = pd.read_csv(\"test.csv\")

無事取り込みが完了したら、train.csv内の中身や項目を確認します。

train.head()
\"\"

各カラムの説明は以下の通りです。

PassengerId乗客者ID
Survived生存フラグ(0=死亡、1=生存)
Pclassチケットクラス
Name乗客の名前
Sex性別(male=男性、female=女性)
Age年齢
SibSpタイタニックに同乗している兄弟/配偶者の数
parchタイタニックに同乗している親/子供の数
ticketチケット番号
fare料金
cabin客室番号
Embarked出港地(タイタニックへ乗った港)

このうち、Pclass(チケットクラス)は以下を表します。

  • 1 = 上層クラス(お金持ち)
  • 2 = 中級クラス(一般階級)
  • 3 = 下層クラス(労働階級)

また、Embarked(出港地)は以下の3種類になります。

  • C = Cherbourg
  • Q = Queenstown
  • S = Southanmpton

同じようにtest.csvの中身も確認しましょう。

test.head()
\"\"

train.csvとほぼ同じカラム構成ですが、test.csvには”Survived”カラムが無いのがわかります。つまり、「予測モデルを作成してSurvivedを予測して、実際の正解と比較しましょう」というのが今回の課題になります。

次に両方のサイズを確認しましょう。

test_shape = test.shape
train_shape = train.shape
print(train_shape)
print(test_shape)
\"\"

trainは891名の乗船者情報があり、12個のカラムで構成されています。一方、testは418名の乗船者でカラムがtrainより1個少なく11個となっているのは、上で説明した\”Survivied\”が存在しないためです。

続いて、 pandasのdescribe()を使って、各データセットの統計データを確認してみましょう。

train.describe()
\"\"

countの部分を見ると、Ageで欠損があるのがわかります。

次にヒストグラムで見てみましょう。

train.hist(figsize = (12,12));
\"\"

続いてヒートマップで相関関係を見ます。

plt.figure(figsize=(10,10))
sns.heatmap(train.corr(), annot =True, square=True, vmax=1, vmin=-1, center=0,cmap=\'Blues_r\');
\"\"

PclassとFareが負の相関がみられますが、それ以外はあまりなさそうです。

性別と生存の関係性も見ましょう。

sns.countplot(x=\'Sex\' , hue = \'Survived\',data = train);
\"\"

\’Survived\’=1が生存で0が死亡ですので、女性の方が生存率が高いのがわかります。では、 Pclass(チケットクラス) でも比較してみましょう。

sns.countplot(x=\'Pclass\', hue = \'Survived\',data = train);
\"\"

Pclass=1(上層クラス)だけ生存者数が死者数を上回っていますが、 生存者数はどのクラスも同じくらいです。ただ、Pclass=3(下層クラス)の死者数が圧倒的に多い事がわかります。

最後に、これら両方を組み合わせたヒストグラムを見てみます。

sns.catplot(x=\"Pclass\", col=\"Sex\", hue=\"Survived\",data=train, kind=\"count\");
\"\"

同様にtestの方も見てみましょう。

test.describe()
\"\"

testの方は、AgeだけでなくFareにも1件欠損があるのがわかります。ヒストグラムや相関は省略します。

欠損値の確認

欠損値の確認はisnull().sum()で行います。

train.isnull().sum()
\"\"
test.isnull().sum()
\"\"

上がtrain、下がtestの欠損値の個数になります。 trainではAge、Cabin、Embarkedに、testではAge、Fare、Cabinに欠損値が存在するのがわかります。

※予測モデルの作成ではこの欠損値の扱い方で結果が大きく変わってきます。

前処理

予測モデルの作成の前に色々やっていくことがあります。その1つが欠損値の処理です。上で調べたようにtrainで欠損値を含むのは

  • Age
  • Cabin
  • Embarked

の3つですが、今回はCabinは使わないのでAgeとEmbarkedの2つの欠損値を埋めていきます。

欠損値を埋めるのにはfillna()を使用します。それではまずはAgeから始めます。今回はとりあえず、ということでAgeには平均を入れます。平均はmedian()を使います。

train[\'Age\'] = train[\'Age\'].fillna(train[\'Age\'].median())

Embarkedですが、こちらはカテゴリ変数なので最頻値を入れたいと思います。ですが、その前に、Embarkedの種類と、各変数の個数を調べてみましょう。

train[\'Embarked\'].unique()

array([\’S\’, \’C\’, \’Q\’, nan], dtype=object)

説明どおり、3種類あることがわかりました。 (※nanは値がないことを意味しています)

では次に、各変数の個数を調べます。

train[\"Embarked\"].value_counts()
\"\"

Embarkedは”S”が最も多いことがわかりましたので、今回は欠損値には”S\”を入れることにします。fillna(\”S\”)で直接入れても構いませんし、最頻値をmode()で算出して入れるのも同じです。その場合、mode()はpandas.DataFrameを返すので、iloc[0]を使ってpandas.Seriesとして取得する必要があります。

train[\'Embarked\'] = train[\'Embarked\'].fillna(\'S\')
# または下でもOK
train[\'Embarked\'] = train[\'Embarked\'].fillna(train[\'Embarked\'].mode().iloc[0])

同じようにしてtestのAgeの欠損値を埋めます。

test[\'Age\'] = test[\'Age\'].fillna(test[\'Age\'].median())

最後に、testに1件だけあったFareの欠損値も平均値で埋めます。

test[\'Fare\'] = test[\'Fare\'].fillna(test[\'Fare\'].median())
test[\'Fare\'].isnull().sum()

終わったら、欠損値がなくなっているか確認します。

train[\'Age\'].isnull().sum()
train[\'Embarked\'].isnull().sum()
test[\'Age\'].isnull().sum()
test[\'Fare\'].isnull().sum()

カテゴリ変数の変換

予測モデル作成の前にもう1つやらなければならないのが、カテゴリ変数の変換です。Embarkedのように文字列があるとエラーになってしまうため、すべて数値に変換しなければなりません。

最初に、各カラムの型を確認します。

train.dtypes
\"\"

今回使用するなかで変換が必要なのは、Sexと Embarkedになります。

pandas.get_dummies()を使って文字列を数値型に変換

カテゴリ変数を変換する際はいくつか方法はありますが、今回はpandas.get_dummies()を使います。

補足:pandas.get_dummies()とは

get_dummies()は指定したカラムの中身の値の数だけカラムを作成して、該当する箇所に1を入れます。

train_sex = train[[\'PassengerId\',\'Sex\',\'Sex\']]
train_sex.columns = columns=[\'PassengerId\',\'Sex\',\'Sex_replace\']
train_sex.head()
\"\"
train_sex = pd.get_dummies(train_sex, columns=[\'Sex_replace\'])
train_sex.head()
\"\"

このように、Sex_replaceカラムからSex_replace_femaleとSex_replace_maleの2のカラムが作成され、Sex = \’male\’の行は Sex_replace_male = 1になり、 Sex = \’female\’の行は Sex_replace_female = 1になります。

この時元のカラム(Sex_replace)は無くなりますので注意しましょう。

では、実際にSexについて行います。

train = pd.get_dummies(train, columns=[\'Sex\'])
train.head()
\"\"

Sex_femaleとSex_maleカラムが作成されているのがわかります。続いてEmbarkedも行います。

train = pd.get_dummies(train, columns=[\'Embarked\'])
train.head()
\"\"

testについても同様に行います。

test = pd.get_dummies(test, columns=[\'Sex\'])
test = pd.get_dummies(test, columns=[\'Embarked\'])
test.head()
\"\"

カテゴリ変数の変換について

「カテゴリ変数を0,1,2・・・のような数値に変換すれば?」と思った人もいるかも知れません。今回の性別のように値が2種類しかない場合は0,1でも良いのですが、3種類以上ある場合、1,2、3としてしまうと、3=1+2という関係性ができあがってしまう可能性がでてきます。それを避けるために、今回のようにpandas.get_dummies()を使うのが安全です。

不要なカラムの削除

これは最初に行っても良いのですが、今回は\”PassengerId\”, \”Name\”, \”Ticket\”, \”Cabin\”カラムは予測に使うのは難しいので、今回は削除して、それ以外の”Pclass”(チケットクラス),\”Age\”, \”Sex\”,\” SibSp\”(同乗兄弟・姉妹・配偶者者数),\”Parch\”(同乗親子者数), \”Fare\”で予測します。

ただし、最後にtestの\”PassengerId\”を使うので、これだけtest_PassengerIdに格納しておきます。

train = train.drop([\'PassengerId\',\'Name\',\'Ticket\',\'Cabin\'],axis=1)
test_PassengerId = test[\'PassengerId\']
test = test.drop([\'PassengerId\',\'Name\',\'Ticket\',\'Cabin\'],axis=1)
train.head()
\"\"
test.head()
\"\"

予測モデルの作成

データとラベルに分割

最初に、trainを訓練データと訓練ラベル(評価・結果)に分割します。

trainのうち、最初の1カラム目”Survived”が評価ラベル(=結果)になるのでこれをy_trainに、それ以外のカラムが訓練データになるのでX_trainに入れます。

X_train, y_train, X_test, y_testという変数名は予測モデルでは頻繁に出てきますので、変な名前をつけるよりはこれを毎回使うようにしたほうが間違いを避けられます。

X_train = train.iloc[:, 1:]
y_train= train.iloc[:,0]
X_train.head()
\"\"
y_train.head()
\"\"

決定木で予測モデルを作成

いよいよ、予測モデルの作成になります。 今回は「決定木」を使って予測します。

  1. DecisionTreeClassifierをインポート
  2. DecisionTreeClassifierを作成
  3. fit()に上記で作成したX_train,y_trainを引数に加えて実行
  4. predict()にtestを引数に加えて実行
from sklearn.tree import DecisionTreeClassifier
clf = DecisionTreeClassifier(random_state = 0, max_depth=4)
clf = clf.fit(X_train , y_train)
pred = clf.predict(test)
pred

結果(pred)は下記のようになります。

\"\"

結果のサイズをみると、418行生成されているのがわかります。

pred.shape
\"\"

最後に、結果をCSVに格納しますが、上記だけだとどの乗客が生存・死亡したのかがわかりませんので、先ほど保管しておいたPassengerId(test_PassengerId)と組み合わせてからCSVに出力します。ファイルはUTF-8形式で書き込みます。

df_pred = pd.DataFrame(test_PassengerId, columns=[\'PassengerId\'])
df_pred[\"Survived\"] = pred
df_pred = df_pred.set_index(\'PassengerId\')
df_pred.to_csv(\"titanic_pred.csv\", index_label = [\"PassengerId\"], encoding=\'utf-8\')

結果ファイルをテキストエディタで開くと下記のようになります。

\"\"

Kaggleで結果を検証

このCSVファイルをKaggleにアップロードして検証します。 Kaggleへログイン後、タイタニックページへ進み、以下の手順で検証を行います。

  1. 上部メニューに「Submit Predictions」という項目がありますので、こちらをクリック
  2. ファイルをアップロード
  3. 下の「Make Submission」をクリック
\"\"

数分で結果が返ってきます。右側の「Score」が正解率にいなります。今回は76.5%でした。

下にある「Jump to your position on the leaderboard 」をクリックすると、自分が世界中で何番目かがわかります。中には正解率100%の人もいるので驚きです。

\"\"

次回は、他の予測モデルで挑戦したいと思います。

]]>

コメントする

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

上部へスクロール