WEB系の技術を詳しく解説

【機械学習入門】kaggleでタイタニック号の生存者予測

機械学習を勉強する機会があったので、メモがてらブログにて記録しておこうと思います。僕は全くの機械学習初心者なのですが、kaggleでも導入として紹介されている「タイタニック号の生存者予測」が初学者向けの分析ということで、今回はそれにチャレンジしていこうと思います。

kaggle「カグル」とは

機械学習が流行し始めて結構な期間が過ぎた気がしますが、日本でも最近このkaggle「カグル」が定着してきている様です。これは世界中の機械学習やデータ分析エンジニアが集うコミュニティの名前で、コミュニティの機能のみならず「コンペ」と呼ばれる競争が行われるのも特徴です。

コンペは政府や企業が問題を配布し、賞金と引き換えに最も精度の高いモデルを買い取るという形式だそうです。

初心者向けにカーネルとか、ディスカッション等の機能もあるみたいですが、それはまた別記事にしてまとめていこうと思います。とりあえず分析したい。

kaggleからデータを取得する

kaggle公式ページから登録を済ませたら、こちらのタイタニックのページからデータをダウンロードできます。それぞれ必要なデータセットがcsv形式となっているのでそれぞれ確認していきましょう。

・gender_submission.csv :生存の是非の予測の例です。これを参考にしてcsvファイルを作成して、kaggleに提出します。

・test.csv :テストデータです。顧客情報が記載されています。この情報を元に生存の是非を予測します。

・train.csv :トレーニングデータです。顧客情報と生存の是非が記載されています。これを学習用データとして利用します。

基本的に必要なデータはtest.csv とtrain.csv のデータです。コンペとは違い、初学者学習用の問題なのでファイルサイズも非常に小さく、扱いやすいデータとなっています。

これらをダウンロードしておきます。

Pythonの仮想環境を作る

この項目は任意です。僕は練習がてら仮想環境の作成も体験したかったので

$ python -m venv titanic

として仮想環境を作成しました。titanicディレクトリに移動して、

$ source bin/activate

これで仮想環境構築は終了です。簡単。モジュールをインストールします。

#pipのアップグレード
$ pip install --upgrade pip
#NumPy(数値計算ライブラリ)
$ pip install numpy
#SciPy(科学計算ライブラリ)
$ pip install scipy
#matplotlib(グラフ描画ライブラリ)
$ pip install matplotlib
#pandas(データフレームを扱うためのライブラリ)
$ pip install pandas
#scikit-learn(基本的な機械学習)
$ pip install scikit-learn
#flake8(コードチェッカー)
$ pip install flake8

とりあえずこのくらい入れておけば大丈夫だろうという事で。

データを確認する

手始めにデータをpandas使って読み込みます。

import pandas as pd
import numpy as np
 
train = pd.read_csv("train.csv")
test = pd.read_csv("test.csv")

print(train.head())

head()は上から5行分だけデータを取ってくるメソッドです。以下、出力結果。

   PassengerId  Survived  Pclass  ...     Fare Cabin  Embarked
0            1         0       3  ...   7.2500   NaN         S
1            2         1       1  ...  71.2833   C85         C
2            3         1       3  ...   7.9250   NaN         S
3            4         1       1  ...  53.1000  C123         S
4            5         0       3  ...   8.0500   NaN         S
実際にはこの様なデータ

それぞれ項目を確認しておきます。

PassengerId – 乗客識別連番ID

Survived – 生存フラグ(0=死亡、1=生存)

Pclass – チケットクラス(1=上層クラス、2=一般クラス、3=下層クラス)

Name – 乗客の名前

Sex – 性別(male=男性、female=女性)

Age – 年齢

SibSp – タイタニックに同乗している兄弟/配偶者の数

Parch – タイタニックに同乗している親/子供の数

Ticket – チケット番号

Fare – 料金(おそらく単位は$)

Cabin – 客室番号

