論文一覧に戻る 📚 用語集トップ 🗺 概念マップ
📚 用語解説
📚 用語解説
scikit-learn
scikit-learn
ライブラリ
別称: sklearn

🔖 キーワード索引

scikit-learnscikit-learnライブラリsklearn

本ページは scikit-learn(scikit-learn)を多角的に解説します。 上のチップは、 検索・関連語の手がかりです。

🔖 Round 43 増補: scikit-learnを相関ページ級に読む

この増補は、scikit-learnを「用語の暗記」ではなく、実データで使い、数式を言葉に戻し、結果の限界まで説明するための補講である。使用データは data/raw/SSDSE-B-2026.csv の実測値であり、np.random による合成データは使わない。

DataFramepandasNumPy標準化線形回帰相関過学習訓練/テスト分割交差検証特徴量目的変数損失関数正則化機械学習機械学習の基礎深層学習活性化関数ReLUMLPscikit-learn数理最適化尺度水準概念マップ用語集トップ

🔖 キーワード索引(拡張版)

本ページに登場する主要キーワード 10 件。 クリックで該当セクションへ。

🔖 scikit-learn🔖 fit/predict🔖 Pipeline🔖 GridSearchCV🔖 cross_val_score🔖 ColumnTransformer🔖 StandardScaler🔖 OneHotEncoder🔖 RandomForest🔖 SSDSE-B-2026

🔖 scikit-learnを実データで読む最終補講

💡 scikit-learnは、表データを「説明変数の行列」と「目的変数の列」に分け、同じ手順で前処理、学習、評価をつなぐための道具である。ここでは data/raw/SSDSE-B-2026.csv の実値だけを使い、np.random による合成データは使わない。

📍 例として、2023年の47都道府県について、総人口 A1101、年平均気温 B4101、延べ宿泊者数 G7101、一般病院数 I510120 から、二人以上世帯の消費支出 L3221 を説明する。目的は高精度な予測ではなく、実データをscikit-learnの型に落とし込む過程を読むことである。

🔖 手順 💡 直感 📍 実データ 🎨 可視化前提 📐 形 🔬 検証 🧮 数式 🐍 Python ⚠️ 注意 🌐 汎用性 🔗 関連 📚 復習

🧮 数式を言葉で読み解く

Ridge回帰は、予測値と実測値のずれを小さくしつつ、係数が極端に大きくならないように罰則を加える。式で書くと「残差平方和 + α × 係数平方和」を小さくする。言葉で読むなら、「当てはまりを良くしたいが、47件しかない表で一部の列に過剰反応しないよう、係数を少し控えめにする」という考え方である。

📐 StandardScaler は各列を平均0、標準偏差1の尺度にそろえる。総人口は何百万人、気温は十数度、宿泊者数は何百万人泊というように単位が違うため、そのまま係数を比べると単位の影響が混ざる。標準化後の係数は「その列が1標準偏差ぶん動いたとき、目的変数がどちらにどれくらい動くか」という読み方に近づく。

🐍 PythonでSSDSE-B-2026をscikit-learnに渡す

このコードでやることdata/raw/SSDSE-B-2026.csv をCP932で読み込み、2023年の47都道府県だけを抽出し、Pipeline(StandardScaler, Ridge) で学習・評価する。学習と評価は train_test_split と5分割交差検証の両方で見る。

入力例:入力行は都道府県、入力列は A1101B4101G7101I510120、目的変数は L3221。ファイル内の列名はASCIIなので、列選択はそのまま行える。

📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 A1101(総人口) B4101(年平均気温) G7101(延べ宿泊者数) I510120(一般病院数) L3221(消費支出(二人以上の世帯)) 北海道 5,092,000 11.0 32,783,470 464 296,888 東京都 14,086,000 17.6 80,273,650 588 341,320 沖縄県 1,468,000 23.8 20,038,190 76 251,222 …(全 47 行)
 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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
from pathlib import Path

import pandas as pd
from sklearn.inspection import permutation_importance
from sklearn.linear_model import Ridge
from sklearn.metrics import mean_absolute_error, r2_score
from sklearn.model_selection import KFold, cross_val_score, train_test_split
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler

path = Path("data/raw/SSDSE-B-2026.csv")
df = pd.read_csv(path, skiprows=[1], encoding="cp932")
df2023 = df[df["SSDSE-B-2026"] == 2023].copy()

features = ["A1101", "B4101", "G7101", "I510120"]
target = "L3221"

X = df2023[features].apply(pd.to_numeric, errors="coerce")
y = pd.to_numeric(df2023[target], errors="coerce")
pref = df2023["Prefecture"]

mask = X.notna().all(axis=1) & y.notna()
X, y, pref = X[mask], y[mask], pref[mask]

X_train, X_test, y_train, y_test, pref_train, pref_test = train_test_split(
    X, y, pref, test_size=0.25, random_state=42
)

model = make_pipeline(StandardScaler(), Ridge(alpha=1.0))
model.fit(X_train, y_train)
pred = model.predict(X_test)

cv = KFold(n_splits=5, shuffle=True, random_state=42)
cv_r2 = cross_val_score(model, X, y, cv=cv, scoring="r2")
cv_mae = -cross_val_score(model, X, y, cv=cv, scoring="neg_mean_absolute_error")

print(f"n={len(X)}")
print(f"holdout_r2={r2_score(y_test, pred):.3f}")
print(f"holdout_mae={mean_absolute_error(y_test, pred):.1f}")
print(f"cv_r2_mean={cv_r2.mean():.3f}")
print(f"cv_mae_mean={cv_mae.mean():.1f}")

result = (
    pd.DataFrame({"pref": pref_test, "actual": y_test, "pred": pred})
    .assign(abs_err=lambda d: (d["actual"] - d["pred"]).abs())
    .sort_values("abs_err", ascending=False)
)
print(result.head(5).round({"pred": 1, "abs_err": 1}))

model.fit(X, y)
importance = permutation_importance(
    model, X, y, n_repeats=30, random_state=42, scoring="neg_mean_absolute_error"
)
print(pd.Series(importance.importances_mean, index=features).sort_values(ascending=False).round(1))

実行結果

n=47
holdout_r2=-0.298
holdout_mae=20445.6
cv_r2_mean=0.110
cv_mae_mean=18498.0
    pref  actual      pred  abs_err
144  東京都  341320  303509.5  37810.5
300  京都府  314636  280259.7  34376.3
48   秋田県  272086  302935.2  30849.2
96   栃木県  325226  295508.7  29717.3
312  大阪府  271246  294586.2  23340.2
A1101      7588.9
I510120    2420.3
B4101      2024.8
G7101        -7.1
dtype: float64

結果の読み方:holdoutのR2が負なので、この分割では「テストデータの平均で予測するだけ」より悪い。5分割交差検証の平均R2も0.110にとどまり、実用的な予測モデルというより、変数選択と評価手順を学ぶ教材として読むべき結果である。平均絶対誤差は約18,498から20,446円で、都道府県別の消費支出を4列だけで説明するには情報が不足している。

🔬 ここで見えていること、見えていないこと

📚 レポートに書くときの短い型

本分析では、data/raw/SSDSE-B-2026.csv の2023年都道府県データ47件を用い、scikit-learnの Pipeline で標準化とRidge回帰を接続した。評価はholdoutと5分割交差検証で行ったが、R2は低く、4つの説明変数だけでは消費支出を十分に説明できなかった。したがって、この結果は予測モデルの完成ではなく、実データを読み込み、前処理し、評価し、限界を記述する一連の作業例として位置づける。

💡 30秒で分かる結論

🍰 まずはやさしく

機械学習のための便利な道具箱です。

データの分析や予測を簡単にするために使います。

スマホのアプリが好みを予測する仕組みに似ています。

この章では基本的な使い方を学びます。

💡 30秒で分かる結論

💡 30 秒で分かる結論(拡張)

📍 文脈 — どこで使う概念か

🍰 まずはやさしく

データ分析の中心となるツールです。

正しい答えを導き出すために使います。

部活の成績やテストの結果を分析する時に役立ちます。

どのような場面で使うのかを解説します。

scikit-learn は 2007 年に David Cournapeau が GSoC で開始したプロジェクトで、 Python の機械学習エコシステムの 中核です。 NumPy / SciPy / pandas / matplotlib との連携が完璧で、 入門から実務まで幅広く使われます。 ドキュメントの質も非常に高く、 機械学習を学ぶならまず scikit-learnと言われるほど。

📍 文脈ボックス

統計・データ解析コンペでscikit-learnが出てくる場面は、探索的分析、特徴量設計、モデル構築、検証、説明資料のどこかに必ずある。たとえば都道府県別の人口、年平均気温、宿泊者数、消費支出を扱うとき、値をそのまま比べてよいのか、標準化すべきか、目的関数をどう置くか、評価指標をどう読むかが問題になる。

観点確認する問い誤ると起きること
入力列名・単位・年度は何か別年度や別単位を混ぜて結論が崩れる
変換標準化・活性化・尺度分類は妥当か数値は出るが意味が薄い
評価誤差・順位・係数をどう読むか精度だけを見て限界を落とす
報告何を意味しないかを書いたか因果や一般化を言い過ぎる

📍 文脈ボックス — あなたが今見ているもの

あなたは scikit-learn の用語ページを読んでいる。 これは Python で機械学習を行う際の 事実上の標準ツール。 表形式データ(テーブルデータ)の前処理・特徴量抽出・モデル学習・評価・選択を、 一貫したインターフェースで提供する。 ディープラーニングは PyTorch / TensorFlow に譲るが、 ベースライン作成・実験管理・前処理パイプラインの中核として現在も第一選択。 本ページでは SSDSE-B-2026 の 47 都道府県データで、 sklearn の主要 API を一通り体験する。

🎨 直感で掴む — 具体例で理解する

🍰 まずはやさしく

使い方がそろった万能セットのようなものです。

色々な分析手法を簡単に比べるために使います。

買い物で商品を選ぶように、最適な方法を選べます。

具体的な機能と使い方の流れを説明します。

scikit-learn の哲学は 「統一 API」。 どんなアルゴリズムでも同じ 3 つのメソッドで使える:

これにより、 ロジスティック回帰を SVM に差し替えるのに クラス名 1 行だけ変えれば済む。 アルゴリズム比較が極めて容易です。

主な機能群:

🎨 直感で掴む

scikit-learnを直感で捉えるには、表計算の一列だけを見るのではなく、値がどの手順で判断に変わるかを見る。SSDSE-B-2026では、北海道、東京都、沖縄県のように人口規模、気候、観光、消費が大きく違う地域が同じ表に並ぶ。ここで同じ処理を機械的に当てると、規模の効果と構造の効果が混ざる。

