Python基礎

【保存版】ハイパーパラメータ入門:Grid/Random/Optunaの実務チューニング完全ガイド

チューニングのゴールは「スコアの数字遊び」ではありません。意思決定に耐える安定した最適化を短時間で作ること。本記事は未経験〜初学者が週10時間×2週間で、GridSearchCV / RandomizedSearchCV / (準)ベイズ最適化(Optuna)の使い分け、探索空間の設計CVの選び方閾値・コスト設計再現性まで“実務の型”で身につけられるよう設計しました。
各セクションはコピペで動くコード付きです。

この記事で身に付く力

  • Grid / Random / Optuna の使い分け(いつ・どれを選ぶか)
  • 探索空間の設計(粗→精の2段階でムダを削る)
  • Pipeline×CV標準化(リーク防止&再現性)
  • 業務KPI連動(Top-k/閾値/コストで判断)

まず押さえる:チューニングで陥りがちな罠

よくある落とし穴

  • テストデータを何度も見る → “偶然勝ち”で本番が崩れる
  • 探索空間が広すぎ/狭すぎ → 計算浪費 or 改善余地を見逃す
  • 前処理がPipeline外 → CV内でデータリーク
  • 指標が業務と未接続 → AUCは上がったがTop-kコストが悪化

解決策は、Pipeline×CVを標準化し、探索空間の粒度を決め、テストは最後に1回の原則を徹底すること。

筆者(ふみと)は大手企業のデータサイエンティスト歴10年・案件100件超。現場では「精度+1pt」よりも、再現性と説明可能性が評価されます。ここでは、私が実務で使う「ベースライン→軽量探索→絞り込み→閾値/コスト調整」の型をそのまま共有します。

前提(環境)

Python 3.10+ / scikit-learn 1.x / pandas 2.x を想定。必要に応じて Optuna を使用します。

ハイパーパラメータ探索 “実務の型” 10ステップ

サンプル前準備(分類:乳がんデータ)

import pandas as pd
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split, StratifiedKFold
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.compose import ColumnTransformer
from sklearn.impute import SimpleImputer

Xy = load\_breast\_cancer(as\_frame=True)
X, y = Xy.data, Xy.target
X\_tr, X\_te, y\_tr, y\_te = train\_test\_split(X, y, test\_size=0.2, stratify=y, random\_state=42)

num\_cols = X.columns
prep = ColumnTransformer(\[
("num", Pipeline(\[("imp", SimpleImputer()), ("sc", StandardScaler())]), num\_cols)
])
cv = StratifiedKFold(n\_splits=5, shuffle=True, random\_state=42)

1) ベースライン:単純モデルで“地図”を描く

from sklearn.linear_model import LogisticRegression
from sklearn.metrics import roc_auc_score

logit = Pipeline(\[("prep", prep), ("clf", LogisticRegression(max\_iter=1000, n\_jobs=-1, random\_state=42))])
logit.fit(X\_tr, y\_tr)
auc = roc\_auc\_score(y\_te, logit.predict\_proba(X\_te)\[:,1])
print(f"AUC(base-logit)={auc:.3f}")

判断基準:まずベースライン ≥ 業務基準を確認。以降はAUCの平均±stdで安定性を見ます。

2) GridSearchCV:狭い範囲を丁寧に

from sklearn.model_selection import GridSearchCV
from sklearn.ensemble import RandomForestClassifier

rf = Pipeline(\[("prep", prep), ("clf", RandomForestClassifier(random\_state=42, n\_jobs=-1))])
param\_grid = {
"clf\_\_n\_estimators": \[200, 400, 800],
"clf\_\_max\_depth": \[None, 8, 12, 16],
"clf\_\_min\_samples\_split": \[2, 5, 10]
}
search = GridSearchCV(rf, param\_grid, scoring="roc\_auc", cv=cv, n\_jobs=-1)
search.fit(X\_tr, y\_tr)
print("best:", search.best\_params\_, "AUC=", search.best\_score\_)

活用指針:候補が各2〜4水準×2〜4変数の狭い空間を全列挙で“近傍確認”。

3) RandomizedSearchCV:広い空間を素早く

from sklearn.model_selection import RandomizedSearchCV
from scipy.stats import randint, uniform

param\_dist = {
"clf\_\_n\_estimators": randint(200, 1000),
"clf\_\_max\_depth": \[None] + list(range(4, 21)),
"clf\_\_min\_samples\_split": randint(2, 20),
"clf\_\_min\_samples\_leaf": randint(1, 10),
"clf\_\_max\_features": uniform(0.3, 0.7)  # 0.3〜1.0
}
rand = RandomizedSearchCV(rf, param\_distributions=param\_dist, n\_iter=40,
scoring="roc\_auc", cv=cv, random\_state=42, n\_jobs=-1)
rand.fit(X\_tr, y\_tr)
print("best:", rand.best\_params\_, "AUC=", rand.best\_score\_)

活用指針:20〜50試行で概形を把握→良さそうな範囲にGridで寄せるのが鉄板。

