STEP 16:サポートベクターマシン(SVM)

🎯 STEP 16: サポートベクターマシン(SVM)

マージンを最大化する強力な分類手法とカーネルトリックを学びます

📋 このステップで学ぶこと

  • SVMの基本的な考え方
  • マージンとサポートベクター
  • カーネルトリックの原理
  • 主要なカーネル(線形、RBF、多項式)
  • パラメータ(C、gamma)の調整
  • 実践:画像分類プロジェクト

演習問題: 4問

🎯 1. SVMとは?

SVMの基本的な考え方

📏 「最も安全な境界線を引く」

サポートベクターマシン(Support Vector Machine)は、2つのクラスをできるだけ大きな余裕(マージン)を持って分ける手法です。

イメージ:道路の真ん中に線を引くとき、できるだけ両側から離れた場所に引きたい
→ より安全(新しいデータにも対応しやすい)

マージンとサポートベクター

【SVMの概念図】 ○ × ○ ○ × × ○ ○ × × | | |マージ| | ン | ○ | | × ○ |境界線| × ○ | | × | | サポートベクター: 境界に最も近いデータポイント マージン: サポートベクターから境界線までの距離 【目標】マージンを最大化する境界線を見つける!
✅ SVMの特徴
  • マージン最大化:汎化性能が高い
  • カーネルトリック:非線形パターンに対応
  • 高次元データに強い:特徴量が多くても有効
  • 外れ値に比較的頑健
⚠️ 注意点
  • データが多いと遅い
  • スケーリング必須
  • パラメータ調整が必要
  • 解釈しにくい

線形SVMの実装

import numpy as np from sklearn.svm import SVC from sklearn.datasets import make_classification from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler # 線形分離可能なデータを生成 X, y = make_classification( n_samples=200, n_features=2, n_informative=2, n_redundant=0, n_clusters_per_class=1, class_sep=1.5, random_state=42 ) # データ分割 X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.3, random_state=42 ) # スケーリング(重要!) scaler = StandardScaler() X_train_scaled = scaler.fit_transform(X_train) X_test_scaled = scaler.transform(X_test) # 線形SVM svm_linear = SVC(kernel=’linear’, C=1.0, random_state=42) svm_linear.fit(X_train_scaled, y_train) print(“=== 線形SVMの結果 ===”) print(f”訓練精度: {svm_linear.score(X_train_scaled, y_train):.4f}”) print(f”テスト精度: {svm_linear.score(X_test_scaled, y_test):.4f}”) print(f”\nサポートベクターの数: {len(svm_linear.support_vectors_)}”) print(f”全訓練データに対する割合: {len(svm_linear.support_vectors_) / len(X_train) * 100:.1f}%”)
=== 線形SVMの結果 === 訓練精度: 0.9571 テスト精度: 0.9500 サポートベクターの数: 23 全訓練データに対する割合: 16.4%
💡 サポートベクターの意味

サポートベクターだけが決定境界の位置に影響します。他のデータポイントは、マージン外にあれば無視されます。
効率的で外れ値に強い!

🌀 2. カーネルトリック

非線形データの問題

❌ 線形SVMでは分類できないデータ

実際のデータは、直線や平面では分けられないことが多いです。

例:同心円状のデータ(内側の円 = クラス0、外側の円 = クラス1)
→ どこに直線を引いても分類できない!

カーネルトリックの魔法

【カーネルトリックのアイデア】 2次元空間(分離できない): × ○○○ × × ○ ○ × × ○ ○ × × ○○○ × ↓ 高次元空間に写像 3次元空間(分離できる!): 高次元では直線(超平面)で分離可能! 【ポイント】 実際には高次元計算をせず、 カーネル関数で効率的に計算(カーネルトリック)

主要なカーネル

カーネル 数式 特徴
線形
(linear)
K(x, x’) = x · x’ ・シンプル
・線形分離可能なデータ向け
・高速
RBF
(Gaussian)
K(x, x’) = exp(-γ||x – x’||²) 最もよく使われる
・柔軟
・ほとんどの問題に対応
多項式
(poly)
K(x, x’) = (γx·x’ + r)^d ・多項式の関係
・次数dで調整
・特定の問題向け

異なるカーネルの比較

