
機械学習で迷子になる最大の理由は、前処理→学習→評価→改善の順番が曖昧なまま個々のアルゴリズムに飛びつくこと。本記事は、未経験〜初学者が週10時間×2〜3週で到達できるscikit-learnの最短ルートを、実務の型に沿って解説します。
回帰/分類、前処理、パイプライン、交差検証、ハイパラ探索、ベースラインづくりまで、コピペで動くNotebook用コードとレビュー観点チェックリスト付きで一気通貫。
この記事で身に付く力
- 再現性の高いワークフロー(パイプライン+交差検証+ベースライン)
- 回帰・分類の基本モデル実装力(Ridge/Logistic/RF)
- 評価設計の勘所(RMSE/AUC、閾値調整、CVの使い分け)
- 現場ですぐ使える雛形(ディレクトリ構成、保存・再現)
まず“事故の元”を断つ:よくあるつまずき
学習がうまくいかないとき、アルゴリズム選びよりも設計ミスが原因なことがほとんど。ふみとが現場で見てきた“あるある”を、先に潰しておきましょう。
- データリーク:標準化やエンコードをテストにまで“見せて”しまう。
- 評価設計の曖昧さ:どの指標で何点を合格とするかが決まっていない。
- 前処理と学習の分離:Notebook上でバラけて再現できない。
- ベースライン不在:改善幅の妥当性が判断できない。
解決策は、ベースライン → パイプライン → 交差検証を“標準装備”にすること。これだけで事故らず・盛らず・再現できる状態に近づきます。
10年で残った“現場の型”
筆者(ふみと)は大手企業でデータ/マーケ案件を100件以上担当。評価では1〜2ptの精度差より、再現性・説明可能性・意思決定との接続が重視されました。そこで定着したルーチンが「ベースライン→パイプライン→交差検証→改善」です。以降、そのままポートフォリオに持ち込める手順で解説します。
この記事で使う環境
Python 3.10+ / pandas 2.x / scikit-learn 1.x を想定(pip install scikit-learn
済み)。
- 回帰サンプル:
fetch_california_housing
(住宅価格) - 分類サンプル:
load_breast_cancer
(乳がん診断)
Step1:まずは“ベースライン”を作る(回帰/分類)
import numpy as np, pandas as pd
from sklearn.datasets import fetch_california_housing, load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.dummy import DummyRegressor, DummyClassifier
from sklearn.metrics import mean_squared_error, accuracy_score
# 回帰データ
d = fetch\_california\_housing(as\_frame=True)
Xr, yr = d.data, d.target
Xr\_train, Xr\_test, yr\_train, yr\_test = train\_test\_split(Xr, yr, test\_size=0.2, random\_state=42)
base\_r = DummyRegressor(strategy="median").fit(Xr\_train, yr\_train)
rmse\_base = mean\_squared\_error(yr\_test, base\_r.predict(Xr\_test), squared=False)
print(f"回帰ベースライン RMSE={rmse\_base:.3f}")
# 分類データ
d2 = load\_breast\_cancer(as\_frame=True)
Xc, yc = d2.data, d2.target
Xc\_train, Xc\_test, yc\_train, yc\_test = train\_test\_split(Xc, yc, test\_size=0.2, stratify=yc, random\_state=42)
base\_c = DummyClassifier(strategy="most\_frequent").fit(Xc\_train, yc\_train)
acc\_base = accuracy\_score(yc\_test, base\_c.predict(Xc\_test))
print(f"分類ベースライン ACC={acc\_base:.3f}")
判断基準:ベースライン未満のモデルは採用不可。改善幅の妥当性を数値で把握します。
Step2:前処理は必ず“パイプライン”に格納
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
num\_cols = Xr.columns # 例:数値のみ
numeric\_proc = Pipeline(\[
("impute", SimpleImputer(strategy="median")),
("scale", StandardScaler())
])
# 例:カテゴリ列がある場合
# cat\_cols = \["gender", "area"]
# categorical\_proc = Pipeline(\[
# ("impute", SimpleImputer(strategy="most\_frequent")),
# ("onehot", OneHotEncoder(handle\_unknown="ignore", sparse\_output=False))
# ])
preprocess = ColumnTransformer(\[
("num", numeric\_proc, num\_cols),
\# ("cat", categorical\_proc, cat\_cols)
], remainder="drop")
判断基準:前処理は必ずPipeline/ColumnTransformerに格納し、学習データのみでfit。
Step3:回帰モデル(Ridge/ランダムフォレスト)
from sklearn.linear_model import Ridge
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import r2_score, mean_squared_error
from sklearn.pipeline import Pipeline
ridge = Pipeline(\[
("prep", preprocess),
("model", Ridge(alpha=1.0, random\_state=42))
])
rf = Pipeline(\[
("prep", preprocess),
("model", RandomForestRegressor(n\_estimators=300, random\_state=42, n\_jobs=-1))
])
ridge.fit(Xr\_train, yr\_train)
rf.fit(Xr\_train, yr\_train)
for name, mdl in {"Ridge": ridge, "RF": rf}.items():
pred = mdl.predict(Xr\_test)
print(name, "RMSE=", mean\_squared\_error(yr\_test, pred, squared=False), "R2=", r2\_score(yr\_test, pred))
判断基準:単純モデル→非線形モデルの順で比較。過学習はtrain/valの乖離で検知。
Step4:分類モデル(ロジスティック回帰/ランダムフォレスト)
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import roc_auc_score, classification_report, accuracy_score
pre\_num\_c = Pipeline(\[("impute", SimpleImputer()), ("scale", StandardScaler())])
prep\_c = ColumnTransformer(\[("num", pre\_num\_c, Xc.columns)])
logit = Pipeline(\[("prep", prep\_c), ("model", LogisticRegression(max\_iter=1000))])
rf\_c = Pipeline(\[("prep", prep\_c), ("model", RandomForestClassifier(n\_estimators=400, random\_state=42, n\_jobs=-1))])
for name, mdl in {"Logit": logit, "RF": rf\_c}.items():
mdl.fit(Xc\_train, yc\_train)
proba = mdl.predict\_proba(Xc\_test)\[:,1]
pred = (proba >= 0.5).astype(int)
print(name, "ACC=", accuracy\_score(yc\_test, pred), "AUC=", roc\_auc\_score(yc\_test, proba))
print(classification\_report(yc\_test, rf\_c.predict(Xc\_test)))
判断基準:クラス不均衡がある場合はAUC/再現率も確認。閾値0.5が最適とは限りません。
Step5:交差検証で“安定度”を測る
from sklearn.model_selection import cross_val_score, KFold, StratifiedKFold
cv\_r = KFold(n\_splits=5, shuffle=True, random\_state=42)
cv\_scores = cross\_val\_score(rf, Xr\_train, yr\_train, scoring="neg\_root\_mean\_squared\_error", cv=cv\_r, n\_jobs=-1)
print("CV RMSE(mean±std)=", -cv\_scores.mean(), "+/-", cv\_scores.std())
cv\_c = StratifiedKFold(n\_splits=5, shuffle=True, random\_state=42)
cv\_auc = cross\_val\_score(rf\_c, Xc\_train, yc\_train, scoring="roc\_auc", cv=cv\_c, n\_jobs=-1)
print("CV AUC(mean±std)=", cv\_auc.mean(), "+/-", cv\_auc.std())
原則:モデル選択はCV平均で決める。テストスコアは最後に一度だけ使う。
Step6:ハイパーパラメータ探索(Grid/Random)
from sklearn.model_selection import GridSearchCV, RandomizedSearchCV
from scipy.stats import randint
# 例:RF回帰(Grid)
param\_grid = {
"model\_\_n\_estimators": \[200, 400, 800],
"model\_\_max\_depth": \[None, 10, 20],
"model\_\_min\_samples\_split": \[2, 5, 10]
}
rf\_gs = GridSearchCV(rf, param\_grid, scoring="neg\_root\_mean\_squared\_error", cv=3, n\_jobs=-1)
rf\_gs.fit(Xr\_train, yr\_train)
print("best\_params=", rf\_gs.best\_params\_)
# 例:RF分類(Random)
param\_dist = {
"model\_\_n\_estimators": randint(100, 800),
"model\_\_max\_depth": \[None] + list(range(3, 15)),
}
rf\_rs = RandomizedSearchCV(rf\_c, param\_distributions=param\_dist, n\_iter=20, scoring="roc\_auc", cv=3, random\_state=42, n\_jobs=-1)
rf\_rs.fit(Xc\_train, yc\_train)
print("best\_params=", rf\_rs.best\_params\_)
原則:探索はCV内で行い、テストは最後に1回。過適合を防ぐ鉄則です。
Step7:特徴量の重要度(Permutation)
from sklearn.inspection import permutation_importance
rf\_c.fit(Xc\_train, yc\_train)
r = permutation\_importance(rf\_c, Xc\_test, yc\_test, n\_repeats=10, random\_state=42, n\_jobs=-1)
imp = pd.Series(r.importances\_mean, index=Xc.columns).sort\_values(ascending=False)
print(imp.head(10))
重要度は因果ではなく“説明の補助”。リーク検査やモデル妥当性の確認に使いましょう。
Step8:モデルの保存・再現(joblib)
import joblib
from sklearn.metrics import roc_auc_score
joblib.dump(rf\_c, "model\_rf\_class.joblib")
mdl = joblib.load("model\_rf\_class.joblib")
print("loaded AUC:", roc\_auc\_score(yc\_test, mdl.predict\_proba(Xc\_test)\[:,1]))
バージョン固定(sklearn/pandas)はrequirements.txt
へ。README
に再現手順を記載。
Step9:よくある地雷と回避策
地雷 | 症状 | 回避策 |
---|---|---|
データリーク | CVは高いのに本番で失敗 | 前処理をPipeline化、fitは学習データのみ |
クラス不均衡 | 高精度でも実務で誤検知 | AUC/再現率/適合率を併記、閾値調整/重み付け |
評価の誤設計 | RMSEとMAEが混在、KPIと非連動 | 指標を1〜2個に限定しKPIと接続 |
テスト流出 | チューニングにテストを再使用 | テストは最後に1回の原則 |
Step10:読みやすいMLプロジェクトの雛形
project/
├─ data/ # 入出力(追跡対象外も検討)
├─ notebooks/ # 実験ノート
├─ src/ # 前処理/学習の関数
│ ├─ features.py
│ ├─ models.py
│ └─ evaluate.py
├─ reports/ # 図/スライド
├─ requirements.txt
└─ README.md # 再現手順
Tips:Notebookはアイデア検証、本流は関数化してsrc/
へ。最小でもデータ検査のテストを書いておくと安全です。
学習の“目的別”おすすめ配分
- 転職(未経験→DS):パイプライン/交差検証/ベースライン/README厚め。Permutation重要度で妥当性を説明。
- 副業(納品力):保存・再現手順/Excel出力を重視。運用を意識した閾値調整。
- 在宅(短時間×高頻度):関数化と小さい実験の反復。
RandomizedSearchCV
で探索を軽く。
ミニプロジェクト(提出推奨)
課題:「乳がんデータでAUC≥0.98、再現性付きで“説明可能”な分類器を仕上げる」
- ベースライン(Dummy)→ ロジスティック → ランダムフォレストで比較。
- パイプライン+StratifiedKFoldでCV計測、AUC平均±stdを報告。
- RandomizedSearchCVで軽量探索→最終モデルをテスト一度だけ評価。
- Permutation重要度で主要特徴を確認、示唆を3行で記載。
requirements.txt
とREADME
に再現手順を明記、joblib
でモデル保存。
レビュー観点チェックリスト(コピペ可)
- [ ] 前処理はPipeline/ColumnTransformerに格納されている
- [ ] テストは最後に1回しか使っていない
- [ ] 指標がKPIと連動し、平均±stdで安定性を示している
- [ ] リークなし(標準化/エンコードは学習データでfit)
- [ ] README/requirementsで再現できる
付録A:分類の閾値最適化(Youden/Jカットなど)
import numpy as np
from sklearn.metrics import roc_curve
proba = rf\_c.predict\_proba(Xc\_test)\[:,1]
fpr, tpr, thr = roc\_curve(yc\_test, proba)
youden = tpr - fpr
thr\_opt = thr\[np.argmax(youden)]
print("best threshold by Youden:", thr\_opt)
付録B:時系列CV(時間依存データの例)
from sklearn.model_selection import TimeSeriesSplit, cross_val_score
ts = TimeSeriesSplit(n\_splits=5)
scores = cross\_val\_score(ridge, Xr\_train.sort\_index(), yr\_train.sort\_index(), cv=ts,
scoring="neg\_root\_mean\_squared\_error")
print("TimeSeries CV RMSE=", -scores.mean())
次に読むべき記事
-
-
【保存版】モデル評価:指標の選び方・交差検証・閾値最適化・ビジネス接続を“実務の型”で解説
精度が上がらない原因の多くは「評価設計の誤り」にあります。評価とは「何点取れたか」ではなく、意思決定に耐えるかを測る営み。この記事では、回帰/分類/ランキングの指標の選び方、交差検証の正しい使い分け、 ...
-
-
【保存版】ハイパーパラメータ入門:Grid/Random/Optunaの実務チューニング完全ガイド
チューニングのゴールは「スコアの数字遊び」ではありません。意思決定に耐える安定した最適化を短時間で作ること。本記事は未経験〜初学者が週10時間×2週間で、GridSearchCV / Randomiz ...
-
-
【実務で差がつく】pandas実践:欠損処理・結合・ウィンドウ関数・時系列・品質保証まで“読みやすく速い”型を習得
リード(結論)基礎を終えたら次は実務の現場で頻出する処理を“型”で覚える段階です。本記事は、pandas 2.x を前提に、欠損・外れ値・結合・ウィンドウ関数・時系列・カテゴリ処理・集計の自動化・大規 ...
-
-
【保存版】可視化入門:Matplotlib/Plotlyの使い分けと“伝わるグラフ設計”10ステップ
結論:可視化は「きれいに描く」ことではなく、意思決定を動かすための設計です。本稿では、未経験〜初学者が 週10時間×1〜2週 で、Matplotlib/Plotlyを軸に “伝わるグラフ”の設計と実装 ...
-
-
【保存版】データ職のポートフォリオ完全ガイド|再現性・評価・LTまで
ポートフォリオって「作ったものの置き場」でしょ? いいえ。採用側が見たいのは「意思決定に効いた証拠」と「再現性」です。 本ガイドは、未経験〜初学者が週10時間×4〜6週で、テーマ選定→要件定義→データ ...
FAQ:よくある質問
Q1. どのモデルから始めれば良い?
A. ロジスティック/リッジから。次にツリーモデル(RF/GB)で非線形性を拾う。
Q2. 精度が上がらない
A. まず特徴量とリークを疑う。次に評価設計とデータの質を見直す。 [内部リンク:pandas実践] [内部リンク:モデル評価]
Q3. クラス不均衡がキツい
A. class_weight="balanced"
、閾値調整、適切な指標(再現率/PR-AUC)で評価。
Q4. とにかく再現性が欲しい
A. random_state固定・Pipeline・requirements固定・READMEの4点セット。
それでは、また次の記事でお会いしましょう。
最近のコメント