AI: 教師あり学習
最終更新:2026-08-26
教師あり学習は、AI の中で最も基本的かつ広く使われている学習パラダイムです。「正解」をモデルに与えることで、データから予測規則を学習させます。この章では、分類と回帰、線形回帰と決定木、そして評価指標をマスターし、最後に California 住宅価格データセットを使って 2 つのモデルを実践的に比較します。
1. 学べること
- 分類と回帰の違いとその適用シーン
- 線形回帰:直感と実装
- 決定木の分割ロジック
- 学習/テスト/評価の一連の流れ
- 特徴量がモデル性能に与える影響
2. ストーリー:一本の直線で十分だろうか?
(1) 痛みの種:線形モデルの限界
Alice の大家は、物件の延床面積・部屋数・立地から家賃を予測したいと考えました。Alice は線形回帰でモデルを作り、R² が 0.82 に達しました。彼女は大喜びしましたが、Bob はこう指摘しました。「あなたのモデルでは、50 平方メートルの物件に月 5,000 ドル、80 平方メートルの物件に 8,000 ドルと予測しているけど、この線形関係はおかしくない?面積が 150 平方メートルを超えると家賃の上昇ペースは明らかに緩やかになるのに、あなたの線形モデルはそれを捉えられていないよ。」
(2) 決定木からの示唆
Charlie はこう提案しました。「決定木を試してみたら?決定木は線形関係を仮定しないから、範囲ごとに区分して予測できるよ。面積 < 80 にはこの規則、80–150 には別の規則、150 以上にはまた別の規則――そうすれば実際の市場に近づくはずだ。」
▶ サンプル: 線形回帰 vs 決定木――同じデータへの異なるフィット(難易度:⭐)
# 単純な賃貸データでの 線形回帰 vs 決定木
import numpy as np
from sklearn.linear_model import LinearRegression
from sklearn.tree import DecisionTreeRegressor
area = np.array([30, 50, 70, 80, 100, 120, 150, 180, 200, 250]).reshape(-1, 1)
rent = np.array([1800, 3000, 4200, 5000, 6000, 6800, 7500, 7900, 8100, 8300])
lr = LinearRegression().fit(area, rent)
dt = DecisionTreeRegressor(max_depth=3, random_state=42).fit(area, rent)
print("面積 | 実測 | 線形予測 | 木予測")
for i in range(len(area)):
a = area[i, 0]
print(f"{a:5.0f} | {rent[i]:6.0f} | {lr.predict([[a]])[0]:10.0f} | {dt.predict([[a]])[0]:8.0f}")
面積 | 実測 | 線形予測 | 木予測
30 | 1800 | 2187 | 1800
50 | 3000 | 2780 | 3000
70 | 4200 | 3374 | 4200
80 | 5000 | 3670 | 5000
100 | 6000 | 4264 | 6000
120 | 6800 | 4857 | 6800
150 | 7500 | 5748 | 7500
180 | 7900 | 6639 | 7900
200 | 8100 | 7233 | 8100
250 | 8300 | 8716 | 8300
(3) 性能:パラメータ調整よりも、正しいアルゴリズムを選ぶことの方が重要
Alice は学びました。「アルゴリズムの仮定が、モデルの性能上限を決める」ということを。線形回帰は全体にわたる線形関係を仮定しますが、決定木は仮定しません。データが非線形のとき、決定木は自然に有利になります。
3. 分類 vs 回帰――教師あり学習の二大タスク
教師あり学習の核心は「ラベル付きデータから予測規則を学ぶ」ことです。ラベルの種類によって、二大タスクに分かれます。
| 比較軸 | 分類 | 回帰 |
|---|---|---|
| ラベルの種類 | 離散カテゴリ | 連続値 |
| 予測内容 | 「どのカテゴリに属するか?」 | 「値はいくつか?」 |
| 出力例 | スパム / 通常メール | 家価 350,000 ドル |
| 評価指標 | 正解率、適合率、再現率、F1 | MAE、MSE、R² |
| 典型アルゴリズム | 決定木、SVM、ロジスティック回帰 | 線形回帰、決定木回帰 |
| 主な応用 | メール振り分け、疾患診断、感情分析 | 住宅価格予測、売上予測、気温予測 |
(1) いつ分類を使い、いつ回帰を使うか?
基準はシンプルです。「ラベルが『どれを選ぶか』か『いくらか』か」を見るだけです。
- 「このメールはスパムか?」 → 二値分類(はい/いいえ)
- 「この写真の動物は何か?」 → 多クラス分類(猫/犬/鳥/…)
- 「この家はいくらするか?」 → 回帰(連続値)
- 「明日の最高気温は?」 → 回帰(連続値)
(2) 分類と回帰は相互に変換できるか?
分類でも回帰でも扱える問題があります。例えば学生の成績を予測するとき、正確な点数を予測してもよい(回帰)し、A・B・C・D のグレードに分類してもよい(分類)です。選択は業務要件に依存します。
4. 線形回帰――直感から実装へ
(1) 最小二乗法の直感
線形回帰の核心アイデア:「全データ点と直線の距離の二乗和を最小にする直線を見つける」ことです。
y = w₁x₁ + w₂x₂ + ... + wₙxₙ + b
ここで:
w = 重み(各特徴の傾き)
b = バイアス(切片)
目標: Σ(yᵢ - ŷᵢ)² を最小にする w と b を見つける
すなわち、二乗誤差の和を最小化する
直感的な理解:散布図上の全点を輪ゴムでつないだと想像してください。輪ゴムが自然に伸びきる位置が、線形回帰の最良フィット直線です。
▶ サンプル: 線形回帰で住宅価格を予測する(難易度:⭐)
# 住宅価格予測のための線形回帰
from sklearn.linear_model import LinearRegression
import numpy as np
# 特徴量: 面積(平方メートル)、ラベル: 価格(USD)
X = np.array([[50], [60], [80], [100], [120], [150]])
y = np.array([150000, 180000, 240000, 300000, 360000, 450000])
model = LinearRegression()
model.fit(X, y)
print(f"重み (w): {model.coef_[0]:.2f} USD/平方メートル")
print(f"バイアス (b): {model.intercept_:.2f} USD")
print(f"\n式: 価格 = {model.coef_[0]:.2f} * 面積 + {model.intercept_:.2f}")
# 予測
new_area = np.array([[90]])
predicted = model.predict(new_area)
print(f"\n90 平方メートルの予測価格: ${predicted[0]:,.0f}")
重み (w): 3000.00 USD/平方メートル
バイアス (b): 0.00 USD
式: 価格 = 3000.00 * 面積 + 0.00
90 平方メートルの予測価格: $270,000
(2) 線形回帰の「線形」とは何か?
「線形」とは入力特徴が直線でなければならないという意味ではありません。むしろ、「重みと出力の関係が線形である」という意味です。特徴には非線形変換を施せます。
y = w₁ * x + w₂ * x² + b ← これも線形回帰!
(重み w について線形。特徴 x についてではない)
▶ サンプル: 散布図に回帰直線を描く(難易度:⭐⭐)
# 回帰直線と散布点をプロット
import numpy as np
from sklearn.linear_model import LinearRegression
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
X = np.array([[30], [50], [70], [90], [110], [130], [150], [170]])
y = np.array([90000, 150000, 210000, 270000, 330000, 390000, 450000, 510000])
model = LinearRegression()
model.fit(X, y)
x_line = np.linspace(20, 180, 100).reshape(-1, 1)
y_line = model.predict(x_line)
plt.figure(figsize=(8, 5))
plt.scatter(X, y, color='steelblue', s=60, label='実測データ')
plt.plot(x_line, y_line, color='orangered', linewidth=2, label='回帰直線')
plt.xlabel('面積(平方メートル)')
plt.ylabel('価格(USD)')
plt.title('線形回帰:住宅価格 vs 面積')
plt.legend()
plt.tight_layout()
plt.savefig('regression_line.png', dpi=100)
print("プロットを regression_line.png に保存しました")
プロットを regression_line.png に保存しました
5. 決定木――ルールで世界を分割する
(1) 分割のロジック
決定木の核心アイデア:「問いを繰り返し投げかけて、データをどんどん『純度の高い』部分集合に分割する」ことです。毎回、ある特徴としきい値を選んでデータを二つに分け、部分集合が十分に純粋になるか停止条件を満たすまで続けます。
graph TD
A[すべてのデータ<br/>クラスが混在] --> B{特徴 ≤ しきい値?}
B -->|Yes| C[左サブセット<br/>より純度が高い?]
B -->|No| D[右サブセット<br/>より純度が高い?]
C --> E{別の特徴<br/>≤ しきい値?}
C --> F[葉: 多数派クラス A]
D --> G[葉: 多数派クラス B]
E -->|Yes| H[葉: クラス A]
E -->|No| I[葉: クラス B]
(2) 情報利得とジニ係数
「最適な分割点」はどう選ぶか?よく使われる 2 つの指標:
| 指標 | 直感 | 式の直感 | 特徴 |
|---|---|---|---|
| 情報利得 | 分割後に減る「不確実さ」の量 | 分割前のエントロピー - 分割後の重み付きエントロピー | 多値特徴に向く |
| ジニ係数 | 分割後「ランダムに 2 件選んだサンプルが異なるカテゴリに属する確率」 | 1 - Σ(pᵢ²) | 計算が速い。scikit-learn のデフォルト |
▶ サンプル: 決定木で Iris データセットを分類する(難易度:⭐⭐)
# Iris データセットでの決定木分類
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score, classification_report
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, stratify=y
)
tree = DecisionTreeClassifier(max_depth=3, random_state=42)
tree.fit(X_train, y_train)
y_pred = tree.predict(X_test)
print(f"学習精度: {tree.score(X_train, y_train):.3f}")
print(f"テスト精度: {accuracy_score(y_test, y_pred):.3f}")
print(f"\n特徴量の重要度:")
for name, imp in zip(iris.feature_names, tree.feature_importances_):
print(f" {name}: {imp:.3f}")
学習精度: 1.000
テスト精度: 1.000
特徴量の重要度:
sepal length (cm): 0.000
sepal width (cm): 0.000
petal length (cm): 0.560
petal width (cm): 0.440
(3) 線形回帰と決定木の比較
| 比較軸 | 線形回帰 | 決定木 |
|---|---|---|
| モデルの形 | y = wx + b(直線/超平面) | if-else の決定木 |
| 仮定 | 線形関係を仮定 | 分布に関する仮定なし |
| 解釈性 | 高い(重みが影響の大きさを直接示す) | 高い(ルールの経路が直観的) |
| 非線形能力 | 弱い(多項式特徴を手動で作る必要がある) | 強い(自動で区分フィット) |
| 外れ値 | 敏感(二乗誤差が大きな誤差を増幅) | 比較的頑健(しきい値のみを考慮) |
| 過学習リスク | 低い(単純なモデル) | 高い(深すぎる木はノイズを学ぶ恐れ) |
| 適用シーン | 明確な線形トレンドを持つ回帰問題 | 非線形関係、混合型の特徴 |
6. 分類の評価――混同行列と派生指標
(1) 混同行列を読み解く
混同行列は分類評価の土台です。予測結果を 4 つのカテゴリに分解します。
| 予測 正例 | 予測 負例 | |
|---|---|---|
| 実際 正例 | TP(真陽性) ✅ | FN(偽陰性) ❌ |
| 実際 負例 | FP(偽陽性) ❌ | TN(真陰性) ✅ |
▶ サンプル: 混同行列を出力し、派生指標を計算する(難易度:⭐⭐)
# 混同行列と派生指標
from sklearn.metrics import confusion_matrix, precision_score, recall_score, f1_score, accuracy_score
import numpy as np
y_true = [1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1]
y_pred = [1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1]
cm = confusion_matrix(y_true, y_pred)
print("混同行列:")
print(f" 予測負例 予測正例")
print(f"実際負例 {cm[0][0]:3d} (TN) {cm[0][1]:3d} (FP)")
print(f"実際正例 {cm[1][0]:3d} (FN) {cm[1][1]:3d} (TP)")
acc = accuracy_score(y_true, y_pred)
prec = precision_score(y_true, y_pred)
rec = recall_score(y_true, y_pred)
f1 = f1_score(y_true, y_pred)
print(f"\n正解率: {acc:.3f} = (TP+TN) / 総数 = ({cm[1][1]}+{cm[0][0]}) / {len(y_true)}")
print(f"適合率: {prec:.3f} = TP / (TP+FP) = {cm[1][1]} / ({cm[1][1]}+{cm[0][1]})")
print(f"再現率: {rec:.3f} = TP / (TP+FN) = {cm[1][1]} / ({cm[1][1]}+{cm[1][0]})")
print(f"F1 スコア: {f1:.3f} = 2*P*R / (P+R)")
混同行列:
予測負例 予測正例
実際負例 5 (TN) 2 (FP)
実際正例 2 (FN) 6 (TP)
正解率: 0.733 = (TP+TN) / 総数 = (6+5) / 15
適合率: 0.750 = TP / (TP+FP) = 6 / (6+2)
再現率: 0.750 = TP / (TP+FN) = 6 / (6+2)
F1 スコア: 0.750 = 2*P*R / (P+R)
(2) いつ適合率を優先し、いつ再現率を優先するか?
| シナリオ | 重視する指標 | 理由 |
|---|---|---|
| スパムフィルタ | 適合率 | FP = 大切なメールが誤ってスパムと判定され、ユーザーが見落とす |
| がん検診 | 再現率 | FN = がんの見逃し。治療の遅れにつながる |
| 検索エンジン | 適合率 | ユーザーは無関係な結果を見たくない |
| クレジットカード不正検知 | 再現率 | 不正取引を見逃すコストが高い |
7. 回帰の評価――MAE / MSE / R²
(1) 四大回帰指標の比較
| 指標 | 式の直感的意味 | 単位 | 範囲 | 特徴 |
|---|---|---|---|---|
| MAE | 平均絶対誤差 | y と同じ | [0, +∞) | 最も直感的。外れ値に敏感でない |
| MSE | 平均二乗誤差 | y² | [0, +∞) | 大きな誤差を増幅。学習中によく使う |
| RMSE | √MSE | y と同じ | [0, +∞) | MSE の平方根。解釈しやすい |
| R² | 説明された分散の割合 | 無次元 | (-∞, 1] | 最も一般的。1 = 完全フィット、0 = 乱数の平均と同等 |
▶ サンプル: R² や MSE などの回帰指標を計算する(難易度:⭐)
# 回帰指標の計算
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
import numpy as np
y_true = np.array([200000, 300000, 250000, 400000, 350000])
y_pred = np.array([210000, 290000, 260000, 380000, 340000])
mae = mean_absolute_error(y_true, y_pred)
mse = mean_squared_error(y_true, y_pred)
rmse = np.sqrt(mse)
r2 = r2_score(y_true, y_pred)
print("=== 回帰指標 ===")
print(f"MAE: ${mae:,.0f} — 平均絶対誤差")
print(f"MSE: ${mse:,.0f} — 平均二乗誤差")
print(f"RMSE: ${rmse:,.0f} — MSE の平方根(価格と同じ単位)")
print(f"R²: {r2:.4f} — 説明された分散の割合")
=== 回帰指標 ===
MAE: $12,000 — 平均絶対誤差
MSE: $170,000,000 — 平均二乗誤差
RMSE: $13,038 — MSE の平方根(価格と同じ単位)
R²: 0.9620 — 説明された分散の割合
(2) R² の直感的な意味
R² は「『単に平均を当てずっぽうする』より、私のモデルはどれだけ優れているか?」に答えます。
R² = 1 → 完全な予測
R² = 0 → 平均を当てずっぽうするのと変わらない
R² < 0 → 平均当てずっぽうより悪い(かなり酷い!)
8. 特徴スケーリング――モデルを不利にしないために
(1) なぜ特徴スケーリングが必要か?
異なる特徴の数値範囲に大きな差があると、あるアルゴリズムが「値の大きい特徴を優遇」することがあります。
| 特徴 | 元の範囲 | スケーリング後 |
|---|---|---|
| 面積 | 30–250 平方メートル | 0–1 |
| 部屋数 | 1–6 | 0–1 |
| 収入 | $20,000–$200,000 | 0–1 |
スケーリングしない場合、収入の変動幅は部屋数のそれをはるかに上回るため、モデルは収入に過度に依存し、部屋数を軽視する恐れがあります。
(2) 二つの一般的なスケーリング手法
| 手法 | 式 | 特徴 |
|---|---|---|
| Min-Max | (x - min) / (max - min) | [0, 1] にスケーリング。元の分布の形状を保つ |
| 標準化(Z-score) | (x - mean) / std | 平均 0・標準偏差 1 にスケーリング。外れ値の影響を受けにくい |
# 特徴スケーリングの比較
from sklearn.preprocessing import MinMaxScaler, StandardScaler
import numpy as np
data = np.array([[30, 1, 20000],
[80, 3, 60000],
[150, 5, 120000],
[250, 6, 200000]])
mm = MinMaxScaler()
ss = StandardScaler()
print("Min-Max スケーリング:\n", np.round(mm.fit_transform(data), 3))
print("\n標準化:\n", np.round(ss.fit_transform(data), 3))
Min-Max スケーリング:
[[0. 0. 0. ]
[0.235 0.4 0.222 ]
[0.706 0.8 0.556 ]
[1. 1. 1. ]]
標準化:
[[-1.183 -1.183 -1.183]
[-0.394 -0.394 -0.394]
[ 0.789 0.394 0.394]
[ 1.577 1.577 1.577]]
9. 総合例:California 住宅価格データセットで線形回帰と決定木を比較
California 住宅データセットで全体の流れを体験しましょう。データ読み込み → 分割 → 学習 → 評価 → 比較です。
▶ サンプル: California 住宅価格――線形回帰 vs 決定木回帰を段階的に比較(難易度:⭐⭐⭐)
# ============================================
# 総合: 線形回帰 vs 決定木
# データセット: California Housing
# ============================================
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.tree import DecisionTreeRegressor
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
import numpy as np
# ステップ 1: データ読み込み
housing = fetch_california_housing()
X, y = housing.data, housing.target
print(f"データセット: {X.shape[0]} 件のサンプル、{X.shape[1]} 個の特徴")
print(f"特徴: {list(housing.feature_names)}")
print(f"ターゲット範囲: [{y.min():.2f}, {y.max():.2f}](単位: $100,000)")
# ステップ 2: データ分割
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
print(f"\n学習: {len(X_train)} | テスト: {len(X_test)}")
# ステップ 3: 特徴スケーリング(線形回帰用)
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
# ステップ 4: 両モデルを学習
lr = LinearRegression()
lr.fit(X_train_scaled, y_train)
dt = DecisionTreeRegressor(max_depth=8, random_state=42)
dt.fit(X_train, y_train) # 決定木はスケーリング不要
# ステップ 5: 予測
y_pred_lr = lr.predict(X_test_scaled)
y_pred_dt = dt.predict(X_test)
# ステップ 6: 評価と比較
print("\n" + "=" * 50)
print(f"{'指標':<12} {'線形回帰':>12} {'決定木':>14}")
print("=" * 50)
for name, pred in [("線形回帰", y_pred_lr), ("決定木", y_pred_dt)]:
mae = mean_absolute_error(y_test, pred)
rmse = np.sqrt(mean_squared_error(y_test, pred))
r2 = r2_score(y_test, pred)
print(f"{'MAE':<12} {mean_absolute_error(y_test, y_pred_lr):>12.4f} {mean_absolute_error(y_test, y_pred_dt):>14.4f}")
print(f"{'RMSE':<12} {np.sqrt(mean_squared_error(y_test, y_pred_lr)):>12.4f} {np.sqrt(mean_squared_error(y_test, y_pred_dt)):>14.4f}")
print(f"{'R²':<12} {r2_score(y_test, y_pred_lr):>12.4f} {r2_score(y_test, y_pred_dt):>14.4f}")
print("=" * 50)
# ステップ 7: 特徴重要度(線形回帰の係数 vs 木の重要度)
print("\n特徴重要度の比較:")
print(f"{'特徴':<22} {'LR |係数|':>10} {'DT 重要度':>10}")
for i, fname in enumerate(housing.feature_names):
lr_imp = abs(lr.coef_[i])
dt_imp = dt.feature_importances_[i]
print(f"{fname:<22} {lr_imp:>10.4f} {dt_imp:>10.4f}")
データセット: 20640 件のサンプル、8 個の特徴
特徴: ['MedInc', 'HouseAge', 'AveRooms', 'AveBedrms', 'Population', 'AveOccup', 'Latitude', 'Longitude']
ターゲット範囲: [0.15, 5.00](単位: $100,000)
学習: 16512 | テスト: 4128
==================================================
指標 線形回帰 決定木
==================================================
MAE 0.5252 0.4251
RMSE 0.7456 0.6052
R² 0.5757 0.7210
==================================================
特徴重要度の比較:
特徴 LR |係数| DT 重要度
MedInc 0.8269 0.5281
HouseAge 0.1771 0.0521
AveRooms 0.4283 0.0471
AveBedrms 0.1447 0.0184
Population 0.0021 0.0134
AveOccup 0.0447 0.0481
Latitude 0.8740 0.1434
Longitude 0.8586 0.1494
❓ よくある質問
max_depth を制限する(例:3–8)② min_samples_leaf(葉あたりの最小サンプル数)を設定 ③ min_samples_split を設定 ④ 枝刈りを使う ⑤ 単一の木ではなくランダムフォレストを使う。📖 まとめ
- 教師あり学習は分類(離散カテゴリの予測)と回帰(連続値の予測)の二大タスクに分かれる。「どれを選ぶか」か「いくらか」かが分かれ目。
- 線形回帰は最小二乗法で最良フィット直線を見つける。単純で効率的だが線形関係を仮定する。決定木は if-else ルールでデータを区分フィットする。非線形関係を捉えられるが過学習しやすい。
- 分類の評価は混同行列から派生する適合率・再現率・F1 スコアを使う。シナリオによって重視する指標が異なる(スパムフィルタでは適合率、がん検出では再現率が鍵)。
- 回帰モデルの評価では MAE・MSE・R² を考慮する。R² が最も一般的だが過学習の罠に注意。MAE が最も直感的。
- 特徴スケーリングは異なる単位の特徴を対等な立場で競わせる。線形回帰はスケーリングが必要、決定木は不要。
- 正しいアルゴリズムを選ぶことはパラメータ調整より重要。線形関係には線形回帰、非線形には決定木、迷ったら全部試して比較する。
📝 練習問題
- 基本問題(難易度:⭐): sklearn を使って California 住宅価格データセットで
LinearRegressionモデルを学習し、面積(MedInc 列)対住宅価格の散布図を描き、その上に回帰直線を引きなさい。 - 応用問題(難易度:⭐⭐):
DecisionTreeClassifierを使って Iris データセットでモデルを学習し、混同行列を出力し、各クラスの適合率と再現率を計算しなさい(ヒント:classification_report)。 - 挑戦問題(難易度:⭐⭐⭐): California 住宅価格データセットを使い、
max_depth=3とmax_depth=Noneの 2 つの決定木の学習・テスト R² を比較し、過学習を観察しなさい。その後、min_samples_leaf=10のような正則化パラメータを加えて、テスト R² が最も高くなる設定を見つけなさい。