Embarked – 出港地(タイタニックへ乗った港。C = Cherbourg、Q = Queenstown、S = Southamptonらしい。)

これがトレーニングデータとして与えられている項目の一覧です。そして今一度、トレーニングデータとテストデータの違いについて理解しておきましょう。

※トレーニングデータとテストデータの違い

統計学、特にパターン認識や機械学習において、テストデータとは、統計手法や、学習されたモデル等の評価・検証を行うためのデータのことである。評価データとも呼ばれる。対義語に、トレーニングデータ、訓練データ、学習データ等がある。

統計学や機械学習において何らかの推定を行う際に、トレーニングデータだけでは学習されたモデルを十分に評価できないという問題がある。この問題は過剰適合や過学習と呼ばれる。この問題を解決するために、学習に用いないデータを用意し、いくつかの学習モデルの評価を行うことがある。この、評価を行うためのデータがテストデータと呼ばれる。交差検証においては、標本データをあらかじめトレーニングデータとテストデータに分割し、トレーニングデータで学習し、テストデータに学習モデルを適用させることで性能の評価を行う。

様々な学習モデルがそれぞれ別々のデータで学習している状況では、モデル同士の性能の優劣を正しく評価できない。そのため、共通のテストデータに対して性能の評価を行うことで、モデル同士の性能を比較することが行われている。

(Wikipedia「テストデータ」より引用)

よってテストデータには「Survived(生存したかどうか)」の項目がありません。tarins.csvで学習したものをtest.csvに適応させることで機械学習のスコアを算出します。なるほど。

それぞれのデータの基本統計情報の確認

pandasのdescribe() メソッドを使って基本統計情報の確認が可能です。

import pandas as pd
import numpy as np
 
train = pd.read_csv("train.csv")
test = pd.read_csv("test.csv")

print(train.describe())
print(test.describe())

以下、出力結果です。

#train.csv
       PassengerId    Survived      Pclass         Age       SibSp       Parch        Fare
count   891.000000  891.000000  891.000000  714.000000  891.000000  891.000000  891.000000
mean    446.000000    0.383838    2.308642   29.699118    0.523008    0.381594   32.204208
std     257.353842    0.486592    0.836071   14.526497    1.102743    0.806057   49.693429
min       1.000000    0.000000    1.000000    0.420000    0.000000    0.000000    0.000000
25%     223.500000    0.000000    2.000000   20.125000    0.000000    0.000000    7.910400
50%     446.000000    0.000000    3.000000   28.000000    0.000000    0.000000   14.454200
75%     668.500000    1.000000    3.000000   38.000000    1.000000    0.000000   31.000000
max     891.000000    1.000000    3.000000   80.000000    8.000000    6.000000  512.329200

#test.csv
       PassengerId      Pclass         Age       SibSp       Parch        Fare
count   418.000000  418.000000  332.000000  418.000000  418.000000  417.000000
mean   1100.500000    2.265550   30.272590    0.447368    0.392344   35.627188
std     120.810458    0.841838   14.181209    0.896760    0.981429   55.907576
min     892.000000    1.000000    0.170000    0.000000    0.000000    0.000000
25%     996.250000    1.000000   21.000000    0.000000    0.000000    7.895800
50%    1100.500000    3.000000   27.000000    0.000000    0.000000   14.454200
75%    1204.750000    3.000000   39.000000    1.000000    0.000000   31.500000
max    1309.000000    3.000000   76.000000    8.000000    9.000000  512.329200

countはデータのそのカラムの件数を表しており、train.csv ではAgeカラム、test.csv ではAgeカラムとFareカラムに欠損データ(データの抜け落ち)があることがわかります。まずはこのデータセットの欠損値の確認から作業していきます。

データ欠損を関数を作成して確認する

このデータ欠損を関数を作成して確認します。

pandas.DataFrame およびpandas.Series には欠損値の確認をするisnull() メソッドが用意されています(便利すぎないか)。欠損値NaN (数値型)であればTrue 、そうでなければFalse が返ります。元のオブジェクトと同じサイズのオブジェクトを返すので欠損値の確認に使うにはもってこいの関数です。

