
チューニングのゴールは「スコアの数字遊び」ではありません。
意思決定に耐える安定した最適化を短時間で作ること。
本記事は未経験〜初学者が週10時間×2週間で、GridSearchCV / RandomizedSearchCV / (準)ベイズ最適化(Optuna)の使い分け、探索空間の設計、CVの選び方、閾値・コスト設計、再現性まで“実務の型”で身につけられるよう設計しました。
各セクションはコピペで動くコード付きです。
この記事で身に付く力
- Grid / Random / Optuna の使い分け(いつ・どれを選ぶか)
- 探索空間の設計(粗→精の2段階でムダを削る)
- Pipeline×CV標準化(リーク防止&再現性)
- 業務KPI連動(Top-k/閾値/コストで判断)
まず押さえる:チューニングで陥りがちな罠
よくある落とし穴
- テストデータを何度も見る → “偶然勝ち”で本番が崩れる
- 探索空間が広すぎ/狭すぎ → 計算浪費 or 改善余地を見逃す
- 前処理がPipeline外 → CV内でデータリーク
- 指標が業務と未接続 → AUCは上がったがTop-kやコストが悪化
解決策は、Pipeline×CVを標準化し、探索空間の粒度を決め、テストは最後に1回の原則を徹底すること。
初心者ポイント:テストデータは「最終試験」。
学習やチューニングの途中で覗くと、試験対策しかできない“偶然に強いモデル”が出来上がります。
前提(環境)
Python 3.10+ / scikit-learn 1.x / pandas 2.x を想定。必要に応じて Optuna を使用します。
ハイパーパラメータ探索 “実務の型” 10ステップ
サンプル前準備(分類:乳がんデータ)
このコードでやること:
- データを読み込み、学習/テストに分割
- 数値特徴量の欠損補完+標準化をパイプライン化
- 層化K-Foldで分割(クラス偏りに強い)
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}")見るべき指標:まず ベースライン ≥ 事業の要求水準 を確認。以降は CV平均±標準偏差 で安定性をチェック。
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
目的:一発当てではなく、粗→精 の二段階で計算を節約しつつ精度を上げる。
rand.best_params_を見て 効いていそうな範囲 を読み取る- その近傍で Grid再探索 して微調整
5) 閾値・コスト設計と連動(Top-k運用)
目的:モデルの良し悪しを業務の意思決定で評価する(例:上位5%だけ営業)
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・バージョン固定・記録
目的:同じ条件なら必ず再現できるようにする。
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)乱数:random_state固定
バージョン:requirements.txt で sklearn/pandas/numpy を固定
実験ログ:JSON/CSVで残す(後述の付録B)
7) (準)ベイズ最適化: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)コツ:Optunaで範囲を掴んだら、最後にGridで微調整も有効。
8) 速度最適化:計算を“賢く減らす”
時短のコツ
- 特徴量を減らす(相関/重要度で間引き)
- CV分割数を段階調整(3→5→10)
n_jobs=-1で並列化(CPU負荷に注意)- 早めの集約(pandasで行削減/サンプリング)
9) 時系列タスク:TimeSeriesSplit×固定ウィンドウ
目的:未来を覗かないCVで、リークを防ぐ。
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\_)注意:時系列は固定ウィンドウ or Expanding。シャッフル禁止、前処理も各Fold内で実行。
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/閾値/コストを添えてテストは一度だけ報告。」
- ベースライン(ロジスティック)→ RFでRandom 40試行
- 有望範囲でGrid再探索→ CV平均±stdを表で提示
- Top-5%のRecall/Precision、Youden閾値とコスト最小閾値を比較
- テストは最後に1回:混同行列・閾値・コストを明記
requirements.txtとsearch_record.jsonで再現性を担保
レビュー観点チェックリスト(コピペ可)
- [ ] Pipeline×CVでリークがない
- [ ] テストを汚していない(最終1回)
- [ ] 探索空間が根拠を持って設計されている
- [ ] Top-k/閾値/コストを併記し、業務接続している
- [ ] 記録(JSON/README/requirements)が残っている
付録A:主要モデルの“当たり”レンジ(目安)
- LogisticRegression:
C ∈ [0.1, 10]、penalty={l2}、class_weight={None,'balanced'} - RandomForest:
n_estimators ∈ [200, 1000]、max_depth ∈ {None, 6〜20}、min_samples_split ∈ [2, 10]、max_features ∈ [0.3, 1.0] - GradientBoosting/HistGB:
learning_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週で、テーマ選定→要件定義→データ ...
最近のコメント