都道府県総人口 A1101年平均気温 B4101消費支出 L3221
北海道5,092,00011.0296,888
東京都14,086,00017.6341,320
沖縄県1,468,00023.8251,222

🎨 直感で掴む — sklearn の哲学

scikit-learn の設計思想は 「一貫した API」。 どんなモデルでも同じ 3 ステップ:

  1. model = SomeModel(...) ── インスタンス化(ハイパラ指定)
  2. model.fit(X_train, y_train) ── 学習
  3. model.predict(X_test) ── 予測

線形回帰、 ランダムフォレスト、 サポートベクトル機、 ニューラルネット全てが同じ顔をしている。 これによりモデルを 1 行差し替えるだけで比較実験ができる。

3 つのオブジェクト種別

種別主要メソッド代表例
Estimator(推定器)fit, predict, scoreLinearRegression, RandomForest
Transformer(変換器)fit, transform, fit_transformStandardScaler, PCA, OneHotEncoder
Meta-estimator上記 + 内部に他オブジェクトGridSearchCV, Pipeline, VotingClassifier

主要モジュール

🎨 概念図で押さえる — scikit-learn の3つの視点

scikit-learn の理解には、 (1) クラスタリング (教師なし学習の代表)、 (2) 決定木 (教師あり分類の代表)、 (3) 正則化 (汎化性能制御) の 3 点を同時に把握する必要があります。 ここでは html/glossary/figures/ にある既存図を借りて、 sklearn の学習アルゴリズムのキャンバスを描き出します。

k-means クラスタリング
図A: k-means は sklearn の教師なし学習の代表アルゴリズム。 sklearn.cluster.KMeans(n_clusters=3).fit(X) の 1 行で SSDSE-B-2026 の 47 都道府県を 3 群に分けられる。 重心が更新される度に境界線が移動する様子を可視化すると、 「同質な観測の集合」 という抽象概念が直感的に掴める。 sklearn の fit/predict API はあらゆるアルゴリズムで統一されているため、 KMeans を覚えれば DBSCAN・Agglomerative などへの拡張が容易。

読み取るポイント:

  • 重心の位置 → 各クラスタの代表値、 業務的解釈に直結
  • 境界線の形状 → k-means は球状クラスタを仮定するサイン
  • クラスタサイズの差 → 不均衡なら別アルゴリズム検討
決定木
図B: 決定木は sklearn の教師あり学習で最も解釈しやすいモデル。 sklearn.tree.DecisionTreeClassifier() + plot_tree() で木を可視化でき、 「どの特徴量で最初に分岐したか」 を一目で確認できる。 SSDSE-B-2026 で「都市部か地方か」を分類すると、 1 段目に総人口 A1101、 2 段目に高齢人口 A1303 が来るような自然な階層が現れる。 ランダムフォレストや勾配ブースティングの基礎であり、 sklearn の Tree 系 API を理解する起点になる。

読み取るポイント:

  • 1 段目の分岐 → 最重要特徴量、 業務的検証の対象
  • 木の深さ → 過学習リスク、 max_depth で制御
  • リーフのサンプル数 → 葉が小さすぎると過学習サイン
正則化
図C: 正則化は sklearn の Ridge・Lasso・ElasticNet 系で活躍する汎化性能制御の中核技術。 alpha を上げるほど係数が縮小し、 過学習を抑える代わりに学習不足のリスクが増す。 GridSearchCV と組み合わせれば、 alpha の最適値を自動探索可能。 SSDSE-B-2026 のような小サンプル (n=47) で多変量回帰を行う場合、 Ridge は必須に近い。 図 A・B のアルゴリズム選択と並んで、 alpha 設定が sklearn 運用の三本柱を完成させる。

読み取るポイント:

  • U 字曲線の最下点 → 最適 alpha、 GridSearchCV で自動取得
  • 左側 (低 alpha) → 過学習、 train R² が高いが test が低い
  • 右側 (高 alpha) → 学習不足、 両方の R² が低い

この 3 枚を順に追うと、 「教師なし学習 → 教師あり学習 → 正則化制御」 という scikit-learn の学習パイプライン全体が体系化される。 関連用語 k-means / 決定木 / Lasso / Ridge へ進むと、 各図の理論背景を深掘りできる。

🧠 理解度チェック

1分で答えられる確認問題。 SSDSE-B-2026 で scikit-learn を使う前後で必ず自分に問うべき項目です。 自分で答えてから解答を開きましょう。

Q1. scikit-learn の Estimator (推定器) が満たす共通インターフェース 3 つを答え、 「人口を 5 つの説明変数から予測する」コード例で fit/predict/score がそれぞれ何をしているか説明せよ。

A. fit(X, y), predict(X), score(X, y) の 3 つ (一部は transform)。

  • fit(X_train, y_train): 説明変数行列 X_train (47×5) と目的変数 y_train (47×1) からモデル内部の係数・分割点・閾値を学習する。
  • predict(X_test): 学習済モデルに新しい X を与えて予測値を返す。 例: 訓練済 LinearRegression に未知都道府県の特徴を入れると人口予測が返る。
  • score(X_test, y_test): 評価指標 (回帰なら R²、 分類なら Accuracy) を計算。 ただし scoring は分割外データで行うこと。
Q2. Pipeline を使うべき理由を、 「データリーク (data leakage) 防止」の観点で説明せよ。 標準化と交差検証を組み合わせる例で具体的に。

A. 標準化は 各 fold ごとに訓練データのみで fit しなければならない。 全データに StandardScaler().fit_transform(X) をしてから cross_val_score すると、 テストデータの平均・分散が訓練に漏れ、 評価が楽観的になる。

Pipeline で [("scaler", StandardScaler()), ("model", Ridge())] を組むと、 cross_val 内部で fold ごとに scaler.fit が呼ばれるため、 データリークなく評価できる。 SSDSE-B-2026 は n=47 と小さく、 1 fold あたり 9-10 サンプルしかないため、 リーク影響が拡大しやすい。

Q3. GridSearchCVRandomizedSearchCV の使い分けを、 ハイパーパラメータ数とその範囲の観点で説明せよ。
  • GridSearchCV: 候補が少なく (例: alpha ∈ {0.01, 0.1, 1, 10})、 全組合せが計算可能な時。 確実に最適点を見つける。
  • RandomizedSearchCV: ハイパーパラメータが多次元 (RandomForest の n_estimators / max_depth / min_samples_split など) または連続値の時。 ランダムサンプリングで効率的に探索。

SSDSE-B-2026 で Ridge α の探索は Grid、 RandomForest の構造探索は Randomized が定石。 さらに BayesSearchCV (scikit-optimize) を使うとベイズ最適化で更に効率化できる。

📐 定義

🍰 まずはやさしく

Pythonで使う機械学習の標準的なライブラリです。

計算の手順を正しく定義するために使います。

学校の点数から合格率を出す計算のようなものです。

詳しい定義と計算の仕組みについて読みます。

Python の機械学習標準ライブラリ

英語名 scikit-learn、 カテゴリ:ライブラリ。

📐 数式または定義

代表的な定義を、分析で読める形に置く。

$$ \\mathrm{score}=\\frac{1}{K}\\sum_{k=1}^{K} L\\left(y_{\\mathrm{test},k}, f_{\\theta,k}(X_{\\mathrm{test},k})\\right) $$

📐 主要モデルの数式

線形回帰 (LinearRegression):

$$\hat{y} = \boldsymbol{w}^\top \boldsymbol{x} + b, \quad \boldsymbol{w}^* = (X^\top X)^{-1} X^\top \boldsymbol{y}$$

Ridge 回帰:

$$\boldsymbol{w}^* = \arg\min_{\boldsymbol{w}} \|\boldsymbol{y} - X \boldsymbol{w}\|^2 + \alpha \|\boldsymbol{w}\|^2_2$$

ロジスティック回帰:

$$P(y=1 \mid \boldsymbol{x}) = \sigma(\boldsymbol{w}^\top \boldsymbol{x} + b), \quad \sigma(z) = \frac{1}{1 + e^{-z}}$$

RandomForest(決定木 $T$ の平均):

$$\hat{y} = \frac{1}{T} \sum_{t=1}^{T} h_t(\boldsymbol{x})$$

cross_val_score の R² 計算:

$$R^2 = 1 - \frac{\sum_{i=1}^{N} (y_i - \hat{y}_i)^2}{\sum_{i=1}^{N} (y_i - \bar{y})^2}$$

🔬 記号・要素の読み解き

Estimator
学習可能なオブジェクト(モデル)。 fit を持つ
Transformer
データ変換するオブジェクト。 fit_transform を持つ
Predictor
予測するオブジェクト。 predict を持つ
Pipeline
複数の処理を直列に繋ぐ。 リーク防止に必須
GridSearchCV
ハイパラ探索を交差検証で行うクラス
random_state
再現性のための乱数シード。 必ず指定

🔬 数式を言葉で読み解く

K個に分けたデータで、K回「学習用でfit、評価用でpredict」を繰り返し、損失 L の平均を取る。scikit-learnではこの流れを Pipeline と cross_val_score で漏洩なく書く。

記号・部品言葉での意味読み間違いを避けるポイント
x / X入力特徴量。SSDSEでは人口、気温、宿泊者数など単位が違う列を同じ重みで読まない
y説明したい値。ここでは消費支出や人口増減など予測対象と説明変数を混同しない
パラメータデータから決める重みや境界大きいほど重要とは限らない
損失・誤差予測と実測のずれ小さいほどよいが、過学習も見る
制約・前提使ってよい解の範囲や仮定現実の制約を落とすと使えない結論になる

🧮 数値例・実値計算

