第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)