Hi,我是小白~
今儿再来和大家聊聊集成算法,每一种算法都给到大家详细的解析和案例总结。
集成算法通过组合多个模型的预测,通常能提升模型的整体性能,减少单一模型的偏差和方差。它能够有效应对过拟合问题,增加模型的泛化能力。常见的集成方法如Bagging、Boosting等在实际应用中表现出更强的鲁棒性和准确性。
今儿和大家分享的集成算法有:
Bagging Random Forest Boosting AdaBoost Gradient Boosting XGBoost LightGBM GBDT Stacking Voting
如果需要本文PDF版本的同学,文末获取~
另外,文末有总结性的干货~
一起来看下具体细化内容~
1. Bagging(Bootstrap Aggregating)
Bagging是通过多次对训练数据集进行有放回抽样,生成多个不同的子数据集。每个子数据集训练一个基模型,最终通过平均或投票的方式集成多个基模型的预测结果。Bagging的主要作用是减少模型的方差,使得集成后的模型更为稳定。
原理
Bootstrap:对数据集进行有放回的采样,形成多个子数据集,每个子集都有一定的重复样本。
Aggregating:训练多个基模型,每个模型在不同的子数据集上进行拟合,最后将多个模型的预测进行平均(回归任务)或投票(分类任务)。
核心公式
假设我们有 个训练样本,采样 次有放回的样本形成 个基模型 ,则集成模型的预测为:
回归任务:
分类任务:
其中 为指示函数,表示基模型 是否预测为类别 。
方差的减少:
假设每个基模型是无偏的(即 ),并且具有相同的方差 ,那么Bagging后的模型的方差为:
通过Bagging,将多个基模型的方差降低到原来的 ,从而减少模型的方差,增强稳定性。
偏差的保持:
由于每个基模型是无偏的,因此Bagging不会改变模型的偏差,即:
Bagging通过降低方差来提高模型的稳定性,但不能显著改变模型的偏差。因此,对于高偏差的模型(如未充分拟合的模型),Bagging的效果可能有限。
Python案例
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.datasets import make_classification
from sklearn.ensemble import BaggingClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
# 设置图形样式
sns.set(style="whitegrid")
plt.figure(figsize=(16, 8))
# 1. 生成虚拟数据集
X, y = make_classification(n_samples=500, n_features=2, n_informative=2, n_redundant=0,
n_clusters_per_class=1, n_classes=2, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
# 2. 单一决策树和Bagging模型
single_tree = DecisionTreeClassifier(random_state=42)
bagging_model = BaggingClassifier(base_estimator=DecisionTreeClassifier(), n_estimators=50, random_state=42)
# 训练模型
single_tree.fit(X_train, y_train)
bagging_model.fit(X_train, y_train)
# 预测结果
y_pred_tree = single_tree.predict(X_test)
y_pred_bagging = bagging_model.predict(X_test)
# 计算精度
accuracy_tree = accuracy_score(y_test, y_pred_tree)
accuracy_bagging = accuracy_score(y_test, y_pred_bagging)
# 打印精度
print(f"单一决策树的测试精度: {accuracy_tree:.2f}")
print(f"Bagging模型的测试精度: {accuracy_bagging:.2f}")
# 3. 决策边界可视化函数
def plot_decision_boundary(model, X, y, subplot, title):
x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.1), np.arange(y_min, y_max, 0.1))
Z = model.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
plt.subplot(subplot)
plt.contourf(xx, yy, Z, alpha=0.4, cmap=plt.cm.RdYlBu)
plt.scatter(X[:, 0], X[:, 1], c=y, s=20, edgecolor='k', cmap=plt.cm.RdYlBu)
plt.title(title)
plt.xlabel('Feature 1')
plt.ylabel('Feature 2')
# 绘制单一决策树和Bagging的决策边界
plot_decision_boundary(single_tree, X_test, y_test, 121, "Decision Boundary: Single Decision Tree")
plot_decision_boundary(bagging_model, X_test, y_test, 122, "Decision Boundary: Bagging with 50 Trees")
# 4. 绘制误差曲线
train_sizes = np.linspace(0.1, 1.0, 10)
train_errors_tree = []
test_errors_tree = []
train_errors_bagging = []
test_errors_bagging = []
for train_size in train_sizes:
X_train_part, _, y_train_part, _ = train_test_split(X_train, y_train, train_size=train_size, random_state=42)
# 单一决策树
single_tree.fit(X_train_part, y_train_part)
y_train_pred_tree = single_tree.predict(X_train_part)
y_test_pred_tree = single_tree.predict(X_test)
train_errors_tree.append(1 - accuracy_score(y_train_part, y_train_pred_tree))
test_errors_tree.append(1 - accuracy_score(y_test, y_test_pred_tree))
# Bagging
bagging_model.fit(X_train_part, y_train_part)
y_train_pred_bagging = bagging_model.predict(X_train_part)
y_test_pred_bagging = bagging_model.predict(X_test)
train_errors_bagging.append(1 - accuracy_score(y_train_part, y_train_pred_bagging))
test_errors_bagging.append(1 - accuracy_score(y_test, y_test_pred_bagging))
# 误差曲线图
plt.figure(figsize=(12, 6))
plt.plot(train_sizes, train_errors_tree, 'o-', color='r', label='Training Error (Tree)', lw=2)
plt.plot(train_sizes, test_errors_tree, 'o-', color='b', label='Testing Error (Tree)', lw=2)
plt.plot(train_sizes, train_errors_bagging, 's-', color='g', label='Training Error (Bagging)', lw=2)
plt.plot(train_sizes, test_errors_bagging, 's-', color='orange', label='Testing Error (Bagging)', lw=2)
plt.title('Error Curves for Decision Tree vs Bagging', fontsize=14)
plt.xlabel('Training Set Size')
plt.ylabel('Error Rate')
plt.legend(loc='best')
plt.grid(True)
plt.tight_layout()
# 展示所有图形
plt.show()
决策边界图:该图用于展示 Bagging 和单一决策树模型的决策能力。通过可视化测试数据的分类边界,可以清楚地看到 Bagging 如何通过多个树的集成,形成更加稳定、平滑的决策边界。相比单一决策树容易过拟合的数据,Bagging 的边界更加平滑。
误差曲线:误差曲线展示了模型的泛化能力,随着训练集大小的增加,训练误差和测试误差的变化趋势。通过对比 Bagging 和单一决策树的误差曲线,可以直观地看到 Bagging 如何减少过拟合(即训练误差和测试误差之间的差距减小),并且在整体上提高模型的泛化能力。
2. Random Forest
Random Forest是Bagging的扩展版本,使用决策树作为基模型。不同于Bagging,Random Forest在每个节点的分裂过程中,还随机选择部分特征进行分裂,从而增加模型的多样性,进一步降低方差。
原理
随机采样数据:与Bagging相同,使用有放回采样的方式生成多个训练子集。
随机选择特征:每棵决策树的每个节点分裂时,不是使用所有特征,而是从随机选择的一部分特征中选出最佳分裂点。
核心公式
与Bagging相似,Random Forest的预测为:
回归任务:
分类任务:
特征随机选择的影响:
在标准决策树算法中,每次分裂节点时,选择能够最大化某种指标(如信息增益或基尼系数)的特征进行分裂。然而,在Random Forest中,每个节点仅从随机选取的一部分特征中寻找最优分裂点。这样做的好处是:
降低了特征相关性对模型的影响。如果某些特征非常强,则所有决策树可能会过度依赖它们,增加模型的相似性,无法有效降低方差。通过随机选择特征,强特征不再总是被选择,从而增加了模型的多样性。
在高维数据集上,减少了模型训练时的计算量。
集成后模型的方差和偏差:
假设有 个特征,每次分裂时选择 个特征,则随机选择特征增加了模型的多样性。类似于Bagging的推导,Random Forest同样能够有效地降低方差,但对偏差的影响较小。
Python案例
import numpy as np
import pandas as pd
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay, roc_curve, auc
import matplotlib.pyplot as plt
import seaborn as sns
# 生成虚拟数据集
X, y = make_classification(n_samples=1000, n_features=10, n_informative=5, n_classes=2, random_state=42)
# 划分数据集为训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
# 创建Random Forest模型并训练
rf = RandomForestClassifier(n_estimators=100, random_state=42)
rf.fit(X_train, y_train)
# 预测测试集
y_pred = rf.predict(X_test)
y_pred_prob = rf.predict_proba(X_test)[:, 1]
# 获取特征重要性
importances = rf.feature_importances_
indices = np.argsort(importances)[::-1]
features = [f"Feature {i}" for i in range(X.shape[1])]
# 计算混淆矩阵
cm = confusion_matrix(y_test, y_pred)
# 绘制ROC曲线
fpr, tpr, _ = roc_curve(y_test, y_pred_prob)
roc_auc = auc(fpr, tpr)
# 创建图形
plt.figure(figsize=(16, 10))
# 子图1:特征重要性
plt.subplot(2, 2, 1)
plt.title("Feature Importance", fontsize=14)
sns.barplot(x=importances[indices], y=[features[i] for i in indices], palette="coolwarm")
plt.xlabel('Importance', fontsize=12)
plt.ylabel('Features', fontsize=12)
# 子图2:混淆矩阵
plt.subplot(2, 2, 2)
plt.title("Confusion Matrix", fontsize=14)
sns.heatmap(cm, annot=True, fmt="d", cmap="Blues", cbar=False)
plt.ylabel('Actual label', fontsize=12)
plt.xlabel('Predicted label', fontsize=12)
# 子图3:ROC曲线
plt.subplot(2, 1, 2)
plt.plot(fpr, tpr, color='darkorange', lw=2, label=f'ROC curve (area = {roc_auc:.2f})')
plt.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--')
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate', fontsize=12)
plt.ylabel('True Positive Rate', fontsize=12)
plt.title('Receiver Operating Characteristic (ROC)', fontsize=14)
plt.legend(loc="lower right")
# 显示图形
plt.tight_layout()
plt.show()
生成虚拟数据集:使用 make_classification
函数创建一个具有 1000 个样本、10 个特征的二分类数据集,其中 5 个特征是有用信息。Random Forest 模型训练与预测:将数据集划分为训练集和测试集。通过 RandomForestClassifier
创建随机森林模型并在训练集上训练。使用训练好的模型对测试集进行预测。
特征重要性柱状图:展示每个特征对模型预测的重要性,帮助理解模型决策的依据。
混淆矩阵:展示模型在测试集上的分类表现,显示正确和错误分类的数量。
ROC 曲线:展示模型在不同阈值下的分类性能,反映模型的整体质量。
3. Boosting
Boosting是一类通过顺序训练多个基模型,并逐步纠正前一模型错误的集成方法。与Bagging主要降低方差不同,Boosting主要减少模型的偏差。Boosting中的每个模型依赖于前一个模型的错误。
原理
初始时每个样本具有相等的权重。
训练第一个基模型,计算其在训练集上的误差。
根据基模型的误差调整样本的权重,增大误分类样本的权重。
训练下一个基模型,重点学习被前一个模型错误分类的样本,逐步减少误差。
最终将多个基模型加权组合,得到强模型。
核心公式
Boosting的最终模型为各个基模型的加权和:
其中 是第 个基模型的权重,通常根据该模型的误差来确定。
错误修正机制:
在每一轮Boosting中,基模型根据样本的权重进行训练,模型训练的重点会放在那些被前一轮模型错误分类的样本上。假设我们要最小化某种损失函数 ,那么每一轮的模型训练实际上是在修正上一轮的预测错误。Boosting通过逐步迭代,使得误差逐步减小。
Boosting常用的具体算法包括 AdaBoost 和 Gradient Boosting。
Python案例
import numpy as np
import matplotlib.pyplot as plt
from sklearn.ensemble import AdaBoostClassifier
from sklearn.datasets import make_classification
from sklearn.tree import DecisionTreeClassifier
from matplotlib.colors import ListedColormap
# 生成虚拟数据集
X, y = make_classification(n_samples=1000, n_features=2, n_informative=2, n_redundant=0, n_clusters_per_class=1, random_state=42)
# 定义弱分类器(决策树),并使用 AdaBoost 进行训练
base_estimator = DecisionTreeClassifier(max_depth=1)
ada_boost = AdaBoostClassifier(base_estimator=base_estimator, n_estimators=50, learning_rate=1.0, random_state=42)
ada_boost.fit(X, y)
# 绘制分类边界函数
def plot_decision_boundary(model, X, y, ax, title):
# 设置颜色映射
cmap_light = ListedColormap(['#FFAAAA', '#AAAAFF'])
cmap_bold = ['#FF0000', '#0000FF']
# 创建网格,范围包含X的最小值到最大值
x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.02),
np.arange(y_min, y_max, 0.02))
Z = model.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
# 绘制决策边界
ax.contourf(xx, yy, Z, cmap=cmap_light)
# 绘制训练数据的散点
for i, color in zip(range(2), cmap_bold):
idx = np.where(y == i)
ax.scatter(X[idx, 0], X[idx, 1], c=color, label=f'Class {i}', edgecolor='k', s=50)
ax.set_xlim(xx.min(), xx.max())
ax.set_ylim(yy.min(), yy.max())
ax.set_title(title)
# 创建图形对象
fig, ax = plt.subplots(1, 2, figsize=(12, 6))
# 左图:原始数据的散点图
ax[0].scatter(X[:, 0], X[:, 1], c=y, cmap=ListedColormap(['#FF0000', '#0000FF']), edgecolor='k', s=50)
ax[0].set_title('Original Data Distribution')
ax[0].set_xlabel('Feature 1')
ax[0].set_ylabel('Feature 2')
# 右图:AdaBoost 的决策边界
plot_decision_boundary(ada_boost, X, y, ax[1], 'AdaBoost Decision Boundary')
# 增加图例
ax[1].legend()
# 显示图形
plt.tight_layout()
plt.show()
数据生成:通过
make_classification
函数生成了一个有 2 个特征的分类数据集,数据有 1000 个样本。模型训练:使用基于弱分类器(深度为 1 的决策树)的
AdaBoostClassifier
,对数据集进行拟合。绘图函数:
plot_decision_boundary
函数用来绘制模型的决策边界,通过在二维网格上计算预测值并可视化。
左图:直观地展示了数据分布,红色和蓝色点分别代表两个类别。通过这个图,我们能直观地看出数据是如何分布的,以及类别之间的可分性。
右图:显示了
AdaBoost
模型如何通过多轮的提升来找到一个非线性决策边界。这个图能够帮助我们理解模型的拟合能力及其在各个区域的分类效果。
4. AdaBoost(Adaptive Boosting)
AdaBoost是Boosting的一种实现,它通过不断调整样本的权重,使得后续的基模型能够重点学习之前被错误分类的样本。AdaBoost通常使用简单的弱分类器(如决策树桩)作为基模型,通过迭代提高整体模型的性能。
原理
初始时为每个样本分配相同的权重。
训练第一个基分类器,根据其错误率来调整分类器的权重。
对于被错误分类的样本,增大其权重,使得下一个分类器更关注这些样本。
迭代过程持续进行,直到达到预定的基模型数量或误差达到阈值。
核心公式
基分类器权重:每个基分类器 的权重 由分类误差 决定:
样本权重更新:
被错误分类的样本权重更新为:
其中 是样本 在第 轮中的权重, 表示样本是否被错误分类。
最终预测为多个基分类器加权投票的结果:
误差分析:
AdaBoost 通过计算每一轮基分类器的误差率 ,并根据误差率调整分类器权重 。误差率越低的分类器在集成中占据的权重越大。通过加大被错误分类的样本权重,后续分类器能够重点修正之前模型的错误。
指数损失函数:
AdaBoost 实际上最小化了指数损失函数:
通过调整样本权重,AdaBoost的迭代训练过程可以看作是一个贪心算法,每一步都在最小化当前的损失函数。
Python案例
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification
from sklearn.ensemble import AdaBoostClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.metrics import log_loss
# 生成虚拟数据集
X, y = make_classification(n_samples=1000, n_features=2, n_informative=2,
n_redundant=0, n_clusters_per_class=1, random_state=42)
# 将数据集划分为训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
# 定义弱分类器为决策树桩
base_estimator = DecisionTreeClassifier(max_depth=1)
# 定义 AdaBoost 模型,使用50个弱分类器
adaboost = AdaBoostClassifier(base_estimator=base_estimator, n_estimators=50, random_state=42)
# 训练模型
adaboost.fit(X_train, y_train)
# 预测结果
y_pred = adaboost.predict(X_test)
# 计算准确率
accuracy = accuracy_score(y_test, y_pred)
print(f"模型准确率: {accuracy:.2f}")
# 获取每轮迭代的错误率和决策函数输出
errors = []
log_losses = []
for i, y_pred_iter in enumerate(adaboost.staged_predict(X_test)):
errors.append(1 - accuracy_score(y_test, y_pred_iter))
proba = adaboost.staged_predict_proba(X_test)
log_losses.append(log_loss(y_test, next(proba)))
# 创建画布,包含4个子图
fig, axs = plt.subplots(2, 2, figsize=(12, 10))
# 绘制决策边界
def plot_decision_boundary(ax, model, X, y, title):
x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.02),
np.arange(y_min, y_max, 0.02))
Z = model.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
ax.contourf(xx, yy, Z, alpha=0.8, cmap=plt.cm.viridis)
ax.scatter(X[:, 0], X[:, 1], c=y, edgecolors='k', marker='o', cmap=plt.cm.cool)
ax.set_title(title)
# 子图1: 绘制决策边界
plot_decision_boundary(axs[0, 0], adaboost, X_test, y_test, "Decision Boundary")
# 子图2: 绘制损失函数曲线
axs[0, 1].plot(range(1, len(log_losses) + 1), log_losses, color='magenta', marker='o', linestyle='-', linewidth=2)
axs[0, 1].set_title("Log Loss Over Iterations")
axs[0, 1].set_xlabel("Iteration")
axs[0, 1].set_ylabel("Log Loss")
# 子图3: 绘制每个弱分类器的权重
axs[1, 0].bar(range(1, len(adaboost.estimator_weights_) + 1), adaboost.estimator_weights_, color='orange')
axs[1, 0].set_title("Weak Classifier Weights")
axs[1, 0].set_xlabel("Classifier Index")
axs[1, 0].set_ylabel("Weight")
# 子图4: 绘制弱分类器性能随迭代的变化
axs[1, 1].plot(range(1, len(errors) + 1), errors, color='cyan', marker='o', linestyle='-', linewidth=2)
axs[1, 1].set_title("Error Rate Over Iterations")
axs[1, 1].set_xlabel("Iteration")
axs[1, 1].set_ylabel("Error Rate")
# 调整布局
plt.tight_layout()
plt.show()
生成虚拟数据集:我们使用
make_classification
函数生成一个 2D 虚拟数据集,便于后续绘制决策边界。划分数据集:数据集被划分为训练集和测试集,训练集用于模型训练,测试集用于验证模型性能。
训练 AdaBoost 模型:我们选择
DecisionTreeClassifier
作为弱分类器,指定 50 个迭代轮次,使用AdaBoostClassifier
训练模型。评估模型性能:模型在测试集上的准确率,以及每轮迭代的错误率和损失函数值均被记录下来。
决策边界:直观展示模型对特征空间的分类结果,有助于理解模型是如何在不同区域进行分类的。
损失函数曲线:展示 AdaBoost 模型在训练过程中的收敛情况,帮助评估模型的稳定性和收敛速度。
弱分类器权重分布:通过柱状图展示每个弱分类器的权重分布,反映了不同弱分类器的重要性。
错误率曲线:展示每一轮弱分类器的错误率变化,帮助分析模型的优化过程。
5. Gradient Boosting
Gradient Boosting 是 Boosting 的另一种实现,它通过逐步构建多个基模型,每个模型试图修正上一个模型的残差。与 AdaBoost 不同,Gradient Boosting 通过最小化损失函数的梯度来优化模型,适用于回归和分类任务。
原理
在每一步,训练一个新的基模型来拟合前一模型的残差。
使用梯度下降的方法最小化损失函数。
将所有基模型的结果累加,形成最终的模型。
核心公式
残差的定义为:
其中 是实际值, 是当前模型的预测值。
在第 轮中,模型更新的过程为:
其中 是基模型, 是学习率。
Gradient Boosting 是基于残差拟合的思想,它通过迭代方式逐步减少模型的误差。
(1) 损失函数的梯度下降:
假设损失函数为 ,其中 是我们希望通过学习获得的预测模型。目标是最小化损失函数:
在每一步,我们用当前模型的预测值 来拟合训练数据,并计算残差(即模型与真实值之间的差异):
接下来,我们用新的基模型 来拟合这些残差。最终模型在第 轮的更新公式为:
其中 是学习率,它控制每次更新步长的大小。
(2) 回归问题中的平方损失函数:
对于回归问题,常用的损失函数是平方误差:
在这种情况下,残差为:
因此,每一轮的基模型 需要拟合的是上一轮的残差 ,然后将其加入到当前模型的预测值中。
(3) 梯度下降过程:
Gradient Boosting 的过程可以理解为逐步优化每个基模型,以最小化损失函数。通过计算损失函数的负梯度,找到下一步最合适的模型更新方向,并逐步逼近真实模型。
Python案例
import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.metrics import mean_squared_error, r2_score
from sklearn.datasets import make_regression
import seaborn as sns
# 生成虚拟数据集
X, y = make_regression(n_samples=1000, n_features=5, noise=0.3, random_state=42)
# 切分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 训练Gradient Boosting模型
model = GradientBoostingRegressor(n_estimators=200, learning_rate=0.1, max_depth=4, random_state=42)
model.fit(X_train, y_train)
# 模型预测
y_train_pred = model.predict(X_train)
y_test_pred = model.predict(X_test)
# 计算训练误差和测试误差
train_mse = mean_squared_error(y_train, y_train_pred)
test_mse = mean_squared_error(y_test, y_test_pred)
train_r2 = r2_score(y_train, y_train_pred)
test_r2 = r2_score(y_test, y_test_pred)
# 获取特征重要性
feature_importance = model.feature_importances_
# 创建图形并设置大小
fig, axs = plt.subplots(2, 2, figsize=(12, 10))
plt.subplots_adjust(hspace=0.4)
# 图1: 真实值 vs 预测值散点图 (训练集和测试集)
axs[0, 0].scatter(y_train, y_train_pred, color='blue', label='Train', alpha=0.6)
axs[0, 0].scatter(y_test, y_test_pred, color='red', label='Test', alpha=0.6)
axs[0, 0].plot([y.min(), y.max()], [y.min(), y.max()], color='green', lw=2)
axs[0, 0].set_title('True vs Predicted Values', fontsize=14)
axs[0, 0].set_xlabel('True Values')
axs[0, 0].set_ylabel('Predicted Values')
axs[0, 0].legend()
# 图2: 残差图 (预测值 vs 误差)
train_residuals = y_train - y_train_pred
test_residuals = y_test - y_test_pred
axs[0, 1].scatter(y_train_pred, train_residuals, color='blue', label='Train Residuals', alpha=0.6)
axs[0, 1].scatter(y_test_pred, test_residuals, color='red', label='Test Residuals', alpha=0.6)
axs[0, 1].hlines(0, y_train_pred.min(), y_train_pred.max(), color='green', lw=2)
axs[0, 1].set_title('Residuals Analysis', fontsize=14)
axs[0, 1].set_xlabel('Predicted Values')
axs[0, 1].set_ylabel('Residuals')
axs[0, 1].legend()
# 图3: 特征重要性图
axs[1, 0].bar(range(X.shape[1]), feature_importance, color=sns.color_palette('husl', X.shape[1]))
axs[1, 0].set_title('Feature Importances', fontsize=14)
axs[1, 0].set_xlabel('Feature Index')
axs[1, 0].set_ylabel('Importance')
# 图4: 学习曲线 (训练误差和测试误差随估计器个数变化)
train_errors = []
test_errors = []
n_estimators = range(1, 201)
for n in n_estimators:
model = GradientBoostingRegressor(n_estimators=n, learning_rate=0.1, max_depth=4, random_state=42)
model.fit(X_train, y_train)
y_train_pred = model.predict(X_train)
y_test_pred = model.predict(X_test)
train_errors.append(mean_squared_error(y_train, y_train_pred))
test_errors.append(mean_squared_error(y_test, y_test_pred))
axs[1, 1].plot(n_estimators, np.sqrt(train_errors), label='Train', color='blue')
axs[1, 1].plot(n_estimators, np.sqrt(test_errors), label='Test', color='red')
axs[1, 1].set_title('Learning Curve', fontsize=14)
axs[1, 1].set_xlabel('Number of Estimators')
axs[1, 1].set_ylabel('RMSE')
axs[1, 1].legend()
plt.show()
真实值 vs 预测值的散点图:通过观察预测值与真实值的分布,可以直观了解模型是否对数据进行了良好的拟合。如果点越接近对角线(y = x),则说明预测的效果越好。
残差图:残差图可以显示模型的预测误差。通过残差图,可以检测模型是否存在系统性的偏差(例如残差是否随着预测值增大而增大)。
特征重要性柱状图:特征重要性图能够展示模型中各个特征对预测结果的影响,帮助我们了解哪些特征在
Gradient Boosting
模型中起到了关键作用。学习曲线:学习曲线展示了随着迭代次数增加,模型在训练集和测试集上的表现,可以帮助我们识别模型是否过拟合或欠拟合。
6. XGBoost(Extreme Gradient Boosting)
XGBoost 是 Gradient Boosting 的优化版本,它引入了二阶导数信息、正则化、并行计算、剪枝等技术,极大地提升了模型性能。XGBoost 被广泛应用于各类机器学习竞赛中,因为它在速度、效率和精度上都表现出色。
原理
XGBoost 基于梯度提升树(GBT)的框架,但在以下几个方面进行了改进:
泰勒展开的二阶梯度优化:利用损失函数的一阶和二阶导数信息进行更新,而不是仅仅使用梯度。
正则化:通过在目标函数中加入正则化项,控制模型的复杂度,减少过拟合。
加速训练:支持并行化训练,使用近似算法进行快速分裂点查找等。
核心公式
XGBoost 的目标函数为:
其中,损失函数 是模型预测值与真实值的差异,而正则化项 控制树的复杂度:
这里, 是决策树的叶子节点数, 是叶子节点的权重, 和 是正则化参数。
1. 泰勒展开:在每轮训练中,对目标函数 进行二阶泰勒展开,利用梯度 和二阶梯度 来更新模型。展开后的目标函数为:
2. 树的优化:通过贪心算法,在每个节点选择最优分裂点,基于样本的梯度和二阶梯度信息进行分裂。叶子节点的权重由梯度和二阶梯度的累积值决定:
其中 是当前叶子节点的样本集合。
3. 正则化的作用:正则化项 控制树的复杂度,通过限制叶子节点数和叶子节点的权重,XGBoost 能有效避免过拟合。
Python案例
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from xgboost import XGBRegressor
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
from sklearn.datasets import make_regression
from xgboost import plot_importance
# 生成虚拟数据集
X, y = make_regression(n_samples=1000, n_features=10, noise=0.1, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 训练 XGBoost 模型
model = XGBRegressor(n_estimators=100, learning_rate=0.1, random_state=42)
model.fit(X_train, y_train)
# 预测结果
y_pred = model.predict(X_test)
# 计算残差
residuals = y_test - y_pred
# 创建图形
fig, axs = plt.subplots(2, 2, figsize=(14, 10))
# 1. 特征重要性图
axs[0, 0].set_title('Feature Importance', fontsize=14)
plot_importance(model, ax=axs[0, 0], importance_type='weight', grid=False, color='dodgerblue')
axs[0, 0].grid(True, linestyle='--', alpha=0.7)
# 2. 真实值 vs 预测值散点图
axs[0, 1].scatter(y_test, y_pred, color='orangered', edgecolor='k', s=50, alpha=0.7)
axs[0, 1].plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'k--', lw=2, color='purple')
axs[0, 1].set_xlabel('True Values', fontsize=12)
axs[0, 1].set_ylabel('Predicted Values', fontsize=12)
axs[0, 1].set_title('True vs Predicted Values', fontsize=14)
axs[0, 1].grid(True, linestyle='--', alpha=0.7)
# 3. 残差图
axs[1, 0].scatter(y_pred, residuals, color='limegreen', edgecolor='k', s=50, alpha=0.7)
axs[1, 0].axhline(0, color='black', linestyle='--', lw=2)
axs[1, 0].set_xlabel('Predicted Values', fontsize=12)
axs[1, 0].set_ylabel('Residuals', fontsize=12)
axs[1, 0].set_title('Residuals Plot', fontsize=14)
axs[1, 0].grid(True, linestyle='--', alpha=0.7)
# 美化与布局调整
plt.tight_layout()
plt.show()
特征重要性图:此图帮助理解模型在训练时,哪些特征对最终预测有最大的影响。这对于解释复杂模型非常重要,因为它揭示了特征的相对重要性。
真实值 vs 预测值散点图:这个图形展示了模型预测的精度。理想情况下,所有点应该分布在对角线上,表示真实值和预测值之间的一致性。通过这个图形,我们可以直观地看到模型是否存在偏差。
残差图:残差图帮助我们分析模型的误差分布。如果残差随机分布在0的两侧,说明模型的预测是合理的;如果存在明显的模式(如弯曲、漏斗形),可能暗示着模型未捕捉到某些非线性关系或存在异方差性问题。
7. LightGBM
LightGBM 是 Gradient Boosting 的另一个优化版本,特别适合处理大规模数据。与 XGBoost 类似,LightGBM 在训练过程中引入了一些高效的技术,如基于直方图的分裂、叶子生长策略等。
原理
叶子生长策略:LightGBM 采用 leaf-wise 的分裂策略,而不是层次分裂策略。这意味着每次选择误差最大的叶节点进行分裂,能够更快地减少训练误差。
直方图近似:LightGBM 使用直方图方法对特征值进行离散化,将连续特征值转化为离散的直方图 bin,极大地加速了分裂点查找的过程。
类别特征优化:对类别特征进行自动排序并优化,减少了数据预处理的开销。
核心公式
LightGBM 和 XGBoost 类似,其目标函数为:
其中,损失函数 和正则化项 的形式与 XGBoost 相似。
1. 直方图近似:LightGBM 对每个特征构建直方图,通过将连续特征值映射到离散 bin 中,快速计算每个分裂点的梯度和二阶梯度,从而加速分裂点的选择。
2. leaf-wise 策略的优势:与 XGBoost 的层级生长策略不同,LightGBM 在每轮迭代时选择当前误差最大的叶节点进行分裂。这样可以更快地减少整体损失,但也可能导致树的结构更加不平衡。因此,LightGBM 提供了最大深度的参数限制树的生长深度。
Python案例
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix, roc_curve, auc, precision_recall_curve
import lightgbm as lgb
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.datasets import make_classification
from sklearn.preprocessing import StandardScaler
# 虚拟数据集
X, y = make_classification(n_samples=2000, n_features=10, n_informative=6, n_classes=2, random_state=42)
# 标准化数据
scaler = StandardScaler()
X = scaler.fit_transform(X)
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
# 创建 LightGBM 数据集
train_data = lgb.Dataset(X_train, label=y_train)
test_data = lgb.Dataset(X_test, label=y_test, reference=train_data)
# 设置参数
params = {
'boosting_type': 'gbdt',
'objective': 'binary',
'metric': 'binary_logloss',
'learning_rate': 0.05,
'num_leaves': 31,
'max_depth': -1,
'min_data_in_leaf': 20,
'feature_fraction': 0.9
}
# 训练模型
lgb_model = lgb.train(params, train_data, valid_sets=[test_data], num_boost_round=200, early_stopping_rounds=20, verbose_eval=False)
# 模型预测
y_pred_prob = lgb_model.predict(X_test)
y_pred = np.where(y_pred_prob > 0.5, 1, 0)
# 计算混淆矩阵
conf_matrix = confusion_matrix(y_test, y_pred)
# 计算 ROC 曲线
fpr, tpr, _ = roc_curve(y_test, y_pred_prob)
roc_auc = auc(fpr, tpr)
# 计算 Precision-Recall 曲线
precision, recall, _ = precision_recall_curve(y_test, y_pred_prob)
# 特征重要性
feature_importance = lgb_model.feature_importance()
feature_names = ['Feature' + str(i) for i in range(X.shape[1])]
# 画图
plt.figure(figsize=(14, 10))
# 子图1:混淆矩阵
plt.subplot(2, 2, 1)
sns.heatmap(conf_matrix, annot=True, fmt='d', cmap='Blues', cbar=False, annot_kws={"size": 16})
plt.title('Confusion Matrix', fontsize=16)
plt.xlabel('Predicted', fontsize=12)
plt.ylabel('Actual', fontsize=12)
# 子图2:特征重要性
plt.subplot(2, 2, 2)
sns.barplot(x=feature_importance, y=feature_names, palette='Spectral')
plt.title('Feature Importance', fontsize=16)
plt.xlabel('Importance', fontsize=12)
plt.ylabel('Feature', fontsize=12)
# 子图3:ROC 曲线
plt.subplot(2, 2, 3)
plt.plot(fpr, tpr, color='darkorange', lw=2, label='ROC curve (area = %0.2f)' % roc_auc)
plt.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--')
plt.fill_between(fpr, tpr, color='orange', alpha=0.3)
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate', fontsize=12)
plt.ylabel('True Positive Rate', fontsize=12)
plt.title('Receiver Operating Characteristic', fontsize=16)
plt.legend(loc="lower right", fontsize=12)
# 子图4:Precision-Recall 曲线
plt.subplot(2, 2, 4)
plt.plot(recall, precision, color='blue', lw=2)
plt.fill_between(recall, precision, color='blue', alpha=0.3)
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('Recall', fontsize=12)
plt.ylabel('Precision', fontsize=12)
plt.title('Precision-Recall Curve', fontsize=16)
# 调整布局
plt.tight_layout()
plt.show()
数据生成和处理:我们使用 make_classification
生成一个包含2000个样本、10个特征的虚拟二分类数据集。然后使用StandardScaler
进行标准化处理。LightGBM 模型:使用 lgb.train()
来训练 LightGBM 模型。params
字典包含了模型的超参数设置。
混淆矩阵:用
seaborn
绘制的热力图(heatmap),显示分类结果。特征重要性:用条形图显示每个特征对模型的贡献,特征名称为
Feature0
到Feature9
。ROC 曲线:绘制模型的 ROC 曲线,显示不同阈值下的性能,计算 AUC。
Precision-Recall 曲线:展示 Precision-Recall 关系,用于分析模型在不同召回率下的精度。
8. GBDT
GBDT (Gradient Boosting Decision Tree),即梯度提升决策树,是一种集成学习算法,由多棵决策树组成,通过逐步地将弱学习器提升为强学习器。GBDT 是一种基于决策树的梯度提升框架,广泛应用于回归、分类等任务。
GBDT 核心思想是:从一个简单的模型(即一棵弱学习器,如深度为 1 的决策树)开始,计算其残差(即预测值与真实值之间的差异),然后构建新的决策树来拟合这个残差。不断叠加这样的决策树,逐步改进模型的预测性能,直到模型收敛或者达到预设的树数。
原理
GBDT 的工作原理可以从三个核心概念来理解:
决策树作为基础学习器:GBDT 中使用了CART 决策树(Classification And Regression Tree),这是一种递归地将数据分割成多个区域的树形模型。
Boosting 机制:Boosting 是一种集成学习方法,通过将多个弱学习器(如浅层决策树)组合成一个强学习器。每一轮迭代中,新的模型拟合的是前一轮的残差(真实值与预测值之间的差距),从而逐步减少整体的预测误差。
梯度下降优化:GBDT 使用梯度下降法在函数空间中进行优化。每一轮迭代中,新的树拟合的是损失函数对当前模型预测值的负梯度方向,这样可以快速减少损失。
核心公式
假设我们有一个训练集 ,目标是学习一个模型 来最小化损失函数 。
1. 初始模型
GBDT 的第一步是选择一个简单的初始模型,通常是一个常数模型(对于回归任务来说就是样本均值):
其中 是损失函数,比如平方误差 或交叉熵损失。
2. 逐步拟合残差
GBDT 的第二步是通过构建新的决策树来拟合前一轮模型的残差。假设我们已经有了模型 (第 m 轮的模型),那么第 m+1 轮的模型更新如下:
其中, 是第 m 轮的新决策树, 是学习率(0 < ≤ 1),用于控制每次更新的幅度。学习率较小时,虽然收敛更慢,但模型的泛化能力更好。
3. 拟合负梯度
在第 m 轮的模型中,新模型拟合的是损失函数对模型预测值的负梯度,即:
这里 是第 m 轮的负梯度,对应的就是每个样本的残差。在这一步中,新的树 将会拟合这些负梯度值。
4. 更新模型
模型的最终更新公式为:
也就是我们在上一轮模型 基础上,通过学习率 来控制新树 对模型的贡献。
5. 最终预测
在训练结束后,模型的预测值 就是通过叠加所有树的输出:
其中 是总共的树的数量。
GBDT 通过多轮迭代,使用基于决策树的弱学习器来拟合残差。其核心原理是梯度下降法,通过构建多个弱学习器来逐步减少损失。
Python案例
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix, roc_curve, auc, accuracy_score
from sklearn.preprocessing import label_binarize
from sklearn.multiclass import OneVsRestClassifier
from sklearn.metrics import roc_auc_score
# 生成虚拟分类数据集
X, y = make_classification(n_samples=1000, n_features=20, n_informative=10, n_redundant=5, n_classes=2, random_state=42)
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
# 创建并训练GBDT模型
gbdt = GradientBoostingClassifier(n_estimators=100, learning_rate=0.1, max_depth=4, random_state=42)
gbdt.fit(X_train, y_train)
# 预测
y_pred = gbdt.predict(X_test)
y_pred_prob = gbdt.predict_proba(X_test)[:, 1]
# 混淆矩阵
conf_matrix = confusion_matrix(y_test, y_pred)
# ROC曲线
fpr, tpr, thresholds = roc_curve(y_test, y_pred_prob)
roc_auc = auc(fpr, tpr)
# 图形绘制
plt.figure(figsize=(14, 8))
# 第一个子图:混淆矩阵
plt.subplot(1, 2, 1)
sns.set(font_scale=1.4)
sns.heatmap(conf_matrix, annot=True, fmt="d", cmap="Blues", cbar=False, annot_kws={"size": 16})
plt.title("Confusion Matrix", fontsize=18)
plt.xlabel("Predicted label", fontsize=14)
plt.ylabel("True label", fontsize=14)
# 第二个子图:ROC 曲线
plt.subplot(1, 2, 2)
plt.plot(fpr, tpr, color='darkorange', lw=2, label=f'ROC curve (area = {roc_auc:.2f})')
plt.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--')
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate', fontsize=14)
plt.ylabel('True Positive Rate', fontsize=14)
plt.title('ROC Curve', fontsize=18)
plt.legend(loc="lower right", fontsize=12)
# 调整布局并显示图形
plt.tight_layout()
plt.show()
# 计算并输出模型性能指标
accuracy = accuracy_score(y_test, y_pred)
print(f"Accuracy: {accuracy:.2f}")
print(f"ROC AUC: {roc_auc:.2f}")
混淆矩阵 (Confusion Matrix): 通过混淆矩阵,可以清晰展示模型对每个类别的分类准确度。它帮助我们观察模型的错误分类情况,尤其是模型在识别不同类别时的性能差异。图中每个单元格的数字表示模型对真实标签和预测标签的计数。对角线上的值表示正确分类数,而非对角线的值表示误分类的数量。
ROC 曲线 (Receiver Operating Characteristic Curve): 展示模型在不同阈值下的分类性能,反映出真阳率(True Positive Rate, TPR)和假阳率(False Positive Rate, FPR)之间的权衡。通过观察 ROC 曲线的形状和曲线下面积 (AUC, Area Under Curve),我们可以判断模型的分类性能。ROC 曲线越接近左上角,模型性能越好。AUC 值在 0.5 至 1 之间,接近 1 的值表示模型表现更好。
9. Stacking
Stacking 是一种将多个不同模型组合起来的集成方法,区别于 Bagging 和 Boosting,Stacking 通过训练一个元学习器(通常是简单的线性模型)来组合多个基模型的输出,从而生成最终的预测结果。
原理
第一层基模型:训练多个不同的基模型(如决策树、随机森林、SVM 等),每个基模型独立预测。
第二层元模型:将第一层基模型的输出作为新的输入,训练一个元模型来整合这些输出并给出最终的预测结果。
核心公式
假设我们有 个基模型,输入特征为 ,基模型的预测为 ,则 Stacking 的最终预测为元模型 的输出:
元模型 可以是简单的线性回归、逻辑回归或更复杂的模型。
Stacking 的关键在于如何训练元模型 。为了避免过拟合,通常采用交叉验证来生成基模型的预测值作为训练元模型的数据。在每次交叉验证中,基模型在训练集上进行拟合,并在验证集上生成预测值,最终通过这些预测值来训练元模型。
Python案例
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split, learning_curve
from sklearn.datasets import make_regression
from sklearn.ensemble import StackingRegressor, RandomForestRegressor
from sklearn.linear_model import LinearRegression, Ridge
from sklearn.neighbors import KNeighborsRegressor
from sklearn.metrics import mean_squared_error
import seaborn as sns
# Step 1: 生成虚拟数据集
X, y = make_regression(n_samples=1000, n_features=5, noise=0.3, random_state=42)
# Step 2: 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
# Step 3: 定义Stacking模型
base_learners = [
('lr', Ridge()),
('rf', RandomForestRegressor(n_estimators=50, random_state=42)),
('knn', KNeighborsRegressor(n_neighbors=5))
]
stack_model = StackingRegressor(estimators=base_learners, final_estimator=LinearRegression())
# 训练Stacking模型
stack_model.fit(X_train, y_train)
# 基础模型和Stacking模型的预测结果
models = {
'Ridge': Ridge(),
'RandomForest': RandomForestRegressor(n_estimators=50, random_state=42),
'KNN': KNeighborsRegressor(n_neighbors=5),
'Stacking': stack_model
}
# Step 4: 学习曲线绘制
plt.figure(figsize=(14, 10))
for i, (name, model) in enumerate(models.items(), 1):
plt.subplot(2, 2, i)
# 计算学习曲线
train_sizes, train_scores, test_scores = learning_curve(model, X_train, y_train, cv=5,
scoring='neg_mean_squared_error',
train_sizes=np.linspace(0.1, 1.0, 10))
train_scores_mean = -np.mean(train_scores, axis=1)
test_scores_mean = -np.mean(test_scores, axis=1)
plt.plot(train_sizes, train_scores_mean, 'o-', color="r", label="Training error")
plt.plot(train_sizes, test_scores_mean, 'o-', color="g", label="Cross-validation error")
plt.title(f'{name} Learning Curve')
plt.xlabel("Training size")
plt.ylabel("MSE")
plt.legend(loc="best")
# Step 5: 误差分布绘制
y_pred_test = {name: model.fit(X_train, y_train).predict(X_test) for name, model in models.items()}
plt.figure(figsize=(12, 8))
plt.subplot(1, 2, 1)
for name, y_pred in y_pred_test.items():
sns.histplot(y_test - y_pred, kde=True, label=name, element="step")
plt.title('Error Distribution')
plt.xlabel('Prediction Error')
plt.legend()
# Step 6: 随机森林特征重要性绘制
plt.subplot(1, 2, 2)
rf = RandomForestRegressor(n_estimators=50, random_state=42)
rf.fit(X_train, y_train)
importances = rf.feature_importances_
indices = np.argsort(importances)[::-1]
plt.barh(range(X.shape[1]), importances[indices], align="center", color="c")
plt.yticks(range(X.shape[1]), [f'Feature {i}' for i in indices])
plt.title("Feature Importance (RandomForest)")
plt.xlabel("Importance")
plt.tight_layout()
plt.show()
生成虚拟数据集:使用
make_regression
创建包含 1000 个样本和 5 个特征的回归数据集,加入 0.3 的噪声。Stacking模型:使用 Ridge、RandomForest、KNN 作为基础模型,最终使用线性回归作为元学习器进行 Stacking。
绘制学习曲线:使用
learning_curve
来评估不同模型的性能。学习曲线显示随着训练集大小的增加,模型的训练误差和交叉验证误差的变化情况,帮助我们理解模型的表现。误差分布图:展示每个模型在测试集上的预测误差分布,通过直方图与核密度估计可以对比不同模型的偏差和方差。
特征重要性图:通过随机森林的特征重要性分析,了解哪些特征对最终预测最为重要。
学习曲线 可以帮助我们看到模型的偏差和方差。随着训练集大小的增加,如果训练误差和验证误差逐渐接近,说明模型表现良好。若训练误差较低但验证误差很高,模型可能过拟合。 预测误差分布 显示模型预测值与真实值之间的差距。误差越接近零,模型的预测越准确。 特征重要性图 有助于解释模型如何利用不同特征进行预测,特别是对于解释性较强的模型如随机森林。
10. Voting
Voting 是一种简单的集成方法,通过对多个基模型的预测结果进行投票或平均,来获得最终的预测。Voting 通常用于分类任务。
原理
硬投票:每个基模型独立预测类别,最终的预测类别是基模型投票结果中得票最多的类别。
软投票:每个基模型预测类别概率,最终的预测类别是所有基模型预测概率的加权平均结果。
核心公式
硬投票:
其中 表示第 个模型的预测是否为类别 。
软投票:
其中 表示第 个模型预测为类别 的概率。
Voting 的效果取决于基模型的性能和多样性。如果多个基模型的错误是互补的,Voting 可以通过综合多个模型的预测结果来提升整体准确性。特别是软投票方法,由于它考虑了模型预测的置信度,因此通常能够提供更好的结果。
Python案例
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.ensemble import VotingClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.neighbors import KNeighborsClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import roc_curve, auc
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
# 生成虚拟数据集
X, y = make_classification(n_samples=1000, n_features=10, n_classes=2, n_informative=5, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
# 标准化数据
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
# 定义基础分类器
clf1 = LogisticRegression(random_state=42)
clf2 = KNeighborsClassifier(n_neighbors=5)
clf3 = DecisionTreeClassifier(random_state=42)
# 定义 Voting 分类器 (硬投票)
voting_clf = VotingClassifier(estimators=[('lr', clf1), ('knn', clf2), ('dt', clf3)], voting='hard')
voting_clf.fit(X_train_scaled, y_train)
# 模型预测概率 (用于 ROC 曲线)
y_pred_proba_lr = clf1.fit(X_train_scaled, y_train).predict_proba(X_test_scaled)[:, 1]
y_pred_proba_knn = clf2.fit(X_train_scaled, y_train).predict_proba(X_test_scaled)[:, 1]
y_pred_proba_dt = clf3.fit(X_train_scaled, y_train).predict_proba(X_test_scaled)[:, 1]
# 计算 ROC 曲线和 AUC
fpr_lr, tpr_lr, _ = roc_curve(y_test, y_pred_proba_lr)
fpr_knn, tpr_knn, _ = roc_curve(y_test, y_pred_proba_knn)
fpr_dt, tpr_dt, _ = roc_curve(y_test, y_pred_proba_dt)
# 绘制 ROC 曲线
plt.figure(figsize=(15, 10))
# ROC 曲线子图
plt.subplot(2, 2, 1)
plt.plot(fpr_lr, tpr_lr, color='blue', label='Logistic Regression (AUC = {:.2f})'.format(auc(fpr_lr, tpr_lr)))
plt.plot(fpr_knn, tpr_knn, color='green', label='KNN (AUC = {:.2f})'.format(auc(fpr_knn, tpr_knn)))
plt.plot(fpr_dt, tpr_dt, color='red', label='Decision Tree (AUC = {:.2f})'.format(auc(fpr_dt, tpr_dt)))
plt.plot([0, 1], [0, 1], color='grey', linestyle='--')
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('ROC Curve Comparison')
plt.legend(loc='lower right')
# 使用 PCA 降维到 2D,便于绘制决策边界
pca = PCA(n_components=2)
X_train_pca = pca.fit_transform(X_train_scaled)
X_test_pca = pca.transform(X_test_scaled)
# 决策边界子图
plt.subplot(2, 2, 2)
h = .02 # 网格步长
x_min, x_max = X_train_pca[:, 0].min() - 1, X_train_pca[:, 0].max() + 1
y_min, y_max = X_train_pca[:, 1].min() - 1, X_train_pca[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))
# 在降维后的 2D 空间训练模型
clf1.fit(X_train_pca, y_train)
clf2.fit(X_train_pca, y_train)
clf3.fit(X_train_pca, y_train)
voting_clf.fit(X_train_pca, y_train)
Z1 = clf1.predict(np.c_[xx.ravel(), yy.ravel()])
Z2 = clf2.predict(np.c_[xx.ravel(), yy.ravel()])
Z3 = clf3.predict(np.c_[xx.ravel(), yy.ravel()])
Zv = voting_clf.predict(np.c_[xx.ravel(), yy.ravel()])
Z1 = Z1.reshape(xx.shape)
Z2 = Z2.reshape(xx.shape)
Z3 = Z3.reshape(xx.shape)
Zv = Zv.reshape(xx.shape)
plt.contourf(xx, yy, Z1, alpha=0.4, cmap='Blues')
plt.scatter(X_test_pca[:, 0], X_test_pca[:, 1], c=y_test, edgecolor='k', marker='o', cmap='coolwarm')
plt.title('Logistic Regression Decision Boundary')
# 特征重要性子图 (适用于决策树)
plt.subplot(2, 2, 3)
importances = clf3.fit(X_train_scaled, y_train).feature_importances_ # 在这里训练决策树并获取特征重要性
indices = np.argsort(importances)[::-1]
plt.bar(range(X_train.shape[1]), importances[indices], color='r', align='center')
plt.xticks(range(X_train.shape[1]), indices)
plt.title('Feature Importances (Decision Tree)')
plt.xlabel('Feature Index')
plt.ylabel('Importance')
# 决策树的决策边界
plt.subplot(2, 2, 4)
plt.contourf(xx, yy, Z3, alpha=0.4, cmap='Reds')
plt.scatter(X_test_pca[:, 0], X_test_pca[:, 1], c=y_test, edgecolor='k', marker='o', cmap='coolwarm')
plt.title('Decision Tree Decision Boundary')
plt.tight_layout()
plt.show()
虚拟数据集:使用
make_classification
生成一个包含10个特征的二分类数据集,其中5个特征是信息性的。Voting Classifier:集成逻辑回归(
LogisticRegression
)、K近邻(KNN
)和决策树(DecisionTree
)作为基础分类器。
第一个子图是不同模型的 ROC 曲线,用于比较它们的分类性能。 第二个子图展示了逻辑回归在降维后的数据上的决策边界。 第三个子图是决策树的特征重要性,用于揭示每个特征在决策树中的重要性。 第四个子图展示了决策树在降维后的数据上的决策边界。
最后