scikit-learn 標準ワークフロー(典型 7 ステップ):

  1. データを train_test_split で分割
  2. StandardScaler で標準化(fit は訓練データのみ)
  3. モデル選択(例:RandomForestClassifier
  4. fit(X_train, y_train) で学習
  5. predict(X_test) で予測
  6. accuracy_score 等で評価
  7. 必要なら GridSearchCV でハイパラ調整

🧮 実値で計算してみる

このコードでやること:SSDSE-B-2026の実データを使い、StandardScaler と RidgeCV を Pipeline に入れて、消費支出 L3221 を5分割交差検証で評価する。

入力例data/raw/SSDSE-B-2026.csv から2023年の47都道府県を抽出し、Prefecture は文字列、A1101 などの統計列は数値に変換する。

Prefecture A1101 A1303 B4101 G7101 I510120 L3221 北海道 5092000 1681000 11.0 32783470 464 296888 青森県 1184000 417000 12.6 3880720 72 263371 岩手県 1163000 407000 12.5 4980740 76 298536
📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 A1101(総人口) A1303(65歳以上人口) B4101(年平均気温) G7101(延べ宿泊者数) I510120(一般病院数) 北海道 5,092,000 1,681,000 11.0 32,783,470 464 東京都 14,086,000 3,205,000 17.6 80,273,650 588 沖縄県 1,468,000 350,000 23.8 20,038,190 76 …(全 47 行)
 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
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import RidgeCV
from sklearn.model_selection import KFold, cross_val_score
from sklearn.metrics import r2_score

raw = pd.read_csv("data/raw/SSDSE-B-2026.csv", encoding="cp932")
df = raw.iloc[1:].copy()
df = df[df["SSDSE-B-2026"].astype(str) == "2023"].copy()
features = ["A1101", "A1303", "B4101", "G7101", "I510120"]
for col in features + ["L3221"]:
    df[col] = pd.to_numeric(df[col], errors="coerce")
X, y = df[features], df["L3221"]
mask = X.notna().all(axis=1) & y.notna()
X, y = X[mask], y[mask]
pipe = Pipeline([("scaler", StandardScaler()), ("ridge", RidgeCV(alphas=[0.1, 1.0, 10.0, 100.0]))])
cv = KFold(n_splits=5, shuffle=True, random_state=42)
mae = -cross_val_score(pipe, X, y, cv=cv, scoring="neg_mean_absolute_error")
pipe.fit(X, y)
print(f"alpha = {pipe.named_steps['ridge'].alpha_}")
print(f"CV MAE = {mae.mean():,.2f} (std {mae.std():,.2f})")
print(f"train R2 = {r2_score(y, pipe.predict(X)):.3f}")

実行結果

n = 47 都道府県 RidgeCV alpha = 100.0 5分割CV MAE = 19,593.70 円 (std 5,674.19) 学習データ上 R^2 = 0.115 係数: A1101=1,745.57, A1303=1,584.29, B4101=-1,909.96, G7101=843.00, I510120=491.37

結果の読み方:Pipelineにすることで、標準化は各foldの学習データだけでfitされる。精度は高くないが、評価の手順としては漏洩しにくい。

🧮 実値で計算してみる(SSDSE-B-2026, 47 都道府県)

STEP 1: モデル比較ベンチマーク

出生率 A4103 を 3 説明変数で予測、 sklearn の主要モデルを横並びに比較:

モデルクラスtrain R²5-fold CV R²(shuffle)主要ハイパラ
線形回帰LinearRegression0.6520.615 ± 0.086(なし)
RidgeRidge(α=1.0)0.6520.618 ± 0.086α
LassoLasso(α=0.01)0.6380.609 ± 0.091α
KNN (k=5)KNeighborsRegressor0.7150.503 ± 0.140n_neighbors
SVR (rbf)SVR0.6770.205 ± 0.440C, gamma
RandomForestRandomForestRegressor0.9310.434 ± 0.214n_estimators, max_depth
GradientBoostingGradientBoostingRegressor0.9950.350 ± 0.235n_estimators, learning_rate
MLP (16, 8)MLPRegressor-6.053-9.573 ± 5.831hidden_layer_sizes

→ 47 サンプルの小規模データを shuffle 込みの 5-fold CV で評価すると、 Ridge が CV R²=0.618 で最良。 線形・正則化線形(Linear/Ridge/Lasso)が 0.61 前後で安定する一方、 RandomForest(0.434)・GradientBoosting(0.350)はむしろ CV では劣り、 MLP は train でも CV でも大きく崩れる(過学習と最適化不安定)。

📝 より正確な分析:上のベンチは KFold(n_splits=5, shuffle=True, random_state=42) で実測した値。 「47 サンプルでは RandomForest が最良」は成り立たず、 実際には最も単純な Linear・Ridge・Lasso が CV R²≈0.61 で頭一つ抜ける。 tree 系(RF/GB)は train R² が 0.93〜0.99 と高い割に CV では 0.35〜0.43 に沈み、 典型的な過学習を示す。 MLP は 47 サンプル・3 特徴では最適化が安定せず CV R² が負に振れる。 なお shuffle を付けない cv=5(データ並び順のまま分割)だと、 SSDSE が都道府県コード順に並ぶため fold ごとの分布が偏り、 スコアがさらに不安定になる。 小標本ではモデルの複雑さより交差検証の設計(shuffle・乱数シード)が結果を左右する点に注意。

STEP 2: GridSearchCV で Ridge のハイパラ最適化

param_grid: alpha = [0.001, 0.01, 0.1, 1.0, 10.0, 100.0]\n結果 (KFold shuffle=True, random_state=42):\n best_params = {\'alpha\': 1.0}\n best_CV R² = 0.618\n full grid scores:\n α=0.001: CV R² = 0.615\n α=0.01 : CV R² = 0.615\n α=0.1 : CV R² = 0.616\n α=1.0 : CV R² = 0.618 ← 最良\n α=10.0 : CV R² = 0.606\n α=100.0: CV R² = 0.322

🧮 数式に値を入れて手で計算する: sklearn の典型コード

合成データで sklearn による回帰モデルの精度を計算する。

Step 1: データと処理

x = [2,4,7,5,3], y = [3,7,12,9,4] LinearRegression fit → β=1.892, α=-0.946

Step 2: R²

R² ≈ 0.981 (前出)

🐍 Python で再現

1
2
3
4
5
6
7
import numpy as np
from sklearn.linear_model import LinearRegression
x = np.array([2,4,7,5,3]).reshape(-1,1)
y = np.array([3,7,12,9,4])
m = LinearRegression().fit(x, y)
print(f"β: {m.coef_[0]:.3f}, α: {m.intercept_:.3f}")
print(f"R²: {m.score(x, y):.3f}")

📤 実行結果

β: 1.892, α: -0.946 R²: 0.981

💬 手計算 (Step 2) と Python 出力が完全一致。

🐍 Python 実装例

最小コードで動かしてみる例:

📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 SSDSE-B-2026(年度) A1303(65歳以上人口) A1101(総人口) L3221(消費支出(二人以上の世帯)) Prefecture(都道府県) 北海道 2,023 1,681,000 5,092,000 296,888 北海道 東京都 2,023 3,205,000 14,086,000 341,320 東京都 沖縄県 2,023 350,000 1,468,000 251,222 沖縄県 …(全 47 行)
 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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# ── この抜粋で使うデータを用意します(SSDSE-B の 47 都道府県・最新年度)──
import pandas as pd

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', header=1)
df = df[df['地域コード'].astype(str).str.match(r'^R\d{5}$', na=False)].copy()
df['年度'] = pd.to_numeric(df['年度'], errors='coerce')
df = df[df['年度'] == df['年度'].max()]
for _c in df.columns[3:]:
    df[_c] = pd.to_numeric(df[_c], errors='coerce')
df['高齢化率'] = df['65歳以上人口'] / df['総人口'] * 100

# 見本でよく使われる仮の列名を、実データから作っておく
df['income'] = df['消費支出(二人以上の世帯)']
df['population'] = df['総人口']
_region = {'北海道': '北海道', '青森県': '東北', '岩手県': '東北', '宮城県': '東北',
           '秋田県': '東北', '山形県': '東北', '福島県': '東北', '茨城県': '関東',
           '栃木県': '関東', '群馬県': '関東', '埼玉県': '関東', '千葉県': '関東',
           '東京都': '関東', '神奈川県': '関東'}
df['region'] = df['都道府県'].map(_region).fillna('その他')
df['地域'] = df['region']

_feats = ['総人口', '出生数', '死亡数', '婚姻件数']
df = df.dropna(subset=_feats + ['消費支出(二人以上の世帯)'])
X = df[_feats].to_numpy(dtype=float)
# ここは「分類器」の例なので、目的変数も 2 値でなければならない。
# 消費支出(連続値)のままだと 1 県 1 クラス扱いになり Acc=0 になる。
y = (df['高齢化率'] >= df['高齢化率'].median()).astype(int).to_numpy()
x = df['総人口'].to_numpy(dtype=float)

from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline

# パイプラインで前処理 + モデルを連結
pipe = Pipeline([
    ('scaler', StandardScaler()),
    ('rf', RandomForestClassifier(n_estimators=100, random_state=42)),
])

X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=0.2, random_state=42)
pipe.fit(X_tr, y_tr)
print('Acc:', accuracy_score(y_te, pipe.predict(X_te)))

🐍 Python 実装

このコードでやること:予測表を作り、actual・pred・abs_err を都道府県名と一緒に出す。モデル評価は平均だけでなく、どこで外しているかを見る。

入力例:同じSSDSE-B-2026の実データを用いる。必要な列だけを取り出し、列名を明示してから処理する。

📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 A1101(総人口) A1303(65歳以上人口) B4101(年平均気温) G7101(延べ宿泊者数) I510120(一般病院数) 北海道 5,092,000 1,681,000 11.0 32,783,470 464 東京都 14,086,000 3,205,000 17.6 80,273,650 588 沖縄県 1,468,000 350,000 23.8 20,038,190 76 …(全 47 行)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
import pandas as pd
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import RidgeCV

raw = pd.read_csv("data/raw/SSDSE-B-2026.csv", encoding="cp932")
df = raw.iloc[1:].copy()
df = df[df["SSDSE-B-2026"].astype(str) == "2023"].copy()
features = ["A1101", "A1303", "B4101", "G7101", "I510120"]
for col in features + ["L3221"]:
    df[col] = pd.to_numeric(df[col], errors="coerce")
X, y = df[features], df["L3221"]
mask = X.notna().all(axis=1) & y.notna()
pipe = Pipeline([("scaler", StandardScaler()), ("ridge", RidgeCV(alphas=[100.0]))])
pipe.fit(X[mask], y[mask])
out = pd.DataFrame({"Prefecture": df.loc[mask, "Prefecture"], "actual": y[mask], "pred": pipe.predict(X[mask])})
out["abs_err"] = (out["actual"] - out["pred"]).abs()
print(out.head())

実行結果

北海道 actual=296,888 pred=307,610.7 abs_err=10,722.7 青森県 actual=263,371 pred=297,342.3 abs_err=33,971.3 岩手県 actual=298,536 pred=297,484.9 abs_err=1,051.1 宮城県 actual=305,541 pred=296,801.6 abs_err=8,739.4 秋田県 actual=272,086 pred=295,820.7 abs_err=23,734.7

結果の読み方:平均MAEだけでは、どの地域で外したかが見えない。scikit-learnの出力はpandas表に戻して、外れ方を業務文脈で確認する。

🐍 Python 実装(scikit-learn、 SSDSE-B-2026)

コード 1: fit/predict の基本 — Ridge で出生率予測

🎯 このコードでやること:scikit-learn の最も基本的な使い方。 train/test 分割 → fit → predict → 評価の 4 ステップ。

📥 入力データ (SSDSE-B-2026 から抽出):

df.head() (年度=2023): Prefecture A1101 A1303 A4103 B4101 北海道 5092000 1681000 1.06 11.0 青森県 1184000 417000 1.23 12.6
 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.linear_model import Ridge
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import r2_score, mean_squared_error
import numpy as np

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
d = df[df['SSDSE-B-2026'] == 2023]
X = d[['A1101', 'A1303', 'B4101']].values.astype(float)
y = d['A4103'].values

X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=0.3, random_state=42)
sc = StandardScaler().fit(X_tr)
X_tr_s, X_te_s = sc.transform(X_tr), sc.transform(X_te)

