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

【DS検定対策】AIの心臓部!深層ニューラルネットワーク(DNN)の正体

現代のAIブームを支える中心技術「ディープラーニング」。その技術的な実体である「深層ニューラルネットワーク(DNN)」の定義をしっかり整理しましょう。

1. 【 問題 】

深層ニューラルネットワーク(DNN)に関する説明として、最も適切なものはどれでしょうか?

① 入力層と出力層だけで構成され、中間層を持たないネットワークのこと
② 中間層(隠れ層)が多層に重なった構造を持つニューラルネットワークのこと
③ 計算を1回だけで完結させる、統計学的な単線型モデルのこと
④ 人間が特徴量をすべて手作業で設計しなければならないモデルのこと


2. 【 解答 】

正解: ② 中間層(隠れ層)が多層に重なった構造を持つニューラルネットワークのこと

3. 整理:なぜ「深層(ディープ)」と呼ぶのか?

DNNは、人間の脳の神経回路(ニューロン)を模したモデルを何層にも積み重ねたものです。層が深くなることで、より複雑な情報を扱えるようになります。

【 DNNの構造イメージ 】

[ 1. 入力層 ]
画像や数値などのデータを受け取る最初の窓口。

[ 2. 中間層(隠れ層) × 何層も! ]
★ ここが深層ニューラルネットワーク!
1層目:線や点などの単純な特徴を捉える。
2層目:それらを組み合わせて形を捉える。
n層目:高度で抽象的な概念(顔や車など)を理解する。

[ 3. 出力層 ]
最終的な予測結果(「これは猫です」など)を出す。

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

ポイント: このDNNを利用して学習を行う手法そのものを「ディープラーニング(深層学習)」と呼びます。

4. 従来のモデルとの違い

1. 特徴量の自動抽出: かつては人間が「猫なら耳が三角」と教えていましたが、DNNはデータから自動で特徴を見つけ出します。
2. 高い表現力: 層を深くすることで、複雑な非線形(直線では表せない関係)のデータも分類・予測可能になります。
3. 大量データでの進化: データ量が増えれば増えるほど、精度が向上しやすい性質を持っています。


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

問:深層ニューラルネットワーク(DNN)において、入力層に近い層よりも出力層に近い層の方が、より抽象的で複雑な特徴を捉える傾向にある。この記述は正しいか。

① 正しい   ② 誤りである

【 正解: ① 】

解説: DNNは層を経るごとに、単純な特徴から複雑な概念へと情報の抽象度を上げていきます。この「階層的な学習」こそが、画像認識や自然言語処理で高い性能を発揮する理由です。


6. まとめ

DS検定において「多層のニューラルネットワーク」「ディープラーニングの別称・基盤」というキーワードが出たら「DNN」が正解です。現代AIの最も基礎的な用語として、構造のイメージとセットで押さえておきましょう!

PR

【DS検定対策】AIの「知能」をゼロから作る!事前学習の本質とは?

GPTなどの大規模言語モデル(LLM)が、なぜ多様なタスクをこなせるのか。その答えは、特定の用途に絞る前に行われる「事前学習」という工程に隠されています。

1. 【 問題 】

言語モデルにおける「事前学習(Pre-training)」の説明として、最も適切なものはどれでしょうか?

① 専門的なデータを用いて、特定のタスク(翻訳や要約など)に特化させること
② 膨大なラベルなしデータを用いて、言語の構造や一般的な知識をゼロから獲得させること
③ 学習済みモデルを圧縮して、スマートフォンのような小型端末で動くようにすること
④ 人間のフィードバックに基づき、AIの回答をより安全で好ましいものに調整すること


2. 【 解答 】

正解: ② 膨大なラベルなしデータを用いて、言語の構造や一般的な知識をゼロから獲得させること

3. 整理:事前学習の役割と仕組み

事前学習は、モデルが「言葉の並び順」や「概念のつながり」を自ら発見し、知能の土台(基盤モデル)を作るプロセスです。

【 事前学習のメカニズム 】

[ 1. データの性質 ]
インターネット上のテキストや書籍など、正解ラベルのない「生のデータ」をそのまま利用します。

[ 2. 学習タスク(次単語予測) ]
★ ここが本質!
「今日はとても[ ]が良い」の[ ]に入る単語を、膨大なパターンから予測。これを繰り返すことで、文法や知識を身につけます。