isnull() からそのままを出力すると膨大なテーブルデータが返ってくるので関数を使ってデータを整形します。

def missing_value_table(df):
        #欠損値がそのカラムにいくつあるかを出力する
        null_val = df.isnull().sum()
        #それぞれのカラムで欠損値がどのくらいの割合を占めているかを計算する
        percent = 100 * df.isnull().sum()/len(df)
        #pandasのconcatメソッドで欠損数とその割合をaxis=1方向に連結する
        missing_table = pd.concat([null_val, percent], axis=1)
        #pandas.DataFrameの行名・列名を変更する
        missing_table_ren_columns = missing_table.rename(
        columns = {0 : '欠損数', 1 : '%'})
        return missing_table_ren_columns

プログラムを以下の様に修正して動作するか確認します

import pandas as pd
import numpy as np
 
train = pd.read_csv("train.csv")
test = pd.read_csv("test.csv")

def missing_value_table(df):
  #欠損値がそのカラムにいくつあるかを出力する
  null_val = df.isnull().sum()
  #それぞれのカラムで欠損値がどのくらいの割合を占めているかを計算する
  percent = 100 * df.isnull().sum()/len(df)
  #pandasのconcatメソッドで欠損数とその割合をaxis=1方向に連結する
  missing_table = pd.concat([null_val, percent], axis=1)
  #pandas.DataFrameの行名・列名を変更する
  missing_table_ren_columns = missing_table.rename(
  columns = {0 : '欠損数', 1 : '%'})
  return missing_table_ren_columns

print(missing_value_table(train))
print(missing_value_table(test))

以下出力結果。

             欠損数          %
PassengerId    0   0.000000
Survived       0   0.000000
Pclass         0   0.000000
Name           0   0.000000
Sex            0   0.000000
Age          177  19.865320
SibSp          0   0.000000
Parch          0   0.000000
Ticket         0   0.000000
Fare           0   0.000000
Cabin        687  77.104377
Embarked       2   0.224467
             欠損数          %
PassengerId    0   0.000000
Pclass         0   0.000000
Name           0   0.000000
Sex            0   0.000000
Age           86  20.574163
SibSp          0   0.000000
Parch          0   0.000000
Ticket         0   0.000000
Fare           1   0.239234
Cabin        327  78.229665
Embarked       0   0.000000

この結果からどちらのデータもAgeとCabinの項目に特に欠損値が多いことが目立つことがわかります。一目瞭然ですね!

データセットの事前処理をする

さて、データの確認ができたところで機械学習の肝とも言える「データの事前処理・整形」についてやっていこうと思います。機械学習をしていく上で非常に重要なフェーズです。

今回は

・欠損データを自分で決めた代理データに入れ替える

・文字列カテゴリカルデータを数値へ変換する

この2つをやっていこうと思います。

train.csvの欠損データに代理データを入れる

trainデータでは、「Age(年齢)」「Cabin(客室番号)」「Embarked(出港地)」の項目に欠損値がありました。うち、予測データに必要ないと思われるのは「Cabin」カラムです。(部屋番号は生存確率に関係しないと考えて。予測モデルに使用しても問題ない。)今回は「Age」と「Embarked」カラムの欠損値に代理データを入れていきます。

・「Age」にはtrainデータの全データの中央値を入れてみます。

・「Embarked」には最頻値であるSを代入します。

今回は単純にこのようなルールで欠損値を埋めていきます。コードは以下。

# 欠損値を穴埋めする
train["Age"] = train["Age"].fillna(train["Age"].median())
train["Embarked"] = train["Embarked"].fillna("S")

pandasのfillna() メソッドで欠損値の穴埋めを行うことができます。

             欠損数          %