model = Ridge(alpha=1.0).fit(X_tr_s, y_tr)
pred = model.predict(X_te_s)
print(f'train R² = {model.score(X_tr_s, y_tr):.4f}')
print(f'test  R² = {r2_score(y_te, pred):.4f}')
print(f'test RMSE = {np.sqrt(mean_squared_error(y_te, pred)):.4f}')
print(f'係数 = {model.coef_.round(3)}, 切片 = {model.intercept_:.3f}')

📤 実行すると次の出力が得られる:

train R² = 0.7113 test R² = 0.4586 test RMSE = 0.0987 係数 = [-0.022 -0.05 0.072], 切片 = 1.314

💬 結果の読み方:train/test 分割(30% test)後、 標準化(fit は train のみ)、 Ridge で学習。 test R² 0.46 が汎化性能の指標。 係数の符号は『総人口・高齢化が増えると出生率減、 気温上昇で出生率増』を示す。

コード 2: Pipeline + ColumnTransformer で前処理統合

🎯 このコードでやること:数値列は標準化、 カテゴリ列は OneHot を Pipeline で 1 つに統合。 リーク防止 + 再現性確保。

📥 入力データ (SSDSE-B-2026 から抽出):

X (47×4): A1101(数), A1303(数), B4101(数), 地方ブロック(カテゴリ) / y (47,): A4103
 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
import pandas as pd
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.ensemble import RandomForestRegressor

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
d = df[df['SSDSE-B-2026'] == 2023].copy()
# 簡易な地方ブロックを追加
def region(code):
    n = int(code[1:3])
    return '北海道東北' if n<=7 else '関東' if n<=14 else '中部' if n<=23 else '近畿' if n<=30 else '中四国' if n<=39 else '九州沖縄'
d['region'] = d['Code'].apply(region)
X = d[['A1101', 'A1303', 'B4101', 'region']]
y = d['A4103'].values

pre = ColumnTransformer([
    ('num', StandardScaler(), ['A1101', 'A1303', 'B4101']),
    ('cat', OneHotEncoder(handle_unknown='ignore'), ['region'])
])
pipe = Pipeline([('pre', pre),
                 ('rf', RandomForestRegressor(n_estimators=100, random_state=42))])
pipe.fit(X, y)
print(f'train R² = {pipe.score(X, y):.4f}')
print(f'前処理後の特徴量数: {pipe.named_steps["pre"].transform(X).shape[1]}')

📤 実行すると次の出力が得られる:

train R² = 0.9412 前処理後の特徴量数: 9

💬 結果の読み方:数値 3 列 + 地方ブロック 6 種 OneHot = 9 特徴量。 Pipeline 1 オブジェクトで前処理〜学習を統合、 train R² 0.94 に到達。 cross_val でも安全に使える。

コード 3: GridSearchCV でハイパラ自動探索

🎯 このコードでやること:Ridge の正則化強度 $\alpha$ を 6 候補で 5-fold CV、 最良値を選択。

📥 入力データ (SSDSE-B-2026 から抽出):

X (47, 3 標準化済), y (47,) -- コード 1 と同じ。
 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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# ── この抜粋で使うデータを用意します(SSDSE-B の 47 都道府県・最新年度)──
import pandas as pd

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', header=1)
df = df[df['地域コード'].astype(str).str.match(r'^R\d{5}$', na=False)].copy()
df['年度'] = pd.to_numeric(df['年度'], errors='coerce')
df = df[df['年度'] == df['年度'].max()]
for _c in df.columns[3:]:
    df[_c] = pd.to_numeric(df[_c], errors='coerce')
df['高齢化率'] = df['65歳以上人口'] / df['総人口'] * 100

# 見本でよく使われる仮の列名を、実データから作っておく
df['income'] = df['消費支出(二人以上の世帯)']
df['population'] = df['総人口']
_region = {'北海道': '北海道', '青森県': '東北', '岩手県': '東北', '宮城県': '東北',
           '秋田県': '東北', '山形県': '東北', '福島県': '東北', '茨城県': '関東',
           '栃木県': '関東', '群馬県': '関東', '埼玉県': '関東', '千葉県': '関東',
           '東京都': '関東', '神奈川県': '関東'}
df['region'] = df['都道府県'].map(_region).fillna('その他')
df['地域'] = df['region']

_feats = ['総人口', '出生数', '死亡数', '婚姻件数']
df = df.dropna(subset=_feats + ['消費支出(二人以上の世帯)'])
X = df[_feats].to_numpy(dtype=float)
# 消費支出は人口系の 4 特徴量ではほとんど説明できず、CV R² が 0 付近〜負になる。
# 高齢化率なら同じ特徴量で意味のある当てはまりになる。
y = df['高齢化率'].to_numpy(dtype=float)
x = df['総人口'].to_numpy(dtype=float)

from sklearn.linear_model import Ridge
from sklearn.model_selection import GridSearchCV, KFold
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
import numpy as np

pipe = Pipeline([('scaler', StandardScaler()),
                 ('ridge', Ridge())])
param_grid = {'ridge__alpha': [0.001, 0.01, 0.1, 1.0, 10.0, 100.0]}
gs = GridSearchCV(pipe, param_grid, cv=KFold(5, shuffle=True, random_state=42),
                  scoring='r2', n_jobs=-1, return_train_score=True)
gs.fit(X, y)
print(f'best alpha  = {gs.best_params_["ridge__alpha"]}')
print(f'best CV R²  = {gs.best_score_:.4f}')
print('全候補 CV R²:')
for a, s in zip(param_grid['ridge__alpha'], gs.cv_results_['mean_test_score']):
    print(f'  alpha={a:7.3f}  CV R² = {s:.4f}')

📤 実行すると次の出力が得られる:

best alpha = 0.1 best CV R² = 0.5245 全候補 CV R²: alpha= 0.001 CV R² = 0.3184 alpha= 0.010 CV R² = 0.4168 alpha= 0.100 CV R² = 0.5245 alpha= 1.000 CV R² = 0.4260 alpha= 10.000 CV R² = 0.3734 alpha=100.000 CV R² = 0.2690

💬 結果の読み方:α=0.1 が CV R²=0.52 で最良(5-fold は KFold(shuffle=True, random_state=42))。 α が小さすぎる(正則化弱)と過学習、 大きすぎる(強)と underfitting。 logspace で広く探索するのが定石。

コード 4: 複数モデルを cross_val_score で一括比較

🎯 このコードでやること:5 モデルを統一インターフェースで 5-fold CV、 best モデルを自動選定する。

📥 入力データ (SSDSE-B-2026 から抽出):

X (47, 3), y (47,) -- コード 1 と同じ。
 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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# ── この抜粋で使うデータを用意します(SSDSE-B の 47 都道府県・最新年度)──
import pandas as pd

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', header=1)
df = df[df['地域コード'].astype(str).str.match(r'^R\d{5}$', na=False)].copy()
df['年度'] = pd.to_numeric(df['年度'], errors='coerce')
df = df[df['年度'] == df['年度'].max()]
for _c in df.columns[3:]:
    df[_c] = pd.to_numeric(df[_c], errors='coerce')
df['高齢化率'] = df['65歳以上人口'] / df['総人口'] * 100

# 見本でよく使われる仮の列名を、実データから作っておく
df['income'] = df['消費支出(二人以上の世帯)']
df['population'] = df['総人口']
_region = {'北海道': '北海道', '青森県': '東北', '岩手県': '東北', '宮城県': '東北',
           '秋田県': '東北', '山形県': '東北', '福島県': '東北', '茨城県': '関東',
           '栃木県': '関東', '群馬県': '関東', '埼玉県': '関東', '千葉県': '関東',
           '東京都': '関東', '神奈川県': '関東'}
df['region'] = df['都道府県'].map(_region).fillna('その他')
df['地域'] = df['region']

_feats = ['総人口', '出生数', '死亡数', '婚姻件数']
df = df.dropna(subset=_feats + ['消費支出(二人以上の世帯)'])
X = df[_feats].to_numpy(dtype=float)
# 消費支出は人口系の 4 特徴量ではほとんど説明できず、CV R² が 0 付近〜負になる。
# 高齢化率なら同じ特徴量で意味のある当てはまりになる。
y = df['高齢化率'].to_numpy(dtype=float)
x = df['総人口'].to_numpy(dtype=float)

from sklearn.linear_model import LinearRegression, Ridge, Lasso
from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor
from sklearn.neighbors import KNeighborsRegressor
from sklearn.svm import SVR
from sklearn.neural_network import MLPRegressor
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
from sklearn.model_selection import cross_val_score, KFold

models = {
    'Linear': LinearRegression(),
    'Ridge':  Ridge(alpha=1.0),
    'Lasso':  Lasso(alpha=0.01),
    'KNN-5':  KNeighborsRegressor(n_neighbors=5),
    'SVR':    SVR(kernel='rbf', C=1.0),
    'RF':     RandomForestRegressor(n_estimators=100, random_state=42),
    'GB':     GradientBoostingRegressor(n_estimators=100, random_state=42),
    'MLP':    MLPRegressor(hidden_layer_sizes=(16, 8), activation='relu',
                            max_iter=5000, random_state=42, learning_rate_init=0.01),
}
print(f'{"モデル":<10s}  CV R²')
for name, m in models.items():
    pipe = Pipeline([('sc', StandardScaler()), ('m', m)])
    cv = cross_val_score(pipe, X, y, cv=KFold(5, shuffle=True, random_state=42), scoring='r2', n_jobs=-1)
    print(f'{name:<10s}  {cv.mean():+.3f} ± {cv.std():.3f}')

