<?xml version="1.0" encoding="UTF-8" ?>
<rdf:RDF
  xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
  xmlns="http://purl.org/rss/1.0/"
  xmlns:dc="http://purl.org/dc/elements/1.1/"
  xmlns:content="http://purl.org/rss/1.0/modules/content/">

  <channel rdf:about="http://learnms.blog.shinobi.jp/RSS/100/">
    <title>いけいけ機械学習</title>
    <link>http://learnms.blog.shinobi.jp/</link>
    <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://learnms.blog.shinobi.jp/RSS/" />
    <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" />
    <description>統計、機械学習、AIを学んでいきたいと思います。 お役に立てば幸いです。</description>
    <dc:language>ja</dc:language>
    <dc:date>2026-04-12T20:40:19+09:00</dc:date>
    <items>
    <rdf:Seq>
      <rdf:li rdf:resource="http://learnms.blog.shinobi.jp/Entry/60/" />
      <rdf:li rdf:resource="http://learnms.blog.shinobi.jp/Entry/59/" />
      <rdf:li rdf:resource="http://learnms.blog.shinobi.jp/Entry/58/" />
      <rdf:li rdf:resource="http://learnms.blog.shinobi.jp/Entry/57/" />
      <rdf:li rdf:resource="http://learnms.blog.shinobi.jp/Entry/56/" />
      <rdf:li rdf:resource="http://learnms.blog.shinobi.jp/Entry/55/" />
      <rdf:li rdf:resource="http://learnms.blog.shinobi.jp/Entry/54/" />
      <rdf:li rdf:resource="http://learnms.blog.shinobi.jp/Entry/53/" />
      <rdf:li rdf:resource="http://learnms.blog.shinobi.jp/Entry/52/" />
      <rdf:li rdf:resource="http://learnms.blog.shinobi.jp/Entry/51/" />
    </rdf:Seq>
    </items>
  </channel>

  <item rdf:about="http://learnms.blog.shinobi.jp/Entry/60/">
    <link>http://learnms.blog.shinobi.jp/Entry/60/</link>
    <title>【DS検定対策】AIの「知能」をゼロから作る！事前学習の本質とは？</title>
    <description>GPTなどの大規模言語モデル（LLM）が、なぜ多様なタスクをこなせるのか。その答えは、特定の用途に絞る前に行われる「事前学習」という工程に隠されています。
