0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

scikit-learnってなんだ?〜「fit→predict」の2行で何が起きているのか〜

0
Last updated at Posted at 2026-04-12

この記事の対象読者

  • チュートリアルで model.fit(X, y)model.predict(X_test) と書いたが、中で何が起きているか理解していない方
  • 「教師あり学習」「教師なし学習」の違いを明確に説明できない方
  • 機械学習を始めたいが、PyTorch / TensorFlowとの使い分けがわからない方
  • 「過学習」「交差検証」の概念を感覚的にしか理解していない方

この記事で得られること

この記事を読むと、以下のことが理解できます:

  • scikit-learnの「立ち位置」— PyTorch/TensorFlowとの明確な棲み分け
  • Estimator APIの「設計思想」— なぜ全モデルがfit/predictで統一されているのか
  • 教師あり/教師なし学習の「仕組み」— 分類・回帰・クラスタリングの内部ロジック
  • モデル評価の「正しいやり方」— train/test split、交差検証、評価指標の使い分け
  • Pipelineの「威力」— 前処理からモデル訓練までを1本の流れに統合する方法

この記事で扱わないこと

  • 各アルゴリズムの数学的導出(勾配降下法の数式、カーネル関数の理論等)
  • ディープラーニング(CNN、RNN、Transformer等)の詳細
  • 大規模データでの分散学習(Dask-ML、Spark MLlib等)

本記事ではscikit-learn 1.4〜1.5系を前提としています。コード例はPython 3.11以降で動作確認済みです。


本記事の比喩について

この記事では、scikit-learnの仕組みを天気予報の気象台に見立てて解説する。

scikit-learnの概念 気象台での対応物
特徴量(X) 観測データ — 気温、湿度、気圧、風速などの測定値
ラベル/ターゲット(y) 予報したいもの — 「明日は晴れか雨か」「降水量は何mm か」
モデルの学習(fit) 過去の観測記録からパターンを見つける作業
予測(predict) 今日の観測データから明日の天気を予報する
train/test split 過去データを「パターン学習用」と「予報精度検証用」に分ける
交差検証 複数の異なる期間で検証し、予報精度の信頼性を高める
過学習(overfitting) 過去の天気を丸暗記してしまい、新しい日の予報が外れる
未学習(underfitting) パターンを十分に学べず「毎日晴れ」と予報する
Pipeline 観測→データ整理→予報モデル→結果出力の一連の処理フロー
GridSearchCV 「どの計算パラメータが最も精度が高いか」を総当たりで試す

1. scikit-learnの「立ち位置」— いつ使い、いつ使わないか

1.1 機械学習ライブラリの地図

機械学習のライブラリは山ほどあるが、役割は明確に分かれている。

気象台の比喩で言えば、scikit-learnは地上の気象台だ。温度計、湿度計、気圧計といった「数値データ」を使って天気を予報する。一方、PyTorch / TensorFlow衛星画像やレーダー画像を解析する高度なシステムで、画像・音声・テキストのような非構造化データに対応する。

1.2 scikit-learnを選ぶべき場面

条件 scikit-learn PyTorch/TensorFlow
データが表形式(CSV/DB) 最適 過剰
データ量が数万〜数百万行 最適 過剰
画像・音声・テキスト 不向き 最適
データ量が数億行以上 不向き 最適
GPUが必要 不要 必要
プロトタイプの速度 数行で完成 モデル定義が必要
説明可能性(なぜこの予測か) 高い 低い(ブラックボックス)

「まずscikit-learnで試して、精度が足りなければディープラーニングに進む」が実務の定石です。scikit-learnで十分な精度が出るケースは非常に多く、無理にディープラーニングを使う必要はありません。

scikit-learnの立ち位置が明確になったところで、次はこのライブラリの最大の特徴 — Estimator APIの設計思想を理解しよう。「なぜ全モデルが同じインターフェースなのか」の答えがここにある。


2. Estimator API — 全モデルを貫く「fit → predict」の設計思想

2.1 統一インターフェースという革命

scikit-learnが他のMLライブラリと一線を画す最大の理由は、全てのモデルが同じAPIで動くことだ。