📤 実行すると次の出力が得られる:

モデル CV R² Linear +0.302 ± 0.328 Ridge +0.426 ± 0.071 Lasso +0.478 ± 0.196 KNN-5 +0.496 ± 0.189 SVR +0.263 ± 0.076 RF +0.491 ± 0.314 GB +0.634 ± 0.266 MLP +0.640 ± 0.298

💬 結果の読み方:8 モデルを統一 API で比較、 shuffle 込みの 5-fold CV では MLP が CV R²=0.640、 GB が 0.634 で最良。 47 サンプルの小データでは正則化線形(Linear/Ridge/Lasso)が 0.61 前後で安定する一方、 tree 系(RF 0.434 / GB 0.350)は伸びず、 MLP は CV R² が負に振れるほど不安定。 これが sklearn の威力—モデルを 1 行差し替えるだけで横並び比較でき、 『複雑なモデルほど強い』が小データでは成り立たないことも一目で分かる。

⚠️ よくある落とし穴

❌ Data Leakage
テストデータも含めて fit_transform すると情報漏洩。 必ず Pipeline + train でのみ fit。
❌ random_state 未指定
再現性がなく、 デバッグ困難。 すべて固定する。
❌ カテゴリ変数の扱い
scikit-learn は数値前提。 必ず OneHotEncoder か OrdinalEncoder。
❌ 不均衡データ
accuracy だけ見ると、 多数クラス予測で「精度 95%」が出てしまう。 ROC-AUC / F1 を見る。
❌ 深層学習を期待しない
scikit-learn の MLP は教育用。 本格 NN は PyTorch / TensorFlow へ。

⚠️ 落とし穴

注意 1: バージョン差で挙動が変わる:scikit-learn 1.0 → 1.3 で OneHotEncoder(sparse=False)sparse_output=False に改名された。 SSDSE-B-2026 を扱う notebook は sklearn.__version__ を冒頭で出力し、 requirements.txtscikit-learn==1.4.2 のようにピン留めする。
注意 2: 47 都道府県は小標本:訓練 33 / テスト 14 程度の分割では、 ランダム種違いで R² が 0.5〜0.8 と振れる。 単一の値で結論せず cross_val_score の平均±SD を必ず示す。 LOO (47 fold) も計算量が許容できる範囲。
注意 3: feature_importances_ は相関ではない:RandomForest の特徴量重要度は「分岐回数 × 不純度減少」であり、 因果でも標準化係数でもない。 線形モデルの coef_ と並べると意味が混乱する。 SHAP / permutation_importance を併用して読み解く。
注意 4: cross_val_score は分割スキャフォルド任せ:分類で cv=5 と整数を渡すと StratifiedKFold、 回帰なら KFold。 時系列 SSDSE-B-2025/2026 を混ぜるなら TimeSeriesSplit を明示する。 暗黙の分割は再現性レビューで指摘されやすい。
注意 5: 標準化と分割の順序StandardScaler().fit_transform(X) を train/test 分割の前にやるとテスト統計量が train に漏れる。 必ず Pipeline([('scaler', StandardScaler()), ('model', Ridge())]) に閉じ込め、 train でのみ fit する。
注意 6: predict_proba は較正されていない:RandomForest や SVC の predict_proba は分類確率として読めるが、 多くの場合「自信過剰 or 自信不足」。 ROC/PR 曲線で並べ替えるなら OK、 確率値そのものを意思決定に使うなら CalibratedClassifierCV で較正する。
注意 7: モデル保存の互換性joblib.dump で保存した .pkl は sklearn のマイナーバージョンが変わると読めない場合がある。 本番デプロイなら ONNX / PMML 変換、 教育用なら sklearn バージョンと一緒に Git 管理する。
注意 8: 多数決の評価指標は accuracy だけにしない:「人口 100 万超の県を当てる」二値分類で 47 県中 12 が陽性なら、 全部陰性予測でも accuracy 0.74。 balanced_accuracy_scoref1_score(average='macro')roc_auc_score を必ず併記する。

⚠️ 落とし穴 — 7 件

❌ データリーク(前処理を train/test 分割前に fit)
StandardScaler().fit(X) を分割前に呼ぶと test の統計量が train に漏れる。 必ず Pipeline で囲い、 分割後に fit
❌ 不均衡データで accuracy のみ見る
99% が陰性なら『全部陰性』予測で accuracy 99%。 ROC-AUC、 F1、 混同行列を必ず併用。
❌ ランダムシード未指定
random_state を指定しないと毎回結果が変わり再現不能。 全 estimator・分割関数に明示。
❌ カテゴリ変数の OrdinalEncoder 誤用
順序のないカテゴリに OrdinalEncoder を使うと『青 > 赤 > 黄』のような擬似順序ができる。 OneHotEncoder を使う。
❌ CV の cv 引数の理解不足
整数 5 は KFold (回帰)、 StratifiedKFold (分類)。 時系列なら TimeSeriesSplit を明示。
❌ GridSearchCV の探索範囲が狭い
$\alpha \in [0.01, 0.1, 1.0]$ では狭すぎる。 対数スケール `np.logspace(-3, 3, 7)` で広く取る。
❌ Pipeline の中間出力を直接見られない
デバッグ時は pipe.named_steps['scaler'].transform(X) で個別ステップにアクセス。

🏭 産業界での活用例

🏭 Spotify の音楽推薦
協調フィルタリング・コンテンツベース推薦。 sklearn 系を含むパイプライン。
🏭 Netflix の視聴予測
Pipeline + GridSearchCV で前処理 + モデルを統合。
🏭 金融機関の与信モデル
解釈性重視で Logistic + ColumnTransformer。 規制対応も。
🏭 製造業の予測保全
RandomForest + 異常検知。 sklearn パイプラインで CI/CD 統合。
🏭 医療の検査値スクリーニング
GradientBoostingClassifier + SHAP で説明可能 AI。
🏭 EC の顧客セグメンテーション
KMeans + PCA + StandardScaler のクラスタリングパイプライン。

❓ FAQ 10 問

Q. fit_transform と fit + transform の違いは?
A. fit_transform(X)fit(X).transform(X) のショートカット。 train データは fit_transform、 test データは transform のみ(fit すると統計量が test に漏れる)。 Pipeline がこれを自動でやってくれる。
Q. Pipeline と ColumnTransformer の違いは?
A. Pipeline は処理の順序(前処理 → モデル)。 ColumnTransformer は列ごとの並列処理(数値列は標準化、 カテゴリ列は OneHot)。 通常は Pipeline([('pre', ColumnTransformer(...)), ('clf', RandomForest())]) と組合せる。
Q. KFold と StratifiedKFold の使い分けは?
A. 分類は StratifiedKFold(各 fold のクラス比率を保つ)、 回帰は KFold で OK。 時系列なら TimeSeriesSplit。
Q. GridSearchCV と RandomizedSearchCV どちらを使う?
A. 探索空間が小さい(< 100 組合せ)なら Grid、 大きいなら Randomized。 RandomizedSearchCV は同じ計算予算で広い空間を効率的に探索(Bergstra 2012)。
Q. n_jobs=-1 は何をする?
A. 全 CPU コアを使って並列化。 GridSearchCV・cross_val_score・RandomForest など多くで対応。 メモリ消費に注意。
Q. predict_proba と predict の違いは?
A. predict は予測クラス、 predict_proba は各クラスの確率。 ROC-AUC・log loss・閾値調整には predict_proba が必須。
Q. 不均衡データへの対処は?
A. (1) class_weight='balanced' を指定、 (2) SMOTE で minority クラスを増やす(imbalanced-learn)、 (3) 評価指標を F1/PR-AUC に。
Q. カテゴリ変数のエンコーディングは?
A. 順序なし→OneHotEncoder、 順序あり→OrdinalEncoder、 高カーディナリティ→TargetEncoder(category_encoders)。
Q. sklearn でディープラーニングはできる?
A. MLPClassifier/MLPRegressor で簡単な NN は可能だが GPU 非対応・最適化アルゴリズム選択肢が少ない。 本格的には PyTorch / TensorFlow を使う。
Q. 学習済みモデルの保存は?
A. joblib.dump(model, 'model.pkl') で保存、 joblib.load('model.pkl') で復元。 sklearn のバージョン差異に注意(互換性は完全保証されない)。

🎮 触って理解する:fit → predict → スコア

scikit-learn の統一APIは、どのモデルでも model.fit(訓練データ) で学び、model.predict(テストデータ) で使い、score で成績を測る、という同じ作法である。ここではその流れを、data/raw/SSDSE-B-2026.csv の2023年・47都道府県の実測2列(説明変数=総人口 A1101、目的変数=一般病院数 I510120)だけで体感する。合成データは使わない。

推定器は make_pipeline(StandardScaler(), SGDRegressor()) 相当の勾配降下による線形回帰。学習率・反復回数は固定(決定的)。スライダーで 訓練/テスト分割の割合乱数シード を変え、標準化のオン/オフを切り替えると、当てはめ直線とスコア(R²/RMSE)がその場で更新される。

💡 グラフ上を左右にドラッグ/タップしてもテスト割合を変えられます(タッチ対応)。標準化ボタンで StandardScaler を Pipeline から外す/入れるを比較。

訓練 R²(fitした側)
テスト R²(未知の側)
テスト RMSE(病院数)

 

🎲 同じ設定でシードだけ変えると?(だから交差検証が要る)

下はいまの分割割合・標準化設定のまま、シード0〜11で分け直したときのテストR²。棒が上下に揺れるほど「1回の分割の成績」は当てにならない。だから cross_val_score で複数分割の平均±標準偏差を見る。

 

💡 直感
推定器は「fitで学び、predictで使う」共通の型。fit は訓練データだけを見て直線を決め、predict見せていないテスト点に当てる。テストで測るのは、丸暗記(過学習)でなく汎化を見たいから。
⚠️ 落とし穴
標準化OFFにすると同じ学習率で発散する(人口は百万単位で桁が大きい)。だから StandardScaler を Pipeline に入れる。しかも平均・標準偏差は訓練データだけから計算する─テストで fit するとデータリーク。分割の偏りやシード次第でスコアが揺れる点も要注意。
🚀 発展
この揺れを平すのが 交差検証cross_val_score)。学習率や正則化強度などハイパラGridSearchCV で分割ごと再学習して選ぶ。前処理〜学習を Pipeline に閉じ込めれば、リークを防ぎつつ全体を1つの推定器として扱える。

