訓練・テスト分割(Train/Test Split):データを訓練用と評価用に分けること。 ML プロジェクトの最も基本的な作法
このページは「訓練・テスト分割(Train/Test Split)」の用語解説です。 機械学習の基礎カテゴリにおける重要概念で、 機械学習の基礎 グループ教材の中で繰り返し登場します。 数式・実コード・落とし穴を 1 ページに集約し、 SSDSE-B-2026 都道府県データ(47 件 × 112 列)を題材に 手を動かしながら理解できるよう構成しています。
別称:ホールドアウト法。 まず 💡 30秒結論 で全体像を、 次に 🎨 直感 → 📐 数式 → 🧮 実値 → 🐍 Python の順で読むのがおすすめ。
訓練・テスト分割は、 「学習に使ったデータでは性能を測らない」という原則を守るための最も簡単な方法です。 全データを 70/30 や 80/20 で 2 つに割り、 前者でモデル学習、 後者で評価のみ。
落とし穴は (1) シャッフルなしで偏る、 (2) 時系列を無視、 (3) 同じグループ(同じ顧客・同じ県)が訓練と評価に跨ると 事実上のリーケージ。 用途に応じて train_test_split / KFold / StratifiedKFold / TimeSeriesSplit / GroupKFold を選びます。
本概念は次のように記述されます(KaTeX で描画)。
英語名 Train/Test Split。 別称:ホールドアウト法。
記号と意味を逐一突き合わせて読みます。 慣れないうちは式を「日本語で読む」ことが理解の近道です。
SSDSE-B で ランダム分割 / 層化分割 / 時系列分割 / グループ分割 の 4 通りを比較します。
データ出典:SSDSE-B-2026(独立行政法人統計センター)。 47 都道府県 × 複数年(最新 2023)の社会統計データ。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | import pandas as pd import numpy as np from sklearn.model_selection import (train_test_split, StratifiedKFold, TimeSeriesSplit, GroupKFold) df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=1) df = df[df['年度'] == 2023].reset_index(drop=True) df['ラベル'] = (df['15歳未満人口'] / df['総人口'] >= (df['15歳未満人口']/df['総人口']).median()).astype(int) X = df[['総人口','65歳以上人口']].values y = df['ラベル'].values # (1) ランダム 70/30 X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=0.3, random_state=0) print('(1) random ', y_tr.mean(), y_te.mean()) # (2) 層化(クラス比率を保つ) X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=0.3, random_state=0, stratify=y) print('(2) stratify', y_tr.mean(), y_te.mean()) # (3) 時系列分割(複数年データ前提:年度列で時系列扱い) df_ts = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=1) tss = TimeSeriesSplit(n_splits=4) for tr_idx, te_idx in tss.split(df_ts): print('(3) TS train_yrs=', sorted(df_ts.iloc[tr_idx]['年度'].unique())[-3:], ' test_yrs=', sorted(df_ts.iloc[te_idx]['年度'].unique())) break # (4) グループ分割(都道府県をグループに) gkf = GroupKFold(n_splits=5) for tr_idx, te_idx in gkf.split(df_ts, groups=df_ts['都道府県']): print('(4) Group test 県=', df_ts.iloc[te_idx]['都道府県'].unique()[:5]) break |
実行結果の要約(出力は環境依存。 概算値):
| 項目 | 値 |
|---|---|
| (1) ランダム y平均 訓練/テスト | 0.53 / 0.40 |
| (2) 層化 y平均 訓練/テスト | 0.50 / 0.50 |
| (3) TimeSeriesSplit | 古い年で訓練 → 新しい年で評価 |
| (4) GroupKFold | 1 県は訓練か検証どちらか片方 |
| 47 県データの推奨 | 5-fold StratifiedKFold |
| 時系列データの推奨 | TimeSeriesSplit |
scikit-learn / pandas を使った最小実装パターン。 上の SSDSE-B 計算と同じスタイルですが、 ここでは「読み込み→前処理→学習→評価」のテンプレを 4 つのスニペットに分けます。
1 2 3 4 | import pandas as pd df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=1) df = df[df['年度'] == 2023].reset_index(drop=True) print(df.shape, df.columns.tolist()[:8]) |
1 2 3 | X = df[['総人口','65歳以上人口']].values y = df['15歳未満人口'].values print('X shape =', X.shape, ', y shape =', y.shape) |
1 2 3 4 5 6 | from sklearn.model_selection import train_test_split from sklearn.ensemble import RandomForestRegressor X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=0.3, random_state=0) model = RandomForestRegressor(n_estimators=300, random_state=0).fit(X_tr, y_tr) print('R^2 (test) =', model.score(X_te, y_te)) |
1 2 3 4 5 6 | import matplotlib.pyplot as plt pred = model.predict(X_te) plt.scatter(y_te, pred) plt.plot([y_te.min(), y_te.max()], [y_te.min(), y_te.max()], 'r--') plt.xlabel('実測'); plt.ylabel('予測'); plt.title('「訓練・テスト分割」関連モデルの予測精度') plt.tight_layout(); plt.savefig('out.png', dpi=150) |
※ 「訓練・テスト分割」固有の本格コードは上の 🧮 SSDSE-B 実値計算 節を参照。