機械学習の第一歩として、scikit-learnを使って回帰と分類を体験してみます。
設定から最小実装、評価や交差検証、前処理までを一つずつ確認しながら進めます。
Python初心者でも動かせるサンプルコードと実行結果を示し、つまずきやすいポイントの回避策もまとめます。
scikit-learnの基本と始め方
scikit-learnとは
scikit-learnは、Pythonで古典的な機械学習(回帰/分類/クラスタリング/次元削減など)を統一的なAPIで扱えるライブラリです。
特徴は以下の通りです。
モデルはfit
で学習し、predict
で予測し、score
や各種評価関数で性能を測るという一貫した流れになっています。
さらに、Pipeline
で前処理とモデルをつなげられ、train_test_split
やcross_validate
などの補助機能も豊富です。
ディープラーニングではなく、まずは基本モデルを試したい場合に最適です。
インストールと準備
インストールはpipで行います。
仮想環境の利用を推奨しますが、まずは手元で動かしてみましょう。
# Pythonのバージョン確認(3.9+推奨)
python -V
# scikit-learnと周辺ツールのインストール
pip install scikit-learn numpy pandas matplotlib seaborn
# Jupyterで試す場合
pip install jupyterlab
動作確認の最小コード
以下のスクリプトでscikit-learnが動くかを確認します。
# ファイル名の例: check_sklearn.py
from sklearn import __version__ as skver
import numpy as np
print("scikit-learn version:", skver)
print("NumPy OK:", np.array([1, 2, 3]).mean())
scikit-learn version: 1.5.0
NumPy OK: 2.0
最小の流れ
機械学習の一連の流れは、データ準備 → 学習データ/テストデータ分割 → モデル選択 → 学習(fit
) → 予測(predict
) → 評価です。
回帰の超ミニ例を示します。
# 最小の回帰例: 合成データで線形回帰を試す
from sklearn.datasets import make_regression
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error, r2_score
import numpy as np
# 1) データ準備(合成)
X, y = make_regression(n_samples=200, n_features=3, noise=10.0, random_state=42)
# 2) 学習/テスト分割
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=0
)
# 3) モデル選択 → 線形回帰
model = LinearRegression()
# 4) 学習
model.fit(X_train, y_train)
# 5) 予測
y_pred = model.predict(X_test)
# 6) 評価(RMSEとR^2)
rmse = np.sqrt(mean_squared_error(y_test, y_pred))
r2 = r2_score(y_test, y_pred)
print(f"RMSE: {rmse:.2f}")
print(f"R^2: {r2:.3f}")
RMSE: 10.85
R^2: 0.986
サンプルデータの使い方
scikit-learnは学習用のサンプルデータを同梱しています。
まずはサンプルで動かして感触を掴むのが近道です。
from sklearn.datasets import load_iris, load_diabetes, load_breast_cancer
iris = load_iris() # 多クラス分類(アヤメ)
diabetes = load_diabetes() # 回帰(糖尿病データ)
cancer = load_breast_cancer() # 二値分類(乳がん)
# Xは特徴量(2次元配列)、yは目的変数(1次元配列)
print(iris.data.shape, iris.target.shape) # (150, 4) (150,)
print(diabetes.data.shape, diabetes.target.shape) # (442, 10) (442,)
print(cancer.data.shape, cancer.target.shape) # (569, 30) (569,)
(150, 4) (150,)
(442, 10) (442,)
(569, 30) (569,)
サンプル以外に、pandas.read_csv
でCSVを読み込むこともできます。
実務ではこちらが中心になります。
回帰モデルを試す
データ読み込み
ここでは回帰タスクとしてload_diabetes
を使います。
最初はDataFrameにして中身を眺めると理解が早まります。
import pandas as pd
from sklearn.datasets import load_diabetes
diabetes = load_diabetes()
X = pd.DataFrame(diabetes.data, columns=diabetes.feature_names)
y = pd.Series(diabetes.target, name="target")
print(X.head(3))
print("y describe:\n", y.describe())
age sex bmi bp s1 s2 s3 s4 s5 s6
0 0.038076 0.050680 0.061696 0.021872 -0.044223 -0.034821 -0.043401 -0.002592 0.019907 -0.017646
1 -0.001882 -0.044642 -0.051474 -0.026328 -0.008449 -0.019163 0.074412 -0.039493 -0.068332 -0.092204
2 0.085299 0.050680 0.044451 -0.005670 -0.045599 -0.034194 -0.032356 -0.002592 0.002861 -0.025930
y describe:
count 442.000000
mean 152.133484
std 77.093005
min 25.000000
25% 87.000000
50% 140.500000
75% 211.500000
max 346.000000
Name: target, dtype: float64
データ分割
評価のために学習用とテスト用に分けます。
テスト用は最後まで触らないことが重要です。
from sklearn.model_selection import train_test_split
from sklearn.datasets import load_diabetes
import pandas as pd
diabetes = load_diabetes()
X = pd.DataFrame(diabetes.data, columns=diabetes.feature_names)
y = pd.Series(diabetes.target, name="target")
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
print(X_train.shape, X_test.shape)
(353, 10) (89, 10)
モデル選択
最初はシンプルなモデルから試し、必要に応じて複雑なモデルに移行します。
- 線形モデル:
LinearRegression
,Ridge
,Lasso
- 木系モデル:
RandomForestRegressor
,GradientBoostingRegressor
以下は代表的なモデルの特徴です。
モデル | 特徴 | 前処理との相性 |
---|---|---|
LinearRegression | 解釈しやすく学習が高速 | スケーリングは必須ではない |
Ridge/Lasso | 正則化で過学習抑制 | スケーリング推奨 |
RandomForest | 非線形に強い・外れ値に頑健 | スケーリング不要 |
GradientBoosting | 高精度だがチューニング必要 | スケーリング不要 |
学習と予測
ここでは正則化付きのRidge回帰をスケーリングと一緒にPipeline
で実行します。
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import Ridge
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
from sklearn.model_selection import train_test_split
from sklearn.datasets import load_diabetes
import pandas as pd
import numpy as np
diabetes = load_diabetes()
X = pd.DataFrame(diabetes.data, columns=diabetes.feature_names)
y = pd.Series(diabetes.target, name="target")
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
# スケーリング → Ridge回帰
pipe = Pipeline([
("scaler", StandardScaler()),
("model", Ridge(alpha=1.0, random_state=42))
])
pipe.fit(X_train, y_train)
y_pred = pipe.predict(X_test)
mae = mean_absolute_error(y_test, y_pred)
rmse = np.sqrt(mean_squared_error(y_test, y_pred))
r2 = r2_score(y_test, y_pred)
print(f"MAE: {mae:.2f}")
print(f"RMSE: {rmse:.2f}")
print(f"R^2: {r2:.3f}")
MAE: 42.81
RMSE: 53.78
R^2: 0.454
評価指標
回帰では状況に応じて複数の指標を見ます。
1つの指標だけで判断しないことが大切です。
指標 | 説明 | 良い方向 |
---|---|---|
MAE | 絶対誤差の平均 | 小さいほど良い |
MSE | 二乗誤差の平均(外れ値に敏感) | 小さいほど良い |
RMSE | MSEの平方根(元スケールに戻る) | 小さいほど良い |
R^2 | 決定係数(0〜1程度、1が理想) | 大きいほど良い |
交差検証
データ分割の偶然に左右されないように、交差検証を使って安定した評価を得ます。
from sklearn.model_selection import cross_validate, KFold
import numpy as np
cv = KFold(n_splits=5, shuffle=True, random_state=42)
scores = cross_validate(
pipe, X, y, cv=cv, n_jobs=-1,
scoring=("r2", "neg_mean_absolute_error", "neg_mean_squared_error"),
return_train_score=False
)
mae_cv = -scores["test_neg_mean_absolute_error"]
rmse_cv = np.sqrt(-scores["test_neg_mean_squared_error"])
print(f"R^2 mean: {scores['test_r2'].mean():.3f} ± {scores['test_r2'].std():.3f}")
print(f"MAE mean: {mae_cv.mean():.2f}")
print(f"RMSE mean: {rmse_cv.mean():.2f}")
R^2 mean: 0.442 ± 0.055
MAE mean: 43.90
RMSE mean: 57.60
前処理が必要な場合
実務では欠損値やカテゴリ変数を含む表形式データ(CSV)が一般的です。
データ前処理はPipelineとColumnTransformerで一体化するのが安全です。
学習前にテストデータへ情報が漏れないよう(データリーク防止)、前処理も学習データから学ぶのが原則です。
# 数値: 欠損補完→標準化、カテゴリ: 欠損補完→One-Hot、モデル: ランダムフォレスト
import pandas as pd
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import OneHotEncoder, StandardScaler
from sklearn.impute import SimpleImputer
from sklearn.ensemble import RandomForestRegressor
from sklearn.pipeline import Pipeline
from sklearn.model_selection import train_test_split
# 例: house.csvとして数値列とカテゴリ列を持つデータを想定
# df = pd.read_csv("house.csv")
# ここでは例示としてダミーデータを作成します
df = pd.DataFrame({
"area": [50, 80, 60, None, 45], # 数値(欠損あり)
"age": [10, 5, 20, 15, None], # 数値(欠損あり)
"station": ["A", "B", "A", "C", "B"], # カテゴリ
"layout": ["2LDK", "3LDK", "2DK", "1LDK", "2LDK"], # カテゴリ
"price": [1800, 3200, 2000, 1600, 2100] # 目的変数
})
num_cols = ["area", "age"]
cat_cols = ["station", "layout"]
X = df[num_cols + cat_cols]
y = df["price"]
numeric_pipe = Pipeline([
("imputer", SimpleImputer(strategy="median")),
("scaler", StandardScaler())
])
categorical_pipe = Pipeline([
("imputer", SimpleImputer(strategy="most_frequent")),
("onehot", OneHotEncoder(handle_unknown="ignore"))
])
preprocess = ColumnTransformer([
("num", numeric_pipe, num_cols),
("cat", categorical_pipe, cat_cols),
])
model = RandomForestRegressor(n_estimators=200, random_state=42, n_jobs=-1)
pipe = Pipeline([
("preprocess", preprocess),
("model", model)
])
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.4, random_state=42
)
pipe.fit(X_train, y_train)
print("学習完了。前処理とモデルが一体化されています。")
学習完了。前処理とモデルが一体化されています。
分類モデルを試す
データ読み込み
二値分類としてload_breast_cancer
を利用します。
乳がんデータで、0と1の2クラス分類です。
import pandas as pd
from sklearn.datasets import load_breast_cancer
cancer = load_breast_cancer()
X = pd.DataFrame(cancer.data, columns=cancer.feature_names)
y = pd.Series(cancer.target, name="target")
print(X.shape, y.value_counts().to_dict()) # クラス分布の確認
(569, 30) {1: 357, 0: 212}
データ分割
分類ではクラス比率を保つstratify
が重要です。
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, stratify=y, random_state=42
)
print(y_train.value_counts(normalize=True).round(3).to_dict())
{1: 0.627, 0: 0.373}
モデル選択
最初の一歩としてはロジスティック回帰がおすすめです。
線形判別が効かなければ木系モデルやSVMへ広げます。
- 線形:
LogisticRegression
(軽量・解釈しやすい) - 木系:
RandomForestClassifier
(非線形に強い) - SVM:
SVC
(カーネルで柔軟だがスケーリング必須)
学習と予測
ロジスティック回帰はスケーリングと組み合わせるのが定石です。
予測確率predict_proba
も確認します。
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
clf = Pipeline([
("scaler", StandardScaler()),
("model", LogisticRegression(max_iter=1000, random_state=42))
])
clf.fit(X_train, y_train)
y_pred = clf.predict(X_test)
y_proba = clf.predict_proba(X_test)[:, 1] # クラス1の確率
print("最初の5件 予測/確率:", list(zip(y_pred[:5], y_proba[:5].round(3))))
最初の5件 予測/確率: [(1, 0.997), (1, 0.999), (0, 0.031), (1, 0.998), (1, 0.996)]
評価指標
分類では複数の観点を併せて評価します。
正解率(Accuracy)だけだと見誤ることがあるため、適合率(Precision)、再現率(Recall)、F1、ROC-AUCも確認します。
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, roc_auc_score, confusion_matrix, classification_report
acc = accuracy_score(y_test, y_pred)
pre = precision_score(y_test, y_pred)
rec = recall_score(y_test, y_pred)
f1 = f1_score(y_test, y_pred)
auc = roc_auc_score(y_test, y_proba)
cm = confusion_matrix(y_test, y_pred)
print(f"Accuracy: {acc:.3f}")
print(f"Precision: {pre:.3f}")
print(f"Recall: {rec:.3f}")
print(f"F1: {f1:.3f}")
print(f"ROC-AUC: {auc:.3f}")
print("Confusion matrix:\n", cm)
print("Report:\n", classification_report(y_test, y_pred, digits=3))
Accuracy: 0.982
Precision: 0.986
Recall: 0.986
F1: 0.986
ROC-AUC: 0.995
Confusion matrix:
[[41 1]
[ 1 71]]
Report:
precision recall f1-score support
0 0.976 0.976 0.976 42
1 0.986 0.986 0.986 72
accuracy 0.982 114
macro avg 0.981 0.981 0.981 114
weighted avg 0.982 0.982 0.982 114
交差検証
分類ではStratifiedKFold
でクラス比を保ちながら評価します。
from sklearn.model_selection import cross_validate, StratifiedKFold
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
scores = cross_validate(
clf, X, y, cv=cv, n_jobs=-1,
scoring=("accuracy", "precision", "recall", "f1", "roc_auc")
)
print("CV Accuracy mean:", scores["test_accuracy"].mean().round(3))
print("CV Precision mean:", scores["test_precision"].mean().round(3))
print("CV Recall mean:", scores["test_recall"].mean().round(3))
print("CV F1 mean:", scores["test_f1"].mean().round(3))
print("CV ROC-AUC mean:", scores["test_roc_auc"].mean().round(3))
CV Accuracy mean: 0.972
CV Precision mean: 0.981
CV Recall mean: 0.978
CV F1 mean: 0.979
CV ROC-AUC mean: 0.995
前処理
実務の分類データはカテゴリ変数が多く、欠損も含みます。
回帰と同様にColumnTransformer
で安全に前処理を組み込みます。
また、クラス不均衡が強い場合はclass_weight='balanced'
なども検討します。
import pandas as pd
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import OneHotEncoder, StandardScaler
from sklearn.impute import SimpleImputer
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import Pipeline
# ダミーの顧客離反データ例
df = pd.DataFrame({
"age": [25, 40, None, 35, 50],
"income": [300, 500, 450, None, 700],
"plan": ["basic", "pro", "pro", "basic", "premium"],
"region": ["E", "W", "N", "S", "E"],
"churn": [0, 1, 0, 0, 1]
})
num_cols = ["age", "income"]
cat_cols = ["plan", "region"]
X = df[num_cols + cat_cols]
y = df["churn"]
numeric = Pipeline([
("imputer", SimpleImputer(strategy="median")),
("scaler", StandardScaler())
])
categorical = Pipeline([
("imputer", SimpleImputer(strategy="most_frequent")),
("onehot", OneHotEncoder(handle_unknown="ignore"))
])
preprocess = ColumnTransformer([
("num", numeric, num_cols),
("cat", categorical, cat_cols)
])
clf = Pipeline([
("preprocess", preprocess),
("model", LogisticRegression(max_iter=1000, class_weight="balanced", random_state=42))
])
clf.fit(X, y)
print("学習完了。クラス不均衡対策をclass_weightで適用しました。")
学習完了。クラス不均衡対策をclass_weightで適用しました。
初心者向けのコツとつまずき対策
データ形状とdtypeのエラー対処
scikit-learnは特徴量は2次元配列(サンプル数×特徴量数)、目的変数は1次元配列を前提にしています。
ValueError: Expected 2D array, got 1D array
のようなエラーは、配列形状が原因であることがほとんどです。
import numpy as np
from sklearn.linear_model import LinearRegression
# NG例: 1Dのまま
# X = np.array([1, 2, 3, 4]) # これだと1D
# OK例: 2Dにreshape
X = np.array([1, 2, 3, 4]).reshape(-1, 1)
y = np.array([10, 20, 30, 40])
model = LinearRegression().fit(X, y)
print("学習OK。X形状:", X.shape)
学習OK。X形状: (4, 1)
また、文字列の列が混ざっているとdtypeエラーになります。
カテゴリ列はOne-Hotエンコードし、数値列は型変換.astype(float)
で揃えます。
乱数シード(random_state)で結果を固定
データ分割や一部モデルは乱数を使います。
再現性のためにrandom_state
を固定しましょう。
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
# 分割時
X_train, X_test, y_train, y_test = train_test_split(
X, y, random_state=42, stratify=y
)
# モデルにも
rf = RandomForestClassifier(n_estimators=300, random_state=42, n_jobs=-1)
再現性のある分割と学習設定にしました。
過学習を避ける
過学習は「学習データにだけ強いが、未知データに弱い」状態です。
対策としては以下が有効です。
基本は「交差検証」と「正則化」で十分戦えます。
- データ分割と交差検証で汎化性能を確認する
- 正則化付きモデル(
Ridge
/Lasso
、LogisticRegression
)を使う - 木系は深さ制限や木数を調整(
max_depth
,n_estimators
) - 前処理・特徴量作成はPipelineにまとめてデータリークを防ぐ
from sklearn.linear_model import Ridge
from sklearn.model_selection import GridSearchCV
param_grid = {"alpha": [0.1, 1.0, 10.0, 100.0]}
search = GridSearchCV(Ridge(random_state=42), param_grid, cv=5, scoring="r2")
search.fit(X_train, y_train)
print("Best alpha:", search.best_params_["alpha"])
print("CV best R^2:", round(search.best_score_, 3))
Best alpha: 10.0
CV best R^2: 0.452
学習時間を短くする
学習時間が長いと感じたら、サンプル数・特徴量・モデルの複雑さの3点を調整します。
まずはデータのサンプルを減らしてプロトタイピングし、うまくいったら全量に広げる流れが実践的です。
木系モデルではn_estimators
やmax_depth
を抑える、並列化n_jobs=-1
を使う、浮動小数点をfloat32
に落とすなどの工夫も効果的です。
from sklearn.ensemble import RandomForestRegressor
import numpy as np
X_small = np.array(X_train, dtype=np.float32)[:500] # まずは少量で確認
y_small = np.array(y_train)[:500]
rf = RandomForestRegressor(
n_estimators=200, max_depth=8, n_jobs=-1, random_state=42
)
rf.fit(X_small, y_small)
print("小さなデータで形を作ってから全量へ拡張するのが効率的です。")
小さなデータで形を作ってから全量へ拡張するのが効率的です。
モデル保存と再利用
学習済みモデルはjoblib
で保存・読み込みできます。
前処理付きPipelineごと保存すると実運用が楽になります。
import joblib
from pathlib import Path
Path("models").mkdir(exist_ok=True)
# 保存
joblib.dump(clf, "models/cancer_logreg_pipeline.joblib")
# 読み込み
loaded = joblib.load("models/cancer_logreg_pipeline.joblib")
print(type(loaded))
<class 'sklearn.pipeline.Pipeline'>
まとめ
本記事では、scikit-learnで回帰と分類を最小限の構成から実用的な前処理・評価・交差検証まで一通り体験しました。
ポイントは「Pipelineで前処理とモデルを一体化」「交差検証で安定評価」「random_stateで再現性確保」の3つです。
まずはサンプルデータで流れを掴み、次に自前のCSVにColumnTransformerを適用してみてください。
扱うデータや目的に応じて、線形モデルから木系モデル、SVMへと広げることで、少ないコードで堅実な機械学習の原型を作れます。
慣れてきたらハイパーパラメータ探索や特徴量エンジニアリングに挑戦し、より高い精度と再現性を手に入れていきましょう。