[ 3. 到達点 ]
特定の目的は持たないものの、あらゆる質問に応用できる「汎用的な能力」が備わります。

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

重要: この段階で出来上がったものを「基盤モデル(Foundation Model)」と呼びます。

4. なぜ「事前」に学習するのか?

1. 知識の共有: 言語の基本を一度マスターしておけば、後の専門学習(ファインチューニング)が非常に効率的になります。
2. データの有効活用: ラベル付きデータは貴重ですが、ラベルなしデータはネット上に無限にあるため、大規模なスケーリングが可能です。
3. ゼロショット能力: 事前学習だけで、一度も解いたことがない問題に対してある程度の回答を出す能力が生まれます。


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

問:事前学習において、人間が正解を与えなくてもデータ自身から学習信号を作り出す手法を何と呼ぶか。

① 教師あり学習   ② 自己教師あり学習   ③ 転移学習   ④ 蒸留

【 正解: ② 】

解説: 事前学習の多くは、データの一部を隠してそれを予測させる「自己教師あり学習(Self-supervised Learning)」という手法で行われます。これにより、人間が手作業でラベルを付ける手間を省き、巨大なモデルの構築が可能になりました。


6. まとめ

DS検定において「事前学習」は、AIが汎用的な知能を得るための「第一段階」として定義されます。「ゼロから学ぶ」「ラベルなしデータの活用」「自己教師あり学習」というキーワードをセットで押さえておきましょう!


【Kaggle挑戦記】Titanic 攻略 #18:Title再投入の罠。CV 0.8440 からの「急転下落」

前回、LightGBMに Title(敬称) を再投入し、手元の検証(CV)では 0.8440 という過去最高レベルの数字を叩き出しました。「今度こそ0.8突破か?」と震える手で提出。しかし、返ってきた結果は 0.76076。期待とは裏腹に、前回(0.77511)よりもスコアを下げる「急転直下」の結末となりました。

1. 敗因分析:Titleという「劇薬」

CVスコアが上がり、本番スコアが下がる。これは典型的な 過学習(オーバーフィッティング) の再発です。なぜあんなに制限をかけたのに失敗したのか、エンジニア的に振り返ります。

  • 「敬称」のヒントが強すぎた: Titleには生存率に直結する情報(性別や年齢層)が凝縮されています。LightGBMがこれに依存しすぎてしまい、他の特徴量との組み合わせによる「本質的な予測」を疎かにしてしまった可能性があります。
  • CVスコアの「甘い誘惑」: CV 0.8440 という数字は、訓練データ内の特定のパターンを覚え込むことで得られた「虚像」でした。データの少ないTitanicでは、特徴量を1つ増やすだけで、モデルは簡単に「カンニング(暗記)」を始めてしまいます。
  • 汎化性能の喪失: パラメータ探索で min_child_samples: 20 が選ばれ、制約が緩んだことも、ノイズを拾う原因になったと考えられます。

2. 【教訓】光と影を記録した実装コード

CVスコアに一喜一憂し、過学習を招いてしまった「失敗の記録」としてのコードです。

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を追加したのが裏目に…)
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)

3. 実験結果:0.77511 → 0.76076 への後退

アルゴリズムや特徴量を変えるたびに、スコアは大きく変動します。今回はその「負の側面」が強く出てしまいました。

  • 前回: Score 0.77511 (LightGBM 制限付き)
  • 今回: Score 0.76076 (Title追加)
  • 分析: CVスコア 0.844 との乖離は 0.08 以上。明らかな過学習です。

4. 考察:0.8の壁は「暗記」では越えられない

エンジニア的な視点:
Titleという強力な特徴量を入れたことで、モデルは「考える」ことをやめ、「覚える」ことに走ってしまいました。Titanic攻略において、0.8という数字がなぜ高い壁なのか。それは、強力なモデルや特徴量を「いかに使わずに、本質だけを捉えさせるか」という、引き算の思考が求められるからだと痛感しました。


スコアが下がったことは敗北ではありません。この強力なTitleを「どう手なずけるか」、あるいは「Titleに頼らない別の道を探すか」。この試行錯誤こそが、データサイエンスの醍醐味です。次こそ、真の汎化性能を求めて!

【Kaggle挑戦記】Titanic 攻略 #17:0.77511への浮上。LightGBMを「制御」する手応え