from sklearn.linear_model import LinearRegression
from sklearn.ensemble import RandomForestClassifier
from sklearn.svm import SVC
from sklearn.cluster import KMeans

# どのモデルも同じ3ステップ
# 1. モデルの生成
model = LinearRegression()

# 2. 学習(fit)
model.fit(X_train, y_train)

# 3. 予測(predict)
predictions = model.predict(X_test)

線形回帰でもランダムフォレストでもSVMでも、fitpredict の2行で動く。気象台の比喩で言えば、予報手法が変わっても、入力(観測データ)と出力(予報結果)のフォーマットは同じということだ。気圧配置型予報からAI予報に切り替えても、気象台のオペレーションは変わらない。

2.2 3つのAPI

scikit-learnのオブジェクトは、役割に応じて3つのAPIを持つ。

API メソッド 意味 気象台の比喩
Estimator fit(X, y) データからパターンを学習する 過去の観測記録を分析する
Predictor predict(X) 新しいデータに対して予測する 明日の天気を予報する
Transformer transform(X) データを変換する 生の観測データを整理・加工する
# Estimator + Predictor(教師あり学習モデル)
from sklearn.tree import DecisionTreeClassifier
clf = DecisionTreeClassifier()
clf.fit(X_train, y_train)        # 学習
predictions = clf.predict(X_test) # 予測

# Transformer(データ前処理)
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
scaler.fit(X_train)                       # 平均と標準偏差を学習
X_train_scaled = scaler.transform(X_train) # 学習した統計量で変換
X_test_scaled = scaler.transform(X_test)   # 同じ統計量で変換

# fit + transform を1行で
X_train_scaled = scaler.fit_transform(X_train)

fit_transform はtrain用データにのみ使用してください。testデータには transform だけを適用します。testデータでfitし直すと、データリーク(未来の情報で学習してしまう問題)が発生します。

2.3 fit の中で何が起きているか

model.fit(X, y) の1行が実行されるとき、内部では何が起きているのか。線形回帰を例に見てみよう。

from sklearn.linear_model import LinearRegression
import numpy as np

# サンプルデータ(気温と降水量の関係)
X = np.array([[15], [20], [25], [30], [35]])  # 気温
y = np.array([50, 40, 30, 20, 10])             # 降水量

model = LinearRegression()
model.fit(X, y)

# fit後にモデル内部に保存されたパラメータ
print(f"係数 (coef_):    {model.coef_}")      # [-2.] — 気温が1度上がると降水量が2mm減る
print(f"切片 (intercept_): {model.intercept_}") # 80.0

# predict はこのパラメータを使って計算するだけ
# y = coef_ * X + intercept_ = -2 * 22 + 80 = 36
print(f"気温22度の予測降水量: {model.predict([[22]])}") # [36.]

ポイント: fit は「パラメータの最適化」、predict は「パラメータを使った計算」。この2つは完全に分離されている。

Estimator APIの設計思想がわかったところで、次は機械学習の2大カテゴリ — 教師あり学習と教師なし学習を具体的なコードで理解しよう。


3. 教師あり学習 — 「正解付きデータ」から学ぶ

3.1 分類と回帰

教師あり学習は正解付きデータでパターンを学ぶ手法だ。気象台で言えば、「過去の観測データ(X)と実際の天気結果(y)」のペアから法則を見つける。

タスク 予測対象 気象台の比喩 代表的モデル
分類 カテゴリ(離散値) 明日は「晴れ/曇り/雨」のどれか DecisionTree, RandomForest, SVC, LogisticRegression
回帰 連続値 明日の降水量は何mmか LinearRegression, Ridge, SVR, GradientBoosting

3.2 分類の実践 — アヤメの品種判定

scikit-learn定番のIrisデータセットで分類を実践する。

from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score, classification_report

# 1. データの読み込み
iris = load_iris()
X, y = iris.data, iris.target
print(f"特徴量: {iris.feature_names}")
# ['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)']
print(f"クラス: {iris.target_names}")
# ['setosa' 'versicolor' 'virginica']

# 2. 学習データとテストデータに分割
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42, stratify=y
)
print(f"学習データ: {X_train.shape[0]}件, テストデータ: {X_test.shape[0]}")

