STEP 5:データの分割とホールドアウト法

🤖 STEP 5: データの分割とホールドアウト法

なぜデータを分割するのか、その重要性を理解します

📋 このステップで学ぶこと

  • なぜデータを分割するのか
  • 訓練データ・検証データ・テストデータの役割
  • train_test_splitの実装
  • ホールドアウト法の実践
  • random_stateの重要性

練習問題: 2問

🎯 1. なぜデータを分割するのか?

機械学習では、モデルを作った後に「このモデルはどれくらい良いか?」を評価する必要があります。しかし、評価の方法を間違えると、実際よりも良い結果に見えてしまうことがあります。

🎓 学校のテストに例えると

❌ 悪い例:同じ問題で学習して、同じ問題でテスト

生徒が問題集を解いて勉強する
→ テストは同じ問題集から出題
→ 生徒は満点を取れる!

でも、これって本当に理解してる?
問題を丸暗記しただけかもしれません。新しい問題が出たら解けない可能性があります。

✅ 良い例:問題集で学習して、見たことのない問題でテスト

生徒が問題集を解いて勉強する
→ テストは見たことのない問題から出題
→ 生徒の本当の実力がわかる!

これなら、「本当に理解しているか」がわかります。

🤖 機械学習でも同じ

機械学習のモデルも、訓練に使ったデータで評価してはいけません。なぜなら、モデルが「データを丸暗記しただけ」なのか、「本当にパターンを学習した」のかわからないからです。

❌ 悪い例:全データで訓練して、全データで評価
# 全データで訓練 model.fit(X, y) # 同じデータで評価(これは「カンニング」!) score = model.score(X, y) print(f”精度: {score}”) # → 99%!すごい! # でも実際は… # モデルはデータを丸暗記しただけかも # 新しいデータが来たら全然予測できないかも
✅ 良い例:データを分割して評価
from sklearn.model_selection import train_test_split # データを「訓練用」と「テスト用」に分割 X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2 # 20%をテスト用に ) # 訓練データで学習 model.fit(X_train, y_train) # テストデータで評価(未知のデータで評価!) score = model.score(X_test, y_test) print(f”精度: {score}”) # → 85%(現実的な数値) # これならモデルの「本当の実力」がわかる!
💡 データ分割の本質

「訓練に使っていないデータで評価する」ことで、モデルが「新しいデータに対応できるか」を確認します。

これができないモデルは、実務では役に立ちません。

📊 2. 訓練・検証・テストデータの役割

データは通常、3つに分割します。それぞれに明確な役割があります。

【データ分割の基本】 全データ(100%) │ ├─ 訓練データ(Training Data):60% │ → モデルを学習させるデータ │ ├─ 検証データ(Validation Data):20% │ → モデルの調整・改善に使うデータ │ └─ テストデータ(Test Data):20% → 最終的な性能評価に使うデータ

① 訓練データ(Training Data)

🎓 役割:モデルを学習させる

モデルが「パターンを学ぶ」ためのデータです。このデータから、モデルは重みやパラメータを決定します。

  • 割合:全データの60〜80%
  • 使い方:model.fit(X_train, y_train)
  • 例え:学校で授業を受ける、問題集を解く

② 検証データ(Validation Data)

⚙️ 役割:モデルの調整・改善

モデルのハイパーパラメータを調整したり、どのモデルが良いかを比較したりするデータです。

  • 割合:全データの10〜20%
  • 使い方:model.score(X_val, y_val) で精度確認 → 調整
  • 例え:模擬試験を受けて、弱点を把握する

③ テストデータ(Test Data)

✅ 役割:最終的な性能評価

モデルが完成した後、一度だけ使うデータです。「このモデルは実際にどれくらいの性能か」を測定します。

  • 割合:全データの10〜20%
  • 使い方:最後に一度だけ model.score(X_test, y_test)
  • 例え:本番の入学試験(やり直しはできない)
  • 重要:テストデータを見てモデルを調整してはいけない

🍳 料理に例えると

