[機械学習のはじめ方] Part49: MLパイプラインの構築(scikit-learn Pipelineなど)

機械学習

機械学習のワークフローを整理して、再現性と効率を高める方法を学びます。

はじめに:なぜMLパイプラインが必要なのか?🤔

機械学習プロジェクトでは、データの前処理(欠損値処理、スケーリング、エンコーディングなど)、特徴量エンジニアリング、モデルの学習、評価といった一連のステップが必要です。これらのステップを手作業で順番に行うと、コードが複雑になったり、同じ処理を何度も書くことになったりします。また、学習データとテストデータで異なる処理を適用してしまう「データリーケージ」という問題を引き起こす可能性もあります。

MLパイプラインは、これら一連の処理ステップをひとまとめにし、ワークフロー全体を効率化・自動化するための仕組みです。これにより、コードがシンプルになり、再現性が高まり、データリーケージのようなミスを防ぐことができます。

主なメリット:
  • 効率化: 複数の処理を一度に実行でき、コードが簡潔になります。
  • 再現性向上: 処理手順が明確になり、誰が実行しても同じ結果を得やすくなります。
  • ミスの防止: データリーケージ(特にクロスバリデーション時)を防ぎます。
  • 保守性向上: 各ステップをモジュール化でき、管理や修正が容易になります。

scikit-learnの`Pipeline`とは?

scikit-learnライブラリには、MLパイプラインを簡単に構築するためのPipelineクラスが用意されています。Pipelineは、複数の「変換器(Transformer)」と最終的な「推定器(Estimator)」を連結するツールです。

  • 変換器 (Transformer): データの前処理や特徴量抽出などを行うオブジェクトで、fit()メソッドとtransform()メソッド(またはfit_transform())を持ちます。(例: StandardScaler, SimpleImputer, PCA
  • 推定器 (Estimator): 最終的にモデルの学習や予測を行うオブジェクトで、fit()メソッドを持ちます。分類器や回帰モデルなどが該当します。(例: LogisticRegression, RandomForestClassifier, LinearRegression

Pipelineオブジェクト自体も推定器として振る舞うため、通常のモデルと同じようにfit(), predict(), score()といったメソッドを使うことができます。

`Pipeline`の基本的な使い方

Pipelineは、処理ステップをリスト形式で定義します。各ステップは、(名前, オブジェクト)というタプルの形で指定します。名前は任意の文字列で、後で特定のステップのパラメータを参照する際に使われます。

簡単な例として、データの標準化(StandardScaler)を行い、その後ロジスティック回帰(LogisticRegression)で分類するパイプラインを作成してみましょう。


# 必要なライブラリをインポート
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.datasets import load_iris

# データの準備 (Irisデータセットを使用)
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.3, random_state=42)

# パイプラインの定義
# ステップ1: 'scaler' - 標準化
# ステップ2: 'classifier' - ロジスティック回帰
pipe = Pipeline([
    ('scaler', StandardScaler()),
    ('classifier', LogisticRegression(random_state=42))
])

# パイプライン全体を学習データに適合させる
# StandardScalerのfit_transformが呼ばれ、その出力がLogisticRegressionのfitに渡される
pipe.fit(X_train, y_train)

# テストデータで予測
y_pred = pipe.predict(X_test)

# 精度を評価
accuracy = pipe.score(X_test, y_test)
print(f"Pipeline Accuracy: {accuracy:.4f}")
        

このコードでは、Pipelineオブジェクトpipeを作成し、fitメソッドを一度呼び出すだけで、標準化とロジスティック回帰の学習が順番に実行されます。予測時(predictscore)も、入力データに対して自動的に標準化処理(transform)が適用された後に予測が行われます。

make_pipeline関数を使うと、ステップ名を自動生成してくれるため、より簡潔に書くことも可能です。


from sklearn.pipeline import make_pipeline

# make_pipelineを使うとステップ名を省略できる(クラス名の小文字が自動で使われる)
pipe_simple = make_pipeline(StandardScaler(), LogisticRegression(random_state=42))

# 使い方はPipelineと同じ
pipe_simple.fit(X_train, y_train)
accuracy_simple = pipe_simple.score(X_test, y_test)
print(f"make_pipeline Accuracy: {accuracy_simple:.4f}")
        

応用:`ColumnTransformer`で列ごとに異なる処理を適用する

データセットには数値データとカテゴリデータが混在していることがよくあります。数値データにはスケーリングを、カテゴリデータにはワンホットエンコーディングを適用したい場合など、列ごとに異なる前処理を行いたいケースがあります。このような場合に便利なのがColumnTransformerです。

ColumnTransformerを使うと、指定した列に対して特定の変換器を適用し、その結果を結合することができます。これをPipelineの中に組み込むことで、より複雑な前処理フローも一元管理できます。


import pandas as pd
import numpy as np
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.impute import SimpleImputer
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import Pipeline
from sklearn.model_selection import train_test_split

# サンプルデータの作成 (数値特徴量とカテゴリ特徴量を含む)
data = {
    'age': [25, 45, 35, 50, 23, np.nan, 60],
    'gender': ['Male', 'Female', 'Female', 'Male', 'Female', 'Male', 'Female'],
    'income': [50000, 80000, 60000, 120000, 40000, 75000, np.nan],
    'purchased': [0, 1, 0, 1, 0, 1, 1] # 目的変数
}
df = pd.DataFrame(data)

X = df.drop('purchased', axis=1)
y = df['purchased']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)

