閉じる

はじめてのscikit-learn (回帰/分類)をPythonで動かす

機械学習の第一歩として、scikit-learnを使って回帰と分類を体験してみます。

設定から最小実装、評価や交差検証、前処理までを一つずつ確認しながら進めます。

Python初心者でも動かせるサンプルコードと実行結果を示し、つまずきやすいポイントの回避策もまとめます。

scikit-learnの基本と始め方

scikit-learnとは

scikit-learnは、Pythonで古典的な機械学習(回帰/分類/クラスタリング/次元削減など)を統一的なAPIで扱えるライブラリです。

特徴は以下の通りです。

モデルはfitで学習し、predictで予測し、scoreや各種評価関数で性能を測るという一貫した流れになっています。

さらに、Pipelineで前処理とモデルをつなげられ、train_test_splitcross_validateなどの補助機能も豊富です。

ディープラーニングではなく、まずは基本モデルを試したい場合に最適です。

インストールと準備

インストールはpipで行います。

仮想環境の利用を推奨しますが、まずは手元で動かしてみましょう。

Shell
# Pythonのバージョン確認(3.9+推奨)
python -V

# scikit-learnと周辺ツールのインストール
pip install scikit-learn numpy pandas matplotlib seaborn

# Jupyterで試す場合
pip install jupyterlab

動作確認の最小コード

以下のスクリプトでscikit-learnが動くかを確認します。

Python
# ファイル名の例: 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) → 評価です。

回帰の超ミニ例を示します。

Python
# 最小の回帰例: 合成データで線形回帰を試す
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は学習用のサンプルデータを同梱しています。

まずはサンプルで動かして感触を掴むのが近道です。

Python
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にして中身を眺めると理解が早まります。

Python
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

データ分割

評価のために学習用とテスト用に分けます。

テスト用は最後まで触らないことが重要です。

Python
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で実行します。

Python
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二乗誤差の平均(外れ値に敏感)小さいほど良い
RMSEMSEの平方根(元スケールに戻る)小さいほど良い
R^2決定係数(0〜1程度、1が理想)大きいほど良い

交差検証

データ分割の偶然に左右されないように、交差検証を使って安定した評価を得ます。

Python
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で一体化するのが安全です。

学習前にテストデータへ情報が漏れないよう(データリーク防止)、前処理も学習データから学ぶのが原則です。

Python
# 数値: 欠損補完→標準化、カテゴリ: 欠損補完→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クラス分類です。

Python
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が重要です。

Python
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も確認します。

Python
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)F1ROC-AUCも確認します。

Python
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でクラス比を保ちながら評価します。

Python
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'なども検討します。

Python
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のようなエラーは、配列形状が原因であることがほとんどです。

Python
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を固定しましょう。

Python
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/LassoLogisticRegression)を使う
  • 木系は深さ制限や木数を調整(max_depth, n_estimators)
  • 前処理・特徴量作成はPipelineにまとめてデータリークを防ぐ
Python
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_estimatorsmax_depthを抑える、並列化n_jobs=-1を使う、浮動小数点をfloat32に落とすなどの工夫も効果的です。

Python
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ごと保存すると実運用が楽になります。

Python
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へと広げることで、少ないコードで堅実な機械学習の原型を作れます。

慣れてきたらハイパーパラメータ探索や特徴量エンジニアリングに挑戦し、より高い精度と再現性を手に入れていきましょう。

この記事を書いた人
エーテリア編集部
エーテリア編集部

人気のPythonを初めて学ぶ方向けに、文法の基本から小さな自動化まで、実際に手を動かして理解できる記事を書いています。

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!