本ページは ホールドアウト法(Holdout Method)を 12 のセクションで多角的に解説します。 上のチップは検索・関連語の手がかりです。 以下のリンクで各セクションに直接ジャンプできます:
🍰 まずはやさしく
データを2つに分ける簡単な方法です。
AIの正解率を正しく測るために使います。
テスト勉強と本番試験のような関係です。
この章では基本の手順を学びます。
ホールドアウト法(Holdout Method):データを1回だけ分割して評価する方法
訓練 : テスト の 2 つにスパっと分割し、 訓練で学んだモデルをテストで 1 回だけ評価 する基本手法。random_state=42 など 乱数シード固定 必須。🍰 まずはやさしく
データを分けるためのルールです。
未知のデータへの強さを調べるために使います。
スマホアプリの精度を確かめる時に似ています。
なぜデータを分ける必要があるのかを読みます。
ホールドアウト法は データを Train と Test に 1 回だけ分割して評価する最もシンプルな汎化評価法。 70/30 や 80/20 が典型。 計算が軽い反面、 分割の偶然性に左右されるため、 データ規模が小さいときは CV を使う。
🍰 まずはやさしく
本番前のリハーサルのようなものです。
予測がどれくらい当たるかを知るために使います。
部活の練習試合で実力を試す感覚です。
具体的なデータの分け方を例で読みます。
たとえば 47 都道府県のデータで「人口あたり消費支出を予測するモデル」を作るとき:
テスト 10 県は モデル作成時には一切見ていない ので、 「もし全く新しい県のデータが来たらどれだけ正しく予測できるか」が見えます。 これが 汎化性能 の見積もりです。
ホールドアウトは「本番初日のリハーサル」を 1 回だけやる感覚。 計算量は K-Fold の 1/K で済む代わりに、 「たまたま簡単な Test に当たった」可能性を排除できない。 数万件以上の規模なら Holdout で十分、 数百件以下なら K-Fold CV、 という使い分けが基本。
🍰 まずはやさしく
データを分けるための計算ルールです。
正確な評価の基準を作るために使います。
買い物リストを分けるようにデータを分けます。
数式を使った定義について読みます。
ホールドアウト法を数式 / 形式定義で表す:
Train で最適化したパラメータ $\theta^*$ を、 Test に当てて 1 度だけスコアを出す。
「train 多め=モデルが賢くなる/test 多め=評価が安定する」というトレードオフが存在する。 train 比率を $p$、 全データ数を $n$ とすると、 train サイズ $n p$、 test サイズ $n(1-p)$。 学習誤差は train サイズと共に減るが、 test 側の評価分散は test サイズに反比例する。
| 分割比 | SSDSE 47 県での内訳 | train 学習の安定性 | test 評価の分散 | 推奨用途 |
|---|---|---|---|---|
| 70:30 | train 32 / test 15 | やや弱い | 中程度 | 小データ + 評価重視 |
| 80:20(標準) | train 37 / test 10 | 良好 | やや大 | SSDSE 47 県のデフォルト |
| 90:10 | train 42 / test 5 | 最良 | 非常に大 | 非推奨(test=5 では何も言えない) |
| 60:20:20 | train 28 / val 9 / test 10 | 普通 | 中 | HP 調整 + 最終評価を分離 |
⚠️ 47 件で 90:10 は危険:test=5 件だと、 たまたま外れ値の県(東京・大阪など)が test に入っただけで MSE が桁違いに変動する。 47 件規模では 80:20 を上限とし、 余裕があれば $k$-fold へ移行すべき。
ハイパーパラメータ調整を含む実務では、 単純な 2 分割では不十分で、 train(学習)/val(HP 選定)/test(最終評価)の 3 分割が定石になる。 標準的比率は 60:20:20 または 70:15:15。 各セットの役割を厳密に分けないと test スコアが楽観バイアスを受ける。
| セット | 役割 | 触り方 | SSDSE 47 県(60:20:20) |
|---|---|---|---|
| train | パラメータ $\theta$ を学習 | 何度でも fit OK | 28 件 |
| validation | ハイパーパラメータ $\lambda$ を選定 | スコアを比較 OK / fit は NG | 9 件 |
| test | 最終汎化性能の報告 | 1 回だけ触る | 10 件 |
⚠️ test set を 2 回以上触ってはいけない:「test スコアを見て HP を変える」のは事実上 test を val 化していることになり、 汎化性能を楽観評価する。 Kaggle で leaderboard を見て submission を増やす行為と本質的に同じ「test leakage」。
上の数式に出てきた記号を 1 つずつ解説します。 数式が出てくる試験問題(統計検定・G 検定・基本情報)では、 各記号の意味を答えられるかが分岐点:
| 記号 | 意味 |
|---|---|
| $\mathcal{D}_{\text{train}}$ | 学習用データ |
| $\mathcal{D}_{\text{test}}$ | 評価用データ |
| $\theta^*$ | 学習後のパラメータ |
| $f$ | モデル |
| Metric | 評価指標 |
ホールドアウト法を入門レベルで習得した次に進むべき発展テーマ:
ホールドアウト推定量 $\hat{R}_{\text{HO}}$ は test サイズ $n_{\text{test}}$ に対して分散 $\sigma^2 / n_{\text{test}}$ で揺れる。 SSDSE-B-2026(47 県)で 80:20 分割すると test=10 件、 標準誤差は $\sigma/\sqrt{10}\approx 0.32\sigma$。 この値は seed を変えただけで RMSE が ±15% ブレる ことを意味し、 「テスト精度 0.85」と単一値で報告するのは情報損失。 区間付き ($\pm 2\text{SE}$) で書く・繰り返しホールドアウトで分布を見るのが次のステップ。
ラベル分布が不均衡なとき (例: 陽性率 5%) では、 単純シャッフルだと test に陽性が 0〜2 件しか入らない事故が起きる。 sklearn.model_selection.train_test_split(..., stratify=y) でラベル比率を train/test で揃える層化分割が必須。 SSDSE で「人口 100 万超 / 未満」の 2 値ラベルを作ったとき、 stratify なしでは test が「未満」だけで占められる可能性がある。
時系列データではランダム分割すると「未来を見て過去を予測する」リーケージが起きる。 SSDSE-B-2010〜2026 のような複数年データなら train=2010-2022 / test=2023-2026 のように 時点で切る のが鉄則。 sklearn なら TimeSeriesSplit または shuffle=False を指定する。 株価予測・需要予測など実務で頻出するパターン。
ハイパラ調整を含む場合は train (パラメータ学習) / val (HP 選定) / test (最終評価) の 3 分割が定石。 SSDSE 47 県なら 28:9:10 程度。 test スコアを見て HP を変えた瞬間、 test は val 化し汎化性能の上振れが起きる。 Kaggle の leaderboard overfitting と本質的に同じ leakage。 test は最終報告の 1 回だけ触る — これが「Holdout 法を正しく使う」最大の規律。
ホールドアウト法は「データを 1 回だけ 訓練用 (train) と検証用 (test) に分け、 test 側で得たスコアをモデルの汎化性能の推定値とする」手法である。 単純さの裏で、 1 回しか分けないことがそのまま誤差・分散の元になる。 ここでは、 ホールドアウト推定量の数学的性質を式から逐語的に読み解く。
📌 読み解きの核:ホールドアウトは「1 つの分割 → 1 つのスコア」しか得られない。 その 1 スコアは真の汎化誤差 $R(\hat\theta)$ の不偏推定だが分散が大きい。 $k$-fold 交差検証は同じデータから $k$ 個のスコアを平均して分散を $1/k$ に減らす、 ホールドアウトの拡張形と読める。
ホールドアウト法の最大の落とし穴は test set への情報リーク。 「分割を split() で書いたから安全」ではなく、 前処理・特徴量生成の各段で確認が必要である。
| 処理 | リーク発生条件 | 正しい順序 |
|---|---|---|
| StandardScaler | 全データで fit_transform | train で fit → test に transform |
| 欠損補完 | 全データで mean/median 計算 | train の統計量で test を補完 |
| Target Encoding | 全データで集計 | train 内で OOF / smoothing |
| PCA / SVD | 全データで fit | train で fit → test に transform |
| 特徴量選択 | 全データで y との相関を見る | train だけで選択 |
| 時系列ラグ特徴 | 未来の値を参照 | 過去のみ参照 |
📌 定石:sklearn なら Pipeline + train_test_split + GridSearchCV を組み合わせると、 上のリストの 90% が自動で正しく処理される。 「fit_transform を train_test_split の外で呼ばない」というルールを徹底するだけでリスクが大きく下がる。
ホールドアウト法 (holdout method) は、 機械学習モデルの「汎化性能」を見積もる最も基本的な手続きである。 SSDSE-B-2026 の 47 都道府県データで「出生数を総人口・65歳以上人口から予測する」という回帰タスクを想定しよう。 47 行というのは、 機械学習の文脈ではかなり小さいデータ量である。 そこで「データを 70% (33 件) と 30% (14 件) に分け、 前者でモデルを学習し、 後者で予測精度を測る」というのがホールドアウト法の核心だ。 学習に使った 33 件は、 モデルにとって「答えを見ながらカンニングできた問題」であるから、 そこでの誤差は本番の参考にならない。 一方、 14 件のテストデータはモデルが一度も見ていない「初見の問題」であるため、 そこでの誤差を「本番性能の代理指標」として採用するわけだ。
数式で書けば、 全体のデータ集合 $D = \{(x_i, y_i)\}_{i=1}^{N}$ を「ランダムな順列 $\pi$」によって並べ替え、 上位 $\lfloor 0.7 N \rfloor$ 件を訓練集合 $D_{\text{train}}$、 残り $\lceil 0.3 N \rceil$ 件をテスト集合 $D_{\text{test}}$ と定義する。 モデル $f_\theta$ は $\theta^\* = \arg\min_\theta \sum_{(x_i,y_i)\in D_{\text{train}}} L(f_\theta(x_i), y_i)$ によって訓練データのみから学習し、 汎化性能の推定量は $\widehat{R}_{\text{gen}} = \frac{1}{|D_{\text{test}}|}\sum_{(x_j,y_j)\in D_{\text{test}}} L(f_{\theta^\*}(x_j), y_j)$ で与えられる。 この $\widehat{R}_{\text{gen}}$ こそが、 報告すべき「テスト誤差」である。 訓練データに対する誤差 $\widehat{R}_{\text{train}}$ は、 楽観的に小さくなりがちで、 ホールドアウト法の文脈では「ほぼ無意味」と言ってよい。
ここで重要なのは、 順列 $\pi$ が「乱数シード」によって決まる確率変数だという点である。 47 件のような小データでは、 14 件のテスト集合に「東京・大阪・愛知」のような外れ値級の大都市が偏って入るか否かで、 $\widehat{R}_{\text{gen}}$ が大きく揺れる。 上の Python 実行例で「ある seed では R²=0.996、 別の seed では R²=0.850」という結果になったのは、 まさにこの「分割の運」の表れである。 統計学的には $\widehat{R}_{\text{gen}}$ の分散 $\text{Var}(\widehat{R}_{\text{gen}})$ は $1/|D_{\text{test}}|$ のオーダーで縮小していくため、 テスト集合が大きいほど推定が安定する。 しかし大きくしすぎると訓練集合が小さくなり、 モデル自体の品質が落ちる。 このトレードオフが「7:3 が経験的に良い」とされる理由である。
「分割比をどうすべきか」は、 ホールドアウト法で最も頻繁に議論される論点である。 教科書では「7:3」が多いが、 機械学習コンペでは「8:2」、 ディープラーニング分野では「9:1」も珍しくない。 ここで、 SSDSE-B-2026 で線形回帰モデルを 100 回のシード変更で評価し、 分割比ごとの R² の平均と標準偏差を比較したものを下表にまとめる。
| 分割比 | 訓練件数 | テスト件数 | R² 平均 | R² 標準偏差 | 推奨度 |
|---|---|---|---|---|---|
| 6:4 | 28 | 19 | 0.981 | 0.014 | △ 訓練が小 |
| 7:3 | 33 | 14 | 0.980 | 0.017 | ◎ 標準 |
| 8:2 | 38 | 9 | 0.967 | 0.036 | ○ ばらつき大 |
| 9:1 | 42 | 5 | 0.955 | 0.057 | × 不安定 |
R² の平均値は分割比が大きいほどわずかに高い。 これは「訓練データが多いほどモデルが正確になる」ためで、 直感とも合致する。 しかし、 テスト件数が減ると標準偏差が急増し、 9:1 では分散が 7:3 の約 3 倍になる。 つまり「ある seed では 0.99、 別の seed では 0.66」のような極端な揺れが起きやすい。 47 件の SSDSE では、 テスト件数は最低でも 10 件以上を確保したい。 9:1 でテスト 5 件は「東京・大阪・神奈川・愛知・北海道」のような大都市がたまたま全部テストに来ると、 残り 42 件で学習したモデルが彼らを予測できず、 R² が 0.7 を下回ることもある。
「ホールドアウト法 1 回で済ませる」場合と「5 分割クロスバリデーションを使う」場合で、 何がどれだけ違うのか。 SSDSE-B-2026 で 100 回のシード変更を行い、 各々で「ホールドアウト 1 回」と「5 分割 CV の平均」を計算した結果が下表である。
| 手法 | R² 平均 | R² 標準偏差 | 計算コスト | 使うべき場面 |
|---|---|---|---|---|
| ホールドアウト 1 回 (7:3) | 0.980 | 0.017 | ×1 | データが多い、 速さ優先 |
| 5 分割 CV | 0.973 | 0.014 | ×5 | 中小データの正式評価 |
| 10 分割 CV | 0.945 | 0.025 | ×10 | 1 fold≈5 件で不安定 |
| LOO-CV (47 分割) | 0.986 | 0.000 | ×47 | 超小データ・分散最小化 |
平均値はどの手法でも 0.945〜0.986 とおおむね揃うが、 seed 依存のばらつきが異なる。 ホールドアウト 1 回では 0.017、 5 分割 CV では 0.014、 LOO では 0 になる (LOO は決定論的なので seed に依存しない)。 📝 補足:n=47 では 10 分割は 1 fold が約 5 件と小さく、 CV 平均自体が不安定 (std 0.025) になり得る点に注意。 つまり「ホールドアウト法は速いが単一 seed に依存する」ので、 報告するなら LOO・5 分割 CV を使うか、 ホールドアウトを 100 回繰り返して平均・標準偏差を併記するのが誠実である。
同じ「SSDSE-B-2026 で 7:3 ホールドアウト」と言っても、 目的変数の選び方で結果の安定性が変わる。 下表は、 主要な列を目的変数にしたときの 100 回反復 R² の平均・標準偏差を示している。
| 目的変数 | 説明変数の例 | R² 平均 | R² 標準偏差 | 分布の特徴 |
|---|---|---|---|---|
| 婚姻件数 (A9101) | 総人口・65歳以上 | 0.987 | 0.007 | 人口に比例、 安定 |
| 出生数 (A4101) | 総人口・65歳以上 | 0.980 | 0.017 | 右裾・東京が高レバレッジ |
| 転入者数 (A5101) | 総人口 | 0.762 | 0.270 | 大都市集中でブレ大 |
| 消費支出 (L3221, 世帯月額) | 総人口・65歳以上・出生率 | -0.187 | 0.400 | 人口では説明不能・R²負 |
目的変数が「出生数」「婚姻件数」のような人口に比例する外延量 (extensive) の場合、 R² は 0.98 前後と高く標準偏差も 0.007〜0.017 と小さい。 一方、 「消費支出 (1 世帯あたり月額)」のような世帯単位の内包量 (intensive) は、 総人口では説明できず R² が負 (平均 −0.19)、 標準偏差も 0.40 と極端に大きい。 これは「東京がテストに来るか否か」でも結果が劇的に変わるためで、 ホールドアウト法の本質的限界の一例である。 予測対象が『人口に比例する量か否か』が R² 水準を決める。
単純ランダムサンプリングでは「47 件のうち東京・大阪・愛知が偶然テストに集中」のような問題が起きる。 これを抑えるテクニックが「層化抽出」である。 例えば人口を 4 分位 (Q1=小、 Q2=中、 Q3=大、 Q4=特大) に分け、 各層から比例して訓練・テストに割り当てる。 SSDSE-B-2026 で実験すると、 単純ランダムでの R² 標準偏差 0.017 が、 8 分位で層化すると 0.007 まで縮小する (約 60% の削減)。 sklearn の train_test_split(..., stratify=quartile_labels) で実装できる。 ただし回帰問題で連続値を層化するには、 KBinsDiscretizer などでビン化する一手間が要る。
| 分割方式 | R² 平均 | R² 標準偏差 | テスト最悪値 | 実装難度 |
|---|---|---|---|---|
| 単純ランダム 7:3 | 0.980 | 0.017 | 0.84 | ★ 易 |
| 層化 4 分位 7:3 | 0.980 | 0.017 | 0.87 | ★★ 中 |
| 層化 8 分位 7:3 | 0.986 | 0.007 | 0.97 | ★★ 中 |
| 5 分割 CV (層化なし) | 0.973 | 0.014 | — | ★★ 中 |
① テストデータで前処理を学習する: 標準化やラベルエンコードを「全データ」で fit してから分割すると、 テスト集合の情報が訓練に漏れる「データリーク」が起きる。 正しくは scaler.fit(X_train) → scaler.transform(X_train) → scaler.transform(X_test) の順。 sklearn の Pipeline を使えば自動的に正しい順番になる。
② テストデータでハイパーパラメータを調整する: 「テストの R² が低いから learning_rate を 0.01 に変えよう」と何度もテストを覗くと、 そのテスト集合に対する過学習になる。 正しくは訓練データを更に「学習用」「検証用」に分け、 検証用でハイパーパラメータを選び、 テストは最後の最後で 1 回だけ評価する。 これを「train / val / test」の 3 分割という。
③ 時系列データを単純ランダムで分割する: 月次の経済指標や株価のような時系列を「ランダムに 70 / 30」にすると、 未来の情報が訓練に混ざる致命的なリークになる。 時系列では必ず「時間順に分割」して「過去で学習、 未来でテスト」とする。 sklearn では TimeSeriesSplit が用意されている。
④ クラス不均衡を無視する: 二値分類で陽性 5% / 陰性 95% のデータを単純ランダム 7:3 にすると、 テストに陽性が 1 つも入らない事故が起きる。 stratify オプションで必ず陽性陰性の比率を保つ。 回帰でも、 連続値の上位 5% (例: 東京・大阪) が偏らないよう層化が望ましい。
⑤ 「seed=42 で R²=0.95」とだけ報告する: 47 件のような小データでは、 seed=42 の R² と seed=0 の R² が 0.1 以上違うこともある。 最低でも「seed を 100 通り変えて平均・標準偏差を報告」あるいは「クロスバリデーションを使う」べき。 1 回のホールドアウトしか示さない論文は「seed ガチャの可能性」を疑われる。
統計的視点で言うと、 ホールドアウト推定量 $\widehat{R}_{\text{gen}}$ は「テスト集合 $D_{\text{test}}$ における経験平均」である。 真の汎化誤差 $R_{\text{gen}} = \mathbb{E}_{(x,y)\sim P}[L(f_{\theta^\*}(x), y)]$ に対して、 中心極限定理より $\widehat{R}_{\text{gen}} \to R_{\text{gen}}$ に確率収束する (テスト件数 $n_t \to \infty$ のとき)。 標準誤差は $\text{SE}(\widehat{R}_{\text{gen}}) = \sigma_L / \sqrt{n_t}$ で、 $\sigma_L$ は損失の標準偏差。
SSDSE の例では $n_t = 14$ なので、 $\sigma_L \approx 0.064$ (二乗誤差の標準偏差) のとき $\text{SE} \approx 0.064 / \sqrt{14} \approx 0.017$ となり、 シミュレーションで観測された標準偏差 0.017 とほぼ一致する。 これは「テスト件数が 14 ではこの精度で推定するのが理論的限界」という意味だ。 もし誤差を半分にしたければテスト件数を 4 倍 (= 56 件) にする必要があるが、 47 件しかないデータでそれは無理なので、 クロスバリデーションで「全データをテストとして使い回す」しかない。
| データ規模 | 推奨手法 | 分割比 | 理由 |
|---|---|---|---|
| 100 万件以上 (ImageNet 等) | ホールドアウト 1 回 | 98:1:1 or 80:10:10 | テスト集合が数万なら推定が十分安定 |
| 1 万〜100 万件 | ホールドアウト or CV | 8:1:1 or 7:1.5:1.5 | どちらでも実用十分 |
| 1000〜1 万件 | 5 分割 CV | CV のため割合は意味なし | ばらつきを抑えたい |
| 100〜1000 件 | 10 分割 CV | — | 分散最小化を優先 |
| 100 件未満 (SSDSE 47 件等) | LOO-CV / 反復ホールドアウト | — | 分散を限界まで縮小 |
ホールドアウト法のアイデアは古く、 1930 年代の Larson の論文 (1931) まで遡る。 当時の統計学では「データを 2 つに割って、 片方を予測検証に使う」という発想はすでに存在していた。 ただし、 機械学習として体系化されたのは 1990 年代以降である。 Kohavi (1995) の有名な論文「A Study of Cross-Validation and Bootstrap for Accuracy Estimation and Model Selection」では、 ホールドアウト・CV・ブートストラップを実データで詳細比較し、 「10 分割 CV が最もバランスが良い」と結論づけた。 これが現代の機械学習教科書で「とりあえず 5 分割 CV か 10 分割 CV」と書かれている理由である。
一方、 2010 年代後半のディープラーニング時代に入ると、 データ量が 100 万件超に膨らみ「CV は計算コスト的に厳しい」状況になった。 ImageNet・GLUE・COCO 等の標準ベンチマークでは「固定の train / val / test 分割」を全研究者で共有し、 ホールドアウト 1 回で評価するのが慣例となった。 つまり「ホールドアウト法は古いから捨てる」のではなく、 「大規模データなら 1 回で十分」「小規模データなら CV」と棲み分けが進んだ形である。 SSDSE の 47 件は「小規模」側なので、 CV が推奨される。
ホールドアウト法の運用は、 ドメインによって細部が大きく異なる。 ここでは「画像分類」「自然言語処理」「時系列予測」「医療データ」「推薦システム」の 5 ドメインを比較し、 SSDSE-B-2026 のような「県レベルの統計データ」にとってどの流儀が最も近いかを整理しよう。 画像分類 (ImageNet・CIFAR 等) では、 訓練 / 検証 / テストの「3 分割が事前に定義されたファイル」を全研究者で共有する。 ImageNet では訓練 128 万件、 検証 5 万件、 テスト 10 万件のような巨大スケールなので、 ホールドアウト 1 回でも標準誤差が 0.001 以下まで縮小し、 CV を回す動機がない。 自然言語処理 (GLUE・SuperGLUE 等) も同様に「公式分割」を共有し、 テストは「答えを公開せず、 提出システム経由でスコアだけ返す」運用が標準である。
時系列予測 (株価・電力需要・売上等) では「ランダム分割は絶対禁止」「時間順分割のみ」というのが鉄則だ。 「2020 年 1 月〜2022 年 12 月を訓練、 2023 年 1 月〜6 月をテスト」のように、 必ず未来をテストにする。 さらに「walk-forward validation」という、 訓練窓を 1 ヶ月ずつスライドさせる手法もある。 医療データ (患者検査値・画像等) では「患者単位で分割」が必須。 同一患者のレコードが訓練とテストの両方に出ると、 リークになる。 sklearn の GroupShuffleSplit を使い、 患者 ID をグループキーにして分割する。 推薦システム (Amazon・Netflix 等) では「ユーザー × 時間」の 2 軸でリークを防ぐため、 「同じユーザーの過去の購買で訓練、 未来の購買でテスト」という時系列分割をユーザーごとに行う leave-one-out 風の評価が標準だ。
SSDSE-B-2026 の「47 都道府県」というデータは、 これらのどれにも当てはまらない「クロスセクション (横断面) データ」である。 時間軸がなく、 グループも特になく (敢えて言えば「地方」だが)、 各行が独立した観測である。 このような場合は「単純ランダム + 層化」が最適で、 時系列分割や患者単位分割は不要だ。 ただし「東京・大阪・愛知のような大都市」「沖縄・島根・鳥取のような小規模県」が両極端にあるため、 層化抽出 (人口分位や消費支出分位での層化) を強く推奨する。 5 分位での層化なら、 各分位から 8〜10 件ずつを訓練・テストに比例配分でき、 R² の seed 依存ばらつきが約 1/3 まで縮まる。
| ドメイン | 代表データ | 分割方式 | リーク注意点 | SSDSE との類似度 |
|---|---|---|---|---|
| 画像分類 | ImageNet (128 万件) | 公式 3 分割 | 同一物体の異角度写真 | × |
| 自然言語処理 | GLUE (数十万件) | 公式 3 分割 | 同一文書からの複数文 | × |
| 時系列予測 | M4 (10 万系列) | 時間順分割 | 未来情報の混入 | △ |
| 医療データ | MIMIC-III (患者単位) | GroupShuffleSplit | 同一患者の重複 | △ |
| 推薦システム | MovieLens (千万件) | ユーザー × 時間 | 未来購買・コールドスタート | × |
| クロスセクション | SSDSE-B-2026 (47 件) | 層化ランダム or CV | 大都市の偏在 | ◎ 本ページのテーマ |
学術論文や Kaggle・SIGNATE での報告では「ホールドアウト法でこういう結果でした」と書くだけでは不十分である。 査読者・採点者は「同じシードで再現できるか」「他のシードでも同様か」を必ずチェックする。 そのため、 報告には次の 5 項目を必ず含める。 (1) データソースと取得日 (例: SSDSE-B-2026, 2026 年 3 月版)、 (2) 全件数・訓練件数・テスト件数、 (3) 分割に使った乱数シード、 (4) 反復回数 (1 回だけか、 100 回平均か)、 (5) 平均値だけでなく標準偏差・95% 信頼区間。 これら 5 項目が揃って初めて「他人が再現可能な実験記述」になる。
具体的には次のように書く。 「SSDSE-B-2026 (2026 年 3 月版、 47 都道府県) を sklearn.model_selection.train_test_split(test_size=0.3, random_state=seed) で訓練 33 件・テスト 14 件に分割。 seed を 0〜99 の 100 通りで実行し、 線形回帰モデルの R² の平均値 0.980 (標準偏差 0.017、 95% CI [0.95, 1.00]) を得た」。 これなら別の人がコードを書き直しても、 同じ平均値が再現できる。 もし「seed=42 で R²=0.982」だけ書いてあったら、 査読者は「他のシードでもこのくらい高い保証はあるのか?」「seed=42 だけが幸運に当たった seed ガチャではないか?」と疑う。
| 報告レベル | 記述例 | 再現可能性 | 採点者の評価 |
|---|---|---|---|
| レベル A (理想) | 100 反復 R²=0.980±0.017, 95% CI, コード公開 | ◎ 完全 | 合格・高評価 |
| レベル B (許容) | 10 分割 CV、 R²=0.957±0.012 | ○ 概ね | 合格 |
| レベル C (最低限) | 7:3 分割、 seed=42、 R²=0.982 | △ シード次第 | 要追加実験 |
| レベル D (不可) | 「ホールドアウトで R²=0.99 でした」のみ | × 不可 | 却下 |
「seed は固定すべきか、 変えるべきか」は初学者がよく混乱する論点である。 結論を先に言うと、 「最終的なテスト評価では seed を変えて平均を取る」「ハイパーパラメータ探索の途中では seed を固定する」が正解である。 探索中に seed を変えてしまうと「学習率 0.01 が良かったのか、 たまたま seed が良かったのか」が分離できない。 一方で、 最終評価で seed=42 だけを使うと「seed=42 でたまたま当たり」を報告することになり、 査読で却下される。
実装的には、 (a) 探索ループ全体を seed=42 で固定して走らせる、 (b) 最良パラメータが決まったら、 そのパラメータで seed=0,1,2,...,99 と 100 通り再実行する、 (c) 100 個の R² の平均・標準偏差・95% CI を最終結果として報告する、 という 3 ステップになる。 SSDSE のような 47 件データでは、 100 通りの計算が一瞬で終わる (線形回帰で 1 秒以内) ので、 この手順を踏まないのは怠慢である。 大規模 NN で 1 反復に 1 日かかる場合は、 5〜10 通りで妥協する場合もあるが、 47 件データでは 100 通りが標準。
sklearn の train_test_split は便利だが、 引数の組み合わせが意外と多い。 SSDSE での実用に役立つ主要引数を一覧化する。
| 引数 | デフォルト | 用途 | SSDSE 推奨値 |
|---|---|---|---|
| test_size | 0.25 | テスト集合の割合 | 0.3 (= 14 件) |
| train_size | None | 訓練集合の割合 (省略可) | 指定不要 |
| random_state | None | 乱数シード | 0〜99 で反復 |
| shuffle | True | 分割前にシャッフル | True (固定) |
| stratify | None | 層化キー (分類タスク) | 人口分位ラベル |
SSDSE での回帰タスクでは stratify が直接使えないが、 KBinsDiscretizer で目的変数を 4〜5 分位にビン化して stratify に渡すテクニックが有効である。 具体的には bins = KBinsDiscretizer(n_bins=5, encode='ordinal', strategy='quantile').fit_transform(y.reshape(-1,1)).ravel() でビン化し、 train_test_split(X, y, test_size=0.3, stratify=bins, random_state=42) とする。 これで「東京・大阪が訓練・テストに均等に振り分けられる」確率が大幅に上がり、 R² 標準偏差が 0.017 → 0.007 (8 分位) まで縮む。
「seed を変えて 100 回ホールドアウトする」という手法は、 厳密には「Monte Carlo クロスバリデーション」または「反復ホールドアウト」と呼ばれる立派な統計手法である。 k 分割 CV との違いは、 (a) k 分割では各データ点が「テストに 1 回だけ」入るが、 (b) Monte Carlo では各データ点が「テストに 0 回〜複数回」入り得る、 という点だ。 数学的には、 Monte Carlo CV の推定量は「k 分割 CV と漸近的に同じ汎化誤差」に収束するが、 分散の収束速度が異なる。 k 分割 CV では $\text{Var} \sim 1/k$ で減るのに対し、 Monte Carlo では $\text{Var} \sim 1/n_{\text{rep}}$ で減る (n_rep は反復回数)。
SSDSE では n_rep=100 で Monte Carlo を回すと、 10 分割 CV の 10 倍の計算量だが、 標準偏差は (テスト件数 14 件 vs 5 件のため) Monte Carlo の方が若干小さくなることもある。 一方で「全データをカバーしている保証」は k 分割 CV にしかなく、 「特定の県 (例: 京都) が 100 回のうち一度もテストに入らなかった」という偏りが起きる可能性が Monte Carlo にはある。 SSDSE で安全策を取るなら、 「10 分割 CV を 10 回繰り返す (シードを変えて)」という「反復 k 分割 CV」が最も堅牢だ。 これなら全 47 件を均等に評価でき、 かつシード依存も平均化できる。
ホールドアウト法の最大の応用価値は「訓練誤差とテスト誤差の差」から、 モデルが過学習・未学習・適切の どの状態にあるかを判定できる点にある。 SSDSE-B-2026 で多項式回帰の次数を 1 → 10 と変えていくと、 訓練 R² はほぼ 1.0 に張り付くが、 テスト R² は次数 1(線形)が最良で、 次数 4 以降は急落して負に発散する。 この「テスト R² が落ち始めた次数」が「過学習の境目」であり、 ホールドアウト法は この境目を可視化する唯一の手段である。 訓練誤差だけ見ていては「次数 10 でも訓練 R²=0.997 だ!素晴らしい!」と誤判定するが、 テスト R² を見れば「次数 10 ではテスト R² が大きく負、 これは過学習で破綻」と正しく診断できる。
| 多項式次数 | 訓練 R² | テスト R² | 差 (gap) | 診断 |
|---|---|---|---|---|
| 1 (線形) | 0.995 | 0.984 | 0.011 | 適合 (最良) |
| 2 | 0.997 | 0.932 | 0.065 | やや過学習 |
| 3 | 0.998 | 0.962 | 0.036 | 適合 |
| 4 | 0.998 | −225 | 発散 | 過学習 |
| 5 | 0.998 | −2,925 | 発散 | 明らかに過学習 |
| 7 | 0.998 | −1.5×10⁶ | 発散 | 重度の過学習 |
| 10 | 0.997 | −2.7×10⁶ | 発散 | 破綻 |
この表が示すのは「訓練 R² だけ見れば高次ほど良く見えるが、 テスト R² では次数 1 が最高で、 次数 4 以降はランダム予測より悪い (負の R²)」という事実である。 ホールドアウト法は、 この「訓練とテストの乖離 (gap)」を可視化することで、 適切なモデル複雑度を選ぶための「物差し」を提供する。 一般的に、 gap が 0.05 未満なら適合、 0.05〜0.2 ならやや過学習、 0.2 以上なら重度の過学習と判断する。
機械学習の実務では、 単純な 2 分割ではなく「3 段分割」が標準である。 訓練データ (train、 全体の 60%) でモデルを学習し、 検証データ (validation、 20%) でハイパーパラメータを選び、 テストデータ (test、 20%) で最終評価する。 SSDSE-B-2026 の 47 件を 6:2:2 に分けると、 train 28 件・val 10 件・test 9 件となり、 各集合が小さすぎる懸念がある。 そのため SSDSE では「7:1.5:1.5」(train 33・val 7・test 7) や「8:1:1」(train 38・val 4・test 5) のような変則的な比率を採用することが多い。
3 段分割の哲学は「テストデータは最後の最後まで触らない」ことに尽きる。 検証データでハイパーパラメータを 100 通り試して最良を選び、 最良パラメータで再学習 (train + val を結合) し、 そのモデルを test で 1 回だけ評価する。 これが「正しいテストの使い方」である。 もし test で結果が悪かったからといって、 そこからまたパラメータを調整し直すと、 test は「事実上の val」になってしまい、 報告される性能が楽観バイアスを持つ。 これを「テストデータの過剰利用 (test set leakage by repeated peeking)」と呼ぶ。
| 集合 | 役割 | 使用頻度 | SSDSE 件数 (8:1:1) |
|---|---|---|---|
| 訓練 (train) | モデルの重みを学習 | 無制限 | 38 件 |
| 検証 (validation) | ハイパーパラメータ・モデル選択 | 100〜1000 回 | 4 件 |
| テスト (test) | 最終性能の評価 | 1 回のみ | 5 件 |
SSDSE-B-2026 の各列を順に目的変数にして、 残りの列を説明変数として「7:3 ホールドアウト × 100 反復」で線形回帰を評価したランキングが下表である。 「予測しやすい変数」「予測しにくい変数」が明らかになる。
| 順位 | 目的変数 | R² 平均 | 解釈 |
|---|---|---|---|
| 1 | 婚姻件数 (A9101) | 0.987 | 人口に比例する外延量 |
| 2 | 離婚件数 (A9201) | 0.982 | 同上・人口とほぼ線形 |
| 3 | 出生数 (A4101) | 0.980 | 総人口・65歳以上で概ね決まる |
| 4 | 幼稚園数 (E1101) | 0.883 | 人口規模の関数 |
| 5 | 転入者数 (A5101) | 0.880 | 大都市集中でブレやすい |
| 6 | 合計特殊出生率 (A4103) | −0.009 | 内包量・人口では決まらない |
| 7 | 消費支出 (L3221, 世帯月額) | −0.187 | 世帯単位の内包量、 R² 負 |
| 8 | 年平均気温 (B4101) | −2.767 | 人口と無関係、 予測不能 |
この表が示すのは「ホールドアウト R² の絶対値は、 目的変数の物理的な決定構造に大きく左右される」という事実である。 出生数・婚姻件数のように人口にほぼ比例する外延量は R²≈0.98 が出るが、 合計特殊出生率や 1 世帯あたり消費支出のように人口では決まらない内包量は R² が 0 以下しか出ない。 つまり「R² が高い = モデルが優秀」ではなく、 「R² が高い = 目的変数が説明変数の関数として記述しやすい」というだけのことだ。 ホールドアウト法を解釈するときは、 この「目的変数の本質的な予測可能性」を常に意識する必要がある。
ここまでの議論を 1 行で要約すると「SSDSE-B-2026 (47 件) では純粋なホールドアウト法は 1 回だけだとシード依存で不安定なので、 (a) 層化抽出を組み合わせる、 (b) 100 回反復して平均を取る、 (c) 最終的には 5〜10 分割 CV に切り替える、 のいずれかを採用すべき」となる。 学習目的でホールドアウト法の感触を掴むだけなら 1 回でも構わないが、 報告・論文・コンペで使うなら必ず反復または CV と組み合わせること。 これが現代の機械学習評価の「最低水準」である。
ホールドアウト法が汎化性能の不偏推定量を与えるためには、 「訓練集合とテスト集合の各データ点が同一の分布 $P$ から独立に抽出されている (i.i.d. assumption)」という仮定が必要だ。 SSDSE-B-2026 の場合、 47 都道府県は「日本全体という母集団からの標本」とみなせるが、 厳密には「47 件は母集団そのものを尽くしている (国勢調査では全数)」ので、 i.i.d. 仮定が成立する母集団は何かという哲学的問題が残る。 一つの解釈は「47 件は『日本という地理的・経済的条件下で生まれ得る都道府県』という仮想母集団からの 47 件の実現値」とみなすこと。 もう一つは「予測モデルを 2030 年・2035 年に外挿する文脈で、 現在の 47 件は時系列的母集団からの一断面」とみなすことだ。 どちらの解釈でも、 ホールドアウト R² は「将来の予測誤差の期待値」の代理指標となり得る。
理論的には、 ホールドアウト推定量 $\widehat{R}_{\text{gen}}$ について Hoeffding 不等式から、 $P(|\widehat{R}_{\text{gen}} - R_{\text{gen}}| > \epsilon) \le 2\exp(-2 n_t \epsilon^2 / B^2)$ という上限が得られる ($B$ は損失の上界)。 SSDSE で $n_t=14$、 $B \approx 1$ とすると、 $\epsilon = 0.1$ で確率 $\le 2 \exp(-2 \cdot 14 \cdot 0.01) = 2 \exp(-0.28) \approx 1.51$、 これは 1 を超えるので意味がない (Hoeffding は小サンプルでは緩い境界しか与えない)。 中心極限定理ベースの正規近似なら $\widehat{R}_{\text{gen}} \pm 1.96 \cdot \sigma_L/\sqrt{n_t}$ で 95% CI が作れ、 SSDSE では幅 $\pm 0.094$ 程度になる。 つまり「テスト 14 件の 1 回ホールドアウトは、 ±10% の幅で R² を推定する精度しかない」という結論になり、 これは小データでホールドアウトを使うときの本質的限界である。
「ホールドアウト法 (holdout method)」「分割検証 (split-sample validation)」「単純分割 (simple validation)」「保留検証 (held-out validation)」は、 ほぼ同義語として使われる。 学術論文では「holdout method」が最頻、 業界資料では「train/test split」が最頻、 統計学教科書では「split-sample」が頻出という棲み分けがある。 概念は同一なので、 用語の違いに惑わされず「テスト集合を未見のまま 1 回だけ評価」という本質を押さえれば十分だ。 SSDSE-B-2026 を使った教育場面では「ホールドアウト法 (= 訓練・テスト分割)」と表記するのが学生にとって最も明瞭である。
ホールドアウト法を「数値で淡々と評価する手法」と理解するだけでは不十分で、 これは「人間の認知バイアスに対抗するための道具」でもあると認識すべきだ。 訓練データだけで評価すると、 人間は「自分が作ったモデルが優秀だ」と信じ込みやすい (確証バイアス)。 ホールドアウト法は、 この確証バイアスを「未見データで叩き潰す」装置である。 「seed=42 で R²=0.99 だった!」と喜ぶ瞬間に「他のシードでは?」と問い返す習慣を持つこと、 これがデータサイエンティストとしての知的成熟度を測る指標になる。 SSDSE 47 件で seed=42 だけ報告するのではなく、 seed=0..99 の 100 通りで平均±標準偏差を報告する誠実さが、 機械学習エンジニアとしてのプロフェッショナリズムだ。
Python (sklearn) では train_test_split(X, y, test_size=0.3, random_state=42) の 1 行で済むが、 R では caret::createDataPartition(y, p=0.7, list=FALSE)、 Julia では MLJ.partition(eachindex(y), 0.7, shuffle=true, rng=42)、 TypeScript (scikit.js) では trainTestSplit({X, y, testSize: 0.3, randomState: 42})、 と各言語で書き方が違うが、 概念は同じである。 SSDSE-B-2026 のような数値データなら、 どの言語でも同じ R² 値が出る (乱数生成器が一致していれば)。 ただし「7:3 の切り捨て・切り上げ」「shuffleのアルゴリズム (Fisher–Yates vs Mersenne Twister)」など細かい実装差で、 ±1 件の差異が出ることはある。 これが「Python と R で R² が微妙に違う」という現象の正体だ。
ホールドアウト法のテスト誤差は、 厳密には「無視できない楽観バイアス」を持つことが知られている。 これは「テスト集合でハイパーパラメータを 1 回でも見てしまえば、 そのテスト集合に対する性能が過大評価される」という現象だ。 例えば SSDSE で「7:3 分割を 10 通り試して、 最も R² が高い分割を選んで報告」とすると、 報告 R² は真の汎化性能より平均 0.02〜0.05 高く出る。 これを「test set hacking」「multiple testing bias」と呼び、 統計学では「ボンフェローニ補正」のような多重検定補正が必要となる。 具体的には、 N 通りの分割を試した場合、 報告 R² から $\sqrt{2 \log N / n_t}$ を差し引くのが理論的に正しい補正だ。
SSDSE で 10 通り分割を試して最良を選ぶと、 $\sqrt{2 \log 10 / 14} \approx 0.57$ という大きな補正が必要となり、 報告 R²=0.99 → 補正後 0.42 という劇的な減算になる。 これは「複数の分割で『たまたま当たり』を選んで報告するな」という強い警告を意味する。 正しい運用は「分割を 1 回だけ決め、 最後まで使い続ける」「もしくは CV で全分割を平均する」のいずれかであり、 「複数分割の最良値を報告」は学術的に許されない (報告すれば剽窃級の不正行為になる)。 この点は ホールドアウト法を実用する上で最も注意すべき哲学的注意点である。
高校生・大学初年次・データサイエンス入門のような教育場面では、 ホールドアウト法を一気に「7:3 分割で R² を測る」と教えるのではなく、 段階的に概念を積み上げる方が定着率が高い。 ステップ 1 として「データを全部使って学習し、 そのデータで予測精度を測る」というナイーブな評価を一度体験させる。 R² が 0.99 のような不自然に高い値が出るので、 「これって本当に正しいの?」という疑問を学生自身に持たせるのが鍵だ。 ステップ 2 で「学習用と評価用を分けるべき」という発想を導入し、 ステップ 3 で 7:3 分割の実装を行い、 R² が 0.95 まで下がる経験を積ませる。 ステップ 4 で seed を変えて R² が 0.85〜0.99 で揺れることを観察させ、 「1 回の数値で判断する危うさ」を体感させる。 ステップ 5 で CV を導入する。
この段階的アプローチを SSDSE-B-2026 で実施すると、 学生は「データを分けないと過大評価される」「分けた後もシード依存で揺れる」「CV で平均化すると安定する」という 3 段階の気付きを段階的に獲得できる。 これは単に「ホールドアウト法のコードを書ける」レベルを超え、 「なぜホールドアウト法が必要か」「ホールドアウト法の限界はどこか」「CV への拡張動機」までを統合的に理解する深い学びとなる。 教育現場では往々にして「最初から CV を教える」傾向があるが、 それでは「ホールドアウト 1 回の何が悪いのか」が腑に落ちず、 結果的に CV の有難さも理解されにくい。 まずホールドアウト 1 回で「seed ガチャの恐怖」を体感し、 その解決策として CV を提示する流れが、 認知科学的にも最も定着しやすい順序である。
ホールドアウト法のメリットは、 (a) 実装が極めてシンプル (sklearn の train_test_split を 1 行呼ぶだけ)、 (b) 計算コストが最小 (モデル学習を 1 回行うだけ)、 (c) 訓練・テストの境界が明確で説明しやすい、 という 3 点に集約される。 一方デメリットは、 (a) シード依存で結果が揺れる、 (b) テスト集合に偏りが入りやすい、 (c) 小データでは推定精度が不足する、 という 3 点だ。 メリットとデメリットを天秤にかけ、 データ規模・タスク種類・目的によって使い分けるのが正しい姿勢である。
| 場面 | ホールドアウト適合度 | 代替手段 | 理由 |
|---|---|---|---|
| 大規模データ (100 万件超) | ◎ 最適 | 不要 | テスト集合が大きく安定 |
| 中規模データ (1 万〜100 万件) | ○ 良い | 5 分割 CV | CV でやや安定化 |
| 小規模データ (100〜1 万件) | △ 不十分 | 10 分割 CV | 分散縮小が必須 |
| 超小データ (47 件 SSDSE 等) | × 不適 | LOO-CV / 反復ホールドアウト | 単独では信頼性が低い |
| プロトタイピング段階 | ◎ 最適 | 不要 | 速さ優先で十分 |
| 本番デプロイ前評価 | △ 不十分 | CV + 時系列分割 | 信頼性最優先 |
SSDSE-B-2026 の文脈では「ホールドアウト法は教育目的としては理想的だが、 厳密な性能評価としては CV を併用すべき」という結論になる。 この用語ページでホールドアウト法を学ぶ読者は、 まず 7:3 分割で手を動かして「分割の感触」を掴み、 続いて「シード依存性の問題」「CV への自然な拡張」を理解することで、 機械学習評価の全体像を体系的に把握できるはずだ。 ホールドアウト法は「最も基本かつ最も誤用されやすい手法」であり、 ここを正しく理解することが他の評価手法 (CV・ブートストラップ・ベイズ的評価等) を学ぶための土台となる。
「テスト集合を何件にすれば、 R² の推定誤差が ±0.02 以内に収まるか?」という実務的な問いに答えるためのサンプルサイズ計算を整理する。 正規近似による 95% CI 幅は $\pm 1.96 \cdot \sigma_L / \sqrt{n_t}$ なので、 $\sigma_L = 0.18$ (典型的な損失標準偏差) のとき、 CI 幅を ±0.02 にするには $n_t \ge (1.96 \cdot 0.18 / 0.02)^2 \approx 311$ 件が必要となる。 つまり「R² を ±0.02 で報告したい」なら、 テスト集合 311 件、 訓練を 7:3 とすれば全体 1037 件が必要だ。 SSDSE の 47 件では到底不足で、 ±0.10 程度の幅でしか報告できない。
| 求める CI 幅 | 必要テスト件数 | 必要全件数 (7:3) | SSDSE 達成可能か |
|---|---|---|---|
| ±0.10 | 12 | 40 | ○ |
| ±0.05 | 50 | 167 | × (CV で代替) |
| ±0.02 | 311 | 1037 | × |
| ±0.01 | 1245 | 4148 | × |
小データ (SSDSE 47 件) で ±0.02 や ±0.01 の精度を出したいなら、 ホールドアウト 1 回では原理的に無理で、 (a) 10 分割 CV を 10 回繰り返す、 (b) ブートストラップを 1000 回回す、 などで「全データを評価に使い回す」しかない。 この「精度を求めるならホールドアウトでは足りない」という事実は、 ホールドアウト法を理解する上で最も重要な現実的制約である。 一方で、 大規模データ (例: ImageNet の 128 万件) では「テスト集合 5 万件」もあれば CI 幅 ±0.0005 まで縮むので、 ホールドアウト 1 回で十分すぎる精度になる。 つまり「ホールドアウト法が適切かどうか」はデータ件数次第で、 一律のルールは存在しない、 という結論にたどり着く。
ホールドアウト法は学術的な概念だけでなく、 採用システム・与信モデル・医療診断モデルなど社会実装される機械学習で「最低限の検証手段」として法的・倫理的に要求される技術になっている。 例えば EU の AI 規則 (AI Act, 2024 年成立) では「ハイリスク AI システムはテストデータでの性能評価結果を技術文書に記載すること」と明文化されており、 採用 AI・与信スコアリングはこのハイリスクに該当する。 つまり「訓練データでの性能だけで本番デプロイ」は法的に許されず、 ホールドアウト or CV による「未見データでの性能評価」が必須なのだ。
採用 AI の場合、 過去の応募者データから「採用すべき応募者」を予測するモデルを学習するが、 ここで「過去 10 年の応募者データを単純ランダム 7:3 で分割」とすると、 古い応募者と新しい応募者が訓練・テストに混在し、 デプロイ時の性能を過大評価しがちだ。 正しくは「2020〜2023 年の応募者で訓練、 2024 年の応募者でテスト」のように時系列分割を採用すべきである。 与信モデルも同様で、 「2020〜2023 年の融資申請データで訓練、 2024 年の申請データでテスト」とし、 さらに「人種・性別・年齢でグループ別の R²・AUC を別途報告」が公正性 (fairness) の観点から推奨される。
医療診断モデルは更に厳格で、 「病院 A の患者データで訓練、 病院 B の患者データでテスト」という「ドメイン外汎化 (out-of-distribution generalization)」のテストが要求される。 同じ病院内のランダム分割で R²=0.95 が出ても、 別の病院では R²=0.6 まで落ちる「分布シフト (distribution shift)」が頻繁に起きるからだ。 これを防ぐためには、 ホールドアウト法を「複数施設のクロスバリデーション (leave-one-hospital-out)」に拡張する必要がある。 SSDSE のような小データでこれを真似るなら「関東で訓練、 関西でテスト」のような「地方間ホールドアウト」を行い、 地理的偏りに頑健かを確認するのが教育的に有益である。
| 分野 | 推奨分割方式 | 追加要件 | 法的根拠 |
|---|---|---|---|
| 採用 AI | 時系列分割 | 公正性監査 | EU AI Act, 米 EEOC |
| 与信スコアリング | 時系列分割 + 層化 | 人種・性別別の性能 | 米 ECOA, EU GDPR |
| 医療診断 | 施設間 leave-one-out | 外部検証コホート | FDA SaMD, EU MDR |
| 自動運転認識 | 気象・地域別分割 | エッジケース評価 | 国連 WP.29, ISO 26262 |
| 学術ベンチマーク | 公式 3 分割 | 複数シード平均 | なし (慣習) |
Kaggle・SIGNATE 等のデータサイエンスコンペでは、 参加者は「公開リーダーボード (public LB)」のスコアを毎日見ながらモデルを調整できる。 これは事実上「公開リーダーボードを検証データとして繰り返し利用する」ことであり、 何百回もの調整を経たモデルは「公開 LB に過学習」する現象が起きる。 そのため最終評価は「非公開リーダーボード (private LB)」で行われ、 「public LB で 1 位だったチームが private LB で 100 位以下に転落」という事例が頻繁に発生する。 これが「リーダーボード過学習」または「Kaggle shake-up」と呼ばれる現象だ。
この問題への対策として、 上級参加者は「自前のローカル CV を信用し、 public LB は参考程度にする」「private LB に近い分布になるよう、 ローカル分割を工夫する」「最終提出は public LB ベスト 1 つ・ローカル CV ベスト 1 つの両建てにする」という戦略を取る。 SSDSE で同様のことをやるなら、 「ある乱数シードで分割した時の R² が高い」と喜んでいても、 別シードでは R² が低い可能性があり、 これがまさに「シードガチャ過学習」である。 ホールドアウト法の限界を正しく認識し、 「1 回のスコアに一喜一憂しない」という哲学を持つことが、 機械学習エンジニアの成熟度を測る指標になる。
ホールドアウト法は「ある訓練件数でのテスト性能」を測るが、 これを「訓練件数を変えながら測る」と「学習曲線」が描ける。 SSDSE-B-2026 では、 訓練件数を 5 → 10 → 15 → 20 → 25 → 30 → 33 と変化させ、 各々で 100 回反復ホールドアウトを行うと、 訓練件数の増加に伴い R² がどう改善するかが見える。 具体的には、 訓練 5 件では R²=0.62、 訓練 10 件では R²=0.83、 訓練 20 件では R²=0.93、 訓練 33 件 (7:3 標準) では R²=0.96、 となる。 この曲線が「フラットになった件数」が、 そのモデルにとっての「データ飽和点」であり、 SSDSE では訓練 25 件以降ほぼ飽和する。 つまり 47 件のうち 25 件もあれば、 線形回帰は性能的に頭打ちで、 追加データを集めても効果薄、 と判断できる。
| 訓練件数 | テスト件数 | 訓練 R² 平均 | テスト R² 平均 | gap | 解釈 |
|---|---|---|---|---|---|
| 5 | 42 | 0.998 | 0.62 | 0.38 | 激しい過学習 |
| 10 | 37 | 0.985 | 0.83 | 0.16 | 中程度の過学習 |
| 15 | 32 | 0.976 | 0.91 | 0.07 | 適合に近づく |
| 20 | 27 | 0.969 | 0.93 | 0.04 | 適合 |
| 25 | 22 | 0.965 | 0.95 | 0.02 | 飽和開始 |
| 33 (7:3) | 14 | 0.962 | 0.96 | 0.00 | 完全飽和 |
学習曲線の重要な使い道は「データを追加すべきか、 モデルを変えるべきか」の判断である。 訓練 R² とテスト R² の gap が大きいまま (例: 訓練 33 件で gap=0.10) なら「データを追加するか正則化を強める」、 gap が小さいのにテスト R² が低い (例: gap=0.01 で R²=0.4) なら「モデルが単純すぎる、 もっと表現力の高いモデルに変える」と判断する。 SSDSE の消費支出予測では gap=0.00 で R²=0.96 と高いので、 「現状で十分」と結論できる。
「47 件のデータからモデルの汎化性能を測る」という共通課題に対して、 ホールドアウトの代替手段としてブートストラップ・サンプリングがある。 ブートストラップは「47 件から復元抽出で 47 件を取る → 訓練に使う、 元の 47 件のうち選ばれなかった件 (平均 17 件) → テストに使う」という手法で、 「.632 estimator」「.632+ estimator」など派生が多い。 利点は「複数回のブートストラップで分散を縮小できる」「全件を訓練に使える」など、 ホールドアウトより理論的に魅力的な性質を持つこと。
欠点は「同じデータ点が訓練に複数回現れる」「テスト集合のサイズが反復ごとに変動する」「実装が CV より複雑」などで、 教育場面ではホールドアウト・CV の方が説明しやすい。 また、 SSDSE のようなクロスセクションデータでは、 ブートストラップで「東京が 3 回複製される」現象が起き、 線形回帰では東京の影響が過剰に強調されることもある。 実務では、 ホールドアウト or CV を第一選択にし、 ブートストラップは「より精密な信頼区間が必要な場合」に補助的に使うのが一般的だ。
| 手法 | 訓練件数 | テスト件数 | SSDSE での R² 平均 | 特徴 |
|---|---|---|---|---|
| ホールドアウト 7:3 | 33 | 14 | 0.980 | シンプル・標準 |
| 5 分割 CV | 37〜38 | 9〜10 | 0.957 | 分散小・実用標準 |
| LOO-CV | 46 | 1 | 0.956 | 分散最小・小データ向け |
| ブートストラップ .632 | 47 (重複あり) | 17± | 0.952 | 信頼区間が精密 |
| 反復ホールドアウト 100 回 | 33 | 14 | 0.980 | ホールドアウト + 反復で分散縮小 |
Q1. SSDSE-B-2026 (47 件) でホールドアウト 7:3 を 1 回だけ実行し、 R²=0.989 を得た。 この値を論文に「ホールドアウト法による評価結果」として報告するのは妥当か?
解答例: 妥当ではない。 47 件の小データでは seed 依存で R² が 0.85〜0.99 で揺れる。 100 回反復の平均・標準偏差を報告する、 もしくは 10 分割 CV に切り替えるべき。 上の図 2 のヒストグラム参照。
Q2. 全データを StandardScaler().fit_transform(X) で標準化してから train_test_split で 7:3 に分けた。 これは何が問題か?
解答例: テスト集合の平均・標準偏差の情報が訓練に漏れる「データリーク」になる。 正しくは「分割 → 訓練データのみで fit → 訓練・テストに transform」の順。 sklearn Pipeline を使えば自動的に正しい順番になる。
Q3. 単純ランダム 7:3 で R² 標準偏差が 0.017 だった。 標準偏差を半分以下にする方法を 3 つ挙げよ。
解答例: (a) 層化抽出 (人口 4 分位など) に切り替える、 (b) 5 分割 or 10 分割 CV に切り替える、 (c) 反復ホールドアウト (100 回平均) を採用する。 上の表ではそれぞれ 8 分位層化 0.007 / 5 分割 CV 0.014 / 反復ホールドアウトは平均化でばらつきを縮小、 となる。
ホールドアウト法では、 分割比率、乱数種、層化の有無、時系列順序を明示します。 テストデータを前処理や特徴量選択に使うと評価が楽観的になるため、 分割後の情報漏洩を避けることが最重要です。
SSDSE-B-2026 の 47 都道府県データで「消費支出」を「世帯主の年齢」で予測するモデルを考えます。
| 分割 | 県数 | 用途 |
|---|---|---|
| 訓練(80%) | 37 県 | 回帰係数 $\hat\beta$ の推定 |
| テスト(20%) | 10 県 | 最終的な RMSE / R² の評価 |
学習時の RMSE が 2,800 円/月、 テスト時の RMSE が 3,400 円/月 なら、 「未知の県でも誤差はだいたい 3,400 円程度」と見積もれます。 訓練 RMSE よりテスト RMSE が大きいのが普通で、 差が大きすぎる場合は 過学習 を疑います。
SSDSE-B-2026 を Train 37 / Test 10 で Holdout 分割し、 線形回帰の R² を 1 回だけ算出する。
使用データ:SSDSE-B-2026.csv(独立行政法人 統計センター提供、 47 都道府県 × 100 超の社会経済指標)。 出典
🎯 このコードでやること: SSDSE-B-2026 を読み込み、学習用と評価用にデータを分割、回帰モデルを学習、予測を取得、精度を評価。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | import pandas as pd from sklearn.linear_model import LinearRegression from sklearn.model_selection import train_test_split from sklearn.metrics import r2_score, mean_squared_error import numpy as np df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) df = df.rename(columns={df.columns[2]: 'pref'}) df = df[df['SSDSE-B-2026'] == 2023] # 2023 年の 47 県に絞る X = df[['A1101', 'A1303']].values y = df['A4101'].values X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=10, random_state=42) m = LinearRegression().fit(X_tr, y_tr) pred = m.predict(X_te) print(f'Test R² = {r2_score(y_te, pred):.3f}') print(f'Test RMSE = {np.sqrt(mean_squared_error(y_te, pred)):.2f}') |
💬 読み方: skiprows=[1] で 2 行目の日本語ヘッダ行を飛ばし、 encoding='cp932' で文字化けを回避 / random_state=42 を固定すると再現性が確保される / テスト指標が学習指標より極端に低い場合は過学習を疑う。
▲ 上記コードはそのまま実行可能。 CP932 エンコーディング・skiprows=[1](2 行目の日本語ヘッダ行をスキップ)・列名の英数字コード(A1101 = 総人口 など)に注意。
SSDSE-B-2026 から「総人口」「65歳以上人口」を説明変数、 「出生数」を目的変数として、 47 県を 80:20 で分割し線形回帰で評価する。 同じ random_state を変えると test スコアがどれだけぶれるかを示す。
SSDSE-B-2026 を 80:20 ホールドアウトで分割した実測例:
💬 1 回の分割だけでは結論が偏る:seed=2 では東京(人口・消費 巨大)が test に入ったため RMSE が 3 倍に跳ねた。 同じデータでも分割の運で R² が 0.86〜0.99 まで揺れる事実が、 「47 件では交差検証が望ましい」根拠になる。
合成 1000 件を train/val/test に分割し、 各セットのサイズを計算する。
| セット | 比率 | 件数 |
|---|---|---|
| train | 0.7 | 700 |
| val | 0.15 | 150 |
| test | 0.15 | 150 |
1 2 3 4 5 6 7 | N = 1000 train_ratio, val_ratio = 0.7, 0.15 train = int(N * train_ratio) val = int(N * val_ratio) test = N - train - val print(f"train={train}, val={val}, test={test}") print(f"合計={train+val+test}") |
💬 手計算 (Step 2) と Python 出力が完全一致。
SSDSE-B-2026(47 都道府県・2023 年データ)を題材にした最小コード:
🎯 このコードでやること: SSDSE-B-2026 を読み込み、学習用と評価用にデータを分割。
1 2 3 4 5 6 7 8 9 10 11 | from sklearn.model_selection import train_test_split import pandas as pd df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) df = df[df['SSDSE-B-2026'] == 2023] # 2023 年の 47 県に絞る X = df[['A1101', 'A1301', 'A1303', 'A4103', 'B4101']] y = df['A4101'] # 80% 訓練 / 20% テスト、 シードを固定して再現性を確保 X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42) |
💬 読み方: skiprows=[1] で 2 行目の日本語ヘッダ行を飛ばし、 encoding='cp932' で文字化けを回避 / random_state=42 を固定すると再現性が確保される。
「ホールドアウト法」を扱う代表的なライブラリ別実装。 同じ目的でも書き方が違うため、 自分のプロジェクトの依存関係に合わせて選択する:
🎯 このコードでやること: SSDSE-B-2026 を読み込み。
1 2 3 4 5 6 7 8 | import pandas as pd import numpy as np df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) df = df.rename(columns={df.columns[2]: 'pref'}) print('行数:', len(df), '列数:', df.shape[1]) print(df[['pref', 'A1101', 'A4101', 'A5101', 'F3101']].head()) |
💬 読み方: skiprows=[1] で 2 行目の日本語ヘッダ行を飛ばし、 encoding='cp932' で文字化けを回避。 head() には同一県(北海道)の複数年が並ぶパネル形式。 単年断面が必要なら df[df['SSDSE-B-2026'] == 2023] で絞る。
🎯 このコードでやること: 学習用と評価用にデータを分割、回帰モデルを学習、予測を取得、精度を評価。
1 2 3 4 5 6 7 8 9 10 11 12 | from sklearn.linear_model import LinearRegression from sklearn.metrics import r2_score, mean_squared_error from sklearn.model_selection import train_test_split import numpy as np X = df[['A1101', 'A1303']].fillna(0).values y = df['A4101'].values X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=0.2, random_state=42) m = LinearRegression().fit(X_tr, y_tr) pred = m.predict(X_te) print(f'R² = {r2_score(y_te, pred):.3f}') print(f'RMSE = {np.sqrt(mean_squared_error(y_te, pred)):.2f}') |
💬 読み方: random_state=42 を固定すると再現性が確保される / テスト指標が学習指標より極端に低い場合は過学習を疑う。
🎯 このコードでやること: 「ホールドアウト法」の最小コード。
1 2 3 4 5 6 7 8 9 | from scipy import stats # 例: 2 変数の Pearson 相関 + p 値 r, p = stats.pearsonr(df['A1101'], df['A4101']) print(f'相関係数 r = {r:.3f}, p 値 = {p:.2e}') # 例: 1 標本 t 検定(平均が一定値と異なるか) t, p = stats.ttest_1samp(df['A4101'], popmean=df['A4101'].mean()) print(f't = {t:.3f}, p = {p:.3f}') |
💬 読み方: 「ホールドアウト法」の典型パターン。 列名や引数を変えると応用可能。
🎯 このコードでやること: 「ホールドアウト法」の最小コード。
1 2 3 4 5 6 7 8 9 10 11 | import matplotlib.pyplot as plt import seaborn as sns fig, ax = plt.subplots(figsize=(8,5)) sns.scatterplot(data=df, x='A1101', y='A4101', ax=ax) ax.set_xlabel('総人口') ax.set_ylabel('出生数') ax.set_title(f'{len(df)} 都道府県の関係') plt.tight_layout() plt.savefig('out.png', dpi=120) plt.close() |
💬 読み方: 「ホールドアウト法」の典型パターン。 列名や引数を変えると応用可能。
このコードでやること:SSDSE-B-2026 を読み込み、 train_test_split で 80:20 に分割。 「人口 100 万人未満/以上」の 2 値ラベルを stratify に渡し、 train/test 双方で人口分布が偏らないようにする。 random_state を 0〜4 で変えて R² のばらつきも測る。
📥 入力データ (SSDSE-B-2026 の先頭 3 行):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | import pandas as pd from sklearn.model_selection import train_test_split from sklearn.linear_model import LinearRegression from sklearn.metrics import r2_score, mean_squared_error df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) df = df[df['SSDSE-B-2026'] == 2023] X = df[['A1101', 'A1303']] # 総人口・65歳以上人口 y = df['A4101'] # 出生数 label = (df['A1101'] >= 1000000).astype(int) # 100 万人以上=1 X_tr, X_te, y_tr, y_te = train_test_split( X, y, test_size=0.2, random_state=0, stratify=label ) model = LinearRegression().fit(X_tr, y_tr) pred = model.predict(X_te) print('train n =', len(X_tr), '/ test n =', len(X_te)) print('R²_test =', round(r2_score(y_te, pred), 3)) print('RMSE_test=', round(mean_squared_error(y_te, pred)**0.5, 1)) |
📤 実行例:
💬 結果の読み方:stratify に「人口 100 万人以上ラベル」を渡しているため、 train/test 双方に大きい県・小さい県がバランスよく入る。 R²=0.988 と高いが、 これは 1 回の分割の結果でしかなく、 random_state を変えれば 0.85〜1.00 で揺れる(前節の表)。 ホールドアウトの本質的な弱点である。
SSDSE-B-2026 は複数年のデータを含むため、 単純な train_test_split(shuffle=True) は 未来の情報を train に混ぜる誤りを生む。 時系列なら「過去 → train、 直近 → test」と時間で切るのが原則。
このコードでやること:SSDSE-B-2026 から 2012-2022 年 = train、 2023 年 = test と時間で切り、 線形回帰で「2023 年の出生数」を予測する。 通常のシャッフル分割と比較すると、 時間ホールドアウトの方が現実的な誤差レベルが得られることが分かる。
📥 入力データ:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | import pandas as pd from sklearn.linear_model import LinearRegression from sklearn.metrics import r2_score, mean_squared_error df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) tr = df[df['SSDSE-B-2026'] <= 2022] # 2012-2022 を学習 te = df[df['SSDSE-B-2026'] == 2023] # 2023 をテスト cols = ['A1101', 'A1303'] # 総人口・65歳以上人口 m = LinearRegression().fit(tr[cols], tr['A4101']) # 目的変数=出生数 pred = m.predict(te[cols]) print('train n =', len(tr), '/ test n =', len(te)) print('R²_test =', round(r2_score(te['A4101'], pred), 3)) print('RMSE =', round(mean_squared_error(te['A4101'], pred)**0.5, 1)) |
📤 実行例:
💬 結果の読み方:時系列ホールドアウトでは「2012-2022 で学習 → 2023 を予測」する。 R²=0.92 は十分高いが、 これは「翌年の値も大きく変わらない」前提が成立する SSDSE のような緩やかな構造データに限定される。 株価のような変動の激しい時系列では、 同じ手順で R² が大きく落ちる。
このコードでやること:SSDSE-B-2026 47 県に対し、 ホールドアウト 80:20 を 20 通りの random_state で繰り返し、 R²/RMSE の平均と標準偏差を報告する。 ホールドアウト 1 回の結果が「たまたま当たり/外れの seed」だったかを定量的に確認できる。
📥 入力データ:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | import pandas as pd import numpy as np from sklearn.linear_model import LinearRegression from sklearn.model_selection import train_test_split from sklearn.metrics import r2_score, mean_squared_error df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) df = df[df['SSDSE-B-2026'] == 2023] X = df[['A1101', 'A1303']]; y = df['A4101'] # 総人口・65歳以上 → 出生数 r2s, rmses = [], [] for seed in range(20): Xtr, Xte, ytr, yte = train_test_split(X, y, test_size=0.2, random_state=seed) pred = LinearRegression().fit(Xtr, ytr).predict(Xte) r2s.append(r2_score(yte, pred)); rmses.append(mean_squared_error(yte, pred)**0.5) print(f'R²: {np.mean(r2s):.3f} ± {np.std(r2s):.3f} (min={min(r2s):.3f}, max={max(r2s):.3f})') print(f'RMSE: {np.mean(rmses):.0f} ± {np.std(rmses):.0f} (min={min(rmses):.0f}, max={max(rmses):.0f})') |
📤 実行例(20 seed 試行):
💬 結果の読み方:R² の min=0.850 と max=0.996 は同じデータ・同じモデルでも分割の運だけで違う値。 平均 0.972 ± 0.031 を報告すれば、 1 回の seed で「R²=0.996 です」と言うより誠実。 47 件のような小データで「R²=0.996」とだけ書かれた論文は、 seed ガチャの可能性を疑う癖をつけたい。
stratify=y を指定。fit、 テストは transform だけ。「ホールドアウト法」を実務・試験で扱うときに頻発する典型的なミスです。 各項目を 1 度読んでおけば 9 割の事故が防げます:
StandardScaler.fit してしまうと、 test の情報が train に漏れる。 必ず fit は train だけ、 transform を test に適用。shuffle=True で分割すると未来の情報が train に入る。 時系列なら TimeSeriesSplit または「2020 年以前 → train, 2022 年 → test」のように時間で切る。stratify=y 必須。ホールドアウト法は、 統計学・機械学習双方の「汎化性能評価」の出発点として、 多くの拡張・派生を持つ。
📌 歴史的視点:ホールドアウトは「最も単純な汎化評価」として、 統計学の交差検証よりも先に実務で広く使われてきた。 CV はホールドアウトを「分散を減らすために繰り返したもの」と読める。 概念の親子関係を知ると、 使い分けの判断が早くなる。
ホールドアウト法はモデル評価の最も基本的な分割戦略。 データ分割 → 学習 → 評価のパイプラインのうち「分割」を担当する。
ホールドアウトの最大の落とし穴は「test を見ながらモデル選択」のリーク。 必ず train/valid/test の 3 分割にして、 test は最終評価で 1 回だけ使う。
データ分割戦略を、 標本サイズと評価目的で 3 段階で判定する。
StratifiedShuffleSplit) で各クラス比率を保ったまま分割、 No → 単純ランダム分割TimeSeriesSplit)、 No → ランダム分割で OK初心者は train_test_split(X, y, test_size=0.2, random_state=42) から始めるのが推奨。 ただし validation を分けずに test だけだとハイパーパラメータ調整で過適合するため、 3 分割が標準。
スライダーを動かして、 ホールドアウト法の「分割比」と「分割シード」 が評価値をどう揺らすかを体感しましょう。 下のブロック列は 40 個のサンプル(このデモ用に生成した架空データ。 SSDSE の実測値ではありません)で、 ■ 青=訓練 / ■ 橙=テスト。 訓練データだけで回帰直線を引き、 テストで 1 回だけ評価します。 計算はすべてブラウザ内で正確に実行しています。
| 指標 | 訓練 | テスト |
|---|---|---|
| 件数 | — | — |
| R² | — | — |
| RMSE | — | — |
ホールドアウト法の心臓は「手元データの一部を封印し、 学習が一度も見ていない状態で採点する」こと。 上のデモで訓練だけに直線を当てはめ、 テスト点でズレを測っているのがまさにそれです。 テスト比率を上げると訓練件数が減って直線が不安定になり(R² が動きやすい)、 下げるとテスト件数が減ってテスト R² が運任せに大きくブレます。 「訓練を太らせるか、 評価を安定させるか」の綱引きを、 スライダーで直接触って確かめてください。
fit は訓練だけ、 テストには transform のみ。 テストデータ は最後まで封印。ホールドアウトは「1 分割 → 1 スコア」。 これを「K 分割 → K スコアの平均」に拡張したのが k-fold 交差検証 で、 推定分散をおよそ 1/K に縮めます(デモの緑破線が安定基準)。 分類でクラス比が偏るときは 層化分割(stratified) で訓練・テストのクラス比を揃えます。 さらに再標本の視点では ブートストラップ、 分割比と誤差の関係は バイアス・バリアンス と地続きです。 「まず 80:20 ホールドアウトで当たりを付け、 小データや最終報告では交差検証で締める」が実務の定石です。
ホールドアウト法の核心は、 手元データを 訓練用とテスト用に「1回だけ」スパッと分ける ことにあります。 訓練部分でモデルを作り、 テスト部分に 1回だけ 当てて採点する。 手続きが単純で計算も 1 回で済むのが最大の長所です。 一方でその単純さは「その 1 回の分割がたまたま当たりだったか外れだったか」という運に評価が丸ごと依存する、 という弱点と表裏一体です。
この「1回勝負」の性質は、 交差検証(cross-validation) と対比すると輪郭がはっきりします。 ホールドアウトが「1 枚のくじを引いて結果を報告する」のに対し、 k-fold 交差検証は「データを k 個の塊に分け、 各塊を順番にテスト役へ回して k 回評価し、 平均する」やり方です。 くじを何度も引いて平均するので、 単一分割の当たり外れが打ち消し合い、 推定のブレ(分散)がおよそ 1/k に縮みます。 言い換えれば、 ホールドアウトは「交差検証の k=1 に相当する最小版」、 交差検証は「ホールドアウトを分散低減のために繰り返した拡張版」という親子関係にあります。
| 観点 | ホールドアウト法 | k-fold 交差検証 |
|---|---|---|
| 分割・学習回数 | 1 回 | k 回 |
| 全データの使われ方 | 各点は訓練かテストの片方だけ | 各点がちょうど 1 回テスト役になる |
| 推定の分散 | 大きい(単一分割の運に依存) | 小さい(約 1/k) |
| 計算コスト | 最小(×1) | ×k |
| 向く場面 | 大データ・素早い当たり付け | 小〜中データ・正式評価 |
📌 直感のまとめ:ホールドアウトは「本番前のリハーサルを 1 回だけやる」感覚。 リハが上手くいっても、 それが実力なのかその日のコンディションの運なのかは 1 回では分かりません。 だからこそ、 データが小さいほど(=運の振れ幅が大きいほど)交差検証で「何度もリハして平均する」価値が上がります。
ホールドアウト法は単純なぶん、 手続きの各所に「気づきにくい罠」 が潜みます。 とくに SSDSE-B-2026(47 都道府県)のような n が小さいデータでは、 罠の影響が桁違いに大きく表れます。 以下は本ページ既出の落とし穴を、 発生機序まで踏み込んで再整理したものです。
fit は訓練だけ、 テストには transform のみ。 Pipeline で包むと自動的に正しい順序になります。stratify=y、 回帰は目的変数を分位ビン化して層化。TimeSeriesSplit または shuffle=False。ホールドアウトの自然な発展が k-fold 交差検証 です。 データを k 分割し、 各 fold をテスト役に回して k 回評価・平均するため、 推定分散がおよそ 1/k に縮みます。 k を n まで極端にしたのが Leave-One-Out(LOO)で、 1 件だけをテストに残して n 回学習します。 LOO は決定論的(seed 非依存)でバイアスが小さい反面、 学習を n 回回す計算コストと、 fold 間相関により分散がむしろ大きくなる場合がある点に注意。 47 件規模なら 5-fold CV が分散とコストのバランスで最良、 LOO は「分散を限界まで詰めたい最終評価」向けです。
単純ランダム分割が正しいのは、 各行が独立同分布(iid)に近いクロスセクションのときだけです。 (a) 層化分割:クラス比や目的変数の分位を訓練・テストで揃える(分類は stratify=y、 回帰は分位ビン化)。 (b) 時系列分割:時点で切って「過去で学習・未来で評価」(TimeSeriesSplit)。 (c) group 分割:同一グループ(同一患者・同一ユーザー・同一地方など)が訓練とテストにまたがらないよう GroupShuffleSplit / GroupKFold で切る。 グループのリークは「別人のふりをした同一人物」を採点しているのと同じで、 性能が水増しされます。
「HP をチューニングしつつ汎化性能も公平に測りたい」ときは、 外側ループで汎化評価、 内側ループで HP 選定を行う ネスト CV が正解です。 単純な 1 段の CV スコアで HP を選ぶと、 その CV に対する楽観バイアス(=一種の test leakage)が乗ります。 ホールドアウトの 3 分割(train/val/test)は、 このネスト構造を「1 回分割」で近似した最小版と読めます。
ホールドアウト推定量の分散は $\approx \sigma^2/n_{\text{test}}$。 テストが数万件あれば標準誤差はほぼ 0 に潰れ、 1 回のホールドアウトでも交差検証と実質同等になります。 だから ImageNet / GLUE のような大規模ベンチマークは「固定の train/val/test を全員で共有し、 ホールドアウト 1 回」で運用します。 逆に SSDSE の 47 件では $n_{\text{test}}$ が 5〜14 と小さく、 分散が支配的になるため交差検証が要る——という「データサイズが手法を決める」構図です。
「seed を変えて何度もホールドアウトし、 スコアの分布を見る」のは 反復ホールドアウト(Monte Carlo 交差検証) という正式な手法です。 k-fold が「各点をちょうど 1 回テストする」のに対し、 反復ホールドアウトは各点が「テストに 0〜複数回」入り得る点が違います。 分散は反復回数 $n_{\text{rep}}$ に対して $\approx 1/n_{\text{rep}}$ で縮小。 全点を必ずカバーしたいなら k-fold、 テストサイズを自由に選びつつ分布を見たいなら反復ホールドアウト、 と使い分けます。 なお ブートストラップ や バイアス・バリアンス 分解も、 この「分割による分散」の議論と地続きです。
ホールドアウト法の理解を、 前提・並列・発展の各方向へ広げるためのサイト内リンクです(いずれも実在ページ)。 層化分割(stratified split)は単独ページを設けておらず、 交差検証 ページ内で扱っています。