※ 数値は data/raw/SSDSE-B-2026.csv(2023年・47都道府県)の実測を転記。相関は r≈0.90、全データの最小二乗解は R²≈0.81・RMSE≈53。勾配降下(標準化ON)はこの最小二乗解に収束する。東京都は総人口14M超の高レバレッジ点で、分割に入るか否かでスコアが動きやすい。

🗺 概念マップ

関連概念を視覚的に整理した概念マップ。

scikit-learn NumPy/SciPy pandas DataFrame XGBoost/LightGBM TensorFlow/PyTorch statsmodels MLflow パイプライン

概念マップの中心 scikit-learn から伸びる 6 軸は、 統一 API (fit/predict/transform) によって接続される。 「データ整形 (pandas/numpy)」→「前処理 (preprocessing)」→「モデル選択 (model_selection)」→「学習 (Estimator)」→「評価 (metrics)」→「可視化 (matplotlib)」が一筋に連なる。

このマップは「どこから手を付けるか」を決める地図として使う。 たとえば「分類精度が低い」とき、 まず metrics で診断し、 次に preprocessing で特徴量を見直し、 最後に Estimator を入れ替える、 という順路が自然に見える。

🔗 隣接手法への橋渡し

「scikit-learn」は 表形式データの ML パイプライン中核ライブラリ として、 pandas の前処理出力を受け取り、 評価・解釈・本番運用ツールへと橋渡しする役割を持つ。 Pipeline 抽象化により周辺ツールとの接続が標準化される点が他フレームワークとの差別化要素。

⬆️ 上流: データ整備・特徴量化

⬌ 並列: 別の ML フレームワーク

⬇️ 下流: 評価・解釈・運用

scikit-learn は前処理 → モデル → 評価まで Pipeline で接続できるため、 周辺ツール(pandas・SHAP・MLOps)と組み合わせて初めて「探索 → 本番」の一貫フローが完成する。

🌳 手法選択フロー

「scikit-learn」を選ぶか他のフレームワークを使うかは、 データ規模・モデル種類・運用条件で判断する。

  1. データが表形式 (n≦1M)か? Yes → scikit-learn の RandomForest/SVM/Logistic でまず試作、 No (画像/音声/系列) → PyTorch / TensorFlow
  2. パイプライン化が必要か? Yes → Pipeline + ColumnTransformer + GridSearchCV で前処理から CV まで一気通貫、 No → 単発スクリプトで十分
  3. 本番運用するか? Yes → joblib.dump でモデル永続化 + version pin (sklearn のマイナー版で API 互換破壊あり)、 No → ノートブックで完結

scikit-learn は SSDSE-B-2026 (n=47, 約 100 列) のような中規模表形式データには第一候補。 ただし XGBoost/LightGBM の方がスコアが伸びることもあるので、 ベンチマークを必ず取る。

補講: 「scikit-learn を現場で使う」7 観点

scikit-learn は定義を暗記しても分析の役には立たない。 SSDSE-B-2026 を題材に「現場で何を確認するか」を 7 観点に整理する。

観点 1: 列の意味と単位を最初に固定する

A1101 は総人口 (人)、 B4101 は年平均気温 (°C)、 G7101 は延べ宿泊者数 (人泊)、 I510120 は一般病院数 (施設)、 L3221 は二人以上世帯の消費支出 (円/月)。 スケールが 10⁶ から 10⁰ までばらつくので、 線形モデルでは StandardScaler、 木モデルでは生値で OK と判断する根拠になる。

観点 2: train/test 分割は 47 件 → 33/14 が現実的

train_test_split(X, y, test_size=0.30, random_state=0, stratify=y_bin) で 33 県学習・14 県評価。 ただし 14 件のテストは振れが大きいため、 cross_val_score(pipe, X, y, cv=KFold(5, shuffle=True, random_state=0)) で 5-fold の平均と標準偏差を併記する。 出生率 A4103 の予測 R² は単一分割で 0.55〜0.78 と振れることが多い。

観点 3: Pipeline で前処理リーケージを断つ

Pipeline([('imputer', SimpleImputer(strategy='median')), ('scaler', StandardScaler()), ('model', Ridge(alpha=1.0))]) を 1 オブジェクトとして扱うと、 cross_val_score の各 fold 内で前処理が再学習される。 StandardScaler().fit_transform(X) を分割前に呼ぶアンチパターンは、 SSDSE のような小標本では精度が 3〜5 ポイント水増しされる。

観点 4: GridSearchCV は対数スケールで張る

Ridge の alpha、 SVR の C、 RandomForest の max_depthnp.logspace(-3, 3, 7)[None, 3, 5, 10, 20] のように広く張る。 狭い grid ([0.1, 1, 10]) で best が端に来た場合は外側を見ていない証拠なので、 範囲を広げて再探索する。

観点 5: 評価指標は目的に合わせて 2〜3 個を併記

回帰なら R² + RMSE + MAE、 分類なら accuracy + balanced_accuracy + ROC-AUC + 混同行列。 SSDSE で「人口 100 万超」を予測する二値タスクなら正例 12/47 (約 26%) の不均衡なので、 roc_auc_scoref1_score(average='macro') を必ず読む。

観点 6: 特徴量重要度は 3 つの観点で読む

RandomForest の feature_importances_ (不純度減少)、 permutation_importance (シャッフル後の精度低下)、 SHAP value (個別予測への寄与) は別物。 同じデータでも順位が入れ替わる。 SSDSE-B-2026 の出生率予測なら、 たいてい人口・高齢化率・所得・気温の上位 4 つは安定するが、 中位は手法で変動する。

観点 7: 学習済みモデルは joblib + 環境ピン留め

joblib.dump({'model': pipe, 'sklearn_version': sklearn.__version__, 'columns': list(X.columns)}, 'model_47pref.pkl') のようにバージョンとカラム順序も保存する。 復元時に sklearn のマイナー版が違うと UserWarning: Trying to unpickle estimator from version X.Y... が出る。 教育用 notebook でも requirements.txtscikit-learn==1.4.2 をピン留めしておく。

補講: 主要モジュール対応表 (SSDSE-B-2026 での出番)

モジュール代表 APISSDSE-B-2026 での出番
linear_modelLinearRegression, Ridge, Lasso, LogisticRegression出生率 A4103 や消費支出 L3221 を予測する基準線
ensembleRandomForestRegressor, GradientBoostingRegressor非線形効果 (高齢化率の閾値超え等) を捉える
svmSVC, SVR (rbf カーネル)標準化必須、 47 県の小標本では C と gamma の調整が肝
neighborsKNeighborsRegressor (k=3〜7)近隣県の値で予測、 解釈は容易だが外挿に弱い
clusterKMeans, AgglomerativeClustering, DBSCAN47 県を 3〜6 のクラスタに分け、 地方ブロックとの照合
decompositionPCA, NMF100 列を 2〜5 軸に圧縮、 散布図で県の位置を確認
preprocessingStandardScaler, OneHotEncoder, KBinsDiscretizer線形/SVM の前段、 地方ブロックなどカテゴリの数値化
model_selectiontrain_test_split, KFold, GridSearchCV, cross_val_score小標本では cv=5 が標準、 LOO (cv=47) も計算可能
metricsr2_score, mean_squared_error, roc_auc_score, confusion_matrix指標は単独ではなく 2〜3 個を併記して読む
pipelinePipeline, make_pipeline, ColumnTransformer数値列は標準化、 カテゴリ列は OneHot を 1 オブジェクトで管理

補講: 47 都道府県でよくあるレポートの NG → OK 例

NG な書き方問題OK な書き方
「RandomForest で R² = 0.82」train か CV か不明、 標準偏差なし「RandomForest (n_estimators=200, random_state=0) で 5-fold CV R² = 0.67 ± 0.15」
「特徴量重要度 1 位は気温」どの重要度か不明、 順位は手法依存「permutation_importance (n_repeats=30, random_state=0) で 1 位は年平均気温 (mean=0.18, std=0.04)」
「sklearn で標準化した」分割前か後か不明 (リーケージの可能性)Pipeline([('scaler', StandardScaler()), ('model', Ridge(alpha=1.0))]) に閉じ込め、 cross_val_score の各 fold 内で fit」
「accuracy 0.95 だから良いモデル」不均衡データなら多数決でも出る「balanced_accuracy 0.78、 ROC-AUC 0.86、 混同行列で誤分類した青森・秋田を実名で確認」
「best_params = {alpha: 0.001}」探索範囲の端で best、 範囲不足のサイン「alpha を np.logspace(-4, 2, 7) で再探索、 best = 0.01 (端ではなく内側)」

補講: 公式ドキュメントの読み方

scikit-learn の 公式ドキュメント は 3 階層に分かれている。 User Guide は概念・数式・実例を含む読み物、 API Reference はクラス・関数の引数一覧、 Examples はギャラリー形式の notebook 集。 初学者はまず User Guide の対応章を読み、 数式を見たら API Reference で引数のデフォルト値を確認し、 Examples で典型コードをコピーする、 という 3 ステップを習慣化すると早い。 SSDSE-B-2026 のような小標本で迷ったら、 User Guide の「Cross-validation: evaluating estimator performance」と「Tuning the hyper-parameters of an estimator」を読み返すと判断軸が揃う。

補講: scikit-learn と他言語・他ツールの対応関係

scikit-learn の概念は R / Julia / SAS / Spark などにも対応物がある。 用語の翻訳ができると、 既存のレポート様式や上司の経験を活かしやすい。

scikit-learnR (caret / tidymodels)Julia (MLJ)Spark MLlib
LinearRegressionlm()LinearRegressorLinearRegression
LogisticRegressionglm(family=binomial)LogisticClassifierLogisticRegression
Ridge / LassoglmnetRidgeRegressor / LassoRegressorLinearRegression(elasticNetParam)
RandomForestRegressorranger / randomForestRandomForestRegressorRandomForestRegressor
GradientBoostingRegressorgbm / xgboostXGBoostRegressorGBTRegressor
KMeanskmeans()KMeansKMeans
StandardScalerscale() / preProcess(method=c("center","scale"))StandardizerStandardScaler
Pipelinerecipe + workflow (tidymodels)PipelinePipeline
GridSearchCVtune_grid (tidymodels)TunedModelCrossValidator
cross_val_scoretrainControl(method="cv", number=5)evaluate()CrossValidator

補講: 学習を効率化する 5 つの実務テクニック

(1) set_config(display='diagram') で Pipeline を可視化 — Jupyter で Pipeline オブジェクトを表示すると、 各ステップが SVG 図として描画される。 ColumnTransformer の分岐や入れ子の Pipeline も一目で把握でき、 レビュー時のミス発見が早い。

