忍者ブログ
統計、機械学習、AIを学んでいきたいと思います。 お役に立てば幸いです。

【Kaggle挑戦記】Titanic 攻略 #5:特徴量への Fare(運賃)追加と精度向上

これまでの攻略を経て、私たちは重要な2つの結論を導き出しました。

  • #3 の結論(データ分析): 相関分析の結果、生存率に最も影響を与えるのは「性別(Sex)」だが、それに次いで「運賃(Fare)」も強い相関があることが判明。
  • #4 の結論(モデル理解): ランダムフォレストの内部を可視化した結果、100本の木による「合議制(多数決)」が論理的に機能していることを確認。

今回の戦略は極めてシンプルです。
「信頼できるアルゴリズム(#4)に、より生存率と関連の深いデータ項目(#3)を投入すれば、予測精度は向上するはずだ」
という仮説を検証します。

1. 「特徴量(とくちょうりょう)」への納得感

AIの予測ヒントにするデータ項目を「特徴量」と呼びますが、今回選んだ項目には人間が見ても直感的な納得感があります。

  • Pclass(客室クラス): 1等客なら避難が優先されたのではないか?
  • Sex(性別): 女性が優先的に救命ボートに乗ったのではないか?
  • Fare(運賃): 高い運賃を払ったVIP客ほど、手厚いサポートがあったのではないか?

このように、背景にあるストーリーとデータが結びつくことで、AIの予測モデルはより説得力のあるものになります。

2. 【実装】特徴量を 5 項目に増強したソースコード

これまでの 4 項目に、相関係数の高かった "Fare"(運賃) を追加します。テストデータ(test.csv)に1件だけ存在する「欠損値(空欄)」を中央値で埋める対策も1行追加しています。

import pandas as pd
from sklearn.ensemble import RandomForestClassifier

# 1. データの読み込み
train_data = pd.read_csv('train.csv')
test_data = pd.read_csv('test.csv')

# 2. 特徴量の選択(Fare を追加して 5 項目に!)
features = ["Pclass", "Sex", "SibSp", "Parch", "Fare"]

# カテゴリ変数(Sex)を数値に変換
X = pd.get_dummies(train_data[features])
y = train_data["Survived"]

# 重要:テストデータの Fare に1件だけある欠損値を中央値で補完する
X_test = pd.get_dummies(test_data[features])
X_test['Fare'] = X_test['Fare'].fillna(X_test['Fare'].median())

# 3. モデルの構築と学習(#4 で信頼性を確認したランダムフォレスト)
model = RandomForestClassifier(n_estimators=100, max_depth=5, random_state=1)
model.fit(X, y)

# 4. 予測の実行
predictions = model.predict(X_test)

# 5. 提出用ファイルの作成
output = pd.DataFrame({'PassengerId': test_data.PassengerId, 'Survived': predictions})
output.to_csv('submission_fare.csv', index=False)
print("Submission with Fare saved!")

3. 結果の考察:データ項目増加の効果

実際にこのコードで予測を行い Kaggle に提出したところ、スコアに以下の変化が現れました。

  • 4 項目(前回): Score 0.76749
  • 5 項目(今回): Score 0.77511(微増!)

考察:
わずかな向上ですが、エンジニアリングにおける重要な原則「良質なデータを入れれば、良質な結果が出る」ことが実証されました。アルゴリズム自体は変えなくても、納得感のある「特徴量」を正しく追加することで、着実に正解率が上がることが確認できました。


仮説は正しかったと言えます。しかし、まだ生存率に大きく関わりそうなデータ項目が残っています。それは「年齢(Age)」です。ただし、年齢データには「欠損値」が非常に多いという大きな壁があります。次回、この欠損値処理(インピュテーション)に挑戦します。


PR

【Kaggle挑戦記】Titanic 攻略 #4:ランダムフォレストの正体

第2回では「最短ルート」での初提出を優先し、モデルの選定理由は「なんとなく」進めてきました。ここでは、そこで使用したモデルの正体を明らかにするとともに、その実行過程(個別の判断ルール)を可視化するための改良コードを整理します。

1. 【振り返り】前回のソースコード

まずは、第2回で実際に提出ファイルを生成したベースラインのソースコードを正確に振り返ります。この時点では「Pclass, Sex, SibSp, Parch」の4項目を特徴量としていました。

import pandas as pd
from sklearn.ensemble import RandomForestClassifier

# 1. データの読み込み
train_data = pd.read_csv('train.csv')
test_data = pd.read_csv('test.csv')

# 2. 特徴量の選択と前処理
features = ["Pclass", "Sex", "SibSp", "Parch"]
# get_dummiesでカテゴリ変数(Sex)を数値(Sex_male, Sex_female)に変換
X = pd.get_dummies(train_data[features])
y = train_data["Survived"]
X_test = pd.get_dummies(test_data[features])

# 3. モデルの構築と学習(100本の決定木を作成)
model = RandomForestClassifier(n_estimators=100, max_depth=5, random_state=1)
model.fit(X, y)

# 4. 予測の実行
predictions = model.predict(X_test)

# 5. 提出用ファイルの作成
output = pd.DataFrame({'PassengerId': test_data.PassengerId, 'Survived': predictions})
output.to_csv('submission.csv', index=False)
print("Your submission was successfully saved!")

2. 手法の解説:二値分類とランダムフォレスト

今回のタイタニック課題は、乗客が「生存したか、死亡したか」という二値分類問題を解くものです。代表的な手法には「ロジスティック回帰」「SVM」「ランダムフォレスト」「勾配ブースティング」などがありますが、今回は安定性が高くベースライン作りに適したランダムフォレストを採択しました。

3. 【改良】実行過程を可視化するソースコード

「100本の木による多数決」という実行プロセスを正確に把握するため、可視化用のライブラリをインポートし、model.fit(X, y) のすぐ下に処理を追加します。

import pandas as pd
from sklearn.ensemble import RandomForestClassifier
from sklearn.tree import export_text # 可視化用のインポートを追加

# --- 前処理・モデル構築(前回と同じ) ---
train_data = pd.read_csv('train.csv')
features = ["Pclass", "Sex", "SibSp", "Parch"]
X = pd.get_dummies(train_data[features])
y = train_data["Survived"]
model = RandomForestClassifier(n_estimators=100, max_depth=5, random_state=1)

# 3. モデルの学習
model.fit(X, y)

# --- model.fit(X, y) の直下に可視化処理を追加 ---
# 100本の木から最初の1本(index=0)を取り出す
estimator = model.estimators_[0]

# 判断ルールをテキスト形式で出力(feature_namesを指定)
tree_rules = export_text(estimator, feature_names=list(X.columns))
print("--- 最初の1本の決定木による判断プロセス ---")
print(tree_rules)
# --- ここまで追加 ---

# 以降、予測の実行(前回と同じ)
# predictions = model.predict(X_test)

実行時の出力サンプル:

--- 最初の1本の決定木による判断プロセス ---
|--- Sex_female <= 0.50
| |--- SibSp <= 2.50
| | |--- Pclass <= 1.50
| | | |--- Parch <= 3.00
| | | | |--- Parch <= 1.50
| | | | | |--- class: 0.0
| | | | |--- Parch > 1.50
| | | | | |--- class: 1.0
| | | |--- Parch > 3.00
| | | | |--- class: 0.0
| | |--- Pclass > 1.50
| | | |--- Parch <= 0.50
| | | | |--- Pclass <= 2.50
| | | | | |--- class: 0.0
| | | | |--- Pclass > 2.50
| | | | | |--- class: 0.0
| |--- SibSp > 2.50
| | |--- class: 0.0

4. 【考察】最も精度の高い木が選ばれるのか?

ここで重要な疑問が浮かびます。「これら100本の木の中から、テストデータに対して最も精度の高い1本が選ばれて予測に使われるのか?」という点です。

答えは「NO」です。ランダムフォレストの本質は「選別」ではなく「合議制(多数決)」にあります。

  • アンサンブル学習: 1本の完璧な木を探すのではなく、少しずつ異なる視点を持つ100本の木全員に予測をさせます。
  • 多数決の原理: 例えば、60本の木が「死亡」、40本の木が「生存」と予測したら、最終回答は「死亡」となります。
  • なぜそうするのか: 1本の優秀すぎる木は、学習データに依存しすぎる(過学習)リスクがあります。多様な視点を持つ木々の平均をとることで、未知のデータに対しても安定した予測が可能になるのです。

手法の実行プロセスと「多数決」の論理を理解したことで、モデルの挙動に対する納得感が得られました。次は第3回で算出した相関係数に基づき、さらなる精度向上を目指します。

【Kaggle挑戦記】Titanic 攻略 #3:データの「当たり」をつける

最短ルートで初提出(スコア:0.76794)を終えた段階で、次に行うべきは、どのデータ(特徴量)が生存に直結しているのか、論理的な仮説と数値で切り分ける作業です。

1. 仕様確認:データ項目の定義一覧

まずは手元にあるテーブルの定義を正確に把握し、全12項目の中で何が利用可能かを確認します。

項目名 (Column)意味 (Description)型 (Type)補足 (Notes)
PassengerId 乗客ID int 連番。予測には使わない主キー
Survived 生存フラグ int 【正解データ】 0=死亡、1=生存
Pclass チケットクラス int 1=上層(1等)、2=中層(2等)、3=下層(3等)
Name 氏名 object 文字列
Sex 性別 object male / female
Age 年齢 float 欠損値あり
SibSp 兄弟・配偶者数 int 同乗している兄弟や配偶者の数
Parch 両親・子供数 int 同乗している両親や子供の数
Ticket チケット番号 object 文字列
Fare 旅客運賃 float 数値データ
Cabin 客室番号 object 欠損値多
Embarked 出港地 object C / Q / S

2. 仮説:項目の切り分け

各項目が生存に関係しそうかどうかを、とりあえず、勘で事前に切り分けます。

項目名 (Column)関連性の推論
Sex ありそう
Pclass ありそう
Age ありそう
Fare ありそう
SibSp / Parch ありそう
Name なさそう
Embarked なさそう
Ticket / Cabin なさそう
PassengerId なさそう

3. 検証:相関分析の実行(完全なソースコード)

「ありそう」と踏んだ項目について、実際に Survived(生存率) との関係性を算出します。汎用性が高く、まずは全体像を把握するために適しているためピアソンの積率相関係数を採用する。

import pandas as pd

# 1. データの読み込み
train_df = pd.read_csv('train.csv')

# 2. 相関計算のため「文字列」を「数値」に一時変換(エンコード)
train_encoded = train_df.copy()
train_encoded['Sex'] = train_encoded['Sex'].map({'male': 0, 'female': 1})

# 3. 算出対象の項目選定(「ありそう」と踏んだ項目に限定)
target_columns = ['Survived', 'Pclass', 'Sex', 'Age', 'SibSp', 'Parch', 'Fare']

# 4. 相関係数(ピアソン)の算出
pearson_corr = train_encoded[target_columns].corr(method='pearson')

# 5. 「Survived」との相関を表示
print("--- Survived との相関係数(ピアソン) ---")
print(pearson_corr['Survived'].sort_values(ascending=False))

【 分析結果:生存率との相関係数 】

  • Sex:0.543
  • Fare:0.257
  • Pclass:-0.338
  • Age:-0.077
  • Parch:0.081
  • SibSp:-0.035

4. 前回プログラムの振り返り

第2回の最短ルートで使用した特徴量と、今回の相関計算の結果を照らし合わせます。

# 前回の特徴量選択
features = ["Pclass", "Sex", "SibSp", "Parch"]

前回は「ありそう」と推測した項目のうち、FareAge を除いた 4項目を特徴量として利用しました。今回の計算結果を見ると、利用しなかった Fare が一定の相関(0.25)を示しており、これを特徴量に加えることでモデルの判断材料がどう変化するかを試す価値はありそうです。

また、Age については相関係数こそ低いですが、欠損値を適切に補完した上で投入することで、どのような変化が出るかを検証する必要があります。



【Kaggle挑戦記】Titanic 攻略 #2:最短ルートで初提出(Submit)まで駆け抜ける

前回、データを読み込むところまで成功しました。今回は精度を一旦度外視し、「最短で予測結果を Kaggle に提出(Submit)すること」を目標にします。分析の世界では、まず一度最後まで通してみることが、全体の流れを把握する近道です。

1. 準備:環境構築とデータの再確認

まずは必要なライブラリの導入と、前回ロードしたデータの状態を再読込して準備を整えます。

[ ターミナルでのインストール ]
conda install scikit-learn

[ ライブラリのインポートとバージョン確認 ]
import pandas as pd
import sklearn
print(sklearn.__version__) # 1.7.2 であることを確認

[ データの読み込み(前回の復習) ]
train_df = pd.read_csv('train.csv')
test_df = pd.read_csv('test.csv')

2. 現状把握:AIの「計算」を阻害する Null を特定する

データを読み込んだら、次にすべきはデータの「健康診断」です。プログラムの演算を停止させたり、予測不能に陥らせたりする「Null(欠損値)」「文字列(Object型)」を特定します。

【 1. Null の確認:isnull() と sum() の組み合わせ 】

データセット内に値が存在しない状態を Null と呼びます。pandas の isnull() 関数を実行すると、全データに対して「Null なら True(1)」「値があれば False(0)」というブール値(論理値)のデータセットが内部で生成されます。

これに sum() を繋げることで、True(1) の数を合計し、各項目にいくつ Null が含まれているかを一気に集計できる仕組みです。

# 各項目に含まれる Null の数を集計
print(train_df.isnull().sum())

[ 実行結果 ]
Age 177 # ← 177件の Null(年齢不明)
Cabin 687 # ← 687件の Null(客室不明)
Embarked 2 # ← 2件の Null(港不明)

→ 数値が入るべき場所に値がない(Null)状態では、AIモデルが数学的な行列演算を行えずエラーとなります。何らかの値で埋めるか、除外する判断が必要です。

【 2. データ型(dtypes)の確認:数値化が必要な項目 】

# データの型を確認
print(train_df.dtypes)

[ 実行結果 ]
Name object
Sex object # ← 文字列型。数値への変換が必須
Embarked object # ← 文字列型。数値への変換が必須

object 型と表示される項目は、そのままではモデルの学習には使えません。

3. 最短の「前処理」:AIが読める形に整える

特定した課題に対し、今回は「最短完走」を優先して以下の処理を施します。object 型のうち、今回の予測に採用する Sex のみ数値に置換します。

# 1. 欠損値を埋める(Fare:運賃 などの空欄を中央値で補完)
# ※学習項目に使わない Age や Cabin の Null は一旦放置します
test_df['Fare'] = test_df['Fare'].fillna(test_df['Fare'].median())

# 2. 文字列を数値に変換(Sex を male=0, female=1 に変換)
# ※Embarked も object型ですが、今回は特徴量に含めないため変換をスキップします
train_df['Sex'] = train_df['Sex'].replace({'male': 0, 'female': 1})
test_df['Sex'] = test_df['Sex'].replace({'male': 0, 'female': 1})

4. 学習と予測:標準モデル「ランダムフォレスト」

予測モデルには、強力で使い勝手の良い RandomForestClassifier を使用します。

from sklearn.ensemble import RandomForestClassifier

# Pclass(チケットクラス)、Sex(性別)、SibSp(兄弟・配偶者数)、Parch(両親・子供数)を使用
# これらは Null がなく、かつ数値化が済んでいる項目です
features = ["Pclass", "Sex", "SibSp", "Parch"]
X = train_df[features]
y = train_df["Survived"]
X_test = test_df[features]

# モデル作成・学習
model = RandomForestClassifier(n_estimators=100, max_depth=5, random_state=1)
model.fit(X, y)

# 予測実行
predictions = model.predict(X_test)

5. 出力結果の確認:submission.csv の中身

予測が完了すると、Kaggle 提出用の CSV ファイルが生成されます。中身を確認すると、テストデータの乗客(PassengerId 892番〜)に対して、AIが導き出した「生存(1) / 死亡(0)」のフラグが並んでいることがわかります。

PassengerId,Survived
892,0
893,1
894,0
895,0
896,0
897,0
898,1

6. 実践:Kaggle へのアップロードとスコア確認

作成した submission.csv を Kaggle に提出する最終ステップです。

  1. Titanic コンペのページ右上にある青い [Submit Predictions] ボタンをクリックします。
  2. 専用の提出ページに移動するので、画面中央の「Upload files」エリアに submission.csv をドラッグ&ドロップします。
  3. 右下の [Submit] をクリックします。

数秒後、同じ画面上に以下の結果が表示されます。

submission.csv
Complete · Just now
Score: 0.76794

【 自分の順位を確認する 】

提出履歴から Leaderboard(順位表)へ移動し、自分の立ち位置を確認してみましょう。

9065 XXXX(私の登録名)
Score: 0.76794

順位表を見ると、トップ層には Score 1.00000(正解率100%) がズラリと並んでいます。今の自分とはまだ距離がありますが、最短ルートで完走したことで、戦いの舞台には上がりました。ここからロジックを磨き、上位へと食い込んでいきましょう。頑張ろう、エンジニア諸君!

7. 最短完走:全ソースコード

ここまでの手順を一つにまとめた、提出用ファイルを生成するための全コードです。

import pandas as pd
from sklearn.ensemble import RandomForestClassifier

# 1. データの読み込み
train_df = pd.read_csv('train.csv')
test_df = pd.read_csv('test.csv')

# 2. 最小限の前処理
test_df['Fare'] = test_df['Fare'].fillna(test_df['Fare'].median())
train_df['Sex'] = train_df['Sex'].replace({'male': 0, 'female': 1})
test_df['Sex'] = test_df['Sex'].replace({'male': 0, 'female': 1})

# 3. 予測モデルの作成
# 項目:チケットクラス、性別、兄弟配偶者数、親子数(これらはNullなし・数値済み)
features = ["Pclass", "Sex", "SibSp", "Parch"]
X = train_df[features]
y = train_df["Survived"]
X_test = test_df[features]

model = RandomForestClassifier(n_estimators=100, max_depth=5, random_state=1)
model.fit(X, y)

# 4. 予測と保存
predictions = model.predict(X_test)
output = pd.DataFrame({'PassengerId': test_df.PassengerId, 'Survived': predictions})
output.to_csv('submission.csv', index=False)

print("Your submission was successfully saved!")

8. まとめ

今回の最短攻略ソースはこれでした!「なぜエラーが出るのか」「何を直すべきか」を特定し、最短で Kaggle にコミットするサイクルを完走しました。これだけで、あなたも立派な Kaggler の仲間入りです。

次回は、今回あえてスルーした「年齢(Age)」の Null をどう埋めるべきか、あるいは「港(Embarked)」をどうモデルに組み込むか。精度向上のための「EDA(探索的データ分析)」に挑戦しましょう!

【Kaggle挑戦記】Titanic 攻略 #1:Mac環境構築とデータロードの第一歩

Kaggle の「Titanic: Machine Learning from Disaster」は、機械学習を学ぶ誰もが通る登竜門です。1912年に起きたタイタニック号の沈没事故を題材に、乗客の「年齢」「性別」「客室の等級」などのデータから、その乗客が生き残るか、それとも犠牲になるか(生存予測)を当てるという、非常に明確な目的を持ったコンペティションです。

今回は、macOS 15.7.4Miniconda 環境(Python 3.13.5)を使い、データを読み込んで分析の土俵に上がるまでの手順を整理していきましょう。

1. 準備:データの入手と配置

【 手順 】 まずは Kaggle の公式サイトにログインし、以下のリンクから Titanic コンペティションのページへアクセスします。

URL: https://www.kaggle.com/competitions/titanic/data

「Data」タブから 「Download All」 をクリックして ZIP ファイルをダウンロードし、作業用フォルダ(例:~/Documents/kaggle_titanic/)に解凍します。中には以下の3つの重要なファイルが入っています。

【 ダウンロードファイルの内訳 】

train.csv : 学習用データ。乗客の属性と「生存(1)・死亡(0)」の正解が含まれています。
test.csv : 予測用データ。このデータに対して、生存・死亡を予測します。
gender_submission.csv : 提出用サンプル。「女性は全員生存、男性は全員死亡」と仮定した、提出ファイルのフォーマット見本です。

2. 深掘り:実装前に「データ定義」を読み解く

コードを書く前に、Data ページにある Data Dictionary をよく読み、各カラム(変数)が何を意味しているかを理解しましょう。これが予測の精度を左右します。

VariableDefinitionKey
survival 生存か否か 0 = No, 1 = Yes
pclass チケットのクラス(社会経済的ステータス) 1 = 1st(Upper), 2 = 2nd(Middle), 3 = 3rd(Lower)
sex 性別
Age 年齢(1歳未満は分数、推定値はxx.5)
sibsp 同乗している兄弟・配偶者の数
parch 同乗している両親・子供の数
ticket チケット番号
fare 旅客運賃
cabin 客室番号
embarked 出港地(乗船した港) C=Cherbourg, Q=Queenstown, S=Southampton

【 ここが分析のヒント! 】

  • 家族構成: sibspparch を合わせることで、「家族連れか、単身か」という新しい特徴が見えてきます。※乳母と同行した子供は parch=0 になるという注意書きも重要です。
  • ステータス: pclass は単なる座席ランクではなく、当時の社会的な地位を反映しています。
  • 推定値: 年齢(Age)に 28.5 のような値がある場合、それは「推定値」であることを意味します。

3. 実装:Miniconda 環境で Python を動かす

今回の環境は Python 3.13.5 です。ターミナルを開き、Miniconda で pandas をインストールしてから、バージョン確認とデータの読み込みを行います。

【 環境構築とコードの実行イメージ 】

[ ターミナルでの準備 ]
conda install pandas
python --version # Python 3.13.5 であることを確認

[ ライブラリのバージョン確認 ]
>>> import pandas as pd
>>> print(pd.__version__)
2.3.3

[ データの読み込み ]
df = pd.read_csv('~/Documents/kaggle_titanic/train.csv')

[ 読み込みの確認 ]
print(df.shape)
# (891, 12)

print(df.head())

[ 実行結果 ]
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

[5 rows x 12 columns]

--------------------------

Mac 上で Titanic の学習用データが正常に展開されました!

4. まとめ

最新の macOS 15.7.4Python 3.13.5 の組み合わせでも、pandas 2.3.3 を使った基本操作は非常にスムーズです。まずは shape でデータの全体像を掴み、head() で「顔」を眺めるところから。これで分析の土俵に上がることができました!

次回は、読み込んだデータの中身を詳しく診察する「健康診断(データの可視化や欠損値の確認)」、そして分析に欠かせないデータの加工(0/1の置き換えなど)に挑戦しましょう。