from sklearn.datasets import make_circles, make_moons # 同心円データを生成 X_circles, y_circles = make_circles(n_samples=200, noise=0.1, factor=0.5, random_state=42) # スケーリング scaler = StandardScaler() X_scaled = scaler.fit_transform(X_circles) # 異なるカーネルを試す kernels = [‘linear’, ‘rbf’, ‘poly’] print(“=== カーネル別の精度(同心円データ) ===”) for kernel in kernels: svm = SVC(kernel=kernel, gamma=’auto’, random_state=42) svm.fit(X_scaled, y_circles) accuracy = svm.score(X_scaled, y_circles) print(f”{kernel:10s}: {accuracy:.4f}”)
=== カーネル別の精度(同心円データ) === linear : 0.4850 rbf : 0.9900 poly : 0.8750
🔍 観察ポイント

同心円データ:
・線形カーネル:分類できない(48.5%)
・RBFカーネル:きれいに分類できる(99.0%)
・多項式カーネル:まあまあ(87.5%)

→ 迷ったらRBFカーネルを試す!

⚙️ 3. パラメータ調整(C と gamma)

パラメータCの役割

⚖️ Cは「誤分類の許容度」

C(正則化パラメータ)は、マージンの幅と誤分類のトレードオフを調整します。

Cが小さい(例:0.1):マージンを広く取る、誤分類を多少許容、滑らかな決定境界、過学習しにくい

Cが大きい(例:100):マージンを狭くしてでも正確に分類、誤分類を許さない、複雑な決定境界、過学習しやすい

パラメータgammaの役割(RBFカーネル)

📐 gammaは「影響範囲」

gammaは、1つのサンプルの影響がどこまで及ぶかを決めます。

gammaが小さい(例:0.01):影響範囲が広い、滑らかな決定境界、過学習しにくい

gammaが大きい(例:10):影響範囲が狭い、各データポイントに密着、過学習しやすい

Cとgammaの影響

# サンプルデータ X_sample, y_sample = make_classification( n_samples=100, n_features=2, n_redundant=0, n_informative=2, random_state=42 ) scaler = StandardScaler() X_sample_scaled = scaler.fit_transform(X_sample) # データ分割 X_train, X_test, y_train, y_test = train_test_split( X_sample_scaled, y_sample, test_size=0.3, random_state=42 ) # 異なるCとgammaを試す C_values = [0.1, 1, 10, 100] gamma_values = [0.01, 0.1, 1, 10] print(“=== Cとgammaの組み合わせによる精度 ===”) print(f”{‘C’:<10} {'gamma':<10} {'Train':<10} {'Test':<10}") print("-" * 40) for C in C_values: for gamma in gamma_values: svm = SVC(kernel='rbf', C=C, gamma=gamma, random_state=42) svm.fit(X_train, y_train) train_acc = svm.score(X_train, y_train) test_acc = svm.score(X_test, y_test) print(f"{C:<10} {gamma:<10} {train_acc:<10.4f} {test_acc:<10.4f}")
=== Cとgammaの組み合わせによる精度 === C gamma Train Test —————————————- 0.1 0.01 0.9143 0.9000 0.1 0.1 0.9286 0.9333 0.1 1 0.9429 0.9333 0.1 10 0.5000 0.5333 1 0.01 0.9143 0.9000 1 0.1 0.9571 0.9333 1 1 0.9857 0.9667 1 10 0.9714 0.8667 10 0.01 0.9286 0.9000 10 0.1 0.9571 0.9333 10 1 1.0000 0.9667 10 10 1.0000 0.8000 100 0.01 0.9286 0.9000 100 0.1 0.9571 0.9333 100 1 1.0000 0.9667 100 10 1.0000 0.7667
⚠️ 過学習の兆候

C=10, gamma=10 で訓練精度100%、テスト精度80%!
→ これは過学習です。決定境界が非常に複雑になり、訓練データに過度に適応しています。

推奨:C=1, gamma=1(訓練98.57%、テスト96.67%)がバランス良い

GridSearchCVで最適パラメータを探す

from sklearn.model_selection import GridSearchCV # パラメータグリッド param_grid = { ‘C’: [0.1, 1, 10, 100], ‘gamma’: [0.001, 0.01, 0.1, 1], ‘kernel’: [‘rbf’] } # GridSearch grid_search = GridSearchCV( SVC(random_state=42), param_grid, cv=5, scoring=’accuracy’, n_jobs=-1 ) grid_search.fit(X_train, y_train) print(“=== GridSearchCVの結果 ===”) print(f”最良パラメータ: {grid_search.best_params_}”) print(f”最良CVスコア: {grid_search.best_score_:.4f}”) print(f”テストスコア: {grid_search.score(X_test, y_test):.4f}”)
=== GridSearchCVの結果 === 最良パラメータ: {‘C’: 1, ‘gamma’: 0.1, ‘kernel’: ‘rbf’} 最良CVスコア: 0.9429 テストスコア: 0.9333
💡 パラメータ選択のコツ