1. 【 問題 】
言語モデルにおける「事前学習（Pre-training）」の説明として、最も適切なものはどれでしょうか？
① 専門的なデータ...</description>
    <content:encoded><![CDATA[<p>GPTなどの大規模言語モデル（LLM）が、なぜ多様なタスクをこなせるのか。その答えは、特定の用途に絞る前に行われる「事前学習」という工程に隠されています。</p>
<h3 style="color: blue;">1. 【 問題 】</h3>
<p>言語モデルにおける「事前学習（Pre-training）」の説明として、最も適切なものはどれでしょうか？</p>
<p>① 専門的なデータを用いて、特定のタスク（翻訳や要約など）に特化させること<br />
② 膨大なラベルなしデータを用いて、言語の構造や一般的な知識をゼロから獲得させること<br />
③ 学習済みモデルを圧縮して、スマートフォンのような小型端末で動くようにすること<br />
④ 人間のフィードバックに基づき、AIの回答をより安全で好ましいものに調整すること</p>
<hr />
<h3 style="color: #d32f2f;">2. 【 解答 】</h3>
<div style="background-color: #eeeeee; border: 2px solid #333; padding: 15px; font-size: 1.2em; font-weight: bold; text-align: center;">正解： ② 膨大なラベルなしデータを用いて、言語の構造や一般的な知識をゼロから獲得させること</div>
<hr />
<h3 style="color: blue;">3. 整理：事前学習の役割と仕組み</h3>
<p>事前学習は、モデルが「言葉の並び順」や「概念のつながり」を自ら発見し、知能の土台（基盤モデル）を作るプロセスです。</p>
<p>【 事前学習のメカニズム 】</p>
<div style="font-family: monospace; border: 1px solid #ccc; padding: 10px; line-height: 1.6;">[ 1. データの性質 ]<br />
インターネット上のテキストや書籍など、正解ラベルのない「生のデータ」をそのまま利用します。<br />
<br />
[ 2. 学習タスク（次単語予測） ]<br />
<b>★ ここが本質！</b><br />
「今日はとても[ ]が良い」の[ ]に入る単語を、膨大なパターンから予測。これを繰り返すことで、文法や知識を身につけます。<br />
<br />
[ 3. 到達点 ]<br />
特定の目的は持たないものの、あらゆる質問に応用できる「汎用的な能力」が備わります。<br />
<br />
--------------------------<br />
<br />
◎ <b>重要： この段階で出来上がったものを「基盤モデル（Foundation Model）」と呼びます。</b></div>
<h3 style="color: blue;">4. なぜ「事前」に学習するのか？</h3>
<p>1. <b>知識の共有</b>: 言語の基本を一度マスターしておけば、後の専門学習（ファインチューニング）が非常に効率的になります。<br />
2. <b>データの有効活用</b>: ラベル付きデータは貴重ですが、ラベルなしデータはネット上に無限にあるため、大規模なスケーリングが可能です。<br />
3. <b>ゼロショット能力</b>: 事前学習だけで、一度も解いたことがない問題に対してある程度の回答を出す能力が生まれます。</p>
<hr />
<h3 style="color: orange;">5. DS検定形式：実戦4択クイズ</h3>
<p><b>問：事前学習において、人間が正解を与えなくてもデータ自身から学習信号を作り出す手法を何と呼ぶか。</b></p>
<p>① 教師あり学習 &nbsp; ② 自己教師あり学習 &nbsp; ③ 転移学習 &nbsp; ④ 蒸留</p>
<p><b>【 正解： ② 】</b></p>
<p><b>解説：</b> 事前学習の多くは、データの一部を隠してそれを予測させる「自己教師あり学習（Self-supervised Learning）」という手法で行われます。これにより、人間が手作業でラベルを付ける手間を省き、巨大なモデルの構築が可能になりました。</p>
<hr />
<h3 style="color: blue;">6. まとめ</h3>
<p>DS検定において「事前学習」は、AIが汎用的な知能を得るための「第一段階」として定義されます。「ゼロから学ぶ」「ラベルなしデータの活用」「自己教師あり学習」というキーワードをセットで押さえておきましょう！<br />
<br />
<br />
</p>]]></content:encoded>
    <dc:subject>DS検定＞1. データサイエンス＞1-4. 応用技術＞1-4-2. 言語モデル</dc:subject>
    <dc:date>2026-04-12T19:50:09+09:00</dc:date>
    <dc:creator>吟遊詩人</dc:creator>
    <dc:publisher>NINJA BLOG</dc:publisher>
    <dc:rights>吟遊詩人</dc:rights>
  </item>
  <item rdf:about="http://learnms.blog.shinobi.jp/Entry/59/">
    <link>http://learnms.blog.shinobi.jp/Entry/59/</link>
    <title>【Kaggle挑戦記】Titanic 攻略 #18：Title再投入の罠。CV 0.8440 からの「急転下落」</title>
    <description>前回、LightGBMに Title（敬称） を再投入し、手元の検証（CV）では 0.8440 という過去最高レベルの数字を叩き出しました。「今度こそ0.8突破か？」と震える手で提出。しかし、返ってきた結果は 0.76076。期待とは裏腹に、前回（0.77511）よりもスコアを下げる「急転直下」の結...</description>
    <content:encoded><![CDATA[<p>前回、LightGBMに <b>Title（敬称）</b> を再投入し、手元の検証（CV）では <b>0.8440</b> という過去最高レベルの数字を叩き出しました。「今度こそ0.8突破か？」と震える手で提出。しかし、返ってきた結果は <b>0.76076</b>。期待とは裏腹に、前回（0.77511）よりもスコアを下げる「急転直下」の結末となりました。</p>
<h3 style="color: blue;">1. 敗因分析：Titleという「劇薬」</h3>
<p>CVスコアが上がり、本番スコアが下がる。これは典型的な <b>過学習（オーバーフィッティング）</b> の再発です。なぜあんなに制限をかけたのに失敗したのか、エンジニア的に振り返ります。</p>
<ul>
<li><b>「敬称」のヒントが強すぎた：</b> Titleには生存率に直結する情報（性別や年齢層）が凝縮されています。LightGBMがこれに依存しすぎてしまい、他の特徴量との組み合わせによる「本質的な予測」を疎かにしてしまった可能性があります。</li>
<li><b>CVスコアの「甘い誘惑」：</b> CV 0.8440 という数字は、訓練データ内の特定のパターンを覚え込むことで得られた「虚像」でした。データの少ないTitanicでは、特徴量を1つ増やすだけで、モデルは簡単に「カンニング（暗記）」を始めてしまいます。</li>
<li><b>汎化性能の喪失：</b> パラメータ探索で <code>min_child_samples: 20</code> が選ばれ、制約が緩んだことも、ノイズを拾う原因になったと考えられます。</li>
</ul>
<h3 style="color: blue;">2. 【教訓】光と影を記録した実装コード</h3>
<p>CVスコアに一喜一憂し、過学習を招いてしまった「失敗の記録」としてのコードです。</p>
<pre style="border: 1px solid #ccc; padding: 10px; background-color: #f9f9f9; overflow-x: auto; font-family: Consolas, Monaco, 'Courier New', monospace; line-height: 1.5;">import pandas as pd
import numpy as np
import lightgbm as lgb
from sklearn.model_selection import GridSearchCV

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

# 2. Title（敬称）の抽出（SyntaxWarning対策済み）
for df in [train_data, test_data]:
    df['Title'] = df['Name'].str.extract(r' ([A-Za-z]+)\.', expand=False)
    df['Title'] = df['Title'].replace(['Lady', 'Countess','Capt', 'Col','Don', 'Dr', 'Major', 'Rev', 'Sir', 'Jonkheer', 'Dona'], 'Rare')
    df['Title'] = df['Title'].replace('Mlle', 'Miss')
    df['Title'] = df['Title'].replace('Ms', 'Miss')
    df['Title'] = df['Title'].replace('Mme', 'Mrs')

# 3. 前処理（ベスト構成を維持）
for df in [train_data, test_data]:
    df['Embarked'] = df['Embarked'].fillna('S')
    df['FamilySize'] = df['SibSp'] + df['Parch'] + 1

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

# 4. 特徴量選択（ここでTitleを追加したのが裏目に&hellip;）
features = ["Pclass", "Sex", "Age", "SibSp", "Parch", "Fare", "FamilySize", "Embarked", "Title"]
X = pd.get_dummies(train_data[features], drop_first=True)
y = train_data["Survived"]
X_test = pd.get_dummies(test_data[features], drop_first=True)
X, X_test = X.align(X_test, join='left', axis=1, fill_value=0)

# 5. パラメータ設定
param_grid = {
    'num_leaves': [7, 10, 15],
    'learning_rate': [0.01, 0.05],
    'n_estimators': [100, 200, 300],
    'max_depth': [3, 4], 
    'min_child_samples': [20, 40],
    'random_state': [1]
}

gbm = lgb.LGBMClassifier(verbosity=-1)
grid_search = GridSearchCV(gbm, param_grid, cv=5, n_jobs=-1, verbose=0)
grid_search.fit(X, y)

print(f"Best CV Score: {grid_search.best_score_:.4f}") # 0.8440

# 6. 予測・出力
best_model = grid_search.best_estimator_
predictions = best_model.predict(X_test)
pd.DataFrame({'PassengerId': test_data.PassengerId, 'Survived': predictions}).to_csv('submission_lgbm_title.csv', index=False)
</pre>
<h3 style="color: blue;">3. 実験結果：0.77511 &rarr; 0.76076 への後退</h3>
<p>アルゴリズムや特徴量を変えるたびに、スコアは大きく変動します。今回はその「負の側面」が強く出てしまいました。</p>
<ul>
<li><b>前回：</b> Score 0.77511 (LightGBM 制限付き)</li>
<li><b>今回：</b> <b style="color: #d73a49;">Score 0.76076</b> (Title追加)</li>
<li><b>分析：</b> CVスコア 0.844 との乖離は 0.08 以上。明らかな過学習です。</li>
</ul>
<h3 style="color: blue;">4. 考察：0.8の壁は「暗記」では越えられない</h3>
<p><b>エンジニア的な視点：</b><br />
Titleという強力な特徴量を入れたことで、モデルは「考える」ことをやめ、「覚える」ことに走ってしまいました。Titanic攻略において、0.8という数字がなぜ高い壁なのか。それは、強力なモデルや特徴量を<b>「いかに使わずに、本質だけを捉えさせるか」</b>という、引き算の思考が求められるからだと痛感しました。</p>
<hr />
<p>スコアが下がったことは敗北ではありません。この強力なTitleを「どう手なずけるか」、あるいは「Titleに頼らない別の道を探すか」。この試行錯誤こそが、データサイエンスの醍醐味です。次こそ、真の汎化性能を求めて！<br />
</p>]]></content:encoded>
    <dc:subject>【Kaggle挑戦記】</dc:subject>
    <dc:date>2026-04-11T19:50:56+09:00</dc:date>
    <dc:creator>吟遊詩人</dc:creator>
    <dc:publisher>NINJA BLOG</dc:publisher>
    <dc:rights>吟遊詩人</dc:rights>
  </item>
  <item rdf:about="http://learnms.blog.shinobi.jp/Entry/58/">
    <link>http://learnms.blog.shinobi.jp/Entry/58/</link>
    <title>【Kaggle挑戦記】Titanic 攻略 #17：0.77511への浮上。LightGBMを「制御」する手応え</title>
    <description>前回、最強モデル LightGBM を投入するも、過学習により 0.76315 という惨敗を喫しました。今回はその反省を活かし、モデルにあえて「制約」を課すリベンジマッチ。結果は 0.77511。自己ベストには届きませんでしたが、確かな改善の兆しが見えてきました。