# 3. モデルの学習
clf = RandomForestClassifier(n_estimators=100, random_state=42)
clf.fit(X_train, y_train)

# 4. 予測と評価
y_pred = clf.predict(X_test)
print(f"正解率: {accuracy_score(y_test, y_pred):.3f}")
print(classification_report(y_test, y_pred, target_names=iris.target_names))

3.3 回帰の実践 — 住宅価格の予測

from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.metrics import mean_squared_error, r2_score
import numpy as np

# 1. データの読み込み
housing = fetch_california_housing()
X, y = housing.data, housing.target

# 2. 分割
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)

# 3. 学習
reg = GradientBoostingRegressor(n_estimators=200, max_depth=4, random_state=42)
reg.fit(X_train, y_train)

# 4. 予測と評価
y_pred = reg.predict(X_test)
rmse = np.sqrt(mean_squared_error(y_test, y_pred))
r2 = r2_score(y_test, y_pred)
print(f"RMSE: {rmse:.4f}")  # 二乗平均平方根誤差(小さいほど良い)
print(f"R²:   {r2:.4f}")    # 決定係数(1に近いほど良い)

stratify=y は分類タスクのtrain_test_splitで必ず指定してください。これにより、各クラスの比率がtrainとtestで均等になります。偏ったデータ分割は評価結果を歪めます。

教師あり学習の基本がわかったところで、次は「正解がないデータ」からパターンを見つける教師なし学習を解説しよう。


4. 教師なし学習 — 「正解なし」でパターンを見つける

4.1 教師なし学習とは

気象台の比喩で言えば、教師なし学習は過去の天気結果を見ずに、観測データだけから気象パターンを発見する作業だ。「この期間の観測データは似た傾向がある」「こっちのグループは明らかに違うパターンだ」といった構造を、正解ラベルなしで見つけ出す。

タスク 目的 気象台の比喩 代表的モデル
クラスタリング データをグループに分ける 似た気象パターンの日をグループ化 KMeans, DBSCAN, AgglomerativeClustering
次元削減 特徴量を圧縮する 100種類の観測項目を主要3パターンに要約 PCA, t-SNE, UMAP

4.2 クラスタリングの実践

from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler
import numpy as np

# サンプルデータ: 顧客の購買行動(年間購入額、購入頻度)
np.random.seed(42)
X = np.vstack([
    np.random.randn(50, 2) * 0.5 + [2, 2],    # グループA: 少額・低頻度
    np.random.randn(50, 2) * 0.5 + [8, 8],    # グループB: 高額・高頻度
    np.random.randn(50, 2) * 0.8 + [5, 2],    # グループC: 中額・低頻度
])

# スケーリング(KMeansは距離ベースなのでスケール統一が必須)
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

# KMeansでクラスタリング
kmeans = KMeans(n_clusters=3, random_state=42, n_init=10)
kmeans.fit(X_scaled)

labels = kmeans.labels_
print(f"各クラスタのサンプル数: {np.bincount(labels)}")
print(f"クラスタ中心:\n{scaler.inverse_transform(kmeans.cluster_centers_)}")

4.3 次元削減の実践 — PCA

from sklearn.decomposition import PCA
from sklearn.datasets import load_iris

# Irisデータ(4次元)を2次元に圧縮
iris = load_iris()
X = iris.data  # (150, 4) — 4つの特徴量

pca = PCA(n_components=2)
X_pca = pca.fit_transform(X)  # (150, 2) — 2次元に圧縮

print(f"元の次元: {X.shape[1]}D → 圧縮後: {X_pca.shape[1]}D")
print(f"寄与率: {pca.explained_variance_ratio_}")
# 例: [0.7296, 0.2285] — 2次元で約96%の情報を保持
print(f"累積寄与率: {pca.explained_variance_ratio_.sum():.3f}")

ポイント: PCAのexplained_variance_ratio_を見れば、圧縮後にどれだけの情報が保持されているかがわかります。累積で95%以上あれば、ほぼ情報損失なしに次元を削減できています。