4) 2段階探索:Random →(絞り込み)→ Grid

一発で当てに行かず、粗→精の2段構えで計算を節約しつつ精度を上げます。rand.best_params_効いていそうな範囲を読み取り、近傍でGrid再探索。

5) 閾値・コスト設計と連動(Top-k運用)

from sklearn.metrics import precision_recall_curve, confusion_matrix
import numpy as np

best = rand.best\_estimator\_.fit(X\_tr, y\_tr)
proba = best.predict\_proba(X\_te)\[:,1]

# 例:上位5%のみ営業(架電枠)

k = int(len(proba) \* 0.05)
idx = np.argsort(-proba)\[:k]
recall\_at\_k = y\_te.iloc\[idx].mean()  # Positive率
print("Recall\@5%:", float(recall\_at\_k))

判断基準:AUCが上がってもTop-kが下がるなら業務的には改悪。評価設計と同時に確認します。

6) 再現性:random_state・バージョン固定・記録

乱数はrandom_state固定、sklearn/pandas/numpyrequirements.txtで固定。実験ログはJSON/CSVへ。

import json, time
rec = {"when": time.strftime("%Y-%m-%d %H:%M"),
       "best_params": rand.best_params_,
       "cv_auc": float(rand.best_score_)}
with open("search_record.json", "w", encoding="utf-8") as f:
    json.dump(rec, f, ensure_ascii=False, indent=2)

7) (準)ベイズ最適化:Optunaで“少試行で良い点”を探す(任意)

第三者ライブラリが使える環境ならOptunaがおすすめ。少ない試行で有望領域を探索できます。

# pip install optuna
import optuna
from sklearn.metrics import roc_auc_score
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score

rf0 = Pipeline(\[("prep", prep), ("clf", RandomForestClassifier(random\_state=42, n\_jobs=-1))])

def objective(trial: optuna.Trial) -> float:
params = {
"clf\_\_n\_estimators": trial.suggest\_int("clf\_\_n\_estimators", 200, 1000),
"clf\_\_max\_depth": trial.suggest\_int("clf\_\_max\_depth", 4, 20),
"clf\_\_min\_samples\_split": trial.suggest\_int("clf\_\_min\_samples\_split", 2, 20),
"clf\_\_min\_samples\_leaf": trial.suggest\_int("clf\_\_min\_samples\_leaf", 1, 10),
"clf\_\_max\_features": trial.suggest\_float("clf\_\_max\_features", 0.3, 1.0)
}
model = rf0.set\_params(\*\*params)
scores = cross\_val\_score(model, X\_tr, y\_tr, scoring="roc\_auc", cv=cv, n\_jobs=-1)
return scores.mean()

study = optuna.create\_study(direction="maximize")
study.optimize(objective, n\_trials=40)
print("best:", study.best\_params, "AUC=", study.best\_value)

使いどころ:探索コストが重い/空間が広いとき。最後はGridで微調整も有効。

8) 速度最適化:計算を“賢く減らす”

時短のコツ

  • 特徴量を減らす(相関/重要度で間引き)
  • CV分割数を段階調整(3→5→10)
  • n_jobs=-1で並列化(CPU負荷に注意)
  • 早めの集約(pandasで行削減/サンプリング)

9) 時系列タスク:TimeSeriesSplit×固定ウィンドウ

from sklearn.model_selection import TimeSeriesSplit, GridSearchCV
from sklearn.linear_model import Ridge

cv\_ts = TimeSeriesSplit(n\_splits=5)
reg = Pipeline(\[("prep", prep), ("reg", Ridge())])
param\_grid = {"reg\_\_alpha": \[0.1, 0.3, 1.0, 3.0, 10.0]}
search = GridSearchCV(reg, param\_grid, scoring="neg\_mean\_absolute\_error", cv=cv\_ts, n\_jobs=-1)
search.fit(X\_tr, y\_tr)
print("best:", search.best\_params\_, "MAE=", -search.best\_score\_)

注意:時系列は未来を覗かないCVのみ。データリークに最大注意。

10) 最終評価:テストは“一度だけ”+閾値/コストを添える

from sklearn.metrics import roc_auc_score, confusion_matrix, roc_curve

best = search.best\_estimator\_.fit(X\_tr, y\_tr)
proba = best.predict\_proba(X\_te)\[:,1]
print("TEST AUC:", roc\_auc\_score(y\_te, proba))

# 例:Youdenで閾値決定

fpr, tpr, thr = roc\_curve(y\_te, proba)
thr\_opt = thr\[(tpr - fpr).argmax()]
cm = confusion\_matrix(y\_te, (proba >= thr\_opt).astype(int))
print(cm, "threshold=", float(thr\_opt))

原則:テストは最後に一度だけ開封。混同行列/Top-k/コストを添えて報告します。