1. 戦略の検証：過学習の抑制は成...</description>
    <content:encoded><![CDATA[<p>前回、最強モデル <b>LightGBM</b> を投入するも、過学習により <b>0.76315</b> という惨敗を喫しました。今回はその反省を活かし、モデルにあえて「制約」を課すリベンジマッチ。結果は <b>0.77511</b>。自己ベストには届きませんでしたが、確かな改善の兆しが見えてきました。</p>
<h3 style="color: blue;">1. 戦略の検証：過学習の抑制は成功したか？</h3>
<p>今回の修正の肝は、モデルに「深追いさせない」ことでした。その結果、スコアには以下のような変化が現れました。</p>
<ul>
<li><b>本番スコアの向上：</b> 0.76315 &rarr; <b>0.77511</b>（+0.012の改善）</li>
<li><b>CVスコアの適正化：</b> 0.8418 &rarr; <b>0.8373</b>（手元の数字が下がり、本番が上がった）</li>
</ul>
<p>この「手元の数字を下げて、本番を上げる」という現象こそ、過学習が解消に向かっている決定的な証拠です。F1マシンを路地裏に合わせて減速させたことで、壁にぶつからずにコーナーを曲がれるようになったのです。</p>
<h3 style="color: blue;">2. 【実装】「制約」を刻んだリベンジ・コード</h3>
<p>過学習した前回（負け）の設定をコメントで残し、今回（改善）の設定を対比させました。</p>
<pre style="border: 1px solid #ccc; padding: 10px; background-color: #f9f9f9; overflow-x: auto; font-family: Consolas, Monaco, 'Courier New', monospace; line-height: 1.5;">import pandas as pd
import numpy as np
import lightgbm as lgb
from sklearn.model_selection import GridSearchCV

# 1. 前処理（自己ベスト 0.78468 時の最強布陣を維持）
train_data = pd.read_csv('train.csv')
test_data = pd.read_csv('test.csv')

for df in [train_data, test_data]:
    df['Embarked'] = df['Embarked'].fillna('S')
    df['FamilySize'] = df['SibSp'] + df['Parch'] + 1

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

# 2. 特徴量のダミー変数化
features = ["Pclass", "Sex", "Age", "SibSp", "Parch", "Fare", "FamilySize", "Embarked"]
X = pd.get_dummies(train_data[features], drop_first=True)
y = train_data["Survived"]
X_test = pd.get_dummies(test_data[features], drop_first=True)
X, X_test = X.align(X_test, join='left', axis=1, fill_value=0)

# 3. ライトGBM：過学習を防ぐための「抑制」チューニング
param_grid = {
    # 'num_leaves': [31],        # 前回：多すぎて細かく分けすぎていた
    'num_leaves': [7, 10, 15],   # 修正：モデルをシンプルに保つ

    'learning_rate': [0.01, 0.05],
    'n_estimators': [100, 200],

    # 'max_depth': [-1],         # 前回：無制限が過学習の主因
    'max_depth': [3, 4],         # 修正：あえて「浅い木」に限定する

    'min_child_samples': [20, 40], # 修正：1つの枝に40人以上のデータを要求（ノイズ対策）

    'random_state': [1]
}

# 4. グリッドサーチ実行
gbm = lgb.LGBMClassifier(verbosity=-1)
grid_search = GridSearchCV(gbm, param_grid, cv=5, n_jobs=-1, verbose=0)
grid_search.fit(X, y)

# 5. 結果の記録
print(f"Best Params: {grid_search.best_params_}")
print(f"Best Score (CV): {grid_search.best_score_:.4f}") # 0.8373 を記録

# 6. 予測
best_model = grid_search.best_estimator_
predictions = best_model.predict(X_test)
pd.DataFrame({'PassengerId': test_data.PassengerId, 'Survived': predictions}).to_csv('submission_lgbm_v2.csv', index=False)
</pre>
<h3 style="color: blue;">3. 考察：0.8の壁を越えるために必要なこと</h3>
<p><b>エンジニア的な視点：</b><br />
今回の実験で、「強力なアルゴリズムを使うなら、強力なブレーキが必要である」という教訓が実証されました。0.77511 という数字は、まだ自己ベストには届きませんが、<b>「モデルの使いこなし」</b>という点では過去最高のレベルに達しています。<br />
<br />
ここからさらに 0.03 スコアを伸ばし、0.8の大台に乗るには、モデルの微調整（チューニング）だけでは限界があるかもしれません。次は、データの背後に隠れた「家族の運命」や「チケット番号の繋がり」など、人間らしい洞察（特徴量エンジニアリング）を LightGBM に教え込むフェーズに来ていると感じます。</p>
<hr />
<p>数字は嘘をつきません。改善した 0.012 は、私たちが正しい方向に進んでいる証拠。この調子で、次なる一手「特徴量の深化」へ進みます！<br />
<br />
<br />
<br />
</p>]]></content:encoded>
    <dc:subject>【Kaggle挑戦記】</dc:subject>
    <dc:date>2026-04-11T17:20:10+09:00</dc:date>
    <dc:creator>吟遊詩人</dc:creator>
    <dc:publisher>NINJA BLOG</dc:publisher>
    <dc:rights>吟遊詩人</dc:rights>
  </item>
  <item rdf:about="http://learnms.blog.shinobi.jp/Entry/57/">
    <link>http://learnms.blog.shinobi.jp/Entry/57/</link>
    <title>【Kaggle挑戦記】Titanic 攻略 #16：LightGBMの洗礼。CVスコアの「罠」と過学習の恐怖&lt;/h2&gt;</title>
    <description>前回、最強の刺客 LightGBM を投入し、手元の交差検証（CV）で 0.8418 という驚異的なスコアを叩き出しました。「ついに0.8の大台か？」と期待に胸を膨らませて提出した結果、待っていたのは 0.76315 という非情な現実。自己ベスト（0.78468）から大きく後退する結果となりました。...</description>
    <content:encoded><![CDATA[<p>前回、最強の刺客 <b>LightGBM</b> を投入し、手元の交差検証（CV）で <b>0.8418</b> という驚異的なスコアを叩き出しました。「ついに0.8の大台か？」と期待に胸を膨らませて提出した結果、待っていたのは <b>0.76315</b> という非情な現実。自己ベスト（0.78468）から大きく後退する結果となりました。</p>
<h3 style="color: blue;">1. なぜ「手元の高スコア」が「本番の惨敗」を招いたのか？</h3>
<p>今回の敗因は、機械学習において最も警戒すべき <b>過学習（オーバーフィッティング）</b> です。原因をエンジニア的に分析すると、以下の3点に集約されます。</p>
<ul>
<li><b>モデルが「賢すぎた」：</b> LightGBMは非常に強力なため、約890件という少ない訓練データの「偶然の偏り」まで完璧に学習してしまいました。</li>
<li><b>CVスコアの信憑性：</b> CVスコア 0.84 というのは、訓練データ内での「予行演習」に過ぎません。本番のテストデータとの間に、学習しきれないギャップが存在していました。</li>
<li><b>パラメータの攻めすぎ：</b> <code>max_depth: -1</code>（無制限）や <code>num_leaves: 20</code> という設定が、少数のデータに対しては複雑すぎた可能性があります。</li>
</ul>
<h3 style="color: blue;">2. 【実装】光と影を記録した LightGBM コード</h3>
<p>CVスコア 0.8418 を出しながらも、本番で 0.76315 に沈んだ「教訓」としてのコードです。</p>
<pre style="border: 1px solid #ccc; padding: 10px; background-color: #f9f9f9; overflow-x: auto; font-family: Consolas, Monaco, 'Courier New', monospace; line-height: 1.5;">import pandas as pd
import numpy as np
import lightgbm as lgb
from sklearn.model_selection import GridSearchCV

# 1. データの読み込みと前処理（ベスト布陣を維持）
train_data = pd.read_csv('train.csv')
test_data = pd.read_csv('test.csv')

for df in [train_data, test_data]:
    df['Embarked'] = df['Embarked'].fillna('S')
    df['FamilySize'] = df['SibSp'] + df['Parch'] + 1

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

# 2. 特徴量の準備（ダミー変数化）
features = ["Pclass", "Sex", "Age", "SibSp", "Parch", "Fare", "FamilySize", "Embarked"]
X = pd.get_dummies(train_data[features], drop_first=True)
y = train_data["Survived"]
X_test = pd.get_dummies(test_data[features], drop_first=True)
X, X_test = X.align(X_test, join='left', axis=1, fill_value=0)

# 3. LightGBMの設定（ここでの最適化が本番で裏目に出た）
param_grid = {
    'num_leaves': [10, 20, 31],
    'learning_rate': [0.01, 0.05, 0.1],
    'n_estimators': [100, 500],
    'max_depth': [-1, 3, 5],
    'random_state': [1]
}

gbm = lgb.LGBMClassifier(verbosity=-1)
grid_search = GridSearchCV(gbm, param_grid, cv=5, n_jobs=-1, verbose=0)
grid_search.fit(X, y)

# 結果出力（CVスコアは 0.8418 をマーク）
print(f"LGBM Best Params: {grid_search.best_params_}")
print(f"LGBM Best Score (CV): {grid_search.best_score_:.4f}")

# 予測実行
best_model = grid_search.best_estimator_
predictions = best_model.predict(X_test)
pd.DataFrame({'PassengerId': test_data.PassengerId, 'Survived': predictions}).to_csv('submission_lgbm_overfit.csv', index=False)
</pre>
<h3 style="color: blue;">3. 実験結果：期待と現実のギャップ</h3>
<p>アルゴリズムを変えたことで、数字に大きな「動き」が出ましたが、今回は悪い方へ転がりました。</p>
<ul>
<li><b>自己ベスト（ランダムフォレスト）：</b> Score 0.78468</li>
<li><b>今回（LightGBM）：</b> <b style="color: #d73a49;">Score 0.76315</b>（CVスコアとの乖離：-0.078）</li>
</ul>
<h3 style="color: blue;">4. 考察：高すぎるCVスコアを疑え</h3>
<p><b>エンジニア的な視点：</b><br />
「手元で完璧なモデルが、外の世界で通用するとは限らない」。今回の結果は、AI開発における本質的な難しさを教えてくれました。CVスコアが 0.84 まで跳ね上がった時点で、「学習しすぎではないか？」と疑うべきだったのです。Titanicのような少人数データでは、LightGBMのような強力なモデルを「いかに抑え込むか（正則化）」が次の鍵となります。</p>
<hr />
<p>スコアダウンは失敗ではなく、モデルの特性を理解するための貴重なデータです。次は、LightGBMをあえて「弱く」する（パラメータを厳しく制限する）か、あるいはランダムフォレストの安定感を見直すか。この 0.02 の差を埋めるための戦いは、さらに深化していきます。<br />
<br />
<br />
</p>]]></content:encoded>
    <dc:subject>【Kaggle挑戦記】</dc:subject>
    <dc:date>2026-04-11T17:07:43+09:00</dc:date>
    <dc:creator>吟遊詩人</dc:creator>
    <dc:publisher>NINJA BLOG</dc:publisher>
    <dc:rights>吟遊詩人</dc:rights>
  </item>
  <item rdf:about="http://learnms.blog.shinobi.jp/Entry/56/">
    <link>http://learnms.blog.shinobi.jp/Entry/56/</link>
    <title>【Kaggle挑戦記】Titanic 攻略 #15：0.78468 不変。ハイパーパラメータ最適化が証明した「モデルの限界」</title>
    <description>前回（攻略 #14）、デッキ情報の追加でスコアを落とした反省を活かし、今回はベストスコア 0.78468 を出した最強の布陣に対し、科学的なメスを入れました。GridSearchCV（交差検証）による「木の深さ」の最適化。しかし、返ってきたスコアは驚くほど正確に前回と同じ 0.78468 でした。
