本ページは グリッドサーチ(Grid Search)を 12 のセクションで多角的に解説します。 上のチップは検索・関連語の手がかりです。 以下のリンクで各セクションに直接ジャンプできます:
🍰 まずはやさしく
設定の組み合わせをすべて試す方法です。
一番良い設定を見つけるために使います。
スマホのアプリ設定を全部試すようなものです。
この章ではグリッドサーチの結論を読みます。
グリッドサーチ(Grid Search):ハイパーパラメータの組合せを網羅的に試す手法
GridSearchCV が定番。 内部で交差検証と組み合わせて評価する。各パラメータの候補数の積。 4変数×5候補で 625 通り → 計算コスト爆発に注意。🍰 まずはやさしく
人間が決める設定値をさがす手法です。
モデルの性能を最大限に引き出します。
部活の練習メニューの組み合わせを選ぶ例です。
この章では使う場面や注意点を読みます。
α、 ランダムフォレストの n_estimators、 SVM の C や gamma など。 これらを「総当たり」で試して最良を選ぶのがグリッドサーチ。 論文の 「best parameters were selected via grid search with 5-fold CV」 という記述が出てきたら、 これのことです。グリッドサーチは ハイパーパラメータの候補を格子状に並べ、 全組合せを試して最良を選ぶ手法。 シンプルで実装容易だが、 候補数が指数的に増えるため 3–4 ハイパラまでが現実的。 それ以上は Random Search や Bayesian Optimization に切り替える。
🍰 まずはやさしく
地図のマス目を全部歩くようなイメージです。
どの設定が正解かを確認するために使います。
買い物で色とサイズを全部試すようなものです。
この章では具体的な動き方を読みます。
Ridge回帰の正則化強度 α を選びたいとします。 候補:[0.01, 0.1, 1, 10, 100]。
α=0.01 で学習 → 5-fold CV で平均 RMSE を測定α=0.1 で学習 → 平均 RMSE を測定2 パラメータなら格子状(grid)に組合せが並ぶので「グリッドサーチ」。
| α \ C | 0.01 | 0.1 | 1 | 10 |
|---|---|---|---|---|
| linear | 0.52 | 0.48 | 0.41 | 0.45 |
| rbf | 0.55 | 0.50 | 0.43 | 0.47 |
上記の例なら (C=1, kernel=linear) で CV-RMSE = 0.41 が最小なのでこれを採用。
Grid Search は「地図上の格子点を全部歩いてみる」イメージ。 確実だが時間がかかる。 一方、 ハイパラの一部だけが性能に影響することが多い(Bergstra & Bengio 2012)ため、 Random Search の方が同じ計算量で良い解に到達することが知られる。 とはいえ Grid は「結果が再現的・説明しやすい」ため、 候補が少ない時の標準。
🍰 まずはやさしく
設定の組み合わせを数式で表したものです。
正解を計算で導き出すために使います。
テストの点数を最大にする組み合わせ選びです。
この章では計算方法や定義を読みます。
グリッドサーチを数式 / 形式定義で表す:
ハイパーパラメータ集合 $\Lambda$ の中で、 交差検証損失を最小にする組合せ $\hat{\boldsymbol{\lambda}}$ を選ぶ。 Grid は $\Lambda$ を直積(格子)で構成する。
ハイパラ探索の総評価回数は次式で表せる。
数式を言葉で読み解く:
例:5 ハイパラ × 各 5 候補 × 5-fold CV = $5 \times 5^5 = 15{,}625$ 回学習。 1 回 1 分なら 10 日以上。 これが「grid search の指数的爆発」の正体。
Random Search では $N_{\text{eval}}$ を 固定(例 100 回)し、 各ハイパラを独立に確率分布から抽出。 重要なハイパラの粒度を細かく取れる利点がある。
上の数式に出てきた記号を 1 つずつ解説します。 数式が出てくる試験問題(統計検定・G 検定・基本情報)では、 各記号の意味を答えられるかが分岐点:
| 記号 | 意味 |
|---|---|
| $\boldsymbol{\lambda}$ | ハイパーパラメータベクトル |
| $\Lambda$ | 候補集合(格子) |
| CV-Loss | 交差検証損失 |
| $\hat{\boldsymbol{\lambda}}$ | 選ばれた最適ハイパラ |
「グリッドサーチ」を入門レベルで習得した次に進むべき発展テーマ:
基本概念を 確率論・情報理論・最適化理論の観点で再定式化すると、 隣接する手法との理論的な関係が見えてきます。 たとえば 正則化は事前分布の最大事後推定と等価、 クロスエントロピー損失は 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-2026(2023 年断面・47 都道府県)で Ridge 回帰の最適な α を選ぶ。 X=年齢 3 区分人口(A1301/A1302/A1303)を StandardScaler で標準化し、 y=出生数(A4101)。 下の実装ブロックと同一設定での実測値:
| α | 5-fold CV の RMSE 平均(人) |
|---|---|
| 0.01 | 991.6 ← 最小 |
| 0.1 | 1128.5 |
| 1.0 | 1373.7 |
| 10 | 2053.9 |
| 100 | 7642.4 |
α=0.01 が最良 → モデル最終化はこの α で全訓練データを使って再学習し、 ホールドアウトしておいたテストで最終評価。 なお最良値がグリッドの端に来ているため、 本来はさらに小さい α まで範囲を広げて確認するのが定石(この課題は特徴量 3 個 × 47 行で過学習が弱く、 正則化を弱めるほど OLS の性能に漸近する)。
SSDSE-B-2026 の 「総人口 → 出生数」回帰で、 ランダムフォレストの 3 ハイパラ(n_estimators / max_depth / min_samples_leaf)を Grid Search し、 最良 R² を出すパラメータを発見する。 2023 年断面(47 行)・random_state=42 での実測は best params: max_depth=7, min_samples_leaf=1, n_estimators=50、 CV R²=0.8041。
使用データ:SSDSE-B-2026.csv(独立行政法人 統計センター提供、 47 都道府県 × 100 超の社会経済指標)。 出典
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | import pandas as pd from sklearn.ensemble import RandomForestRegressor from sklearn.model_selection import GridSearchCV 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', 'A1303', 'F3101']].fillna(0).values y = df['A4101'].values # 教材では計算時間の都合で候補を絞っています。実務ではもっと広い範囲を探索します。 # (本来は n_estimators=[50,100,200] × max_depth=[3,5,7,None] × min_samples_leaf=[1,2,4] など) param_grid = { 'n_estimators': [30, 60], 'max_depth': [3, 5, None], 'min_samples_leaf': [1, 4], } gs = GridSearchCV(RandomForestRegressor(random_state=42), param_grid, cv=5, scoring='r2', n_jobs=-1) gs.fit(X, y) print(f'Best params: {gs.best_params_}') print(f'Best CV R² : {gs.best_score_:.4f}') |
▲ 上記コードはそのまま実行可能。 CP932 エンコーディング・skiprows=1(英語ヘッダ行をスキップ)・列名の英数字コード(A1101 = 総人口 など)に注意。
このコードでやること:SSDSE-B-2026 の 2023 年断面(47 行)で、 Ridge 回帰の正則化強度 alpha を 5 候補で grid search する。 人口系の特徴量は百万オーダーなので、 StandardScaler を Pipeline に組み込んで標準化してから探索する(標準化しないと alpha がほぼ効かず、 どの候補でも R² が同値になってしまう)。 最良値を 5-fold CV で選び、 出力結果を確認する。
📥 入力データ:SSDSE-B-2026、 X = 15歳未満人口・15-64歳人口・65歳以上人口、 y = 出生数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | import pandas as pd from sklearn.linear_model import Ridge from sklearn.model_selection import GridSearchCV from sklearn.pipeline import make_pipeline from sklearn.preprocessing import StandardScaler 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[['A1301', 'A1302', 'A1303']] y = df['A4101'] pipe = make_pipeline(StandardScaler(), Ridge()) # 標準化してから Ridge param_grid = {'ridge__alpha': [0.01, 0.1, 1.0, 10.0, 100.0]} gs = GridSearchCV(pipe, param_grid, cv=5, scoring='r2') gs.fit(X, y) print("最良 alpha:", gs.best_params_) print(f"最良 CV R²: {gs.best_score_:.4f}") print("全 alpha のスコア:") for a, s in zip(param_grid['ridge__alpha'], gs.cv_results_['mean_test_score']): print(f" alpha={a:7.2f} → R²={s:.4f}") |
📤 実行例:
💬 結果の読み方:標準化したことで alpha がはっきり効く。 alpha が大きいほど正則化が強まり係数が縮小され(alpha=0.01 で約 15,244 あった第 1 係数は alpha=100 で約 3,340 まで縮む)、 この課題では R² が単調に低下(alpha=100 で 0.41 まで悪化)。 最良は最小候補 0.01 でグリッドの境界にあるため、 定石どおり範囲を広げて確認する — 実測では alpha=0.001 でも約 0.992 で頭打ちし、 OLS(正則化なし、 CV R²≈0.9923)に漸近する。 特徴量 3 個 × 47 行では過学習が弱く、 正則化の恩恵が出にくい設定だと分かる。 候補値の 対数スケールでの探索が肝心(線形に並べると重要域を見逃す)。
3 ハイパーパラメータ × 5 CV の組合せ数と計算量を計算する。
| パラ | 候補数 |
|---|---|
| C | 5 |
| γ | 4 |
| kernel | 3 |
1 2 3 4 5 6 7 8 | import numpy as np params = np.array([5, 4, 3]) grid = np.prod(params) cv = 5 fits = grid * cv print(f"グリッド組: {grid}") print(f"総フィット: {fits}") print(f"所要時間 (10s/fit): {fits*10/60:.0f} 分") |
💬 手計算 (Step 2) 60 組 / 50 分と Python 出力が完全一致。
正則化の強さ alpha のような「桁で効く」パラメータは、等間隔ではなく対数間隔で刻みます。
理由は実際に走らせると一目で分かります。SSDSE-B-2026(2023 年・47 都道府県)で、
総人口・15 歳未満人口・65 歳以上人口・婚姻件数から出生数を当てる Ridge 回帰を組み、
5-fold 交差検証の R² を測った結果が次の表です。
| alpha(対数で 8 点) | CV R² | 読み取れること |
|---|---|---|
| 0.001 | +0.9954 | 0.001〜0.1 はほぼ差が無い。正則化が弱すぎて「実質かけていない」状態。 |
| 0.01 | +0.9950 | |
| 0.1 | +0.9909 | |
| 1 | +0.9848 | ここから少しずつ効き始める。 |
| 10 | +0.9800 | |
| 100 | +0.8313 | 急に崩れ始める。変化はこの桁で起きている。 |
| 1000 | +0.2305 | 係数が潰れ、ほぼ平均を答えるだけのモデルに。 |
| 10000 | +0.0129 |
同じ 8 点を 1 から 1000 まで等間隔(1, 143.7, 286.4, …, 1000)に取ると、こうなります。
| alpha | 1.0 | 143.7 | 286.4 | 429.1 | 571.9 | 714.6 | 857.3 | 1000.0 |
|---|---|---|---|---|---|---|---|---|
| CV R² | +0.985 | +0.752 | +0.560 | +0.440 | +0.361 | +0.305 | +0.263 | +0.231 |
💬 ここが要点:等間隔では 8 点のうち 7 点が「もう崩れきった領域」に落ちてしまい、
いちばん知りたい 0.001〜10 の範囲を 1 点(alpha=1.0)でしか見ていません。
対数間隔なら同じ 8 回の計算で 0.001 から 10000 まで 8 桁を均等に覆えます。
np.logspace(-3, 4, 8) と書くだけなので、桁で効くパラメータは必ず logspaceと覚えてください。
学習率・C・gamma・alpha はすべてこの仲間です。逆に max_depth や n_estimators のように
「1 ずつ増える」性質のものは等間隔(あるいは整数の候補列)で構いません。
グリッドサーチの学習回数は (候補の積)×(fold 数) です。パラメータを 1 つ足すと点数は足し算ではなく掛け算で増えます。 上と同じ 47 行のデータで RandomForest を 5-fold で回した実測が次の表です。
| 探索するパラメータ | 点数 | 学習回数 | 実測時間 |
|---|---|---|---|
| n_estimators(2 候補) | 2 | 10 | 0.1 秒 |
| + max_depth(3 候補) | 6 | 30 | 0.3 秒 |
| + min_samples_leaf(2 候補) | 12 | 60 | 0.7 秒 |
47 行なら 12 点でも 1 秒未満ですが、データが 1 万行になれば同じ 12 点で数分〜数十分になります。 パラメータを 5 個、各 5 候補にすると 5⁵ = 3,125 点 × 5 fold = 15,625 回の学習です。 ここが「グリッドサーチは丁寧だが高くつく」と言われる理由で、 ランダムサーチや逐次的な絞り込み(Successive Halving)が使われる動機になります。 まずは効きそうな 2 つに絞って粗く探し、当たりの周辺だけ細かく刻み直すのが、実務では一番早く済みます。
SSDSE-B-2026(47 都道府県・2023 年データ)を題材にした最小コード(雛形:X_train / y_train は準備済みとする。 Ridge 系は実際には StandardScaler を Pipeline に組み込んで標準化してから探索する — 実測例は上の 🧮 を参照):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | import pandas as pd # ── この抜粋だけで動くように、47 都道府県・最新年度を読み込む ── _d = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) _d = _d[_d['SSDSE-B-2026'] == 2023] from sklearn.model_selection import GridSearchCV, train_test_split from sklearn.linear_model import Ridge # ── この抜粋だけで動くように、学習用データを用意する ── _X = _d[['A1101', 'A1301', 'A1303', 'A9101']].astype(float).values _y = _d['A4101'].astype(float).values X_train, X_test, y_train, y_test = train_test_split(_X, _y, test_size=0.3, random_state=0) param_grid = {'alpha': [0.01, 0.1, 1, 10, 100]} gs = GridSearchCV(Ridge(), param_grid, cv=5, scoring='neg_root_mean_squared_error') gs.fit(X_train, y_train) print('Best:', gs.best_params_, '| CV RMSE:', -gs.best_score_) best_model = gs.best_estimator_ |
「グリッドサーチ」を扱う代表的なライブラリ別実装。 同じ目的でも書き方が違うため、 自分のプロジェクトの依存関係に合わせて選択する:
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()) |
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}') |
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() |
このコードでやること:同じ計算予算(20 回評価)で Grid Search と Random Search を比較する。 Random Search が小さい予算で広範囲を探索できることを確認。 2023 年断面(47 行)・seed 固定(random_state=0 / 42)の実測。
📥 入力データ:SSDSE-B-2026、 X = 人口関連 3 変数、 y = 出生数。
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 import numpy as np from sklearn.ensemble import RandomForestRegressor from sklearn.model_selection import GridSearchCV, RandomizedSearchCV from scipy.stats import randint 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', 'A4200']] y = df['A4101'] # 教材では計算時間の都合で候補を絞っています。実務ではもっと広い範囲を探索します。 # 木の本数の上限を 1000 → 100 に下げています(比較の趣旨は変わりません) # Grid: 5x4 = 20 評価 grid = {'n_estimators': [20, 40, 60, 80, 100], 'max_depth': [3, 5, 10, 20]} gs = GridSearchCV(RandomForestRegressor(random_state=0), grid, cv=3, scoring='r2', n_jobs=-1) gs.fit(X, y) # Random: 20 サンプル dist = {'n_estimators': randint(20, 101), 'max_depth': randint(3, 21)} rs = RandomizedSearchCV(RandomForestRegressor(random_state=0), dist, n_iter=20, cv=3, scoring='r2', random_state=42, n_jobs=-1) rs.fit(X, y) print(f"Grid 最良: {gs.best_params_} R²={gs.best_score_:.4f}") print(f"Random 最良: {rs.best_params_} R²={rs.best_score_:.4f}") |
📤 実行例:
💬 結果の読み方:Random Search は 連続値の細かい候補(n_estimators=81 のような格子に無い値)を試せる。 今回は Grid のほうがわずかに上(0.9207 vs 0.9188)だが、この差は fold 分割のゆらぎの範囲。 Grid は離散値で固定されるため、 最適値が候補に含まれていない可能性がある。 今回の差は僅少で fold 分割にも依存するが、 高次元(パラメータ 4+)では Random Search の優位が明確になる。
このコードでやること:Optuna の TPE(Tree-structured Parzen Estimator)で Ridge の alpha を 連続範囲から最適化する。 過去の評価結果を使って次の試行を賢く選ぶため、 同じ予算でより良い解が見つかる。 Ridge は StandardScaler+Pipeline で標準化し、 TPESampler(seed=42) で結果を再現可能にする(seed を固定しないと結果は試行ごとに変動する)。 2023 年断面(47 行)の実測。
📥 入力データ:SSDSE-B-2026、 X = 人口 3 変数、 y = 出生数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | import pandas as pd import optuna from sklearn.linear_model import Ridge from sklearn.model_selection import cross_val_score from sklearn.pipeline import make_pipeline from sklearn.preprocessing import StandardScaler 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', 'A4200']] y = df['A4101'] def objective(trial): alpha = trial.suggest_float('alpha', 1e-3, 1e3, log=True) model = make_pipeline(StandardScaler(), Ridge(alpha=alpha)) return cross_val_score(model, X, y, cv=5, scoring='r2').mean() sampler = optuna.samplers.TPESampler(seed=42) # seed 固定で再現可能に study = optuna.create_study(direction='maximize', sampler=sampler) study.optimize(objective, n_trials=30, show_progress_bar=False) print(f"最良 alpha: {study.best_params['alpha']:.4f}") print(f"最良 R²: {study.best_value:.4f}") |
📤 実行例:
💬 結果の読み方:Optuna は対数一様分布 [1e-3, 1e3] を 30 試行で探索し、 alpha≈0.001(探索範囲の下端)に収束、 CV R²=0.9901。 grid の離散候補に縛られず連続値を試せるのが TPE の利点だが、 この課題では正則化を弱めるほど良い(特徴量 3 個 × 47 行で過学習が弱く OLS に漸近する。 🧮 実値計算の結論と整合)ため、 最良が範囲の端に張り付いた。 端に来た場合は下限をさらに下げて確認するのが定石。 TPESampler(seed=42) を固定しているので上記の値は再現される(seed を外すと試行系列が変わり best alpha は毎回ぶれる)。
このコードでやること:scikit-learn の HalvingGridSearchCV で、 少量のリソースで多数の候補を評価 → 上位だけ残してリソースを増やす、 を繰り返す。 grid search より大幅に高速。
📥 入力データ:SSDSE-B-2026、 X = 人口 3 変数、 y = 出生数(A4101)。
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 | import pandas as pd from sklearn.experimental import enable_halving_search_cv # noqa from sklearn.model_selection import HalvingGridSearchCV from sklearn.ensemble import GradientBoostingRegressor 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', 'A4200']] y = df['A4101'] # 教材では計算時間の都合で候補を絞っています。実務ではもっと広い範囲を探索します。 # n_estimators の上限を 500 → 150 に下げています(候補数 64 は変えていません) param_grid = { 'n_estimators': [20, 40, 80, 150], 'max_depth': [2, 3, 5, 7], 'learning_rate': [0.01, 0.05, 0.1, 0.3] } hs = HalvingGridSearchCV( GradientBoostingRegressor(random_state=0), param_grid, cv=3, scoring='r2', factor=3, resource='n_samples', min_resources=15, random_state=42, n_jobs=-1 ) hs.fit(X, y) print(f"最良: {hs.best_params_}") print(f"最良 CV R²: {hs.best_score_:.4f}") print(f"各段の n_samples: {hs.n_resources_}") |
📤 実行例:
💬 結果の読み方:候補 64 個を全評価せず、 まず 15 サンプルで全 64 候補をスクリーニング → 上位 22 候補だけ 45 サンプルで再評価(n_candidates_=[64, 22])。 これで学習回数を大幅に節約できる。 ただし今回は 47 行しかなく 第 1 段の 15 サンプルが荒すぎるため、 通常の GridSearchCV の最良とは別の解(R²=0.9429)に落ち着いた。 Successive Halving は計算量削減と引き換えに、 低リソース段のノイズで真の最適候補を早期に切り落とすリスクがある——データが小さいほど min_resources を大きめに取るのが安全。 大規模データでこそ本領を発揮する。
| モデル | 主要ハイパラ | 推奨範囲 | スケール |
|---|---|---|---|
| Ridge / Lasso | alpha | [1e-3, 1e3] | log |
| SVM | C, gamma | [1e-3, 1e3] | log |
| Random Forest | n_estimators, max_depth, min_samples_leaf | [50, 1000] / [3, 20] / [1, 20] | linear |
| XGBoost / LightGBM | learning_rate, max_depth, n_estimators, subsample | [0.01, 0.3] / [3, 10] / [100, 2000] / [0.5, 1.0] | log / linear |
| Neural Net | lr, batch_size, dropout, weight_decay | [1e-5, 1e-1] / [16, 512] / [0, 0.5] / [1e-6, 1e-2] | log |
| k-NN | n_neighbors | [1, 50] | linear |
注:「推奨範囲」はあくまで出発点。 業務データやモデルサイズに応じて狭めるか広げる。
このコードでやること:GridSearchCV.cv_results_ を DataFrame に変換し、 各ハイパラ値ごとのスコアを表として確認する。 ハイパラ感度(どのパラメータが効くか)を読み取れる。
📥 入力データ:SSDSE-B-2026、 X = 人口関連、 y = 出生数。
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 | import pandas as pd from sklearn.linear_model import ElasticNet from sklearn.model_selection import GridSearchCV from sklearn.pipeline import make_pipeline from sklearn.preprocessing import StandardScaler 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', 'A4200']] y = df['A4101'] pipe = make_pipeline(StandardScaler(), ElasticNet(max_iter=5000)) grid = { 'elasticnet__alpha': [0.01, 0.1, 1.0], 'elasticnet__l1_ratio': [0.1, 0.5, 0.9] } gs = GridSearchCV(pipe, grid, cv=5, scoring='r2') gs.fit(X, y) res = pd.DataFrame(gs.cv_results_)[[ 'param_elasticnet__alpha', 'param_elasticnet__l1_ratio', 'mean_test_score', 'std_test_score']] pivot = res.pivot(index='param_elasticnet__alpha', columns='param_elasticnet__l1_ratio', values='mean_test_score') print(pivot.round(4)) |
📤 実行例:
💬 結果の読み方:標準化したことで 両軸ともスコアが明確に動く。 alpha を上げるほど正則化が強まり性能が落ち(0.01 行 → 1.0 行で最大 0.988→0.835 まで低下)、 l1_ratio を上げる(L1 寄り=スパース化)ほど改善する(特に alpha=1.0 で 0.835→0.940 と大きく効く)。 最良セルは alpha=0.01・l1_ratio=0.9 の 0.9883。 → 次の探索では alpha を小さい側([0.001, 0.1] など)に絞り、 l1_ratio は高め(0.9 近傍)を重点的に刻む戦略が立てられる。 cv_results_ の ヒートマップ可視化は、 こうした軸ごとの感度を読み取る出発点として最強。
neg_ プレフィックスを使う。「グリッドサーチ」を実務・試験で扱うときに頻発する典型的なミスです。 各項目を 1 度読んでおけば 9 割の事故が防げます:
n_jobs=-1 と並列度を確認。| 年 | 出来事 |
|---|---|
| 1960s | 最適実験計画(Box & Wilson) |
| 1998 | SMBO(Sequential Model-Based Optimization)の理論 |
| 2011 | Hyperopt(TPE)公開(Bergstra ら) |
| 2012 | Bergstra & Bengio「Random Search が Grid より効率的」を実証 |
| 2013 | scikit-learn 0.14 で GridSearchCV 標準化 |
| 2017 | Hyperband 提案(Li ら) |
| 2019 | Optuna 公開(Preferred Networks) |
| 2020 | scikit-learn 0.24 で HalvingGridSearchCV 追加 |
Q1. Grid と Random はいつ使い分ける?
A. パラメータ数 ≤ 3 なら Grid、 4 以上なら Random または BO。 Bergstra & Bengio の論文を読むと納得しやすい。
Q2. Optuna は本番運用にも使える?
A. Yes。 SQLite/PostgreSQL バックエンドで study を永続化でき、 分散実行も対応。 LightGBM/XGBoost との統合 API もある。
Q3. 並列化したい場合は?
A. GridSearchCV は n_jobs=-1 で全 CPU 使用可。 Optuna は n_jobs 引数 + RDB 永続化で分散実行可能。
Q4. 探索範囲はどう決める?
A. 公式ドキュメント・論文・Kaggle 解法を参考に。 alpha, learning_rate は対数スケール、 max_depth は線形でよい。
Q5. 結果を信頼する条件は?
A. (1) CV スコアの fold 間ばらつきが小さい、 (2) ベスト候補が grid の境界にない、 (3) 独立 test set で検証済み、 の 3 点が揃ったとき。
グリッドサーチを中心に、ハイパーパラメータ探索の代替手法(ランダムサーチ・ベイズ最適化・Hyperband)、評価フレーム(交差検証・Nested CV)、scikit-learn の実装(GridSearchCV)、応用先(SVM のカーネル C/γ・Random Forest の n_estimators/max_depth・XGBoost の learning_rate/depth)を放射状に配置した。次元の呪い(高次元では指数的に組合せ爆発)と、ベイズ最適化への移行ポイントを矢印で示している。
グリッドサーチを中心とする概念マップは、 「ハイパーパラメータ最適化」「交差検証」「探索ログの分析」「Test set を絶対に見ない」の 4 つの隣接トピックで構成される。 scikit-learn の GridSearchCV は cv (交差検証分割数) と param_grid を同時に与えると、 (パラメータ組合せ数) × (cv 分割数) 回のモデル学習を自動実行し、 最良パラメータと CV スコアを返す。 探索空間が大きいときは RandomizedSearchCV (Bergstra-Bengio 2012)、 評価コストが高いときは BayesSearchCV (skopt) や Optuna の TPE で代替する流れが定番。
「グリッドサーチ」は単独で完結する手法ではなく、 隣接領域と連携することで真価を発揮する。
上流のハイパーパラメータ範囲設定と評価指標選択で探索空間を定義し、 並列のランダムサーチ・ベイズ最適化・Hyperband と探索効率を比較し、 下流のクロスバリデーションで最適パラメータを評価する。 グリッドサーチは全探索の素朴版で、 次元の呪いに早く到達するため小規模問題か他手法のベースラインとして使う。
「グリッドサーチ」を実際の課題に当てはめるとき、 状況別に何を選ぶかを 3 段階で判定する。
グリッドサーチは「全探索による安心感」と引き換えに次元の呪いを抱える。 4 次元以上は急速に非現実的になるため、 ランダムサーチを第一選択、 ベイズ最適化 (Optuna・Hyperopt) を最適化として用いる。