初期値の目安:C = 1.0、gamma = ‘scale’(自動調整)

調整の方針:
・訓練精度が低い → Cを大きく、gammaを大きく
・過学習している → Cを小さく、gammaを小さく

🖼️ 4. 実践プロジェクト:手書き数字認識

複数モデルの比較

from sklearn.datasets import load_digits from sklearn.linear_model import LogisticRegression from sklearn.tree import DecisionTreeClassifier from sklearn.ensemble import RandomForestClassifier import pandas as pd # 手書き数字データセット digits = load_digits() X, y = digits.data, digits.target print(f”サンプル数: {len(X)}, 特徴量数: {X.shape[1]}, クラス数: {len(set(y))}”) # データ分割 X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.3, random_state=42, stratify=y ) # スケーリング scaler = StandardScaler() X_train_scaled = scaler.fit_transform(X_train) X_test_scaled = scaler.transform(X_test) # 複数のモデルを比較 models = { ‘Logistic Regression’: LogisticRegression(max_iter=1000, random_state=42), ‘Decision Tree’: DecisionTreeClassifier(max_depth=10, random_state=42), ‘Random Forest’: RandomForestClassifier(n_estimators=100, random_state=42), ‘SVM (linear)’: SVC(kernel=’linear’, random_state=42), ‘SVM (RBF)’: SVC(kernel=’rbf’, gamma=’scale’, random_state=42) } print(“\n=== モデルの比較 ===”) for name, model in models.items(): model.fit(X_train_scaled, y_train) train_acc = model.score(X_train_scaled, y_train) test_acc = model.score(X_test_scaled, y_test) print(f”{name:25s}: Train={train_acc:.4f}, Test={test_acc:.4f}”)
サンプル数: 1797, 特徴量数: 64, クラス数: 10 === モデルの比較 === Logistic Regression : Train=1.0000, Test=0.9722 Decision Tree : Train=1.0000, Test=0.8556 Random Forest : Train=1.0000, Test=0.9741 SVM (linear) : Train=0.9984, Test=0.9815 SVM (RBF) : Train=0.9992, Test=0.9889
🏆 結果の分析

最良:SVM(RBFカーネル) – 98.89%
次点:SVM(線形カーネル) – 98.15%

SVMが高性能な理由:
・高次元データ(64次元)に強い
・各数字の特徴を効果的に捉える
・RBFカーネルで非線形パターンに対応

📝 練習問題

問題1 やさしい

SVMの基本概念

SVMについて、正しい説明をすべて選んでください。

  • A. マージンを最大化する境界線を見つける
  • B. サポートベクターは全データポイントである
  • C. スケーリングが必須である
  • D. 高次元データに弱い
  • E. カーネルトリックで非線形対応できる
正解:A、C、E
  • A(正解):SVMはマージン最大化が目的です。
  • B(誤り):サポートベクターは境界に最も近いデータポイントのみです。
  • C(正解):SVMは距離ベースなのでスケーリングが必須です。
  • D(誤り):SVMは高次元データに強いです。
  • E(正解):カーネルトリックで非線形パターンに対応できます。
問題2 ふつう

カーネルの選択

以下のデータに対して、最適なカーネルを選んでください。

  1. 同心円状のデータ
  2. 線形分離可能なデータ
  3. 非線形だが、パターンが不明なデータ
正解:1.RBF、2.linear、3.RBF
  • 1. RBF:同心円は線形で分離できないため、RBFカーネルが最適です。
  • 2. linear:線形分離可能なら、シンプルな線形カーネルで十分です。計算も速いです。
  • 3. RBF:パターンが不明な場合、RBFカーネルが最も汎用的です。迷ったらRBF!
問題3 ふつう

パラメータCとgammaの理解

以下の状況で、CとgammaをどのようにCとgammaを調整すべきか答えてください。

  1. 訓練精度100%、テスト精度70%(過学習)
  2. 訓練精度60%、テスト精度55%(過小適合)
解答

1. 過学習の場合:

  • Cを小さく:マージンを広げ、誤分類を許容
  • gammaを小さく:影響範囲を広げ、滑らかな境界に

2. 過小適合の場合:

  • Cを大きく:誤分類を減らす
  • gammaを大きく:各データポイントに密着した境界に