# 数値特徴量とカテゴリ特徴量のリスト
numerical_features = ['age', 'income']
categorical_features = ['gender']

# 数値特徴量用の前処理パイプライン (欠損値補完 + 標準化)
numeric_transformer = Pipeline(steps=[
    ('imputer', SimpleImputer(strategy='median')),
    ('scaler', StandardScaler())
])

# カテゴリ特徴量用の前処理パイプライン (欠損値補完 + OneHotEncoding)
categorical_transformer = Pipeline(steps=[
    ('imputer', SimpleImputer(strategy='most_frequent')),
    ('onehot', OneHotEncoder(handle_unknown='ignore')) # 未知のカテゴリは無視する
])

# ColumnTransformerで数値・カテゴリ特徴量にそれぞれの処理を適用
preprocessor = ColumnTransformer(
    transformers=[
        ('num', numeric_transformer, numerical_features),
        ('cat', categorical_transformer, categorical_features)
    ])

# 前処理とモデルを結合した最終的なパイプライン
model_pipeline = Pipeline(steps=[
    ('preprocessor', preprocessor),
    ('classifier', LogisticRegression(random_state=42))
])

# パイプライン全体を学習
model_pipeline.fit(X_train, y_train)

# 評価
accuracy_complex = model_pipeline.score(X_test, y_test)
print(f"Complex Pipeline Accuracy: {accuracy_complex:.4f}")

        

この例では、まず数値データとカテゴリデータそれぞれに対する前処理パイプライン(欠損値補完とスケーリング/エンコーディング)を作成し、ColumnTransformerでそれらをまとめます。最後に、この前処理ステップと分類モデル(LogisticRegression)をPipelineで結合しています。

パイプラインとハイパーパラメータチューニング

Pipelineのもう一つの大きな利点は、ハイパーパラメータチューニング(例: GridSearchCV, RandomizedSearchCV)と非常に相性が良いことです。

パイプライン全体のステップに含まれるパラメータ(例: 前処理方法のパラメータ、モデルのハイパーパラメータ)をまとめて探索できます。パラメータを指定する際は、ステップ名__パラメータ名という形式で指定します。


from sklearn.model_selection import GridSearchCV
from sklearn.svm import SVC # 例としてSVMを使う

# 前処理とSVMを組み合わせたパイプライン
pipe_svm = Pipeline([
    ('scaler', StandardScaler()),
    ('svm', SVC(random_state=42))
])

# 探索するパラメータグリッドを定義
# SVMのCとkernel、StandardScalerのwith_meanを変更してみる
param_grid = {
    'scaler__with_mean': [True, False], # StandardScalerのパラメータ
    'svm__C': [0.1, 1, 10],          # SVCのパラメータ C
    'svm__kernel': ['linear', 'rbf'] # SVCのパラメータ kernel
}

# グリッドサーチの実行 (パイプラインを推定器として渡す)
grid_search = GridSearchCV(pipe_svm, param_grid, cv=3) # 3-fold CV
grid_search.fit(X_train, y_train)

print(f"Best parameters: {grid_search.best_params_}")
print(f"Best cross-validation score: {grid_search.best_score_:.4f}")
print(f"Test set score: {grid_search.score(X_test, y_test):.4f}")

        

このように、パイプラインを使うことで、前処理の選択肢も含めた最適なパラメータの組み合わせを効率的に見つけることができます。

まとめと次のステップ 🚀

MLパイプライン、特にscikit-learnPipelineColumnTransformerは、機械学習のワークフローを整理し、効率化、再現性の向上、ミスの防止に役立つ強力なツールです。

今回のポイント:
  • MLパイプラインは、データ前処理からモデル学習までの一連のステップを連結する仕組み。
  • scikit-learnPipelineクラスで簡単に実装できる。
  • ColumnTransformerを使えば、列ごとに異なる前処理を適用可能。
  • パイプラインを使うと、コードが簡潔になり、データリーケージを防ぎ、ハイパーパラメータチューニングも効率化できる。

実際のプロジェクトでは、データの前処理はさらに複雑になることが多いですが、パイプラインを使いこなすことで、これらの複雑さを管理しやすくなります。ぜひ、ご自身のプロジェクトでも積極的にパイプラインを活用してみてください!

次のステップでは、作成したモデルを管理するためのツール(MLflowなど)について学んでいきましょう。

コメント

タイトルとURLをコピーしました