...</description>
    <content:encoded><![CDATA[<p>前回（攻略 #14）、デッキ情報の追加でスコアを落とした反省を活かし、今回はベストスコア <b>0.78468</b> を出した最強の布陣に対し、科学的なメスを入れました。<code>GridSearchCV</code>（交差検証）による「木の深さ」の最適化。しかし、返ってきたスコアは驚くほど正確に前回と同じ <b>0.78468</b> でした。</p>
<h3 style="color: blue;">1. なぜ「最適化」したのにスコアが変わらなかったのか？</h3>
<p>勘に頼っていた <code>max_depth=5</code> という設定を、交差検証によって [3, 4, 5, 6, 7, 8] の中から最も優れたものへ自動選択させました。それにも関わらずスコアが不変だった理由。そこにはエンジニアとして納得のいく理由が隠れています。</p>
<ul>
<li><b>「深さ5」がすでに黄金比だった：</b> 交差検証の結果、実はこれまでの「深さ5」が、学習データとテストデータのバランスを保つ上で既に最適な値であった可能性が高いです。</li>
<li><b>特徴量の表現力の限界：</b> パラメータという「火加減」を調整してもスコアが動かないのは、材料である「特徴量」が持つ情報の限界に達していることを意味します。</li>
<li><b>高い汎化性能の証明：</b> スコアが落ちなかったということは、モデルが変に過学習せず、安定した予測能力を維持できている証拠でもあります。</li>
</ul>
<h3 style="color: blue;">2. 【実装】GridSearchCV による最適化の全記録</h3>
<p>結果は維持でしたが、今後の試行錯誤において「確信」を持ってパラメータを設定するための必須ステップです。</p>
<pre style="border: 1px solid #ccc; padding: 10px; background-color: #f9f9f9; overflow-x: auto; font-family: Consolas, Monaco, 'Courier New', monospace; line-height: 1.5;">import pandas as pd
import numpy as np
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import GridSearchCV

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

# 2. 前処理（ベストスコア時の構成を維持）
train_data['Embarked'] = train_data['Embarked'].fillna('S')
test_data['Embarked'] = test_data['Embarked'].fillna('S')

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

for df in [train_data, test_data]:
    df['FamilySize'] = df['SibSp'] + df['Parch'] + 1
test_data['Fare'] = test_data['Fare'].fillna(test_data['Fare'].median())

# 3. 特徴量のダミー変数化
features = ["Pclass", "Sex", "Age", "SibSp", "Parch", "Fare", "FamilySize", "Embarked"]
X = pd.get_dummies(train_data[features])
y = train_data["Survived"]
X_test = pd.get_dummies(test_data[features])
X, X_test = X.align(X_test, join='left', axis=1, fill_value=0)

# 4. パラメータの探索
param_grid = {
    'n_estimators': [500],
    'max_depth': [3, 4, 5, 6, 7, 8],
    'min_samples_leaf': [1, 3, 5],
    'random_state': [1]
}

# 5. グリッドサーチによる交差検証
grid_search = GridSearchCV(
    estimator=RandomForestClassifier(),
    param_grid=param_grid,
    cv=5,
    n_jobs=-1,
    verbose=1
)
grid_search.fit(X, y)

# 最適なモデルを抽出
best_model = grid_search.best_estimator_
predictions = best_model.predict(X_test)

# 6. 出力
output = pd.DataFrame({'PassengerId': test_data.PassengerId, 'Survived': predictions})
output.to_csv('submission_optimized.csv', index=False)

print(f"Best Params found: {grid_search.best_params_}")
</pre>
<h3 style="color: blue;">3. 考察：次のフェーズは「材料そのもの」の変革</h3>
<p><b>エンジニア的な視点：</b><br />
今回の実験で、「今の特徴量の組み合わせ（Embarked, FamilySizeなど）を、今のランダムフォレストで回す限り、これ以上の伸び代はない」ということがはっきりしました。0.78468 は一つの完成形です。ここから 0.79、0.80 を目指すには、微調整（チューニング）ではなく、<b>革新（イノベーション）</b>が必要です。</p>
<hr />
<p>スコアが変わらなかったことは、決して無駄ではありません。「迷い」が「確信」に変わった瞬間です。次回からは、これまでの安定した布陣をベースにしつつ、全く新しいアルゴリズム（XGBoostやLightGBM）を試すか、あるいは「チケット番号の重複」など、より高度な特徴量生成（Feature Engineering）の深淵へと足を踏み入れます！<br />
<br />
</p>]]></content:encoded>
    <dc:subject>【Kaggle挑戦記】</dc:subject>
    <dc:date>2026-04-11T14:42:54+09:00</dc:date>
    <dc:creator>吟遊詩人</dc:creator>
    <dc:publisher>NINJA BLOG</dc:publisher>
    <dc:rights>吟遊詩人</dc:rights>
  </item>
  <item rdf:about="http://learnms.blog.shinobi.jp/Entry/55/">
    <link>http://learnms.blog.shinobi.jp/Entry/55/</link>
    <title>【Kaggle挑戦記】Titanic 攻略 #14：0.77990への後退。欠損率77%の「Cabin」が招いた過学習</title>
    <description>前回（攻略 #13）、自己ベストの 0.78468 を記録し、ついに波に乗ったかと思われました。次なる一手として投入したのは、船の階層を示す Cabin（客室番号）からのデッキ情報。物理的な生存率の差を捉える決定打になるはずが、結果は 0.77990 への大幅なランクダウンとなりました。