教師あり/なし学習の全体像が掴めたところで、次はMLで最も重要なテーマ — モデル評価の正しいやり方を解説しよう。ここを間違えると「精度99%なのに本番で使えない」モデルが出来上がる。


5. モデル評価 — 「精度99%」を信じてはいけない理由

5.1 なぜtrain/test splitが必要なのか

気象台の比喩に戻ろう。過去30年分の気象データでパターンを学んだ予報モデルがある。このモデルの精度を「過去30年分のデータで評価」したら、当然精度は高い。答えを見ながらテストを受けているようなものだ。

from sklearn.model_selection import train_test_split

# 基本のsplit(80:20)
X_train, X_test, y_train, y_test = train_test_split(
    X, y,
    test_size=0.2,      # テストデータの割合
    random_state=42,     # 再現性のためのシード
    stratify=y           # クラス比率を維持(分類タスクのみ)
)

5.2 交差検証 — 1回の分割では不安なとき

train/test splitは1回の分割しかしない。「たまたまテストデータが簡単だっただけ」という可能性を排除できない。

交差検証(Cross-Validation)は、データをK個に分割して、K回のテストを行う。気象台で言えば、「2020年を予報検証 → 2021年を予報検証 → 2022年を予報検証 → ...」と複数の期間で精度を確認する方法だ。

from sklearn.model_selection import cross_val_score
from sklearn.ensemble import RandomForestClassifier

clf = RandomForestClassifier(n_estimators=100, random_state=42)

# 5分割交差検証
scores = cross_val_score(clf, X, y, cv=5, scoring="accuracy")
print(f"各foldの精度: {scores}")
print(f"平均精度:     {scores.mean():.3f} ± {scores.std():.3f}")

cv=5 が一般的ですが、データ量が少ない場合は cv=10 やLeave-One-Out交差検証を検討してください。

5.3 評価指標の使い分け

「精度(Accuracy)」だけでは不十分なケースがある。

# 例: 病気の検出(陽性:1%, 陰性:99%)
# → 「全員陰性」と予測するだけでAccuracy 99%になってしまう

from sklearn.metrics import (
    accuracy_score,
    precision_score,
    recall_score,
    f1_score,
    confusion_matrix,
)

y_true = [0, 0, 0, 0, 0, 0, 0, 0, 0, 1]  # 10人中1人だけ陽性
y_pred = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]  # 全員陰性と予測

print(f"Accuracy:  {accuracy_score(y_true, y_pred):.2f}")   # 0.90 — 高い!
print(f"Recall:    {recall_score(y_true, y_pred):.2f}")      # 0.00 — 陽性を1人も見つけられていない
指標 意味 いつ重視するか
Accuracy 全体の正解率 クラスが均等なとき
Precision 「陽性」と予測したもののうち、本当に陽性の割合 誤検出を減らしたいとき(スパム判定等)
Recall 実際の陽性のうち、正しく検出できた割合 見逃しを減らしたいとき(病気の検出等)
F1-score PrecisionとRecallの調和平均 両方のバランスを取りたいとき
RMSE 予測値と実測値の二乗平均平方根誤差 回帰タスク
決定係数(1に近いほど良い) 回帰タスク

モデル評価の正しいやり方がわかったところで、次はMLで最も恐ろしい落とし穴 — 過学習と未学習のメカニズムを解説する。


6. 過学習と未学習 — モデルの「ちょうどいい」を見つける

6.1 過学習は「丸暗記」、未学習は「理解不足」

気象台の比喩で考えてみよう。

未学習: 予報官が「季節の平均気温」しか見ていない。梅雨の時期でも「平均的には晴れが多いから晴れ」と予報する。パターンの読み取りが雑すぎる。

過学習: 予報官が過去30年の全データを丸暗記している。「2015年4月12日は晴れだったから、4月12日は晴れ」と予報する。過去には完璧に当たるが、今年の天気は過去のコピーではないので外れる。

適切なフィット: 「低気圧が近づくと雨になりやすい」「気温と湿度がこの範囲なら曇りやすい」という汎用的なパターンを学んでいる。過去データでも新しいデータでも、そこそこの精度で予報できる。

6.2 過学習を検出する方法

