Skip to content

第7章:随机森林与集成方法

集成方法是机器学习中的一个重要概念,它通过组合多个基学习器来构建更强大的预测模型。随机森林是最流行的集成方法之一,本章将详细介绍各种集成技术及其应用。

7.1 什么是集成学习?

集成学习的核心思想是"三个臭皮匠,顶个诸葛亮"。通过组合多个弱学习器,我们可以得到一个性能更好、更稳定的强学习器。

7.1.1 集成学习的优势

  • 提高预测精度:多个模型的组合通常比单个模型更准确
  • 减少过拟合:通过平均化减少方差
  • 提高稳定性:对数据变化更加鲁棒
  • 处理复杂问题:能够学习更复杂的决策边界

7.1.2 集成方法的分类

  1. Bagging(装袋法):并行训练多个模型,如随机森林
  2. Boosting(提升法):串行训练模型,如AdaBoost、Gradient Boosting
  3. Stacking(堆叠法):使用元学习器组合基学习器
  4. 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'] = False

7.3 随机森林

7.3.1 随机森林原理

随机森林通过以下两个随机化过程来构建多样化的决策树:

  1. Bootstrap采样:每棵树使用不同的训练子集
  2. 特征随机选择:每次分割时只考虑特征的一个随机子集
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:随机森林调优

  1. 使用葡萄酒数据集训练随机森林分类器
  2. 通过网格搜索优化超参数
  3. 分析不同参数对性能的影响

练习2:集成方法比较

  1. 选择一个回归数据集
  2. 实现并比较随机森林、AdaBoost和Gradient Boosting回归器
  3. 分析它们在不同噪声水平下的表现

练习3:投票分类器

  1. 构建一个包含至少4个不同基学习器的投票分类器
  2. 比较硬投票和软投票的性能
  3. 分析基学习器的多样性对集成性能的影响

练习4:特征重要性分析

  1. 使用多个集成方法分析同一数据集的特征重要性
  2. 比较不同方法给出的特征重要性排序
  3. 基于特征重要性进行特征选择并评估效果

7.11 小结

在本章中,我们深入学习了集成学习的各个方面:

核心概念

  • 集成学习原理:通过组合多个学习器提高性能
  • Bagging方法:并行训练,减少方差
  • Boosting方法:串行训练,减少偏差
  • 投票和堆叠:不同的组合策略

主要技术

  • 随机森林:最流行的集成方法,平衡性能和效率
  • Extra Trees:更随机的决策树集成
  • AdaBoost:自适应提升算法
  • Gradient Boosting:梯度提升算法
  • 投票方法:简单有效的组合策略

实践技能

  • 超参数调优:网格搜索、交叉验证
  • 特征重要性:多角度分析特征贡献
  • 模型选择:根据场景选择合适的集成方法
  • 性能评估:全面的模型比较和分析

关键要点

  • 集成方法通常比单个学习器性能更好
  • 不同的集成方法适用于不同的场景
  • 基学习器的多样性是集成成功的关键
  • 需要在性能和计算复杂度之间找到平衡

7.12 下一步

现在你已经掌握了强大的集成学习方法!在下一章支持向量机中,我们将学习另一个重要的机器学习算法——支持向量机,它在处理高维数据和非线性问题方面有独特的优势。


章节要点回顾

  • ✅ 理解了集成学习的基本原理和优势
  • ✅ 掌握了随机森林和各种Bagging方法
  • ✅ 学会了Boosting方法的原理和应用
  • ✅ 了解了投票和堆叠等组合策略
  • ✅ 掌握了集成方法的超参数调优
  • ✅ 能够根据实际场景选择合适的集成方法

本站内容仅供学习和研究使用。