【3つのデータの関係:料理の上達に例える】 ■ 訓練データ = レシピを見て練習する材料 → 何度も作って、コツを覚える → 失敗しても大丈夫、何度でもやり直せる ■ 検証データ = 家族に試食してもらう → 「もっと塩を足そう」「火加減を調整しよう」と改善 → フィードバックをもとに腕を上げる ■ テストデータ = レストランでお客様に提供する(本番) → お客様の評価が最終的な評価 → お客様の反応を見てから、その料理を作り直すのはNG! → 次の料理は改善できるが、今回の評価は確定
💡 テストデータの扱い方

テストデータは「最後の砦」です。

テストデータでの結果を見て、「もう少し改善しよう」とモデルを調整すると、テストデータに「過学習」してしまいます。

テストデータは一度だけ、最後に使うようにしましょう。改善したければ、検証データを使います。

📊 シンプルな場合(2分割)

実務では、検証データを使わずに訓練とテストの2分割で済ませることも多いです。特にデータ量が少ない場合や、モデルの調整をあまり行わない場合です。

【2分割の場合】 全データ(100%) │ ├─ 訓練データ(Training Data):80% │ → モデルの学習に使用 │ └─ テストデータ(Test Data):20% → 最終評価に使用

💻 3. train_test_splitの実装

Scikit-learnのtrain_test_split関数を使うと、簡単にデータを分割できます。

基本的な使い方

from sklearn.model_selection import train_test_split # データの準備(例) X = [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]] # 特徴量 y = [0, 0, 1, 1, 1] # ラベル # データ分割(訓練80%、テスト20%) X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, # テストデータの割合(20%) random_state=42 # 乱数シード(再現性のため) ) print(“訓練データ数:”, len(X_train)) # → 4件 print(“テストデータ数:”, len(X_test)) # → 1件

⚙️ 重要なパラメータ

パラメータ 説明
test_size テストデータの割合を指定
・0.2 = 20%
・0.3 = 30%
一般的には0.2〜0.3(20〜30%)
random_state 乱数のシード値(再現性のため)
・同じ値にすると、毎回同じ分割になる
・よく42が使われる(意味はない)
・0、1、123、2023なども可
stratify 層化抽出(分類問題で重要)
・クラスの比率を保ったまま分割
stratify=y とすることが多い
・不均衡データで特に重要
shuffle 分割前にシャッフルするか
・デフォルトはTrue
・時系列データの場合はFalseにする

⚠️ stratify(層化抽出)の重要性

データが不均衡な場合(例:クラス0が90%、クラス1が10%)、普通に分割すると問題が起きることがあります。

❌ 問題:stratifyなしで不均衡データを分割
# 不均衡なデータ(クラス0が90%、クラス1が10%) y = [0]*900 + [1]*100 # 合計1000件 # stratifyなしで分割 X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2 ) # 運が悪いと… # y_test = [0, 0, 0, …, 0] ← クラス1が1つもない可能性! # これでは分類モデルを正しく評価できない # クラス1の予測精度が測れない
✅ 解決策:stratifyを使う
# stratifyを使って分割 X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, stratify=y # ← クラスの比率を保つ ) # 結果 # y_train: クラス0が720件、クラス1が80件(比率 90:10を維持) # y_test: クラス0が180件、クラス1が20件(比率 90:10を維持) # これなら正しく評価できる!
💡 stratifyを使うべき場面
  • 分類問題全般(特に不均衡データ)
  • クラスの比率を保ちたいとき
  • テストデータにすべてのクラスを含めたいとき

回帰問題では通常stratifyは使いません(連続値なので比率という概念がない)。

🔄 4. ホールドアウト法とは?

📌 ホールドアウト法の定義

ホールドアウト法とは?

データを訓練とテストに一度だけ分割して評価する方法です。最もシンプルで、最もよく使われる方法です。

【ホールドアウト法の流れ】 1. データを訓練とテストに分割(例:80%:20%) ↓ 2. 訓練データでモデルを学習 ↓ 3. テストデータで評価 ↓ 4. 終わり(評価結果を報告)

📊 メリット・デメリット

項目 説明
メリット ・シンプルでわかりやすい
・計算が速い(一度だけ学習すればOK)
・データ量が多い場合に有効
デメリット 運に左右される(分割の仕方で結果が変わる)
・データ量が少ないと、評価が不安定
・一部のデータしか評価に使えない

⚠️ ホールドアウト法の問題点