1. 敗因...</description>
    <content:encoded><![CDATA[<p>前回（攻略 #13）、自己ベストの 0.78468 を記録し、ついに波に乗ったかと思われました。次なる一手として投入したのは、船の階層を示す <b>Cabin（客室番号）からのデッキ情報</b>。物理的な生存率の差を捉える決定打になるはずが、結果は <b>0.77990</b> への大幅なランクダウンとなりました。</p>
<h3 style="color: blue;">1. 敗因分析：なぜ「強力なヒント」が毒になったのか？</h3>
<p>客室番号から抽出した「デッキ（A〜G）」は、理論上はボートへの距離を示す重要な指標です。しかし、そこには機械学習特有の罠が潜んでいました。</p>
<ul>
<li><b>圧倒的な欠損率：</b> Cabinデータは約77%が欠損しています。この「穴だらけ」のデータを無理にカテゴリ化して学習させたことで、モデルが数少ないサンプルに過剰に反応（過学習）してしまった可能性があります。</li>
<li><b>低頻度カテゴリのノイズ：</b> 「Tデッキ」などの極端に乗客が少ない階層が、予測において「意味のない分岐」を作り出し、未知のデータに対する判断を狂わせたと考えられます。</li>
<li><b>情報の不純度：</b> デッキ情報は Pclass（客室階級）と極めて強い相関があります。すでに Pclass で説明できている情報に、ノイズの多い Cabin 情報を混ぜたことが、モデルの焦点をぼかしてしまったようです。</li>
</ul>
<h3 style="color: blue;">2. 【実装】敗北の記録：デッキ情報を導入した全コード</h3>
<p>結果としてスコアを下げてしまいましたが、検証の記録として、デッキ抽出を組み込んだコードを掲載します。</p>
<pre style="border: 1px solid #ccc; padding: 10px; background-color: #f9f9f9; overflow-x: auto; font-family: Consolas, Monaco, 'Courier New', monospace; line-height: 1.5;">import pandas as pd
import numpy as np
from sklearn.ensemble import RandomForestClassifier

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

# 2. Cabinからデッキ情報を抽出（今回の挑戦ポイント）
for df in [train_data, test_data]:
    df['Deck'] = df['Cabin'].apply(lambda x: x[0] if pd.notnull(x) else 'U')

# 3. 安定した前処理（0.78468時と同じ構成）
train_data['Embarked'] = train_data['Embarked'].fillna('S')
test_data['Embarked'] = test_data['Embarked'].fillna('S')

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

for df in [train_data, test_data]:
    df['FamilySize'] = df['SibSp'] + df['Parch'] + 1
test_data['Fare'] = test_data['Fare'].fillna(test_data['Fare'].median())

# 4. 特徴量の選択（Deckを追加）
features = ["Pclass", "Sex", "Age", "SibSp", "Parch", "Fare", "FamilySize", "Embarked", "Deck"]

# 5. ダミー変数化
X = pd.get_dummies(train_data[features])
y = train_data["Survived"]
X_test = pd.get_dummies(test_data[features])

# 列の整合性を確保
X, X_test = X.align(X_test, join='left', axis=1, fill_value=0)

# 6. モデル学習
model = RandomForestClassifier(n_estimators=500, max_depth=5, random_state=1)
model.fit(X, y)

# 7. 予測と出力
predictions = model.predict(X_test)
pd.DataFrame({'PassengerId': test_data.PassengerId, 'Survived': predictions}).to_csv('submission_deck_failed.csv', index=False)
</pre>
<h3 style="color: blue;">3. 実験結果：0.78468 &rarr; 0.77990 への転落</h3>
<p>「垂直の階層」を加えた結果、スコアは改善するどころか、前々回の水準まで後退しました。</p>
<ul>
<li><b>自己ベスト（攻略 #13）：</b> Score 0.78468</li>
<li><b>今回（Deck追加）：</b> <b style="color: #d73a49;">Score 0.77990</b></li>
</ul>
<h3 style="color: blue;">4. 考察：欠損値は「埋めればいい」ものではない</h3>
<p><b>エンジニア的な視点：</b><br />
今回の学びは、「欠損率があまりに高いカラムは、無理に特徴量化するとモデルの汎化性能を破壊する」ということです。デッキ情報は確かに生存に影響したはずですが、データの密度が薄すぎました。Kaggleにおいては、情報の「正しさ」だけでなく、情報の「密度」と「安定性」がいかに重要かを痛感させられる結果となりました。</p>
<hr />
<p>自己ベスト更新直後の落とし穴。しかし、この 0.77990 という数字が「Deck情報の生投入は悪手である」と教えてくれました。次は、この Deck 情報をより大胆にグルーピング（例：Cabinの有無だけにする等）してリベンジするか、あるいは再び「引き算」をして別の道を探るか&hellip;&hellip;。戦略の練り直しです。<br />
<br />
</p>]]></content:encoded>
    <dc:subject>【Kaggle挑戦記】</dc:subject>
    <dc:date>2026-04-11T14:29:58+09:00</dc:date>
    <dc:creator>吟遊詩人</dc:creator>
    <dc:publisher>NINJA BLOG</dc:publisher>
    <dc:rights>吟遊詩人</dc:rights>
  </item>
  <item rdf:about="http://learnms.blog.shinobi.jp/Entry/54/">
    <link>http://learnms.blog.shinobi.jp/Entry/54/</link>
    <title>【Kaggle挑戦記】Titanic 攻略 #13：自己ベスト更新 0.78468！「引き算」と「新しい視点」の勝利</title>
    <description>前回（攻略 #12）では、精緻に作り込んだ「敬称」データがまさかの裏目に出て、スコアが 0.77751 まで下落するという苦い経験をしました。しかし、その敗北から学んだ「情報の重複を削る」という教訓、そして新たな物理的要因 Embarked（乗船港） の導入が、ついに 0.78468 という自己ベス...</description>
    <content:encoded><![CDATA[<p>前回（攻略 #12）では、精緻に作り込んだ「敬称」データがまさかの裏目に出て、スコアが 0.77751 まで下落するという苦い経験をしました。しかし、その敗北から学んだ「情報の重複を削る」という教訓、そして新たな物理的要因 <b>Embarked（乗船港）</b> の導入が、ついに <b>0.78468</b> という自己ベスト更新を呼び込みました！</p>
<h3 style="color: blue;">1. 逆転の戦略：複雑さを捨て、文脈を足す</h3>
<p>今回の成功のポイントは、単なる「足し算」ではなく、勇気を持った「引き算」にあります。スコアを下げていた <code>Title</code>（敬称）を一度捨て、モデルをスリム化した上で、全く新しい切り口である <code>Embarked</code> を追加しました。</p>
<ul>
<li><b>多重共線性の解消：</b> 性別・年齢・敬称という似通った情報の衝突を解消し、モデルの迷いを取り除きました。</li>
<li><b>乗船港が示す「背景」：</b> シェルブール(C)＝富裕層、サウサンプトン(S)＝一般層といった、港ごとの生存傾向が、Pclassだけでは説明しきれなかった微細な生存確率を補完しました。</li>
</ul>
<h3 style="color: blue;">2. 【実装】0.78468 を叩き出した最終コード</h3>
<p>シンプルながらも強力な、現時点での「最適解」と言えるコード全文です。Embarkedの欠損値を最頻値で埋め、ダミー変数化して投入しています。</p>
<pre style="border: 1px solid #ccc; padding: 10px; background-color: #f9f9f9; overflow-x: auto; font-family: Consolas, Monaco, 'Courier New', monospace; line-height: 1.5;">import pandas as pd
import numpy as np
from sklearn.ensemble import RandomForestClassifier

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

# 2. Embarked（乗船港）の欠損値補完
# 最も多い乗船港である 'S' で補完
train_data['Embarked'] = train_data['Embarked'].fillna('S')
test_data['Embarked'] = test_data['Embarked'].fillna('S')

# 3. 安定した前処理の再現（属性別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'))

for df in [train_data, test_data]:
    df['FamilySize'] = df['SibSp'] + df['Parch'] + 1

# テストデータの運賃欠損を中央値で補完
test_data['Fare'] = test_data['Fare'].fillna(test_data['Fare'].median())

# 4. 学習に使用する特徴量の定義
# 迷走した Title はあえて外し、Embarked を新規採用
features = ["Pclass", "Sex", "Age", "SibSp", "Parch", "Fare", "FamilySize", "Embarked"]

# 5. カテゴリ変数をダミー変数に変換
X = pd.get_dummies(train_data[features])
y = train_data["Survived"]
X_test = pd.get_dummies(test_data[features])

# 学習とテストの列を一致させる
X, X_test = X.align(X_test, join='left', axis=1, fill_value=0)

# 6. モデル学習（500本の決定木、最大深さ5）
model = RandomForestClassifier(n_estimators=500, max_depth=5, random_state=1)
model.fit(X, y)

# 7. 予測の実行と提出用CSV作成
predictions = model.predict(X_test)
output = pd.DataFrame({'PassengerId': test_data.PassengerId, 'Survived': predictions})
output.to_csv('submission_final_best.csv', index=False)

print("Score 0.78468 達成の予測ファイルを保存しました。")
</pre>
<h3 style="color: blue;">3. 実験結果：0.77751 &rarr; 0.78468 へ跳ねる！</h3>
<p>特徴量を1つ入れ替え、整理しただけで、スコアは劇的に改善しました。</p>
<ul>
<li><b>前回（Title追加）：</b> Score 0.77751</li>
<li><b>今回（Title削除＋Embarked追加）：</b> <b style="color: #28a745;">Score 0.78468 (New Record!)</b></li>
</ul>
<h3 style="color: blue;">4. 考察：エンジニアリングの本質は「整理整頓」にあり</h3>
<p><b>エンジニア的な視点：</b><br />
今回の結果が教えてくれたのは、「良いデータ」を足すことと同じくらい、「不要な相関を削る」ことが重要であるという事実です。一時は 0.77 台まで落ち込み絶望しましたが、そこで立ち止まらずに「なぜ落ちたのか？」を考え、別の軸（Embarked）に切り替えたことが勝機となりました。0.785の壁も見えてきました。次はこの安定した土台の上に、さらなるエッセンスを加えていきます。</p>
<hr />
<p>自己ベスト更新の余韻に浸りつつも、Kaggleの道はまだ続きます。今回の勝利で「情報の質と整理」の重要性が証明されました。次は、今回あえて削った `Title` を「ノイズにならない形」で再統合するか、あるいは `Cabin`（客室）の深淵に踏み込むか&hellip;&hellip;。戦略を練り直します！<br />
<br />
</p>]]></content:encoded>
    <dc:subject>【Kaggle挑戦記】</dc:subject>
    <dc:date>2026-04-11T14:18:28+09:00</dc:date>
    <dc:creator>吟遊詩人</dc:creator>
    <dc:publisher>NINJA BLOG</dc:publisher>
    <dc:rights>吟遊詩人</dc:rights>
  </item>
  <item rdf:about="http://learnms.blog.shinobi.jp/Entry/53/">
    <link>http://learnms.blog.shinobi.jp/Entry/53/</link>
    <title>【Kaggle挑戦記】Titanic 攻略 #12：0.77751の呪縛。精緻なクレンジングでも動かぬ壁</title>
    <description>前回（攻略 #11）、家族サイズ（FamilySize）の導入で 0.78229 という自己ベストを叩き出した勢いのまま、今回は「最強の特徴量」と名高い Title（敬称） を投入しました。しかし、返ってきた結果は 0.77751 への後退。さらに、表記ゆれを疑い徹底したクレンジングを施すも、スコア...</description>
    <content:encoded><![CDATA[<p>前回（攻略 #11）、家族サイズ（FamilySize）の導入で 0.78229 という自己ベストを叩き出した勢いのまま、今回は「最強の特徴量」と名高い <b>Title（敬称）</b> を投入しました。しかし、返ってきた結果は <b>0.77751</b> への後退。さらに、表記ゆれを疑い徹底したクレンジングを施すも、スコアは1ミリも動きませんでした。</p>
<h3 style="color: blue;">1. 仮説と検証：なぜ「完璧なはずのコード」でスコアが動かないのか</h3>
<p>抽出ミスや空白の混入を疑い、<code>strip()</code> や徹底した表記統合を行いました。論理的には、これでデータは綺麗になり、モデルはより正確に「Master（子供）」や「Rare（特権階級）」を識別できるはずでした。しかし、結果は非情な現状維持です。</p>
<ul>
<li><b>仮説1：情報の重複（多重共線性）</b><br />
既に「Sex」や「Age（精密補完済み）」、「Pclass」の中に、敬称が持つ情報の大部分が含まれてしまっている可能性。</li>
<li><b>仮説2：ランダムフォレストの限界</b><br />
現在のパラメータ設定（深さ5など）では、これ以上細かい特徴量を読み取っても、汎化性能（未知のデータへの対応力）に繋がっていない可能性。</li>
</ul>
<h3 style="color: blue;">2. 【実装】クレンジングを徹底した Title 導入コード</h3>
<p>不備のない、現時点で最も「綺麗」な状態の全コードです。結果としてスコアは改善しませんでしたが、データ処理の型としては正攻法と言えます。</p>
<pre style="border: 1px solid #ccc; padding: 10px; background-color: #f9f9f9; overflow-x: auto; font-family: Consolas, Monaco, 'Courier New', monospace; line-height: 1.5;">import pandas as pd
import numpy as np
from sklearn.ensemble import RandomForestClassifier

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

# 2. 敬称抽出関数の定義（徹底した空白除去）
def get_title(name):
    if '.' in name:
        return name.split(',')[1].split('.')[0].strip()
    return 'Unknown'

# 3. データのクレンジングと正規化
for df in [train_data, test_data]:
    df['Title'] = df['Name'].map(get_title)
    
    # 希少な敬称の統合
    rare_titles = ['Lady', 'Countess','Capt', 'Col','Don', 'Dr', 'Major', 'Rev', 'Sir', 'Jonkheer', 'Dona']
    df['Title'] = df['Title'].replace(rare_titles, 'Rare')
    
    # 表記ゆれの統一
    df['Title'] = df['Title'].replace(['Mlle', 'Ms'], 'Miss')
    df['Title'] = df['Title'].replace('Mme', 'Mrs')

# 4. 精密なAge補完（Pclass &times; Sex &times; Title）
group_cols = ['Pclass', 'Sex', 'Title']
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'))

# 5. 家族サイズと運賃の処理
for df in [train_data, test_data]:
    df['FamilySize'] = df['SibSp'] + df['Parch'] + 1
test_data['Fare'] = test_data['Fare'].fillna(test_data['Fare'].median())

# 6. 特徴量のダミー変数化
features = ["Pclass", "Sex", "Age", "SibSp", "Parch", "Fare", "FamilySize", "Title"]
X = pd.get_dummies(train_data[features])
y = train_data["Survived"]
X_test = pd.get_dummies(test_data[features])

# 学習とテストで列を完全に一致させる
X, X_test = X.align(X_test, join='left', axis=1, fill_value=0)

# 7. モデル学習（500本の木、深さ5）
model = RandomForestClassifier(n_estimators=500, max_depth=5, random_state=1)
model.fit(X, y)

# 8. 予測と出力
predictions = model.predict(X_test)
pd.DataFrame({'PassengerId': test_data.PassengerId, 'Survived': predictions}).to_csv('submission_cleansed.csv', index=False)
</pre>
<h3 style="color: blue;">3. 実験結果：0.77751（変化なし）</h3>
<p>クレンジング前と後で、スコアは一桁も変わりませんでした。この結果が意味するのは、<b>「現在の私のモデルにとって、Title情報は恩恵よりもノイズ、あるいは過学習の要因になっている」</b>という不都合な真実です。</p>
<ul>
<li><b>前回（FamilySize追加）：</b> Score 0.78229</li>
<li><b>今回（Title追加＋徹底洗浄）：</b> <b style="color: #d73a49;">Score 0.77751</b></li>
</ul>
<h3 style="color: blue;">4. 考察：引き算の勇気が必要か</h3>
<p><b>エンジニア的な視点：</b><br />
「良かれと思って追加した機能が、システム全体のパフォーマンスを落とす」。これは開発現場でもよくある話です。今回の Title 導入は、一見すると情報の追加でしたが、実際にはモデルの焦点をぼかしてしまった可能性があります。0.78229 という自己ベストに戻るには、一度「Titleを捨てる」という引き算の決断、あるいは全く別の次元の特徴量（チケット番号や客室番号）を検討する必要がありそうです。</p>
<hr />
<p>成功体験よりも、こうした停滞こそが思考を深くしてくれます。次回の攻略では、この「0.77751の壁」を突破するために、特徴量の取捨選択を根本から見直す「モデルのスリム化」に挑みます。<br />
<br />
</p>]]></content:encoded>
    <dc:subject>【Kaggle挑戦記】</dc:subject>
    <dc:date>2026-04-11T14:08:48+09:00</dc:date>
    <dc:creator>吟遊詩人</dc:creator>
    <dc:publisher>NINJA BLOG</dc:publisher>
    <dc:rights>吟遊詩人</dc:rights>
  </item>
  <item rdf:about="http://learnms.blog.shinobi.jp/Entry/52/">
    <link>http://learnms.blog.shinobi.jp/Entry/52/</link>
    <title>【DS検定対策】未来は「現在」だけで決まる？確率過程の基礎「マルコフ連鎖」</title>
    <description>明日の天気が「今日の天気」だけで決まるとしたら？ そんな風に、過去の経緯を無視して現在の状態だけで次のステップが決まる確率モデルを「マルコフ連鎖」と呼びます。
