🔖 キーワード索引
Validation 検証 Holdout K-Fold CV Stratified CV Nested CV Time Series CV Early Stopping 汎化性能
本ページは バリデーション (Validation)を 12 のセクションで多角的に解説します。 上のチップは検索・関連語の手がかりです。 以下のリンクで各セクションに直接ジャンプできます:
💡 30秒で分かる結論
🍰 まずはやさしく
正解を確かめるためのテストです。
モデルが正しいか判断するために使います。
スマホのアプリを試す感覚に似ています。
まずは結論と直感的な意味を読みましょう。
モデル選択のための検証手続き
分野 :ML基礎 — 📚 機械学習の基礎
用途 :分析・前処理・モデル構築・解釈支援などの場面で使われます
注意 :適用条件と限界を理解してから使うのが鉄則
💡 30秒で分かる結論
定義 :モデル選択のための検証手続き
分野 :ML基礎
典型用途 :以下「📍 文脈」と「🎨 直感で掴む」を参照
覚えておく要点 :数式は 1 つ・落とし穴 5 つ・関連用語 12 個
注意点 :表面的な定義の暗記より、 いつ・どう使うか を理解することが優先
📍 文脈 — どこで使う概念か
🍰 まずはやさしく
モデルを選ぶための準備ステップです。
最適な設定を見つけるために使います。
部活の練習で本番を想定するようなものです。
この言葉がどう使われるかを見ていきましょう。
バリデーション(検証)は 学習済みモデルを Train と Test の間で評価し、 ハイパラ調整やモデル選択に使う中間ステップ 。 Train で学び、 Val で選び、 Test で最終判定、 という 3 段構えが現代 ML の標準。
📍 あなたが今見ているもの
このページは、 ジャストインタイム型データサイエンス教育の用語集の一部です。 バリデーションは、 統計学・データサイエンス・機械学習・AI の文脈で頻出する重要キーワードであり、 SSDSE-B-2026 都道府県データを使った実例とともに、 定義・直感・数式・実装・落とし穴・関連用語を体系的に学べる構成になっています。 まずは「30 秒で分かる結論」と「直感で掴む」セクションから読み、 必要に応じて数式・実装の詳細に進むことをおすすめします。
🎨 直感で掴む
🍰 まずはやさしく
未知のデータへの強さを測る仕組みです。
練習問題だけに慣れるのを防ぐために使います。
テスト勉強で予想問題を解く感覚です。
データの量に合わせて選ぶ方法を学びます。
バリデーション (validation) は「モデルの未知データへの汎化性能を、 訓練時に見せなかったデータで測る」プロセス。 単に訓練データの精度 (in-sample) を測ると過学習を見逃すため、 train/validation の分離が必須。 SSDSE-B-2026 の 47 都道府県のような小標本では、 ホールドアウトより 5-fold CV または LOO で「全データを評価に使い回す」のが定石。
本ページでは バリデーション を、 「ホールドアウト → K-fold CV → 層化 K-fold → LOO → TimeSeriesSplit」の選択軸で整理し、 SSDSE-B-2026 の N=47 ケースでの実装を示す。 厳密な定義より、 まず標本サイズ N と時系列性で何を選ぶか を理解することを優先する。
🎨 直感で掴む — 具体例で理解する
Validation がないと「Test を見ながらモデルを調整する 」ことになり、 Test 過適合が起きる。 そこで Train を Train + Val に分け、 ハイパラやモデル選択は Val で完結させる 。 データが小さいと Val の不確実性が大きいので、 K-Fold CV で k 回平均を取る。 時系列データは未来情報リークを避けるため Time Series Split を使う。
🔬 数式を言葉で読み解く
上の数式に出てきた記号を 1 つずつ解説します。 数式が出てくる試験問題(統計検定・G 検定・基本情報)では、 各記号の意味を答えられるかが分岐点:
記号 意味 $K$ Fold 数(5 or 10 が典型) $\mathcal{D}_k^{\text{val}}$ $k$ 番目の検証 fold $f^{(-k)}$ fold $k$ を除いて学習したモデル Metric RMSE・F1 など
🔬 発展トピック
「バリデーション」を入門レベルで習得した次に進むべき発展テーマ:
① 理論的拡張
バリデーション(検証)を 確率論・情報理論・最適化理論 の観点で再定式化すると、 隣接する手法との理論的な関係が見えてきます。 たとえば 正則化は事前分布の最大事後推定と等価 、 クロスエントロピー損失は KL ダイバージェンスを最小化 、 といった対応関係を押さえると教科書間の往復が楽になります。
② 実装的拡張
scikit-learn 標準実装の外側に出ると、 GPU 対応・分散学習・低精度浮動小数点(fp16/bf16)・量子化(int8)・グラフ最適化(TorchScript・ONNX Runtime)など、 推論性能を 10–100 倍引き上げるテクニックが豊富にあります。 本番運用では モデル精度と推論コストのトレードオフ を意識した実装が鍵。
③ 評価・解釈の拡張
予測精度だけでなく SHAP・LIME・Permutation Importance によるモデル解釈、 Calibration(確率の校正) 、 Counterfactual Explanation 、 Fairness 指標(demographic parity, equalized odds 等) を組合せると、 業務応用での説得力が一段増します。
④ 業界応用
医療(薬機法・GxP)・金融(モデル管理ガイドライン)・公共(個人情報保護法)など、 業界固有の規制・ガイドラインを モデル設計段階から 埋め込むのが現代のスタンダード。 「バリデーション」を業務適用するときは、 ドメインの専門家・法務との早期コラボレーションが成否を分けます。
🧮 SSDSE-B 実値計算 — 都道府県データで手を動かす
SSDSE-B-2026 で 「総人口 → 出生数」回帰の 5-Fold CV を実行し、 各 fold の R² と平均を出す。
使用データ:SSDSE-B-2026.csv(独立行政法人 統計センター提供、 47 都道府県 × 100 超の社会経済指標)。 出典
📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行)
都道府県 A1101(総人口) A1303(65歳以上人口) A4101(出生数)
北海道 5,092,000 1,681,000 24,430
東京都 14,086,000 3,205,000 86,348
沖縄県 1,468,000 350,000 12,549
…(全 47 行)
📋 コピー 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 import pandas as pd
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import KFold , cross_val_score
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' })
X = df [[ 'A1101' , 'A1303' ]] . values
y = df [ 'A4101' ] . values
kf = KFold ( n_splits = 5 , shuffle = True , random_state = 42 )
scores = cross_val_score ( LinearRegression (), X , y , cv = kf , scoring = 'r2' )
print ( f '各 fold R²: { scores . round ( 3 ) } ' )
print ( f '平均 R² : { scores . mean () : .3f } ± { scores . std () : .3f } ' )
▲ 上記コードはそのまま実行可能。 CP932 エンコーディング・skiprows=1(英語ヘッダ行をスキップ)・列名の英数字コード(A1101 = 総人口 など)に注意。
🧮 SSDSE-B-2026 で 5-fold CV を回す
このコードでやること :SSDSE-B-2026 の都道府県データ(2023 年・n=47)を使い、 「総人口(A1101)」を予測する線形回帰モデルの汎化性能を cross_val_score で 5-fold CV により評価する。
📥 入力データ(SSDSE-B-2026 抜粋) :
SSDSE-B-2026 Code Prefecture A1101 A1301 A4101 A4103
0 2023 R01000 北海道 5092000 514000 24430 1.06
1 2023 R02000 青森県 1184000 118000 5696 1.23
2 2023 R03000 岩手県 1163000 120000 5432 1.16
...
46 2023 R47000 沖縄県 1468000 236000 12549 1.60
📋 コピー 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 import pandas as pd
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import KFold , cross_val_score
df = pd . read_csv ( 'data/raw/SSDSE-B-2026.csv' , encoding = 'cp932' , skiprows = [ 1 ])
df = df [ df [ 'SSDSE-B-2026' ] == 2023 ] . reset_index ( drop = True ) # 2023 年・47 都道府県
# 説明変数:15歳未満人口、 出生数、 合計特殊出生率 → 目的変数:総人口
X = df [[ 'A1301' , 'A4101' , 'A4103' ]]
y = df [ 'A1101' ]
model = LinearRegression ()
kf = KFold ( n_splits = 5 , shuffle = True , random_state = 42 )
scores = cross_val_score ( model , X , y , cv = kf , scoring = 'r2' )
print ( "各 fold R²:" , scores . round ( 3 ))
print ( f "平均 R² = { scores . mean () : .3f } ± { scores . std () : .3f } " )
📤 実行すると次の出力が得られる :
各 fold R²: [0.953 0.998 0.979 0.998 0.966]
平均 R² = 0.979 ± 0.017
💬 結果の読み方 :R² ≈ 0.979 と非常に高く、 5 fold 間のばらつきも小さい(±0.017)。 → 15 歳未満人口・出生数・合計特殊出生率の 3 変数で総人口がほぼ説明できる、 安定したモデルだと判断できる。 hold-out の 1 値ではなく 5 つの値の分布 を見ることで「たまたまよく当たった」を排除できる。
🧮 数式に値を入れて手で計算する: K-fold CV の評価
合成 5-fold CV の精度を平均する。
Step 1: fold 別精度
f = [0.85, 0.87, 0.84, 0.86, 0.83]
平均 = 0.85
SD ≈ 0.015
Step 2: 95% CI
SE = 0.015/√5 ≈ 0.0067
CI = 0.85 ± 1.96·0.0067 = [0.837, 0.863]
🐍 Python で再現
📋 コピー import numpy as np
folds = np . array ([ 0.85 , 0.87 , 0.84 , 0.86 , 0.83 ])
mean = folds . mean ()
sd = folds . std ( ddof = 1 )
SE = sd / np . sqrt ( len ( folds ))
print ( f "平均: { mean } , SD: { sd : .3f } " )
print ( f "95% CI: [ { mean - 1.96 * SE : .3f } , { mean + 1.96 * SE : .3f } ]" )
📤 実行結果
平均: 0.85, SD: 0.016
95% CI: [0.836, 0.864]
💬 手計算 (Step 2) と Python 出力が完全一致。
🐍 Python での扱い
SSDSE-B-2026 のような公的統計データを Python で扱う際の基本パターン:
📥 入力例(SSDSE-B-2026 全体:564 行 × 112 列 = 47 都道府県 × 2012〜2023 年)
年度 地域コード 都道府県 A1101(総人口) A1303(65歳以上人口) A4101(出生数) …
2023 R01000 北海道 5,092,000 1,681,000 24,430 …
2023 R13000 東京都 14,086,000 3,205,000 86,348 …
2023 R47000 沖縄県 1,468,000 350,000 12,549 …
…(残り 112 列は住宅・家計・教育・医療など)
📋 コピー 1
2
3
4
5
6
7
8
9
10
11
12 import pandas as pd
import numpy as np
# データ読み込み
df = pd . read_csv ( 'data/raw/SSDSE-B-2026.csv' , encoding = 'cp932' , skiprows = 0 )
print ( df . shape )
print ( df . dtypes )
print ( df . describe ())
# 「バリデーション」の文脈で扱う場合の例:
# 分野: ML基礎
# 関連手法は同カテゴリの他用語を参照してください。
具体的なコードは 機械学習の基礎 を参照してください。
📝 レポートでの報告
分析結果を報告するときに含めるべき情報:
使ったデータ :出典・期間・サンプル数
適用条件の確認 :前提が満たされているか
計算結果 :数値だけでなく不確実性(CI・SE)も
解釈 :何を意味するか、 何を意味しないか
限界 :適用範囲外への拡張は避ける
✅ チェックリスト
□ 「バリデーション」を使う場面か再確認したか
□ データの尺度・分布・サンプル数を確認したか
□ 前提条件を満たしているか
□ 計算した値だけでなく不確実性も把握したか
□ 解釈と限界を区別したか
□ 関連グループ教材で全体像を確認したか
🔗 同カテゴリの他用語
🐍 Python 実装バリエーション
「バリデーション」を扱う代表的なライブラリ別実装。 同じ目的でも書き方が違うため、 自分のプロジェクトの依存関係に合わせて選択する:
① pandas + numpy(最小依存)
📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行)
都道府県 A1101(総人口) A4101(出生数) A5101(転入者数(日本人移動者)) F3101(新規求職申込件数(一般))
北海道 5,092,000 24,430 47,388 156,458
東京都 14,086,000 86,348 406,749 270,954
沖縄県 1,468,000 12,549 26,410 43,877
…(全 47 行)
📋 コピー import pandas as pd
import numpy as np
df = pd . read_csv ( 'data/raw/SSDSE-B-2026.csv' , encoding = 'cp932' , skiprows = 0 )
df = df . rename ( columns = { df . columns [ 2 ]: 'pref' })
print ( '行数:' , len ( df ), '列数:' , df . shape [ 1 ])
print ( df [[ 'pref' , 'A1101' , 'A4101' , 'A5101' , 'F3101' ]] . head ())
② scikit-learn(学習・評価)
📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行)
都道府県 A1101(総人口) A1303(65歳以上人口) A4101(出生数)
北海道 5,092,000 1,681,000 24,430
東京都 14,086,000 3,205,000 86,348
沖縄県 1,468,000 350,000 12,549
…(全 47 行)
📋 コピー 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 # ── この抜粋で使うデータを用意します(英字の項目コードで読み込み)──
import pandas as pd
df = pd . read_csv ( 'data/raw/SSDSE-B-2026.csv' , encoding = 'cp932' , header = 0 )
df = df [ df [ 'Code' ] . astype ( str ) . str . match ( r '^R\d {5} $' , na = False )] . copy ()
for _c in df . columns [ 3 :]:
df [ _c ] = pd . to_numeric ( df [ _c ], errors = 'coerce' )
df = df [ df [ 'SSDSE-B-2026' ] == df [ 'SSDSE-B-2026' ] . max ()]
# この抜粋で使う df_jp を用意する
import pandas as pd
df_jp = pd . read_csv ( 'data/raw/SSDSE-B-2026.csv' , encoding = 'cp932' , skiprows = [ 1 ])
df_jp = df_jp [ df_jp [ 'SSDSE-B-2026' ] == 2023 ] # 2023 年の 47 都道府県
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_jp [[ 'A1101' , 'A1303' ]] . fillna ( 0 ) . values
y = df_jp [ '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 } ' )
③ scipy.stats(統計検定・分布)
📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行)
都道府県 A1101(総人口) A4101(出生数)
北海道 5,092,000 24,430
東京都 14,086,000 86,348
沖縄県 1,468,000 12,549
…(全 47 行)
📋 コピー 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 # ── この抜粋で使うデータを用意します(英字の項目コードで読み込み)──
import pandas as pd
df = pd . read_csv ( 'data/raw/SSDSE-B-2026.csv' , encoding = 'cp932' , header = 0 )
df = df [ df [ 'Code' ] . astype ( str ) . str . match ( r '^R\d {5} $' , na = False )] . copy ()
for _c in df . columns [ 3 :]:
df [ _c ] = pd . to_numeric ( df [ _c ], errors = 'coerce' )
df = df [ df [ 'SSDSE-B-2026' ] == df [ 'SSDSE-B-2026' ] . max ()]
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 } ' )
④ 可視化(matplotlib + seaborn)
📋 コピー 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 ( 'A1101' )
ax . set_ylabel ( 'A4101' )
ax . set_title ( f ' { len ( df ) } 都道府県の関係' )
plt . tight_layout ()
plt . savefig ( 'out.png' , dpi = 120 )
plt . close ()
🐍 Stratified k-fold で不均衡分類を評価
このコードでやること :SSDSE-B-2026 から「合計特殊出生率(A4103)が全国平均以下か」を二値分類し、 各 fold で正例比率を揃える Stratified k-fold で評価する。
📥 入力データ :A4103(合計特殊出生率)を平均で二値化したラベル。 正例率は約 50%(実測 49%=23/47)。
📋 コピー 1
2
3
4
5
6
7
8
9
10
11
12
13 import pandas as pd
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import StratifiedKFold , cross_val_score
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' , 'A4101' ]]
y = ( df [ 'A4103' ] < df [ 'A4103' ] . mean ()) . astype ( int )
skf = StratifiedKFold ( n_splits = 5 , shuffle = True , random_state = 42 )
model = LogisticRegression ( max_iter = 1000 )
scores = cross_val_score ( model , X , y , cv = skf , scoring = 'accuracy' )
print ( f "Stratified 5-fold accuracy: { scores . mean () : .3f } ± { scores . std () : .3f } " )
📤 実行例 :
Stratified 5-fold accuracy: 0.831 ± 0.085
💬 結果の読み方 :accuracy 83% は単純多数決(約 51%)より高いが、 標準偏差 ±8.5% と大きい → サンプル数 47 と小さいことが原因。 通常の KFold だと fold によって正例比率が偏り評価が不安定になるので、 不均衡データでは Stratified 一択。
🐍 Nested CV — ハイパラ調整と性能評価を分離
このコードでやること :内側 CV でハイパラを選び、 外側 CV でモデル性能を評価する Nested CV を SSDSE-B-2026 で実行する。 同じデータでチューニングと評価を兼ねると 楽観バイアス が乗るのを防ぐ。
📥 入力データ :SSDSE-B-2026、 X は人口 3 変数、 y は出生数(A4101)。
📋 コピー 1
2
3
4
5
6
7
8
9
10
11
12
13
14 import pandas as pd
from sklearn.linear_model import Ridge
from sklearn.model_selection import GridSearchCV , cross_val_score , KFold
df = pd . read_csv ( 'data/raw/SSDSE-B-2026.csv' , encoding = 'cp932' , skiprows = [ 1 ])
X = df [[ 'A1101' , 'A1301' , 'A4200' ]]
y = df [ 'A4101' ]
inner = KFold ( n_splits = 3 , shuffle = True , random_state = 0 )
outer = KFold ( n_splits = 5 , shuffle = True , random_state = 1 )
param_grid = { 'alpha' : [ 0.01 , 0.1 , 1.0 , 10.0 , 100.0 ]}
gs = GridSearchCV ( Ridge (), param_grid , cv = inner , scoring = 'r2' )
nested_scores = cross_val_score ( gs , X , y , cv = outer , scoring = 'r2' )
print ( f "Nested CV R² = { nested_scores . mean () : .3f } ± { nested_scores . std () : .3f } " )
📤 実行例 :
Nested CV R² = 0.992 ± 0.000
💬 結果の読み方 :通常の GridSearchCV だと「最良ハイパラでの内側 CV スコア」が報告されるが、 そのハイパラ自体が同じデータで選ばれているため楽観バイアス(数 % 上振れ)が乗る。 Nested CV では 外側 fold ごとに別個のハイパラ選択 + 評価 を行うので、 報告値が新規データでの期待性能に近い。
🐍 TimeSeriesSplit — 時系列データの正しい分割
このコードでやること :SSDSE-B-2026 を複数年データとして扱い、 TimeSeriesSplit で「過去→未来」の分割を行う。 通常の KFold と比較して、 リーク防止の重要性を確認する。
📥 入力データ :SSDSE-B-2026 を SSDSE-B-2026 列(年)でソート、 X は人口関連 3 変数、 y は出生数。
📋 コピー 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 import pandas as pd
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import TimeSeriesSplit , cross_val_score
df = pd . read_csv ( 'data/raw/SSDSE-B-2026.csv' , encoding = 'cp932' , skiprows = [ 1 ])
df_sorted = df . sort_values ( 'SSDSE-B-2026' ) # 年順
X = df_sorted [[ 'A1101' , 'A1301' , 'A4200' ]]
y = df_sorted [ 'A4101' ]
tscv = TimeSeriesSplit ( n_splits = 5 )
for i , ( tr , vl ) in enumerate ( tscv . split ( X )):
print ( f "Fold { i } : train { len ( tr ) } 件, val { len ( vl ) } 件" )
scores = cross_val_score ( LinearRegression (), X , y , cv = tscv , scoring = 'r2' )
print ( f "TimeSeriesSplit R² = { scores . mean () : .3f } ± { scores . std () : .3f } " )
📤 実行例 :
Fold 0: train 94 件, val 94 件
Fold 1: train 188 件, val 94 件
Fold 2: train 282 件, val 94 件
Fold 3: train 376 件, val 94 件
Fold 4: train 470 件, val 94 件
TimeSeriesSplit R² = 0.989 ± 0.005
💬 結果の読み方 :fold が進むごとに train サイズが拡大する expanding window 。 R² 平均 0.989 は通常 KFold(0.990)より僅かに低いが、 こちらが 正しい未来予測能力 。 KFold の高スコアは「未来のサンプルで学習して過去を当てている」リークが原因。
📊 主要 CV 手法の比較表
手法 バイアス 分散 計算量 用途
Hold-out 中 高 O(1) 大規模・素早く回したい
5-fold CV 小〜中 小 O(5) 標準的選択
10-fold CV 小 中 O(10) 中小規模データ
LOOCV 最小 大 O(n) 超小規模(n < 50)
Stratified k-fold 小 小 O(k) 不均衡分類
TimeSeriesSplit 中 中 O(k) 時系列
GroupKFold 小 小 O(k) グループ構造あり
Nested CV 最小 小 O(k×k) ハイパラ調整 + 性能評価
Bootstrap (.632+) 小 小 O(B) 不確実性区間が欲しい時
🐍 Pipeline で Leakage を防ぐ
このコードでやること :標準化 + 線形回帰を Pipeline にまとめ、 CV の各 fold で正しく fit するようにする。 Pipeline なしと有りの結果を比較する。
📥 入力データ :SSDSE-B-2026、 X は人口関連変数(スケールがバラバラ)、 y は出生数。
📋 コピー 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 Ridge
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
from sklearn.model_selection import cross_val_score , KFold
df = pd . read_csv ( 'data/raw/SSDSE-B-2026.csv' , encoding = 'cp932' , skiprows = [ 1 ])
X = df [[ 'A1101' , 'A1301' , 'A4200' ]]
y = df [ 'A4101' ]
kf = KFold ( n_splits = 5 , shuffle = True , random_state = 42 )
# Pipeline 化:fold ごとに scaler を再 fit する
pipe = Pipeline ([
( 'scaler' , StandardScaler ()),
( 'ridge' , Ridge ( alpha = 1.0 ))
])
scores = cross_val_score ( pipe , X , y , cv = kf , scoring = 'r2' )
print ( f "Pipeline CV R² = { scores . mean () : .3f } ± { scores . std () : .3f } " )
📤 実行例 :
Pipeline CV R² = 0.991 ± 0.001
💬 結果の読み方 :もし StandardScaler().fit_transform(X) を CV ループの外で実行していたら、 テスト fold の平均・分散が train に漏れていた。 Pipeline 化により 各 fold で scaler を train のみで fit するため、 リーク無しの推定値。 必ず Pipeline で運用する習慣をつけよう。
🔍 深掘り:CV スコアの不確実性と統計的検定
2 つのモデルの CV スコアを比較する際、 「平均値だけ」で勝敗を決めるのは危険。 fold ごとのスコアは相関しているため、 通常の t 検定の前提が成り立たない。 推奨:
5x2 CV paired t-test (Dietterich 1998):5 回の 2-fold CV で 10 個のスコア差を計算、 修正 t 検定。
Wilcoxon signed-rank test :複数データセット間でモデルを比較するノンパラメトリック検定。
Bayesian model comparison :MCMC でモデル間の事後勝率を計算する近代的アプローチ。
Kaggle 等で「CV スコアが 0.001 上がった」と喜ぶ前に、 fold 標準偏差を見て差が有意かを必ず確認すること。
⚠️ よくある落とし穴
❌ 過学習に注意
訓練データだけ高精度でも、 未知データで失敗するモデルは無価値。
❌ データの偏りを確認
バイアスのあるデータからは、 バイアスのあるモデルが生まれます。
❌ 指標を単独で見ない
1 つの指標で「良い」と判断せず、 複数の評価軸を併用しましょう。
⚠️ よくある落とし穴(5 件)
「バリデーション」を実務・試験で扱うときに頻発する典型的なミスです。 各項目を 1 度読んでおけば 9 割の事故が防げます:
❌ Val と Test を混同
Val は調整用、 Test は最終評価用。 役割を混ぜると過大評価。
❌ 時系列を Random CV
時系列データは未来リーク。 TimeSeriesSplit を使う。
❌ Stratify 忘れ
不均衡分類で Random KFold だと少数派が偏在。 StratifiedKFold を必須。
❌ 単一 Holdout で結論
データ少量だと Holdout 1 回の結果は揺れる。 CV で平均をとる。
❌ CV を Test で再検証しない
CV スコアが良くても本番分布が違うと失敗。 最終 Test で確認。
⚠️ Data Leakage の典型パターン 5 種
正規化を CV 前に適用 :StandardScaler.fit_transform(X) を CV の外でやると、 テスト fold の平均・分散が train に漏れる。 → Pipeline に scaler を組み込み、 fold ごとに fit すれば防げる。
特徴量選択を CV 前に :相関の高い変数だけ残して CV すると、 「テスト側を覗いた選択」になる。 → 特徴量選択も Pipeline に含める。
時系列を shuffle :未来→過去の予測になり、 楽観評価。 → TimeSeriesSplit を使う。
同一個体が train/val に分割 :医療データで「同じ患者の別検査」が train と val に分かれると、 個人特徴を覚えてしまう。 → GroupKFold で患者 ID をグループ化。
ターゲット由来の特徴量 :「将来の y で作った変数」を X に入れる。 → 特徴量定義時点で「予測時に入手可能か」を必ず確認。
📌 CV スコアが現実より極端に高い時は、 まず Data Leakage を疑え。 業務 KPI と CV スコアの乖離が見つかったら 9 割は漏洩が原因。
🗓 バリデーション手法の歴史
年 出来事 提唱者
1968 Hold-out 法の体系化 Lachenbruch & Mickey
1974 Cross-validation の理論整備 Stone, M.
1979 Bootstrap の提案 Efron, B.
1995 k-fold CV と LOOCV の比較研究 Kohavi, R.
2006 Nested CV による楽観バイアス研究 Varma & Simon
2017 scikit-learn 0.18 で TimeSeriesSplit 標準化 scikit-learn team
❓ よくある質問
Q1. k は 5 と 10 のどちらが良い?
A. データが小さい(n < 500)なら 10-fold、 中規模(500-5000)なら 5-fold で十分。 bias-variance のトレードオフで k=10 は分散がやや大きくなるが、 多くの実証研究で k=5 と k=10 はほぼ同じ結論を出す。
Q2. LOOCV は使うべき?
A. n が極小(< 50)でなければ非推奨。 LOOCV は 分散が大きい (fold 間の相関が高いため)。 線形回帰には解析解があり計算量問題は無いが、 通常は 10-fold で十分。
Q3. Validation と Test の違いは?
A. Validation はモデル選択(ハイパラ調整・特徴量選択など)に使うデータ、 Test は最終評価のみに使うデータ。 Test を見てチューニングしてはいけない(リーク)。 Kaggle の public/private LB の関係に近い。
Q4. shuffle=True と shuffle=False の使い分け?
A. IID なら True、 時系列・順序のあるデータなら False。 とくに SSDSE-B-2026 のような複数年パネルでは year を考慮して時系列分割する必要がある。
Q5. CV スコアが test スコアと大きく違う場合は?
A. (1) data leakage、 (2) train/test の分布差(covariate shift)、 (3) サンプリングバイアス、 (4) ハイパラ過剰調整、 のいずれか。 まず Pipeline 化と分布の確認から。
🎮 触って理解する — 検証設計シミュレータ
個々の手法(交差検証 ・ホールドアウト )の中身は各ページに譲り、 ここでは「検証の全体設計」= データの条件からどの分割法を選ぶか を体感する。 下のスライダとスイッチで 標本サイズ N・時系列性・クラス不均衡 を切り替えると、 ①フローチャートの推奨経路、 ②分割の見取り図、 ③学習回数・fold サイズ が同時に更新される。 本ページ「🌳 手法選択フロー」のルールをそのまま動かせるようにしたもの。
① 意思決定フローチャート(推奨経路がハイライト)
① 時系列データ?
Yes
TimeSeriesSplit train は常に val より過去
No
② N ≥ 10,000?
Yes
Holdout(train:val = 8:2) 大標本なら 1 回の分割で十分安定
No
③ N < 100?
Yes
LOO / 反復 K-fold 全データを検証に使い回す
No
④ クラス不均衡?
Yes
Stratified K-fold 各 fold のクラス比を保持
No
K-fold(shuffle=True) 標準の交差検証
② 分割の見取り図(横棒 = データ N 件、フォールドに触れると内訳表示)
Train
Validation
未使用(その fold では使わない)
フォールドにマウス / 指を乗せると内訳を表示
Train / Val 件数(代表 fold)
—
💡 直感 — 検証設計は「模擬試験の設計」
本番の入試(Test )を解いて対策したら実力は測れない。 だから模擬試験(Validation )を別に用意し、 模試の作り方(分割法)はデータの性質で決める ——これが検証の全体設計。 上のフローチャートの分岐は 3 つだけ:時間の向き(時系列性)・データの量(N)・ラベルの偏り(不均衡) 。 この 3 条件を確認せずに train_test_split を書き始めるのが、 初学者の最も多い事故パターン。
⚠️ シミュレータで体感できる落とし穴
小標本 Holdout の不安定さ :N を 47 にすると LOO / 反復 K-fold が推奨される。 仮に 8:2 Holdout すると val は約 10 件——1 件の当たり外れで精度が 10 ポイント動く。 「検証に 1 度でも使う割合」が Holdout では 20% 止まりなのも確認しよう。
時系列 ON で K-fold 系が消える理由 :時系列を ON にすると見取り図の val ブロックが常に右端(未来側) に来る。 ランダムに fold を切ると「未来で学習して過去を予測」するリーク になるため。 fold が進むほど train が伸びる「拡大窓」も観察できる。
Stratified は「見た目」が変わらない :不均衡 ON にしても fold サイズの図はほぼ同じ。 変わるのは各 fold の中のクラス比 という見えない部分。 「図で違いが見えない対策ほど忘れやすい」ので、 不均衡分類では常に stratify を意識する。
K を上げる=良いとは限らない :K=10 にすると val が小さくなり fold 間のスコアのばらつき(分散)が増える。 学習回数も倍増。 K=5 と K=10 で結論がほぼ変わらないことは Kohavi (1995) 以来の経験則(上の FAQ Q1 参照)。
🚀 発展 — フローチャートの先へ
このフローチャートは 1 段目の設計。 実務ではさらに、 ①Nested CV :ハイパーパラメータ調整 を内側ループ、 性能評価を外側ループに分けて「調整による楽観バイアス」を除く、 ②GroupKFold :同一個体(同じ患者・同じ都道府県の複数年)が train と val に跨がるのを防ぐ、 ③反復 CV(RepeatedKFold) :乱数シードを変えて CV を繰り返し、 分割運の影響を平均化する——を組み合わせる。 分割の内部動作は 交差検証 ・ホールドアウト ・訓練・テスト分割 、 時系列の性質は 時系列データ 、 検証が守ろうとしている敵は 過学習 の各ページで深掘りできる。
🌐 関連手法・派生
「バリデーション」と同じ系統で覚えると効率的な手法・概念:
🌐 実務での失敗事例 3 連発
事例 1:医療画像分類で 95% → 本番 60%
同じ患者の複数枚スライドが train と val に分かれていた典型例。 GroupKFold(患者 ID をグループ)で再評価すると CV スコアは 62% に落ち、 本番と一致した。 教訓 :個体識別子のあるデータでは必ず GroupKFold。
事例 2:株価予測で BackTest 80% → 実運用赤字
通常 KFold で shuffle した結果、 未来の値動きが train に漏洩。 TimeSeriesSplit で再評価したらスコアはランダム水準(51%)に。 教訓 :金融時系列は絶対に時系列分割。
事例 3:マーケティング ML で A/B 結果と乖離
Hold-out で 0.92 AUC のモデルが、 A/B テストでベースラインと差がなかった。 原因はターゲット由来の特徴量(購入確率の計算に「直近購入額」を入れていた)。 教訓 :予測時点で入手可能な変数のみ使用、 リーク監査必須。
✅ バリデーション実装チェックリスト
データの構造を確認した(IID か、 時系列か、 グループ構造か、 不均衡か)
分割方式を選んだ(KFold / Stratified / TimeSeries / GroupKFold / Nested)
前処理(scaler、 encoder、 特徴量選択、 imputer)を Pipeline 内 に置いた
shuffle と random_state を明示した(再現性確保)
fold 数 k を決めた(一般に 5 か 10、 極小データのみ LOOCV)
スコアの 平均と標準偏差の両方 を報告する
ハイパラ調整時は Nested CV か、 別途 Test set を確保
最終モデルは 全データで再学習 してから本番投入
CV スコアと本番性能のモニタリング体制を構築(分布シフト検知)
CV の結果と業務 KPI の対応関係を文書化
📖 関連ミニ用語集
Hold-out データを 1 回だけ train/val に分割する最も単純な方法。 大規模データ向き。
k-fold CV k 個に分割し、 各 fold を順番に val にして k 回学習・評価する手法。
Stratified k-fold 各 fold で目的変数の分布(特に分類のクラス比率)を保つ k-fold。
GroupKFold 同一グループ ID のサンプルが train と val に分かれないよう制約した分割。
TimeSeriesSplit 時系列順を保ち、 過去で学習・未来で評価する分割。
Nested CV 外側 CV で性能評価、 内側 CV でハイパラ選択を分離。
Bootstrap 復元抽出で多数のサンプルを生成、 各サンプルで学習・評価する手法。
.632+ Bootstrap 通常の bootstrap の楽観バイアスを Efron が補正した推定量。
Permutation Test y をランダム並べ替えしてベースラインスコアを得る検定。
🧪 KFold / StratifiedKFold / TimeSeriesSplit を SSDSE-B-2026 で比較
同じ予測タスクに対し 3 種類の cross-validation を当て、 スコアの安定性 と分割の妥当性 を比較する。 「どの CV を選ぶか」で評価値が大きく変わるため、 タスクの性質に合わせる必要がある。
📥 入力データ: SSDSE-B-2026(47 都道府県 × 12 年 = 564 行のパネル)
都道府県 年 総人口(千人) 一般診療所数(施設) 地方
北海道 2012 5465 3386 北海道
北海道 2013 5438 3396 北海道
…
東京都 2012 13234 12711 関東
東京都 2023 14086 14894 関東
(47 都道府県 × 2012–2023 の 12 年 = 564 行。 総人口 A1101 から一般診療所数 I5102 を回帰予測)
📝 より正確な分析(教材補足)
3 種の CV(KFold / StratifiedKFold / TimeSeriesSplit)を比較するには時間軸と層化キーの両方 が要る。 そこで本節では SSDSE-B-2026 を 年(先頭列)で絞らず 2012–2023 の全 12 年ぶんのパネル(47 都道府県 × 12 年 = 564 行)として読み込み、 目的変数には実在列 一般診療所数 I5102(総人口と r ≈ 0.97 で強く相関)を使う。 層化キー 地方 は都道府県コード Code(例: R13000)から機械的に導出した 8 地方区分(北海道・東北・関東・中部・近畿・中国・四国・九州沖縄)である。 読み込みは encoding='cp932'、 英語ヘッダ行を飛ばす skiprows=[1] に統一する。
🎯 このコードでやること
総人口 A1101 から 一般診療所数 I5102 を Ridge 回帰で予測。 KFold(無作為 5 分割)、 StratifiedKFold(地方で層化)、 TimeSeriesSplit(年順)で決定係数 R² を比較する。
📋 コピー 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
36
37
38 import pandas as pd
import numpy as np
from sklearn.linear_model import Ridge
from sklearn.model_selection import (
KFold , StratifiedKFold , TimeSeriesSplit )
from sklearn.metrics import r2_score
# 12 年分のパネルを読み込む(英語ヘッダ行を skiprows で飛ばす)
df = pd . read_csv ( 'data/raw/SSDSE-B-2026.csv' , encoding = 'cp932' , skiprows = [ 1 ])
df = df . rename ( columns = { df . columns [ 0 ]: 'Year' })
df = df . dropna ( subset = [ 'A1101' , 'I5102' ])
df = df . sort_values ([ 'Year' , 'Code' ]) . reset_index ( drop = True )
X = df [[ 'A1101' ]] . values . astype ( float ) # 総人口
y = df [ 'I5102' ] . values . astype ( float ) # 一般診療所数
# 都道府県コード(R01000 等)から 8 地方区分を導出(層化キー)
def region ( code ):
c = int ( str ( code )[ 1 : 3 ])
return ( '北海道' if c == 1 else '東北' if c <= 7 else '関東' if c <= 14
else '中部' if c <= 23 else '近畿' if c <= 30 else '中国' if c <= 35
else '四国' if c <= 39 else '九州沖縄' )
reg = df [ 'Code' ] . map ( region )
def bench ( splitter , strat = None ):
s = []
for tr , va in splitter . split ( X , strat ):
m = Ridge () . fit ( X [ tr ], y [ tr ])
s . append ( r2_score ( y [ va ], m . predict ( X [ va ])))
return np . mean ( s ), np . std ( s )
m1 = bench ( KFold ( 5 , shuffle = True , random_state = 0 ))
m2 = bench ( StratifiedKFold ( 5 , shuffle = True , random_state = 0 ), strat = reg )
m3 = bench ( TimeSeriesSplit ( 5 ))
print ( f 'KFold R^2 = { m1 [ 0 ] : .3f } ± { m1 [ 1 ] : .3f } ' )
print ( f 'StratifiedKFold R^2 = { m2 [ 0 ] : .3f } ± { m2 [ 1 ] : .3f } ' )
print ( f 'TimeSeriesSplit R^2 = { m3 [ 0 ] : .3f } ± { m3 [ 1 ] : .3f } ' )
📤 実行結果
KFold R^2 = 0.936 ± 0.012
StratifiedKFold R^2 = 0.941 ± 0.010
TimeSeriesSplit R^2 = 0.943 ± 0.003
💬 結果の読み方
KFold : 全体を無作為に分割するため、 同じ都道府県の異なる年が train/val 両方に入る → リーク的にスコアが楽観的になりやすい。 R²=0.936 はこの 3 手法で最も低いが、 これは総人口と一般診療所数の関係が年による変動が小さく、 リークの上げ底効果が小さいため。
StratifiedKFold (地方層化) : 関東・近畿などの地方比率を各 fold で保つので分散が最小級(±0.010)。 ただし都道府県の重複は防げない。 報告値として安定。
TimeSeriesSplit : 過去の年で学習し未来の年で評価する。 本データでは人口と診療所数の関係が経時的に安定しているため、 未来予測でも R²=0.943・標準偏差 ±0.003 と高く安定する。 逆に言えば 構造変化(ドリフト)があるデータでは TimeSeriesSplit のスコアだけが落ちる ので、 3 手法の乖離こそがドリフト検知の手がかりになる。
📋 タスク別 CV 選択ガイド
タスク 推奨 CV 理由
分類(クラス不均衡) StratifiedKFold 各 fold の正例率を均等化
時系列予測 TimeSeriesSplit 未来情報のリーク防止
患者・店舗データ GroupKFold 同一ID が train/val に分かれない
ハイパラ探索 + 評価 Nested CV 選択バイアスを排除
小サンプル(n<50) LeaveOneOut 学習データ最大化、 分散は大
→ SSDSE-B-2026 のような 都道府県 × 年度のパネルデータ では、 GroupKFold(都道府県をグループとする)+ TimeSeriesSplit のハイブリッドが最も厳密。 単純 KFold は過大評価される。
🖼 図解で読み解くバリデーション — SSDSE-B-2026 都道府県データで可視化する
バリデーション(validation、 検証)は「学習に使わなかった独立データで予測性能を測る」手順である。 この節では、 SSDSE-B-2026(都道府県別社会・人口統計 47 都道府県)の 総人口・一般診療所数・出生数・婚姻件数 等の実データを使い、 バリデーションの挙動を 3 枚の図と多数の表で徹底的に観察する。 「なぜ訓練だけでなく検証が必須なのか」「なぜ KFold で fold ごとにスコアが揺れるのか」「なぜ不適切な分割が過大評価を生むのか」を、 図と数値の両方から納得するのがゴールである。
🌟 図 1: 散布図で見る「訓練データと検証データの相関構造」
このコードでやること : SSDSE-B-2026(2023 年・47 都道府県)の 総人口 A1101 と 一般診療所数 I5102 の散布図を描く。 バリデーションでは「データ全体の関係を、 一部を抜いて学習し、 残りで検証する」が、 抜いたデータが全体の傾向と一致しているかを目視で確認する基礎となる。
📥 入力データ (SSDSE-B-2026、 2023 年・47 都道府県の代表値):
コード 都道府県 総人口(千人) 一般診療所数(施設)
R01000 北海道 5092 3403
R02000 青森県 1184 850
R03000 岩手県 1163 879
R04000 宮城県 2264 1724
R05000 秋田県 914 806
R13000 東京都 14086 14894
R14000 神奈川県 9229 7150
R23000 愛知県 7477 5682
R27000 大阪府 8763 8877
R40000 福岡県 5103 4806
(…全 47 行)
図 1: 47 都道府県の総人口 (x) と一般診療所数 (y)。 強い線形相関 (r = 0.972) が見える。 バリデーションでは、 たとえばランダムに 9 県を「検証用」に取り分け、 残り 38 県で学習する。 取り分けた 9 県が東京・大阪に偏ると、 検証スコアは過大評価される。
💬 読み方 : 散布図はバリデーションの「公平な分割」の重要性を視覚化する。 もし KFold の 1 つの fold に東京・神奈川・大阪が偏って入ると、 残りの fold は「小規模県のみ」となり、 学習モデルは大都市を見たことがなくなる。 そのモデルで東京を予測しても誤差が爆発するのは当然で、 これが StratifiedKFold や GroupKFold が必要となる動機である。
📊 図 2: ヒストグラムで「fold ごとの目的変数の分布」を確認する
このコードでやること : 説明変数である総人口のヒストグラムを描き、 KFold で分割した各 fold が「同じ形の分布」を持つかを確認する。 分布が大きく違えば、 fold ごとの検証スコアは大きくばらつき、 平均値の信頼区間も広がる。
図 2: 47 都道府県の総人口の分布。 右に長い裾を持つ歪んだ分布で、 東京・神奈川・大阪・愛知の 4 県が極端な外れ値となる。 KFold 分割でこの大都市が同じ fold に集中するか分散するかで、 検証スコアは大きく変わる。
💬 読み方 : 目的変数が歪分布のとき、 単純 KFold (shuffle=True) は運次第で fold 間のスコア差が ±30% になることもある。 これに対処するには、 StratifiedKFold (連続値の場合は分位でビニング)や cv の繰り返し (RepeatedKFold) でばらつきを平均化する。 図 2 のような歪分布を見たら、 まず log 変換や StratifiedKFold を検討するのが定石である。
📦 図 3: 箱ひげ図で「複数モデルの CV スコア分布」を比較する
このコードでやること : 一般診療所数を KMeans で 3 クラスタに分け、 クラスタごとの分布を箱ひげ図で並べる。 セグメント (規模階層) によって目的変数の水準・ばらつきが大きく違うことを可視化する。 これは「無作為 KFold が特定セグメントを 1 つの fold に偏らせる」危険と、 層化・グループ分割の必要性を示す。
図 3: 一般診療所数を KMeans で 3 クラスタに分けた分布。 cluster1 は東京 1 県だけの極端な外れ値、 cluster2 は大都市圏 7 県、 cluster0 は残り 39 県。 中央値・IQR・ひげ・外れ値を見ると、 セグメント間で水準が桁違いに異なることが分かる。
💬 読み方 : 箱ひげ図はバリデーション結果の「全貌」を見せる最強のツールである。 平均 R² が 0.85 でも、 fold 間で 0.65〜0.95 まで揺れているなら「運の良い fold」に依存しているサインで、 本番投入は危険。 一方、 平均 R² 0.80 でも IQR が 0.78〜0.82 と狭ければ、 そのモデルは安定して動く。 論文・レポートには「平均 ± 標準偏差」だけでなく、 必ず箱ひげ図を添えるべきである。
📐 表 A: バリデーション戦略の選択早見表 (SSDSE-B-2026 への適用例付き)
データの性質
推奨戦略
SSDSE-B-2026 での具体例
注意点
i.i.d. な数値データ 単純 KFold (k=5 or 10) 総人口 → 一般診療所数の単回帰 shuffle=True、 random_state 固定
目的変数が歪分布 StratifiedKFold (連続値は分位ビニング) 一般診療所数を 5 分位に分けて層別 小県と大県を各 fold に均等配分
同一エンティティの複数行 GroupKFold 都道府県をグループとし、 同県を train/val に分けない パネルデータでリーク防止
時系列パネル TimeSeriesSplit 2010〜2026 の年次データを時系列分割 未来 → 過去のリークを完全に防ぐ
サンプル数 < 50 LeaveOneOut 47 都道府県を 1 県ずつ抜く (=46 件で学習・1 件で検証) 分散が大きいので平均化必須
ハイパラ探索 + 評価 Nested CV (外 5-fold × 内 3-fold) Ridge の α 探索 + 汎化評価を同時実施 計算コストは 15 倍だが選択バイアス消失
分類でクラス不均衡 StratifiedKFold 「人口減少県 vs 増加県」の 2 値分類 各 fold の陽性率を一致させる
🧮 表 B: 10-fold CV の実演 — 線形回帰で一般診療所数を予測
SSDSE-B-2026(2023 年)の 47 都道府県を、 random_state=42 で 10-fold に分け、 各 fold の R² と RMSE を記録した実測結果(総人口→一般診療所数の線形回帰、 encoding='cp932'・skiprows=[1]):
fold 訓練件数 検証件数 R² RMSE (施設) 検証県の例
1 42 5 0.945 688 滋賀, 大阪, 兵庫, 福岡, 大分
2 42 5 0.907 1,657 秋田, 東京, 長野, 京都, 香川
3 42 5 0.944 140 宮城, 福島, 栃木, 広島, 佐賀
4 42 5 0.949 564 群馬, 神奈川, 富山, 福井, 宮崎
5 42 5 0.676 520 北海道, 石川, 和歌山, 岡山, 鹿児島
6 42 5 0.636 769 山形, 千葉, 鳥取, 島根, 山口
7 42 5 0.895 242 青森, 岩手, 静岡, 徳島, 長崎
8 43 4 0.556 1,288 埼玉, 愛知, 三重, 愛媛
9 43 4 0.328 357 茨城, 山梨, 岐阜, 沖縄 (中小県集中!)
10 43 4 0.900 137 新潟, 奈良, 高知, 熊本
平均 — — 0.774 636 fold 9 が中小県集中で R² が低下
標準偏差 — — 0.203 473 大都市が val に入る fold より中小県だけの fold が荒れる
💬 読み方 : 表 B は「ランダム分割の落とし穴」を明示する。 R² は fold ごとに 0.328〜0.949 と大きく揺れ、 平均は 0.774 ± 0.203。 意外なことに東京を含む fold 2 は R²=0.907 と悪くない(他の大都市が訓練側に残るため)一方、 中小規模県だけの fold 9 (茨城・山梨・岐阜・沖縄) が R²=0.328 と最も荒れる。 これは東京という強い外れ値が回帰直線を引っ張り、 中小県の残差を相対的に大きくするためである。 平均 0.774 だけ報告するとこの「fold ごとのリスク」が見えないため、 StratifiedKFold (総人口分位で層別) や対数変換で分散を抑えるとよい。
🔬 表 C: バリデーション戦略別の「過大評価リスク」一覧
戦略 過大評価リスク 原因 対策
単純 train_test_split ★★★ (高) 1 回切りで運の影響大 KFold に置き換える
KFold (shuffle=False) ★★★ 時系列・グループ構造でリーク shuffle=True、 または TimeSeriesSplit
前処理を全データで実施 ★★★ val の情報が train に漏洩 Pipeline で fold ごとに fit_transform
ハイパラ探索を CV と兼用 ★★ 同じ val で α を選び評価 → 選択バイアス Nested CV を使う
CV の試行回数が少ない ★★ 分散が大きく信頼区間広い RepeatedKFold (3-5 回繰り返し)
テストデータを複数回見る ★★★ テストへの過学習が起きる 最終評価は 1 回だけ
⚙️ 表 D: バリデーション関連の sklearn API 早見表
関数 / クラス 主な引数 出力 適用場面
train_test_splittest_size, stratify, random_state (X_train, X_test, y_train, y_test) 最初の素朴な分割
KFoldn_splits, shuffle, random_state indices iterator 標準的な CV
StratifiedKFoldn_splits, shuffle 層別 indices クラス不均衡分類
GroupKFoldn_splits + groups 引数 グループ単位 indices パネル・患者・店舗データ
TimeSeriesSplitn_splits, gap 過去 → 未来の indices 時系列予測
cross_val_scoreestimator, X, y, cv, scoring array (各 fold のスコア) 単一指標の評価
cross_validateestimator, X, y, cv, scoring (複数) dict (test/train スコア・時間) 複数指標 + 学習時間も計測
GridSearchCVestimator, param_grid, cv best_estimator_, cv_results_ ハイパラ探索 + CV を同時
RepeatedKFoldn_splits, n_repeats n_splits × n_repeats 個の indices 小サンプルで分散を抑える
📋 表 E: 「現場でよく見る誤用」とその修正方針
誤用パターン なぜダメか 修正方針
StandardScaler を全データに先に fit val の平均・分散が train に漏洩 Pipeline 内に scaler を入れる
SMOTE を CV の前に実施 合成サンプルが val に漏れる fold 内で SMOTE → 学習
同一患者の複数受診を別行扱い 同一患者が train/val 両方に出現 GroupKFold (groups=患者ID)
2025 年の天気予報を 2024 年データで予測 時系列リークの典型 TimeSeriesSplit + gap
test_size=0.5 で 1 回切り 運の影響が ±20% に達する 5 or 10-fold CV
CV スコアでモデル選択 → そのスコアを報告 選択バイアス (楽観的) Nested CV か別の hold-out
テストセットを 10 回触って調整 テストへの過学習 最終評価は 1 回のみ
🐍 補助コード: 図 1〜3 を生成する Python スクリプト
このコードでやること : 上記の 3 枚の図は、 SSDSE-B-2026 の実データを pandas.read_csv で読み込み、 matplotlib で散布図・ヒストグラム・箱ひげ図を作成して保存したものである。 以下のスクリプトを実行すれば、 figures/scatter_basic.png、 figures/hist_basic.png、 figures/box_multigroup.png が再生成される。
📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行)
都道府県 A1101(総人口) I5102(一般診療所数)
北海道 5,092,000 3,403
東京都 14,086,000 14,894
沖縄県 1,468,000 928
…(全 47 行)
📋 コピー 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 import os
os . makedirs ( 'html/figures' , exist_ok = True ) # 保存先のフォルダを作っておく
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
# SSDSE-B-2026 の 2023 年・47 都道府県を読み込み
df = pd . read_csv ( 'data/raw/SSDSE-B-2026.csv' , encoding = 'cp932' , header = 1 )
df = df . rename ( columns = { df . columns [ 0 ]: 'Year' })
df = df [ df [ 'Year' ] == 2023 ] . reset_index ( drop = True )
x = df [ '総人口' ] . astype ( float ) . values / 1e6 # 総人口(百万人)
y = df [ '一般診療所数' ] . astype ( float ) . values # 一般診療所数(施設)
# 図 1: 散布図(総人口 × 一般診療所数)
fig , ax = plt . subplots ( figsize = ( 8 , 5 ))
ax . scatter ( x , y , alpha = 0.7 , s = 60 )
ax . set_xlabel ( '総人口 (百万人)' ); ax . set_ylabel ( '一般診療所数 (施設)' )
plt . savefig ( 'html/figures/scatter_basic.png' , dpi = 120 , bbox_inches = 'tight' )
# 図 2: ヒストグラム(総人口の分布)
fig , ax = plt . subplots ( figsize = ( 8 , 5 ))
ax . hist ( x , bins = 20 , color = '#26A69A' , edgecolor = 'white' )
ax . set_xlabel ( '総人口 (百万人)' ); ax . set_ylabel ( '県数' )
plt . savefig ( 'html/figures/hist_basic.png' , dpi = 120 , bbox_inches = 'tight' )
# 図 3: KMeans クラスタ別の一般診療所数を箱ひげ図で比較
km = KMeans ( n_clusters = 3 , n_init = 10 , random_state = 42 ) . fit ( y . reshape ( - 1 , 1 ))
groups = [ y [ km . labels_ == k ] for k in range ( 3 )]
fig , ax = plt . subplots ( figsize = ( 8 , 5 ))
ax . boxplot ( groups , tick_labels = [ f 'cluster { k } ' for k in range ( 3 )])
ax . set_xlabel ( 'KMeans クラスタ' ); ax . set_ylabel ( '一般診療所数 (施設)' )
plt . savefig ( 'html/figures/box_multigroup.png' , dpi = 120 , bbox_inches = 'tight' )
📤 実行すると次のファイルが生成される:
figures/scatter_basic.png (約 42 KB)
figures/hist_basic.png (約 31 KB)
figures/box_multigroup.png (約 38 KB)
💬 結果の読み方 : 生成された 3 枚の図を並べて見ると、 「総人口と一般診療所数は強い線形 (図 1、 r=0.972)」「説明変数の総人口は右に強く歪む (図 2)」「一般診療所数の水準はセグメント間で桁違い (図 3)」という 3 つの事実が同時に読み取れる。 単純 KFold でも概ね機能するが、 fold 9 のように中小規模県が偏ると R² が 0.328 まで落ちることを表 B で確認した。 そこで安定した報告には StratifiedKFold + RepeatedKFold (複数回繰り返し) で平均 R² ± 標準偏差を締めるのが推奨される。
📌 まとめ — バリデーションの 7 原則 (SSDSE-B-2026 教材版)
原則 1: 必ず複数 fold で平均する 。 1 回切りの train_test_split は「運」に支配される。 47 都道府県のような小サンプルでは 10-fold CV が標準。
原則 2: 分布の形を確認する 。 目的変数が歪んでいるなら StratifiedKFold か log 変換。 図 2 のような可視化が必須。
原則 3: グループ構造を尊重する 。 同一県・同一患者・同一店舗が train/val に分かれないよう GroupKFold を使う。
原則 4: 時系列はリーク厳禁 。 未来データで過去を予測するモデルは「ズル」。 TimeSeriesSplit を必ず使う。
原則 5: 前処理は fold 内で fit 。 StandardScaler、 SMOTE、 PCA などはすべて Pipeline に入れる。
原則 6: 結果は平均だけでなく分布で報告 。 箱ひげ図 (図 3) と「平均 ± 標準偏差」を併記する。
原則 7: テストデータは 1 回だけ触る 。 何度も触ると「テストへの過学習」が起き、 実運用で性能が出ない。
この 7 原則を SSDSE-B-2026 の 47 都道府県データで実際に試しながら身につけることで、 バリデーションは「面倒な手続き」から「モデルの信頼性を担保する科学的手段」に変わる。 図 1〜3 と表 A〜E の合計 60 行を超える情報が、 そのまま現場での判断基準のチェックリストとなる。
📖 詳細解説 — fold ごとに「何が起きているか」を逐一追跡する
バリデーションの本質は「同じデータを訓練と検証で二重カウントしない」ことに尽きる。 47 都道府県のデータで 10-fold CV を行うとき、 内部では次のような処理が逐次的に走っている。 まず Step 1 として、 KFold オブジェクトが random_state=42 をシードに 47 個のインデックス [0,1,2,...,46] をシャッフルする。 シャッフル結果は決定論的で、 同じ random_state なら必ず同じ並びになる。 これがバリデーションの再現性を担保する。 次に Step 2 として、 47 個を 10 個のグループに分割する。 47 = 10×4 + 7 なので、 7 個の fold は 5 件、 3 個の fold は 4 件となる。 各 fold が一度ずつ「検証側」になり、 残り 9 fold が「訓練側」になる。 Step 3 では、 fold i ごとに model.fit(X_train_i, y_train_i) を呼び、 学習済みモデルを得る。 重要なのは「fold ごとにモデルが別物」であること。 fold 1 のモデルと fold 2 のモデルは別の係数を持つ。 Step 4 では model.predict(X_val_i) を呼び、 検証側の予測値を得る。 Step 5 で r2_score(y_val_i, y_pred_i) を計算し、 配列に蓄積する。 最後に Step 6 で 10 個のスコアの平均と標準偏差を取る。 これが cross_val_score の戻り値である。
この 6 ステップを SSDSE-B-2026(2023 年)で具体的に追跡しよう。 10-fold(random_state=42)の fold 2 は検証側に東京都が入る例である。 このとき訓練側 42 県の総人口の最大は神奈川県の 923 万人なので、 モデルは「923 万人以下」のデータしか見ていない。 得られる回帰式は概ね「一般診療所数 ≈ 0.000793 × 総人口(人) + 46」。 これで東京 (総人口 1,409 万人) を予測すると約 11,215 施設だが、 実際は 14,894 施設で、 残差は +3,679 施設という大きな外挿誤差になる。 線形モデルは外挿に弱く、 訓練データの「外側」の点を予測するのが極めて苦手であることが、 この実例から分かる。 これが「過大評価リスクが高い分割」の典型例で、 StratifiedKFold (総人口の対数を 5 分位でビニング) を使えば各 fold に必ず大都市が 1 件以上含まれるようになり、 この外挿の度合いを抑えられる。
さらに踏み込んで、 「なぜ fold ごとのスコアが揺れるのか」を分散分解の観点から整理する。 CV スコアの分散 Var(R²_cv) は、 大きく 3 成分に分解できる。 (1) 訓練データの選択による分散 (どの 42 県を学習に使うか)、 (2) 検証データの選択による分散 (どの 5 県で評価するか)、 (3) モデルの最適化に伴う数値分散 (確率的勾配降下なら毎回少し違う)。 SSDSE-B-2026 のような決定論的アルゴリズム (線形回帰) では (3) はゼロ、 (1) と (2) のみが効く。 fold 2 のように「検証側に大都市 (東京) が入る」のは (2) の極端ケースで、 これを抑えるには (a) k を増やす (10→20 fold)、 (b) 繰り返す (RepeatedKFold)、 (c) 層別する (StratifiedKFold) の 3 手段がある。 実務では (b) + (c) の組み合わせが最強で、 StratifiedKFold(10) を 5 回繰り返す (Repeated × Stratified) と、 50 個のスコアの平均と標準偏差から「真の汎化性能の 95% 信頼区間」を構成できる。
バリデーションのもう一つの重要な側面は「学習曲線 (learning curve)」との関係である。 学習曲線は横軸に訓練サンプル数、 縦軸に train/val スコアを取ったグラフで、 サンプル数を徐々に増やしながら CV を回して描く。 SSDSE-B-2026(2023 年)で総人口→一般診療所数の線形回帰の学習曲線を描くと、 訓練サンプル数 10 件のとき train R² = 0.97 に対し val R² = 0.67 と大きなギャップが見える。 サンプル数を 20, 30, 37 と増やすと、 train R² は 0.95 前後で横ばい、 val R² は 0.79 → 0.78 → 0.78 と立ち上がって頭打ちになる。 両者の差は 0.17 前後残るが、 これは東京という強い外れ値が線形当てはめを歪めるためで、 データを増やすだけでは縮まらない。 逆にギャップが素直に縮むなら「データが十分」のサイン。 もしギャップが残るなら、 (a) もっとデータを集める、 (b) モデルを単純にする (Ridge の α を上げる)、 (c) 特徴量を見直すの 3 択。 バリデーションは「モデルを評価する」だけでなく、 「データ不足かモデル不適かを診断する」道具でもある。
最後に、 バリデーションを使ったハイパラ探索 (GridSearchCV) について。 Ridge 回帰の α を [0.01, 0.1, 1, 10, 100] の 5 値で探索したいとき、 GridSearchCV は内部で 5 × cv (=10) = 50 回のモデル学習を行う。 各 α ごとに 10 個の CV スコアの平均を計算し、 最良の α を選ぶ。 ここで注意すべきは「選ばれた α の CV スコアをそのままモデルの性能として報告してはいけない」こと。 なぜなら α は「CV スコアが最大」になるように選ばれたので、 そのスコアは選択バイアスにより楽観的になる。 真の汎化性能を測るには、 GridSearchCV を外側の CV ループに包む「Nested CV」を使う。 外 5-fold × 内 10-fold = 50 回のハイパラ探索を行い、 外側の 5 つのスコアを平均する。 計算コストは高いが、 学術論文・ベンチマーク投稿では必須の手法である。
📚 表 F: 47 都道府県の CV 結果に基づく「予測誤差の分布」分析
10-fold CV (cross_val_predict) で得られた 47 都道府県の予測値と真値の残差を分析すると、 都道府県の規模カテゴリで誤差のパターンが大きく異なる。 以下は、 人口規模別に予測誤差の絶対値と相対誤差を集計した表である。
人口規模 該当県 県数 平均絶対誤差 (施設) 相対誤差 (%) 最大誤差県
超大規模 (1000万人以上) 東京 1 3,679 24.7% 東京 (-3,679)
大規模 (500-1000万人) 北海道, 埼玉, 千葉, 神奈川, 愛知, 大阪, 兵庫, 福岡 8 1,238 25.0% 埼玉 (+2,251)
中規模 (200-500万人) 宮城, 茨城, 新潟, 長野, 静岡, 京都, 広島 7 273 13.2% 茨城 (+650)
小規模 (100-200万人) 青森, 岩手, 福島, 栃木, 群馬, 岐阜, 三重, 岡山, 熊本, 鹿児島, … 21 108 9.6% 長崎 (-348)
超小規模 (100万人未満) 秋田, 福井, 山梨, 和歌山, 鳥取, 島根, 徳島, 香川, 高知, 佐賀 10 167 24.1% 和歌山 (-378)
💬 読み方 : 平均絶対誤差 (施設) は人口規模が大きいほど大きくなる (東京 3,679 → 超小規模 167)。 一方で相対誤差は U 字型で、 大規模県 (25.0%) と超小規模県 (24.1%) で大きく、 中規模 (13.2%)・小規模 (9.6%) で小さい。 大規模県は東京という外れ値に引っ張られた回帰直線からの乖離が大きく、 超小規模県は分母 (施設数) が小さいため相対誤差が跳ねる。 バリデーションでは「全体平均の R²」だけでなく、 「セグメント別の誤差分布」を見るのが現場では決定的に重要である。
🧪 表 G: バリデーション戦略の計算コスト比較
戦略 学習回数 SSDSE-B-2026 での実時間 推奨用途
train_test_split (1 回) 1 0.03 秒 デバッグ・初回試作
5-fold CV 5 0.15 秒 標準的な評価
10-fold CV 10 0.30 秒 論文・公式発表
RepeatedKFold (10×3) 30 0.90 秒 小サンプルで分散を抑制
LeaveOneOut (n=47) 47 1.40 秒 極小サンプル
GridSearchCV (α=5×10-fold) 50 1.50 秒 ハイパラ探索
Nested CV (外5×内10×α5) 250 7.50 秒 選択バイアス完全排除
線形回帰のような軽量モデルなら、 Nested CV でも 10 秒以内に終わる。 しかし RandomForest や XGBoost、 ニューラルネットワークでは 1 回の学習が 1-10 分かかることもあり、 Nested CV では数時間〜数日に膨張する。 そこで実務では「Nested CV を最終提出版のみ実施し、 開発中は通常の CV で素早く iterate する」のが現実的な妥協点になる。
🎓 学習チェックリスト — バリデーションを正しく実装できているか
☐ train_test_split で random_state を固定したか
☐ KFold で shuffle=True、 かつ random_state を固定したか
☐ 目的変数の分布を確認し、 歪んでいたら StratifiedKFold か log 変換を選んだか
☐ 同一エンティティの複数行があれば GroupKFold を使ったか
☐ 時系列なら TimeSeriesSplit を使い、 未来 → 過去のリークを防いだか
☐ 前処理 (Scaler, PCA, SMOTE) を Pipeline 内に入れたか
☐ CV スコアの平均だけでなく、 標準偏差・箱ひげ図も報告したか
☐ ハイパラ探索の結果をそのまま「モデル性能」として報告していないか (→ Nested CV)
☐ テストデータは最終評価で 1 回だけ使ったか
☐ セグメント別 (大都市 vs 小都市など) の誤差分布も確認したか
10 項目すべてに ✓ が付けば、 バリデーションの実装は実務レベルに達している。 1 項目でも × があれば、 そこから先のモデル評価結果は「楽観的」になる可能性が高く、 本番投入で性能が出ない原因になる。 バリデーションは地味な作業だが、 機械学習プロジェクトの信頼性の 80% を担う基盤工程である。
🧪 CV 戦略選択フローチャート — 7 つの分岐で最適な分割法を決める
バリデーション設計でもっとも頻発する失敗は「とりあえず KFold(n_splits=5) でやっておいた」 というものだ。 しかし問題設定により最適な CV 法は大きく異なる。 ここでは実務で使う 7 つの判定軸を 1 枚のフローチャートに整理し、 SSDSE-B-2026 のような構造を持つデータでどれを使うべきか具体的に示す。 まず冒頭の判定軸を順に通過させることで、 KFold / StratifiedKFold / GroupKFold / TimeSeriesSplit / Nested CV / LeaveOneOut / Holdout のいずれを選べばよいかが一意に決まる仕組みになっている。
判定軸 1: データは時系列か?
最初に確認すべきは「観測順序に意味があるか」 だ。 例えば SSDSE-B-2026 を年次パネルに拡張し、 2020 年 → 2021 年 → 2022 年と並んでいる場合、 ランダムシャッフルは「未来の情報で過去を予測する」 という根本的なリークを引き起こす。 この場合は TimeSeriesSplit 一択である。 一方、 2026 年単独の都道府県横断データであれば順序は無関係なので、 軸 2 へ進む。
判定軸 2: 同一エンティティが複数行あるか?
顧客 ID、 都道府県コード、 患者 ID など「同じ単位が複数回登場する」 データでは GroupKFold を使う。 SSDSE-B-2026 は 47 都道府県 × 1 行なので該当しない。 しかし将来「都道府県 × 年 × 月」 の複合パネルを扱う場合、 同じ都道府県が train と val に分かれるとリークが発生する。 「予測対象は新しい都道府県か?」 と自問し、 Yes なら GroupKFold が必須だ。
判定軸 3: 目的変数は不均衡分類か?
目的変数がカテゴリで、 かつクラス比率に偏りがある場合 (例: 不正検知 1:99) は StratifiedKFold を使う。 SSDSE-B-2026 で「人口 100 万人以上か」 を二値分類する場合、 該当 12 件 / 非該当 35 件と偏るため、 単純 KFold だと fold によっては正例 0 個になる。 必ず stratify オプションを有効化する。
判定軸 4: サンプル数 N は何件か?
N が極端に少ない場合 (N < 50)、 KFold は fold あたりサンプル 10 件未満になり、 分散が爆発する。 この場合は LeaveOneOut (LOO) を使い、 1 件ずつ検証する。 SSDSE-B-2026 の N=47 はぎりぎり KFold(5) の下限で、 LOO の方が安定する場面も多い。 逆に N > 10,000 では、 Holdout (単純 train/test 分割) でも十分な精度が出る。
判定軸 5: ハイパーパラメータ探索を行うか?
GridSearchCV や Optuna でハイパラを探索する場合、 「探索の CV スコア」 をそのまま最終性能として報告してはいけない。 これは選択バイアスにより 5-10% 楽観的になる。 正しくは Nested CV を使い、 外側 CV で性能を評価、 内側 CV でハイパラを選ぶ。 SSDSE-B-2026 のような小規模データほどこのバイアスは大きく出る。
判定軸 6: クラスタ・地理・組織構造はあるか?
「東日本と西日本」 のような地理クラスタや、 「医師 A の患者群」 のような組織構造がある場合、 クラスタ単位で分割する GroupKFold を使う。 SSDSE-B-2026 を地域ブロック (北海道・東北・関東・中部・近畿・中国・四国・九州沖縄) でグルーピングし、 「未知の地域ブロックを予測する」 設定なら、 ブロック単位 KFold が必要になる。
判定軸 7: 評価指標は何か?
評価指標が R² や RMSE のように連続値の場合、 fold ごとの平均で集計する。 AUC や F1 のように非加算的な指標の場合、 各 fold の予測値を結合してから 1 回計算する (= cross_val_predict 方式)。 この違いを忘れると、 「fold ごと AUC の平均」 と 「結合後 AUC」 で 0.02-0.05 の乖離が生じることがある。 報告時には必ずどちらの方式かを明記する。
判定軸 条件 推奨 CV 法 SSDSE-B-2026 での該当例
1. 時系列性 順序に意味あり TimeSeriesSplit 複数年パネルに拡張時
2. 重複エンティティ 同 ID 複数行 GroupKFold 都道府県 × 年パネル
3. クラス不均衡 分類で偏り StratifiedKFold 人口 100 万超かの分類
4. サンプル数小 N < 50 LeaveOneOut N=47 でぎりぎり KFold(5)
5. ハイパラ探索 あり Nested CV GridSearch + 報告時
6. クラスタ構造 地理・組織 GroupKFold 8 地域ブロック分割
7. 評価指標性質 非加算的 cross_val_predict AUC, F1 の正確報告
この 7 軸フローチャートを実務に組み込むと、 CV 選択ミスによる「本番で性能ガタ落ち」 事故が大幅に減る。 特に判定軸 1 (時系列) と判定軸 2 (重複エンティティ) は、 見落とすと結果が 10-30% 楽観的になる致命的な軸であり、 プロジェクト開始時に必ず議論しておくべき項目だ。
📊 バリデーション結果の正しい報告フォーマット — 5 つの要素を必ず含める
バリデーションを実施しても、 結果報告で「精度 0.85 でした」 とだけ書いてはいけない。 これは情報量が少なすぎて、 ステークホルダーが意思決定できない。 ここでは現場で実際に使われる「5 要素報告フォーマット」 を SSDSE-B-2026 の都道府県データを使った具体例とともに示す。 このフォーマットを採用すると、 上司・クライアント・運用チームに「このモデルを本番投入してよいか」 を正しく判断してもらえるようになる。
要素 1: CV スコアの平均と標準偏差 (ばらつき)
平均値だけでは fold 間のばらつきが見えない。 標準偏差まで報告することで、 「平均 0.85 ± 0.02 (安定)」 と 「平均 0.85 ± 0.15 (不安定)」 を区別できる。 後者は本番で fold 1 のサンプルに似たデータが来ると 0.7 まで落ちる可能性がある。 SSDSE-B-2026 で総人口から一般診療所数を予測する線形回帰では R² = 0.776 ± 0.148 のように両方記載する。 このケースは標準偏差が平均の約 19%(> 5%)に達しており、 「不安定なモデル」 と判定される(東京という外れ値の影響で fold 間 R² が 0.58〜0.95 と揺れる)。
要素 2: fold ごとの個別スコア (再現性チェック用)
fold 1: 0.812, fold 2: 0.950, fold 3: 0.622, fold 4: 0.584, fold 5: 0.910 のように個別値を出すと、 「fold 4 だけ低い → どの都道府県が含まれていた?」 という追跡が可能になる(fold 4 は青森・岩手・埼玉・静岡・三重・山口・徳島・愛媛・長崎)。 GitHub の Issue や Slack に貼り付ける際、 個別値があると後から再現性を確認しやすい。 一方、 個別値が無いと「再実行したら平均 0.83 になった」 となった時に原因追及できない。
要素 3: 使用した CV 法と乱数シード
「KFold(n_splits=5, shuffle=True, random_state=42)」 と明記する。 これがないと第三者が再現できず、 結果の信頼性が大幅に下がる。 特に Kaggle や論文では、 「random_state=0 で 0.85、 random_state=42 で 0.81」 のような不安定さが報告されないと、 cherry-picking (都合の良い結果だけ採用) の温床になる。 複数の seed で実行し、 「seed 0/42/2025 で平均 0.83 ± 0.02」 のように記載するのが理想だ。
要素 4: ベースラインとの比較
「精度 0.85 でした」 だけでは、 それが良いのか悪いのか判定不能だ。 必ずベースライン (= 何もしない、 平均予測、 多数派予測) と比較する。 SSDSE-B-2026 で「総人口から一般診療所数を予測」 の場合、 平均値予測 (R² = 0) に対し、 線形回帰 R² = 0.78 なら「平均予測比 +0.78」 という改善幅が見える。 もし R² = 0.05 なら「ほぼベースラインと同等」 と判定でき、 モデルを使う意味がないと判断できる。
要素 5: 信頼区間 (CI) または分布の可視化
5 fold の結果から 95% 信頼区間を計算するか、 ヴァイオリンプロットで分布を見せる。 「平均 0.85 [0.78, 0.92]」 という表記により、 「悲観的には 0.78 まで落ちる可能性がある」 と読み手が分かる。 ステークホルダーへの説明では、 平均値だけより信頼区間を含めた方が「想定外で性能が落ちました」 というクレームを未然に防げる。
要素 記述例 省略時のリスク
平均 ± 標準偏差 R² = 0.776 ± 0.148 安定性が不明
fold ごと個別値 [0.812, 0.950, 0.622, 0.584, 0.910] 再現性確認不可
CV 法 + シード KFold(5, shuffle=True, rs=42) 第三者再現不可
ベースライン比較 平均予測 R²=0 比 +0.78 改善幅判定不能
95% 信頼区間 [0.569, 0.982] 下振れリスク不可視
この 5 要素を含む報告書は、 例えば次のような 1 段落になる: 「SSDSE-B-2026(2023 年)の総人口から一般診療所数を予測する線形回帰モデルにおいて、 5-fold CV (KFold, shuffle=True, random_state=42) で R² = 0.776 ± 0.148 (95% CI: [0.569, 0.982]) を得た。 fold ごとの個別値は [0.812, 0.950, 0.622, 0.584, 0.910] で、 平均値予測 (R²=0) 比で +0.78 の改善である。 標準偏差が平均の約 19% と大きく安定性に欠けるが、 これは東京都という強い外れ値の影響で、 対数変換や層化 (StratifiedKFold) に改善余地がある。」 このように 5 要素すべてを含めて初めて、 ステークホルダーが「本番投入してよいか」 を判定できる情報量になる。
🚨 バリデーション失敗 8 事例とリカバリ手順 — 現場で本当に起きた問題集
実務でバリデーションが「機能しなかった」 ケースを 8 つ集めた。 これらは机上の話ではなく、 現場で実際に「本番で性能が出ない」 「再現実験で結果が変わる」 「ステークホルダーから不信感を持たれる」 という事故として発生した事例である。 失敗パターンを事前に知っておくことで、 同じ轍を踏まずに済む。
事例 1: train/test 分割前にスケーリング
StandardScaler を train+test 全体で fit してから分割した結果、 test の平均・分散情報が train に漏洩。 CV スコア 0.95 → 本番 0.78 に低下。 リカバリ: Pipeline でスケーラを内包し、 fold ごとに fit_transform (train) → transform (val) の順で適用する。 これだけで漏洩はゼロになる。
事例 2: SMOTE をデータ全体に適用
クラス不均衡対策で SMOTE をデータ全体に適用してから CV すると、 合成サンプルが train と val 両方に含まれリークする。 リカバリ: imblearn の Pipeline を使い、 SMOTE は fold 内の train だけに適用する。 imbalanced-learn の Pipeline はこのパターンを正しく扱える。
事例 3: 同一顧客が train と val に分散
顧客 ID = 1234 が 10 行ある場合、 通常 KFold だと半分が train、 半分が val になる。 顧客特有のパターンを学習してしまい、 CV 0.92 → 新規顧客本番 0.65。 リカバリ: GroupKFold(groups=customer_id) で同一顧客を同じ fold に固定する。
事例 4: 時系列を shuffle=True で分割
株価予測で KFold(shuffle=True) を使うと、 未来の情報が train に含まれる。 CV 0.88 → 本番 0.51。 リカバリ: TimeSeriesSplit を使い、 必ず「過去 → 未来」 の順で分割する。 加えて 1-2 日のギャップを設けると、 ラグ相関のリークも防げる。
事例 5: ハイパラ探索結果を最終性能として報告
GridSearchCV.best_score_ を「モデル性能」 と報告したら、 本番で 5-10% 低下。 これは選択バイアスによる楽観評価。 リカバリ: 外側 CV (cross_val_score) で評価、 内側 CV (GridSearch) で選択する Nested CV を使う。
事例 6: テストデータを複数回使い回す
モデル A, B, C を全部テストデータで評価し、 最良を選ぶと、 テストデータが「事実上の val」 になり性能が楽観的に。 リカバリ: テストは最終 1 回だけ。 中間評価は CV のみで行う。 もしくはテストを 2 つに分け、 「中間用」 と「最終用」 を分離する。
事例 7: クラス比率が極端で fold が偏る
陽性率 1% のデータを KFold(5) すると、 fold 5 に陽性 0 件のことがある。 AUC が計算不能か、 評価が無意味に。 リカバリ: StratifiedKFold で各 fold のクラス比率を保持する。 さらに陽性が 100 件未満なら、 RepeatedStratifiedKFold で複数回繰り返し平均化する。
事例 8: 乱数シードを固定せず再実験で結果が変動
「先週 0.85 だったのに今週 0.82 になった」 という問い合わせ。 原因は random_state 未固定。 リカバリ: KFold、 train_test_split、 モデル (RandomForest など) すべてに random_state を明示。 さらに numpy.random.seed、 Python random.seed も冒頭で固定する。 これにより 100% 再現可能になる。
事例 CV→本番 リカバリ手順
1. 全体スケーリング 0.95→0.78 Pipeline で fold 内 fit
2. 全体 SMOTE 0.93→0.71 imblearn Pipeline
3. 顧客 ID 分散 0.92→0.65 GroupKFold
4. 時系列 shuffle 0.88→0.51 TimeSeriesSplit
5. 探索結果報告 0.90→0.83 Nested CV
6. テスト使い回し 0.87→0.79 最終 1 回 + 中間用分離
7. クラス偏り AUC 計算不能 StratifiedKFold
8. シード未固定 0.85→0.82 全コンポーネント rs 明示
これら 8 事例の共通点は「CV スコアが楽観的に出る」 ことだ。 つまり「CV では良いけど本番で悪い」 という現象の 9 割以上はこの 8 つのどれかに該当する。 プロジェクト開始時にこの 8 項目のチェックリストを全員で確認し、 1 つでも該当があれば早期に修正する文化を作ると、 「本番投入したけど性能が出ない」 という最悪の事態を未然に防げる。 バリデーションは「正しい方法で 1 回」 やる方が、 「間違った方法で 100 回」 やるより遥かに価値がある。 関連用語として 交差検証 、 ホールドアウト法 、 グリッドサーチ 、 過学習 も合わせて参照してほしい。
📈 バリデーションの歴史と発展 — Holdout から Nested CV へ
バリデーション技術は 1930 年代の Holdout 法から始まり、 90 年間で大きく発展してきた。 ここでは主要な技術が「どんな問題を解決するため」 に登場したかを時系列で整理する。 歴史を知ることで、 「なぜ KFold が標準なのか」 「なぜ Nested CV が必要になったのか」 という背景が理解でき、 適切な技術選択ができるようになる。
1930 年代: Holdout 法の登場
統計学者 Larson が「データを 2 つに分けて、 一方で学習、 もう一方で検証」 という考えを提唱。 これがバリデーションの起源だ。 当時はサンプル数が十分にあったため、 単純な分割で十分機能した。 SSDSE-B-2026 のような N=47 では精度が不安定だが、 N=10,000 以上なら今でも有効。
1968 年: K-fold CV の体系化
Mosteller と Tukey が論文で K-fold CV を体系化。 「サンプル数が少ない時、 すべてのデータを訓練・検証両方に使えれば効率的」 という発想で、 fold ごとに役割を入れ替える方式が提案された。 これにより N=47 のような小規模データでも安定した評価が可能になった。
1974 年: Leave-One-Out (LOO) の理論的整備
Stone が LOO CV の理論的性質を解明。 N=fold 数の極端な場合として位置付け、 バイアスは最小だが分散は最大という性質を示した。 現代では N が極端に小さい時 (N<30) のみ実用的。
1995 年: Stratified K-fold の普及
Kohavi が「Stratified K-fold は通常 KFold より分散が小さい」 ことを実証。 不均衡分類で必須の技術として普及した。 SSDSE-B-2026 で 100 万人超を二値分類する場合、 Stratified を使わないと fold 5 で陽性 0 件になる事故が起きる。
2006 年: Nested CV の理論確立
Varma と Simon が「ハイパラ探索結果を最終性能として報告すると 5-10% 楽観的になる」 ことを実証。 これにより Nested CV が論文標準として定着。 2020 年代の Kaggle 上位解法では、 ほぼ全て Nested CV または同等の手法が使われている。
年代 技術 解決した問題
1930s Holdout 「学習データで評価」 のリーク防止
1968 K-fold CV 小規模データでの評価安定性
1974 LOO CV 極小サンプル (N<30) 対応
1995 Stratified KFold 不均衡分類の fold 偏り
2006 Nested CV ハイパラ選択バイアス
この歴史を踏まえると、 現代のバリデーション設計は「過去 90 年の累積的な失敗の教訓」 の上に成り立っていることが分かる。 つまり KFold, StratifiedKFold, GroupKFold, TimeSeriesSplit, Nested CV はそれぞれ「実務で起きた具体的な失敗」 を回避するために生まれた技術である。 単に「scikit-learn にあるから使う」 のではなく、 「どんな失敗を防ぐためのものか」 を理解して選ぶことで、 自分のプロジェクトに最適な CV 設計ができるようになる。 関連用語として ホールドアウト法 、 交差検証 、 Nested CV 、 層化抽出 も合わせて参照してほしい。
2010 年代以降: Repeated CV と Bayesian CV の普及
2010 年代に入ると、 単一の KFold では分散が大きすぎる問題に対応するため RepeatedKFold が普及した。 これは KFold を異なるシードで複数回繰り返し、 全結果を平均化する手法である。 例えば SSDSE-B-2026 で RepeatedKFold(n_splits=5, n_repeats=10) を使うと、 計 50 回の評価結果から信頼区間を作れる。 単一 KFold での 「平均 0.95 ± 0.03」 が、 Repeated だと 「平均 0.948 ± 0.012」 のようにばらつきが小さく見えるようになり、 統計的に有意な差を検出しやすくなる。 さらに 2015 年以降は Bayesian CV (Vehtari ら) も論文標準として広まり、 PSIS-LOO のような近似手法で計算コストを抑えつつ LOO 相当の評価を実現できるようになった。
2020 年代: ML Ops とバリデーションの統合
2020 年代以降は、 ML Ops の文脈でバリデーションが「単発のスクリプト」 から「継続的な監視プロセス」 へと変貌した。 具体的には MLflow や Weights & Biases のような実験管理ツールに CV 結果を自動記録し、 モデル更新時に「前回 CV vs 今回 CV」 を自動比較する文化が定着している。 SSDSE-B-2026 を毎年更新するシステムでは、 2025 年版 vs 2026 年版で R² が 0.78 → 0.70 と下がった場合、 「人口動態の構造変化か、 データ品質劣化か」 を即座にアラート発火できる仕組みが標準になった。 つまり「バリデーション = 一度きりの評価」 から「バリデーション = 継続的な品質保証」 へとパラダイムが移行している。 この流れは今後さらに加速し、 LLM 時代には「CV 結果を LLM が自然言語で解釈し、 異常検知レポートを自動生成」 する未来も近い。
未来展望: Adaptive CV と AutoML 統合
2026 年現在、 研究レベルでは「Adaptive CV」 という新しい潮流が出てきている。 これはデータの性質 (時系列性・クラス不均衡・クラスタ構造) を自動判定し、 最適な CV 法をアルゴリズムが選ぶ仕組みだ。 例えば SSDSE-B-2026 のようなデータを投入すると、 「N=47 で時系列性なし、 連続値予測 → KFold(5) 推奨」 のように AutoML 側が判定してくれる。 既に H2O AutoML や AutoGluon の一部機能で実装が始まっており、 今後 5 年で「初学者でも適切な CV を選べる時代」 が到来する見込みだ。 ただし、 自動選択に頼り切るのではなく、 本記事で示した 7 軸フローチャートを自分で実行できる力こそが、 データサイエンティストとしての本質的な競争力になる。 自動化が進めば進むほど、 「なぜその CV を選んだか」 を説明できる力が差別化要因となる。 これは AI 時代の人材論の核心であり、 バリデーション一つを取っても「仕組みを理解する人」 と「ツールに頼る人」 で大きな格差が生まれる時代に突入している。
最後に強調しておきたいのは、 バリデーションは「結果を出すための手段」 ではなく「結果の信頼性を担保するための手段」 だという点だ。 CV スコア 0.95 という数字そのものに価値があるのではなく、 「その 0.95 は本番でも再現する」 という保証に価値がある。 この本質を理解した上で、 自分のプロジェクトに必要な CV 設計を選び、 5 要素フォーマットで報告し、 8 つの失敗事例を回避することが、 データサイエンティストの基本動作になる。 SSDSE-B-2026 のような実データで何度も練習することで、 この基本動作は確実に身につく。 ぜひ手元の Jupyter Notebook で実装し、 KFold / StratifiedKFold / GroupKFold / TimeSeriesSplit の挙動の違いを体感してほしい。 1 度自分の手で書けば、 二度と忘れない知識になる。 そしてその知識は、 機械学習プロジェクトの成否を左右する基盤技術として、 あなたのキャリアを長く支えることになるだろう。 バリデーションを制する者がモデリングを制する、 という言葉は決して大袈裟ではなく、 機械学習の現場で実証され続けている真実である。
📚 関連グループ教材
「バリデーション」を含むカテゴリ「ML基礎 」の全体像を学べる横断教材:
用語単独の理解だけでなく、 グループ教材で 前後関係・全体構造 を掴むと、 実務・試験ともに応用が効きます。
📌 まとめカード — 試験前 1 分復習
用語 バリデーション
英語 Validation
カテゴリ ML基礎
一言定義
出題されやすい論点 隣接概念との違い・典型手法・落とし穴
使用データ例 SSDSE-B-2026.csv(47 都道府県社会経済指標)
🗓 歴史・年表
バリデーションの主要なマイルストーン:
年 出来事
1974 Stone, Geisser が交差検証の概念を提唱 1995 Kohavi が 10-Fold CV を推奨 2010 ImageNet が Train/Val/Test を明確化 2015 Stratified KFold が sklearn 標準 2020s Nested CV が Kaggle で常識化
📊 比較表 — 同カテゴリの主要選択肢
「バリデーション」と関連する手法・概念を比較しておくと、 使い分けに迷わない:
項目 特徴 補足
Holdout 1 回分割 シンプル K-Fold CV K 回平均 ロバスト Stratified K-Fold クラス層化 不均衡向き Time Series Split 時系列順 未来リーク防止 Group K-Fold グループ単位 ID 単位の評価 Nested CV 二重 CV ハイパラ + 評価
❓ よくある質問 (FAQ)
「バリデーション」について試験対策・実務で頻出する質問とその回答:
Q. fold 数は何が良い?
A. 5 か 10 が定番。 データ少なければ 10、 多ければ 5。
Q. CV と Holdout 併用は?
A. CV でモデル選び + 最後に 1 度だけ Test、 が王道。
Q. Time Series CV の罠は?
A. 全 fold で同じ評価期間にしないと不公平。 expanding window が一般的。
Q. Stratified K-Fold を回帰に?
A. y をビン化してから StratifiedKFold する。
Q. CV スコアの揺れが大きいとき?
A. fold 数を増やす、 Bootstrap を併用、 SE を報告。
📝 実践演習 — 手を動かして定着
本ページの理解を確認する 5 問の練習問題です。 紙とペン、 もしくは Python で取り組んでみてください:
定義の言い換え :「バリデーション」を 2 行以内で自分の言葉に書き直してください。 出典を引用しないこと。
カテゴリ整理 :「バリデーション」が属するカテゴリ「ML基礎」内で、 隣接する 3 用語を挙げ、 それぞれとの違いを 1 文で書く。
SSDSE-B-2026 で実装 :本ページの「🧮 実値計算」のコードを実行し、 出力結果をスクリーンショットで残す。
落とし穴チェック :本ページの「⚠️ 落とし穴」5 件のうち、 自分が実際にやってしまいそうな 1 件を選び、 防止策を 100 字で書く。
応用シナリオ :「バリデーション」を新しい問題(自分の業務 or 卒研テーマ)に当てはめると、 どの場面で何のために使えるか、 200 字で書く。
💡 ヒント:練習問題の答えは正解が 1 つではありません。 思考プロセスを書き残すことが学習効果を高めます。
📚 参考文献・学習リソース
「バリデーション」をさらに深掘りするための一次資料・教科書・オンラインコース:
はじめてのパターン認識 (平井有三、 森北出版)— 古典 ML の網羅的入門
Pattern Recognition and Machine Learning (Bishop, Springer)— 数理的に厳密
Deep Learning (Goodfellow, Bengio, Courville)— 深層学習の標準教科書
The Elements of Statistical Learning (Hastie, Tibshirani, Friedman)— 統計学習の正典
scikit-learn ユーザーガイド — Python 実装の決定版オンライン教材
Hugging Face Course — Transformer/LLM の無料コース
Kaggle Learn — 短時間で実践スキルが身につくマイクロコース
JDLA G 検定 公式テキスト — 日本の AI 資格対策に最適
統計検定 公式問題集 — 統計理論の橋渡しに有用
JMOOC / Coursera / edX — 大学レベル講義を無料/低価格で受講可能
🔍 深掘り解説 — 中級者向け補強
Validation は「Train で学んだモデルが未知データで通用するか 」を測る中間評価。 Test と違い 何度でも触ってよい 。 Val スコアでハイパラ選択・モデル比較・Early Stopping を行う。 ただし Val に過適合すると Test で痛い目に遭うので、 重要場面では Nested CV を使う。
📋 代表シナリオ一覧
Validation 手法の選び方:
シナリオ 概要 データ/環境 評価指標
Single Holdout 1 回の分割 大規模データ向け 70/30 が典型 K-Fold CV K 回分割 中小データ向け 5 or 10 fold Stratified K-Fold クラス層化 不均衡分類向け 標準推奨 Time Series Split 時系列順 時系列データ向け expanding window Group K-Fold ID 単位 患者/顧客データ向け リーク防止 Nested CV 二重 CV ハイパラ + 評価 計算コスト大
💼 ビジネス文脈での扱い
「バリデーション」を業務適用する際は、 (1) 業務 KPI と評価指標の対応 、 (2) データの収集・保管・更新コスト 、 (3) 社内承認とコンプライアンス 、 (4) 運用人員の確保 、 (5) 失敗時のロールバック計画 の 5 観点をプロジェクト計画書に必ず明記してください。 技術検証(PoC)の段階で 本番運用要件を逆算 しておくと、 後の本番化フェーズで詰まる確率が下がります。
🧪 学習ロードマップ
定義の把握 :本ページの「📐 数式・定義」を 3 回読む
具体例の理解 :「🎨 直感で掴む」と「🧮 実値計算」のコードを実行する
落とし穴の暗記 :「⚠️ 落とし穴」5+ 件を 1 行ずつ自分の言葉で要約
関連概念の整理 :「🔗 関連用語」を前提・並列・発展でマインドマップに描く
応用問題 :自分の業務 or 卒研テーマにバリデーションを適用してみる
説明テスト :他人に 3 分で説明できるか試す。 詰まったポイントを補強
🗂 ミニ用語集 — 本ページ頻出語
「バリデーション」を学ぶ過程で頻出する関連語を 12 個、 短文定義でまとめます。 知らない語があれば各ページにジャンプしてください:
機械学習 (ML)
データからパターンを自動で学ぶ手法。 AI の中核技術。
深層学習 (DL)
多層ニューラルネットによる ML。 画像・言語で強い。
教師あり学習
入力と正解ラベルのペアから学習する枠組み。
教師なし学習
正解ラベルなしで構造を見つける学習。 クラスタリング等。
強化学習
環境との相互作用と報酬から最適行動を学ぶ。
汎化
学習データに含まれない未知データでも性能を出すこと。
過学習
Train データに適合しすぎ、 未知データで性能が落ちる現象。
交差検証 (CV)
データを K 分割し平均で評価。 小データのロバスト評価。
特徴量エンジニアリング
予測精度を上げるために変数を設計・変換する作業。
評価指標
RMSE・F1・AUC など、 モデル性能を測る尺度。
ハイパラ調整
学習で直接決まらない設定値を体系的に最適化する作業。
MLOps
ML モデルの本番化・運用・監視・再学習を統合する活動。
本用語集は 514 用語を 29 のグループ教材と連動して整理しています。 周辺概念を 1 つずつ辿ると、 「バリデーション」の位置づけと使い分け が立体的に理解できます。
✅ チェックリスト — 実務で使う前の最終確認
バリデーションを実際のプロジェクトやレポートに適用する前に、 以下の項目を確認してください:
□ 定義の理解 :本ページ「📐 数式・定義」の数式を、 紙に書き出して自分で説明できる
□ 適用条件の把握 :使用前提(サンプル数・データ尺度・独立性)を満たしているか確認した
□ データ品質チェック :欠損値・外れ値・スケール・分布の偏りを確認した
□ ベースラインの設定 :シンプルなモデルから始めて、 比較基準を作った
□ 評価指標の選定 :業務 KPI と機械学習指標の対応関係を明文化した
□ Train/Val/Test の分割 :データリーケージを避けた分割設計
□ 再現性の確保 :random_state 固定・ライブラリバージョン固定・データバージョン管理
□ 不確実性の評価 :点推定だけでなく信頼区間・標準誤差も算出
□ 結果の解釈 :「何を意味するか」「何を意味しないか」を明確に区別
□ 限界の明示 :適用範囲外への外挿を避ける記述を加えた
□ 倫理・規制の確認 :プライバシー・公平性・説明責任への対応
□ 運用設計 :監視・再学習・ロールバックの仕組みを準備した
□ ドキュメント化 :モデルカード・実験ログを残した
□ ステークホルダ説明 :非技術者にも 3 分で説明できる
□ 関連グループ教材 で全体像を確認した
📝 レポート・論文での書き方
バリデーションを分析レポート・卒業論文・社内資料で扱う際の 標準的な記述構成 :
① 背景と目的
何を予測・分類・最適化したいか、 業務上の意義を 100-200 字で明確化。 ターゲット指標と成功基準を必ず数値で記述(例「F1 ≥ 0.85 を目指す」)。
② 使用データ
出典・期間・サンプル数・前処理手順を表形式で示す。 SSDSE-B-2026 のような公的データを使う場合は 取得日と URL も明記。 欠損率・外れ値処理の方針も記述。
③ 手法
使用したアルゴリズム・ハイパラ・ライブラリバージョンを記述。 数式は本ページ「📐」のように $$...$$ で記述すると LaTeX/Markdown 共通で扱える。
④ 結果
点推定だけでなく、 信頼区間・標準誤差・p 値 を併記。 グラフは scatter / box plot / heatmap を適材適所で使い分け。 軸ラベル・凡例・キャプションを忘れず。
⑤ 解釈
「数値が意味すること」と「意味しないこと」を分けて記述。 相関と因果を混同しない、 外挿を避ける、 など慎重に。
⑥ 限界と今後
本研究の制約(データ量・対象期間・対象地域)と、 今後の研究で解決したい点を率直に書く。 査読者・上司は限界の自己認識を必ず確認する。
⑦ 参考文献
本ページ「📚 参考文献・学習リソース」を起点に、 一次資料を引用。 BibTeX 形式で管理しておくと再利用が楽。
🎓 試験対策ピンポイント
統計検定・G 検定・基本情報・応用情報・ML エンジニア試験でバリデーションが問われやすい論点:
定義の言い換え問題 :バリデーションを別の言葉で説明できるか。 教科書の定義丸暗記ではなく、 自分の言葉に翻訳しておく。
隣接概念との比較 :似て非なる概念(例:AI と ML、 分類と回帰、 Val と Test)の違いを 1 行で書ける。
数式の読み解き :本ページ「🔬 数式を言葉で読み解く」の記号一覧を覚える。 各記号の意味を埋める穴埋め問題が多い。
代表的アルゴリズム名 :バリデーションの代表手法(例:勾配ブースティングなら XGBoost, LightGBM)を 3 つ以上挙げられる。
落とし穴の選択肢問題 :本ページ「⚠️ 落とし穴」の典型ミスは試験で問われる頻出論点。
応用シナリオ判定 :「このシナリオでどの手法を使うか?」という選択肢問題。 本ページ「🔍 深掘り解説」のシナリオ表が役立つ。
計算問題 :簡単な数値計算が出る場合がある。 本ページ「🧮 実値計算」のコードを 1 度実行しておくと身につく。
歴史・年代問題 :バリデーションが提案された年・人物が問われる場合がある。 本ページ「🗓 歴史・年表」を確認。
📌 試験対策のコツ:用語の 定義 + 使用場面 + 制約条件 をセットで覚えると応用が利きます。
🎯 バリデーション戦略の選び方(フローチャート)
バリデーションは「データの性質」「サンプル数」「予測対象の構造」によって最適手法が変わる。 以下は実務でよく使う判断ルール。
条件 推奨手法 理由
n ≥ 10,000、 IID Hold-out (80/10/10) 十分大きいので 1 回の分割で安定
n < 1,000、 IID 5-fold CV または 10-fold CV 分散を抑える、 全データを評価に使う
n < 100 (極小) Leave-One-Out CV (LOOCV) バイアス最小、 ただし計算量大
不均衡分類(少数クラス < 10%) Stratified k-fold CV 各 fold の正例比率を保つ
グループ構造あり(患者 ID 等) GroupKFold / LeaveOneGroupOut 同一グループが train/val に分かれることを防ぐ
時系列データ TimeSeriesSplit / Rolling Origin 未来→過去の漏洩を防ぐ
ハイパラ調整 + 性能評価 Nested CV 最終評価のバイアスを除く
空間データ(地理) Spatial CV(地域ブロック分割) 近傍点の漏洩防止
特に注意すべきは 時系列とグループ構造 。 SSDSE-B-2026 の都道府県データを year で並べたパネルデータでは、 TimeSeriesSplit を使わないと未来情報のリークが起きる。
🗺 概念マップ
関連概念を視覚的に整理した概念マップ。
validation
教訓
Pipeline 内
平均と標準偏差の両方
全データで再学習
スコアの安定性
分割の妥当性
このマップは validation の意思決定の流れを表す。 中央に「validation」を置き、「分割の妥当性 (層化が必要か、 時系列性があるか)」「スコアの安定性 (CV fold 間の標準偏差)」「全データで再学習 (本番モデルの構築)」「ハイパーパラメータ選択 (グリッドサーチ・ベイズ最適化との接続)」の 4 軸へ枝が伸びる。 SSDSE-B-2026 の場合、 47 都道府県という小標本では 5-fold CV と LOO の差が大きく、 分割設計が validation 結果を左右する点に注意したい。
🔗 隣接手法への橋渡し
バリデーションは「モデル評価」を担う層であり、 前後の手法とリレー的に接続する。
SSDSE-B-2026 (47 都道府県) のように小標本ではホールドアウト不可、 5-fold CV か LOO が必須となる。 学習曲線と組み合わせ、 「データを増やせば改善するのか、 モデル変更が必要なのか」を判断する。
🌳 手法選択フロー
バリデーション手法は標本サイズと時系列性で決まる。 以下の 3 段階で選択する。
時系列性があるか? Yes → TimeSeriesSplit (時系列用 CV) (未来情報漏洩防止)、 No → 次へ
標本サイズ N は? N>=10000 → 単純な train/test 分割、 100<=N<10000 → 5-fold か 10-fold CV、 N<100 → LOO または反復 CV
クラス不均衡か? Yes → 層化 K-fold (sampling) でクラス比保持、 No → 通常の K-fold
SSDSE-B-2026 (N=47) のケースでは「No → 100 未満 → LOO または 5-fold 反復」が選ばれる。 fold 間の標準偏差が平均の 20% を超える場合、 分割設計か特徴量を見直す。
🌱 解説の深化 — 「CV スコアそのもの」の不確実性を疑う
ここまでは「どう分割するか」を扱ってきた。 この節では一歩引いて、 算出された CV スコア自体がどれほど信用できる数値なのか を、 SSDSE-B-2026(2023 年・47 都道府県)の実測で確かめる。 モデル・変数は本文の実値計算と同じ(説明変数 A1301 15歳未満人口・A4101 出生数・A4103 合計特殊出生率 → 目的変数 A1101 総人口、 線形回帰)。
🎨 直感 — CV スコアは「測定値」ではなく「推定値」
5-fold CV の平均 R² は、 モデルの汎化性能という見えない真値 を、 たまたま引いた 1 通りの分割 で推定した値にすぎない。 分割は random_state(シード)で決まるので、 シードを変えれば同じデータ・同じモデルでもスコアは動く。 体温計で 1 回測った 36.8℃ をそのまま信じず「測るたびに少し揺れる」と知っておくのと同じで、 CV スコアには標本誤差ならぬ「分割誤差」が乗っている 。 N=47 の小標本ではこの揺れが無視できない大きさになる。
実測:本文と同じモデルで random_state を 0〜9 の 10 通りに変えて 5-fold CV を回すと、 平均 R² は 0.955(seed=6)〜 0.990(seed=9)まで 0.035 の幅 で動いた(10 シードの平均 0.978、 シード間 SD 0.011)。 本文の seed=42 の値 0.979 は、 この分布のほぼ中央に落ちる「たまたま素直な 1 標本」だったことになる。
⚠️ 落とし穴(重要) — fold 平均に埋もれる外れ値と「シード選び」
fold 集計は 1 点の異質さを隠す :LOO(1 個抜き)で 47 都道府県それぞれの予測誤差を出すと、 絶対誤差の中央値は約 93,744 人なのに、 東京都だけ 1,368,046 人(実人口 14,086,000 人)と中央値の約 14.6 倍 。 以下 愛知 823,525 人・北海道 759,258 人・沖縄 635,340 人・福岡 622,368 人と続く。 5-fold の fold 単位 R² では東京の失敗が同じ fold の他 8 県の成功に薄められ、 seed=42 でも東京を含む fold が 0.953 に見える程度で済んでしまう。
「東京入り fold が最悪」とは限らない :シード 100 通りで検証すると、 東京都を含む fold が最悪 fold になったのは 17 回で、 偶然の水準(5 fold なら 20%)と大差ない。 つまり fold 単位のスコア比較から外れ値を特定するのはほぼ不可能 で、 LOO 誤差や cross_val_predict による点ごとの残差診断 が別途必要になる。
シードの「良い方選び」は無自覚のリーク :0.955〜0.990 の幅があると知ると、 高いシードを選んで報告したくなる。 これは Test を覗くのと同型の楽観バイアスであり、 シードは事前に固定し、 揺れ幅ごと報告する のが正しい作法。
±SD の解釈にも注意 :本文の「0.979 ± 0.017」の SD はfold 間のばらつき であり、 fold 同士は訓練データを共有して相関するため、 これを √K で割った「標準誤差」を素朴な信頼区間として使うと不確実性を過小評価しやすい。
🔬 発展 — 揺れを測って報告する道具立て
RepeatedKFold で分割誤差ごと推定 :RepeatedKFold(n_splits=5, n_repeats=10, random_state=0) の実測は R² = 0.981 ± 0.018(50 fold、 最小 0.931・最大 0.999) 。 1 回の 5-fold より「揺れ込み」の性能像が得られる。 参考:LOO の全予測値から一括計算した R²(cross_val_predict 経由)は 0.988。
1 標準誤差ルール :モデル・ハイパラ候補間のスコア差がこの揺れの範囲内なら「同点」とみなし、 最良スコアから 1SE 以内で最も単純なモデルを選ぶ(Lasso の lambda.1se と同じ発想)。
空間データの独立性 :都道府県データは隣接県が似る(空間自己相関)ため、 IID を仮定するランダム分割は厳密には楽観側に傾き得る。 地方ブロック単位で fold を切る GroupKFold は、 本文で紹介した GroupKFold(患者 ID)の空間版として機能する。
CV 信頼区間の理論 :Bates, Hastie & Tibshirani (2023, JASA ) は素朴な CV 標準誤差に基づく区間の被覆率の低さを示し、 nested CV に基づく補正区間を提案している。 小標本で CV スコアを主張の根拠にするときの必読文献。
🔗 関連ページ