はじめに
仕事でデータ分析やAIに関わっており、Kaggleという存在も知ってはいました。
が、勉強モチベが高かった時期にアカウントだけ作成したものの、放置していた…
先輩との会話で久しぶりにKaggleの話題になり、そういえばアカウント作ったな…と思い出し、Kaggleの初心者向けチュートリアルコンペ「Titanic: Machine Learning from Disaster(タイタニック号の生存予測)」に挑戦してみました!
今回は、初参加の手続きと初コンペでやったこと、そこで得た気づきをレポートとしてまとめます。
1. 環境構築は一切不要!
参加する前は「まずはPythonをインストールして、エディタソフトも入れて…」と思っていましたが、Kaggleにはブラウザ上でコードが書けて実行できる「Kaggle Notebooks」という無料環境が用意されていました。
- PCへのインストールは一切なし(ブラウザとアカウントがあればOK)
- データ分析に必要なライブラリ(pandasなど)が最初から全部入っている
- ボタン一つでコードが動く
このおかげで、環境構築で挫折することなく、1分でいきなりデータ分析のスタートラインに立つことができました。
環境構築は、参加するコンペの「Code」から、「+New Notebook」をクリックし、

必要なライブラリなどをインポートするコマンドがセットされているので、実行(コード左側の▷)するだけで、セットアップ完了。

