🔍 概要
本記事では、初代ポケモン151種の「攻撃(Attack)」および「特攻(Sp. Atk)」の種族値を用いて、「物理型(0)」か「特殊型(1)」かを分類するモデルを構築・比較しました。
使用した分類アルゴリズムは以下の8種類です:
- ロジスティック回帰(Logistic Regression)
- k近傍法(k-NN)
- 決定木(Decision Tree)
- ランダムフォレスト(Random Forest)
- サポートベクタマシン(SVM)
- XGBoost
- ナイーブベイズ(Gaussian Naive Bayes)
- 多層パーセプトロン(MLP)
🧪 分析の流れ
1. データセット
対象は初代151体から、以下のような**攻撃・特攻・ラベル(0:物理, 1:特殊)**を抜粋したものです:
| Name | Attack | Sp. Atk | Label |
|---|---|---|---|
| Charizard | 84 | 109 | 1 |
| Machamp | 130 | 65 | 0 |
| Gengar | 65 | 130 | 1 |
| ... | ... | ... | ... |
2. 前処理
-
StandardScalerにより Attack / Sp. Atk を標準化 - 訓練・テストに 8:2 で分割
3. 分類器の訓練と評価
各モデルについて以下を実施:
- 学習(
model.fit()) - 正解率(Accuracy)の算出とプロットタイトルへの表示
- 決定領域の可視化
- 混同行列の出力
- F1スコア・精度・再現率の出力
📊 結果の可視化と評価指標
各モデルごとに以下のような図とともに、テストデータでの性能が評価されます:
- プロット:決定境界と分類結果
- Accuracy:テストデータに対する分類正解率
- Confusion Matrix
- Classification Report(精度 / 再現率 / F1スコア)
✅ 比較まとめ(一部例)
| モデル | Accuracy | 特徴 |
|---|---|---|
| Logistic Regression | 0.91 | 境界が直線的で分かりやすい |
| Random Forest | 0.95 | 非線形で柔軟、安定した性能 |
| XGBoost | 0.96 | 強力な分類性能、微調整可能 |
| k-NN | 0.89 | パラメータに敏感(kの選定重要) |
| SVM | 0.93 | 境界がシャープで適合性高い |
| MLP | 0.92 | 多層による抽象化、過学習注意 |
コード
# Program Name: pokemon_classifier_comparison.py
# Creation Date: 20250603
# Overview: Compare classification models on Pokémon data using Attack and Sp. Atk
# Usage: Run in Python environment with required libraries installed
# -------------------- Install Required Libraries --------------------
!pip install pandas numpy matplotlib scikit-learn xgboost
# -------------------- Import Libraries --------------------
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.neighbors import KNeighborsClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.svm import SVC
from sklearn.naive_bayes import GaussianNB
from sklearn.neural_network import MLPClassifier
from sklearn.metrics import accuracy_score, confusion_matrix, classification_report
from matplotlib.colors import ListedColormap
from xgboost import XGBClassifier
# -------------------- Parameters (一元管理) --------------------
RANDOM_STATE = 42
TEST_SIZE = 0.2
K_NEIGHBORS = 5
RESOLUTION = 0.1
# -------------------- Sample Data --------------------
data = [
['Bulbasaur', 49, 65, 1],
['Ivysaur', 62, 80, 1],
['Venusaur', 82, 100, 1],
['Charmander', 52, 60, 1],
['Charmeleon', 64, 80, 1],
['Charizard', 84, 109, 1],
['Squirtle', 48, 50, 1],
['Wartortle', 63, 65, 1],
['Blastoise', 83, 85, 1],
['Caterpie', 30, 20, 0],
['Metapod', 20, 25, 0],
['Butterfree', 45, 80, 1],
['Weedle', 35, 20, 0],
['Kakuna', 25, 25, 0],
['Beedrill', 90, 45, 0],
['Pidgey', 45, 35, 0],
['Pidgeotto', 60, 50, 0],
['Pidgeot', 80, 70, 0],
['Rattata', 56, 25, 0],
['Raticate', 81, 50, 0],
['Spearow', 60, 31, 0],
['Fearow', 90, 61, 0],
['Ekans', 60, 40, 0],
['Arbok', 85, 65, 0],
['Pikachu', 55, 50, 0],
['Raichu', 90, 90, 1],
['Sandshrew', 75, 20, 0],
['Sandslash', 100, 45, 0],
['Nidoran♀', 47, 40, 0],
['Nidorina', 62, 55, 0],
['Nidoqueen', 82, 75, 1],
['Nidoran♂', 57, 40, 0],
['Nidorino', 72, 55, 0],
['Nidoking', 92, 85, 1],
['Clefairy', 45, 60, 1],
['Clefable', 70, 85, 1],
['Vulpix', 41, 50, 1],
['Ninetales', 76, 81, 1],
['Jigglypuff', 45, 45, 0],
['Wigglytuff', 70, 75, 0],
['Zubat', 45, 30, 0],
['Golbat', 80, 65, 0],
['Oddish', 50, 75, 1],
['Gloom', 65, 85, 1],
['Vileplume', 80, 100, 1],
['Paras', 70, 55, 0],
['Parasect', 95, 60, 0],
['Venonat', 55, 40, 0],
['Venomoth', 65, 90, 1],
['Diglett', 55, 35, 0],
['Dugtrio', 80, 50, 0],
['Meowth', 45, 40, 0],
['Persian', 70, 65, 0],
['Psyduck', 52, 65, 1],
['Golduck', 82, 95, 1],
['Mankey', 80, 35, 0],
['Primeape', 105, 60, 0],
['Growlithe', 70, 50, 0],
['Arcanine', 110, 80, 0],
['Poliwag', 50, 40, 0],
['Poliwhirl', 65, 50, 0],
['Poliwrath', 85, 70, 0],
['Abra', 20, 105, 1],
['Kadabra', 35, 120, 1],
['Alakazam', 50, 135, 1],
['Machop', 80, 35, 0],
['Machoke', 100, 50, 0],
['Machamp', 130, 65, 0],
['Bellsprout', 75, 70, 1],
['Weepinbell', 90, 85, 1],
['Victreebel', 105, 100, 1],
['Tentacool', 40, 50, 1],
['Tentacruel', 70, 80, 1],
['Geodude', 80, 30, 0],
['Graveler', 95, 45, 0],
['Golem', 110, 55, 0],
['Ponyta', 85, 65, 0],
['Rapidash', 100, 80, 0],
['Slowpoke', 65, 40, 1],
['Slowbro', 75, 100, 1],
['Magnemite', 35, 95, 1],
['Magneton', 60, 120, 1],
['Farfetch’d', 65, 58, 0],
['Doduo', 85, 35, 0],
['Dodrio', 110, 60, 0],
['Seel', 45, 45, 0],
['Dewgong', 70, 70, 1],
['Grimer', 80, 40, 0],
['Muk', 105, 65, 0],
['Shellder', 65, 45, 0],
['Cloyster', 95, 85, 0],
['Gastly', 35, 100, 1],
['Haunter', 50, 115, 1],
['Gengar', 65, 130, 1],
['Onix', 45, 30, 0],
['Drowzee', 48, 43, 1],
['Hypno', 73, 73, 1],
['Krabby', 105, 25, 0],
['Kingler', 130, 50, 0],
['Voltorb', 30, 55, 1],
['Electrode', 50, 80, 1],
['Exeggcute', 40, 60, 1],
['Exeggutor', 95, 125, 1],
['Cubone', 50, 40, 0],
['Marowak', 80, 50, 0],
['Hitmonlee', 120, 35, 0],
['Hitmonchan', 105, 35, 0],
['Lickitung', 55, 60, 0],
['Koffing', 65, 60, 0],
['Weezing', 90, 85, 0],
['Rhyhorn', 85, 30, 0],
['Rhydon', 130, 45, 0],
['Chansey', 5, 35, 1],
['Tangela', 55, 100, 1],
['Kangaskhan', 95, 40, 0],
['Horsea', 40, 70, 1],
['Seadra', 65, 95, 1],
['Goldeen', 67, 35, 0],
['Seaking', 92, 65, 0],
['Staryu', 45, 70, 1],
['Starmie', 75, 100, 1],
['Mr. Mime', 45, 100, 1],
['Scyther', 110, 55, 0],
['Jynx', 50, 115, 1],
['Electabuzz', 83, 95, 1],
['Magmar', 95, 100, 1],
['Pinsir', 125, 55, 0],
['Tauros', 100, 40, 0],
['Magikarp', 10, 15, 0],
['Gyarados', 125, 60, 0],
['Lapras', 85, 85, 1],
['Ditto', 48, 48, 0],
['Eevee', 55, 45, 0],
['Vaporeon', 65, 110, 1],
['Jolteon', 65, 110, 1],
['Flareon', 130, 95, 0],
['Porygon', 60, 85, 1],
['Omanyte', 40, 90, 1],
['Omastar', 60, 115, 1],
['Kabuto', 80, 55, 0],
['Kabutops', 115, 65, 0],
['Aerodactyl', 105, 60, 0],
['Snorlax', 110, 65, 0],
['Articuno', 85, 95, 1],
['Zapdos', 90, 125, 1],
['Moltres', 100, 125, 1],
['Dratini', 64, 50, 0],
['Dragonair', 84, 70, 0],
['Dragonite', 134, 100, 0],
['Mewtwo', 110, 154, 1],
['Mew', 100, 100, 1]
]
df = pd.DataFrame(data, columns=['Name', 'Attack', 'Sp_Atk', 'Label'])
# -------------------- Feature & Target --------------------
X = df[['Attack', 'Sp_Atk']]
y = df['Label']
# -------------------- Train-Test Split --------------------
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=TEST_SIZE, random_state=RANDOM_STATE)
# -------------------- Scaling --------------------
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
# -------------------- Classifiers --------------------
models = {
'Logistic Regression': LogisticRegression(),
'k-NN': KNeighborsClassifier(n_neighbors=K_NEIGHBORS),
'Decision Tree': DecisionTreeClassifier(random_state=RANDOM_STATE),
'Random Forest': RandomForestClassifier(random_state=RANDOM_STATE),
'SVM': SVC(probability=True),
'XGBoost': XGBClassifier(use_label_encoder=False, eval_metric='logloss', random_state=RANDOM_STATE),
'Naive Bayes': GaussianNB(),
'MLP': MLPClassifier(random_state=RANDOM_STATE, max_iter=1000)
}
# -------------------- Train & Plot --------------------
xx, yy = np.meshgrid(
np.arange(X_train_scaled[:, 0].min() - 1, X_train_scaled[:, 0].max() + 1, RESOLUTION),
np.arange(X_train_scaled[:, 1].min() - 1, X_train_scaled[:, 1].max() + 1, RESOLUTION)
)
fig, axes = plt.subplots(3, 3, figsize=(20, 15))
axes = axes.flatten()
cmap = ListedColormap(['#FFAAAA', '#AAAAFF'])
for idx, (name, model) in enumerate(models.items()):
model.fit(X_train_scaled, y_train)
y_pred = model.predict(X_test_scaled)
accuracy = accuracy_score(y_test, y_pred)
Z = model.predict(np.c_[xx.ravel(), yy.ravel()]).reshape(xx.shape)
ax = axes[idx]
ax.contourf(xx, yy, Z, alpha=0.3, cmap=cmap)
ax.scatter(X_train_scaled[:, 0], X_train_scaled[:, 1], c=y_train, s=40, edgecolor='k', cmap=cmap)
ax.set_title(f"{name}\nAccuracy: {accuracy:.2f}")
ax.set_xlabel('Attack (Standardized)')
ax.set_ylabel('Sp. Atk (Standardized)')
# -------------------- Console Output --------------------
print(f"===== {name} =====")
print("Confusion Matrix:\n", confusion_matrix(y_test, y_pred))
print("Classification Report:\n", classification_report(y_test, y_pred))
# -------------------- Hide Extra Axes --------------------
for ax in axes[len(models):]:
ax.axis('off')
plt.tight_layout()
plt.show()