PassengerId    0   0.000000
Survived       0   0.000000
Pclass         0   0.000000
Name           0   0.000000
Sex            0   0.000000
Age            0   0.000000
SibSp          0   0.000000
Parch          0   0.000000
Ticket         0   0.000000
Fare           0   0.000000
Cabin        687  77.104377
Embarked       0   0.000000

Cabinカラム以外の欠損値がなくなったことが確認できます。

文字列を数値に変換する

欠損データの修正が終わったので、次にカテゴリカルデータの文字列を数値に変換するという作業を行います。こうすることでカラムに使われている文字列を数値で定義できるので、それらのカラムを機械学習に利用できる様になります。

#文字列を数値に変換する
train["Sex"][train["Sex"] == "male"] = 0
train["Sex"][train["Sex"] == "female"] = 1
train["Embarked"][train["Embarked"] == "S" ] = 0
train["Embarked"][train["Embarked"] == "C" ] = 1
train["Embarked"][train["Embarked"] == "Q"] = 2

to_csv() を使って確認してみました。きちんと数値に変換されています。

test.csvにも同様の作業をする

train.csv に行なった作業をtest.csv に対しても同様に適用します。

#testデータに対しても同様に処理を行う
test["Age"] = test["Age"].fillna(test["Age"].median())
test["Sex"][test["Sex"] == "male"] = 0
test["Sex"][test["Sex"] == "female"] = 1
test["Embarked"][test["Embarked"] == "S"] = 0
test["Embarked"][test["Embarked"] == "C"] = 1
test["Embarked"][test["Embarked"] == "Q"] = 2
#Fareには同様に中央値で欠損データを補完
test.Fare[152] = test.Fare.median()

testデータには1つだけ欠損値があったので、それに対して直接同カラムの中央値を入れておきます。

これでデータの前処理は終わりです!正解はないので、あくまでも一例として捉えてみて下さい。

予測モデルの決定

データの整形が終わったので、予測モデルを決定します。今回は機械学習のベースともなる予測モデルである「決定木」を使って機械学習をします。決定木は、Python機械学習の有名ライブラリである「scikit-learn(サイキット・ラーン)」を使うと手軽に実装できます。

from sklearn import tree  としてscikit-learnをインポートしておきます。tree は、決定木による分類が実装されているクラスです。

説明変数と目的変数

訓練データから説明変数目的変数を決定します。

説明変数とは、目的変数を説明する変数のことで「独立変数」とも呼ばれます。物事の原因と捉えることもできます。

対して目的変数とは、予測したい変数のことです。「従属変数」「外的基準」とも呼ばれます。物事の結果とする変数なので、今回のケースでは「Survived(生存したかどうか)」が目的変数に対応するカラムになります。

#目的変数と説明変数を決定して取得
target = train["Survived"].values
explain = train[["Pclass", "Sex", "Age", "Fare"]].values

決定木の作成をして、学習させる

scikit-learnでは決定木を用いた分類器は、sklearn.tree.DecisionTreeClassifierというクラスで実装されています。これを使うだけで勝手に決定木を作成してくれるので楽チン。

#決定木の作成
d_tree = tree.DecisionTreeClassifier()
#fit()で学習させる。第一引数に説明変数、第二引数に目的変数
d_tree = d_tree.fit(explain, target)

fit() メソッドで決定木を用いて学習させます。

決定木を用いて予測する

予測モデルを作成したので、これをテストデータに適応させます。

#testデータから説明変数を抽出
test_explain = test[["Pclass", "Sex", "Age", "Fare"]].values
#predict()メソッドで予測する
prediction = d_tree.predict(test_explain)

#出力結果を確認する
#予測データのサイズ
print(prediction.shape)
#予測データの中身
print(prediction)

出力結果は以下の様になりました。

