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

【Kaggle挑戦記】Titanic 攻略 #8:フラグ追加の試みと、見えてきた「欠損値補完」の罠

前回(攻略 #7)では、性別と客室クラスを掛け合わせた精緻な Age(年齢)補完を行い、スコアは 0.77511 → 0.77990 と微増しました。今回は、さらに「子供」という属性を強調し、0.78 の大台を突破するための特徴量エンジニアリングに挑戦しました。

1. これまでの振り返りと今回の仮説

現在の私たちの戦績と構成は以下の通りです。

  • 攻略 #6: パラメータ調整(木の数)ではスコアに変化なし
  • 攻略 #7: Age(年齢)を補完して投入。スコアが 0.00479 上昇

現在、Age は連続値としてモデルに渡っています。しかし、生存の鍵は「子供か大人か」という二値的な境界線にあるはず。そこで、「12歳以下(Child)」か「それ以外(Adult)」かというフラグ(IsChild)を明示的に作ることで、モデルの判断を助けるという仮説を立てました。

2. 【実装】子供フラグ(IsChild)の導入とコード全文

Age が 12 以下の場合は 1、それ以外を 0 とする新しいカラム IsChild を作成し、特徴量に加えました。前回からの変更・追加箇所がわかるように実装したコードの全文です。

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. 【攻略 #7からの継承】Age の欠損値を「性別×客室クラス」ごとの中央値で補完
# ※ここでの中央値補完が、後のフラグ判定に影響を与えた可能性あり
group_cols = ['Pclass', 'Sex']
train_data['Age'] = train_data['Age'].fillna(train_data.groupby(group_cols)['Age'].transform('median'))
test_data['Age'] = test_data['Age'].fillna(test_data.groupby(group_cols)['Age'].transform('median'))
test_data['Fare'] = test_data['Fare'].fillna(test_data['Fare'].median())

# 3. 【攻略 #8での追加】子供フラグ(IsChild)の作成
# ---------------------------------------------------------
# 12歳以下を子供(1)、それ以外を大人(0)として新規カラム作成
# ---------------------------------------------------------
train_data['IsChild'] = (train_data['Age'] <= 12).astype(int)
test_data['IsChild'] = (test_data['Age'] <= 12).astype(int)

# 4. 【攻略 #8での変更】特徴量の選択
# ---------------------------------------------------------
# 新たに作成した "IsChild" をリストに追加
# ---------------------------------------------------------
features = ["Pclass", "Sex", "Age", "IsChild", "SibSp", "Parch", "Fare"]

X = pd.get_dummies(train_data[features])
y = train_data["Survived"]
X_test = pd.get_dummies(test_data[features])

# 5. モデルの構築(攻略 #6 で決めた 500本 を維持)
model = RandomForestClassifier(n_estimators=500, max_depth=5, random_state=1)
model.fit(X, y)

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

# 7. 提出用ファイルの作成
output = pd.DataFrame({'PassengerId': test_data.PassengerId, 'Survived': predictions})
output.to_csv('submission_ischild_added.csv', index=False)

3. 実験結果:無情にもスコアは 0.77511 へ下落

期待に反し、スコアは前回の Age 投入前と同じ 0.77511 まで落ち込んでしまいました。良かれと思って追加した「子供フラグ」が、なぜ足を引っ張ったのでしょうか。

4. 考察:欠損値補完が「真の子供」を消した可能性

ここで非常に重要な仮説が浮かび上がりました。「Ageが空欄で、適当な中央値で埋められた人たち」の中に、実は助かったはずの子供(Master)が混ざっていたのではないか? という点です。

攻略 #7 で行った「クラス別の中央値補完」では、たとえば3等客のAge欠損値に対し、一律で「20代半ば」といった大人の数値を割り当ててしまいました。その結果、本来は子供であったはずの乗客が、この IsChild フラグによって「確定的な大人(0)」としてモデルに誤学習されてしまった恐れがあります。

つまり、現在の「Ageベースのフラグ」は、補完データの不正確さを増幅させるノイズになってしまったと言えます。


次の一手は、この矛盾を解消するために「名前(Name)」に含まれる情報を使います。年齢が不明でも、名前に "Master." とあればその乗客は確実に男の子です。次回、「名前 + 年齢」による真の子供特定に挑み、0.78 の壁を叩き割ります。



PR

【Kaggle挑戦記】Titanic 攻略 #7:データクレンジングの深化(Age の欠損値補完)

前回(攻略 #6)の実験では、アルゴリズムのパラメータ(木の数)を増やすという「計算側の工夫」を行いましたが、結果は無情にもスコアに変化なし。エンジニアとして、現在の材料(特徴量)だけでは限界に達したことを確認しました。

1. これまでの振り返り

現在の私たちの立ち位置を整理します。これまでは、極めてシンプルなデータ処理に留まっていました。

  • 利用した特徴量: Pclass, Sex, SibSp, Parch, Fare(計5項目)
  • 利用したアルゴリズム: ランダムフォレスト(Random Forest)
  • 前回の実験: 木の数を 100 → 500 へ増強したが、Score 0.77511(変化なし)

この結果から導き出されるロジカルな結論は、「計算式をいじくり回すよりも、モデルに与える『新しいヒント(特徴量)』を増やすべきだ」ということです。

2. 今回のターゲット:Age(年齢)の救出

生存率に大きく関与しているはずなのに、これまで無視してきたデータがあります。それが Age(年齢) です。無視していた理由は、データの約20%が空欄(欠損値)であり、そのままでは機械学習にかけられないからです。

今回は、この欠損値を「単なる全体平均」で埋めるのではなく、データの背景を洞察した「属性別の層別補完」という手法で救い出します。

3. 【戦略】性別 × 客室クラス別の中央値で補完

「1等客の男性」と「3等客の女性」では、明らかに年齢層が異なります。そこで、Pclass(客室クラス)と Sex(性別)を掛け合わせたグループごとに中央値を算出し、より実態に近い値を穴埋めします。

# 【実装】性別×客室クラスごとの中央値で Age の欠損値を埋める
group_cols = ['Pclass', 'Sex']
train_data['Age'] = train_data['Age'].fillna(train_data.groupby(group_cols)['Age'].transform('median'))
test_data['Age'] = test_data['Age'].fillna(test_data.groupby(group_cols)['Age'].transform('median'))

4. 実験結果:0.77511 → 0.77990 へ上昇!

Ageを特徴量に加えて再学習させた結果、ついにスコアが動きました。

  • 前回(Ageなし): Score 0.77511
  • 今回(Ageあり): Score 0.77990

エンジニア的な考察:
「少ししか上がらないな」というのが正直な感想ですが、前回のパラメータ調整ではピクリとも動かなかったスコアが、データのクレンジングによって確実に前進しました。これは、モデルが「Women and Children First(婦女子優先)」という歴史的背景を、より正しく理解し始めた証拠と言えます。


「質の高いヒント」を投入することで、停滞していた壁をわずかながら突破しました。しかし、まだ 0.78 の大台には届いていません。次はさらに踏み込み、名前(Name)に含まれる敬称から「社会的地位」や「未婚・既婚」を抽出する、より高度な特徴量エンジニアリングが必要かもしれません。



【DS検定対策】統計学の王道!どんな分布も正規分布に導く「中心極限定理」

統計学の世界で、データの分析を支える最も強力な法則の一つが「中心極限定理」です。なぜ多くのデータ分析で「正規分布」が前提とされるのか、その理由がここにあります。

1. 【 問題 】

母集団の分布がどのような形であっても、そこから抽出するサンプルサイズ(n)を大きくするにつれて、標本平均の分布はどのような分布に近づくでしょうか?

① ポアソン分布
② 指数分布
③ 正規分布
④ カイ二乗分布


2. 【 解答 】

正解: ③ 正規分布

3. 整理:「平均」を繰り返すと整う世界

元のデータがバラバラ(一様分布や偏った分布)でも、何度も「平均」をとって集めると、不思議ときれいな山の形(正規分布)になります。

【 中心極限定理のプロセス 】

[ 1. 母集団 ]
サイコロの目(1〜6が均等に出る分布)など、形は自由。

[ 2. 標本抽出(サンプリング) ]
100個のサイコロを振って、その「平均値」を出す。

[ 3. 試行を繰り返す ]
★ ここが中心極限定理!
その「平均値」を何セットも記録してグラフにすると……

[ 4. 収束 ]
サンプルサイズ(n)が大きいほど、見事な「正規分布(ベルカーブ)」になる。

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

重要: 元の分布が正規分布でなくても、標本平均は正規分布に従うようになります。

4. なぜこれが重要なのか?

1. 推測統計の根拠: 母集団の正確な形がわからなくても、正規分布の性質を使って「母平均」を推定できるようになります。
2. 検定の基礎: t検定などの統計的検定が成り立つのは、この定理のおかげです。
3. 実務での安心感: サンプルサイズが十分に大きければ(一般にn=30以上など)、正規分布を前提とした分析手法が使いやすくなります。


5. DS検定形式:実戦4択クイズ

問:中心極限定理の説明として、不適切なものはどれか。

① 元の母集団がどのような分布であっても成り立つ。
② サンプルサイズが大きくなるほど、平均の分布は尖った形になる。
③ 標本平均の期待値は、母平均に等しい。
④ 母集団が正規分布でないと、標本平均は決して正規分布にならない。

【 正解: ④ 】

解説: 「母集団が正規分布でなくても、平均の分布は正規分布に近づく」のが中心極限定理の最大のポイントです。したがって④は誤りです。サンプルサイズが大きくなれば、どんな歪んだ分布から取った平均でも正規分布に収束します。


6. まとめ

DS検定において「サンプルが大きくなる」「標本平均の分布」「正規分布に近づく」というキーワードがセットで出たら、それは「中心極限定理」を指しています。統計分析の「免罪符」とも呼ばれるこの定理を、しっかり味方につけましょう!


z

【DS検定対策】統計の落とし穴を攻略!極端な値が落ち着く「平均への回帰」

「前回のテストが満点だったのに、次は下がってしまった」「大活躍した新人が、2年目に成績を落とした」。これらは偶然ではなく、統計的な「平均への回帰」という現象かもしれません。

1. 【 問題 】

ある変数の測定を繰り返した際、1回目に非常に高い(または低い)といった極端な観測値が出たとしても、2回目以降の測定では、より中心(平均)に近い値が観測されやすくなる現象を何と呼ぶでしょうか?

① 平均への回帰
② 大数の法則
③ 中心極限定理
④ 標本誤差


2. 【 解答 】

正解: ① 平均への回帰

3. 整理:極端な値は「長続きしない」世界

観測値には「実力」だけでなく「運(偶然の誤差)」が含まれます。極端な値が出たときは、その「運」が最大に振れていた可能性が高いのです。

【 平均への回帰のイメージ 】

[ 1回目の測定 ]
実力 + 「ものすごい幸運」 = 100点(極端な値)

[ 2回目の測定 ]
実力 + 「普通の運」 = 75点(平均に近い値)

[ 3回目の測定 ]
実力 + 「少しの不運」 = 65点

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

「運」は毎回ランダムなので、繰り返すと結局「平均」の方へ引き戻されます。

4. 間違いやすいポイント

1. 「実力が落ちた」と誤解する: 2回目の成績が下がったのは、実力が落ちたのではなく、単に1回目の運が良すぎただけかもしれません。
2. 因果関係の取り違え: 「叱ったから成績が戻った(平均へ回帰した)」のか、「叱らなくても勝手に平均へ戻った」のかを区別する必要があります。
3. ビジネスでの活用: キャンペーン初日の異常な売上増が、2日目以降に落ち着くのもこの現象の一種です。


5. DS検定形式:実戦4択クイズ

問:高い相関がある2つの変数(親の身長と子の身長など)において、親が極端に高身長であっても、子の身長は親ほど極端ではなく、より全体の平均に近い値をとる傾向を指摘した統計学者は誰か。

① フィッシャー   ② ゴルトン   ③ ピアソン   ④ ベイズ

【 正解: ② 】

解説: フランシス・ゴルトンは、親子の身長の研究を通じて「平均への回帰」という概念を見出しました。これがのちに「回帰分析」という手法の語源となりました。


6. まとめ

DS検定において「極端な値が中心に近づく」「繰り返すと平均的になる」というキーワードが出たら「平均への回帰」が正解です。データを見る際に、それが「真の変化」なのか「統計的な揺り戻し」なのかを見極める視点を持ちましょう!

【Kaggle挑戦記】Titanic 攻略 #6:モデルの「合議制」を強化し、精度向上の実験へ

前回(攻略 #5)では、生存率と相関の強かった「Fare(運賃)」を特徴量に加え、スコアを 0.76749 → 0.77511 へと上昇させることに成功しました。今回は、さらに精度を上げるための「次の一手」を、エンジニアらしく最小の工数で検証します。

1. これまでの復習

まずは、現在の私たちの武器(モデル構成)を整理しておきます。

  • 利用した特徴量(5項目): Pclass, Sex, SibSp, Parch, Fare
    (生存に直結する納得感のあるヒントを投入済みです)
  • 利用したアルゴリズム: ランダムフォレスト(Random Forest)
    (攻略 #4 で中身を覗いた、100本の決定木による「合議制」モデルです)

2. 今回の戦略:精度向上の実験

さらなる精度向上を狙うにあたり、今回は「木の数(n_estimators)」を変更する実験を行います。検討の理由は以下の通りです。

  1. 木の深さ(max_depth)の限界:
    現在は特徴量が5項目と少なく、これ以上深く掘り下げても学習データへの過剰適合(過学習)を招くリスクが高いと判断しました。
  2. 多数決(合議制)の強化:
    判断材料が限られている現状では、個々の木の判断をより多くの「仲間」で補完し、予測のブレを最小限に抑える(多数決の精度を上げる)ほうが、スコア向上への確実な一手になると仮説を立てました。

3. 【実験】木の数を 100 → 500 へ増強

具体的には、`RandomForestClassifier` を定義している一行にある n_estimators という引数を書き換えます。これは「アンサンブル(合議制)に参加させる木の数」を指定するパラメータです。

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. 特徴量の選択(前回と同じ5項目)
features = ["Pclass", "Sex", "SibSp", "Parch", "Fare"]
X = pd.get_dummies(train_data[features])
y = train_data["Survived"]

X_test = pd.get_dummies(test_data[features])
X_test['Fare'] = X_test['Fare'].fillna(X_test['Fare'].median())

# 3. モデルの構築(★ここを修正!)
# 【変更前】 model = RandomForestClassifier(n_estimators=100, max_depth=5, random_state=1)
model = RandomForestClassifier(n_estimators=500, max_depth=5, random_state=1)

# 4. モデルの学習(書き換えた設定で再計算)
model.fit(X, y)

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

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

修正のポイント:
`n_estimators=100` を `n_estimators=500` に変更しました。これにより、100本の多数決から500本の多数決へと「合議制」がパワーアップします。修正はこの1箇所のみ。極めて手間の少ない「次の一手」です。

4. 考察と実験結果

ここでエンジニアとして一つの疑問が湧きます。「教師データが約800件しかないのに、木の数を500本にするのは多すぎる(リソースの無駄)ではないか?」という点です。

結論から言うと、アンサンブル学習において「合議の数(木の数)」を増やすことは、計算コスト以外のデメリット(精度低下のリスク)はありません。それぞれの木が重複を許して抽出された異なるデータセットを学習するため、数を増やすほど予測は安定する方向に向かいます。

さて、注目の実験結果は以下の通りでした。

  • 前回(100本): Score 0.77511
  • 今回(500本): Score 0.77511(変化なし)

エンジニア的な結論:
残念ながら、今回のパラメータ変更だけではスコアはピクリとも動きませんでした。これは、現在の5つの特徴量から引き出せるパターンは、すでに100本の多数決で「出し尽くされている」ということを意味します。計算式をいじっても限界があることが証明されました。


仮説と検証の結果、次なる道が明確になりました。これ以上「既存のヒントの計算」を強化しても意味がありません。次は、いよいよ「Age(年齢)」という新しいヒント(特徴量)の投入、そしてそのための「欠損値処理」という、より高度な前処理へと進みます。