コンテンツにスキップ

第9章 機械学習の実際

1 テーマ

テーマは豪華客船タイタニック号です。乗客名簿から、どのような特徴を持つ乗客が生き残れたのかを分析します。

まずデータを見てみましょう。 ファイル titanic.ipynb に以下を記述します。

import pandas as pd
df = pd.read_csv("titanic.csv")
df.head()

以下のようなデータです。

  • 生存 1・・生存 0・・死亡
  • 客室 1~3等客室
  • 性別 male:男性 female 女性
  • 年齢
  • 兄弟配偶者 乗船した兄弟/配偶者の人数
  • 親子 乗船した親子の人数
  • 料金
  • 客室番号
  • 出発地 C=Cherbourg,Q=Queenstown,S=Southampton

2 前処理

まず、欠損値がある列があるかを確認します。

df.isnull().sum()

年齢、客室番号、出発地に欠損データがあるようです。 年齢は平均値で埋めましょう。

heikin = df["年齢"].mean()
df["年齢"] = df["年齢"].fillna(heikin)
df.isnull().sum()

客室番号は欠損地が非常に多いため今回は削除します。

df = df.drop('客室番号',axis=1)
df.head()

出発地が無いデータは2件しか無いため、その2件は削除します。

df = df.dropna(subset=["出発地"])
df.isnull().sum()

また、性別データ、出発地は文字列のため、これを数値(0と1)に変換します。

性別は性別IDという列を作り、groubyしてngroupで1または0を代入します。

df['性別ID'] = df.groupby(['性別']).ngroup()
df

出発地はC,Q,Sの3パターンあるので、ngroupを使うと0,1,2 に変換されてしまいます。出発地は量的データではなく、質的データですので、1+1=2 などの計算はできません。 そこで、pd.get_dummiesを使用し、各値について0と1に変換します。これにより、C、Q、Sのそれぞれの列ができます。

x = pd.get_dummies(df['出発地'], dtype='uint8')
x

ただし、列は2列で十分です。なぜなら2つの列が0なら残り1列が1となるからです。 このため、最初の列(C)は削除します。

x = pd.get_dummies(df['出発地'], drop_first=True, dtype='uint8')
x

できた列を元のdfに連結します。

df = pd.concat([df, x], axis=1)
df

3 モデルの構築と予測

列名を表示してみます。

df.columns

学習データとしてどの列を使うかを決めます。 今回は正解ラベルは「生存」列、特徴量は全ての列を選択してみました。

# 特徴量
x = df[['客室', '年齢', '兄弟配偶者', '親子', '料金', '性別ID', 'Q', 'S']]

# 正解ラベル
y = df["生存"]

データを学習用とテスト用に分けて、今回はランダムフォレストで学習します。 テスト用データを全て予測し、結果を見てみましょう。

from sklearn.model_selection import train_test_split

x_train, x_test, y_train, y_test = train_test_split(x, y, test_size = 0.2, random_state=0)

from sklearn.ensemble import RandomForestClassifier
model = RandomForestClassifier(n_estimators=200,max_depth=10,random_state=0)

model.fit(x_train, y_train)
model.score(x_test, y_test)

4 重要度の分析

この時点で何が生存に影響を与えたのかを探ることが出来ます。 決定木の場合、model.feature_importances_ で特徴量の影響度が分かるのでした。

特徴量の列名と組み合わせてグラフで表示してみましょう。

import matplotlib.pyplot as plt
import seaborn as sns
sns.set(font=["Meiryo"])

plt.barh(x_train.columns, model.feature_importances_)

性別が大きな影響を与えたと分かります。

性別ごとに生存者数を棒グラフに出してみましょう。seabornのcountplotで可能です。

sns.countplot(x='性別', hue='生存', data=df)
plt.title('性別ごとの生存人数')
plt.show()

5 チューニング

さらに正解率を上げるにはどうしたらよいでしょうか。一つの方法は、より結果に関わる項目だけで予測することです。

ということで、影響度が低い「兄弟配偶者」「親子」「Q」「S」を省き、「客室」「年齢」「料金」「性別ID」だけで予測してみます。

x = df[['客室', '年齢', '料金', '性別ID']]
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size = 0.2, random_state=0)
model.fit(x_train, y_train)
model.score(x_test, y_test)

より高い確率で予測できるようになりました。

このときの重要度を表示するとまた違う結果がでているはずです。

pd.Series(model.feature_importances_, x_train.columns)