1. 【 問題 】
マルコフ過程のうち、時間の変化が「離散的（1ステップずつ途切れている）」であり、次世代の状態が現在の状態のみに依存して決...</description>
    <content:encoded><![CDATA[<p>明日の天気が「今日の天気」だけで決まるとしたら？ そんな風に、過去の経緯を無視して現在の状態だけで次のステップが決まる確率モデルを「マルコフ連鎖」と呼びます。</p>
<h3 style="color: blue;">1. 【 問題 】</h3>
<p>マルコフ過程のうち、時間の変化が「離散的（1ステップずつ途切れている）」であり、次世代の状態が現在の状態のみに依存して決まる確率過程を何と呼ぶでしょうか？</p>
<p>① ブラウン運動<br />
② ポアソン過程<br />
③ マルコフ連鎖<br />
④ 自己回帰モデル</p>
<hr />
<h3 style="color: #d32f2f;">2. 【 解答 】</h3>
<div style="background-color: #eeeeee; border: 2px solid #333; padding: 15px; font-size: 1.2em; font-weight: bold; text-align: center;">正解： ③ マルコフ連鎖</div>
<hr />
<h3 style="color: blue;">3. 整理：過去を振り返らない「忘却」のモデル</h3>
<p>マルコフ連鎖の最大の特徴は「マルコフ性」です。これは、未来がどうなるかは「現在」がどうであるかだけで決まり、「過去」の履歴は関係ないという性質です。</p>
<p>【 マルコフ連鎖のイメージ：お天気の遷移 】</p>
<div style="font-family: monospace; border: 1px solid #ccc; padding: 10px; line-height: 1.6;">[ 今日の状態 ]<br />
「晴れ」<br />
<br />
[ 1ステップ後（明日）の確率 ]<br />
・晴れのまま： 70%<br />
・雨になる： 30%<br />
<br />
[ 判定のルール ]<br />
<b>★ ここがマルコフ連鎖！</b><br />
「昨日が雨だったか、晴れだったか」は一切考えず、<b>「今日が晴れであること」</b>だけを使って明日の確率を計算します。<br />
<br />
--------------------------<br />
<br />
◎ <b>ポイント： 「離散的」とは、時間が1日、2日&hellip;&hellip;、あるいは1回目、2回目&hellip;&hellip;と、とびとびの値をとることを指します。</b></div>
<h3 style="color: blue;">4. なぜDS検定で重要なのか？</h3>
<p>1. <b>状態遷移行列</b>: 状態が移り変わる確率を表形式にまとめることで、将来の予測計算が可能になります。<br />
2. <b>強化学習の基礎</b>: ロボットの制御やAIの意思決定モデルである「マルコフ決定過程（MDP）」のベースとなります。<br />
3. <b>アルゴリズムへの応用</b>: Googleの初期の検索エンジン（ページランク）も、このマルコフ連鎖の考え方を応用しています。</p>
<hr />
<h3 style="color: orange;">5. DS検定形式：実戦4択クイズ</h3>
<p><b>問：マルコフ連鎖において、現在の状態から次の状態へ移る確率を並べた行列のことを何と呼ぶか。</b></p>
<p>① 相関行列 &nbsp; ② 遷移確率行列 &nbsp; ③ 逆行列 &nbsp; ④ 分散共分散行列</p>
<p><b>【 正解： ② 】</b></p>
<p><b>解説：</b> ある状態から別の状態へ移動する確率を網羅したものを「遷移確率行列」と呼びます。この行列を現在の状態ベクトルに掛け合わせることで、次ステップの予測を行うのがマルコフ連鎖の基本計算です。</p>
<hr />
<h3 style="color: blue;">6. まとめ</h3>
<p>DS検定において「時間が離散的」「現在の状態だけで未来が決まる」という記述があれば「マルコフ連鎖」が正解です。非常にシンプルな仮定ですが、複雑な現象をモデル化するための強力な武器になります！</p>]]></content:encoded>
    <dc:subject>DS検定＞1. データサイエンス ＞1-1. 数理基礎・統計学 ＞1-1-2. 統計数理</dc:subject>
    <dc:date>2026-04-10T21:43:59+09:00</dc:date>
    <dc:creator>吟遊詩人</dc:creator>
    <dc:publisher>NINJA BLOG</dc:publisher>
    <dc:rights>吟遊詩人</dc:rights>
  </item>
  <item rdf:about="http://learnms.blog.shinobi.jp/Entry/51/">
    <link>http://learnms.blog.shinobi.jp/Entry/51/</link>
    <title>【Kaggle挑戦記】Titanic 攻略 #12：名前の中に答えがあった。「敬称（Title）」抽出で0.80超えへ</title>
    <description>前回（攻略 #11）では、家族サイズを導入することで 0.78229 という自己ベストを更新しました。しかし、依然として「生存」か「死亡」かをモデルが確信しきれないグレーゾーンが存在します。今回は、これまで無視してきたテキストデータ Name（名前） から、生存率を決定づける隠れた指標「敬称」を掘り...</description>
    <content:encoded><![CDATA[<p>前回（攻略 #11）では、家族サイズを導入することで 0.78229 という自己ベストを更新しました。しかし、依然として「生存」か「死亡」かをモデルが確信しきれないグレーゾーンが存在します。今回は、これまで無視してきたテキストデータ <b>Name（名前）</b> から、生存率を決定づける隠れた指標「敬称」を掘り起こします。</p>
<h3 style="color: blue;">1. なぜ「名前」ではなく「敬称」なのか？</h3>
<p>機械学習モデルにとって「個別の氏名」はただのラベルですが、名前に含まれる <b>Mr. / Miss. / Mrs. / Master.</b> といった敬称（Title）は情報の宝庫です。これらを抽出することで、当時の社会における立場や家族背景をより深くモデルに反映させることができます。</p>
<ul>
<li><b>「女性」の中のさらなる分類：</b> 未婚（Miss）か既婚（Mrs）かを知ることで、生存傾向の差をより細かく捉えます。</li>
<li><b>子供の確実な特定：</b> 名前に <code>Master</code> とあれば、年齢データが欠損していても確実に「男児」であると断定でき、補完精度が劇的に向上します。</li>
<li><b>社会的地位の捕捉：</b> <code>Dr.</code>（医師）や <code>Col.</code>（大佐）といった希少な敬称は、優先的にボートへ誘導された可能性を示唆します。</li>
</ul>
<h3 style="color: blue;">2. 【実装】敬称抽出とモデル構築の全コード</h3>
<p>正規表現的に敬称を切り出し、主要な5グループに統合した上で、これまでの「家族サイズ」「Age補完」と組み合わせて学習させます。</p>
<pre style="border: 1px solid #ccc; padding: 10px; background-color: #f9f9f9; overflow-x: auto; font-family: Consolas, Monaco, 'Courier New', monospace; line-height: 1.5;">import pandas as pd
import numpy as np
from sklearn.ensemble import RandomForestClassifier

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

# 2. 敬称（Title）を抽出する関数の定義
def get_title(name):
    if '.' in name:
        return name.split(',')[1].split('.')[0].strip()
    return 'Unknown'

# 3. 敬称の抽出と統合
for df in [train_data, test_data]:
    df['Title'] = df['Name'].map(get_title)
    rare_titles = ['Lady', 'Countess','Capt', 'Col','Don', 'Dr', 'Major', 'Rev', 'Sir', 'Jonkheer', 'Dona']
    df['Title'] = df['Title'].replace(rare_titles, 'Rare')
    df['Title'] = df['Title'].replace(['Mlle', 'Ms'], 'Miss')
    df['Title'] = df['Title'].replace('Mme', 'Mrs')

# 4. Age（年齢）の精密補完：Pclass &times; Sex &times; Title 別の中央値
group_cols = ['Pclass', 'Sex', 'Title']
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'))

# 5. 既存の特徴量加工（家族サイズと運賃補完）
for df in [train_data, test_data]:
    df['FamilySize'] = df['SibSp'] + df['Parch'] + 1
test_data['Fare'] = test_data['Fare'].fillna(test_data['Fare'].median())

# 6. 特徴量の選択とダミー変数化
features = ["Pclass", "Sex", "Age", "SibSp", "Parch", "Fare", "FamilySize", "Title"]
X = pd.get_dummies(train_data[features])
y = train_data["Survived"]
X_test = pd.get_dummies(test_data[features])

# 列の不一致調整
X, X_test = X.align(X_test, join='left', axis=1, fill_value=0)

# 7. モデル学習（500本の木）
model = RandomForestClassifier(n_estimators=500, max_depth=5, random_state=1)
model.fit(X, y)

# 8. 予測と出力
predictions = model.predict(X_test)
pd.DataFrame({'PassengerId': test_data.PassengerId, 'Survived': predictions}).to_csv('submission_title.csv', index=False)
</pre>
<h3 style="color: blue;">3. 実験結果：0.78229 &rarr; 0.77751 へ下落</h3>
<p>最強のヒントを投入したはずが、結果は無情なスコアダウンとなりました。</p>
<ul>
<li><b>前回（FamilySize追加）：</b> Score 0.78229</li>
<li><b>今回（Title追加）：</b> <b style="color: #d73a49;">Score 0.77751</b></li>
</ul>
<p><b>エンジニア的な考察：</b><br />
「論理的に正しい加工が、必ずしも精度に直結しない」という機械学習の洗礼を受けました。今回の下落の原因として考えられるのは、モデルが <code>Title</code> という強い情報に頼りすぎてしまい、複雑な分岐を作った結果、未知のデータに対する柔軟性を失った（過学習した）可能性です。また、<code>Sex</code> と <code>Title</code> には強い相関があるため、情報が重複して判断を狂わせたのかもしれません。</p>
<hr />
<p>敗北は、次なる改善への貴重なデータです。0.78突破の喜びから一転、モデルの繊細さを痛感する結果となりました。次は、今回追加した <code>Title</code> を削るべきか、あるいはパラメータを再調整して「情報の衝突」を解消すべきか、慎重な見極めが必要です。<br />
<br />
<br />
</p>]]></content:encoded>
    <dc:subject>【Kaggle挑戦記】</dc:subject>
    <dc:date>2026-04-09T20:31:05+09:00</dc:date>
    <dc:creator>吟遊詩人</dc:creator>
    <dc:publisher>NINJA BLOG</dc:publisher>
    <dc:rights>吟遊詩人</dc:rights>
  </item>
</rdf:RDF>