問題4 むずかしい

SVMの実装と比較

乳がんデータセットで、線形SVMとRBF SVMを比較し、どちらが良いか判断してください。

解答
from sklearn.datasets import load_breast_cancer from sklearn.model_selection import train_test_split, cross_val_score from sklearn.preprocessing import StandardScaler from sklearn.svm import SVC # データ cancer = load_breast_cancer() X, y = cancer.data, cancer.target # スケーリング scaler = StandardScaler() X_scaled = scaler.fit_transform(X) # データ分割 X_train, X_test, y_train, y_test = train_test_split( X_scaled, y, test_size=0.3, random_state=42 ) # 2つのSVMを比較 models = { ‘SVM (linear)’: SVC(kernel=’linear’, random_state=42), ‘SVM (RBF)’: SVC(kernel=’rbf’, gamma=’scale’, random_state=42) } print(“=== 5分割交差検証の結果 ===”) for name, model in models.items(): cv_scores = cross_val_score(model, X_scaled, y, cv=5, scoring=’accuracy’) model.fit(X_train, y_train) test_acc = model.score(X_test, y_test) print(f”{name:15s}: CV={cv_scores.mean():.4f} (±{cv_scores.std():.4f}), Test={test_acc:.4f}”)
=== 5分割交差検証の結果 === SVM (linear) : CV=0.9789 (±0.0123), Test=0.9825 SVM (RBF) : CV=0.9754 (±0.0156), Test=0.9766
結果の解釈:

線形SVM:CV=97.89%、Test=98.25%
RBF SVM:CV=97.54%、Test=97.66%

推奨:線形SVM(わずかに高性能、シンプル、高速)
乳がんデータは高次元(30特徴量)で、線形分離可能に近いため、線形SVMで十分。

📝 STEP 16 のまとめ

✅ このステップで学んだこと
  • SVM:マージンを最大化する分類手法
  • カーネルトリック:高次元空間への写像で非線形対応
  • RBFカーネル:最もよく使われる万能カーネル
  • パラメータC:誤分類の許容度
  • パラメータgamma:影響範囲の調整
  • 実践的な画像分類プロジェクト
🎯 SVMを使うべき場合
  • 高次元データ(特徴量が多い)
  • サンプル数が中程度(数百〜数万)
  • クラス間の境界が明確
⚠️ SVMを避けるべき場合
  • データが非常に多い(10万以上)→ 遅い
  • 解釈性が重要 → 決定木の方が良い
🚀 次のステップへ

次のSTEP 17では、分類モデルの評価を詳しく学びます。Accuracy、Precision、Recall、F1-score、ROC曲線とAUCなど、適切な評価指標を選ぶことが実務では非常に重要です!

❓ よくある質問

Q1. SVMでスケーリングが必須なのはなぜ?
SVMは距離ベースのアルゴリズムです。特徴量のスケールが異なると、スケールの大きい特徴量が支配的になってしまいます。

例:年齢(0〜100)と年収(0〜10,000,000)→ 年収が支配的に

推奨:StandardScalerで標準化(平均0、標準偏差1)
Q2. RBFカーネルのgamma=’scale’とは?
gamma=’scale’は、gamma = 1 / (n_features × X.var()) で自動計算されます。

gamma=’auto’は、gamma = 1 / n_features で計算されます。

推奨:まず’scale’を試し、必要に応じてGridSearchで探索
Q3. SVMで確率を出力するには?
probability=Trueを設定します:

svm = SVC(kernel='rbf', probability=True)
proba = svm.predict_proba(X_test)

注意:計算が遅くなります(Platt Scalingを使用するため)
Q4. 大規模データでSVMを使うには?
LinearSVCを使います:

from sklearn.svm import LinearSVC
svm = LinearSVC()

LinearSVCは線形カーネル専用ですが、大規模データでもSVCより高速です。

または:SGDClassifier(確率的勾配降下法)を使う
Q5. SVMとランダムフォレストはどちらを選ぶ?
SVM:
・高次元データに強い
・サンプル数が少なくても有効
・パラメータ調整が必要

ランダムフォレスト:
・前処理が少ない(スケーリング不要)
・解釈しやすい(特徴量重要度)
・デフォルトで高性能

実務では:まずランダムフォレストを試し、SVMも比較することが多い
📝

学習メモ

機械学習入門 - Step 16

📋 過去のメモ一覧
#artnasekai #学習メモ
LINE