机器学习之支持向量机
一、支持向量机的介绍
支持向量机(Support Vector Machine, SVM)是一种经典的监督学习模型,核心用于分类任务,也可通过改造用于回归(支持向量回归,SVR)。它的核心思想是:在特征空间中找到一个最优超平面,将不同类别的样本分开,且使两类样本到超平面的间隔最大化。
SVM 在小样本、高维特征场景下表现优异,是机器学习领域的基础算法之一,常被用于文本分类、图像识别、模式匹配等任务。
其应用场景:
图像识别:如手写数字识别、目标检测,利用高斯核处理高维像素特征。
文本分类:如垃圾邮件识别、情感分析,将文本转化为词向量后用线性核或 RBF 核分类。
故障诊断:基于设备传感器的高维特征,区分设备正常 / 故障状态。
二、实验
数据准备
ex6data1.mat:线性可分数据集,用于演示硬间隔与软间隔SVM
ex6data2.mat:非线性可分数据集,用于演示RBF核SVM
实验一:线性SVM分析
1.标准化
标准化是SVM中至关重要的预处理步骤,因为SVM对特征尺度敏感。标准化后,所有特征都转换为均值为0、方差为1的分布,这有助于提高模型的收敛速度和性能。
# 加载数据
mat_data = loadmat(mat_path)
X = mat_data['X']
y = mat_data['y'].ravel()
# 数据标准化
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
2硬间隔与软间隔SVM对比
2.1硬间隔SVM(C=1e10)
当惩罚系数C极大时,我们得到硬间隔SVM:
原理:不允许任何训练样本被误分类;特点:决策边界完全由支持向量决定,对噪声敏感
适用场景:数据完全线性可分且无噪声
2.2软间隔SVM(C=1.0)
原理:允许少量样本被误分,以换取更好的泛化能力;特点:通过松弛变量平衡准确率和模型复杂度
适用场景:大多数实际应用场景
3.实验结果

结论:
硬间隔SVM虽然训练集准确率达到100%,但支持向量数量高达527个,模型复杂度高
软间隔SVM在测试集上表现更佳,泛化能力更好
C值选择需要平衡分类准确率和模型复杂度
实验二:非线性SVM与核方法
1.当数据线性不可分时,我们需要引入核方法。RBF(径向基函数)核是最常用的核函数之一:
# RBF核SVM
svm_rbf = SVC(kernel='rbf', gamma=0.1, C=1.0)
svm_rbf.fit(X_scaled, y)
2.Gamma参数的影响分析
核心规律:
1.小gamma值 (γ→0)
决策边界更平滑;模型复杂度低;支持向量数量多;可能欠拟合
2.中等gamma值 (γ≈1)
平衡复杂度和泛化能力;在训练集和测试集上表现均衡;支持向量数量适中
3.大gamma值 (γ→∞)
决策边界高度复杂;可能过拟合;支持向量数量少;对噪声敏感
3.线性核与RBF核对比
# 线性核SVM
svm_linear = SVC(kernel='linear', C=1.0)
svm_linear.fit(X_scaled, y)
# RBF核SVM
svm_rbf = SVC(kernel='rbf', gamma=1.0, C=1.0)
svm_rbf.fit(X_scaled, y)
性能对比