from sklearn.model_selection import learning_curve
import numpy as np

# 学習曲線を計算
train_sizes, train_scores, test_scores = learning_curve(
    RandomForestClassifier(n_estimators=100, random_state=42),
    X, y,
    cv=5,
    train_sizes=np.linspace(0.1, 1.0, 10),
    scoring="accuracy"
)

# 学習データの精度 ≫ テストデータの精度 → 過学習の兆候
train_mean = train_scores.mean(axis=1)
test_mean = test_scores.mean(axis=1)
gap = train_mean[-1] - test_mean[-1]
print(f"学習データ精度: {train_mean[-1]:.3f}")
print(f"テストデータ精度: {test_mean[-1]:.3f}")
print(f"ギャップ: {gap:.3f}")

if gap > 0.1:
    print("⚠️ 過学習の兆候があります")
elif test_mean[-1] < 0.7:
    print("⚠️ 未学習の可能性があります")
else:
    print("✅ 適切にフィットしています")

6.3 過学習への対策

対策 方法 効果
データを増やす 学習データ量の増加 最も効果的だが常に可能ではない
モデルを単純にする max_depth を下げる等 過剰な複雑さを制限
正則化 Ridge / Lasso / ElasticNet パラメータの大きさにペナルティを課す
交差検証 cross_val_score 過学習を早期に検出
特徴量選択 不要な特徴量を削除 ノイズとなる情報を排除

過学習と未学習のメカニズムを理解したところで、次はscikit-learnの実務での真骨頂 — Pipelineを解説しよう。前処理からモデル訓練までを1本の流れに統合するこの仕組みは、コードの品質と再現性を劇的に高める。


7. Pipeline — 前処理からモデルまでを1本の流れに

7.1 なぜPipelineが必要なのか

実務のML処理は「データを受け取って予測を返す」までに多くのステップがある。

# ❌ Pipelineなし — ステップがバラバラ
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)  # ← これを忘れがち

pca = PCA(n_components=5)
X_train_pca = pca.fit_transform(X_train_scaled)
X_test_pca = pca.transform(X_test_scaled)  # ← これも忘れがち

clf = RandomForestClassifier()
clf.fit(X_train_pca, y_train)
y_pred = clf.predict(X_test_pca)

このコードには3つの問題がある。

  1. testデータにfit_transformしてしまうリスク(データリーク)
  2. ステップの順序を間違えるリスク
  3. 交差検証やグリッドサーチと組み合わせにくい

気象台の比喩で言えば、観測データの補正 → 異常値除去 → 予報モデルへの入力を、毎回手作業でバラバラにやっているようなものだ。Pipelineはこれを自動化された処理フローにまとめる。

7.2 Pipelineの基本

from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.ensemble import RandomForestClassifier

# ✅ Pipelineで1本の流れに統合
pipe = Pipeline([
    ("scaler", StandardScaler()),       # Step 1: スケーリング
    ("pca", PCA(n_components=5)),        # Step 2: 次元削減
    ("clf", RandomForestClassifier()),   # Step 3: 分類
])

# fitもpredictもPipeline経由で呼ぶだけ
pipe.fit(X_train, y_train)
y_pred = pipe.predict(X_test)

# 交差検証もそのまま使える!
from sklearn.model_selection import cross_val_score
scores = cross_val_score(pipe, X, y, cv=5)
print(f"CV精度: {scores.mean():.3f} ± {scores.std():.3f}")

7.3 ColumnTransformerで列ごとに異なる前処理

実務データには数値列とカテゴリ列が混在している。ColumnTransformer を使えば、列の型に応じて異なる前処理を適用できる。

from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.impute import SimpleImputer
from sklearn.pipeline import Pipeline
from sklearn.ensemble import GradientBoostingClassifier

# 列の定義
numeric_features = ["age", "bp_sys", "bp_dia"]
categorical_features = ["diagnosis"]

# 数値列の前処理パイプライン
numeric_transformer = Pipeline([
    ("imputer", SimpleImputer(strategy="median")),   # 欠損値を中央値で補完
    ("scaler", StandardScaler()),                     # 標準化
])