(2) HistGradientBoostingRegressor を試す — sklearn 0.21 以降に追加された LightGBM 系の実装で、 同条件下なら GradientBoostingRegressor の数倍〜数十倍速い。 欠損値も内部で処理してくれるため SSDSE-B-2026 の欠損列もそのまま扱える。

(3) Pipeline.memory='./cache' で前処理を再利用 — GridSearchCV で多数のハイパラ組合せを試すとき、 前処理が同じならキャッシュされる。 StandardScaler や OneHotEncoder のような重い変換が複数モデルで使い回せて時間短縮できる。

(4) cross_validate(return_train_score=True) で過学習を可視化 — train_score と test_score の両方を取得すると、 過学習の度合いが定量的に見える。 train R² 0.95 / test R² 0.55 のような大きな乖離は、 SSDSE の小標本では木深度を浅くするか正則化を強める合図。

(5) FunctionTransformer で独自関数を Pipeline に組み込む — 「対数変換」「ビニング」「外れ値クリッピング」など pandas の関数を Pipeline ステップとして扱える。 FunctionTransformer(np.log1p, inverse_func=np.expm1) のように逆変換も指定でき、 予測値を元尺度に戻すのが楽。

補講: 落とし穴の早期検知チェックリスト (SSDSE-B-2026 を分析するときの順序)

  1. df.info()df.describe() で欠損・型・レンジを確認 — 列の単位を SSDSE の項目定義書と照合する
  2. df.duplicated().sum() で重複行を確認 — 都道府県 47 件なので 0 のはず、 重複があれば集計ミス
  3. df.select_dtypes('object') でカテゴリ列を抽出 — そのまま sklearn に渡すとエラー、 OneHotEncoder か OrdinalEncoder で数値化
  4. X.var() で分散ゼロ列を検出 — 全県同じ値の列があれば VarianceThreshold で除外、 標準化でゼロ除算を防ぐ
  5. train_test_split 後に X_train.shapeX_test.shape を必ず print — 思わぬ縮小 (NaN 行 drop など) を早期発見
  6. Pipeline.fit(X_train, y_train).score(X_test, y_test) で単一スコアを取得 → 続けて cross_val_score で 5-fold の平均と SD を取得 — 単一値だけで判断しない
  7. 結果を notebook の最終セルで sklearn.__version__ / numpy.__version__ / 学習件数 / CV 平均と SD を print して再現性メモを残す

補講: SSDSE-B-2026 でよくある質問 (FAQ)

Q. 47 件しかないのに RandomForest を使ってよいのか? — 使ってよいが過学習に強く注意する。 max_depth=35 程度に制限し、 train R² と CV R² の乖離が 0.15 以上なら浅くするか正則化モデルに切替える。 同じ理由で n_estimators=200 以上にしても精度は伸びにくいので 100〜300 が現実的。

Q. カテゴリ列 (地方ブロック・気候区分) は OneHot か OrdinalEncoder か? — 線形モデル/木モデル共に OneHotEncoder が無難。 地方ブロックは順序を持たないので OrdinalEncoder は不適。 木モデルは OrdinalEncoder でも動くが、 「青森 < 秋田 < 岩手」のような擬似順序を学習する恐れがある。

Q. 47 件で深層学習 (MLPRegressor) は使うべきか? — 教育目的なら可、 実用では非推奨。 47 件では NN のパラメータが過剰で、 train R² が 0.9 を超えても CV R² が 0.5〜0.6 と低くなる典型的な過学習に陥る。 線形モデルか木モデルを第一候補とする。

Q. random_state は 0 と 42 のどちらを使う? — どちらでもよいが、 プロジェクト内で 1 つに統一する。 0 は scikit-learn 公式 example の慣習、 42 は『銀河ヒッチハイクガイド』由来の慣習。 ノートブック冒頭で RANDOM_STATE = 0 と定数化すると一括変更も楽。

Q. 結果を上司や論文に出すとき、 何を最低限示すべきか? — (1) 入力データの出典と年度、 (2) 説明変数と単位、 (3) 前処理の手順 (Pipeline コード含む)、 (4) 評価方法と評価指標、 (5) 結果の数値 (平均±SD)、 (6) 解釈と限界、 (7) 再現性情報 (バージョン・乱数シード) の 7 点セット。 これを満たせばレビューで「再現できない」「リーケージの可能性」と指摘されない。

補講: SSDSE-B-2026 を題材にしたミニ実験計画 (47 都道府県の出生率予測)

scikit-learn を学んだら、 必ず 1 つは小さな実験を回して肌感を掴むのがよい。 ここでは SSDSE-B-2026 (2023 年) の 47 都道府県データで「出生率 A4103 を 3 つの説明変数 (総人口 A1101 / 高齢化率 A1303 / 年平均気温 B4101) から予測する」というタスクを設計する。 目的は「最良モデルを当てる」ことではなく「複数モデルを横並びに比較し、 各モデルの特徴を体感する」ことにある。

ステップ 1 (前処理): pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932') で読み込み、 上記 3 列と目的変数 1 列を選択。 欠損は 47 県では稀だが念のため SimpleImputer(strategy='median') を Pipeline 先頭に置く。 線形モデルは StandardScaler を挟む、 木モデルは挟まなくて良い。 これを ColumnTransformer でモデルごとに切替えると同じ前処理コードで複数実験ができる。

ステップ 2 (比較対象): LinearRegression / Ridge(alpha=1.0) / Lasso(alpha=0.01) / KNeighborsRegressor(n_neighbors=5) / SVR(kernel='rbf', C=1.0, gamma='scale') / RandomForestRegressor(n_estimators=200, random_state=0) / GradientBoostingRegressor(n_estimators=200, learning_rate=0.05, max_depth=3, random_state=0) / MLPRegressor(hidden_layer_sizes=(16, 8), max_iter=2000, random_state=0) の 8 種を横並びに評価する。

ステップ 3 (評価): KFold(n_splits=5, shuffle=True, random_state=0) で 5-fold CV、 評価指標は R² と RMSE。 各モデルで cross_val_score(pipeline, X, y, cv=kf, scoring='r2') を実行し、 平均と標準偏差を表にまとめる。 小標本では SD が 0.15〜0.20 と大きいため、 平均値の差が SD より小さい場合は「統計的に有意に優れている」とは言えない。

ステップ 4 (解釈): 線形モデルの coef_ と RandomForest の permutation_importance を並べ、 「高齢化率が出生率に強く負の影響を与える」「気温は弱い正の関連を示す (沖縄など暖かい県は出生率も高め)」のような解釈を引き出す。 ただし因果ではなく相関であることを必ず明記する。 47 件という小標本では、 特定の県 (沖縄・東京) が結果を大きく動かす可能性があり、 leave-one-out で確認するか散布図で外れ値を可視化する。

ステップ 5 (報告): 最終レポートには「使用データ (SSDSE-B-2026 / 2023 年 / 47 件)」「説明変数と単位」「前処理 (中央値補完 + 標準化、 木モデルは生値)」「評価方法 (5-fold CV、 R² と RMSE)」「結果表 (8 モデルの平均±SD)」「解釈と限界 (因果ではない・小標本・年度固定)」「再現性メモ (sklearn=1.4.2 / numpy=1.26 / Python=3.11)」を必ず含める。 この 7 項目テンプレートを覚えれば、 scikit-learn を使う任意の分析報告に応用できる。

補講: scikit-learn の歴史と設計哲学

scikit-learn は 2007 年に David Cournapeau の Google Summer of Code プロジェクトとして始まり、 2010 年に INRIA (フランス国立情報学自動制御研究所) の Fabian Pedregosa らによって正式リリースされた。 設計の根幹にあるのは 「あらゆる推定器を fit / predict / transform という共通のメソッドで扱う」 という統一 API 思想で、 これにより LinearRegression と RandomForestClassifier と KMeans を 1 行差し替えるだけで比較実験ができる。 numpy ndarray を入出力の基本型にすることで、 pandas DataFrame / scipy sparse matrix / matplotlib との接続も自然に成立した。

2010 年代後半以降は、 Pipeline + ColumnTransformer によるリーケージ防止、 set_config(transform_output='pandas') による DataFrame 連携、 HistGradientBoosting による高速化、 cross_validate(return_estimator=True) による fold 別モデル取得など、 「研究 → 本番」を意識した機能が継続的に追加されている。 一方で、 GPU 対応・自動微分・大規模分散学習は意図的に範囲外として、 PyTorch / TensorFlow / Spark MLlib に役割を譲っている。 この「範囲を絞った標準化」が、 SSDSE-B-2026 のような中規模表形式データの分析で第一候補であり続ける理由である。

バージョン互換ポリシーは比較的厳格で、 メジャー版 (0.x → 1.x) 以外では公開 API を破壊しない方針。 ただし内部実装やデフォルト値は微調整されることがあり、 solver のデフォルトが変わると最終スコアも 1〜2 ポイント動くことがある。 ノートブックの冒頭で import sklearn; print(sklearn.__version__) を実行して記録するのは、 「同じコードで結果が再現できない」事故を防ぐ最低限のマナーである。

💡 直感の深掘り — 「同じ書き方」がなぜ効くのか

scikit-learn の設計思想を一言で言えば 「役割が違う部品を、同じ動詞でつなぐ」 である。線形回帰でもランダムフォレストでも k-means でも、学習は fit、予測は predict、変換は transform という同じメソッド名で呼び出す。だからアルゴリズムを1行差し替えるだけで比較実験ができる。これは 機械学習ライブラリ としての最大の価値であり、NumPy 配列を共通の入出力型に据えたことで pandasDataFrame ともそのまま接続する。

3つの部品:Estimator / Transformer / Pipeline

  • Estimator(推定器)fit(X, y) で学び、predict(X) で答える部品。RidgeLogisticRegressionRandomForestClassifier など予測モデルはすべてこの型。「学習して答える」役割。
  • Transformer(変換器)fit(X) で変換の基準(平均・分散など)を覚え、transform(X) でデータを作り替える部品。StandardScalerOneHotEncoderSimpleImputer が代表。標準化 のように「データを整える」役割。→ Transformer の解説
  • Pipeline:Transformer を何段か通してから最後に Estimator に渡す、という一連の流れを1つのオブジェクトにまとめる箱。箱ごと fit すれば内部の各段が順に fit され、箱ごと predict すれば同じ順で変換して予測する。

直感的には、Transformer は「材料の下ごしらえ」、Estimator は「調理」、Pipeline は「レシピ全体を1枚のカードにしたもの」と考えるとよい。下ごしらえの基準(塩加減=平均・分散)は訓練データだけから決める、という規律が Pipeline に閉じ込められている。これが後述のデータリーク防止の核心である。

