第7章:随机森林与集成方法
集成方法是机器学习中的一个重要概念,它通过组合多个基学习器来构建更强大的预测模型。随机森林是最流行的集成方法之一,本章将详细介绍各种集成技术及其应用。
7.1 什么是集成学习?
集成学习的核心思想是"三个臭皮匠,顶个诸葛亮"。通过组合多个弱学习器,我们可以得到一个性能更好、更稳定的强学习器。
7.1.1 集成学习的优势
- 提高预测精度:多个模型的组合通常比单个模型更准确
- 减少过拟合:通过平均化减少方差
- 提高稳定性:对数据变化更加鲁棒
- 处理复杂问题:能够学习更复杂的决策边界
7.1.2 集成方法的分类
- Bagging(装袋法):并行训练多个模型,如随机森林
- Boosting(提升法):串行训练模型,如AdaBoost、Gradient Boosting
- Stacking(堆叠法):使用元学习器组合基学习器
- Voting(投票法):简单的多数投票或平均
7.2 准备环境和数据
python
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.datasets import make_classification, make_regression, load_breast_cancer, load_wine
from sklearn.model_selection import train_test_split, cross_val_score, GridSearchCV, learning_curve
from sklearn.ensemble import (
RandomForestClassifier, RandomForestRegressor,
AdaBoostClassifier, AdaBoostRegressor,
GradientBoostingClassifier, GradientBoostingRegressor,
VotingClassifier, VotingRegressor,
BaggingClassifier, BaggingRegressor,
ExtraTreesClassifier, ExtraTreesRegressor
)
from sklearn.tree import DecisionTreeClassifier, DecisionTreeRegressor
from sklearn.linear_model import LogisticRegression, LinearRegression
from sklearn.svm import SVC, SVR
from sklearn.metrics import (
accuracy_score, classification_report, confusion_matrix,
mean_squared_error, r2_score, roc_auc_score
)
from sklearn.preprocessing import StandardScaler
import warnings
warnings.filterwarnings('ignore')
# 设置随机种子
np.random.seed(42)
# 设置图形样式
plt.style.use('seaborn-v0_8')
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False7.3 随机森林
7.3.1 随机森林原理
随机森林通过以下两个随机化过程来构建多样化的决策树:
- Bootstrap采样:每棵树使用不同的训练子集
- 特征随机选择:每次分割时只考虑特征的一个随机子集
python
def demonstrate_bootstrap_sampling():
"""演示Bootstrap采样过程"""
# 创建示例数据
original_data = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
n_samples = len(original_data)
print("原始数据:", original_data)
print("\nBootstrap采样示例:")
# 生成5个Bootstrap样本
for i in range(5):
bootstrap_indices = np.random.choice(n_samples, size=n_samples, replace=True)
bootstrap_sample = original_data[bootstrap_indices]
unique_samples = len(np.unique(bootstrap_sample))
print(f"样本 {i+1}: {bootstrap_sample} (唯一值: {unique_samples})")
# 可视化Bootstrap采样的多样性
fig, axes = plt.subplots(2, 3, figsize=(15, 8))
fig.suptitle('Bootstrap采样的多样性', fontsize=16)
# 原始数据
axes[0, 0].bar(range(len(original_data)), original_data, color='blue', alpha=0.7)
axes[0, 0].set_title('原始数据')
axes[0, 0].set_xlabel('索引')
axes[0, 0].set_ylabel('值')
# Bootstrap样本
for i in range(5):
row = (i + 1) // 3
col = (i + 1) % 3
bootstrap_indices = np.random.choice(n_samples, size=n_samples, replace=True)
bootstrap_sample = original_data[bootstrap_indices]
axes[row, col].bar(range(len(bootstrap_sample)), bootstrap_sample,
color='orange', alpha=0.7)
axes[row, col].set_title(f'Bootstrap样本 {i+1}')
axes[row, col].set_xlabel('索引')
axes[row, col].set_ylabel('值')
plt.tight_layout()
plt.show()
demonstrate_bootstrap_sampling()7.3.2 随机森林分类
python
# 创建分类数据集
X_class, y_class = make_classification(
n_samples=1000,
n_features=20,
n_informative=10,
n_redundant=5,
n_clusters_per_class=1,
random_state=42
)
# 分割数据
X_train, X_test, y_train, y_test = train_test_split(
X_class, y_class, test_size=0.2, random_state=42
)
# 创建随机森林分类器
rf_classifier = RandomForestClassifier(
n_estimators=100, # 树的数量
max_depth=10, # 最大深度
min_samples_split=5, # 分割所需最小样本数
min_samples_leaf=2, # 叶节点最小样本数
max_features='sqrt', # 每次分割考虑的特征数
random_state=42,
n_jobs=-1 # 并行处理
)
# 训练模型
rf_classifier.fit(X_train, y_train)
# 预测
y_pred_rf = rf_classifier.predict(X_test)
y_pred_proba_rf = rf_classifier.predict_proba(X_test)
# 评估
accuracy_rf = accuracy_score(y_test, y_pred_rf)
print(f"随机森林分类准确率: {accuracy_rf:.4f}")
print("\n详细分类报告:")
print(classification_report(y_test, y_pred_rf))
# 与单个决策树比较
dt_classifier = DecisionTreeClassifier(max_depth=10, random_state=42)
dt_classifier.fit(X_train, y_train)
y_pred_dt = dt_classifier.predict(X_test)
accuracy_dt = accuracy_score(y_test, y_pred_dt)
print(f"\n性能比较:")
print(f"单个决策树准确率: {accuracy_dt:.4f}")
print(f"随机森林准确率: {accuracy_rf:.4f}")
print(f"性能提升: {((accuracy_rf - accuracy_dt) / accuracy_dt * 100):.2f}%")7.3.3 特征重要性分析
python
# 特征重要性
feature_importance = rf_classifier.feature_importances_
feature_names = [f'特征_{i+1}' for i in range(X_class.shape[1])]
# 创建特征重要性DataFrame
importance_df = pd.DataFrame({
'feature': feature_names,
'importance': feature_importance
}).sort_values('importance', ascending=False)
# 可视化特征重要性
plt.figure(figsize=(12, 8))
top_features = importance_df.head(15)
plt.barh(range(len(top_features)), top_features['importance'])
plt.yticks(range(len(top_features)), top_features['feature'])
plt.xlabel('特征重要性')
plt.title('随机森林特征重要性(前15个特征)')
plt.gca().invert_yaxis()
plt.tight_layout()
plt.show()
print("前10个最重要的特征:")
for i, (_, row) in enumerate(top_features.head(10).iterrows()):
print(f"{i+1:2d}. {row['feature']}: {row['importance']:.4f}")
# 比较单棵树和随机森林的特征重要性
dt_importance = dt_classifier.feature_importances_
plt.figure(figsize=(12, 6))
x = np.arange(len(feature_names))
width = 0.35
plt.bar(x - width/2, feature_importance, width, label='随机森林', alpha=0.8)
plt.bar(x + width/2, dt_importance, width, label='单个决策树', alpha=0.8)
plt.xlabel('特征')
plt.ylabel('重要性')
plt.title('随机森林 vs 单个决策树特征重要性比较')
plt.xticks(x, [f'F{i+1}' for i in range(len(feature_names))], rotation=45)
plt.legend()
plt.tight_layout()
plt.show()7.3.4 随机森林回归
python
# 创建回归数据集
X_reg, y_reg = make_regression(
n_samples=500,
n_features=10,
n_informative=5,
noise=0.1,
random_state=42
)
X_train_reg, X_test_reg, y_train_reg, y_test_reg = train_test_split(
X_reg, y_reg, test_size=0.2, random_state=42
)
# 随机森林回归器
rf_regressor = RandomForestRegressor(
n_estimators=100,
max_depth=10,
min_samples_split=5,
min_samples_leaf=2,
random_state=42,
n_jobs=-1
)
rf_regressor.fit(X_train_reg, y_train_reg)
# 预测
y_pred_rf_reg = rf_regressor.predict(X_test_reg)
# 评估
r2_rf = r2_score(y_test_reg, y_pred_rf_reg)
rmse_rf = np.sqrt(mean_squared_error(y_test_reg, y_pred_rf_reg))
print(f"随机森林回归性能:")
print(f"R² 得分: {r2_rf:.4f}")
print(f"RMSE: {rmse_rf:.4f}")
# 与单个决策树比较
dt_regressor = DecisionTreeRegressor(max_depth=10, random_state=42)
dt_regressor.fit(X_train_reg, y_train_reg)
y_pred_dt_reg = dt_regressor.predict(X_test_reg)
r2_dt = r2_score(y_test_reg, y_pred_dt_reg)
rmse_dt = np.sqrt(mean_squared_error(y_test_reg, y_pred_dt_reg))
print(f"\n单个决策树回归性能:")
print(f"R² 得分: {r2_dt:.4f}")
print(f"RMSE: {rmse_dt:.4f}")
# 可视化预测结果
plt.figure(figsize=(12, 5))
plt.subplot(1, 2, 1)
plt.scatter(y_test_reg, y_pred_dt_reg, alpha=0.6, color='red')
plt.plot([y_test_reg.min(), y_test_reg.max()], [y_test_reg.min(), y_test_reg.max()], 'k--', lw=2)
plt.xlabel('真实值')
plt.ylabel('预测值')
plt.title(f'单个决策树 (R² = {r2_dt:.3f})')
plt.grid(True, alpha=0.3)
plt.subplot(1, 2, 2)
plt.scatter(y_test_reg, y_pred_rf_reg, alpha=0.6, color='blue')
plt.plot([y_test_reg.min(), y_test_reg.max()], [y_test_reg.min(), y_test_reg.max()], 'k--', lw=2)
plt.xlabel('真实值')
plt.ylabel('预测值')
plt.title(f'随机森林 (R² = {r2_rf:.3f})')
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()7.4 超参数调优
7.4.1 网格搜索优化
python
# 随机森林超参数网格搜索
param_grid = {
'n_estimators': [50, 100, 200],
'max_depth': [5, 10, 15, None],
'min_samples_split': [2, 5, 10],
'min_samples_leaf': [1, 2, 4],
'max_features': ['sqrt', 'log2', None]
}
# 使用较小的参数网格进行演示
small_param_grid = {
'n_estimators': [50, 100],
'max_depth': [5, 10],
'min_samples_split': [2, 5],
'max_features': ['sqrt', 'log2']
}
grid_search = GridSearchCV(
RandomForestClassifier(random_state=42),
small_param_grid,
cv=5,
scoring='accuracy',
n_jobs=-1,
verbose=1
)
print("正在进行网格搜索...")
grid_search.fit(X_train, y_train)
print(f"最佳参数: {grid_search.best_params_}")
print(f"最佳交叉验证得分: {grid_search.best_score_:.4f}")
# 测试最佳模型
best_rf = grid_search.best_estimator_
y_pred_best = best_rf.predict(X_test)
accuracy_best = accuracy_score(y_test, y_pred_best)
print(f"测试集准确率: {accuracy_best:.4f}")
# 可视化网格搜索结果
results_df = pd.DataFrame(grid_search.cv_results_)
# 选择几个重要参数进行可视化
fig, axes = plt.subplots(1, 2, figsize=(15, 6))
# n_estimators vs 性能
estimators_results = results_df.groupby('param_n_estimators')['mean_test_score'].mean()
axes[0].bar(estimators_results.index.astype(str), estimators_results.values)
axes[0].set_xlabel('树的数量')
axes[0].set_ylabel('平均交叉验证得分')
axes[0].set_title('树的数量对性能的影响')
# max_depth vs 性能
depth_results = results_df.groupby('param_max_depth')['mean_test_score'].mean()
axes[1].bar(depth_results.index.astype(str), depth_results.values, color='orange')
axes[1].set_xlabel('最大深度')
axes[1].set_ylabel('平均交叉验证得分')
axes[1].set_title('最大深度对性能的影响')
plt.tight_layout()
plt.show()7.4.2 学习曲线分析
python
def plot_learning_curve_ensemble(estimator, X, y, title="学习曲线"):
"""绘制集成模型的学习曲线"""
train_sizes, train_scores, val_scores = learning_curve(
estimator, X, y, cv=5, n_jobs=-1,
train_sizes=np.linspace(0.1, 1.0, 10),
scoring='accuracy'
)
train_mean = np.mean(train_scores, axis=1)
train_std = np.std(train_scores, axis=1)
val_mean = np.mean(val_scores, axis=1)
val_std = np.std(val_scores, axis=1)
plt.figure(figsize=(10, 6))
plt.plot(train_sizes, train_mean, 'o-', color='blue', label='训练得分')
plt.fill_between(train_sizes, train_mean - train_std, train_mean + train_std,
alpha=0.1, color='blue')
plt.plot(train_sizes, val_mean, 'o-', color='red', label='验证得分')
plt.fill_between(train_sizes, val_mean - val_std, val_mean + val_std,
alpha=0.1, color='red')
plt.xlabel('训练样本数')
plt.ylabel('准确率')
plt.title(title)
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()
# 绘制不同模型的学习曲线
plot_learning_curve_ensemble(
DecisionTreeClassifier(max_depth=10, random_state=42),
X_train, y_train, "单个决策树学习曲线"
)
plot_learning_curve_ensemble(
best_rf, X_train, y_train, "随机森林学习曲线"
)7.5 其他Bagging方法
7.5.1 Extra Trees(极端随机树)
python
# Extra Trees分类器
et_classifier = ExtraTreesClassifier(
n_estimators=100,
max_depth=10,
min_samples_split=5,
min_samples_leaf=2,
random_state=42,
n_jobs=-1
)
et_classifier.fit(X_train, y_train)
y_pred_et = et_classifier.predict(X_test)
accuracy_et = accuracy_score(y_test, y_pred_et)
print(f"Extra Trees准确率: {accuracy_et:.4f}")
# 比较随机森林和Extra Trees
models = {
'随机森林': rf_classifier,
'Extra Trees': et_classifier,
'单个决策树': dt_classifier
}
results = {}
for name, model in models.items():
y_pred = model.predict(X_test)
accuracy = accuracy_score(y_test, y_pred)
results[name] = accuracy
print("\n模型性能比较:")
for name, accuracy in results.items():
print(f"{name}: {accuracy:.4f}")
# 可视化比较
plt.figure(figsize=(10, 6))
plt.bar(results.keys(), results.values(), color=['blue', 'green', 'red'], alpha=0.7)
plt.title('不同模型准确率比较')
plt.ylabel('准确率')
plt.ylim(0.8, 1.0)
for i, (name, acc) in enumerate(results.items()):
plt.text(i, acc + 0.005, f'{acc:.4f}', ha='center')
plt.show()7.5.2 Bagging分类器
python
# 使用不同基学习器的Bagging
base_estimators = {
'决策树': DecisionTreeClassifier(max_depth=10, random_state=42),
'逻辑回归': LogisticRegression(random_state=42),
'SVM': SVC(random_state=42, probability=True)
}
bagging_results = {}
for name, base_estimator in base_estimators.items():
bagging_clf = BaggingClassifier(
base_estimator=base_estimator,
n_estimators=50,
random_state=42,
n_jobs=-1
)
bagging_clf.fit(X_train, y_train)
y_pred_bag = bagging_clf.predict(X_test)
accuracy_bag = accuracy_score(y_test, y_pred_bag)
bagging_results[f'Bagging + {name}'] = accuracy_bag
# 与单个基学习器比较
base_estimator.fit(X_train, y_train)
y_pred_base = base_estimator.predict(X_test)
accuracy_base = accuracy_score(y_test, y_pred_base)
bagging_results[f'单个{name}'] = accuracy_base
print("Bagging方法性能比较:")
for name, accuracy in bagging_results.items():
print(f"{name}: {accuracy:.4f}")
# 可视化Bagging效果
fig, ax = plt.subplots(figsize=(12, 6))
names = list(bagging_results.keys())
accuracies = list(bagging_results.values())
colors = ['blue' if 'Bagging' in name else 'orange' for name in names]
bars = ax.bar(names, accuracies, color=colors, alpha=0.7)
ax.set_title('Bagging方法 vs 单个学习器性能比较')
ax.set_ylabel('准确率')
ax.tick_params(axis='x', rotation=45)
# 添加数值标签
for bar, acc in zip(bars, accuracies):
height = bar.get_height()
ax.text(bar.get_x() + bar.get_width()/2., height + 0.005,
f'{acc:.4f}', ha='center', va='bottom')
plt.tight_layout()
plt.show()7.6 Boosting方法
7.6.1 AdaBoost
python
# AdaBoost分类器
ada_classifier = AdaBoostClassifier(
base_estimator=DecisionTreeClassifier(max_depth=1), # 弱学习器
n_estimators=100,
learning_rate=1.0,
random_state=42
)
ada_classifier.fit(X_train, y_train)
y_pred_ada = ada_classifier.predict(X_test)
accuracy_ada = accuracy_score(y_test, y_pred_ada)
print(f"AdaBoost准确率: {accuracy_ada:.4f}")
# 可视化AdaBoost的学习过程
def plot_adaboost_learning_process():
"""可视化AdaBoost的学习过程"""
# 使用简单的2D数据进行可视化
X_simple, y_simple = make_classification(
n_samples=200, n_features=2, n_redundant=0, n_informative=2,
n_clusters_per_class=1, random_state=42
)
# 训练AdaBoost
ada_simple = AdaBoostClassifier(
base_estimator=DecisionTreeClassifier(max_depth=1),
n_estimators=5,
learning_rate=1.0,
random_state=42
)
ada_simple.fit(X_simple, y_simple)
# 可视化每个弱学习器
fig, axes = plt.subplots(2, 3, figsize=(15, 10))
fig.suptitle('AdaBoost学习过程', fontsize=16)
# 原始数据
axes[0, 0].scatter(X_simple[y_simple==0, 0], X_simple[y_simple==0, 1],
c='red', alpha=0.6, label='类别0')
axes[0, 0].scatter(X_simple[y_simple==1, 0], X_simple[y_simple==1, 1],
c='blue', alpha=0.6, label='类别1')
axes[0, 0].set_title('原始数据')
axes[0, 0].legend()
axes[0, 0].grid(True, alpha=0.3)
# 每个弱学习器的决策边界
for i in range(5):
row = (i + 1) // 3
col = (i + 1) % 3
# 创建只包含前i+1个弱学习器的AdaBoost
ada_partial = AdaBoostClassifier(
base_estimator=DecisionTreeClassifier(max_depth=1),
n_estimators=i+1,
learning_rate=1.0,
random_state=42
)
ada_partial.fit(X_simple, y_simple)
# 绘制决策边界
h = 0.02
x_min, x_max = X_simple[:, 0].min() - 1, X_simple[:, 0].max() + 1
y_min, y_max = X_simple[:, 1].min() - 1, X_simple[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, h),
np.arange(y_min, y_max, h))
Z = ada_partial.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
axes[row, col].contourf(xx, yy, Z, alpha=0.8, cmap='RdYlBu')
axes[row, col].scatter(X_simple[y_simple==0, 0], X_simple[y_simple==0, 1],
c='red', alpha=0.6)
axes[row, col].scatter(X_simple[y_simple==1, 0], X_simple[y_simple==1, 1],
c='blue', alpha=0.6)
axes[row, col].set_title(f'前{i+1}个弱学习器')
axes[row, col].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
plot_adaboost_learning_process()7.6.2 Gradient Boosting
python
# Gradient Boosting分类器
gb_classifier = GradientBoostingClassifier(
n_estimators=100,
learning_rate=0.1,
max_depth=3,
random_state=42
)
gb_classifier.fit(X_train, y_train)
y_pred_gb = gb_classifier.predict(X_test)
accuracy_gb = accuracy_score(y_test, y_pred_gb)
print(f"Gradient Boosting准确率: {accuracy_gb:.4f}")
# 可视化训练过程中的损失
train_scores = gb_classifier.train_score_
test_scores = []
# 计算测试集上的损失
for i, pred in enumerate(gb_classifier.staged_predict_proba(X_test)):
test_loss = -np.mean(y_test * np.log(pred[:, 1] + 1e-15) +
(1 - y_test) * np.log(pred[:, 0] + 1e-15))
test_scores.append(test_loss)
plt.figure(figsize=(10, 6))
plt.plot(range(1, len(train_scores) + 1), train_scores, label='训练损失', color='blue')
plt.plot(range(1, len(test_scores) + 1), test_scores, label='测试损失', color='red')
plt.xlabel('迭代次数')
plt.ylabel('损失')
plt.title('Gradient Boosting训练过程')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()
# 特征重要性
gb_importance = gb_classifier.feature_importances_
rf_importance = rf_classifier.feature_importances_
plt.figure(figsize=(12, 6))
x = np.arange(len(feature_names))
width = 0.35
plt.bar(x - width/2, gb_importance, width, label='Gradient Boosting', alpha=0.8)
plt.bar(x + width/2, rf_importance, width, label='随机森林', alpha=0.8)
plt.xlabel('特征')
plt.ylabel('重要性')
plt.title('Gradient Boosting vs 随机森林特征重要性比较')
plt.xticks(x, [f'F{i+1}' for i in range(len(feature_names))], rotation=45)
plt.legend()
plt.tight_layout()
plt.show()7.7 投票和堆叠方法
7.7.1 投票分类器
python
# 创建基学习器
lr_clf = LogisticRegression(random_state=42)
rf_clf = RandomForestClassifier(n_estimators=50, random_state=42)
svm_clf = SVC(random_state=42, probability=True)
# 硬投票
hard_voting_clf = VotingClassifier(
estimators=[('lr', lr_clf), ('rf', rf_clf), ('svm', svm_clf)],
voting='hard'
)
# 软投票
soft_voting_clf = VotingClassifier(
estimators=[('lr', lr_clf), ('rf', rf_clf), ('svm', svm_clf)],
voting='soft'
)
# 训练所有模型
models = {
'逻辑回归': lr_clf,
'随机森林': rf_clf,
'SVM': svm_clf,
'硬投票': hard_voting_clf,
'软投票': soft_voting_clf
}
voting_results = {}
print("投票方法性能比较:")
print("模型\t\t准确率")
print("-" * 30)
for name, model in models.items():
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
accuracy = accuracy_score(y_test, y_pred)
voting_results[name] = accuracy
print(f"{name}\t{accuracy:.4f}")
# 可视化投票方法效果
plt.figure(figsize=(10, 6))
names = list(voting_results.keys())
accuracies = list(voting_results.values())
colors = ['blue', 'green', 'red', 'orange', 'purple']
bars = plt.bar(names, accuracies, color=colors, alpha=0.7)
plt.title('投票方法性能比较')
plt.ylabel('准确率')
plt.xticks(rotation=45)
# 添加数值标签
for bar, acc in zip(bars, accuracies):
height = bar.get_height()
plt.text(bar.get_x() + bar.get_width()/2., height + 0.005,
f'{acc:.4f}', ha='center', va='bottom')
plt.tight_layout()
plt.show()7.7.2 简单的堆叠示例
python
from sklearn.model_selection import cross_val_predict
def simple_stacking_classifier(base_models, meta_model, X_train, y_train, X_test):
"""简单的堆叠分类器实现"""
# 第一层:基学习器的交叉验证预测
meta_features_train = np.zeros((X_train.shape[0], len(base_models)))
meta_features_test = np.zeros((X_test.shape[0], len(base_models)))
for i, (name, model) in enumerate(base_models.items()):
# 交叉验证预测训练集
meta_features_train[:, i] = cross_val_predict(
model, X_train, y_train, cv=5, method='predict_proba'
)[:, 1]
# 训练模型并预测测试集
model.fit(X_train, y_train)
meta_features_test[:, i] = model.predict_proba(X_test)[:, 1]
# 第二层:元学习器
meta_model.fit(meta_features_train, y_train)
stacking_pred = meta_model.predict(meta_features_test)
return stacking_pred, meta_features_train, meta_features_test
# 定义基学习器和元学习器
base_models = {
'lr': LogisticRegression(random_state=42),
'rf': RandomForestClassifier(n_estimators=50, random_state=42),
'svm': SVC(random_state=42, probability=True)
}
meta_model = LogisticRegression(random_state=42)
# 执行堆叠
stacking_pred, meta_train, meta_test = simple_stacking_classifier(
base_models, meta_model, X_train, y_train, X_test
)
stacking_accuracy = accuracy_score(y_test, stacking_pred)
print(f"堆叠方法准确率: {stacking_accuracy:.4f}")
# 可视化元特征
plt.figure(figsize=(12, 8))
# 元特征的分布
for i, (name, _) in enumerate(base_models.items()):
plt.subplot(2, 2, i+1)
plt.hist(meta_train[y_train==0, i], alpha=0.6, label='类别0', bins=20)
plt.hist(meta_train[y_train==1, i], alpha=0.6, label='类别1', bins=20)
plt.title(f'{name}的元特征分布')
plt.xlabel('预测概率')
plt.ylabel('频次')
plt.legend()
# 元特征的相关性
plt.subplot(2, 2, 4)
meta_df = pd.DataFrame(meta_train, columns=list(base_models.keys()))
correlation_matrix = meta_df.corr()
sns.heatmap(correlation_matrix, annot=True, cmap='coolwarm', center=0)
plt.title('基学习器预测的相关性')
plt.tight_layout()
plt.show()
# 最终性能比较
final_results = voting_results.copy()
final_results['堆叠方法'] = stacking_accuracy
print("\n所有集成方法性能总结:")
for name, accuracy in sorted(final_results.items(), key=lambda x: x[1], reverse=True):
print(f"{name}: {accuracy:.4f}")7.8 实际应用案例
7.8.1 乳腺癌诊断
python
# 加载乳腺癌数据集
cancer = load_breast_cancer()
X_cancer, y_cancer = cancer.data, cancer.target
X_train_cancer, X_test_cancer, y_train_cancer, y_test_cancer = train_test_split(
X_cancer, y_cancer, test_size=0.2, random_state=42, stratify=y_cancer
)
# 特征标准化
scaler = StandardScaler()
X_train_cancer_scaled = scaler.fit_transform(X_train_cancer)
X_test_cancer_scaled = scaler.transform(X_test_cancer)
# 构建多个集成模型
ensemble_models = {
'随机森林': RandomForestClassifier(n_estimators=100, random_state=42),
'Extra Trees': ExtraTreesClassifier(n_estimators=100, random_state=42),
'AdaBoost': AdaBoostClassifier(n_estimators=100, random_state=42),
'Gradient Boosting': GradientBoostingClassifier(n_estimators=100, random_state=42),
'投票分类器': VotingClassifier([
('rf', RandomForestClassifier(n_estimators=50, random_state=42)),
('ada', AdaBoostClassifier(n_estimators=50, random_state=42)),
('gb', GradientBoostingClassifier(n_estimators=50, random_state=42))
], voting='soft')
}
cancer_results = {}
print("乳腺癌诊断模型性能比较:")
print("模型\t\t\t准确率\t\tAUC得分")
print("-" * 50)
for name, model in ensemble_models.items():
# 训练模型
if name in ['AdaBoost', 'Gradient Boosting']:
model.fit(X_train_cancer, y_train_cancer)
y_pred = model.predict(X_test_cancer)
y_pred_proba = model.predict_proba(X_test_cancer)[:, 1]
else:
model.fit(X_train_cancer_scaled, y_train_cancer)
y_pred = model.predict(X_test_cancer_scaled)
y_pred_proba = model.predict_proba(X_test_cancer_scaled)[:, 1]
# 评估
accuracy = accuracy_score(y_test_cancer, y_pred)
auc_score = roc_auc_score(y_test_cancer, y_pred_proba)
cancer_results[name] = {'accuracy': accuracy, 'auc': auc_score}
print(f"{name:<20}\t{accuracy:.4f}\t\t{auc_score:.4f}")
# 可视化结果
fig, axes = plt.subplots(1, 2, figsize=(15, 6))
# 准确率比较
names = list(cancer_results.keys())
accuracies = [cancer_results[name]['accuracy'] for name in names]
aucs = [cancer_results[name]['auc'] for name in names]
axes[0].bar(names, accuracies, color='skyblue', alpha=0.7)
axes[0].set_title('乳腺癌诊断模型准确率比较')
axes[0].set_ylabel('准确率')
axes[0].tick_params(axis='x', rotation=45)
axes[0].set_ylim(0.9, 1.0)
# AUC比较
axes[1].bar(names, aucs, color='lightcoral', alpha=0.7)
axes[1].set_title('乳腺癌诊断模型AUC比较')
axes[1].set_ylabel('AUC得分')
axes[1].tick_params(axis='x', rotation=45)
axes[1].set_ylim(0.9, 1.0)
plt.tight_layout()
plt.show()7.8.2 特征重要性综合分析
python
# 获取不同模型的特征重要性
feature_names_cancer = cancer.feature_names
# 只分析有feature_importances_属性的模型
importance_models = {
'随机森林': ensemble_models['随机森林'],
'Extra Trees': ensemble_models['Extra Trees'],
'Gradient Boosting': ensemble_models['Gradient Boosting']
}
# 收集特征重要性
importance_data = {}
for name, model in importance_models.items():
importance_data[name] = model.feature_importances_
# 创建特征重要性DataFrame
importance_df = pd.DataFrame(importance_data, index=feature_names_cancer)
# 计算平均重要性
importance_df['平均重要性'] = importance_df.mean(axis=1)
importance_df = importance_df.sort_values('平均重要性', ascending=False)
# 可视化前15个最重要的特征
plt.figure(figsize=(15, 10))
# 热力图
plt.subplot(2, 1, 1)
top_features = importance_df.head(15)
sns.heatmap(top_features[['随机森林', 'Extra Trees', 'Gradient Boosting']],
annot=True, cmap='YlOrRd', fmt='.3f')
plt.title('不同集成模型的特征重要性热力图(前15个特征)')
# 平均重要性柱状图
plt.subplot(2, 1, 2)
plt.barh(range(len(top_features)), top_features['平均重要性'])
plt.yticks(range(len(top_features)), top_features.index)
plt.xlabel('平均特征重要性')
plt.title('特征重要性排序(前15个特征)')
plt.gca().invert_yaxis()
plt.tight_layout()
plt.show()
print("前10个最重要的特征:")
for i, (feature, row) in enumerate(top_features.head(10).iterrows()):
print(f"{i+1:2d}. {feature}: {row['平均重要性']:.4f}")7.9 集成方法的选择指南
7.9.1 不同方法的特点比较
python
def compare_ensemble_methods():
"""比较不同集成方法的特点"""
comparison_data = {
'方法': ['随机森林', 'Extra Trees', 'AdaBoost', 'Gradient Boosting', '投票方法', '堆叠方法'],
'训练方式': ['并行', '并行', '串行', '串行', '并行', '分层'],
'基学习器': ['决策树', '决策树', '弱学习器', '决策树', '多种', '多种'],
'过拟合风险': ['低', '低', '中', '高', '低', '中'],
'训练速度': ['快', '快', '中', '慢', '中', '慢'],
'预测速度': ['快', '快', '快', '快', '中', '慢'],
'参数敏感性': ['低', '低', '中', '高', '低', '中'],
'可解释性': ['中', '中', '低', '低', '低', '低']
}
comparison_df = pd.DataFrame(comparison_data)
print("集成方法特点比较:")
print(comparison_df.to_string(index=False))
return comparison_df
comparison_df = compare_ensemble_methods()
# 可视化比较(数值化某些特征)
numeric_comparison = {
'随机森林': {'过拟合风险': 2, '训练速度': 4, '预测速度': 4, '参数敏感性': 2, '可解释性': 3},
'Extra Trees': {'过拟合风险': 2, '训练速度': 4, '预测速度': 4, '参数敏感性': 2, '可解释性': 3},
'AdaBoost': {'过拟合风险': 3, '训练速度': 3, '预测速度': 4, '参数敏感性': 3, '可解释性': 2},
'Gradient Boosting': {'过拟合风险': 4, '训练速度': 2, '预测速度': 4, '参数敏感性': 4, '可解释性': 2},
'投票方法': {'过拟合风险': 2, '训练速度': 3, '预测速度': 3, '参数敏感性': 2, '可解释性': 2},
'堆叠方法': {'过拟合风险': 3, '训练速度': 2, '预测速度': 2, '参数敏感性': 3, '可解释性': 2}
}
# 雷达图比较
from math import pi
fig, axes = plt.subplots(2, 3, figsize=(18, 12), subplot_kw=dict(projection='polar'))
fig.suptitle('集成方法特点雷达图比较', fontsize=16)
categories = list(list(numeric_comparison.values())[0].keys())
N = len(categories)
angles = [n / float(N) * 2 * pi for n in range(N)]
angles += angles[:1]
for i, (method, values) in enumerate(numeric_comparison.items()):
row = i // 3
col = i % 3
values_list = list(values.values())
values_list += values_list[:1]
axes[row, col].plot(angles, values_list, 'o-', linewidth=2, label=method)
axes[row, col].fill(angles, values_list, alpha=0.25)
axes[row, col].set_xticks(angles[:-1])
axes[row, col].set_xticklabels(categories)
axes[row, col].set_ylim(0, 5)
axes[row, col].set_title(method)
axes[row, col].grid(True)
plt.tight_layout()
plt.show()7.9.2 选择建议
python
def ensemble_selection_guide():
"""集成方法选择指南"""
print("集成方法选择指南:")
print("=" * 50)
scenarios = {
"数据量大,需要快速训练": {
"推荐": ["随机森林", "Extra Trees"],
"原因": "并行训练,速度快,对大数据集友好"
},
"数据量小,追求最高精度": {
"推荐": ["Gradient Boosting", "堆叠方法"],
"原因": "能够充分利用小数据集,通常有更高的精度"
},
"需要模型可解释性": {
"推荐": ["随机森林", "Extra Trees"],
"原因": "提供特征重要性,决策过程相对透明"
},
"数据有噪声": {
"推荐": ["随机森林", "投票方法"],
"原因": "对噪声鲁棒,不容易过拟合"
},
"计算资源有限": {
"推荐": ["随机森林", "投票方法"],
"原因": "训练和预测速度快,资源消耗少"
},
"不平衡数据集": {
"推荐": ["随机森林(调整class_weight)", "AdaBoost"],
"原因": "能够处理类别不平衡问题"
}
}
for scenario, info in scenarios.items():
print(f"\n场景: {scenario}")
print(f"推荐方法: {', '.join(info['推荐'])}")
print(f"原因: {info['原因']}")
print("\n" + "=" * 50)
print("一般性建议:")
print("1. 首先尝试随机森林 - 通常是很好的基线")
print("2. 如果需要更高精度,尝试Gradient Boosting")
print("3. 如果训练时间是瓶颈,考虑Extra Trees")
print("4. 对于复杂问题,可以尝试投票或堆叠方法")
print("5. 始终使用交叉验证来评估和比较不同方法")
ensemble_selection_guide()7.10 练习题
练习1:随机森林调优
- 使用葡萄酒数据集训练随机森林分类器
- 通过网格搜索优化超参数
- 分析不同参数对性能的影响
练习2:集成方法比较
- 选择一个回归数据集
- 实现并比较随机森林、AdaBoost和Gradient Boosting回归器
- 分析它们在不同噪声水平下的表现
练习3:投票分类器
- 构建一个包含至少4个不同基学习器的投票分类器
- 比较硬投票和软投票的性能
- 分析基学习器的多样性对集成性能的影响
练习4:特征重要性分析
- 使用多个集成方法分析同一数据集的特征重要性
- 比较不同方法给出的特征重要性排序
- 基于特征重要性进行特征选择并评估效果
7.11 小结
在本章中,我们深入学习了集成学习的各个方面:
核心概念
- 集成学习原理:通过组合多个学习器提高性能
- Bagging方法:并行训练,减少方差
- Boosting方法:串行训练,减少偏差
- 投票和堆叠:不同的组合策略
主要技术
- 随机森林:最流行的集成方法,平衡性能和效率
- Extra Trees:更随机的决策树集成
- AdaBoost:自适应提升算法
- Gradient Boosting:梯度提升算法
- 投票方法:简单有效的组合策略
实践技能
- 超参数调优:网格搜索、交叉验证
- 特征重要性:多角度分析特征贡献
- 模型选择:根据场景选择合适的集成方法
- 性能评估:全面的模型比较和分析
关键要点
- 集成方法通常比单个学习器性能更好
- 不同的集成方法适用于不同的场景
- 基学习器的多样性是集成成功的关键
- 需要在性能和计算复杂度之间找到平衡
7.12 下一步
现在你已经掌握了强大的集成学习方法!在下一章支持向量机中,我们将学习另一个重要的机器学习算法——支持向量机,它在处理高维数据和非线性问题方面有独特的优势。
章节要点回顾:
- ✅ 理解了集成学习的基本原理和优势
- ✅ 掌握了随机森林和各种Bagging方法
- ✅ 学会了Boosting方法的原理和应用
- ✅ 了解了投票和堆叠等组合策略
- ✅ 掌握了集成方法的超参数调优
- ✅ 能够根据实际场景选择合适的集成方法