📋 このステップで学ぶこと
- 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.RBF、2.linear、3.RBF
- 1. RBF:同心円は線形で分離できないため、RBFカーネルが最適です。
- 2. linear:線形分離可能なら、シンプルな線形カーネルで十分です。計算も速いです。
- 3. RBF:パターンが不明な場合、RBFカーネルが最も汎用的です。迷ったらRBF!
問題3
ふつう
パラメータCとgammaの理解
以下の状況で、CとgammaをどのようにCとgammaを調整すべきか答えてください。
- 訓練精度100%、テスト精度70%(過学習)
- 訓練精度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も比較することが多い
artnasekai
#artnasekai #学習メモ