
チューニングのゴールは「スコアの数字遊び」ではありません。意思決定に耐える安定した最適化を短時間で作ること。本記事は未経験〜初学者が週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/numpy
はrequirements.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/閾値/コストを添えてテストは一度だけ報告。」
- ベースライン(ロジスティック)→ 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週で、テーマ選定→要件定義→データ ...
最近のコメント