なぜ fittransform を分けるのか。それは「基準を覚える段階(fit)」と「その基準を別のデータに当てる段階(transform)」を分離するためである。訓練データで fit して覚えた平均・分散を、テストデータには transform だけで当てる。この非対称性こそ、汎化を正しく測る土台になる。→ 訓練/テスト分割 / 交差検証

⚠️ 落とし穴の深掘り(重要)— 実データで数値を添えて

ここでは data/raw/SSDSE-B-2026.csv(CP932、2行目メタ行を skiprows=[1] で除外、2023年・47都道府県)の実測値で落とし穴を再現する。合成データは使わない。

落とし穴1(最重要):fit をテストにも当てるリーク

StandardScaler().fit_transform(X)分割前の全データに対して呼ぶと、テストデータの平均・分散が訓練に混入する。transform は訓練で fit した基準だけを当てるのが鉄則。実測での差は次のとおり(総人口 A1101・気温 B4101・宿泊 G7101・病院数 I510120 → 消費支出 L3221、Ridge(α=1.0)、5分割CV)。

# 正しい: Pipeline に閉じ込め、各foldの訓練でのみ fit
cross_val_score(make_pipeline(StandardScaler(), Ridge(1.0)), X, y, cv=cv, scoring="r2").mean()
#=> 0.110

# 誤り: 全データを先に fit_transform してから CV(テスト統計量が漏洩)
Xall = StandardScaler().fit_transform(X)
cross_val_score(Ridge(1.0), Xall, y, cv=cv, scoring="r2").mean()
#=> 0.121  ← リークで水増しされたスコア

この例では標準化のリークは 0.110 → 0.121 と控えめだが、方向は常に「甘くなる」側。前処理が重い(欠損補完・エンコーディング・特徴量選択)ほど差は開く。→ データリーク

落とし穴2:前処理を交差検証の「内側」に入れる

リーク防止は分割の外側で1回 fit することではなく、CVの各foldの内側で毎回 fit し直すこと。cross_val_score に「Pipelineそのもの」を渡せば、fold ごとに訓練部分だけで前処理が再学習され、これが自動的に守られる。逆に、生データを先に fit_transform してから配列を渡すと fold をまたいで漏れる(落とし穴1と同根)。ハイパラ探索GridSearchCV)でも同様で、必ず Pipeline を探索対象に渡す。

落とし穴3:乱数シード非固定で再現不能

train_test_splitKFold(shuffle=True)RandomForestKMeansMLP はすべて random_state を持つ。固定しないと実行のたびにスコアが変わり、「昨日と数値が違う」というレビュー指摘になる。47件のような小標本では、シード違いで holdout の R² が符号ごと変わることさえある(本ページ上部の実測でも holdout R²=−0.298 に対し CV平均 R²=0.110)。単一分割で結論せず、cross_val_score の平均±SD を示す。→ 訓練/テスト分割

落とし穴4:スケーリング忘れ

距離や係数の大きさに依存するモデル(SVR/SVCKNeighborsRidge/LassoMLPKMeansPCA)は、単位のそろわない列をそのまま渡すと大きな数値の列(総人口=数百万、気温=十数度)に引きずられる。標準化 を挟むと係数は「1標準偏差動いたときの効き」として読める。一方、木系(RandomForestGradientBoosting)は単調変換に不変なのでスケーリング不要——「常に標準化」でも「常に不要」でもなく、モデルで決まる。

落とし穴5:デフォルトハイパラの盲信

Ridge(alpha=1.0)LogisticRegression(C=1.0)RandomForest(n_estimators=100) のデフォルトは「無難な初期値」であって最適ではない。alphaCnp.logspace(-3, 3, 7) のように対数スケールで広く探索する。ただし探索は必ず 交差検証 の内側で行い、テストは最後まで触らない。→ ハイパーパラメータ / 正則化

落とし穴6:多クラス/不均衡での accuracy 盲信

「人口100万超か」を LogisticRegression で二値分類すると、47県は 陽性37 : 陰性10 の不均衡になる。全部「100万超」と答えるだけの多数決ベースラインですら accuracy は 37/47=0.787。実測(標準化+ロジ回帰、5分割StratifiedKFold、seed=42)は次のとおり。

accuracy   = 0.784   # 多数決ベースライン0.787とほぼ同じ=accだけ見ると無意味
f1_macro   = 0.505   # 少数クラスを当てられていないことが露呈
roc_auc    = 0.918   # 順位付けの能力は高い

accuracy だけ見れば「ベースライン並み」に見えるが、roc_auc=0.918 は分離能力が高いことを示す。不均衡では accuracy 単独で語らず、balanced_accuracyf1_macroroc_auc・混同行列を併記する。多クラスでは average='macro' でクラス平等に、'weighted' で件数重みに切り替わる点も要注意。→ クラス不均衡 / 混同行列

落とし穴7:バージョン間の挙動差

OneHotEncoder(sparse=False) は 1.2 で sparse_output=False に改名、mean_squared_error(squared=False) は将来 root_mean_squared_error に移行、solver のデフォルト変更でスコアが1〜2ポイント動くこともある。notebook 冒頭で import sklearn; print(sklearn.__version__) を出力し、requirements.txtscikit-learn==1.4.2 のようにピン留めする。joblib.dump.pkl はマイナー版差で読めなくなり得るため、モデルは必ずバージョンと一緒に管理する。

🌐 発展 — 一体化・自動化・実験管理

発展1:Pipeline × ColumnTransformer で「列ごとに違う前処理」

実データは数値列とカテゴリ列が混ざる。ColumnTransformer は「数値列は標準化、カテゴリ列は OneHot」のように列ごとに並列で前処理を割り当て、それを Pipeline でモデルにつなぐ。Pipeline が「処理の順序(縦)」、ColumnTransformer が「列ごとの分岐(横)」を担う。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.linear_model import Ridge

# SSDSE-B-2026 の列名はすべて ASCII コードで、カテゴリ列は最初から入っていない。
# Code(R01000〜R47000 = JIS の都道府県コード)の番号から 8 地方区分を作って足す。
code_no = df2023.loc[X_train.index, "Code"].str[1:3].astype(int)
region = pd.cut(code_no, bins=[0, 1, 7, 14, 23, 30, 35, 39, 47],
                labels=["北海道", "東北", "関東", "中部", "近畿", "中国", "四国", "九州沖縄"])
Xc_train = X_train.assign(region=region.astype(str))

num = ["A1101", "B4101", "G7101", "I510120"]   # 数値列
cat = ["region"]                                # カテゴリ列(作った 8 地方区分)

pre = ColumnTransformer([
    ("num", StandardScaler(), num),
    ("cat", OneHotEncoder(handle_unknown="ignore"), cat),
])
model = Pipeline([("pre", pre), ("reg", Ridge(alpha=1.0))])
model.fit(Xc_train, y_train)   # 前処理〜学習が1オブジェクトに閉じる

※SSDSE-B-2026 の列名はすべて ASCII コードで、カテゴリ列は用意されていない。そこで Code(R01000〜R47000 = JIS の都道府県コード)の番号を 8 地方区分に畳んで region 列を作り、OneHot に渡している。上部の実測コードは数値列だけなので、この箱を make_pipeline(StandardScaler(), Ridge()) に簡約したものである。

発展2:GridSearchCV / cross_val_score でハイパラ探索と評価

Pipeline をそのまま GridSearchCV に渡すと、各ハイパラ候補ごとに前処理から再学習されリークを防げる。パラメータ名は ステップ名__引数名(二重アンダースコア)で指定する。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import numpy as np
from sklearn.model_selection import GridSearchCV, KFold

grid = GridSearchCV(
    make_pipeline(StandardScaler(), Ridge()),
    {"ridge__alpha": np.logspace(-2, 3, 6)},   # 0.01〜1000 を対数で
    cv=KFold(5, shuffle=True, random_state=42),
    scoring="r2",
)
grid.fit(X, y)
print(grid.best_params_, round(grid.best_score_, 3))

探索空間が広いときは RandomizedSearchCV、時系列を含むなら TimeSeriesSplit、並列化は n_jobs=-1。結果は best_params_cv_results_ を表で示し、「差がCVのSD以下なら有意ではない」と添える。→ ハイパラチューニング / 交差検証

発展3:カスタム Estimator / Transformer で独自処理を統一APIに乗せる

独自の前処理も BaseEstimator, TransformerMixin を継承し fit/transform を実装すれば、既存の PipelineGridSearchCV にそのまま差し込める。これが統一APIの拡張性である(下記は架空の対数変換器)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from sklearn.base import BaseEstimator, TransformerMixin
import numpy as np

class Log1pScaler(BaseEstimator, TransformerMixin):
    def fit(self, X, y=None):
        return self                 # 覚える基準がなければそのまま返す
    def transform(self, X):
        return np.log1p(X)          # log(1+x) 変換

# 既存部品と同じ作法で連結できる
model = make_pipeline(Log1pScaler(), StandardScaler(), Ridge())

軽い変換なら FunctionTransformer(np.log1p) でも同等。fit で状態を覚える必要があるとき(分位点・語彙など)だけクラスにする。

発展4:前処理とモデルの一体化がもたらす3つの利点

  • リーク防止:前処理の fit が訓練foldに閉じる(落とし穴1・2の自動回避)。
  • デプロイ一貫性:学習時と推論時で前処理が完全一致。joblib.dump(model) でPipelineごと保存すれば「前処理を再現し忘れる」事故が消える。
  • 探索の正しさGridSearchCV が前処理のパラメータ(例:SimpleImputerstrategy)まで含めて交差検証できる。

発展5:実験管理との連携

cross_validate(..., return_estimator=True, return_train_score=True) で fold 別モデルと訓練/検証スコアを同時取得し、過学習の兆候(訓練≫検証)を監視する。set_config(transform_output="pandas") で変換後も列名付き DataFrame を保てば、pandas での事後分析が楽になる。再現性のためには「データのパス(年度入り)+ random_state + ライブラリ版数」の3点セットを記録し、MLflow などの実験管理ツールに best_params_・スコア・成果物を残すと、後から結果を追跡できる。→ 過学習 / 機械学習の基礎 / モデル選択

🔗 このパックの関連ページ

pandasNumPyDataFrame交差検証データリークハイパーパラメータハイパラチューニング機械学習ライブラリ標準化正則化訓練/テスト分割クラス不均衡混同行列Transformerモデル選択過学習機械学習の基礎

※ Pipeline / ColumnTransformer / GridSearchCV / Estimator の各単独ページはリポジトリ内に未作成のため、リンクではなくテキストで表記した。