(418,)
[0 0 1 1 1 0 0 0 1 0 0 0 1 1 1 1 0 1 1 0 0 1 1 1 1 0 1 1 1 0 0 0 1 0 1 0 0
 0 0 1 0 1 0 1 1 0 0 0 1 1 1 0 1 1 1 0 0 0 1 1 0 0 0 1 0 0 1 0 0 1 1 0 1 0
 1 0 0 1 0 1 1 0 0 0 0 0 1 1 1 1 1 1 1 0 0 0 1 1 1 0 1 0 0 0 1 0 0 0 0 0 0
 0 1 1 1 0 1 1 0 1 1 0 1 0 0 1 0 1 0 0 1 0 0 1 0 0 1 0 0 0 0 0 0 0 0 1 1 0
 1 0 1 0 0 1 0 0 1 1 0 1 1 1 1 1 0 1 1 0 0 0 0 1 0 1 0 1 1 0 1 1 0 0 1 0 1
 0 1 0 0 0 0 0 1 0 1 0 1 0 0 0 0 1 0 1 0 0 0 0 1 0 1 1 0 0 0 0 1 0 1 0 1 0
 1 1 1 0 0 1 0 0 0 1 0 0 1 0 0 1 1 1 1 1 1 0 0 0 1 0 1 0 1 0 0 0 0 0 0 0 1
 0 0 0 1 1 0 0 0 0 0 0 0 0 1 0 1 1 0 0 0 0 0 1 1 0 1 0 0 0 1 0 1 0 1 0 0 0
 1 0 0 0 0 0 0 0 1 1 0 1 1 0 0 1 0 0 1 1 0 0 0 0 0 0 0 1 1 0 1 0 0 0 1 0 1
 1 0 0 0 0 0 1 0 0 0 1 0 1 0 0 0 1 1 0 0 0 1 0 1 0 0 1 0 1 1 1 1 0 0 0 1 0
 0 1 0 0 1 1 0 0 0 1 0 0 0 1 0 1 0 0 0 0 0 1 1 0 0 1 0 1 0 0 1 0 1 1 0 0 0
 0 1 1 1 1 0 0 1 0 0 0]

0と1で生存のチェックをしています。

予測データをkaggleに提出する

とりあえず簡単に予測データが取得できたので、これをCSVファイルに出力してkaggleに提出し、スコアを見てみます!要求されるデータフレームのカラムにはPassengerIdが必要なので、それを合わせてCSVファイルに出力します。

# PassengerIdを取得
PassengerId = np.array(test["PassengerId"]).astype(int)
# 予測データとPassengerIdをデータフレームにて結合
result = pd.DataFrame(prediction, PassengerId, columns = ["Survived"])
# result.csvとして書き出し
result.to_csv("result.csv", index_label = ["PassengerId"])
この様なCSVデータが出来上がる

出力されたresult.csv をkaggleに提出します。

「Submit Predictions」からCSVファイルを提出するページに遷移できます。

提出するとスコアリングが始まります。

ファイルがkaggleの投稿基準を満たしていると、スコアリングを自動的に実行し、スコアを算出してくれます。今回の結果はスコア0.72248で、順位は9226位でした(案外上の方の順位と思ったのですが笑)

すなわち今回は72.248%の確率で正解を予測できたことと同値です。機械学習ではデータの整形とアルゴリズムの決定によりより高い精度でスコアを叩き出すのが競い合う点であり、かつ魅力的な点です。

おわりに

今回は簡単なモデルを用いて、機械学習を学びました。いかがだったでしょうか。

実際にkaggleの「タイタニック号の生存者予測」の課題からスコアを算出することで機械学習の流れも掴めたので良かったと思います。

時間のある時にスコアを上げる努力をしてみます。本記事が機械学習の勉強の足がかりにでもなれば幸いです!

⇨より深い分析をしてスコアを上げる努力をしてみました。

kaggleのタイタニック号生存者予測を、より深い分析を使ってスコアを上げる

機械学習(AI)を学べるプログラミンングスクールとして「テックブースト 」さんが挙げられます。

一人では理解が難しい機械学習のアルゴリズムやライブラリの使い方まで分かりやすく受講する事ができますよ!無料カウンセリングも行なっているので気になる方は是非。

テックブーストとは

スポンサードリンク

コメントを残す

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