Claude Codeでscikit-learnモデルを実装する — fit/predict/評価まで
scikit-learnのfit/predict/評価をClaude Codeに実装させる手順と、データリークなどのつまずきどころをまとめます。
scikit-learnのモデル実装は、推定器を選び、fitで学習させ、predictで予測し、交差検証で評価するという決まった流れをたどります。Claude Codeはこの流れをコードとして書くだけでなく、Bashツールで実際に実行して結果を確認しながら進められます。この記事ではfit/predictの基本からPipeline、評価、ハイパーパラメータ探索までを、Claude Codeに何を渡すと迷わず実装できるかという観点でまとめます。
Claude Codeとscikit-learnを組み合わせる基本の流れ
scikit-learnは、教師あり学習と教師なし学習のどちらも扱えるPythonの機械学習ライブラリです。推定器(estimator)と呼ばれるオブジェクトがfit・predict・transformという共通のAPIを持っており、モデルの種類が変わってもコードの型は変わりません。
Claude Codeにこの実装を任せるときの利点は、コードを提案するだけで終わらない点にあります。Bashツールでpythonコマンドを直接実行できるため、fitが正常に終わるか、predictの出力が期待した形か、その場で確認しながら次のステップに進めます。人間が毎回コピーして手元で実行し直す手間が減ります。
ただし実行できることと、正しいコードになることは別問題です。スケーリングを分割前にかけてしまうデータリークのように、動くけれど評価結果が水増しされるコードも「エラーなく実行できた」という理由でそのまま採用されがちです。手順の各段階でどこを確認すべきかは、このあとの節と「よくあるつまずき」で扱います。
前提
この記事の手順は、次の環境を前提に進めます。
- Python環境にscikit-learnがインストール済みであること(
pip install scikit-learn) - Claude Codeがプロジェクトディレクトリ内で起動していること
- 数値計算ライブラリのバージョンをある程度固定していること(scikit-learnはNumPyやSciPyのバージョンに動作が左右されるため)
プロジェクト直下にCLAUDE.mdを置き、「学習と評価の前処理は必ずPipelineに包む」「乱数シードはrandom_state=0で固定する」といった規約を書いておくと、Claude Codeが実装ごとに同じ判断で迷わずに書けます。CLAUDE.mdの具体的な書き方はClaude CodeのCLAUDE.mdを実用に引き上げる10のパターンにまとまっています。
手順1: fit/predictの基本をClaude Codeに実装させる
推定器のfitとpredictは、scikit-learnのすべてのモデルに共通する最小単位です。まずはClaude Codeにこの基本形を書かせて、動作の感触を掴みます。
RandomForestClassifierを使って、サンプルの2次元配列Xとラベルyでfitし、
predictで新しいデータのクラスを予測するスクリプトを書いて。random_stateは0で固定して。scikit-learn公式のGetting Startedで示されている最小構成は次のとおりです。
from sklearn.ensemble import RandomForestClassifier
clf = RandomForestClassifier(random_state=0)
X = [[1, 2, 3], [11, 12, 13]] # 2サンプル、3特徴量
y = [0, 1] # 各サンプルのクラス
clf.fit(X, y)
clf.predict(X) # 学習データのクラスを予測
clf.predict([[4, 5, 6], [14, 15, 16]]) # 新しいデータのクラスを予測fitは基本的に2つの入力を受け取ります。サンプル行列X(行がサンプル、列が特徴量)と、目的変数yです。yは分類なら整数などの離散値、回帰なら実数で、教師なし学習では省略できます。一度fitしたあとは、predictを呼ぶたびに再学習する必要はありません。
手順2: StandardScalerとPipelineでデータリークを防ぐ
実務のモデル実装は、fit/predictだけでは終わりません。前処理(スケーリングや欠損値の補完)と推定器を組み合わせるのが普通です。scikit-learnでは前処理を担う変換器(transformer)も推定器と同じAPIに従い、fitの代わりにtransformで変換後のデータを返します。
StandardScalerとLogisticRegressionをPipelineでつないで、
Irisデータセットをtrain_test_splitで分割してから学習し、
テストデータでの正解率をaccuracy_scoreで出して。from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import make_pipeline
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
pipe = make_pipeline(StandardScaler(), LogisticRegression())
X, y = load_iris(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)
pipe.fit(X_train, y_train)
accuracy_score(pipe.predict(X_test), y_test) # 約0.97Pipelineは変換器と推定器をひとまとめにし、通常の推定器と同じfit・predictのAPIで扱えるようにします。分割後の学習データだけでスケーリングのパラメータを計算するため、上のCalloutで触れたデータリークを構造的に防げます。特徴量ごとに異なる前処理をかけたい場合は、ColumnTransformerで列ごとに変換を振り分けられます。
手順3: cross_validateで汎化性能を評価する
train_test_splitによる1回の分割だけでは、その分割がたまたま良かった可能性を排除できません。scikit-learnは交差検証のためのcross_validateを提供しており、既定で5分割の評価を行います。
LinearRegressionでmake_regressionの合成データを学習して、
cross_validateで5分割交差検証のtest_scoreを出して。from sklearn.datasets import make_regression
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import cross_validate
X, y = make_regression(n_samples=1000, random_state=0)
lr = LinearRegression()
result = cross_validate(lr, X, y) # 既定で5分割交差検証
result["test_score"]折り返しごとのスコア(result["test_score"])を出させると、モデルが分割によってどれだけブレるかが見えます。単一のスコアだけを報告させると、たまたま良かった分割を採用してしまうリスクが残るため、Claude Codeへの依頼では「分割ごとのスコアも出して」と明示しておくと確認しやすくなります。フォールドを手動で回したり、独自のスコアリング関数を使いたい場合はscikit-learnのユーザーガイドに詳しい選択肢があります。
スコアの数値だけでは前処理コード自体のバグまでは見抜けません。「変換後のデータは元と同じ行数を保つ」のような性質を検証したい場合は、性質ベースのテストも組み合わせられます(ClaudeとProperty-Based TestingでPythonのバグを見つける仕組み)。
手順4: RandomizedSearchCVでハイパーパラメータを探索する
すべての推定器はハイパーパラメータを持ち、その値によって汎化性能が大きく変わります。適切な値はデータに依存するため、経験則だけで決め切るのは現実的ではありません。scikit-learnはRandomizedSearchCVで、交差検証を使ったパラメータ探索を自動化できます。
from sklearn.datasets import make_regression
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import RandomizedSearchCV, train_test_split
from scipy.stats import randint
X, y = make_regression(n_samples=20640, n_features=8, noise=0.1, random_state=0)
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)
param_distributions = {
"n_estimators": randint(1, 5),
"max_depth": randint(5, 10),
}
search = RandomizedSearchCV(
estimator=RandomForestRegressor(random_state=0),
n_iter=5,
param_distributions=param_distributions,
random_state=0,
)
search.fit(X_train, y_train)
search.best_params_ # 例: {"max_depth": 9, "n_estimators": 4}
search.score(X_test, y_test)探索が終わると、RandomizedSearchCVオブジェクト自体が最良パラメータで学習済みの推定器として振る舞います。scikit-learn公式は、前処理を含めずに単一の推定器だけを探索対象にすることを避けるよう注意を促しています。データ全体に前処理をかけてから交差検証すると、手順2と同じデータリークが起きるためです。探索対象は常にPipeline全体にするのが安全です。
探索空間をどう渡すか
param_distributionsはパラメータ名と分布のペアで指定します。整数の範囲ならscipy.stats.randint、連続値ならscipy.stats.uniformのように、値の性質に合った分布を選びます。Claude Codeに探索を依頼するときは、パラメータ名だけでなく「木の数は1〜5、深さは5〜10の範囲で探索して」のように具体的な範囲を渡すと、根拠のない探索空間を勝手に決められずに済みます。
よくあるつまずき
- スケーリングを分割前にかけてしまう: StandardScalerなどを訓練データとテストデータの分割前にfitすると、テスト側の情報が学習に漏れます。Pipelineに包んで分割後の学習データだけでfitさせる運用を徹底します。
- random_stateを固定し忘れる: シードを固定しないと、同じコードでも実行のたびにスコアがぶれ、Claude Codeが出した数値が再現できなくなります。推定器と分割の両方で
random_stateを明示するよう依頼します。 - 単一の分割・単一のスコアだけで判断する:
train_test_splitの1回の結果だけを根拠にすると、たまたま良い分割を引いた可能性を見落とします。cross_validateで複数の分割のスコアを確認します。 - 探索対象が単一の推定器のままになっている: RandomizedSearchCVの探索対象を前処理抜きの推定器だけにすると、前処理を後付けで加えたときに再現できない過大評価が出ます。探索は前処理込みのPipeline全体に対して行います。
- Claude Codeの出力が「エラーなく動いた」で完了になる:
accuracy_scoreが高い数値を返しても、その数値自体がデータリークで水増しされている場合があります。動いたことと正しく評価できていることは別で、Claude Codeデバッグの精度は、渡す情報の質で決まるで扱っている「完了条件を明示する」考え方がここでも有効です。pytestでテストを書きながら進める場合は、失敗の記録を引き継げる仕組みとしてpytestの失敗をMCPサーバーでClaudeに解析させるも参考になります。
Claude Codeのモードをどう使い分けるか
モデル実装の各段階で必要な確認の強さは異なります。着手前に構成を確認したい場面と、コードを書かせてすぐ実行させたい場面を同じモードで進める必要はありません。
| 場面 | 向くモード | 理由 |
|---|---|---|
| Pipelineの構成やスコアリング方針を決める段階 | 向くモードPlanモード | 理由編集前に方針を確認できる。ステータスバーに⏸ plan mode onと表示され、コードはまだ書き換わらない |
| fit/predictや評価スクリプトを書いて実行させる段階 | 向くモード通常モード | 理由コード編集とBashでの実行を都度確認しながら進められる |
| CIやバッチで評価結果だけを取得したい段階 | 向くモードclaude -pによる非対話実行 | 理由標準入力・出力を使ってスクリプトから呼び出せる。評価ログをパイプで渡して要約させることもできる |
Planモードはclaude --permission-mode planで開始するか、セッション中にShift+Tabで切り替えます。非対話実行はgit log --oneline -20 | claude -p "summarize these recent commits"のように、Unixのパイプと同じ感覚でClaude Codeに標準入力を渡せます。評価スクリプトの実行結果をログとして貼り付け、「このログから交差検証のスコアが分割ごとにどれくらいブレているか要約して」と-p経由で渡す使い方もできます。
まとめ
scikit-learnのモデル実装は、fit/predictの基本形、Pipelineによる前処理と推定器の結合、cross_validateによる評価、RandomizedSearchCVによるハイパーパラメータ探索という4つの部品の組み合わせです。Claude CodeはBashツールでコードを実際に実行できるため、各部品を書かせてはその場で結果を確認する進め方に向いています。ただしデータリークのように「動くが正しくない」コードは実行結果だけでは見抜けないため、Pipelineへの封じ込めとrandom_stateの固定をCLAUDE.mdなどの規約として渡しておくことが、実装の質を安定させる分かれ目になります。