第10回 クラス分類
はじめに
本シリーズ「Pythonで学ぶデータ分析実践」の第10回では、クラス分類について解説します。
クラス分類は機械学習における最も基本的なタスクの一つで、与えられたデータがどのカテゴリ(クラス)に属するかを予測する手法です。迷惑メール判定、画像認識、顧客セグメンテーションなど、実務で幅広く活用されています。
本記事では、ロジスティック回帰、サポートベクトルマシン、K近傍法、決定木、ランダムフォレストの各手法と、分類モデルの評価方法について実践的に解説します。
シリーズ全体の構成(全12回)
| 回 | テーマ |
|---|---|
| 第1回 | データ分析とは |
| 第2回 | データの前処理 |
| 第3回 | データの把握 |
| 第4回 | 相関分析 |
| 第5回 | 統計的推定 |
| 第6回 | 統計的検定 |
| 第7回 | 分散分析 |
| 第8回 | 回帰分析 |
| 第9回 | 時系列データ分析 |
| 第10回 | クラス分類(本記事) |
| 第11回 | クラスタリング |
| 第12回 | 次元削減 |
10-1. クラス分類とは
クラス分類は、与えられたデータを所属するカテゴリ(クラス)に割り当てる手法です。事前にクラスが割り当てられたサンプル(教師データ)を用いてモデルを学習し、未知のデータのクラスを予測します。
分類の種類
| 分類の種類 | クラス数 | 例 |
|---|---|---|
| 2クラス分類(二値分類) | 2 | 迷惑メール判定、疾病有無、合格/不合格 |
| 多クラス分類 | 3以上 | 手書き数字認識、花の品種分類、顧客セグメント |
主なクラス分類アルゴリズム
| アルゴリズム | 特徴 | 適用場面 |
|---|---|---|
| ロジスティック回帰 | 確率を出力、解釈しやすい | 2クラス分類の基本手法 |
| サポートベクトルマシン(SVM) | マージン最大化、高精度 | 中〜小規模データ |
| K近傍法(KNN) | 単純で直感的 | 少量データ、プロトタイプ |
| 決定木 | 可視化可能、解釈しやすい | ルール抽出が必要な場合 |
| ランダムフォレスト | 高精度、過学習に強い | 汎用的な分類タスク |
分類の基本フロー
1. データ収集・前処理
2. 特徴量(説明変数)と目的変数の設定
3. 学習データとテストデータの分割
4. モデルの学習(fit)
5. テストデータで予測(predict)
6. モデルの評価(精度、混同行列、ROC曲線など)
7. ハイパーパラメータの調整
サンプルデータの準備
本記事では、scikit-learnに付属する乳がんデータセット(2クラス分類)とIrisデータセット(多クラス分類)を使用します。
import numpy as np
import pandas as pd
from sklearn.datasets import load_breast_cancer, load_iris
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
import matplotlib.pyplot as plt
import japanize_matplotlib
# 乳がんデータセット(2クラス分類用)
cancer = load_breast_cancer()
X_cancer = pd.DataFrame(cancer.data, columns=cancer.feature_names)
y_cancer = cancer.target # 0: 悪性, 1: 良性
print("=== 乳がんデータセット ===")
print(f"データ数: {X_cancer.shape[0]}, 特徴量数: {X_cancer.shape[1]}")
print(f"クラス: {cancer.target_names}")
print(f"クラスごとの件数: 悪性={sum(y_cancer==0)}, 良性={sum(y_cancer==1)}")
# 学習データとテストデータに分割
X_train_c, X_test_c, y_train_c, y_test_c = train_test_split(
X_cancer, y_cancer, test_size=0.2, random_state=42, stratify=y_cancer
)
# Irisデータセット(多クラス分類用)
iris = load_iris()
X_iris = pd.DataFrame(iris.data, columns=iris.feature_names)
y_iris = iris.target # 0: setosa, 1: versicolor, 2: virginica
print(f"\n=== Irisデータセット ===")
print(f"データ数: {X_iris.shape[0]}, 特徴量数: {X_iris.shape[1]}")
print(f"クラス: {iris.target_names}")
X_train_i, X_test_i, y_train_i, y_test_i = train_test_split(
X_iris, y_iris, test_size=0.2, random_state=42, stratify=y_iris
)
10-2. ロジスティック回帰
ロジスティック回帰は、ある事象が起こる確率を予測し、その確率に基づいてクラス分類を行う手法です。「回帰」という名前ですが、確率の出力に閾値を設定することで分類に利用します。
ロジスティック回帰の仕組み
ロジスティック回帰式(シグモイド関数):
$$P(y=1|x) = \frac{1}{1 + e^{-(b_0 + b_1 x_1 + b_2 x_2 + \cdots + b_p x_p)}}$$
- 右辺の指数部分は線形回帰と同じ形式
- シグモイド関数により、出力が0〜1の確率値に変換される
- デフォルトでは閾値0.5を超えればクラス1、超えなければクラス0に分類
ロジスティック回帰の特徴
| メリット | デメリット |
|---|---|
| 確率を出力でき解釈しやすい | 非線形な境界を学習できない |
| 計算が高速 | 特徴量間の相互作用を自動で捉えられない |
| 過学習しにくい | 複雑なパターンには不向き |
| 係数から各特徴量の影響度がわかる | 外れ値に影響を受けやすい |
Pythonでの実装
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix
from sklearn.preprocessing import StandardScaler
# スケーリング
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train_c)
X_test_scaled = scaler.transform(X_test_c)
# ロジスティック回帰モデルの構築
lr = LogisticRegression(
penalty='l2', # 正則化(L2: Ridge)
C=1.0, # 正則化の強度(小さいほど強い)
max_iter=1000, # 最大反復回数
random_state=42
)
lr.fit(X_train_scaled, y_train_c)
# 予測
y_pred = lr.predict(X_test_scaled)
# 精度評価
print("=== ロジスティック回帰の結果 ===")
print(f"正解率(Accuracy): {accuracy_score(y_test_c, y_pred):.4f}")
print(f"\n分類レポート:")
print(classification_report(y_test_c, y_pred, target_names=cancer.target_names))
予測確率の確認
# 予測確率の算出
y_proba = lr.predict_proba(X_test_scaled)
print("=== 予測確率(先頭5件) ===")
print(f"{'悪性の確率':>10} {'良性の確率':>10} {'予測':>6} {'正解':>6}")
print("-" * 40)
for i in range(5):
print(f"{y_proba[i, 0]:>10.4f} {y_proba[i, 1]:>10.4f} "
f"{cancer.target_names[y_pred[i]]:>6} {cancer.target_names[y_test_c[i]]:>6}")
係数の解釈
# 偏回帰係数(影響度)の確認
coef_df = pd.DataFrame({
"特徴量": cancer.feature_names,
"係数": lr.coef_[0]
}).sort_values("係数", key=abs, ascending=False)
print("\n=== 影響度の大きい特徴量(上位10件) ===")
print(coef_df.head(10).to_string(index=False))
# 可視化
plt.figure(figsize=(10, 6))
top10 = coef_df.head(10)
colors = ['red' if c < 0 else 'blue' for c in top10["係数"]]
plt.barh(top10["特徴量"], top10["係数"], color=colors, alpha=0.7)
plt.xlabel("係数(正: 良性に寄与、負: 悪性に寄与)")
plt.title("ロジスティック回帰の係数(影響度上位10件)")
plt.grid(axis='x', alpha=0.3)
plt.tight_layout()
plt.show()
10-3. サポートベクトルマシン(SVM)
**サポートベクトルマシン(SVM)**は、データを分離する最適な境界(超平面)を見つける手法です。境界に最も近いデータ点(サポートベクトル)からの距離(マージン)を最大化することで、汎化性能の高い分類を実現します。
SVMの基本概念
マージン最大化
SVMでは、2つのクラスを分ける境界線(決定境界)のうち、両クラスのデータ点から最も遠い(マージンが最大の)境界を選択します。
| 用語 | 説明 |
|---|---|
| 超平面 | データを分離する境界面 |
| サポートベクトル | 決定境界に最も近いデータ点 |
| マージン | サポートベクトルから決定境界までの距離 |
ハードマージンとソフトマージン
| 種類 | 説明 | 適用場面 |
|---|---|---|
| ハードマージン | 誤分類を一切許容しない | データが完全に線形分離可能な場合 |
| ソフトマージン | 一定の誤分類を許容する | ノイズや外れ値がある場合(実務ではこちら) |
パラメータCで誤分類のペナルティを制御:
- C大 → 誤分類ペナルティ大(複雑な境界、過学習のリスク)
- C小 → 誤分類ペナルティ小(単純な境界、汎化性能向上)
カーネルトリック
線形分離不可能なデータに対しては、カーネルトリックを用いてデータを高次元空間に写像し、線形分離可能にします。
| カーネル | 説明 | パラメータ |
|---|---|---|
| linear | 線形分離(直線/平面) | C |
| poly | 多項式カーネル | C, degree |
| rbf(ガウシアン) | 非線形境界、最も汎用的 | C, gamma |
| sigmoid | シグモイド関数ベース | C, gamma |
Pythonでの実装(多クラス分類)
from sklearn.svm import SVC
from sklearn.metrics import accuracy_score, classification_report
from sklearn.preprocessing import StandardScaler
# Irisデータでスケーリング
scaler = StandardScaler()
X_train_i_scaled = scaler.fit_transform(X_train_i)
X_test_i_scaled = scaler.transform(X_test_i)
# 線形SVM
svm_linear = SVC(kernel='linear', C=1.0, random_state=42)
svm_linear.fit(X_train_i_scaled, y_train_i)
y_pred_linear = svm_linear.predict(X_test_i_scaled)
# RBFカーネルSVM
svm_rbf = SVC(kernel='rbf', C=1.0, gamma='scale', random_state=42)
svm_rbf.fit(X_train_i_scaled, y_train_i)
y_pred_rbf = svm_rbf.predict(X_test_i_scaled)
print("=== SVM(Irisデータ: 多クラス分類) ===")
print(f"線形SVM 正解率: {accuracy_score(y_test_i, y_pred_linear):.4f}")
print(f"RBF SVM 正解率: {accuracy_score(y_test_i, y_pred_rbf):.4f}")
print(f"\nRBF SVMの分類レポート:")
print(classification_report(y_test_i, y_pred_rbf, target_names=iris.target_names))
カーネル比較の可視化
from sklearn.svm import SVC
import numpy as np
import matplotlib.pyplot as plt
# 2特徴量のみ使用(可視化のため)
X_2d = X_train_i_scaled[:, 2:4] # petal_length, petal_width
y_2d = y_train_i
fig, axes = plt.subplots(1, 3, figsize=(15, 4))
kernels = ['linear', 'poly', 'rbf']
for ax, kernel in zip(axes, kernels):
svm = SVC(kernel=kernel, C=1.0, gamma='scale')
svm.fit(X_2d, y_2d)
# 決定境界の描画
h = 0.02
x_min, x_max = X_2d[:, 0].min() - 1, X_2d[:, 0].max() + 1
y_min, y_max = X_2d[:, 1].min() - 1, X_2d[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))
Z = svm.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
ax.contourf(xx, yy, Z, alpha=0.3, cmap='Set2')
scatter = ax.scatter(X_2d[:, 0], X_2d[:, 1], c=y_2d, cmap='Set2',
edgecolors='black', s=30)
ax.set_title(f"カーネル: {kernel}\n正解率: {svm.score(X_2d, y_2d):.3f}")
ax.set_xlabel("petal_length(標準化)")
ax.set_ylabel("petal_width(標準化)")
plt.tight_layout()
plt.show()
10-4. K近傍法(KNN)
**K近傍法(KNN: K-Nearest Neighbors)は、未知のデータに対して最も距離の近いK個の学習データを見つけ、多数決によってクラスを推定する手法です。学習時にモデルを構築せず、予測時にすべての計算を行う怠惰学習(Lazy Learning)**に分類されます。
KNNのアルゴリズム
1. 学習データをベクトル空間にプロット
2. Kの数を決定する
3. 未知データからの距離が近い順にK個のデータを取得
4. K個のデータのクラスで多数決を行い、クラスを推定
KNNの特徴
| メリット | デメリット |
|---|---|
| アルゴリズムが単純で理解しやすい | データ数が多いと予測が遅い |
| 学習が不要(データを保持するだけ) | 特徴量が多いと精度が低下(次元の呪い) |
| 非線形な境界も学習可能 | スケーリングが必須 |
| 新しいデータの追加が容易 | 最適なKの選択が必要 |
Kの値の影響
| Kの値 | 決定境界 | 傾向 |
|---|---|---|
| 小さい(K=1,3) | 複雑(ジグザグ) | 過学習しやすい |
| 大きい(K=15,20) | 滑らか | 汎化するが細かいパターンを見逃す |
Pythonでの実装
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import accuracy_score
# K近傍法モデル
knn = KNeighborsClassifier(
n_neighbors=5, # K=5(近傍数)
weights='uniform', # 重み(uniform: 均一, distance: 距離に応じた重み)
algorithm='auto' # 最適なアルゴリズムを自動選択
)
knn.fit(X_train_i_scaled, y_train_i)
y_pred_knn = knn.predict(X_test_i_scaled)
print(f"=== K近傍法(K=5)の結果 ===")
print(f"正解率: {accuracy_score(y_test_i, y_pred_knn):.4f}")
最適なKの探索
from sklearn.model_selection import cross_val_score
import matplotlib.pyplot as plt
# Kを1〜30まで変えて交差検証
k_range = range(1, 31)
cv_scores = []
for k in k_range:
knn_temp = KNeighborsClassifier(n_neighbors=k)
scores = cross_val_score(knn_temp, X_train_i_scaled, y_train_i, cv=5, scoring='accuracy')
cv_scores.append(scores.mean())
# 最適なKの可視化
plt.figure(figsize=(10, 5))
plt.plot(k_range, cv_scores, 'o-', color='steelblue', markersize=5)
best_k = k_range[np.argmax(cv_scores)]
plt.axvline(best_k, color='red', linestyle='--', label=f'最適K={best_k}')
plt.xlabel("K(近傍数)")
plt.ylabel("交差検証スコア(正解率)")
plt.title("Kの値と正解率の関係")
plt.legend()
plt.grid(alpha=0.3)
plt.show()
print(f"最適なK: {best_k}(正解率: {max(cv_scores):.4f})")
10-5. 決定木
**決定木(Decision Tree)**は、入力データに対する出力を「木構造」で決定する手法です。if-else形式の条件分岐を繰り返すことでデータを分割し、分類を行います。
決定木の特徴
| メリット | デメリット |
|---|---|
| 可視化が可能で人間が理解しやすい | 過学習しやすい |
| スケーリング不要 | データの小さな変化に敏感 |
| 非線形な境界を学習可能 | 境界が軸に平行な直線のみ |
| 特徴量の重要度がわかる | 汎化性能が低くなりがち |
| 計算が高速 | 最適な木を見つけるのが困難 |
決定木の仕組み:不純度と情報利得
決定木は「不純度」を最小化するように分割点を決めます。
| 指標 | 説明 | 計算式 |
|---|---|---|
| ジニ不純度 | クラスの混在度合い | $Gini = 1 - \sum_{k}p_k^2$ |
| エントロピー | 情報理論に基づく混在度 | $H = -\sum_{k}p_k \log_2 p_k$ |
情報利得 = 親ノードの不純度 − 子ノードの不純度の加重平均
情報利得が最大となる分割点を選び、これを再帰的に繰り返して木を構築します。
過学習の防止
| パラメータ | 効果 |
|---|---|
| max_depth | 木の深さの上限を設定 |
| min_samples_split | 分割に必要な最小サンプル数 |
| min_samples_leaf | 葉ノードの最小サンプル数 |
| max_leaf_nodes | 葉の最大数 |
Pythonでの実装
from sklearn.tree import DecisionTreeClassifier, export_text, plot_tree
from sklearn.metrics import accuracy_score
# 決定木モデル
dt = DecisionTreeClassifier(
criterion='gini', # 不純度指標
max_depth=4, # 木の最大深さ(過学習防止)
min_samples_leaf=5, # 葉の最小サンプル数
random_state=42
)
dt.fit(X_train_i, y_train_i) # スケーリング不要
y_pred_dt = dt.predict(X_test_i)
print(f"=== 決定木の結果 ===")
print(f"正解率: {accuracy_score(y_test_i, y_pred_dt):.4f}")
# 特徴量の重要度
importances = pd.DataFrame({
"特徴量": iris.feature_names,
"重要度": dt.feature_importances_
}).sort_values("重要度", ascending=False)
print(f"\n特徴量の重要度:")
print(importances.to_string(index=False))
決定木の可視化
# テキスト形式で表示
print("\n=== 決定木のルール ===")
print(export_text(dt, feature_names=list(iris.feature_names)))
# グラフィカル表示
plt.figure(figsize=(16, 8))
plot_tree(dt, feature_names=iris.feature_names, class_names=iris.target_names,
filled=True, rounded=True, fontsize=9)
plt.title("決定木の可視化(Irisデータセット)")
plt.tight_layout()
plt.show()
10-6. ランダムフォレスト
ランダムフォレストは、複数の決定木を組み合わせたアンサンブル学習の手法です。個々の決定木の弱点(過学習しやすい)を、多数の木を集約することで克服します。
ランダムフォレストの仕組み
1. 元データからブートストラップサンプリング(復元抽出)で複数のデータセットを作成
2. 各データセットに対して決定木を構築(特徴量もランダムに選択)
3. 分類の場合: 全決定木の多数決で最終予測を決定
回帰の場合: 全決定木の平均値を最終予測とする
ランダムフォレストの特徴
| メリット | デメリット |
|---|---|
| 過学習に強い | 計算コストが高い |
| 高い予測精度 | 解釈性が低い(ブラックボックス) |
| 特徴量の重要度を算出可能 | メモリ使用量が大きい |
| ハイパーパラメータの調整が比較的容易 | 学習が決定木より遅い |
| 外れ値やノイズに強い | リアルタイム予測にはやや不向き |
Pythonでの実装
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score, classification_report
# ランダムフォレストモデル
rf = RandomForestClassifier(
n_estimators=100, # 決定木の本数
max_depth=None, # 深さ制限なし
min_samples_leaf=2, # 葉の最小サンプル数
max_features='sqrt', # 各木で使う特徴量数(√特徴量数)
random_state=42,
n_jobs=-1 # 全CPUコアを使用
)
rf.fit(X_train_c, y_train_c)
y_pred_rf = rf.predict(X_test_c)
print(f"=== ランダムフォレストの結果(乳がんデータ) ===")
print(f"正解率: {accuracy_score(y_test_c, y_pred_rf):.4f}")
print(f"\n分類レポート:")
print(classification_report(y_test_c, y_pred_rf, target_names=cancer.target_names))
特徴量の重要度
# 特徴量の重要度
importances_rf = pd.DataFrame({
"特徴量": cancer.feature_names,
"重要度": rf.feature_importances_
}).sort_values("重要度", ascending=False)
# 上位15件を可視化
plt.figure(figsize=(10, 6))
top15 = importances_rf.head(15)
plt.barh(top15["特徴量"][::-1], top15["重要度"][::-1], color='steelblue', alpha=0.8)
plt.xlabel("重要度")
plt.title("ランダムフォレスト: 特徴量の重要度(上位15件)")
plt.grid(axis='x', alpha=0.3)
plt.tight_layout()
plt.show()
決定木の本数と精度の関係
# 木の本数を変えて精度を確認
n_trees = [1, 5, 10, 20, 50, 100, 200, 500]
scores = []
for n in n_trees:
rf_temp = RandomForestClassifier(n_estimators=n, random_state=42, n_jobs=-1)
rf_temp.fit(X_train_c, y_train_c)
scores.append(rf_temp.score(X_test_c, y_test_c))
plt.figure(figsize=(8, 5))
plt.plot(n_trees, scores, 'o-', color='steelblue', markersize=8)
plt.xlabel("決定木の本数")
plt.ylabel("正解率")
plt.title("決定木の本数と正解率の関係")
plt.grid(alpha=0.3)
plt.xscale('log')
plt.show()
10-7. モデルの評価
分類モデルの精度を正しく評価するための指標と手法を紹介します。
混同行列(Confusion Matrix)
予測結果と正解の関係を整理した表です。
| 予測: 陽性 | 予測: 陰性 | |
|---|---|---|
| 実際: 陽性 | TP(真陽性) | FN(偽陰性) |
| 実際: 陰性 | FP(偽陽性) | TN(真陰性) |
評価指標一覧
| 指標 | 計算式 | 意味 |
|---|---|---|
| 正解率(Accuracy) | (TP+TN) / (TP+TN+FP+FN) | 全体のうち正しく分類できた割合 |
| 適合率(Precision) | TP / (TP+FP) | 陽性と予測したもののうち実際に陽性の割合 |
| 再現率(Recall/TPR) | TP / (TP+FN) | 実際に陽性のもののうち正しく陽性と予測できた割合 |
| F1スコア | 2×Precision×Recall / (Precision+Recall) | 適合率と再現率の調和平均 |
| 偽陽性率(FPR) | FP / (FP+TN) | 実際に陰性のものを誤って陽性と予測した割合 |
混同行列の実装と可視化
from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay
import seaborn as sns
# ロジスティック回帰の結果で混同行列
lr.fit(X_train_scaled, y_train_c)
y_pred_lr = lr.predict(X_test_scaled)
cm = confusion_matrix(y_test_c, y_pred_lr)
# ヒートマップで可視化
plt.figure(figsize=(6, 5))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',
xticklabels=cancer.target_names,
yticklabels=cancer.target_names)
plt.xlabel("予測ラベル")
plt.ylabel("正解ラベル")
plt.title("混同行列(ロジスティック回帰)")
plt.tight_layout()
plt.show()
# 分類レポート
print(classification_report(y_test_c, y_pred_lr, target_names=cancer.target_names))
ROC曲線とAUC
ROC曲線は閾値を変化させた場合のTPR(真陽性率)とFPR(偽陽性率)の関係をグラフ化したものです。**AUC(Area Under the Curve)**はROC曲線の下の面積で、1に近いほど良いモデルです。
from sklearn.metrics import roc_curve, auc
# 予測確率
y_proba_lr = lr.predict_proba(X_test_scaled)[:, 1]
# ROC曲線のパラメータ計算
fpr, tpr, thresholds = roc_curve(y_test_c, y_proba_lr)
roc_auc = auc(fpr, tpr)
# ROC曲線の描画
plt.figure(figsize=(8, 6))
plt.plot(fpr, tpr, color='steelblue', linewidth=2, label=f'ROC曲線 (AUC = {roc_auc:.4f})')
plt.plot([0, 1], [0, 1], color='gray', linestyle='--', label='ランダム分類器')
plt.fill_between(fpr, tpr, alpha=0.1, color='steelblue')
plt.xlabel("偽陽性率(FPR)")
plt.ylabel("真陽性率(TPR)")
plt.title("ROC曲線")
plt.legend(loc='lower right', fontsize=11)
plt.grid(alpha=0.3)
plt.show()
print(f"AUC: {roc_auc:.4f}")
複数モデルのROC曲線比較
from sklearn.metrics import roc_curve, auc
# 各モデルの予測確率を取得
models = {
"ロジスティック回帰": LogisticRegression(max_iter=1000, random_state=42),
"SVM(RBF)": SVC(kernel='rbf', probability=True, random_state=42),
"ランダムフォレスト": RandomForestClassifier(n_estimators=100, random_state=42),
}
plt.figure(figsize=(8, 6))
for name, model in models.items():
if name == "SVM(RBF)":
model.fit(X_train_scaled, y_train_c)
y_prob = model.predict_proba(X_test_scaled)[:, 1]
elif name == "ロジスティック回帰":
model.fit(X_train_scaled, y_train_c)
y_prob = model.predict_proba(X_test_scaled)[:, 1]
else:
model.fit(X_train_c, y_train_c)
y_prob = model.predict_proba(X_test_c)[:, 1]
fpr, tpr, _ = roc_curve(y_test_c, y_prob)
roc_auc = auc(fpr, tpr)
plt.plot(fpr, tpr, linewidth=2, label=f'{name} (AUC={roc_auc:.4f})')
plt.plot([0, 1], [0, 1], 'k--', alpha=0.5)
plt.xlabel("偽陽性率(FPR)")
plt.ylabel("真陽性率(TPR)")
plt.title("ROC曲線の比較")
plt.legend(fontsize=10)
plt.grid(alpha=0.3)
plt.show()
モデル比較のまとめ
from sklearn.model_selection import cross_val_score
# 全モデルの交差検証スコアを比較
all_models = {
"ロジスティック回帰": LogisticRegression(max_iter=1000, random_state=42),
"SVM(線形)": SVC(kernel='linear', random_state=42),
"SVM(RBF)": SVC(kernel='rbf', random_state=42),
"K近傍法(K=5)": KNeighborsClassifier(n_neighbors=5),
"決定木": DecisionTreeClassifier(max_depth=5, random_state=42),
"ランダムフォレスト": RandomForestClassifier(n_estimators=100, random_state=42),
}
print("=== モデル比較(5分割交差検証) ===")
print(f"{'モデル':<20} {'平均正解率':>10} {'標準偏差':>10}")
print("-" * 45)
# スケーリング済みデータ
X_all_scaled = scaler.fit_transform(X_cancer)
for name, model in all_models.items():
if "SVM" in name or "ロジスティック" in name or "K近傍" in name:
scores = cross_val_score(model, X_all_scaled, y_cancer, cv=5, scoring='accuracy')
else:
scores = cross_val_score(model, X_cancer, y_cancer, cv=5, scoring='accuracy')
print(f"{name:<20} {scores.mean():>10.4f} {scores.std():>10.4f}")
まとめ
本記事では、クラス分類として以下の内容を解説しました。
| 項目 | ポイント |
|---|---|
| クラス分類とは | データをカテゴリに割り当てる教師あり学習の手法 |
| ロジスティック回帰 | 確率ベースの分類、解釈性が高い |
| SVM | マージン最大化、カーネルトリックで非線形対応 |
| K近傍法 | 距離ベースの多数決、Kの選択が重要 |
| 決定木 | 木構造で可視化可能、過学習に注意 |
| ランダムフォレスト | アンサンブル学習、高精度で汎用的 |
| モデル評価 | 混同行列、ROC曲線/AUC、F1スコアで多面的に評価 |
クラス分類のチェックリスト
- 分類タスクの種類(2クラス/多クラス)を確認したか
- データの前処理(スケーリング、欠損値処理)を行ったか
- 学習データとテストデータに分割したか
- 複数のモデルを試して比較したか
- 正解率だけでなく混同行列やF1スコアも確認したか
- 不均衡データの場合、適切な対処をしたか
- 過学習していないか確認したか
- 特徴量の重要度を確認したか
シリーズ記事一覧
- 【第1回】Pythonで学ぶデータ分析実践 〜データ分析とは〜
- 【第2回】データの前処理(欠損値・外れ値・ダミー変数・データスケーリングなど)
- 【第3回】データの把握(基本統計量・ヒストグラム・箱ひげ図・散布図など)
- 【第4回】相関分析(共分散・相関係数・相関比・連関係数など)
- 【第5回】統計的推定(母集団と標本・点推定・区間推定・信頼区間)
- 【第6回】統計的検定(仮説検定・p値・t検定・カイ二乗検定など)
- 【第7回】分散分析(一元分散分析・二元分散分析・多重比較)
- 【第8回】回帰分析(単回帰・重回帰・一般化線形モデル・モデル評価)
- 【第9回】時系列データ分析(トレンド・季節性・ARIMA・SARIMAなど)
- 【第10回】クラス分類(決定木・ランダムフォレスト・ロジスティック回帰など)
- 【第11回】クラスタリング(k-means・階層クラスタリング・エルボー法など)
- 【第12回】次元削減(主成分分析・寄与率・可視化)
- 【発展編】分析精度の向上(特徴量エンジニアリング・ハイパーパラメータ調整・アンサンブル学習など)
- 【発展編】ニューラルネットワーク入門(パーセプトロン・誤差逆伝播法・TensorFlow・Kerasなど)
次回の第11回では、教師なし学習の代表的手法であるクラスタリングについて解説します。k-means、階層クラスタリング、エルボー法、シルエット分析によるクラスタ数の決定方法を学びます。