第64课 - 模型比较与选择策略
学习目标
完成本课学习后,你将能够:
- 理解模型选择在机器学习流程中的重要性
- 掌握使用交叉验证系统性地评估和比较不同模型的方法
- 学会选择合适的评估指标并正确解读结果
- 实现一个简单的自动化模型比较流程
- 认识常见的模型选择陷阱并避免它们
核心概念
为什么需要模型选择?
想象你要选择一个工具来完成工作:螺丝刀、锤子还是扳手?你不会随便选一个,而是会根据具体任务(拧螺丝、钉钉子、拧螺母)来选择最合适的工具。机器学习中的模型选择也是如此,不同的算法适合不同类型的问题和数据。
过拟合与欠拟合
在模型选择中,我们需要平衡两个极端:
- 过拟合:模型像“死记硬背”的学生,在训练数据上表现很好,但遇到新数据就表现很差
- 欠拟合:模型像“没学明白”的学生,连训练数据都学不好,更别说新数据了
关键策略:交叉验证
直接用全部数据训练和测试会导致评估结果不可靠。交叉验证通过多次划分数据来得到更稳定的评估:
# 简化的K折交叉验证概念
数据集 → [第1折, 第2折, ..., 第K折]
第1轮:用第1折作验证集,其余作训练集 → 评估得分1
第2轮:用第2折作验证集,其余作训练集 → 评估得分2
...
第K轮:用第K折作验证集,其余作训练集 → 评估得分K
最终得分 = 平均(得分1, 得分2, ..., 得分K)
评估指标选择
不同任务需要不同指标:
- 分类问题:准确率、精确率、召回率、F1分数
- 回归问题:均方误差(MSE)、平均绝对误差(MAE)、R²分数
- 不平衡数据:优先考虑精确率、召回率、F1,而非准确率
代码示例
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split, cross_val_score, StratifiedKFold
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
from sklearn.svm import SVC
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
warnings.filterwarnings('ignore')
# 1. 准备数据 - 使用红酒质量数据集(分类任务)
# 加载数据(这里模拟数据,实际应用中可以从文件加载)
np.random.seed(42)
n_samples = 500
X = np.random.randn(n_samples, 11) # 11个特征
y = np.random.choice([3, 4, 5, 6, 7, 8], size=n_samples) # 6个质量等级
# 划分训练集和测试集(最终评估用)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)
# 2. 数据预处理
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
# 3. 定义要比较的模型
models = {
"逻辑回归": LogisticRegression(max_iter=1000, random_state=42),
"支持向量机": SVC(kernel='rbf', random_state=42),
"随机森林": RandomForestClassifier(n_estimators=100, random_state=42),
"梯度提升": GradientBoostingClassifier(n_estimators=100, random_state=42)
}
# 4. 使用交叉验证比较模型
print("=== 模型比较:5折交叉验证 ===")
print("模型名称\t\t交叉验证平均准确率\t标准差")
cv_results = {}
for name, model in models.items():
# 使用分层K折交叉验证,保持类别比例
skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
# 执行交叉验证
cv_scores = cross_val_score(
model, X_train_scaled, y_train,
cv=skf, scoring='accuracy', n_jobs=-1
)
cv_results[name] = {
'mean': np.mean(cv_scores),
'std': np.std(cv_scores),
'scores': cv_scores
}
print(f"{name}\t\t{cv_results[name]['mean']:.4f}\t\t\t±{cv_results[name]['std']:.4f}")
# 5. 可视化比较结果
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# 箱线图比较
model_names = list(cv_results.keys())
cv_scores_list = [cv_results[name]['scores'] for name in model_names]
axes[0].boxplot(cv_scores_list, labels=model_names)
axes[0].set_title('模型交叉验证得分分布')
axes[0].set_ylabel('准确率')
axes[0].grid(True, alpha=0.3)
# 条形图比较
means = [cv_results[name]['mean'] for name in model_names]
stds = [cv_results[name]['std'] for name in model_names]
axes[1].bar(model_names, means, yerr=stds, capsize=5, alpha=0.7)
axes[1].set_title('模型平均准确率(带误差条)')
axes[1].set_ylabel('平均准确率')
axes[1].grid(True, alpha=0.3, axis='y')
plt.tight_layout()
plt.show()
# 6. 选择最佳模型并在测试集上评估
best_model_name = max(cv_results, key=lambda x: cv_results[x]['mean'])
print(f"\n=== 最佳模型: {best_model_name} ===")
# 重新在完整训练集上训练最佳模型
best_model = models[best_model_name]
best_model.fit(X_train_scaled, y_train)
# 在测试集上最终评估
y_pred = best_model.predict(X_test_scaled)
test_accuracy = accuracy_score(y_test, y_pred)
print(f"测试集准确率: {test_accuracy:.4f}")
print(f"\n详细分类报告:")
print(classification_report(y_test, y_pred))
# 绘制混淆矩阵
cm = confusion_matrix(y_test, y_pred)
plt.figure(figsize=(8, 6))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',
xticklabels=np.unique(y), yticklabels=np.unique(y))
plt.title(f'{best_model_name} - 混淆矩阵')
plt.xlabel('预测值')
plt.ylabel('真实值')
plt.show()
# 7. 实际应用:自动化模型选择函数
def automated_model_selection(X, y, models_dict, cv_folds=5):
"""
自动化模型选择函数
参数:
X: 特征矩阵
y: 目标变量
models_dict: 字典格式的模型 {模型名: 模型实例}
cv_folds: 交叉验证折数
返回:
best_model_name: 最佳模型名称
results: 所有模型的结果
"""
from sklearn.model_selection import cross_val_score
results = {}
for name, model in models_dict.items():
# 交叉验证
cv_scores = cross_val_score(model, X, y, cv=cv_folds, scoring='accuracy')
results[name] = {
'model': model,
'mean_score': np.mean(cv_scores),
'std_score': np.std(cv_scores),
'cv_scores': cv_scores
}
# 找到最佳模型
best_model_name = max(results, key=lambda x: results[x]['mean_score'])
# 打印结果
print("自动化模型选择结果:")
for name, result in results.items():
marker = " ★" if name == best_model_name else ""
print(f"{name}: {result['mean_score']:.4f} ± {result['std_score']:.4f}{marker}")
return best_model_name, results
# 使用自动化函数
best_name, all_results = automated_model_selection(
X_train_scaled, y_train, models, cv_folds=5
)
实践练习
练习1:基础比较
使用本课代码框架,添加一个新的模型(如K近邻分类器)到模型字典中,并重新执行比较。记录新模型的交叉验证准确率。
预期输出:
- KNN模型被添加到比较中
- 显示包含KNN在内的所有模型的比较结果
- 识别出新的最佳模型(可能是KNN,也可能不是)
练习2:使用不同评估指标
修改代码,将评估指标从accuracy改为f1_weighted(适用于多分类)。观察模型排名是否发生变化。
要求:
- 更改
cross_val_score中的scoring参数为'f1_weighted' - 重新执行比较并分析结果
- 思考:为什么在多分类任务中,加权F1分数可能比准确率更合适?
练习3:处理数据不平衡(进阶)
创建一个不平衡数据集(例如,90%是类别0,10%是类别1),然后比较不同模型在准确率和F1分数上的表现差异。
提示:
# 创建不平衡数据
X_imb = np.vstack([np.random.randn(900, 5), np.random.randn(100, 5) + 2])
y_imb = np.array([0]*900 + [1]*100)
# 比较逻辑回归和随机森林
预期输出:
- 展示不平衡数据的影响
- 演示为什么单一指标可能产生误导
- 展示F1分数在这种情况下的价值
常见错误
错误1:盲目选择复杂模型
# 错误做法:总是选择最复杂的模型
models = {
"逻辑回归": LogisticRegression(),
"神经网络": MLPClassifier(hidden_layer_sizes=(100, 100, 100))
}
# 选择神经网络,即使数据简单
正确做法:先从简单模型开始,逐步增加复杂度,通过交叉验证验证复杂模型是否真的更好。
错误2:只关注单一评估指标
# 错误做法:只看准确率
cv_scores = cross_val_score(model, X, y, scoring='accuracy')
# 在不平衡数据中,高准确率可能无意义
正确做法:使用多个相关指标,并根据业务需求选择主要指标。
错误3:忽略数据划分的合理性
# 错误做法:随机划分时间序列数据
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
# 时间序列数据应该按时间顺序划分
正确做法:根据数据特性选择合适的划分策略(随机划分、时间序列划分、分层划分等)。
错误4:过早使用测试集
# 错误做法:用测试集调整模型
# 在模型选择阶段就用测试集评估
cv_scores = cross_val_score(model, X_test, y_test)
正确做法:测试集应该只在最终评估时使用一次,用于模型选择和调优。
小结
关键要点回顾
- 模型选择是系统性过程:不是随意选择,而是基于数据特性和业务需求进行科学比较
- 交叉验证是核心工具:提供稳定可靠的模型评估,避免过拟合或欠拟合的陷阱
- 选择合适的评估指标:不同问题需要不同指标,要理解各指标的含义和适用场景
- 平衡模型复杂度:简单模型更易理解和部署,复杂模型可能更精确但需要更多数据
- 避免常见陷阱:不过早使用测试集、不盲目追求复杂模型、不依赖单一指标
下一步建议
- 尝试在真实数据集上应用模型选择策略
- 学习更高级的模型选择技术(如贝叶斯优化、元学习)
- 探索模型集成方法(结合多个模型的优势)
- 深入了解评估指标的数学原理和实际意义
课程回顾
本课我们学习了如何科学地比较和选择机器学习模型。从交叉验证的基本原理到实际代码实现,从评估指标选择到常见错误避免,你现在应该具备了进行系统模型选择的能力。记住,好的模型选择是机器学习项目成功的关键之一!
练习编辑器
rust
Loading...