# カテゴリ列の前処理パイプライン
categorical_transformer = Pipeline([
    ("imputer", SimpleImputer(strategy="constant", fill_value="unknown")),
    ("encoder", OneHotEncoder(handle_unknown="ignore")),  # ワンホットエンコーディング
])

# ColumnTransformerで列ごとに異なる前処理を適用
preprocessor = ColumnTransformer([
    ("num", numeric_transformer, numeric_features),
    ("cat", categorical_transformer, categorical_features),
])

# 前処理 + モデルを1本のPipelineに
full_pipe = Pipeline([
    ("preprocessor", preprocessor),
    ("classifier", GradientBoostingClassifier(n_estimators=100, random_state=42)),
])

# あとは fit → predict するだけ
full_pipe.fit(X_train, y_train)
y_pred = full_pipe.predict(X_test)

7.4 GridSearchCVでハイパーパラメータチューニング

Pipelineの威力が最も発揮されるのがハイパーパラメータチューニングだ。

from sklearn.model_selection import GridSearchCV

# パラメータグリッド(Pipeline内のステップ名__パラメータ名)
param_grid = {
    "classifier__n_estimators": [50, 100, 200],
    "classifier__max_depth": [3, 5, 7],
    "classifier__learning_rate": [0.01, 0.1, 0.2],
}

# GridSearchCV — 全組み合わせを交差検証で評価
grid_search = GridSearchCV(
    full_pipe,
    param_grid,
    cv=5,
    scoring="f1_weighted",
    n_jobs=-1,           # 全CPUコアを使用
    verbose=1
)

grid_search.fit(X_train, y_train)

print(f"最適パラメータ: {grid_search.best_params_}")
print(f"最適スコア:     {grid_search.best_score_:.3f}")

# 最適モデルで予測
y_pred = grid_search.predict(X_test)

気象台で言えば、GridSearchCVはどの計算パラメータの組み合わせが最も予報精度が高いかを総当たりで試す作業だ。これをPipelineなしでやろうとすると、前処理を毎回手動で適用する地獄が待っている。

n_jobs=-1 を指定すると全CPUコアが使われます。パラメータの組み合わせ数が多い場合は RandomizedSearchCV で確率的に探索する方が現実的です。


8. よくあるエラーと対処法

# エラー/症状 原因 対処法
1 ValueError: could not convert string to float カテゴリ変数を数値に変換していない OneHotEncoderLabelEncoder で変換
2 ValueError: Input contains NaN 欠損値が含まれている SimpleImputer で補完、または dropna() で除去
3 学習データでは精度99%なのにテストでは60% 過学習 モデルの複雑さを下げる、正則化を追加、データを増やす
4 NotFittedError: This instance is not fitted yet fit を呼ぶ前に predict / transform を呼んだ fitpredict の順序を確認
5 ValueError: Found input variables with inconsistent numbers of samples Xとyのサンプル数が不一致 X.shape[0] == len(y) を確認
6 交差検証で精度のばらつきが大きい データ量不足 or データの偏り cv の分割数を増やす、stratify を使用、データを増やす
7 ConvergenceWarning: Liblinear failed to converge 収束前にイテレーション上限に到達 max_iter を増やす(例: 1000→5000)、スケーリングを追加
8 testデータへの fit_transform によるデータリーク テストデータの情報が学習に混入 Pipelineを使えば自動的に防止される

エラー8の「データリーク」はコードエラーとして表面化しないため、最も発見が困難です。テストデータの精度が異常に高い場合はリークを疑ってください。Pipelineを使う最大の理由は、このリークを構造的に防ぐことです。


9. 環境診断スクリプト

#!/usr/bin/env python3
"""
scikit-learn環境診断スクリプト v1.0
- バージョン/依存ライブラリ確認
- 基本的なモデル動作テスト
- ベンチマーク
"""

import sys
import time

