本文へスキップ
darkmocha.dev
scikit-learnの推定器APIをfit・predict・transformから理解する
scikit-learnの推定器APIをfit・predict・transformから理解する
ホームに戻る

scikit-learnの推定器APIをfit・predict・transformから理解する

scikit-learnで繰り返し登場するfit・predict・transformの役割を、Irisデータセットを動かしながら整理した。

機械学習初学者による、絶望のアウトプット記録。
scikit-learnのサンプルコードを読んでいると、どのモデルにも同じように fit() が登場する。ロジスティック回帰とデータの標準化では役割が違うはずなのに、なぜ同じメソッドを使うのか。
調べてみると、その理由は「推定器」という共通設計にあった。今回は fit()predict()transform() の3つに絞って、自分なりに整理する。

scikit-learnでできること

scikit-learnは、機械学習を試すためのPythonライブラリだ。分類・回帰・クラスタリング・次元削減に加えて、前処理やモデル評価の道具も揃っている。
最初は機能の多さに圧倒されたが、基本的な使い方はかなり統一されている。モデルが変わっても、データを渡して学習し、予測または変換するという流れは大きく変わらない。

推定器という共通の入口

scikit-learnでは、データから何かを学ぶオブジェクトを推定器(Estimator)と呼ぶ。推定器は基本的に fit() を持ち、役割に応じて別のメソッドも実装する。
役割主なメソッド
推定器fit()LogisticRegressionStandardScaler
予測器predict()分類器、回帰器
変換器transform()StandardScalerPCA
ひとつのクラスが複数の役割を持つこともある。LogisticRegression は学習と予測を行い、StandardScaler はデータから平均・標準偏差を学んでから値を変換する。
「どれも同じ書き方で使える」という設計が、scikit-learnの扱いやすさにつながっている。

fit()でデータから学ぶ

fit() は、渡されたデータから必要な値を学ぶメソッドだ。
ロジスティック回帰なら、特徴量と正解ラベルから分類に使う係数を学習する。
from sklearn.linear_model import LogisticRegression
 
model = LogisticRegression()
model.fit(X_train, y_train)
StandardScaler の場合は、訓練データの平均と標準偏差を記憶する。
from sklearn.preprocessing import StandardScaler
 
scaler = StandardScaler()
scaler.fit(X_train)
同じ fit() でも、何を学ぶかは推定器によって違う。
fit()にテストデータを渡さない
テストデータを使って平均や係数を学ぶと、本来は未知であるはずの情報がモデルに漏れてしまう。これをデータリークという。fit() は訓練データだけに使う。

predict()で未知データを予測する

predict() は、学習済みのモデルへ新しいデータを渡し、クラスや数値を予測するメソッドだ。
y_pred = model.predict(X_test)
print(y_pred)
分類器ならクラスラベル、回帰器なら連続した数値が返る。確率出力に対応している分類器では、predict_proba() を使って各クラスに属する確率も取得できる。
proba = model.predict_proba(X_test)
print(proba)
すべての分類器が predict_proba() を持つわけではない点には注意が必要だ。

transform()でデータを変換する

transform() は、学習済みの変換器を使ってデータを別の表現へ変えるメソッドだ。
標準化では、訓練データで覚えた平均と標準偏差を使い、訓練データとテストデータを同じ基準で変換する。
scaler = StandardScaler()
scaler.fit(X_train)
 
X_train_scaled = scaler.transform(X_train)
X_test_scaled = scaler.transform(X_test)
訓練データに対しては、fit()transform() を続けて呼ぶ fit_transform() も使える。
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
fit_transform()を一般化しすぎない
StandardScaler では fit_transform(X_train)fit(X_train) 後の transform(X_train) は同じ結果になる。ただし、すべての変換器で同じとは限らない。まずは使うクラスのドキュメントを確認する。

Irisデータセットで一連の流れを試す

アヤメのIrisデータセットを使い、標準化してからロジスティック回帰で分類してみる。
from sklearn.datasets import load_iris
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
 
# データを読み込む
iris = load_iris()
X, y = iris.data, iris.target
 
# 訓練用とテスト用に分ける
X_train, X_test, y_train, y_test = train_test_split(
    X,
    y,
    test_size=0.2,
    random_state=42,
    stratify=y,
)
 
# 訓練データで基準を学び、両方を同じ基準で変換する
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
 
# モデルを学習する
model = LogisticRegression()
model.fit(X_train_scaled, y_train)
 
# 未知データを予測して評価する
y_pred = model.predict(X_test_scaled)
print(f"正解率: {accuracy_score(y_test, y_pred):.3f}")
コードを追うと、処理の役割がきれいに分かれている。
  1. scaler.fit_transform() で訓練データの基準を学びながら変換する
  2. scaler.transform() でテストデータを同じ基準に揃える
  3. model.fit() で分類モデルを学習する
  4. model.predict() で未知データを予測する
最初は fit()predict()transform() が別々の呪文に見えていた。実際には、学ぶ・予測する・変換するという役割を揃えた共通インターフェースだった。
モデルを別のアルゴリズムへ差し替えても、基本の流れはほとんど変わらない。次は前処理とモデルを Pipeline にまとめる方法を試してみたい。

//Related Posts

読んでいただきありがとうございます。