2. いよいよコンペ挑戦(データの確認と予測モデルの作成)
先ほどの画面(Notebook)から、コードを打ち込んでいきます。
今回は、コード作成はAIに手伝って(ほぼやって)もらいました。
- お題: 乗客の属性データから、各乗客が「生存したか(1)」「死亡したか(0)」を予測する
-
データの中身:
-
train.csv(訓練データ:正解ラベルあり) -
test.csv(本番用データ:正解ラベルなし。客室クラス、性別、年齢、運賃などの属性入り)
-
-
ミッション: 訓練データで「どんな人が生き残りやすかったか」のパターンをAI(ランダムフォレスト)に学習させ、テストデータの生死を予測した提出用ファイル(
submission.csv)を作ること!その予測正解率(Score)を競います。
2.1.データを眺める
まずは基本となるデータの全体像や状態をチェックするために、下記のコードを実行しました。
AIには、基本情報把握したいのでコード書いて~という雑な依頼をしました。
import pandas as pd
# データの読み込み
train_df = pd.read_csv('/kaggle/input/titanic/train.csv')
# データの概要を確認
print(train_df.info())
# データの行数・列数や、各列の欠損値の数を一発で確認する
train_df.info()
# 数値データの要約統計量(平均、最小、最大など)を表示する
train_df.describe()
# 生存者(1)と死亡者(0)の人数を数える
train_df['Survived'].value_counts()
この後、まずはデータの傾向をざっと掴むためにいくつかの属性について生存率との関係を眺めてみました。
こちらも、「年齢と生死の関係をグラフにして〜」というようなシンプルな指示で、AIにサクッと可視化コードを書いてもらっています。
例)
import matplotlib.pyplot as plt
import seaborn as sns
# 生存した人と亡くなった人の年齢層の分布を重ねて表示する
plt.figure(figsize=(8, 4))
sns.histplot(data=train_df, x='Age', hue='Survived', multiple='stack', bins=30)
plt.title('Age Distribution by Survival')
plt.xlabel('Age')
plt.ylabel('Count')
plt.show()
# 客室クラス(Pclass)ごとの生存率の平均を計算する
train_df[['Pclass', 'Survived']].groupby(['Pclass']).mean()
# 性別(Sex)と客室クラス(Pclass)の組み合わせごとの「生存率の平均(割合)」を計算する
pd.crosstab(index=train_df['Sex'], columns=train_df['Pclass'], values=train_df['Survived'], aggfunc='mean')
2.2.データをきれいにする
いわゆるデータクレンジングです。データの欠損値を埋めたり、カテゴリの文字列を数字に変換する作業を行います。
Ageに欠損がいくつかあったため、今回は中央値を使って穴埋めしました。
# Ageの欠損値を、Ageの中央値(median)で穴埋めする
age_median = train_df['Age'].median()
train_df['Age'] = train_df['Age'].fillna(age_median)
# ちゃんと穴埋めできたか(Ageの欠損値が0になったか)確認する
print("Ageの欠損値の数:", train_df['Age'].isnull().sum())
Cabinにも欠損が多数ありましたが、これは何かで穴埋めするのではなく、「Unknown」という1カテゴリとしました。
なぜ "Unknown" にしたの?
Cabinはデータの7割以上が空っぽ(欠損)でした。ここまで欠損が多い場合、無理に他のデータから推測して埋めようとすると、かえって嘘のデータを混ぜることになりモデルの精度にも影響が出そうです。
また、「客室番号が記録されていない」という欠損自体が何らかの重要なヒント(生存率の差)になっている可能性があるため、「不明」というグループとしてそのままAIに学習させることにしました。
# Cabinの欠損値を、文字列の "Unknown" で穴埋めする
train_df['Cabin'] = train_df['Cabin'].fillna('Unknown')
# ちゃんと穴埋めできたか(Cabinの欠損値が0になったか)確認する
print("Cabinの欠損値の数:", train_df['Cabin'].isnull().sum())
# どんな風に書き換わったか、最初の5行をもう一度見てみる
train_df.head()
補足) こんな考えで穴埋めしたのですが、結局Cabinは特徴が出せず、モデル作成は使っておりません…
Embarkedは欠損箇所が少なかったので、多数派の値で穴埋めしました。
ついでに、分析しやすいよう、SやCといった文字列を数字に変換しました。
# 乗船した港(Embarked)の欠損値を一番多い 'S' で埋めて、数字に変換する
train_df['Embarked'] = train_df['Embarked'].fillna('S')
train_df['Embarked'] = train_df['Embarked'].replace({'S': 0, 'C': 1, 'Q': 2})
Sexも同様に、文字列を数字に変換しました。
# 性別(Sex)の 'male' を 0、'female' を 1 に変換する
train_df['Sex'] = train_df['Sex'].replace({'male': 0, 'female': 1})
# 変換がうまくいったか、もう一度最初の5行を見てみる
train_df.head()
2.3.モデル作成
今回は、とりあえずモデルを作ろうと思い、思いついた(ギリギリ名前だけ覚えていた)ランダムフォレストを使ってモデルを作成しました。
使う特徴量は自分で指定しつつ、AIのアドバイス(木の数や深さ)も取り入れながら作成しました。
from sklearn.ensemble import RandomForestClassifier
# 1. AIに学習させる「ヒント(特徴量)」の列を選ぶ
features = ["Pclass", "Sex", "Age", "SibSp", "Parch", "Fare", "Embarked"]
X_train = train_df[features] # ヒントだけのデータ
y_train = train_df["Survived"] # 正解(生存したか:0 or 1)のデータ
# 2. AIのモデル(ランダムフォレスト)の「型」を作成する
# 木の数を100本、深さを最大5に設定しておきます
model = RandomForestClassifier(n_estimators=100, max_depth=5, random_state=1)
# 3. 準備したデータをAIに読み込ませてパターンを学習させる
model.fit(X_train, y_train)
# 4. 学習したAIが、どれくらい正解できるかスコアを出す
accuracy = model.score(X_train, y_train)
print(f"訓練データでの予測正解率: {accuracy * 100:.2f}%")
訓練データでの予測正解率: 85.30%
2.4.提出用ファイル作成
まずは、テストデータの欠損値穴埋めやカテゴリ文字列→数字変換を訓練データ同様にやっておきます。
# 1. 訓練データのときと同じ「年齢の中央値(28歳)」を使って、テストデータの欠損値を埋める
test_df['Age'] = test_df['Age'].fillna(age_median)
# 2. ちゃんと穴埋めできたか(Ageの欠損値が0になったか)確認する
print("テストデータのAgeの欠損値の数:", test_df['Age'].isnull().sum())
# 1. テストデータの「運賃(Fare)」の1件の空っぽを、訓練データの中央値で埋める
fare_median = train_df['Fare'].median()
test_df['Fare'] = test_df['Fare'].fillna(fare_median)
# 2. テストデータの「性別(Sex)」の 'male' を 0、'female' を 1 に変換する
test_df['Sex'] = test_df['Sex'].replace({'male': 0, 'female': 1})
# 3. テストデータの「乗船した港(Embarked)」を数字に変換する
test_df['Embarked'] = test_df['Embarked'].replace({'S': 0, 'C': 1, 'Q': 2})
# 4. 前処理がすべて上手くいったか、最初の5行を表示して確認!
test_df.head()
テストデータに、3.で作成したモデルを適用します。
# 1. テストデータから、AIが学習したのと同じ「7つのヒント(特徴量)」だけを抜き出す
X_test = test_df[features]
# 2. 育てたAI(model)を使って、テストデータの乗客の生存(1)か死亡(0)を予測する
predictions = model.predict(X_test)
# 3. 予測結果の最初の10人分をチラッと覗いてみる
print("予測結果(最初の10人):", predictions[:10])
最後に、提出用のデータに予測結果(生or死)を記入し、保存します。
# 1. 提出用のデータフレーム(表)を作る
# テストデータの乗客IDと、AIの予測結果をガッチャンコします
submission = pd.DataFrame({
"PassengerId": test_df["PassengerId"],
"Survived": predictions
})
# 2. 「submission.csv」という名前で保存する
# index=False をつけて、余計な行番号が入らないようにします
submission.to_csv('submission.csv', index=False)
# 3. ちゃんとファイルができたか、中身を3行だけ確認してみる
submission.head(3)
2.5.提出して、評価を受ける
右側の「Submit to competition」の「Submit」ボタンから、このNotebookごと提出して、完了です。
しばらくして、スコア(=予測の精度の高さ)が表示されます。
私の1回目のスコアは、「0.77751」でした!
2.6.さらなる精度向上を目指して…
ここから、どうすればもっと精度を上げられるかは、いろいろ試しながらやっていきたいところです。
1つ思いついた仮説として、以下を試してみました。
家族がいる人は優先して救助されたりしたのかな…
乗客名のMrやMissといった敬称(肩書き)は生死に関係あるかも?
まずはデータを分析に使えるよう、成型します。
# ==========================================
# 1. 訓練データ(train.csv)の加工
# ==========================================
# 名前から「, 」と「.」の間にある文字(Mr や Miss など)を抜き出します
train_df['Title'] = train_df['Name'].str.split(', ', expand=True)[1].str.split('.', expand=True)[0]
# メジャーな4つの肩書き以外は「Other」にまとめちゃいます
title_mapping = {"Mr": 0, "Miss": 1, "Mrs": 2, "Master": 3}
train_df['Title_num'] = train_df['Title'].map(title_mapping).fillna(4).astype(int)
# ==========================================
# 2. テストデータ(test.csv)の加工
# ==========================================
test_df['Title'] = test_df['Name'].str.split(', ', expand=True)[1].str.split('.', expand=True)[0]
test_df['Title_num'] = test_df['Title'].map(title_mapping).fillna(4).astype(int)
# ちゃんと加工できたか、訓練データの「Name」「Title」「Title_num」を並べて確認!
train_df[['Name', 'Title', 'Title_num']].head()
作成した新しい特徴(MrやMissなどの敬称)を、予測モデルに学習させる
# 1. AIに渡すヒントのリストに、新しく作った「Title_num」を追加します!
features_v2 = ["Pclass", "Sex", "Age", "SibSp", "Parch", "Fare", "Embarked", "Title_num"]
X_train_v2 = train_df[features_v2]
y_train_v2 = train_df["Survived"]
# 2. 新しいヒントを使って、AI(ランダムフォレスト)を新しく作り直して学習
model_v2 = RandomForestClassifier(n_estimators=100, max_depth=5, random_state=1)
model_v2.fit(X_train_v2, y_train_v2)
# 3. 新しいAIの、訓練データでの正解率を見てみる
accuracy_v2 = model_v2.score(X_train_v2, y_train_v2)
print(f"【作戦①】新しい訓練データでの予測正解率: {accuracy_v2 * 100:.2f}%")
# ==========================================
# 4. 本番用(テストデータ)の予測と保存
# ==========================================
X_test_v2 = test_df[features_v2]
predictions_v2 = model_v2.predict(X_test_v2)
# 新しい予測結果で、提出用ファイル(submission.csv)を上書き保存します
submission_v2 = pd.DataFrame({
"PassengerId": test_df["PassengerId"],
"Survived": predictions_v2
})
submission_v2.to_csv('submission.csv', index=False)
print("新しい予測ファイルを保存しました!上書き完了です。")
これで改めて提出したところ、スコアは「0.78468」になりました。
1回目は「0.77751」だったので、少し精度が向上しました!
3. さいごに
今回、初Kaggleコンペに参加しましたが、全体の所要時間としては1.5時間ほどでした。
コード作成自体はAIに手伝って(ほぼやって)もらうという形を取りましたが、その分「どうすれば環境が動くか」というエラーとの戦いではなく、「どうすれば予測精度が上がるか?(仮説検証)」というデータ分析の本質的な楽しさに時間を充てることができました。初心者にとって、これは非常に効率がよく、モチベーションが維持しやすい学び方だと感じています。
「Kaggleは優秀なデータサイエンティストたちが競い合う、敷居の高い場所」という先入観がありましたが、実際に飛び込んでみると、ブラウザ一つで初心者でもサクッと楽しめる、謎解きRPGのような場所でした。
これから雨の日や、暑くて外に出られない日が続きそうなので、そんな時のおうち時間に、ちょいちょい触って気楽に遊んでみようと思います!
