はじめに:scikit-learnが新しいフェーズに入った
Pythonで機械学習をやるなら、まず最初に触れるのがscikit-learn。2007年の誕生以来、分類・回帰・クラスタリング・次元削減・モデル評価まで、機械学習に必要なほぼすべてを統一的なAPIで提供してきたライブラリです。
当サイトでは、Pandas 3.0のCopy-on-Writeや新機能、そしてPolarsの次世代データフレームについて詳しく紹介してきましたが、データの前処理から一歩進んで「モデルを構築・評価・デプロイする」段階になると、やっぱりscikit-learnの出番なんですよね。
そして2025年12月にリリースされたscikit-learn 1.8。これがなかなかすごい。
長年の課題だった「GPU対応」と「GILフリーのマルチスレッド処理」という2つの大きな壁を、ついに突破しました。正直なところ、scikit-learnの歴史の中でも屈指のインパクトがあるリリースだと思います。
この記事では、scikit-learn 1.8の新機能をひと通り網羅しつつ、実務で即使えるPipeline・ColumnTransformerの実践パターンまで徹底的に解説していきます。データの読み込みからモデル評価まで、一つのパイプラインで完結させる方法をコード付きで見ていきましょう。
scikit-learn 1.8の新機能ハイライト
Array API対応:ついにGPUが使えるように
scikit-learn 1.8の目玉は、なんと言ってもArray API対応の大幅拡充です。Python Array API標準への対応が進んだことで、PyTorchテンソルやCuPy配列をそのまま入力として渡せるようになりました。
つまり、GPUを使った計算がscikit-learnでネイティブにできるようになったわけです。これ、待ってた人多いんじゃないでしょうか。
対応が追加されたエスティメータとメトリクスはこちら。
preprocessing.StandardScaler— 標準化をGPU上で実行preprocessing.PolynomialFeatures— 多項式特徴量の生成もGPUでlinear_model.RidgeCV/RidgeClassifierCV— リッジ回帰の交差検証がGPU上でmixture.GaussianMixture— GMM(ガウス混合モデル)のGPU処理calibration.CalibratedClassifierCV— 確率キャリブレーションもGPU対応sklearn.metricsの各種メトリクス関数
実際のコードを見てみましょう。Google ColabのGPUランタイムなどで試せます。
import sklearn
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import RidgeCV
from sklearn.pipeline import make_pipeline
from sklearn.model_selection import cross_validate
import torch
# GPU上にデータを配置
device = torch.device("cuda")
X_gpu = torch.randn(10000, 50, device=device, dtype=torch.float64)
y_gpu = torch.randn(10000, device=device, dtype=torch.float64)
# Array APIディスパッチを有効にしてパイプラインを実行
ridge_pipeline = make_pipeline(StandardScaler(), RidgeCV())
with sklearn.config_context(array_api_dispatch=True):
cv_results = cross_validate(ridge_pipeline, X_gpu, y_gpu)
print(f"テストスコア: {cv_results['test_score'].mean():.4f}")
ベンチマークによると、Google ColabのGPUとシングルCPUコアの比較で約10倍のスピードアップが報告されています。大規模データセットで標準化やリッジ回帰をガッツリ回す場合、この差はかなり大きいです。
ちなみに注意点が一つ。array_api_dispatch=Trueの設定は明示的に行う必要があります。デフォルトでは従来どおりNumPy配列で処理されるので、既存のコードが勝手に壊れるということはありません。
フリースレッドCPython 3.14対応:GILの呪縛からの解放
もう一つの大きなニュースが、フリースレッドCPython 3.14のサポートです。scikit-learn 1.8はPython 3.11〜3.14をサポートしており、Python 3.14のフリースレッドビルド用ホイールがすべてのプラットフォームで提供されています。
ご存知の方も多いと思いますが、「GIL(Global Interpreter Lock)」はPythonでマルチスレッド処理を行う際の最大の壁でした。GILのせいで、複数スレッドが同時にPythonバイトコードを実行できず、CPUバウンドな処理ではマルチスレッドの恩恵がほとんど受けられなかったんです。
scikit-learnは従来、この制約を回避するためにjoblibのサブプロセス(loky)バックエンドで並列処理を実現していました。でも、サブプロセス間のデータ転送にはどうしてもオーバーヘッドが発生します。
フリースレッドPythonなら、スレッドバックエンドで真の並列処理が可能になります。
import joblib
from sklearn.model_selection import GridSearchCV
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import make_classification
X, y = make_classification(n_samples=10000, n_features=20, random_state=42)
clf = RandomForestClassifier(random_state=42)
param_grid = {
"n_estimators": [50, 100, 200],
"max_depth": [5, 10, 20, None],
"min_samples_split": [2, 5, 10],
}
grid_search = GridSearchCV(clf, param_grid=param_grid, cv=5, n_jobs=4)
# フリースレッドPythonではスレッドバックエンドで高速並列処理
with joblib.parallel_config(backend="threading"):
grid_search.fit(X, y)
print(f"最良スコア: {grid_search.best_score_:.4f}")
print(f"最良パラメータ: {grid_search.best_params_}")
CPython 3.14のフリースレッドビルドでは、プロセス間通信のオーバーヘッドがなくなるため、特にグリッドサーチやクロスバリデーションなど、データコピーが発生するタイプの並列処理で大幅な効率改善が見込めます。
一つだけ重要な注意点。CPython 3.13ではなく3.14を使うことを強く推奨します。3.13のフリースレッドビルドにはまだバグが残っているので、安定性の面で3.14がベターです。
Temperature Scalingによる確率キャリブレーション
CalibratedClassifierCVに新しいキャリブレーション手法としてTemperature Scalingが追加されました。method="temperature"で指定します。
これ、多クラス分類をやってる人には地味にうれしいアップデートです。従来のsigmoidやisotonicがOne-vs-Restスキームを使うのに対し、Temperature Scalingは単一のスカラーパラメータ(温度)だけで全クラスのキャリブレーションを行います。パラメータ数が圧倒的に少ないので、オーバーフィッティングのリスクも低い。
from sklearn.calibration import CalibratedClassifierCV
from sklearn.svm import SVC
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
import numpy as np
# 多クラス分類データの生成
X, y = make_classification(
n_samples=5000, n_features=20, n_classes=5,
n_informative=15, random_state=42
)
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42)
# 基本分類器
clf = SVC()
# Temperature Scalingによるキャリブレーション
calibrated_clf = CalibratedClassifierCV(
clf, method="temperature", ensemble=False
)
calibrated_clf.fit(X_train, y_train)
# キャリブレーション済み確率の取得
proba = calibrated_clf.predict_proba(X_test)
print(f"確率の合計(各サンプルで1になるはず): {proba[0].sum():.4f}")
print(f"テストスコア: {calibrated_clf.score(X_test, y_test):.4f}")
線形モデルのフィット時間が大幅改善
L1ペナルティ(Lasso)やElasticNetで使われる座標降下法ソルバーにGap Safe Screening Rulesが導入されました。座標降下法の反復中に「最適解でゼロになることが確実な特徴量」を早い段階で除外できるようになったんです。
改善対象のエスティメータは以下のとおり。
ElasticNet/ElasticNetCVLasso/LassoCVMultiTaskElasticNet/MultiTaskElasticNetCVMultiTaskLasso/MultiTaskLassoCV
L1ペナルティが強いほどゼロになる特徴量が増えるので、スクリーニングの効果も大きくなります。特徴量が数千〜数万あるような高次元データでは、体感できるレベルの速度差があるはずです。
from sklearn.linear_model import ElasticNetCV
from sklearn.datasets import make_regression
import time
# 高次元データ
X, y = make_regression(n_samples=1000, n_features=10000, random_state=42)
start = time.time()
model = ElasticNetCV(cv=5)
model.fit(X, y)
elapsed = time.time() - start
print(f"フィット時間: {elapsed:.1f}秒")
print(f"非ゼロ係数の数: {(model.coef_ != 0).sum()}")
print(f"選択された特徴量の割合: {(model.coef_ != 0).mean():.1%}")
ClassicalMDS(古典的多次元尺度構成法)の追加
sklearn.manifoldにClassicalMDS(古典的多次元尺度構成法、別名PCoA / 主座標分析)が追加されました。ペアワイズのスカラー積を固有値分解で近似する手法で、PCAとはまた違ったアプローチで次元削減ができます。
from sklearn.manifold import ClassicalMDS
from sklearn.datasets import make_swiss_roll
import numpy as np
# スイスロールデータの生成
X, color = make_swiss_roll(n_samples=1500, random_state=42)
# ClassicalMDSで2次元に射影
mds = ClassicalMDS(n_components=2)
X_transformed = mds.fit_transform(X)
print(f"元の次元: {X.shape[1]}")
print(f"変換後の次元: {X_transformed.shape[1]}")
print(f"出力の形状: {X_transformed.shape}")
DecisionTreeRegressorの高速化
DecisionTreeRegressorでcriterion="absolute_error"を使った場合の計算量が、O(n²)からO(n log n)に改善されました。
どのくらいの違いかというと、公式ベンチマークでは100,000サンプルに対する単一分割が約20秒から約100ミリ秒へと、200倍の高速化を達成しています。桁違いですね。
エスティメータのHTML表現の改善
Jupyter NotebookやJupyterLabでのエスティメータ表示も改善されています。ハイパーパラメータのドロップダウンテーブルにオンラインドキュメントへのリンクが付き、ホバー時にはdocstringの説明がツールチップ表示されるようになりました。地味ですが、インタラクティブな開発時の体験がワンランク上がります。
実践:Pipeline・ColumnTransformerでMLパイプラインを構築する
さて、ここからが本題。scikit-learn 1.8の機能も活用しながら、実務で使える本格的な機械学習パイプラインをステップバイステップで組み立てていきます。
なぜPipelineを使うべきなのか
機械学習プロジェクトで一番よく起きるミス、何だと思いますか?
データリーク(data leakage)です。
たとえば、交差検証の前にデータ全体で標準化やエンコーディングをやってしまうと、テストデータの情報がトレーニングデータに漏れてしまいます。これ、やっちゃってる人が思った以上に多い(自分も昔やらかしました)。
Pipelineを使えば、前処理からモデルの学習までを一つのオブジェクトとしてカプセル化できるので、以下のようなメリットがあります。
- データリークの防止:交差検証時に、各foldで前処理のfit/transformが正しく分離される
- コードの簡潔化:前処理→特徴量エンジニアリング→モデル学習を一つのオブジェクトで管理できる
- ハイパーパラメータ探索の統合:前処理のパラメータとモデルのパラメータを同時にチューニング可能
- 再現性の向上:パイプライン全体をpickleで保存・共有できる
サンプルデータの準備
ここでは、実務でよく見る「数値カラムとカテゴリカラムが混在するデータセット」を使います。scikit-learnに組み込まれているAdult Censusデータセット(通称Census Income)で、年収が50Kドルを超えるかどうかを予測する二値分類問題です。
import pandas as pd
from sklearn.datasets import fetch_openml
# Adult Censusデータセットの取得
data = fetch_openml("adult", version=2, as_frame=True, parser="auto")
X = data.data
y = (data.target == ">50K").astype(int)
print(f"データ形状: {X.shape}")
print(f"ターゲットの分布:\n{y.value_counts()}")
print(f"\nカラムのデータ型:")
print(X.dtypes)
このデータセットには数値型(age、hours-per-weekなど)とカテゴリ型(workclass、educationなど)が混在しています。これを適切に前処理するのがColumnTransformerの出番です。
ColumnTransformerで型ごとに前処理を分ける
ColumnTransformerは、DataFrameの各カラムに対して異なる前処理を適用するための仕組みです。make_column_selectorと組み合わせると、データ型ベースで自動的にカラムを振り分けられます。
from sklearn.compose import ColumnTransformer, make_column_selector
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.impute import SimpleImputer
from sklearn.pipeline import Pipeline
# 数値カラム用の前処理パイプライン
numeric_transformer = Pipeline(steps=[
("imputer", SimpleImputer(strategy="median")),
("scaler", StandardScaler()),
])
# カテゴリカラム用の前処理パイプライン
categorical_transformer = Pipeline(steps=[
("imputer", SimpleImputer(strategy="most_frequent")),
("encoder", OneHotEncoder(handle_unknown="ignore", sparse_output=False)),
])
# ColumnTransformerで統合
preprocessor = ColumnTransformer(
transformers=[
("num", numeric_transformer, make_column_selector(dtype_include="number")),
("cat", categorical_transformer, make_column_selector(dtype_include="category")),
],
remainder="drop", # 指定されなかったカラムは除外
)
ここでのポイントはmake_column_selectorを使っているところ。カラム名をハードコードする代わりにデータ型で自動選択するので、データスキーマが変わっても柔軟に対応できます。
Pipelineでモデルまで一気通貫
前処理とモデルをPipelineで結合しましょう。ここではランダムフォレストを使います。
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score
# 前処理 + モデルのパイプライン
clf_pipeline = Pipeline(steps=[
("preprocessor", preprocessor),
("classifier", RandomForestClassifier(n_estimators=100, random_state=42)),
])
# 交差検証で評価(前処理も含めて正しくfoldされる)
scores = cross_val_score(clf_pipeline, X, y, cv=5, scoring="accuracy")
print(f"交差検証スコア: {scores}")
print(f"平均スコア: {scores.mean():.4f} (+/- {scores.std():.4f})")
このコードで大事なのは、cross_val_scoreにパイプライン全体を渡しているところです。こうすることで、各foldで以下の処理がきちんと分離されます。
- トレーニングデータでのみ
SimpleImputerのfit(中央値・最頻値の計算) - トレーニングデータでのみ
StandardScalerのfit(平均・標準偏差の計算) - トレーニングデータでのみ
OneHotEncoderのfit(カテゴリの学習) - テストデータにはtransformのみが適用される
これこそPipelineを使う最大の理由。手作業でこれをやろうとすると、データリークのバグが本当に入り込みやすいんですよ。
ハイパーパラメータの同時チューニング
Pipelineの強力な機能の一つが、前処理とモデルのパラメータを同時にチューニングできること。パラメータ名はステップ名__パラメータ名というダブルアンダースコア記法で指定します。
from sklearn.model_selection import RandomizedSearchCV
from scipy.stats import randint, uniform
# 前処理パラメータとモデルパラメータを同時に探索
param_distributions = {
# 前処理のパラメータ
"preprocessor__num__imputer__strategy": ["mean", "median"],
# モデルのパラメータ
"classifier__n_estimators": randint(50, 300),
"classifier__max_depth": [5, 10, 20, None],
"classifier__min_samples_split": randint(2, 20),
"classifier__min_samples_leaf": randint(1, 10),
}
search = RandomizedSearchCV(
clf_pipeline,
param_distributions=param_distributions,
n_iter=20,
cv=5,
scoring="roc_auc",
random_state=42,
n_jobs=-1, # すべてのCPUコアを使用
)
search.fit(X, y)
print(f"最良のROC AUCスコア: {search.best_score_:.4f}")
print(f"最良のパラメータ:")
for key, value in search.best_params_.items():
print(f" {key}: {value}")
「欠損値を平均で埋めるか中央値で埋めるか」という前処理の選択肢まで、モデルのハイパーパラメータと一緒に最適化できるわけです。手作業では到底やりきれない、Pipelineの真骨頂ですね。
複数モデルの比較をPipelineで効率化
前処理パイプラインを再利用して、複数のモデルをサクッと比較することもできます。
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import (
RandomForestClassifier,
GradientBoostingClassifier,
HistGradientBoostingClassifier,
)
from sklearn.svm import SVC
from sklearn.model_selection import cross_val_score
import numpy as np
# 比較するモデルの定義
models = {
"ロジスティック回帰": LogisticRegression(max_iter=1000, random_state=42),
"ランダムフォレスト": RandomForestClassifier(n_estimators=100, random_state=42),
"勾配ブースティング": GradientBoostingClassifier(n_estimators=100, random_state=42),
"HistGBDT": HistGradientBoostingClassifier(max_iter=100, random_state=42),
}
results = {}
for name, model in models.items():
pipeline = Pipeline(steps=[
("preprocessor", preprocessor),
("classifier", model),
])
scores = cross_val_score(pipeline, X, y, cv=5, scoring="roc_auc")
results[name] = scores
print(f"{name}: ROC AUC = {scores.mean():.4f} (+/- {scores.std():.4f})")
# 最良のモデルを特定
best_model = max(results, key=lambda k: results[k].mean())
print(f"\n最良のモデル: {best_model}")
ちなみにHistGradientBoostingClassifierは内部で欠損値やカテゴリ変数を直接扱えるので、本来は前処理なしでも動きます。ただ、ここでは比較の公平性のために同じ前処理パイプラインを通しています。
特徴量エンジニアリングをPipelineに組み込む
実務では単純なスケーリングやエンコーディングだけじゃ足りないことも多いですよね。特徴量エンジニアリングもPipelineに組み込みたい。そんなとき便利なのがFunctionTransformerです。
from sklearn.preprocessing import FunctionTransformer, PolynomialFeatures
from sklearn.feature_selection import SelectKBest, f_classif
# カスタム特徴量エンジニアリング関数
def create_age_features(X):
"""年齢に関する派生特徴量を追加"""
X = X.copy()
if "age" in X.columns:
X["age_squared"] = X["age"] ** 2
X["age_bin"] = pd.cut(
X["age"],
bins=[0, 25, 35, 50, 65, 100],
labels=False
)
return X
# 拡張された数値カラム用パイプライン
numeric_transformer_advanced = Pipeline(steps=[
("imputer", SimpleImputer(strategy="median")),
("poly", PolynomialFeatures(degree=2, interaction_only=True, include_bias=False)),
("scaler", StandardScaler()),
("selector", SelectKBest(f_classif, k=20)),
])
# 拡張されたColumnTransformer
preprocessor_advanced = ColumnTransformer(
transformers=[
("num", numeric_transformer_advanced, make_column_selector(dtype_include="number")),
("cat", categorical_transformer, make_column_selector(dtype_include="category")),
]
)
# 拡張パイプライン
advanced_pipeline = Pipeline(steps=[
("preprocessor", preprocessor_advanced),
("classifier", HistGradientBoostingClassifier(max_iter=200, random_state=42)),
])
scores = cross_val_score(advanced_pipeline, X, y, cv=5, scoring="roc_auc")
print(f"拡張パイプライン ROC AUC: {scores.mean():.4f} (+/- {scores.std():.4f})")
PolynomialFeaturesで交互作用特徴量を生成して、SelectKBestで上位k個を選択。これらのステップもすべてPipeline内で管理されるので、交差検証時にデータリークが起きません。
TunedThresholdClassifierCVで最適な閾値を見つける
二値分類でデフォルトの閾値0.5がベストとは限りません。特にクラス不均衡な問題や、False PositiveとFalse Negativeのコストが異なるケースでは、閾値の調整がモデルの実用性を大きく左右します。
TunedThresholdClassifierCV(scikit-learn 1.5で追加された機能)を使えば、交差検証ベースで最適な決定閾値を自動的に見つけてくれます。
from sklearn.model_selection import TunedThresholdClassifierCV, train_test_split
from sklearn.metrics import classification_report, f1_score
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 基本パイプライン
base_pipeline = Pipeline(steps=[
("preprocessor", preprocessor),
("classifier", HistGradientBoostingClassifier(max_iter=200, random_state=42)),
])
# F1スコアを最大化する閾値を探索
tuned_clf = TunedThresholdClassifierCV(
base_pipeline,
scoring="f1",
cv=5,
)
tuned_clf.fit(X_train, y_train)
# 結果の確認
y_pred_default = base_pipeline.fit(X_train, y_train).predict(X_test)
y_pred_tuned = tuned_clf.predict(X_test)
print("=== デフォルト閾値(0.5) ===")
print(classification_report(y_test, y_pred_default))
print("=== 最適化された閾値 ===")
print(f"最適閾値: {tuned_clf.best_threshold_:.4f}")
print(classification_report(y_test, y_pred_tuned))
クラス不均衡なデータだと、デフォルトの0.5よりも低い閾値が最適になることが多くて、F1スコアが数ポイント改善するケースも珍しくありません。個人的に、もっと活用されてもいい機能だと思っています。
パイプラインの保存とデプロイ
構築したパイプラインを本番環境で使うには、当然ながらモデルの保存と読み込みが必要です。
joblibを使ったシリアライズ
import joblib
from pathlib import Path
# モデルの保存
model_dir = Path("models")
model_dir.mkdir(exist_ok=True)
# パイプライン全体を保存(前処理 + モデル)
joblib.dump(tuned_clf, model_dir / "income_classifier_v1.joblib")
# 読み込みと推論
loaded_model = joblib.load(model_dir / "income_classifier_v1.joblib")
predictions = loaded_model.predict(X_test)
probabilities = loaded_model.predict_proba(X_test)
print(f"予測結果の形状: {predictions.shape}")
print(f"確率出力の形状: {probabilities.shape}")
パイプライン全体が一つのファイルに保存されるので、デプロイ先で前処理を再実装する必要がありません。これ、Pipelineを使う大きなメリットの一つです。
ONNX形式へのエクスポート
より汎用的なデプロイを考えるなら、ONNX形式へのエクスポートも選択肢に入ります。skl2onnxライブラリを使います。
# pip install skl2onnx
from skl2onnx import to_onnx
import numpy as np
# ONNXへの変換(NumPy配列としてサンプルを渡す必要がある)
# 注意: ColumnTransformerとの互換性は要確認
X_sample = preprocessor.fit_transform(X_train[:1])
onnx_model = to_onnx(
base_pipeline.named_steps["classifier"],
X_sample.astype(np.float32),
)
with open(model_dir / "classifier.onnx", "wb") as f:
f.write(onnx_model.SerializeToString())
print("ONNXモデルを保存しました")
実践的なTips:Pipelineを使いこなすためのノウハウ
1. パイプラインのデバッグ
Pipelineの途中で「いま何が起きてるの?」と確認したくなること、ありますよね。そんなときはset_outputメソッドでpandas DataFrame形式の出力に切り替えると見やすくなります。
# pandas DataFrame形式で出力
clf_pipeline.set_output(transform="pandas")
# 前処理ステップまでの出力を確認
transformed = clf_pipeline[:-1].fit_transform(X)
print(f"変換後の形状: {transformed.shape}")
print(f"カラム名のサンプル: {list(transformed.columns[:10])}")
2. memory引数でキャッシュを活用
前処理が重いとき、memory引数でキャッシュディレクトリを指定すると再計算を避けられます。ハイパーパラメータ探索で何度もfitを繰り返す場面では、かなりの時間節約になります。
from tempfile import mkdtemp
cachedir = mkdtemp()
cached_pipeline = Pipeline(
steps=[
("preprocessor", preprocessor),
("classifier", RandomForestClassifier(n_estimators=100, random_state=42)),
],
memory=cachedir,
)
# 最初のfitでキャッシュが作成される
cached_pipeline.fit(X_train, y_train)
# 同じデータで再度fitすると、前処理はキャッシュから読み込まれる
cached_pipeline.fit(X_train, y_train) # 前処理のステップがスキップされる
3. make_pipelineで簡潔に書く
ステップ名を自動生成してOKな場合は、make_pipelineのほうが簡潔です。
from sklearn.pipeline import make_pipeline
# ステップ名を自動生成
simple_pipeline = make_pipeline(
preprocessor,
HistGradientBoostingClassifier(max_iter=200, random_state=42),
)
# ステップ名はクラス名のスネークケースになる
print(simple_pipeline.named_steps.keys())
# dict_keys(['columntransformer', 'histgradientboostingclassifier'])
4. verbose引数で進捗を確認
# verbose=Trueで各ステップの実行を出力
verbose_pipeline = Pipeline(
steps=[
("preprocessor", preprocessor),
("classifier", RandomForestClassifier(n_estimators=100, random_state=42)),
],
verbose=True,
)
verbose_pipeline.fit(X_train, y_train)
# [Pipeline] ....... (step 1 of 2) Processing preprocessor, total= 0.5s
# [Pipeline] ....... (step 2 of 2) Processing classifier, total= 2.3s
GPU対応パイプラインの構築例
最後に、scikit-learn 1.8のArray API機能を活かしたGPU対応パイプラインの例を紹介します。
import sklearn
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import RidgeClassifierCV
from sklearn.pipeline import make_pipeline
from sklearn.model_selection import cross_validate
from sklearn.metrics import make_scorer, f1_score
# CuPyまたはPyTorchがインストールされている環境で実行
# --- PyTorchを使用する例 ---
# import torch
# device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# X_tensor = torch.tensor(X_numeric.values, dtype=torch.float64, device=device)
# y_tensor = torch.tensor(y.values, dtype=torch.float64, device=device)
# --- NumPy(CPU)での通常実行 ---
import numpy as np
from sklearn.datasets import make_classification
X_np, y_np = make_classification(
n_samples=50000, n_features=100,
n_informative=50, random_state=42
)
# Array APIディスパッチを使ったパイプライン
gpu_pipeline = make_pipeline(
StandardScaler(),
RidgeClassifierCV(alphas=[0.01, 0.1, 1.0, 10.0]),
)
# NumPy(CPU)で実行
with sklearn.config_context(array_api_dispatch=True):
results = cross_validate(
gpu_pipeline, X_np, y_np, cv=5,
scoring="accuracy",
return_train_score=True,
)
print(f"テストスコア: {results['test_score'].mean():.4f}")
print(f"トレーニングスコア: {results['train_score'].mean():.4f}")
print(f"フィット時間: {results['fit_time'].mean():.4f}秒")
# GPU環境では、上記のX_npをPyTorchテンソルやCuPy配列に置き換えるだけで
# 同じコードがGPU上で実行されます
Array APIの一番いいところは、コードを変更せずにバックエンドを切り替えられる点。NumPy配列を渡せばCPUで、PyTorchのCUDAテンソルを渡せばGPUで、まったく同じパイプラインが動きます。これは設計としてかなりエレガントだと思います。
scikit-learn 1.8へのアップグレード時の注意点
既存プロジェクトをscikit-learn 1.8にアップグレードする際に気をつけたいポイントをまとめておきます。
Pythonバージョン要件
scikit-learn 1.8はPython 3.11〜3.14をサポートしています。Python 3.10以前を使っている場合は、まずPythonのアップグレードが先です。
非推奨警告への対応
アップグレード前に、現在のコードでscikit-learnの非推奨警告(FutureWarning)が出ていないか確認しておきましょう。
import warnings
warnings.filterwarnings("error", category=FutureWarning, module="sklearn")
# ここで既存のコードを実行
# FutureWarningがエラーとして発生するので、修正が必要な箇所がわかる
段階的なアップグレード推奨
いきなり1.8に飛ぶのではなく、段階的にアップグレードするのがおすすめです。
- まずscikit-learn 1.7にアップグレードし、非推奨警告をすべて修正
- テストスイートが通ることを確認
- scikit-learn 1.8にアップグレード
- 新機能(Array API、フリースレッドなど)を段階的に導入
まとめ:scikit-learnはまだまだ進化し続けている
scikit-learn 1.8は、Pythonの機械学習エコシステムを大きく前進させるリリースでした。
- Array API対応によるGPU計算のネイティブサポート — 大規模データで最大10倍高速に
- フリースレッドCPython 3.14対応 — GILなしの真のマルチスレッド並列処理
- Temperature Scaling — 多クラス分類の確率キャリブレーションが大幅改善
- 線形モデルの高速化 — Gap Safe Screening Rulesで座標降下法が効率化
- ClassicalMDS — 新しい次元削減手法
- DecisionTreeRegressorの200倍高速化 — absolute_errorの計算量改善
そしてこれらの機能をフル活用するために、PipelineとColumnTransformerによる体系的なMLパイプラインの構築が欠かせません。データリーク防止、コードの再利用性、ハイパーパラメータの統合探索、デプロイの簡素化 — 全部がPipelineを軸に回っています。
当サイトで以前紹介したPandas 3.0やPolarsでデータを整え、scikit-learnのPipelineでモデルを構築する。この組み合わせが、2026年のPythonデータサイエンスのベストプラクティスと言えるでしょう。
次回はscikit-learnのモデル解釈性(SHAP値、部分依存プロットなど)やMLflowとの統合によるMLOpsについて掘り下げていく予定です。