1. 集成学习:三个臭皮匠,如何赛过诸葛亮?
我们之前学习的线性回归、决策树等算法,就像是团队里的某一位“专家”。这位专家可能在某个领域很擅长,但单凭他一人,要解决复杂多变的问题,总会遇到瓶颈。就好比,一位再厉害的“诸葛亮”,也难免有失算的时候。
那怎么办呢?古人云:“三个臭皮匠,赛过诸葛亮”。**集成学习(Ensemble Learning)**的核心思想正是如此:它不依赖于某一个“超级天才”(强学习器),而是将一群“普通人”(弱学习器)的智慧汇集起来,形成一个决策能力超强的“专家团”(强学习器)。
一个弱学习器,通常指那些性能仅比随机猜测好一点点的模型。而一个成功的“专家团”需要满足两个关键条件:
- 个体优秀 (Good):团队里的每个成员(弱学习器)都得有两把刷子,至少得具备一定的判断能力(比如,分类准确率要大于50%)。
- 彼此差异 (Different):如果团队成员的想法和知识背景一模一样,那和一个人决策没区别。成员之间必须有差异性、能互补短长,这样集成的效果才会最好。
为了组建这样的“专家团”,我们主要有两种策略,可以从两个维度来理解:
维度一:团队成员的构成方式 (同质 vs. 异质)
- 异质集成 (Heterogeneous Ensemble):这就像组建一个“跨学科团队”。团队里有搞数学的(线性回归)、搞分类的(决策树)、搞空间分析的(K近邻)等等。我们让每个不同类型的模型都对问题进行预测,然后给那些历史表现更好的“专家”更高的发言权(权重),最后综合所有人的加权意见,得出最终结论。
- 同质集成 (Homogeneous Ensemble):这就像组建一个“专科攻坚小组”,所有成员都是同一类型,比如全是决策树专家。为了避免大家想法一致,我们会给每个专家分发略有不同的“资料包”(搅动数据)。通过这种方式,即使模型类型相同,他们学到的侧重点也各不相同,从而保证了差异性。
维度二:团队的工作模式 (并行 vs. 串行)
并行集成 (Parallel Ensemble):大家“同时开工,独立思考”。就像一场开卷考试,我们给每个学生(弱学习器)发一份略有不同的复习资料(通过自助采样得到的数据子集),让他们独立完成整套试卷。最后,我们统计所有学生的答案,通过“民主投票”来决定最终答案。这种方式互不干扰,非常适合大规模并行计算。
- 代表算法:装袋法 (Bagging) 和 随机森林 (Random Forest)。
串行集成 (Serial Ensemble):大家“接力工作,查漏补缺”。第一位同学(弱学习器)先做一遍题,然后把做错的题目标记出来。第二位同学拿到后,就重点攻克上一位同学搞错的难题。以此类推,每一位新成员都致力于解决前面所有成员留下的“历史遗留问题”。这样层层递进,模型的能力越来越强。
- 代表算法:提升法 (Boosting),如 AdaBoost, GBDT, XGBoost 等。
分类维度 | 类型 | 核心思想 | 工作模式 | 代表算法 | 比喻 |
---|---|---|---|---|---|
按构成 | 异质集成 | 不同类型的模型组合 | 各自预测,加权表决 | Stacking | 跨学科专家会诊 |
同质集成 | 同一类型的模型组合 | 通过数据扰动创造差异 | Bagging, Boosting | 决策树专家小组 | |
按生成 | 并行集成 | 独立、互不依赖 | 同时训练,投票/平均 | Bagging, 随机森林 | 学生独立完成考试 |
串行集成 | 依赖、层层递进 | 迭代训练,弥补前序错误 | Boosting (AdaBoost, GBDT) | 师徒接力解决难题 |
2. 随机森林分类任务:民主投票的决策专家团
随机森林是同质并行集成的杰出代表。顾名思义,“森林”由大量的“树”(决策树)组成。下面我们来看看这个“专家团”是如何工作的。
工作流程:
建造森林:有控制的“随机”
- 样本随机 (Bootstrap Sampling):假设我们有1000个原始样本。为了训练第一棵树,我们从这1000个样本中有放回地随机抽取1000次,形成一个训练集。因为是有放回的,所以这个新的训练集中,有些样本可能出现多次,有些则一次都未出现。这个过程就像是为每个专家准备一份独特的“复习资料”。
- 特征随机 (Feature Sampling):在决策树的每个节点进行分裂时,我们不是从全部 P 个特征中寻找最优分裂点,而是随机抽取 m 个特征(m < P),再从这 m 个中选出最好的。这确保了每棵树都不会过分依赖某几个强特征,增加了树之间的“视角”差异。
- 经验法则:对于分类任务,通常取
m ≈ √p
;对于回归任务,取m ≈ p/3
。 - 与装袋法(Bagging)的关系:如果设置
m = p
,即每次都考虑所有特征,那随机森林就退化成了装袋法。所以说,随机森林是装袋法的一种“进阶版”,它通过增加特征随机性来进一步提升模型的多样性。
- 经验法则:对于分类任务,通常取
独立训练:并行工作的专家
每棵决策树都使用自己独特的样本集和特征子集进行独立训练,互不干扰。集中决策:少数服从多数
当一个新的样本需要预测时,森林里的每一棵树都独立地给出一个自己的分类判断。最后,随机森林采用“多数投票 (Majority Voting)”原则,得票最多的那个类别就是最终的预测结果。
优点与缺点:
- 优点:
- 高准确率与抗过拟合:通过平均/投票多棵树的结果,个别树的错误会被其他树纠正,使得整体模型非常稳健,不易过拟含。
- 处理高维数据:即使有数千个特征,随机森林也能表现良好,并且能帮我们评估哪些特征更重要。
- 易于并行化:各棵树的训练可以分散到多个CPU核心或机器上,效率很高。
- 缺点:
- 可解释性较差:相对于单棵决策树,由成百上千棵树组成的“黑箱”模型,我们很难直观地理解其内部的决策逻辑。(这催生了下面要讲的解释性工具!)
- 计算成本:对于大数据集,训练大量的树会消耗较多的时间和内存。
3. 谁是MVP?解密随机森林的特征重要性
随机森林是一个“黑箱”,但我们依然有办法窥探其内部,比如,搞清楚“在所有决策中,哪个特征的贡献最大?” 这就像评选一支球队的“最有价值球员 (MVP)”。
核心思想:一个特征越重要,意味着它在分裂决策树节点时,起到的“让数据变纯粹”的作用越大。
计算方法:
- 单棵树的贡献:在一棵决策树中,每当一个特征被用来分裂节点时,我们会计算这次分裂带来的“不纯度下降量”(例如,基尼指数下降或信息增益提升)。把这个特征在这棵树里所有分裂点的贡献加起来,就是它在这棵树里的重要性得分。
- 整个森林的总评:将该特征在森林中所有树上的重要性得分进行平均,就得到了它在整个随机森林中的最终重要性排名。
一句话总结:一个特征的重要性,就是它在所有树中、所有分裂决策里,平均贡献度的总和。贡献越大,排名越靠前,它就是我们模型眼中的“MVP”。
4. 不止看“谁”重要,更要看“如何”重要:模型的可解释性利器
特征重要性告诉我们“哪些”特征是MVP,但没有告诉我们这个MVP“如何”影响比赛结果的(例如,是得分能力强还是防守能力强?)。部分依赖图 (PDP) 和 个体条件期望图 (ICE) 就是为了回答“如何影响”这个问题的强大工具。
1. 部分依赖图 (Partial Dependence Plot, PDP):看清“平均趋势”
- 解决的问题:它展示了当其他所有特征保持不变时,某一个特征值的变化,对模型平均预测结果的影响。
- 比喻:就像研究“咖啡因摄入量”对“所有学生平均专注度”的影响。我们想知道,是咖啡因越多越好,还是适量最好?
- 工作原理:
- 选择一个你感兴趣的特征,比如“年龄”。
- 固定住数据集中其他所有特征,然后强制让所有样本的“年龄”都等于20岁,计算出所有样本的平均预测概率。
- 接着,强制让所有样本的“年龄”都等于21岁,再算一个平均预测概率。
- …以此类推,将不同年龄值对应的平均预测结果连接起来,就形成了一条曲线。
- 解读:这条曲线展示了“年龄”这个特征对模型预测结果的边际效应。我们可以直观地看到,随着年龄的增长,预测结果是线性上升、下降,还是呈现更复杂的关系。
2. 个体条件期望图 (Individual Conditional Expectation, ICE Plot):洞察“个体故事”
- 解决的问题:PDP展示的是平均效果,但这种“平均”可能会掩盖个体之间的巨大差异。ICE图则为每一个样本都画出一条线,展示特定特征的变化对该样本预测结果的影响。
- 比喻:PDP是看咖啡因对“所有学生”的平均影响,而ICE图是看咖啡因对“张三”、“李四”每个人专注度的具体影响。可能张三喝了咖啡生龙活虎,李四喝了却心慌意乱。
- 工作原理:它和PDP的原理几乎一样,唯一的区别是——它不对所有样本的预测结果求平均。它为每个样本单独画出一条“特征-预测”关系曲线。
- 解读:
- ICE图是PDP的“拆解版”。所有ICE曲线的平均线,就是PDP曲线。
- 通过观察ICE图,我们可以发现是否存在一些行为特异的子群体。如果所有ICE曲线都大致平行,说明该特征对所有样本的影响是同质的。如果曲线杂乱无章,说明存在复杂的交互效应。
一句话总结:
- 特征重要性:告诉你哪个特征是MVP。
- PDP:告诉你这位MVP对**整个团队(所有样本)**的平均影响是怎样的。
- ICE:告诉你这位MVP对**每一位队员(单个样本)**的具体影响是怎样的。
数据13.1中的数据为例进行讲解。针对“数据13.1”,我们以credit(是否发生违约)为响应变量,以age(年龄)、education(受教育程度)、workyears(工作年限)、resideyears(居住年限)、income(年收入水平)、debtratio(债务收入比)、creditdebt(信用卡负债)、otherdebt(其他负债)为特征变量,使用分类随机森林算法进行。
5.随机森林算法分类问题案例解析
1 变量设置及数据处理
from sklearn.ensemble import RandomForestClassifier
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import confusion_matrix
from sklearn.metrics import classification_report
from sklearn.metrics import cohen_kappa_score
from sklearn.inspection import PartialDependenceDisplay
from mlxtend.plotting import plot_decision_regions
#分类问题随机森林算法示例
#1 变量设置及数据处理
data=pd.read_csv('数据13.1.csv')
X = data.iloc[:,1:]#设置特征变量
y = data.iloc[:,0]#设置响应变量
X_train, X_test, y_train, y_test = train_test_split(X,y,test_size=0.3, stratify=y, random_state=10)
2 二元Logistic回归、单颗分类决策树算法对比观察
#2 二元Logistic回归、单颗分类决策树算法观察
model = LogisticRegression(C=1e10, max_iter=1000,fit_intercept=True)
model.fit(X_train, y_train)
model.score(X_test, y_test)
#单颗分类决策树算法
model = DecisionTreeClassifier()
path = model.cost_complexity_pruning_path(X_train, y_train)
param_grid = {'ccp_alpha': path.ccp_alphas}
kfold = StratifiedKFold(n_splits=10, shuffle=True, random_state=10)
model = GridSearchCV(DecisionTreeClassifier(random_state=10), param_grid, cv=kfold)
model.fit(X_train, y_train)
print("最优alpha值:", model.best_params_)
model = model.best_estimator_
print("最优预测准确率:", model.score(X_test, y_test))
最优alpha值:{'ccp_alpha': 0.004534462760155709}
最优预测准确率:0.861904761904762
3 装袋法分类算法
# 3 装袋法分类算法
model=BaggingClassifier(estimator=DecisionTreeClassifier(random_state=10),n_estimators=300,max_samples=0.8,random_state=0)
model.fit(X_train, y_train)
model.score(X_test, y_test)
0.8666666666666667
4 随机森林分类算法
# 4 随机森林分类算法
model = RandomForestClassifier(n_estimators=300, max_features='sqrt', random_state=10)
model.fit(X_train, y_train)
model.score(X_test, y_test)
5 寻求max_features最优参数
#5 寻求max_features最优参数
scores = []
for max_features in range(1, X.shape[1] + 1):
model = RandomForestClassifier(max_features=max_features,
n_estimators=300, random_state=10)
model.fit(X_train, y_train)
score = model.score(X_test, y_test)
scores.append(score)
index = np.argmax(scores)
range(1, X.shape[1] + 1)[index]
plt.rcParams['font.sans-serif']=['SimHei'] #正常显示中文
plt.plot(range(1, X.shape[1] + 1), scores, 'o-')
plt.axvline(range(1, X.shape[1] + 1)[index], linestyle='--', color='k', linewidth=1)
plt.xlabel('最大特征变量数')
plt.ylabel('最优预测准确率')
plt.title('预测准确率随选取的最大特征变量数变化情况')
plt.show()
plt.savefig('预测准确率随选取的最大特征变量数变化情况.png')
print(scores)
6 寻求n_estimators最优参数
#6 寻求n_estimators最优参数
ScoreAll = []
for i in range(100,300,10):
model= RandomForestClassifier(max_features=2,n_estimators = i,random_state = 10)
model.fit(X_train, y_train)
score = model.score(X_test, y_test)
ScoreAll.append([i,score])
ScoreAll = np.array(ScoreAll)
print(ScoreAll)
max_score = np.where(ScoreAll==np.max(ScoreAll[:,1]))[0][0] #找出最高得分对应的索引
print("最优参数以及最高得分:",ScoreAll[max_score])
plt.rcParams['font.sans-serif']=['SimHei'] #正常显示中文
plt.figure(figsize=[20,5])
plt.xlabel('n_estimators')
plt.ylabel('预测准确率')
plt.title('预测准确率随n_estimators变化情况')
plt.plot(ScoreAll[:,0],ScoreAll[:,1])
plt.show()
plt.savefig('预测准确率随n_estimators变化情况.png')
进一步寻求n_estimators最优参数
#进一步寻求n_estimators最优参数
ScoreAll = []
for i in range(190,210):
model= RandomForestClassifier(max_features=2,n_estimators = i,random_state = 10)
model.fit(X_train, y_train)
score = model.score(X_test, y_test)
ScoreAll.append([i,score])
ScoreAll = np.array(ScoreAll)
print(ScoreAll)
max_score = np.where(ScoreAll==np.max(ScoreAll[:,1]))[0][0] #找出最高得分对应的索引
print("最优参数以及最高得分:",ScoreAll[max_score])
plt.figure(figsize=[20,5])
plt.xlabel('n_estimators')
plt.ylabel('预测准确率')
plt.title('预测准确率随n_estimators变化情况')
plt.plot(ScoreAll[:,0],ScoreAll[:,1])
plt.show()
plt.savefig('预测准确率随n_estimators变化情况.png')
7 随机森林特征变量重要性水平分析
# 7 随机森林特征变量重要性水平分析
sorted_index = model.feature_importances_.argsort()
plt.rcParams['font.sans-serif'] = ['SimHei']#解决图表中中文显示问题
plt.barh(range(X_train.shape[1]), model.feature_importances_[sorted_index])
plt.yticks(np.arange(X_train.shape[1]), X_train.columns[sorted_index])
plt.xlabel('特征变量重要性水平')
plt.ylabel('特征变量')
plt.title('随机森林特征变量重要性水平分析')
plt.tight_layout()
plt.show()
plt.savefig('随机森林特征变量重要性水平分析.png')
8 绘制部分依赖图与个体条件期望图
#8 绘制部分依赖图与个体条件期望图
PartialDependenceDisplay.from_estimator(model, X_train, ['workyears','debtratio'], kind='average')#绘制部分依赖图简称PDP图
PartialDependenceDisplay.from_estimator(model, X_train, ['workyears','debtratio'],kind='individual')#绘制个体条件期望图(ICE Plot)
PartialDependenceDisplay.from_estimator(model, X_train, ['workyears','debtratio'],kind='both')#绘制个体条件期望图(ICE Plot)
plt.show()
plt.savefig('部分依赖图与个体条件期望图.png')
9 模型性能评价
#9 模型性能评价
prob = model.predict_proba(X_test)
prob[:5]
pred = model.predict(X_test)
pred[:5]
print(classification_report(y_test,pred))
cohen_kappa_score(y_test, pred)#计算kappa得分
热力图
#热力图
import seaborn as sns
sns.heatmap(confusion_matrix(y_test, pred), annot=True, fmt='d', cmap='Blues')
plt.show()
plt.savefig('混淆矩阵热力图.png')
10 绘制ROC曲线
#10 绘制ROC曲线
plt.rcParams['font.sans-serif'] = ['SimHei']#解决图表中中文显示问题
from sklearn.metrics import roc_curve,roc_auc_score
# 假设 y_true 和 y_score 是你的真实标签和模型预测的概率得分
predict_target_prob=model.predict_proba(X_test)
fpr, tpr, thresholds = roc_curve(y_test, predict_target_prob[:,1])
# 计算AUC值
auc = roc_auc_score(y_test, predict_target_prob[:,1])
print("AUC值:", auc)
# 绘制 ROC 曲线
plt.plot(fpr, tpr, label='ROC Curve area=%.3f'%auc)
plt.plot(fpr, fpr, 'k--', linewidth=1)
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('随机森林分类树算法ROC曲线')
plt.legend()
plt.show()
plt.savefig('随机森林分类树算法ROC曲线.pdf')
11 运用两个特征变量绘制随机森林算法决策边界图
#11 运用两个特征变量绘制随机森林算法决策边界图
X2 = X.iloc[:, [2,5]]#仅选取workyears、debtratio作为特征变量
model = RandomForestClassifier(n_estimators=300, max_features=1, random_state=1)
model.fit(X2,y)
model.score(X2,y)
plot_decision_regions(np.array(X2), np.array(y), model)
plt.xlabel('debtratio')#将x轴设置为'debtratio'
plt.ylabel('workyears')#将y轴设置为'workyears'
plt.title('随机森林算法决策边界')#将标题设置为'随机森林算法决策边界'
plt.show()
plt.savefig('随机森林算法决策边界.png')