目的別:最短ルートの決め方

  • 転職準備Random → Grid → 閾値/Top-k → CI安定×説明可能を重視
  • 副業(納品)少試行で準最適(Random/Optuna 20〜40試行)→ Excel/レポート出力まで
  • 在宅(短時間×高頻度):小さく毎日(n_iter=10×数日)→ベストをメモして翌日に引き継ぐ

ミニプロジェクト(提出推奨)

「乳がんデータでCV AUC ≥ 0.995を目安に、Random → Gridの2段階探索を実施。Top-k/閾値/コストを添えてテストは一度だけ報告。」

  1. ベースライン(ロジスティック)→ RFでRandom 40試行
  2. 有望範囲でGrid再探索→ CV平均±stdをで提示
  3. Top-5%のRecall/Precision、Youden閾値コスト最小閾値を比較
  4. テストは最後に1回:混同行列・閾値・コストを明記
  5. requirements.txtsearch_record.json再現性を担保

レビュー観点チェックリスト(コピペ可)

  • [ ] Pipeline×CVでリークがない
  • [ ] テストを汚していない(最終1回)
  • [ ] 探索空間が根拠を持って設計されている
  • [ ] Top-k/閾値/コストを併記し、業務接続している
  • [ ] 記録(JSON/README/requirements)が残っている

付録A:主要モデルの“当たり”レンジ(目安)

  • LogisticRegressionC ∈ [0.1, 10]penalty={l2}class_weight={None,'balanced'}
  • RandomForestn_estimators ∈ [200, 1000]max_depth ∈ {None, 6〜20}min_samples_split ∈ [2, 10]max_features ∈ [0.3, 1.0]
  • GradientBoosting/HistGBlearning_rate ∈ [0.01, 0.2]max_depth ∈ [3, 12]max_leaf_nodes ∈ [15, 63]n_estimators ∈ [100, 1000]
  • SVC(RBF)C ∈ [0.1, 100]gamma ∈ [1e-4, 1]

付録B:探索ログの自動保存(CSV)

import pandas as pd
cv_results = pd.DataFrame(search.cv_results_)
cols = ["mean_test_score","std_test_score","params"]
cv_results[cols].to_csv("grid_results.csv", index=False)

FAQ:よくある質問

Q1. 何試行くらい回せばいい?
A. まずRandom 20〜40で地図を作り、Gridで近傍を詰める。重いならOptuna 20〜40も有効。

Q2. 最高スコアが安定しません
A. CV分割random_stateを固定。平均±stdで比較し、テスト1回を厳守。

Q3. AUCは上がったが業務KPIが悪化
A. Top-k/閾値/コストを併記して運用観点で再評価。モデルを変えず閾値最適化で改善する場合も。

Q4. 学習が遅い
A. 特徴量削減、n_jobs=-1、サンプリング、n_iterを段階的に増やす。早めの集約も効果的。

この記事から次に読むべきもの

モデル評価
【保存版】モデル評価:指標の選び方・交差検証・閾値最適化・ビジネス接続を“実務の型”で解説

精度が上がらない原因の多くは「評価設計の誤り」にあります。評価とは「何点取れたか」ではなく、意思決定に耐えるかを測る営み。この記事では、回帰/分類/ランキングの指標の選び方、交差検証の正しい使い分け、 ...

機械学習
【保存版】scikit-learn基礎:回帰・分類・前処理・パイプライン・交差検証を“実務の型”で習得

機械学習で迷子になる最大の理由は、前処理→学習→評価→改善の順番が曖昧なまま個々のアルゴリズムに飛びつくこと。本記事は、未経験〜初学者が週10時間×2〜3週で到達できるscikit-learnの最短ル ...

スキルアップ
【実務で差がつく】pandas実践:欠損処理・結合・ウィンドウ関数・時系列・品質保証まで“読みやすく速い”型を習得

リード(結論)基礎を終えたら次は実務の現場で頻出する処理を“型”で覚える段階です。本記事は、pandas 2.x を前提に、欠損・外れ値・結合・ウィンドウ関数・時系列・カテゴリ処理・集計の自動化・大規 ...

ポートフォリオ
【保存版】データ職のポートフォリオ完全ガイド|再現性・評価・LTまで

ポートフォリオって「作ったものの置き場」でしょ? いいえ。採用側が見たいのは「意思決定に効いた証拠」と「再現性」です。 本ガイドは、未経験〜初学者が週10時間×4〜6週で、テーマ選定→要件定義→データ ...

最近のコメント

    • この記事を書いた人
    • 最新記事

    ふみと

    このブログでは、データサイエンティストとして市場価値を上げる方法を独自にまとめて発信しています。

    【プロフィール】
    ・大手企業データサイエンティスト/マーケティングサイエンティスト(10年、年収900万円台)/案件100件以上
    ・資格:JDLA E資格(日本ディープラーニング協会主催)/JDLA Community(CDLE会員)/Advanced Marketer/ビジネス統計スペシャリスト/統計検定2級/TOEIC 805
    ・スキル:Python/Tableau/SQL/機械学習/Deep Learning/RPA

    -Python基礎