前回、最強モデル LightGBM を投入するも、過学習により 0.76315 という惨敗を喫しました。今回はその反省を活かし、モデルにあえて「制約」を課すリベンジマッチ。結果は 0.77511。自己ベストには届きませんでしたが、確かな改善の兆しが見えてきました。

1. 戦略の検証:過学習の抑制は成功したか?

今回の修正の肝は、モデルに「深追いさせない」ことでした。その結果、スコアには以下のような変化が現れました。

  • 本番スコアの向上: 0.76315 → 0.77511(+0.012の改善)
  • CVスコアの適正化: 0.8418 → 0.8373(手元の数字が下がり、本番が上がった)

この「手元の数字を下げて、本番を上げる」という現象こそ、過学習が解消に向かっている決定的な証拠です。F1マシンを路地裏に合わせて減速させたことで、壁にぶつからずにコーナーを曲がれるようになったのです。

2. 【実装】「制約」を刻んだリベンジ・コード

過学習した前回(負け)の設定をコメントで残し、今回(改善)の設定を対比させました。

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)

3. 考察:0.8の壁を越えるために必要なこと

エンジニア的な視点:
今回の実験で、「強力なアルゴリズムを使うなら、強力なブレーキが必要である」という教訓が実証されました。0.77511 という数字は、まだ自己ベストには届きませんが、「モデルの使いこなし」という点では過去最高のレベルに達しています。

ここからさらに 0.03 スコアを伸ばし、0.8の大台に乗るには、モデルの微調整(チューニング)だけでは限界があるかもしれません。次は、データの背後に隠れた「家族の運命」や「チケット番号の繋がり」など、人間らしい洞察(特徴量エンジニアリング)を LightGBM に教え込むフェーズに来ていると感じます。


数字は嘘をつきません。改善した 0.012 は、私たちが正しい方向に進んでいる証拠。この調子で、次なる一手「特徴量の深化」へ進みます!



【Kaggle挑戦記】Titanic 攻略 #16:LightGBMの洗礼。CVスコアの「罠」と過学習の恐怖

前回、最強の刺客 LightGBM を投入し、手元の交差検証(CV)で 0.8418 という驚異的なスコアを叩き出しました。「ついに0.8の大台か?」と期待に胸を膨らませて提出した結果、待っていたのは 0.76315 という非情な現実。自己ベスト(0.78468)から大きく後退する結果となりました。

1. なぜ「手元の高スコア」が「本番の惨敗」を招いたのか?

今回の敗因は、機械学習において最も警戒すべき 過学習(オーバーフィッティング) です。原因をエンジニア的に分析すると、以下の3点に集約されます。

  • モデルが「賢すぎた」: LightGBMは非常に強力なため、約890件という少ない訓練データの「偶然の偏り」まで完璧に学習してしまいました。
  • CVスコアの信憑性: CVスコア 0.84 というのは、訓練データ内での「予行演習」に過ぎません。本番のテストデータとの間に、学習しきれないギャップが存在していました。
  • パラメータの攻めすぎ: max_depth: -1(無制限)や num_leaves: 20 という設定が、少数のデータに対しては複雑すぎた可能性があります。

2. 【実装】光と影を記録した LightGBM コード

CVスコア 0.8418 を出しながらも、本番で 0.76315 に沈んだ「教訓」としてのコードです。

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)

3. 実験結果:期待と現実のギャップ

アルゴリズムを変えたことで、数字に大きな「動き」が出ましたが、今回は悪い方へ転がりました。

  • 自己ベスト(ランダムフォレスト): Score 0.78468
  • 今回(LightGBM): Score 0.76315(CVスコアとの乖離:-0.078)

4. 考察:高すぎるCVスコアを疑え

エンジニア的な視点:
「手元で完璧なモデルが、外の世界で通用するとは限らない」。今回の結果は、AI開発における本質的な難しさを教えてくれました。CVスコアが 0.84 まで跳ね上がった時点で、「学習しすぎではないか?」と疑うべきだったのです。Titanicのような少人数データでは、LightGBMのような強力なモデルを「いかに抑え込むか(正則化)」が次の鍵となります。


スコアダウンは失敗ではなく、モデルの特性を理解するための貴重なデータです。次は、LightGBMをあえて「弱く」する(パラメータを厳しく制限する)か、あるいはランダムフォレストの安定感を見直すか。この 0.02 の差を埋めるための戦いは、さらに深化していきます。