def diagnose_sklearn():
    try:
        import sklearn
        import numpy as np
        import pandas as pd
    except ImportError as e:
        print(f"{e}")
        print("   pip install scikit-learn numpy pandas でインストールしてください")
        return

    print("=" * 60)
    print("  scikit-learn 環境診断レポート")
    print("=" * 60)

    # 1. バージョン情報
    print("\n[1] バージョン情報")
    print(f"  scikit-learn: {sklearn.__version__}")
    print(f"  NumPy:        {np.__version__}")
    print(f"  pandas:       {pd.__version__}")
    print(f"  Python:       {sys.version.split()[0]}")

    # 2. オプション依存ライブラリ
    print("\n[2] オプション依存ライブラリ")
    optional = {
        "scipy":      "科学計算(多くのモデルが依存)",
        "matplotlib": "学習曲線・混同行列の可視化",
        "joblib":     "並列処理(n_jobs=-1)",
    }
    for pkg, desc in optional.items():
        try:
            mod = __import__(pkg)
            ver = getattr(mod, "__version__", "OK")
            print(f"{pkg:15s} {ver:10s}  ({desc})")
        except ImportError:
            print(f"{pkg:15s} {'':10s}  ({desc})")

    # 3. 基本動作テスト
    print("\n[3] 基本動作テスト")
    from sklearn.datasets import load_iris
    from sklearn.model_selection import cross_val_score
    from sklearn.ensemble import RandomForestClassifier

    iris = load_iris()
    clf = RandomForestClassifier(n_estimators=50, random_state=42)
    scores = cross_val_score(clf, iris.data, iris.target, cv=5)
    status = "" if scores.mean() > 0.9 else "⚠️"
    print(f"  {status} Iris分類 (RF, cv=5): {scores.mean():.3f} ± {scores.std():.3f}")

    # 4. ベンチマーク
    print("\n[4] ベンチマーク")
    from sklearn.datasets import make_classification
    X_bench, y_bench = make_classification(
        n_samples=50000, n_features=20, random_state=42
    )

    models = {
        "LogisticRegression": "sklearn.linear_model.LogisticRegression",
        "RandomForest":       "sklearn.ensemble.RandomForestClassifier",
        "GradientBoosting":   "sklearn.ensemble.GradientBoostingClassifier",
    }

    from sklearn.linear_model import LogisticRegression
    from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier

    for name, cls in [
        ("LogisticRegression", LogisticRegression(max_iter=1000)),
        ("RandomForest(100)", RandomForestClassifier(n_estimators=100, n_jobs=-1)),
        ("GradientBoosting(100)", GradientBoostingClassifier(n_estimators=100)),
    ]:
        start = time.perf_counter()
        cls.fit(X_bench, y_bench)
        elapsed = time.perf_counter() - start
        print(f"  {name:30s} fit時間: {elapsed:.3f}")

    print("\n" + "=" * 60)
    print("  診断完了")
    print("=" * 60)


if __name__ == "__main__":
    diagnose_sklearn()

10. ユースケース別ガイド

ユースケース1: 顧客離脱予測 — 「誰が解約しそうか」

import pandas as pd
import numpy as np
from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.impute import SimpleImputer
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.metrics import classification_report

# サンプルデータ生成
np.random.seed(42)
n = 5000
df = pd.DataFrame({
    "tenure_months":   np.random.randint(1, 72, n),
    "monthly_charge":  np.random.uniform(20, 100, n).round(2),
    "support_tickets": np.random.poisson(2, n),
    "contract_type":   np.random.choice(["month", "year", "two_year"], n),
    "churned":         np.random.choice([0, 1], n, p=[0.75, 0.25]),
})

X = df.drop("churned", axis=1)
y = df["churned"]

# 前処理 + モデルのPipeline
num_features = ["tenure_months", "monthly_charge", "support_tickets"]
cat_features = ["contract_type"]

pipe = Pipeline([
    ("prep", ColumnTransformer([
        ("num", Pipeline([
            ("imp", SimpleImputer(strategy="median")),
            ("scl", StandardScaler()),
        ]), num_features),
        ("cat", OneHotEncoder(handle_unknown="ignore"), cat_features),
    ])),
    ("clf", GradientBoostingClassifier(n_estimators=200, max_depth=4, random_state=42)),
])

# 評価
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, stratify=y, random_state=42)
pipe.fit(X_train, y_train)
print(classification_report(y_test, pipe.predict(X_test), target_names=["継続", "離脱"]))

