本文目录导读:

我来详细介绍如何使用Scikit-learn绘制验证曲线(Validation Curve)来分析模型参数的影响。
验证曲线的基本概念
验证曲线展示不同参数值下模型的训练集和验证集性能,帮助我们:
- 判断模型是否过拟合或欠拟合
- 选择合适的超参数值
- 分析参数对模型性能的影响
基础示例:SVM的RBF核参数gamma验证曲线
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification
from sklearn.model_selection import validation_curve
from sklearn.svm import SVC
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
# 1. 生成数据
X, y = make_classification(
n_samples=1000,
n_features=20,
n_informative=10,
n_redundant=5,
random_state=42
)
# 2. 创建管道(包含标准化和SVM)
pipeline = Pipeline([
('scaler', StandardScaler()),
('svm', SVC(kernel='rbf'))
])
# 3. 定义要测试的参数范围
param_range = np.logspace(-3, 3, 7) # 从0.001到1000,7个值
# 4. 计算验证曲线
train_scores, test_scores = validation_curve(
pipeline, # 估计器
X, y, # 数据
param_name='svm__gamma', # 参数名称(注意管道中的前缀)
param_range=param_range, # 参数范围
cv=5, # 5折交叉验证
scoring='accuracy', # 评估指标
n_jobs=-1 # 使用所有CPU核心
)
# 5. 计算平均分和标准差
train_mean = np.mean(train_scores, axis=1)
train_std = np.std(train_scores, axis=1)
test_mean = np.mean(test_scores, axis=1)
test_std = np.std(test_scores, axis=1)
# 6. 绘制验证曲线
plt.figure(figsize=(10, 6))
plt.semilogx(param_range, train_mean, 'o-', color='blue', label='Training score')
plt.fill_between(param_range,
train_mean - train_std,
train_mean + train_std,
alpha=0.25, color='blue')
plt.semilogx(param_range, test_mean, 's-', color='red', label='Cross-validation score')
plt.fill_between(param_range,
test_mean - test_std,
test_mean + test_std,
alpha=0.25, color='red')
plt.xlabel('Parameter gamma')
plt.ylabel('Score')
plt.legend(loc='best')'Validation Curve with SVM (RBF kernel)')
plt.grid(True, alpha=0.3)
plt.show()
# 7. 打印结果
print("Gamma值验证结果:")
for i, gamma in enumerate(param_range):
print(f"gamma={gamma:.4f}: Train={train_mean[i]:.4f} (+/- {train_std[i]:.4f}), "
f"Test={test_mean[i]:.4f} (+/- {test_std[i]:.4f})")
更完整的示例:随机森林的树数量验证
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import StratifiedKFold
# 1. 使用更复杂的数据集
from sklearn.datasets import load_digits
data = load_digits()
X, y = data.data, data.target
# 2. 创建随机森林模型
rf = RandomForestClassifier(random_state=42)
# 3. 测试不同数量的树
param_range = [10, 50, 100, 200, 500]
# 4. 使用分层K折交叉验证
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
# 5. 计算验证曲线(使用更多评估指标)
from sklearn.metrics import make_scorer, accuracy_score, f1_score
# 同时计算准确率和F1分数
train_scores_acc, test_scores_acc = validation_curve(
rf, X, y,
param_name='n_estimators',
param_range=param_range,
cv=cv,
scoring='accuracy',
n_jobs=-1
)
train_scores_f1, test_scores_f1 = validation_curve(
rf, X, y,
param_name='n_estimators',
param_range=param_range,
cv=cv,
scoring='f1_macro',
n_jobs=-1
)
# 6. 绘制双指标对比图
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 5))
# 准确率曲线
for ax, scores, metric in [(ax1, (train_scores_acc, test_scores_acc), 'Accuracy'),
(ax2, (train_scores_f1, test_scores_f1), 'F1 Score')]:
train_mean = np.mean(scores[0], axis=1)
train_std = np.std(scores[0], axis=1)
test_mean = np.mean(scores[1], axis=1)
test_std = np.std(scores[1], axis=1)
ax.plot(param_range, train_mean, 'o-', color='blue', label='Training')
ax.fill_between(param_range,
train_mean - train_std,
train_mean + train_std,
alpha=0.25, color='blue')
ax.plot(param_range, test_mean, 's-', color='red', label='Cross-validation')
ax.fill_between(param_range,
test_mean - test_std,
test_mean + test_std,
alpha=0.25, color='red')
ax.set_xlabel('Number of trees')
ax.set_ylabel(metric)
ax.set_title(f'Validation Curve ({metric})')
ax.legend(loc='best')
ax.grid(True, alpha=0.3)
ax.set_xscale('log')
plt.tight_layout()
plt.show()
优化计算:使用学习曲线对比
from sklearn.model_selection import learning_curve
# 有时候验证曲线不够直观,可以结合学习曲线
def plot_validation_and_learning_curves(estimator, X, y, param_name, param_range):
"""绘制验证曲线和学习曲线的组合图"""
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 5))
# 1. 验证曲线
train_sizes, train_scores, test_scores = validation_curve(
estimator, X, y,
param_name=param_name,
param_range=param_range,
cv=5,
scoring='accuracy',
n_jobs=-1
)
# 绘制验证曲线
train_mean = np.mean(train_scores, axis=1)
train_std = np.std(train_scores, axis=1)
test_mean = np.mean(test_scores, axis=1)
test_std = np.std(test_scores, axis=1)
ax1.plot(param_range, train_mean, 'o-', color='blue', label='Training score')
ax1.fill_between(param_range, train_mean - train_std,
train_mean + train_std, alpha=0.1, color='blue')
ax1.plot(param_range, test_mean, 's-', color='red', label='Cross-validation score')
ax1.fill_between(param_range, test_mean - test_std,
test_mean + test_std, alpha=0.1, color='red')
ax1.set_xlabel('Parameter value')
ax1.set_ylabel('Score')
ax1.set_title('Validation Curve')
ax1.legend(loc='best')
ax1.grid(True, alpha=0.3)
# 2. 学习曲线(使用最佳参数)
best_idx = np.argmax(test_mean)
estimator.set_params(**{param_name: param_range[best_idx]})
train_sizes_lc, train_scores_lc, test_scores_lc = learning_curve(
estimator, X, y,
train_sizes=np.linspace(0.1, 1.0, 10),
cv=5,
scoring='accuracy',
n_jobs=-1
)
train_mean_lc = np.mean(train_scores_lc, axis=1)
train_std_lc = np.std(train_scores_lc, axis=1)
test_mean_lc = np.mean(test_scores_lc, axis=1)
test_std_lc = np.std(test_scores_lc, axis=1)
ax2.plot(train_sizes_lc, train_mean_lc, 'o-', color='blue', label='Training score')
ax2.fill_between(train_sizes_lc, train_mean_lc - train_std_lc,
train_mean_lc + train_std_lc, alpha=0.1, color='blue')
ax2.plot(train_sizes_lc, test_mean_lc, 's-', color='red', label='Cross-validation score')
ax2.fill_between(train_sizes_lc, test_mean_lc - test_std_lc,
test_mean_lc + test_std_lc, alpha=0.1, color='red')
ax2.set_xlabel('Training examples')
ax2.set_ylabel('Score')
ax2.set_title(f'Learning Curve (best {param_name}={param_range[best_idx]:.4f})')
ax2.legend(loc='best')
ax2.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
return best_idx, param_range[best_idx]
# 使用示例
print("分析SVM的gamma参数...")
best_idx, best_param = plot_validation_and_learning_curves(
pipeline, X, y, 'svm__gamma', param_range
)
print(f"最佳gamma值: {best_param:.4f}")
批量分析多个参数
from sklearn.model_selection import ParameterGrid
def analyze_multiple_params(estimator, X, y, param_grid, scoring='accuracy'):
"""批量分析多个参数的验证曲线"""
results = {}
for param_name, param_range in param_grid.items():
print(f"分析参数: {param_name}")
train_scores, test_scores = validation_curve(
estimator, X, y,
param_name=param_name,
param_range=param_range,
cv=5,
scoring=scoring,
n_jobs=-1
)
results[param_name] = {
'param_range': param_range,
'train_mean': np.mean(train_scores, axis=1),
'train_std': np.std(train_scores, axis=1),
'test_mean': np.mean(test_scores, axis=1),
'test_std': np.std(test_scores, axis=1),
'best_param': param_range[np.argmax(np.mean(test_scores, axis=1))]
}
# 绘制多个参数的验证曲线
n_params = len(results)
fig, axes = plt.subplots(1, n_params, figsize=(5*n_params, 5))
if n_params == 1:
axes = [axes]
for ax, (param_name, result) in zip(axes, results.items()):
ax.plot(result['param_range'], result['train_mean'], 'o-', label='Training')
ax.fill_between(result['param_range'],
result['train_mean'] - result['train_std'],
result['train_mean'] + result['train_std'],
alpha=0.2)
ax.plot(result['param_range'], result['test_mean'], 's-', label='Cross-validation')
ax.fill_between(result['param_range'],
result['test_mean'] - result['test_std'],
result['test_mean'] + result['test_std'],
alpha=0.2)
ax.set_xlabel(param_name)
ax.set_ylabel(scoring)
ax.set_title(f'Validation Curve: {param_name}')
ax.legend()
ax.grid(True, alpha=0.3)
# 标记最佳值
best_val = result['best_param']
ax.axvline(x=best_val, color='green', linestyle='--', alpha=0.5)
ax.annotate(f'Best: {best_val:.4f}',
xy=(best_val, ax.get_ylim()[1]),
xytext=(best_val, ax.get_ylim()[1]*0.9),
arrowprops=dict(arrowstyle='->'))
plt.tight_layout()
plt.show()
return results
# 使用示例
param_grid = {
'svc__C': [0.001, 0.01, 0.1, 1, 10, 100],
'svc__gamma': [0.001, 0.01, 0.1, 1, 10, 100]
}
# 注意:需要调整管道定义
pipeline2 = Pipeline([
('scaler', StandardScaler()),
('svc', SVC(kernel='rbf'))
])
# 由于验证曲线一次只能分析一个参数,我们分别分析
analysis_results = analyze_multiple_params(
pipeline2, X, y,
{'svc__C': [0.001, 0.01, 0.1, 1, 10, 100]}
)
实际应用案例:房价预测
from sklearn.datasets import fetch_california_housing
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.metrics import mean_squared_error, r2_score
# 加载数据
housing = fetch_california_housing()
X, y = housing.data, housing.target
# 创建GBDT模型
gbr = GradientBoostingRegressor(random_state=42)
# 分析学习率参数
param_range = [0.01, 0.05, 0.1, 0.2, 0.5, 1.0]
train_scores, test_scores = validation_curve(
gbr, X, y,
param_name='learning_rate',
param_range=param_range,
cv=5,
scoring='r2',
n_jobs=-1
)
# 绘制详细的结果图
plt.figure(figsize=(12, 5))
plt.subplot(1, 2, 1)
train_mean = np.mean(train_scores, axis=1)
train_std = np.std(train_scores, axis=1)
test_mean = np.mean(test_scores, axis=1)
test_std = np.std(test_scores, axis=1)
plt.semilogx(param_range, train_mean, 'o-', color='blue', label='Training R²')
plt.fill_between(param_range,
train_mean - train_std,
train_mean + train_std,
alpha=0.2, color='blue')
plt.semilogx(param_range, test_mean, 's-', color='red', label='Cross-validation R²')
plt.fill_between(param_range,
test_mean - test_std,
test_mean + test_std,
alpha=0.2, color='red')
plt.xlabel('Learning Rate')
plt.ylabel('R² Score')'Validation Curve: Learning Rate Effect')
plt.legend(loc='best')
plt.grid(True, alpha=0.3)
plt.subplot(1, 2, 2)
# 计算过拟合程度
overfitting = train_mean - test_mean
plt.semilogx(param_range, overfitting, 'd-', color='purple', linewidth=2)
plt.axhline(y=0, color='gray', linestyle='--', alpha=0.5)
plt.xlabel('Learning Rate')
plt.ylabel('Train - Test Score (Overfitting)')'Overfitting Analysis')
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
print(f"最佳学习率: {param_range[np.argmax(test_mean)]:.3f}")
print(f"最佳R²分数: {np.max(test_mean):.4f}")
注意事项和最佳实践
# 1. 参数范围选择
def suggest_param_range(estimator, X, y, param_name):
"""自动建议参数范围"""
# 这里只是一个示例,实际需要根据模型类型调整
base_params = estimator.get_params()
default_val = base_params.get(param_name, 1.0)
# 建议从默认值的1/10到10倍
if isinstance(default_val, (int, float)):
if default_val > 0:
return np.logspace(
np.log10(default_val) - 1,
np.log10(default_val) + 1,
10
)
return np.linspace(0.1, 10, 10)
# 2. 并行计算优化
from joblib import parallel_backend
def optimized_validation_curve(estimator, X, y, param_name, param_range):
"""使用并行优化的验证曲线"""
with parallel_backend('threading', n_jobs=-1):
train_scores, test_scores = validation_curve(
estimator, X, y,
param_name=param_name,
param_range=param_range,
cv=5,
scoring='accuracy',
n_jobs=-1
)
return train_scores, test_scores
# 3. 结果保存和比较
def save_validation_results(results, filename='validation_results.npy'):
"""保存验证曲线结果"""
np.save(filename, results)
print(f"Results saved to {filename}")
def load_validation_results(filename='validation_results.npy'):
"""加载验证曲线结果"""
return np.load(filename, allow_pickle=True).item()
# 4. 自动选择最佳参数
def find_optimal_param(estimator, X, y, param_name, param_range,
metric='accuracy', verbose=True):
"""自动找到使验证分数最高的参数"""
train_scores, test_scores = validation_curve(
estimator, X, y,
param_name=param_name,
param_range=param_range,
cv=5,
scoring=metric,
n_jobs=-1
)
test_mean = np.mean(test_scores, axis=1)
best_idx = np.argmax(test_mean)
best_param = param_range[best_idx]
if verbose:
print(f"Best {param_name}: {best_param}")
print(f"Best {metric}: {test_mean[best_idx]:.4f}")
print(f"Training {metric}: {np.mean(train_scores, axis=1)[best_idx]:.4f}")
# 检查过拟合
overfit = np.mean(train_scores, axis=1)[best_idx] - test_mean[best_idx]
if overfit > 0.1:
print(f"Warning: Possible overfitting (difference: {overfit:.4f})")
return best_param, test_mean[best_idx]
通过以上案例,你可以:
- 快速理解验证曲线的作用和用法
- 掌握不同模型的参数调优方法
- 学会分析过拟合和欠拟合情况
- 自动化参数选择过程
验证曲线是机器学习模型优化的重要工具,建议在实际项目中经常使用!