同じモデル、同じデータでも結果が変わる
# 分割1(random_state=42) X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42 ) model.fit(X_train, y_train) print(model.score(X_test, y_test)) # → 85% # 分割2(random_state=123) X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=123 ) model.fit(X_train, y_train) print(model.score(X_test, y_test)) # → 78% # 分割3(random_state=999) X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=999 ) model.fit(X_train, y_train) print(model.score(X_test, y_test)) # → 82% # → 同じモデルなのに、分割の仕方で結果が変わる! # どの結果を信じればいい?
💡 ホールドアウト法の改善:交差検証

ホールドアウト法の「運に左右される」問題を解決するため、交差検証(Cross-Validation)という方法があります。

データを複数回に分けて、それぞれで評価し、平均を取る方法です。より安定した評価ができます。

STEP 22で詳しく学びます。

🎲 5. random_stateの重要性

🔍 random_stateとは?

random_stateは、乱数のシード値を設定するパラメータです。同じ値にすると、毎回同じ結果が得られます。

🎯 なぜrandom_stateが必要?

再現性(Reproducibility)のためです。

実験や研究では、「同じ条件なら、同じ結果が出る」ことが重要です。random_stateを設定することで、誰がいつ実行しても同じ結果になります。

❌ random_stateなし vs ✅ random_stateあり

❌ random_stateなし(毎回違う結果)
# 1回目 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2) print(X_train[0]) # → [3, 4] # 2回目(同じコードを再実行) X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2) print(X_train[0]) # → [7, 8] ← 違う! # 毎回異なるデータが訓練・テストに分かれる # 結果の再現ができない
✅ random_stateあり(毎回同じ結果)
# 1回目 X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42 ) print(X_train[0]) # → [3, 4] # 2回目(同じコードを再実行) X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42 ) print(X_train[0]) # → [3, 4] ← 同じ! # 毎回同じ結果が得られる(再現性がある) # 他の人が実行しても同じ結果になる

❓ random_stateの値は何でもいい?

💡 答え:基本的にはYes

random_stateの値自体に特別な意味はありません。42がよく使われるのは、「銀河ヒッチハイク・ガイド」という小説で「生命、宇宙、そして万物についての究極の疑問の答え」が42だったことに由来します(つまり、単なるジョーク)。

0、1、123、2023など、何でもOKです。

大切なのは、同じプロジェクト内では同じ値を使うことです。

🔧 6. 実践:シンプルなデータセットで体験

完全なコード例

from sklearn.model_selection import train_test_split from sklearn.linear_model import LogisticRegression from sklearn.datasets import load_iris from sklearn.metrics import accuracy_score # 1. データ読み込み(Irisデータセット) iris = load_iris() X = iris.data # 特徴量(花の計測データ) y = iris.target # ラベル(花の種類:0, 1, 2) print(f”全データ数: {len(X)}件”) # 2. データ分割(訓練70%、テスト30%) X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.3, # テスト30% random_state=42, # 再現性のため stratify=y # クラスの比率を保つ ) print(f”訓練データ数: {len(X_train)}件”) print(f”テストデータ数: {len(X_test)}件”) # 3. モデル作成・訓練 model = LogisticRegression(max_iter=200) model.fit(X_train, y_train) # 4. 予測 y_train_pred = model.predict(X_train) y_test_pred = model.predict(X_test) # 5. 評価 train_accuracy = accuracy_score(y_train, y_train_pred) test_accuracy = accuracy_score(y_test, y_test_pred) print(f”\n訓練データでの精度: {train_accuracy:.2%}”) print(f”テストデータでの精度: {test_accuracy:.2%}”)
全データ数: 150件 訓練データ数: 105件 テストデータ数: 45件 訓練データでの精度: 96.19% テストデータでの精度: 95.56%

📊 結果の解釈

💡 良いモデルの見分け方

① 訓練精度とテスト精度が近い場合(差が5%以内)
→ モデルは適切に学習している(良い状態!)
例:訓練96%、テスト95% ← 上の例はこれ

② 訓練精度 >> テスト精度の場合(訓練99%、テスト60%など)
過学習(Overfitting)が起きている
→ モデルを単純にする、データを増やすなどの対策が必要

③ 訓練精度もテスト精度も低い場合(両方60%など)
過小適合(Underfitting)が起きている
→ モデルが単純すぎる、または特徴量が不足