ユースケース2: 異常検知 — 「いつもと違うデータを見つける」

from sklearn.ensemble import IsolationForest
import numpy as np

# 正常データ + 異常データ
np.random.seed(42)
X_normal = np.random.randn(1000, 2)                          # 正常
X_anomaly = np.random.uniform(-4, 4, size=(20, 2))            # 異常

X = np.vstack([X_normal, X_anomaly])

# Isolation Forest(教師なし異常検知)
iso = IsolationForest(contamination=0.02, random_state=42)
iso.fit(X)

labels = iso.predict(X)  # 1: 正常, -1: 異常
n_anomalies = (labels == -1).sum()
print(f"検出された異常: {n_anomalies}件 / {len(X)}件中")

ユースケース3: テキスト分類 — 「スパムメール判定」

from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn.pipeline import Pipeline
from sklearn.model_selection import cross_val_score

# サンプルデータ
texts = [
    "限定セール!今すぐクリック!",
    "会議の議事録を添付します",
    "無料プレゼント当選おめでとう",
    "来週の打ち合わせの件",
    "最安値!本日限り!即購入!",
    "プロジェクトの進捗報告です",
    "お得なキャンペーン情報",
    "資料を確認しました。問題ありません。",
]
labels = [1, 0, 1, 0, 1, 0, 1, 0]  # 1: スパム, 0: 正常

# TF-IDF + ナイーブベイズのPipeline
pipe = Pipeline([
    ("tfidf", TfidfVectorizer()),
    ("clf", MultinomialNB()),
])

pipe.fit(texts, labels)

# 新しいメールを判定
new_emails = ["緊急!アカウント確認が必要です", "明日の会議資料の件"]
predictions = pipe.predict(new_emails)
for email, pred in zip(new_emails, predictions):
    print(f"  {'🚫スパム' if pred == 1 else '✅正常'}: {email}")

11. 学習ロードマップ

レベル 目安期間 到達状態
Lv.1 基礎 1〜2週間 fit/predictで基本的なモデルを動かせる
Lv.2 実践 2〜4週間 Pipelineで前処理からモデル訓練まで一気通貫で構築できる
Lv.3 応用 1〜2ヶ月 ハイパーパラメータチューニングや特徴量エンジニアリングで精度を追い込める
Lv.4 発展 2ヶ月〜 表形式データの限界を超えてディープラーニングやMLOpsに進める

まとめ

この記事では、scikit-learnの仕組みを「天気予報の気象台」の比喩で解き明かしてきた。

scikit-learnは地上の気象台だ。温度計、湿度計、気圧計といった数値データ(表形式データ)を使って未来を予報する。衛星画像やレーダー画像を解析するPyTorch / TensorFlowとは役割が違う。そしてこの気象台が革命的なのは、どの予報手法を使っても操作方法が同じ(Estimator API: fit→predict)という統一設計にある。

fit は過去の観測記録からパターンを見つける作業。predict はそのパターンに基づいて未来を予報する行為。train_test_split は過去データを「パターン学習用」と「予報精度検証用」に分ける。そして Pipeline は、観測データの補正から予報結果の出力までを1本の自動化フローにまとめる仕組みだった。

個人的な話をすると、筆者がローカルLLMの推論ログ分析で「どのモデルがどのカテゴリの質問に強いか」を分析した際、最初はPyTorchで分類モデルを組もうとしたが、10分で「これ表形式データやん」と気づいてscikit-learnのRandomForestに切り替えた。Pipeline 1本でデータ前処理から交差検証まで完了し、結果が出るまで30分もかからなかった。PyTorchで組んでいたら半日は飛んでいたと思う。

scikit-learnは「古い」ライブラリではない。表形式データに対する機械学習の最速・最短経路だ。ここで学んだEstimator API、交差検証、Pipelineの設計思想は、ディープラーニングに進んだ後も、データの前処理や評価の場面で必ず活きてくる。


参考文献


Python・NumPy・pandasの基礎から学びたい方は、こちらのシリーズもどうぞ:


筆者のXアカウントはこちら(@geneLab_999)

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?