4.核函数选择建议
线性核:特征数量大、样本数量少时,或数据近似线性可分
RBF核:大多数非线性问题,是默认选择
多项式核:需要显式控制多项式次数时
Sigmoid核:某些特定的神经网络场景
三、完整代码
实验一
import numpy as np
import matplotlib.pyplot as plt
from scipy.io import loadmat
from sklearn.svm import SVC
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import accuracy_score, classification_report
from sklearn.model_selection import train_test_split
# 解决中文显示和负号问题
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei']
plt.rcParams['axes.unicode_minus'] = False
class MATSVM_Analyzer:
"""MAT数据集SVM分析器"""
def __init__(self, mat_path, random_state=42):
"""
初始化分析器
Parameters:
- mat_path: MATLAB数据文件路径
- random_state: 随机种子
"""
self.mat_path = mat_path
self.random_state = random_state
self.models = {}
self.results = {}
self.load_data()
def load_data(self):
"""加载MATLAB数据集"""
try:
mat_data = loadmat(self.mat_path)
# 查找数据集中的变量名
print("=" * 60)
print("MAT文件中的变量:")
for key in mat_data.keys():
if not key.startswith('__'):
shape = mat_data[key].shape if hasattr(mat_data[key], 'shape') else 'scalar'
print(f" {key}: {shape}")
print("=" * 60)
# 尝试常见的变量名
X_keys = ['X', 'data', 'features']
y_keys = ['y', 'labels', 'class']
X = None
y = None
for key in X_keys:
if key in mat_data and len(mat_data[key].shape) >= 2:
X = mat_data[key]
print(f"找到特征矩阵: {key}, 形状: {X.shape}")
break
for key in y_keys:
if key in mat_data:
y_temp = mat_data[key]
# 确保y是1D数组
if len(y_temp.shape) > 1:
y_temp = y_temp.ravel()
# 检查是否有多个类别
unique_classes = np.unique(y_temp)
if len(unique_classes) == 2:
y = y_temp
print(f"找到标签向量: {key}, 类别: {unique_classes.tolist()}")
break
elif len(unique_classes) > 2:
print(f"警告: {key}有{len(unique_classes)}个类别,这是多分类问题")
y = y_temp
if X is None or y is None:
raise ValueError("未找到合适的特征矩阵或标签向量")
# 确保数据格式正确
if len(X.shape) > 2:
X = X.reshape(X.shape[0], -1)
print(f"重塑特征矩阵形状为: {X.shape}")
self.X_raw = X
self.y_raw = y
# 划分训练集和测试集
self.X_train_raw, self.X_test_raw, self.y_train, self.y_test = train_test_split(
X, y, test_size=0.3, random_state=self.random_state, stratify=y
)
# 数据标准化
self.scaler = StandardScaler()
self.X_train_scaled = self.scaler.fit_transform(self.X_train_raw)
self.X_test_scaled = self.scaler.transform(self.X_test_raw)
print(f"数据集信息:")
print(f" 原始特征形状: {X.shape}")
print(f" 训练集大小: {self.X_train_scaled.shape[0]}")
print(f" 测试集大小: {self.X_test_scaled.shape[0]}")
print(f" 类别分布: {np.unique(y, return_counts=True)}")
except Exception as e:
print(f"加载数据时出错: {e}")
raise
def train_comparison_models(self):
"""训练用于对比的SVM模型"""
# 基础模型配置
base_models = {
'硬间隔SVM (C=1e10)': {'C': 1e10, 'kernel': 'linear'},
'软间隔SVM (C=0.01)': {'C': 0.01, 'kernel': 'linear'},
'软间隔SVM (C=0.1)': {'C': 0.1, 'kernel': 'linear'},
'软间隔SVM (C=1.0)': {'C': 1.0, 'kernel': 'linear'},
'软间隔SVM (C=10)': {'C': 10, 'kernel': 'linear'},
'RBF核SVM (γ=0.1)': {'C': 1.0, 'kernel': 'rbf', 'gamma': 0.1},
'RBF核SVM (γ=1.0)': {'C': 1.0, 'kernel': 'rbf', 'gamma': 1.0},
'RBF核SVM (γ=10)': {'C': 1.0, 'kernel': 'rbf', 'gamma': 10},
}
print("\n" + "=" * 60)
print("开始训练SVM模型...")
print("=" * 60)
for name, params in base_models.items():
try:
svm = SVC(**params, random_state=self.random_state)
svm.fit(self.X_train_scaled, self.y_train)
self.models[name] = svm
print(f"✓ 训练完成: {name}")
except Exception as e:
print(f"✗ 训练失败 {name}: {e}")
print(f"总共训练了 {len(self.models)} 个模型")
def evaluate_all_models(self):
"""评估所有训练的模型"""
print("\n" + "=" * 70)
print("SVM模型性能评估")
print("=" * 70)
for name, model in self.models.items():
# 训练集预测
y_train_pred = model.predict(self.X_train_scaled)
train_acc = accuracy_score(self.y_train, y_train_pred)
# 测试集预测
y_test_pred = model.predict(self.X_test_scaled)
test_acc = accuracy_score(self.y_test, y_test_pred)
# 支持向量数量
n_support = len(model.support_vectors_) if hasattr(model, 'support_vectors_') else 0
# 存储结果
self.results[name] = {
'train_accuracy': train_acc,
'test_accuracy': test_acc,
'n_support_vectors': n_support,
'parameters': model.get_params(),
'model': model
}
# 打印结果
kernel = model.kernel
C = model.C
gamma = model.gamma if hasattr(model, 'gamma') else 'N/A'
print(f"\n📊 模型: {name}")
print(f" 核函数: {kernel} | C={C}" + (f" | γ={gamma}" if gamma != 'N/A' else ""))
print(f" 训练集准确率: {train_acc:.4f}")
print(f" 测试集准确率: {test_acc:.4f}")
print(f" 支持向量数量: {n_support}")
# 如果是线性核,显示权重向量
if kernel == 'linear':
if hasattr(model, 'coef_'):
weights = model.coef_[0]
print(f" 权重向量范数: {np.linalg.norm(weights):.4f}")
print("=" * 70)
def plot_decision_boundary_2d(self, model, X, y, title, ax):
"""
绘制二维特征的决策边界(仅适用于2维特征)
Parameters:
- model: 训练好的SVM模型
- X: 特征矩阵(2维)
- y: 标签向量
- title: 图表标题
- ax: matplotlib坐标轴
"""
if X.shape[1] != 2:
ax.text(0.5, 0.5, f"特征维度为{X.shape[1]}\n无法可视化",
ha='center', va='center', transform=ax.transAxes)
ax.set_title(title)
return
# 创建网格
h = 0.02
x_min, x_max = X[:, 0].min() - 0.5, X[:, 0].max() + 0.5
y_min, y_max = X[:, 1].min() - 0.5, X[:, 1].max() + 0.5
xx, yy = np.meshgrid(np.arange(x_min, x_max, h),
np.arange(y_min, y_max, h))
# 预测网格点
Z = model.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
# 绘制决策区域
ax.contourf(xx, yy, Z, alpha=0.3, cmap=plt.cm.coolwarm)
ax.contour(xx, yy, Z, colors='k', linestyles=['-'], linewidths=1, levels=[0])
# 绘制数据点
scatter = ax.scatter(X[:, 0], X[:, 1], c=y,
cmap=plt.cm.coolwarm, edgecolors='k', s=40, alpha=0.8)
# 标记支持向量
if hasattr(model, 'support_vectors_'):
sv = model.support_vectors_
ax.scatter(sv[:, 0], sv[:, 1],
facecolors='none', edgecolors='yellow',
s=150, linewidths=2, label='支持向量', alpha=0.8)
# 标记误分类点
y_pred = model.predict(X)
misclassified = np.where(y_pred != y)[0]
if len(misclassified) > 0:
ax.scatter(X[misclassified, 0], X[misclassified, 1],
facecolors='none', edgecolors='red',
s=200, linewidths=2, label='误分类点', alpha=0.8)
ax.set_xlabel('特征 1 (标准化)', fontsize=10)
ax.set_ylabel('特征 2 (标准化)', fontsize=10)
ax.set_title(title, fontsize=11, fontweight='bold')
ax.legend(loc='best', fontsize=8)
ax.grid(True, alpha=0.3)
def visualize_model_comparison(self):
"""可视化多个模型的决策边界(仅当特征为2维时)"""
if self.X_train_scaled.shape[1] != 2:
print("特征维度不是2维,无法绘制决策边界图")
return
# 选择几个关键模型进行可视化
key_models = ['硬间隔SVM (C=1e10)', '软间隔SVM (C=0.1)',
'软间隔SVM (C=1.0)', 'RBF核SVM (γ=1.0)']
# 过滤出存在的模型
available_models = [name for name in key_models if name in self.models]
if not available_models:
print("没有可用的模型进行可视化")
return
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
axes = axes.flatten()
for idx, model_name in enumerate(available_models):
if idx >= len(axes):
break
ax = axes[idx]
model = self.models[model_name]
result = self.results[model_name]
title = f'{model_name}\n测试准确率: {result["test_accuracy"]:.3f}\n支持向量: {result["n_support_vectors"]}'
# 使用测试集进行可视化
self.plot_decision_boundary_2d(
model,
self.X_test_scaled,
self.y_test,
title,
ax
)
plt.suptitle('SVM模型决策边界对比 (测试集)', fontsize=14, fontweight='bold')
plt.tight_layout()
plt.show()
def analyze_c_parameter_effect(self, c_values=None):
"""
分析C参数对线性SVM性能的影响
Parameters:
- c_values: C值列表,如果为None则使用默认值
"""
if c_values is None:
c_values = [0.001, 0.01, 0.1, 0.5, 1, 5, 10, 50, 100, 1000]
train_accuracies = []
test_accuracies = []
n_support_vectors = []
margin_sizes = []
print("\n" + "=" * 70)
print("C参数对线性SVM性能的影响分析")
print("=" * 70)
for C in c_values:
try:
# 训练线性SVM
svm = SVC(C=C, kernel='linear', random_state=self.random_state)
svm.fit(self.X_train_scaled, self.y_train)
# 计算准确率
train_acc = svm.score(self.X_train_scaled, self.y_train)
test_acc = svm.score(self.X_test_scaled, self.y_test)
# 支持向量数量
n_sv = len(svm.support_vectors_)
# 计算间隔大小(对于线性SVM)
margin = 2.0 / np.sqrt(np.sum(svm.coef_ ** 2)) if hasattr(svm, 'coef_') else 0
train_accuracies.append(train_acc)
test_accuracies.append(test_acc)
n_support_vectors.append(n_sv)
margin_sizes.append(margin)
print(f"C={C:7.3f} | 训练集准确率: {train_acc:.4f} | "
f"测试集准确率: {test_acc:.4f} | 支持向量: {n_sv:3d} | "
f"间隔: {margin:.4f}")
except Exception as e:
print(f"C={C:7.3f} | 训练失败: {e}")
continue
# 绘制C值影响图
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
# 准确率随C值变化
axes[0, 0].plot(c_values[:len(train_accuracies)], train_accuracies, 'o-',
label='训练集准确率', linewidth=2, markersize=6)
axes[0, 0].plot(c_values[:len(test_accuracies)], test_accuracies, 's-',
label='测试集准确率', linewidth=2, markersize=6)
axes[0, 0].set_xscale('log')
axes[0, 0].set_xlabel('惩罚系数C (对数尺度)', fontsize=10)
axes[0, 0].set_ylabel('准确率', fontsize=10)
axes[0, 0].set_title('C值对准确率的影响', fontsize=12, fontweight='bold')
axes[0, 0].legend(fontsize=9)
axes[0, 0].grid(True, alpha=0.3)
# 支持向量数量随C值变化
axes[0, 1].plot(c_values[:len(n_support_vectors)], n_support_vectors, 'o-',
color='green', linewidth=2, markersize=6)
axes[0, 1].set_xscale('log')
axes[0, 1].set_xlabel('惩罚系数C (对数尺度)', fontsize=10)
axes[0, 1].set_ylabel('支持向量数量', fontsize=10)
axes[0, 1].set_title('C值对支持向量的影响', fontsize=12, fontweight='bold')
axes[0, 1].grid(True, alpha=0.3)
# 间隔大小随C值变化
axes[1, 0].plot(c_values[:len(margin_sizes)], margin_sizes, 'o-',
color='purple', linewidth=2, markersize=6)
axes[1, 0].set_xscale('log')
axes[1, 0].set_xlabel('惩罚系数C (对数尺度)', fontsize=10)
axes[1, 0].set_ylabel('间隔大小', fontsize=10)
axes[1, 0].set_title('C值对间隔大小的影响', fontsize=12, fontweight='bold')
axes[1, 0].grid(True, alpha=0.3)
# 过拟合程度分析
if len(train_accuracies) == len(test_accuracies):
overfitting_gap = [train - test for train, test in zip(train_accuracies, test_accuracies)]
axes[1, 1].plot(c_values[:len(overfitting_gap)], overfitting_gap, 'o-',
color='red', linewidth=2, markersize=6)
axes[1, 1].set_xscale('log')
axes[1, 1].set_xlabel('惩罚系数C (对数尺度)', fontsize=10)
axes[1, 1].set_ylabel('过拟合程度 (训练-测试)', fontsize=10)
axes[1, 1].set_title('C值对过拟合的影响', fontsize=12, fontweight='bold')
axes[1, 1].grid(True, alpha=0.3)
axes[1, 1].axhline(y=0, color='k', linestyle='--', alpha=0.5)
plt.suptitle('线性SVM中C参数的影响分析', fontsize=14, fontweight='bold')
plt.tight_layout()
plt.show()
def analyze_gamma_parameter_effect(self, gamma_values=None):
"""
分析gamma参数对RBF核SVM性能的影响
Parameters:
- gamma_values: gamma值列表,如果为None则使用默认值
"""
if gamma_values is None:
gamma_values = [0.001, 0.01, 0.1, 0.5, 1, 5, 10, 50, 100]
train_accuracies = []
test_accuracies = []
n_support_vectors = []
print("\n" + "=" * 70)
print("Gamma参数对RBF核SVM性能的影响分析")
print("=" * 70)
for gamma in gamma_values:
try:
# 训练RBF核SVM
svm = SVC(C=1.0, kernel='rbf', gamma=gamma, random_state=self.random_state)
svm.fit(self.X_train_scaled, self.y_train)
# 计算准确率
train_acc = svm.score(self.X_train_scaled, self.y_train)
test_acc = svm.score(self.X_test_scaled, self.y_test)
# 支持向量数量
n_sv = len(svm.support_vectors_)
train_accuracies.append(train_acc)
test_accuracies.append(test_acc)
n_support_vectors.append(n_sv)
print(f"γ={gamma:7.3f} | 训练集准确率: {train_acc:.4f} | "
f"测试集准确率: {test_acc:.4f} | 支持向量: {n_sv:3d}")
except Exception as e:
print(f"γ={gamma:7.3f} | 训练失败: {e}")
continue
# 绘制gamma值影响图
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# 准确率随gamma值变化
axes[0].plot(gamma_values[:len(train_accuracies)], train_accuracies, 'o-',
label='训练集准确率', linewidth=2, markersize=6)
axes[0].plot(gamma_values[:len(test_accuracies)], test_accuracies, 's-',
label='测试集准确率', linewidth=2, markersize=6)
axes[0].set_xscale('log')
axes[0].set_xlabel('Gamma参数γ (对数尺度)', fontsize=10)
axes[0].set_ylabel('准确率', fontsize=10)
axes[0].set_title('Gamma值对准确率的影响', fontsize=12, fontweight='bold')
axes[0].legend(fontsize=9)
axes[0].grid(True, alpha=0.3)
# 支持向量数量随gamma值变化
axes[1].plot(gamma_values[:len(n_support_vectors)], n_support_vectors, 'o-',
color='green', linewidth=2, markersize=6)
axes[1].set_xscale('log')
axes[1].set_xlabel('Gamma参数γ (对数尺度)', fontsize=10)
axes[1].set_ylabel('支持向量数量', fontsize=10)
axes[1].set_title('Gamma值对支持向量的影响', fontsize=12, fontweight='bold')
axes[1].grid(True, alpha=0.3)
plt.suptitle('RBF核SVM中Gamma参数的影响分析', fontsize=14, fontweight='bold')
plt.tight_layout()
plt.show()
def create_summary_report(self):
"""创建综合分析报告"""
print("\n" + "=" * 80)
print("SVM实验综合分析报告")
print("=" * 80)
# 找到最佳模型
best_model_name = None
best_test_acc = 0
for name, result in self.results.items():
if result['test_accuracy'] > best_test_acc:
best_test_acc = result['test_accuracy']
best_model_name = name
print(f"\n🏆 最佳模型: {best_model_name}")
print(f" 测试集准确率: {best_test_acc:.4f}")
# 线性模型和RBF模型对比
linear_models = {name: result for name, result in self.results.items()
if 'linear' in name.lower()}
rbf_models = {name: result for name, result in self.results.items()
if 'rbf' in name.lower()}
if linear_models:
avg_linear_acc = np.mean([r['test_accuracy'] for r in linear_models.values()])
print(f"\n📈 线性核SVM平均测试准确率: {avg_linear_acc:.4f}")
if rbf_models:
avg_rbf_acc = np.mean([r['test_accuracy'] for r in rbf_models.values()])
print(f"📈 RBF核SVM平均测试准确率: {avg_rbf_acc:.4f}")
# 支持向量分析
total_sv = sum([r['n_support_vectors'] for r in self.results.values()])
avg_sv = total_sv / len(self.results) if self.results else 0
print(f"\n🔍 支持向量分析:")
print(f" 平均支持向量数量: {avg_sv:.1f}")
print(f" 支持向量占训练集比例: {avg_sv / len(self.y_train):.2%}")
# 数据特征
print(f"\n📊 数据特征:")
print(f" 特征维度: {self.X_train_scaled.shape[1]}")
print(f" 训练集大小: {len(self.y_train)}")
print(f" 测试集大小: {len(self.y_test)}")
print("\n" + "=" * 80)
def main():
"""主函数:执行完整的SVM分析流程"""
# 设置MAT文件路径
mat_path = r"C:\Users\98189\PycharmProjects\机器学习\支持向量机\ex6data1.mat"
try:
print("🚀 开始MAT数据集SVM分析")
print("=" * 70)
# 1. 创建分析器并加载数据
analyzer = MATSVM_Analyzer(mat_path, random_state=42)
# 2. 训练多个SVM模型进行对比
analyzer.train_comparison_models()
# 3. 评估所有模型
analyzer.evaluate_all_models()
# 4. 可视化模型对比(仅当特征为2维时)
analyzer.visualize_model_comparison()
# 5. 分析C参数影响
analyzer.analyze_c_parameter_effect()
# 6. 分析Gamma参数影响
analyzer.analyze_gamma_parameter_effect()
# 7. 创建综合分析报告
analyzer.create_summary_report()
print("\n✅ SVM分析完成!")
print("=" * 70)
except FileNotFoundError:
print(f"❌ 找不到文件: {mat_path}")
print("请检查文件路径是否正确")
except Exception as e:
print(f"❌ 程序执行出错: {e}")
import traceback
traceback.print_exc()
if __name__ == "__main__":
main()
实验二
import numpy as np
import matplotlib.pyplot as plt
from scipy.io import loadmat
from sklearn.svm import SVC
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import accuracy_score, classification_report
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.inspection import DecisionBoundaryDisplay
# 解决中文显示和负号问题
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei']
plt.rcParams['axes.unicode_minus'] = False
class RBF_SVM_Analyzer:
"""RBF核SVM分析器:专注于MAT数据集和高斯核SVM分析"""
def __init__(self, mat_path, random_state=42):
"""
初始化RBF核SVM分析器
Parameters:
- mat_path: MATLAB数据文件路径
- random_state: 随机种子
"""
self.mat_path = mat_path
self.random_state = random_state
self.models = {}
self.results = {}
self.scaler = StandardScaler()
self.load_and_prepare_data()
def load_and_prepare_data(self):
"""加载MATLAB数据并进行预处理"""
try:
# 加载MAT文件
mat_data = loadmat(self.mat_path)
print("=" * 60)
print("数据集信息")
print("=" * 60)
# 查找特征和标签
X = None
y = None
# 尝试不同的变量名
for key in ['X', 'data', 'features']:
if key in mat_data:
X = mat_data[key]
print(f"特征矩阵: {key}, 形状: {X.shape}")
break
for key in ['y', 'labels', 'Y']:
if key in mat_data:
y_data = mat_data[key]
# 确保y是1D数组
if len(y_data.shape) > 1:
y = y_data.ravel()
else:
y = y_data
unique_classes = np.unique(y)
print(f"标签向量: {key}, 类别: {unique_classes.tolist()}")
print(f"类别分布: {dict(zip(*np.unique(y, return_counts=True)))}")
break
if X is None or y is None:
raise ValueError("无法找到特征矩阵或标签向量")
# 确保特征矩阵是2D
if len(X.shape) > 2:
X = X.reshape(X.shape[0], -1)
print(f"重塑特征矩阵形状为: {X.shape}")
# 检查是否是二分类问题
if len(np.unique(y)) > 2:
print("⚠️ 警告: 这是一个多分类问题,SVM将使用一对一策略")
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=self.random_state, stratify=y
)
print(f"训练集大小: {X_train.shape[0]}")
print(f"测试集大小: {X_test.shape[0]}")
# 数据标准化
self.X_train_raw = X_train
self.X_test_raw = X_test
self.y_train = y_train
self.y_test = y_test
self.X_train_scaled = self.scaler.fit_transform(X_train)
self.X_test_scaled = self.scaler.transform(X_test)
# 用于可视化的数据
self.X_scaled_all = self.scaler.transform(X)
self.y_all = y
print("数据预处理完成")
print("=" * 60)
except Exception as e:
print(f"数据加载失败: {e}")
raise
def train_rbf_models(self, gamma_values=None, C=1.0):
"""
训练多个RBF核SVM模型
Parameters:
- gamma_values: gamma值列表
- C: 惩罚系数,默认1.0
"""
if gamma_values is None:
gamma_values = [0.01, 0.1, 0.5, 1.0, 2.0, 5.0, 10.0, 20.0]
print("\n" + "=" * 60)
print("训练RBF核SVM模型")
print("=" * 60)
self.gamma_values = gamma_values
for gamma in gamma_values:
try:
model_name = f"RBF_γ={gamma}_C={C}"
print(f"正在训练: {model_name}...")
svm = SVC(kernel='rbf', gamma=gamma, C=C,
random_state=self.random_state)
svm.fit(self.X_train_scaled, self.y_train)
# 评估模型
train_acc = svm.score(self.X_train_scaled, self.y_train)
test_acc = svm.score(self.X_test_scaled, self.y_test)
self.models[model_name] = {
'model': svm,
'gamma': gamma,
'C': C,
'train_accuracy': train_acc,
'test_accuracy': test_acc,
'n_support_vectors': len(svm.support_vectors_)
}
print(f"✓ {model_name}: 训练集={train_acc:.4f}, 测试集={test_acc:.4f}, "
f"支持向量={len(svm.support_vectors_)}")
except Exception as e:
print(f"✗ 训练失败 gamma={gamma}: {e}")
print(f"总共训练了 {len(self.models)} 个RBF核SVM模型")
def train_comparison_models(self, C_values=None):
"""
训练用于对比的线性核SVM模型
Parameters:
- C_values: C值列表
"""
if C_values is None:
C_values = [0.01, 0.1, 1.0, 10.0, 100.0]
print("\n" + "=" * 60)
print("训练线性核SVM模型用于对比")
print("=" * 60)
for C in C_values:
try:
model_name = f"Linear_C={C}"
print(f"正在训练: {model_name}...")
svm = SVC(kernel='linear', C=C, random_state=self.random_state)
svm.fit(self.X_train_scaled, self.y_train)
# 评估模型
train_acc = svm.score(self.X_train_scaled, self.y_train)
test_acc = svm.score(self.X_test_scaled, self.y_test)
self.models[model_name] = {
'model': svm,
'gamma': 'linear',
'C': C,
'train_accuracy': train_acc,
'test_accuracy': test_acc,
'n_support_vectors': len(svm.support_vectors_)
}
print(f"✓ {model_name}: 训练集={train_acc:.4f}, 测试集={test_acc:.4f}, "
f"支持向量={len(svm.support_vectors_)}")
except Exception as e:
print(f"✗ 训练失败 C={C}: {e}")
def plot_rbf_decision_boundary(self, model_info, title, ax, h=0.02):
"""
绘制RBF核SVM的决策边界
Parameters:
- model_info: 模型信息字典
- title: 图表标题
- ax: matplotlib坐标轴
- h: 网格步长
"""
model = model_info['model']
# 检查特征维度
if self.X_train_scaled.shape[1] != 2:
ax.text(0.5, 0.5, "特征维度不为2\n无法可视化决策边界",
ha='center', va='center', transform=ax.transAxes, fontsize=10)
ax.set_title(title)
return
try:
# 生成网格
x_min, x_max = self.X_scaled_all[:, 0].min() - 0.5, self.X_scaled_all[:, 0].max() + 0.5
y_min, y_max = self.X_scaled_all[:, 1].min() - 0.5, self.X_scaled_all[:, 1].max() + 0.5
xx, yy = np.meshgrid(np.arange(x_min, x_max, h),
np.arange(y_min, y_max, h))
# 预测网格点
Z = model.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
# 绘制决策区域
ax.contourf(xx, yy, Z, alpha=0.3, cmap=plt.cm.coolwarm)
ax.contour(xx, yy, Z, colors='k', linestyles=['-'], linewidths=1, levels=[0])
# 绘制训练数据点
ax.scatter(self.X_scaled_all[:, 0], self.X_scaled_all[:, 1],
c=self.y_all, cmap=plt.cm.coolwarm,
edgecolors='k', s=30, alpha=0.8)
# 标记支持向量
sv = model.support_vectors_
ax.scatter(sv[:, 0], sv[:, 1],
facecolors='none', edgecolors='yellow',
s=100, linewidths=1.5, label='支持向量')
# 计算并显示准确率
train_acc = model_info['train_accuracy']
test_acc = model_info['test_accuracy']
n_sv = model_info['n_support_vectors']
info_text = f"训练集准确率: {train_acc:.3f}\n测试集准确率: {test_acc:.3f}\n支持向量: {n_sv}"
ax.text(0.02, 0.98, info_text, transform=ax.transAxes,
fontsize=8, verticalalignment='top',
bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))
ax.set_xlabel('特征 1 (标准化)', fontsize=9)
ax.set_ylabel('特征 2 (标准化)', fontsize=9)
ax.set_title(title, fontsize=11, fontweight='bold')
ax.legend(loc='best', fontsize=8)
ax.grid(True, alpha=0.3)
except Exception as e:
ax.text(0.5, 0.5, f"绘图错误: {str(e)[:50]}",
ha='center', va='center', transform=ax.transAxes, fontsize=9)
ax.set_title(title)
def visualize_gamma_comparison(self):
"""可视化不同gamma值的RBF核SVM对比"""
# 获取所有RBF核模型
rbf_models = {k: v for k, v in self.models.items() if 'RBF' in k}
if not rbf_models:
print("没有找到RBF核SVM模型")
return
# 按gamma值排序
sorted_models = sorted(rbf_models.items(),
key=lambda x: x[1]['gamma'])
# 创建子图
n_models = len(sorted_models)
n_cols = min(3, n_models)
n_rows = (n_models + n_cols - 1) // n_cols
fig, axes = plt.subplots(n_rows, n_cols,
figsize=(4 * n_cols, 3.5 * n_rows))
# 如果只有一个子图,axes不是数组
if n_rows == 1 and n_cols == 1:
axes = np.array([axes])
# 将axes转换为一维数组
axes = axes.flatten() if hasattr(axes, 'flatten') else np.array([axes])
for idx, (name, model_info) in enumerate(sorted_models):
if idx >= len(axes):
break
ax = axes[idx] if idx < len(axes) else None
if ax is None:
continue
gamma = model_info['gamma']
C = model_info['C']
title = f'RBF核SVM\nγ={gamma}, C={C}'
self.plot_rbf_decision_boundary(model_info, title, ax)
# 隐藏多余的子图
for idx in range(len(sorted_models), len(axes)):
axes[idx].axis('off')
plt.suptitle('RBF核SVM: Gamma参数对比', fontsize=14, fontweight='bold')
plt.tight_layout()
plt.show()
def visualize_kernel_comparison(self):
"""可视化线性核与RBF核的对比"""
# 获取线性核和RBF核模型
linear_models = {k: v for k, v in self.models.items() if 'Linear' in k}
rbf_models = {k: v for k, v in self.models.items() if 'RBF' in k}
if not linear_models or not rbf_models:
print("需要同时有线性核和RBF核模型进行对比")
return
# 找到最佳线性模型和最佳RBF模型
best_linear = max(linear_models.items(),
key=lambda x: x[1]['test_accuracy'])
best_rbf = max(rbf_models.items(),
key=lambda x: x[1]['test_accuracy'])
# 创建对比图
fig, axes = plt.subplots(1, 2, figsize=(12, 5))
# 确保axes是数组
axes = axes.flatten() if hasattr(axes, 'flatten') else axes
titles = ['线性核SVM (最佳C值)', 'RBF核SVM (最佳γ值)']
models_to_plot = [best_linear, best_rbf]
for idx, ((name, model_info), ax) in enumerate(zip(models_to_plot, axes)):
if ax is None:
continue
kernel_type = "Linear" if "Linear" in name else "RBF"
if kernel_type == "Linear":
title = f'{titles[idx]}\nC={model_info["C"]}, 测试集准确率={model_info["test_accuracy"]:.3f}'
else:
title = f'{titles[idx]}\nγ={model_info["gamma"]}, C={model_info["C"]}, 测试集准确率={model_info["test_accuracy"]:.3f}'
self.plot_rbf_decision_boundary(model_info, title, ax)
plt.suptitle('线性核 vs RBF核SVM对比', fontsize=14, fontweight='bold')
plt.tight_layout()
plt.show()
# 打印对比结果
print("\n" + "=" * 60)
print("线性核 vs RBF核SVM性能对比")
print("=" * 60)
print(f"最佳线性核: {best_linear[0]}")
print(f" 训练集准确率: {best_linear[1]['train_accuracy']:.4f}")
print(f" 测试集准确率: {best_linear[1]['test_accuracy']:.4f}")
print(f" 支持向量数量: {best_linear[1]['n_support_vectors']}")
print(f"\n最佳RBF核: {best_rbf[0]}")
print(f" 训练集准确率: {best_rbf[1]['train_accuracy']:.4f}")
print(f" 测试集准确率: {best_rbf[1]['test_accuracy']:.4f}")
print(f" 支持向量数量: {best_rbf[1]['n_support_vectors']}")
print("=" * 60)
def analyze_gamma_effect(self):
"""分析gamma参数对RBF核SVM性能的影响"""
# 获取所有RBF核模型
rbf_models = {k: v for k, v in self.models.items() if 'RBF' in k}
if len(rbf_models) < 2:
print("需要至少2个不同gamma值的RBF核模型进行分析")
return
# 提取数据
gammas = []
train_accs = []
test_accs = []
n_svs = []
for name, info in rbf_models.items():
gammas.append(info['gamma'])
train_accs.append(info['train_accuracy'])
test_accs.append(info['test_accuracy'])
n_svs.append(info['n_support_vectors'])
# 按gamma值排序
sorted_indices = np.argsort(gammas)
gammas = np.array(gammas)[sorted_indices]
train_accs = np.array(train_accs)[sorted_indices]
test_accs = np.array(test_accs)[sorted_indices]
n_svs = np.array(n_svs)[sorted_indices]
# 创建分析图表
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
# 1. 准确率随gamma变化
axes[0, 0].semilogx(gammas, train_accs, 'o-', label='训练集准确率',
linewidth=2, markersize=8)
axes[0, 0].semilogx(gammas, test_accs, 's-', label='测试集准确率',
linewidth=2, markersize=8)
axes[0, 0].set_xlabel('Gamma参数 (γ)', fontsize=10)
axes[0, 0].set_ylabel('准确率', fontsize=10)
axes[0, 0].set_title('Gamma对准确率的影响', fontsize=12, fontweight='bold')
axes[0, 0].legend(fontsize=9)
axes[0, 0].grid(True, alpha=0.3)
# 2. 过拟合程度随gamma变化
overfitting_gap = train_accs - test_accs
axes[0, 1].semilogx(gammas, overfitting_gap, 'o-', color='red',
linewidth=2, markersize=8)
axes[0, 1].set_xlabel('Gamma参数 (γ)', fontsize=10)
axes[0, 1].set_ylabel('过拟合程度 (训练-测试)', fontsize=10)
axes[0, 1].set_title('Gamma对过拟合的影响', fontsize=12, fontweight='bold')
axes[0, 1].axhline(y=0, color='k', linestyle='--', alpha=0.5)
axes[0, 1].grid(True, alpha=0.3)
# 3. 支持向量数量随gamma变化
axes[1, 0].semilogx(gammas, n_svs, 'o-', color='green',
linewidth=2, markersize=8)
axes[1, 0].set_xlabel('Gamma参数 (γ)', fontsize=10)
axes[1, 0].set_ylabel('支持向量数量', fontsize=10)
axes[1, 0].set_title('Gamma对支持向量的影响', fontsize=12, fontweight='bold')
axes[1, 0].grid(True, alpha=0.3)
# 4. 支持向量比例随gamma变化
sv_ratio = n_svs / len(self.y_train)
axes[1, 1].semilogx(gammas, sv_ratio, 'o-', color='purple',
linewidth=2, markersize=8)
axes[1, 1].set_xlabel('Gamma参数 (γ)', fontsize=10)
axes[1, 1].set_ylabel('支持向量比例', fontsize=10)
axes[1, 1].set_title('Gamma对支持向量比例的影响', fontsize=12, fontweight='bold')
axes[1, 1].grid(True, alpha=0.3)
plt.suptitle('RBF核SVM: Gamma参数影响分析', fontsize=14, fontweight='bold')
plt.tight_layout()
plt.show()
# 打印分析结果
print("\n" + "=" * 60)
print("Gamma参数影响分析总结")
print("=" * 60)
print("小gamma值 (γ→0):")
print(" - 决策边界更平滑,近似线性")
print(" - 模型复杂度低,可能欠拟合")
print(" - 支持向量数量多,泛化能力强")
print("\n大gamma值 (γ→∞):")
print(" - 决策边界更复杂,可能过拟合")
print(" - 模型复杂度高,对训练集拟合好")
print(" - 支持向量数量少,可能泛化能力差")
print("\n建议:")
print(" 1. 使用交叉验证选择最佳gamma值")
print(" 2. 通常从 [0.1, 1, 10] 开始尝试")
print(" 3. 观察训练集和测试集准确率差距")
print("=" * 60)
def plot_3d_decision_boundary(self, model_info):
"""
绘制3D决策边界(仅适用于2维特征)
Parameters:
- model_info: 模型信息字典
"""
if self.X_train_scaled.shape[1] != 2:
print("3D可视化仅适用于2维特征")
return
model = model_info['model']
# 创建网格
h = 0.05
x_min, x_max = self.X_scaled_all[:, 0].min() - 0.5, self.X_scaled_all[:, 0].max() + 0.5
y_min, y_max = self.X_scaled_all[:, 1].min() - 0.5, self.X_scaled_all[:, 1].max() + 0.5
xx, yy = np.meshgrid(np.arange(x_min, x_max, h),
np.arange(y_min, y_max, h))
# 计算决策函数值
Z = model.decision_function(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
# 创建3D图形
fig = plt.figure(figsize=(12, 8))
ax = fig.add_subplot(111, projection='3d')
# 绘制决策曲面
surf = ax.plot_surface(xx, yy, Z, cmap=plt.cm.coolwarm,
alpha=0.6, linewidth=0, antialiased=True)
# 绘制数据点
ax.scatter(self.X_scaled_all[:, 0], self.X_scaled_all[:, 1],
np.zeros_like(self.y_all), # 在z=0平面上绘制点
c=self.y_all, cmap=plt.cm.coolwarm,
s=50, edgecolors='k', depthshade=True)
# 标记支持向量
sv = model.support_vectors_
sv_scores = model.decision_function(sv)
ax.scatter(sv[:, 0], sv[:, 1], sv_scores,
facecolors='none', edgecolors='yellow',
s=150, linewidth=2, label='支持向量')
# 设置标签
ax.set_xlabel('特征 1', fontsize=10)
ax.set_ylabel('特征 2', fontsize=10)
ax.set_zlabel('决策函数值', fontsize=10)
gamma = model_info['gamma']
train_acc = model_info['train_accuracy']
test_acc = model_info['test_accuracy']
title = f'RBF核SVM 3D决策边界 (γ={gamma})\n训练集准确率={train_acc:.3f}, 测试集准确率={test_acc:.3f}'
ax.set_title(title, fontsize=12, fontweight='bold')
ax.legend(loc='best')
plt.colorbar(surf, ax=ax, shrink=0.5, aspect=5)
plt.show()
def run_full_analysis(self):
"""运行完整的RBF核SVM分析"""
print("🚀 开始RBF核SVM分析")
print("=" * 60)
# 1. 训练多个gamma值的RBF核SVM
gamma_values = [0.1, 1.0, 10.0, 100.0] # 与原始代码保持一致
self.train_rbf_models(gamma_values=gamma_values, C=1.0)
# 2. 训练对比的线性核SVM
self.train_comparison_models(C_values=[0.01, 0.1, 1.0, 10.0])
# 3. 可视化不同gamma值的RBF核SVM
self.visualize_gamma_comparison()
# 4. 可视化线性核与RBF核对比
self.visualize_kernel_comparison()
# 5. 分析gamma参数影响
self.analyze_gamma_effect()
# 6. 可选:绘制3D决策边界(用于最佳RBF模型)
rbf_models = {k: v for k, v in self.models.items() if 'RBF' in k}
if rbf_models and self.X_train_scaled.shape[1] == 2:
best_rbf = max(rbf_models.items(),
key=lambda x: x[1]['test_accuracy'])
print(f"\n绘制最佳RBF模型的3D决策边界: {best_rbf[0]}")
self.plot_3d_decision_boundary(best_rbf[1])
print("\n✅ RBF核SVM分析完成!")
print("=" * 60)
def main():
"""主函数:执行RBF核SVM分析"""
# 设置MAT文件路径
mat_path = r"C:\Users\98189\PycharmProjects\机器学习\支持向量机\ex6data2.mat"
try:
# 创建分析器
analyzer = RBF_SVM_Analyzer(mat_path, random_state=42)
# 运行完整分析
analyzer.run_full_analysis()
except FileNotFoundError:
print(f"❌ 找不到文件: {mat_path}")
print("请检查文件路径是否正确")
except Exception as e:
print(f"❌ 程序执行出错: {e}")
import traceback
traceback.print_exc()
if __name__ == "__main__":
main()
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐

所有评论(0)