📝 STEP 5 のまとめ

✅ このステップで学んだこと
  • データ分割は「カンニング」を防ぐために必要
  • データは訓練・検証・テストの3つに分ける(シンプルな場合は2分割)
  • train_test_splitで簡単に分割できる
  • stratifyを使うと、クラスの比率を保てる(分類問題で重要)
  • random_stateで再現性を確保できる
  • ホールドアウト法は最もシンプルな評価方法
  • テストデータは最後に一度だけ使う
💡 最重要ポイント

「訓練データで学習、テストデータで評価」
これが機械学習の基本中の基本です。この原則を守らないと、モデルの本当の性能はわかりません。

🔜 次のステップへ

STEP 6では、いよいよScikit-learnのインストールと基本APIを学びます。

実際にコードを書きながら、機械学習の世界を体験していきましょう!

📝 練習問題

問題1 やさしい

データ分割の目的

次のうち、データを訓練とテストに分割する最も重要な理由はどれですか?

  • A. 計算を速くするため
  • B. モデルが未知のデータでどれくらい性能を発揮するか確認するため
  • C. データ量を減らすため
  • D. 複数のモデルを同時に訓練するため
正解:B

なぜBが正解なのか?

データ分割の最も重要な理由は、「モデルが未知のデータでどれくらい性能を発揮するか確認するため」です。

詳しい理由:

  • 訓練に使ったデータで評価すると、「カンニング」状態になる
  • モデルがデータを丸暗記しただけでも、高精度に見えてしまう
  • テストデータ(未知のデータ)で評価することで、真の性能がわかる
  • 実務では「新しいデータに対応できるか」が重要なので、これを確認する

他の選択肢が間違いの理由:

A(計算を速くするため)が間違いの理由:
データ分割自体は計算速度に影響しません。むしろ、訓練データが減る分だけ訓練が速くなることはありますが、それは主目的ではありません。

C(データ量を減らすため)が間違いの理由:
データ量を減らすことが目的ではありません。むしろ、データ量が減ると評価の精度が下がるデメリットがあります。データ分割は「評価の正確性」のために行います。

D(複数のモデルを同時に訓練するため)が間違いの理由:
複数のモデルを訓練することとデータ分割は直接関係ありません。複数のモデルを比較する場合でも、それぞれのモデルで同じ分割を使います。

問題2 ふつう

適切なデータ分割

あなたは1000件のデータで分類モデルを作っています。
データは、クラス0が900件、クラス1が100件と不均衡です。
どのようにデータを分割すべきですか?

  • A. train_test_split(X, y, test_size=0.2)
  • B. train_test_split(X, y, test_size=0.2, random_state=42)
  • C. train_test_split(X, y, test_size=0.2, stratify=y)
  • D. train_test_split(X, y, test_size=0.2, random_state=42, stratify=y)
正解:D

なぜDが正解なのか?

不均衡データを分割する場合、random_statestratifyの両方を設定するのがベストです。

stratify=y が必要な理由:

  • データが不均衡(クラス0:90%、クラス1:10%)
  • stratifyなしだと、運が悪いとテストデータにクラス1が1つも含まれない可能性がある
  • stratifyを使うと、訓練とテストで同じ比率(90:10)を保てる
  • これにより、すべてのクラスに対する評価ができる

random_state=42 が必要な理由:

  • 再現性を確保するため
  • 実験結果を他の人が再現できる
  • デバッグがしやすい(同じ分割で問題を調査できる)
  • 42という値に特別な意味はない(何でもOK)

他の選択肢が不十分な理由:

A が不十分な理由:
stratifyもrandom_stateもない → 不均衡データに対応できず、再現性もない。最も問題のある選択肢。

B が不十分な理由:
random_stateはあるが、stratifyがない → 再現性はあるが、テストデータにクラス1が含まれない可能性がある。不均衡データには不十分。

C が不十分な理由:
stratifyはあるが、random_stateがない → クラス比率は保たれるが、再現性がない。実行するたびに異なる分割になる。

実務でのベストプラクティス:

分類問題では、常にrandom_statestratify=yの両方を設定しましょう。特に不均衡データでは必須です。

📝

学習メモ

機械学習入門 - Step 5

📋 過去のメモ一覧
#artnasekai #学習メモ
LINE