决策树与随机森林分类
学习目标
决策树是一种直观且易于解释的分类模型,而随机森林则是一种基于决策树的集成学习方法,能够有效提升模型的准确性和鲁棒性。本课程将学习决策树与随机森林算法的基本原理,并通过实际数据进行分类与预测任务,比较两种模型在性能和效果上的差异。
相关知识点
- 决策树与随机森林分类
学习内容
1 决策树与随机森林分类
决策树是一种基本且直观的机器学习算法,广泛应用于分类和回归任务。它通过递归地选择最优特征并划分数据集,构建一棵具有决策功能的树形结构。每个内部节点代表对某个特征的判断,每个叶子节点代表最终的类别或预测值。决策树模型易于理解和解释,能够直接展示决策过程,具有较强的可解释性。它不需要复杂的预处理,对缺失值和异常值不敏感,也能处理非线性关系和多类分类问题。然而,决策树容易过拟合,特别是在树深度较大时。为了解决这一问题,衍生出了如随机森林等集成方法,以提升其泛化能力和稳定性。
随机森林是一种集成学习方法,广泛应用于分类、回归等多种机器学习任务中。它通过构建多个决策树并将它们的结果进行汇总来提高模型的准确性和稳定性。每棵决策树都是基于数据集的一个随机子集(通过自助采样法,即bootstrap sampling)和特征的一个随机子集训练得到的。这种双重随机性不仅减少了单棵树过拟合的风险,还提高了模型对不同数据分布的适应能力。对于分类任务,随机森林通过对多棵树的预测结果进行投票决定最终类别;对于回归任务,则取多棵树预测值的平均作为最终输出。随机森林易于使用且不需要过多的参数调优,同时能够提供特征重要性的评估,因此在实际应用中非常受欢迎。此外,该算法可以处理高维数据,并且在面对缺失值和不平衡数据时表现出较强的鲁棒性。
在本课程中,将使用来自LendingClub.com的公开数据。Lending Club将需要资金的人(借款人)与拥有资金的人(投资者)联系起来。我们的目标是基于一系列与信用相关的数据创建一个模型,预测借出资金给某人的风险。我们将使用2007年至2010年的借贷数据,并尝试分类和预测借款人是否全额偿还了贷款。
以下是数据集中各列所代表的含义:
- credit.policy: 如果客户符合LendingClub.com的信贷承保标准,则为1;否则为0。
- purpose: 贷款的目的(取值包括"credit_card"(信用卡)、“debt_consolidation”(债务合并)、“educational”(教育)、“major_purchase”(重大采购)、“small_business”(小型企业)和"all_other"(其他所有情况))。
- int.rate: 贷款利率,以比例表示(例如,11%的利率记录为0.11)。LendingClub.com认为风险较高的借款人会被分配更高的利率。
- installment: 如果贷款获得批准,借款人每月需支付的分期金额。
- log.annual.inc: 借款人自报年收入的自然对数值。
- dti: 借款人的债务收入比(总债务除以年收入)。
- fico: 借款人的FICO信用评分。
- days.with.cr.line: 借款人拥有信用额度的天数。
- revol.bal: 借款人的循环余额(即在信用卡账单周期结束时未还清的金额)。
- revol.util: 借款人使用的循环信用额度占总可用额度的比例。
- inq.last.6mths: 过去6个月内债权人对借款人的查询次数。
- delinq.2yrs: 过去2年内借款人逾期30天以上的次数。
- pub.rec: 借款人的负面公共记录数量(如破产申请、税务留置权或判决)。
- not.fully.paid: 分类任务的目标变量——借款人是否全额偿还了贷款。
1.1 获取数据集
wget --no-check-certificate https://model-community-picture.obs.cn-north-4.myhuaweicloud.com/ascend-zone/notebook_datasets/c7de6d84309a11f0b9bdfa163edcddae/loan_data.csv
1.2 导入库和数据
导入常用的pandas库用于数据处理以及导入用于绘图的库
%pip install seaborn
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
导入数据
使用pandas读取loan_data.csv
df = pd.read_csv('loan_data.csv')
查看数据集的info(简要信息)、head(前五行)和describe(统计数据摘要)
df.info()
df.describe()
df.head()
print("Follwoing is a breakup of credit approval status. 1 means approved credit, 0 means not approved.")
print(df['credit.policy'].value_counts())
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 9578 entries, 0 to 9577
Data columns (total 14 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 credit.policy 9578 non-null int64
1 purpose 9578 non-null object
2 int.rate 9578 non-null float64
3 installment 9578 non-null float64
4 log.annual.inc 9578 non-null float64
5 dti 9578 non-null float64
6 fico 9578 non-null int64
7 days.with.cr.line 9578 non-null float64
8 revol.bal 9578 non-null int64
9 revol.util 9578 non-null float64
10 inq.last.6mths 9578 non-null int64
11 delinq.2yrs 9578 non-null int64
12 pub.rec 9578 non-null int64
13 not.fully.paid 9578 non-null int64
dtypes: float64(6), int64(7), object(1)
memory usage: 1.0+ MB


Follwoing is a breakup of credit approval status. 1 means approved credit, 0 means not approved.
credit.policy
1 7710
0 1868
Name: count, dtype: int64
1.3 数据分析
按信用批准状态划分的FICO分数直方图
FICO分数是由美国Fair Isaac Corporation(FICO公司)开发的一种个人信用评分体系,主要用于评估个人的信用风险,广泛应用于贷款审批、信用卡发放、保险定价等领域。其评分范围通常在300到850分之间,分数越高代表信用状况越好,一般分为五个等级:800分以上为优秀(Excellent),740至799分为非常好(Very Good),670至739分为良好(Good),580至669分为一般(Fair),低于580分则被视为信用较差(Poor)。FICO分数的计算主要基于五个关键因素:还款历史(占比35%)、负债水平(30%)、信用历史长度(15%)、新信用申请(10%)和信用类型多样性(10%)。
df[df['credit.policy']==1]['fico'].plot.hist(bins=30,alpha=0.5,color='blue', label='Credit.Policy=1')
df[df['credit.policy']==0]['fico'].plot.hist(bins=30,alpha=0.5, color='red', label='Credit.Policy=0')
plt.legend(fontsize=15)
plt.title ("Histogram of FICO score by approved or disapproved credit policies", fontsize=16)
plt.xlabel("FICO score", fontsize=14)

分析在不同的信用批准状态下,各种因素是否存在统计学上的显著差异
sns.boxplot(x=df['credit.policy'],y=df['int.rate'])
plt.title("Interest rate varies between risky and non-risky borrowers", fontsize=15)
plt.xlabel("Credit policy",fontsize=15)
plt.ylabel("Interest rate",fontsize=15)

sns.boxplot(x=df['credit.policy'],y=df['log.annual.inc'])
plt.title("Income level does not make a big difference in credit approval odds", fontsize=15)
plt.xlabel("Credit policy",fontsize=15)
plt.ylabel("Log. annual income",fontsize=15)

sns.boxplot(x=df['credit.policy'],y=df['days.with.cr.line'])
plt.title("Credit-approved users have a slightly higher days with credit line", fontsize=15)
plt.xlabel("Credit policy",fontsize=15)
plt.ylabel("Days with credit line",fontsize=15)

sns.boxplot(x=df['credit.policy'],y=df['dti'])
plt.title("Debt-to-income level does not make a big difference in credit approval odds", fontsize=15)
plt.xlabel("Credit policy",fontsize=15)
plt.ylabel("Debt-to-income ratio",fontsize=15)

创建一个计数图(countplot),展示不同贷款目的的贷款数量分布情况,并通过颜色区分这些贷款是否已被全额偿还(依据not.fully.paid字段)
plt.figure(figsize=(10,6))
sns.countplot(x='purpose',hue='not.fully.paid',data=df, palette='Set1')
plt.title("Bar chart of loan purpose colored by not fully paid status", fontsize=17)
plt.xlabel("Purpose", fontsize=15)

分析和展示个人的FICO信用评分与其获得贷款时的利率之间的关系或趋势
sns.jointplot(x='fico',y='int.rate',data=df, color='purple', size=12)

使用lmplot查看“未完全支付”与“信用政策”之间的趋势是否有所不同
plt.figure(figsize=(14,7))
sns.lmplot(y='int.rate',x='fico',data=df,hue='credit.policy',
col='not.fully.paid',palette='Set1',height=6)

分类特征
将 purpose 列作为分类特征处理。为了使scikit-learn能够正确理解和处理这些分类数据,我们使用虚拟变量(也称为哑变量)对其进行编码转换。
df_final = pd.get_dummies(df,['purpose'],drop_first=True)
df_final.head()
credit.policy int.rate installment log.annual.inc dti fico days.with.cr.line revol.bal revol.util inq.last.6mths delinq.2yrs pub.rec not.fully.paid purpose_credit_card purpose_debt_consolidation purpose_educational purpose_home_improvement purpose_major_purchase purpose_small_business
0 1 0.1189 829.10 11.350407 19.48 737 5639.958333 28854 52.1 0 0 0 0 False True False False False False
1 1 0.1071 228.22 11.082143 14.29 707 2760.000000 33623 76.7 0 0 0 0 True False False False False False
2 1 0.1357 366.86 10.373491 11.63 682 4710.000000 3511 25.6 1 0 0 0 False True False False False False
3 1 0.1008 162.34 11.350407 8.10 712 2699.958333 33667 73.2 1 0 0 0 False True False False False False
4 1 0.1426 102.92 11.299732 14.97 667 4066.000000 4740 39.5 0 1 0 0 True False False False False False
1.4 训练数据
from sklearn.model_selection import train_test_split
X = df_final.drop('not.fully.paid',axis=1)
y = df_final['not.fully.paid']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.30)
X.head()
credit.policy int.rate installment log.annual.inc dti fico days.with.cr.line revol.bal revol.util inq.last.6mths delinq.2yrs pub.rec purpose_credit_card purpose_debt_consolidation purpose_educational purpose_home_improvement purpose_major_purchase purpose_small_business
0 1 0.1189 829.10 11.350407 19.48 737 5639.958333 28854 52.1 0 0 0 False True False False False False
1 1 0.1071 228.22 11.082143 14.29 707 2760.000000 33623 76.7 0 0 0 True False False False False False
2 1 0.1357 366.86 10.373491 11.63 682 4710.000000 3511 25.6 1 0 0 False True False False False False
3 1 0.1008 162.34 11.350407 8.10 712 2699.958333 33667 73.2 1 0 0 False True False False False False
4 1 0.1426 102.92 11.299732 14.97 667 4066.000000 4740 39.5 0 1 0 True False False False False False
1.5 决策树的构建和可视化
训练一个决策树模型,创建并拟合决策树分类器
from sklearn.tree import DecisionTreeClassifier
dtree = DecisionTreeClassifier(criterion='gini',max_depth=None)
dtree.fit(X_train,y_train)
使用测试数据集进行预测,并基于预测结果生成分类报告和混淆矩阵。
predictions = dtree.predict(X_test)
from sklearn.metrics import classification_report,confusion_matrix
print(classification_report(y_test,predictions))
cm=confusion_matrix(y_test,predictions)
print(cm)
print ("Accuracy of prediction:",round((cm[0,0]+cm[1,1])/cm.sum(),3))
out:
precision recall f1-score support
0 0.84 0.82 0.83 2410
1 0.16 0.18 0.17 464
accuracy 0.72 2874
macro avg 0.50 0.50 0.50 2874
weighted avg 0.73 0.72 0.72 2874
[[1983 427]
[ 380 84]]
Accuracy of prediction: 0.719
决策树可解释性可视化分析
决策树的一大优势就是其高度的可解释性。我们可以直观地看到模型是如何做出决策的,这对于理解算法工作原理和建立对模型的信任都非常重要。
首先,我们来可视化决策树的完整结构。由于完整的决策树可能非常复杂,我们先创建一个限制深度的版本用于展示:
# 1. 决策树结构可视化
from sklearn.tree import plot_tree
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] # 设置中文显示
plt.rcParams['axes.unicode_minus'] = False
# 创建一个限制深度的决策树用于可视化(完整树太复杂无法清晰显示)
dtree_viz = DecisionTreeClassifier(criterion='gini', max_depth=3, random_state=42)
dtree_viz.fit(X_train, y_train)
# 绘制决策树结构
plt.figure(figsize=(20, 12))
plot_tree(dtree_viz,
feature_names=X_train.columns,
class_names=['Fully Paid', 'Not Fully Paid'],
filled=True,
fontsize=10)
plt.title("决策树结构 (最大深度 = 3)", fontsize=16)
plt.show()

特征重要性分析
特征重要性告诉我们每个变量对最终预测结果的贡献程度。重要性越高的特征在决策过程中起到更关键的作用:
feature_importance = dtree.feature_importances_
feature_names = X_train.columns
# 创建特征重要性DataFrame并排序
importance_df = pd.DataFrame({
'feature': feature_names,
'importance': feature_importance
}).sort_values('importance', ascending=False)
print("Top 10 Most Important Features:")
print(importance_df.head(10))
# 可视化特征重要性
plt.figure(figsize=(12, 8))
top_features = importance_df.head(10)
plt.barh(top_features['feature'], top_features['importance'])
plt.xlabel('特征重要性', fontsize=12)
plt.title('决策树模型最重要的10个特征', fontsize=14)
plt.gca().invert_yaxis()
plt.tight_layout()
plt.show()

决策规则文本表示
除了图形化展示,我们也可以用文本形式查看决策树的规则,这有助于理解具体的判断逻辑,文本格式的决策规则清晰地展示了每个分支的判断条件,让我们可以像阅读if-else语句一样理解模型的决策逻辑。
# 3. 决策路径示例分析
from sklearn.tree import export_text
# 显示决策树的文本表示(限制深度以便阅读)
tree_rules = export_text(dtree_viz, feature_names=list(X_train.columns))
print("Decision Tree Rules (Max Depth = 3):")
print(tree_rules[:2000]) # 只显示前2000个字符,避免输出过长
Decision Tree Rules (Max Depth = 3):
|--- credit.policy <= 0.50
| |--- inq.last.6mths <= 5.50
| | |--- purpose_small_business <= 0.50
| | | |--- class: 0
| | |--- purpose_small_business > 0.50
| | | |--- class: 0
| |--- inq.last.6mths > 5.50
| | |--- int.rate <= 0.16
| | | |--- class: 0
| | |--- int.rate > 0.16
| | | |--- class: 0
|--- credit.policy > 0.50
| |--- int.rate <= 0.09
| | |--- installment <= 558.20
| | | |--- class: 0
| | |--- installment > 558.20
| | | |--- class: 0
| |--- int.rate > 0.09
| | |--- inq.last.6mths <= 2.50
| | | |--- class: 0
| | |--- inq.last.6mths > 2.50
| | | |--- class: 0
模型复杂度对比分析
最后,让我们分析一下不同决策树的复杂度差异,这有助于理解模型的解释性与性能之间的权衡
# 创建不同深度限制的决策树进行对比
max_depths = [2, 3, 5, 8, None]
complexity_results = []
for depth in max_depths:
# 训练模型
dt_temp = DecisionTreeClassifier(criterion='gini', max_depth=depth, random_state=42)
dt_temp.fit(X_train, y_train)
# 预测和评估
pred_temp = dt_temp.predict(X_test)
accuracy = (pred_temp == y_test).mean()
# 计算复杂度指标
tree = dt_temp.tree_
complexity_results.append({
'max_depth_limit': str(depth) if depth else 'None',
'actual_depth': tree.max_depth,
'total_nodes': tree.node_count,
'leaf_nodes': tree.n_leaves,
'accuracy': accuracy
})
# 转换为DataFrame便于可视化
complexity_df = pd.DataFrame(complexity_results)
print("Decision Tree Complexity vs Performance Comparison:")
print(complexity_df.round(4))
# 创建复合可视化图
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(15, 12))
# 1. 节点数量对比
ax1.bar(complexity_df['max_depth_limit'], complexity_df['total_nodes'], color='skyblue', alpha=0.7)
ax1.set_title('Total Nodes vs Max Depth Limit', fontsize=12)
ax1.set_xlabel('Max Depth Limit')
ax1.set_ylabel('Total Nodes')
ax1.tick_params(axis='x', rotation=45)
# 2. 叶子节点数量对比
ax2.bar(complexity_df['max_depth_limit'], complexity_df['leaf_nodes'], color='lightcoral', alpha=0.7)
ax2.set_title('Leaf Nodes vs Max Depth Limit', fontsize=12)
ax2.set_xlabel('Max Depth Limit')
ax2.set_ylabel('Leaf Nodes')
ax2.tick_params(axis='x', rotation=45)
# 3. 准确率对比
ax3.plot(complexity_df['max_depth_limit'], complexity_df['accuracy'], 'o-', color='green', linewidth=2, markersize=8)
ax3.set_title('Accuracy vs Max Depth Limit', fontsize=12)
ax3.set_xlabel('Max Depth Limit')
ax3.set_ylabel('Accuracy')
ax3.tick_params(axis='x', rotation=45)
ax3.grid(True, alpha=0.3)
# 4. 复杂度vs性能散点图
ax4.scatter(complexity_df['total_nodes'], complexity_df['accuracy'],
c=['red', 'orange', 'yellow', 'lightgreen', 'green'], s=100, alpha=0.7)
ax4.set_title('Performance vs Complexity Trade-off', fontsize=12)
ax4.set_xlabel('Total Nodes (Complexity)')
ax4.set_ylabel('Accuracy')
ax4.grid(True, alpha=0.3)
# 为每个点添加标签
for i, row in complexity_df.iterrows():
ax4.annotate(f"depth={row['max_depth_limit']}",
(row['total_nodes'], row['accuracy']),
xytext=(5, 5), textcoords='offset points', fontsize=9)
plt.tight_layout()
plt.show()
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(20, 8))
# 简单树(高可解释性)
dt_simple = DecisionTreeClassifier(criterion='gini', max_depth=2, random_state=42)
dt_simple.fit(X_train, y_train)
plot_tree(dt_simple, ax=ax1, feature_names=X_train.columns,
class_names=['Fully Paid', 'Not Fully Paid'], filled=True, fontsize=10)
ax1.set_title('Simple Tree (Max Depth = 2)\nHigh Interpretability, Lower Performance', fontsize=14)
# 复杂树(低可解释性)
dt_complex = DecisionTreeClassifier(criterion='gini', max_depth=5, random_state=42)
dt_complex.fit(X_train, y_train)
plot_tree(dt_complex, ax=ax2, feature_names=X_train.columns,
class_names=['Fully Paid', 'Not Fully Paid'], filled=True, fontsize=8)
ax2.set_title('Complex Tree (Max Depth = 5)\nLower Interpretability, Higher Performance', fontsize=14)
plt.tight_layout()
plt.show()



# 性能指标详细对比
print("\n" + "="*80)
print("性能指标详细对比分析:")
print("="*80)
for i, row in complexity_df.iterrows():
dt_eval = DecisionTreeClassifier(criterion='gini',
max_depth=None if row['max_depth_limit']=='None' else int(row['max_depth_limit']),
random_state=42)
dt_eval.fit(X_train, y_train)
pred_eval = dt_eval.predict(X_test)
from sklearn.metrics import precision_score, recall_score, f1_score
print(f"\nMax Depth = {row['max_depth_limit']}:")
print(f" Accuracy: {row['accuracy']:.4f}")
print(f" Precision: {precision_score(y_test, pred_eval):.4f}")
print(f" Recall: {recall_score(y_test, pred_eval):.4f}")
print(f" F1-Score: {f1_score(y_test, pred_eval):.4f}")
print(f" Nodes: {row['total_nodes']}")
print(f" 解释难度: {'简单' if row['total_nodes'] < 10 else '中等' if row['total_nodes'] < 100 else '困难'}")
print("\n" + "="*80)
print("关键发现:")
print("1. 树的深度增加时,节点数量呈指数级增长,解释难度大大增加")
print("2. 性能提升在某个点后趋于平缓,过度复杂化可能导致过拟合")
print("3. 深度为2-3的树具有良好的解释性,适合需要透明决策的场景")
print("4. 实际应用中应根据具体需求在性能和可解释性之间找到最佳平衡点")
print("5. 对于金融风控场景,可能需要优先考虑解释性以符合监管要求")
print("="*80)
================================================================================
性能指标详细对比分析:
================================================================================
Max Depth = 2:
Accuracy: 0.8386
Precision: 0.0000
Recall: 0.0000
F1-Score: 0.0000
Nodes: 7
解释难度: 简单
Max Depth = 3:
Accuracy: 0.8386
Precision: 0.0000
Recall: 0.0000
F1-Score: 0.0000
Nodes: 15
解释难度: 中等
Max Depth = 5:
Accuracy: 0.8340
Precision: 0.2400
Recall: 0.0129
F1-Score: 0.0245
Nodes: 61
解释难度: 中等
Max Depth = 8:
Accuracy: 0.8264
Precision: 0.2785
Recall: 0.0474
F1-Score: 0.0810
Nodes: 223
解释难度: 困难
Max Depth = None:
Accuracy: 0.7234
Precision: 0.1823
Recall: 0.2047
F1-Score: 0.1929
Nodes: 2051
解释难度: 困难
================================================================================
关键发现:
1. 树的深度增加时,节点数量呈指数级增长,解释难度大大增加
2. 性能提升在某个点后趋于平缓,过度复杂化可能导致过拟合
3. 深度为2-3的树具有良好的解释性,适合需要透明决策的场景
4. 实际应用中应根据具体需求在性能和可解释性之间找到最佳平衡点
5. 对于金融风控场景,可能需要优先考虑解释性以符合监管要求
================================================================================
1.6 随机森林模型的预测与评估
创建一个RandomForestClassifier类的实例,并使用之前准备好的训练数据对其进行拟合。
from sklearn.ensemble import RandomForestClassifier
rfc = RandomForestClassifier(n_estimators=600)
rfc.fit(X_train, y_train)
对X_test数据预测“not.fully.paid”类别的值。
rfc_pred = rfc.predict(X_test)
现在根据预测结果生成一个分类报告
cr = classification_report(y_test,predictions)
print(cr)
precision recall f1-score support
0 0.84 0.82 0.83 2410
1 0.16 0.18 0.17 464
accuracy 0.72 2874
macro avg 0.50 0.50 0.50 2874
weighted avg 0.73 0.72 0.72 2874
显示预测的混淆矩阵
cm = confusion_matrix(y_test,rfc_pred)
print(cm)
out:
[[2401 9]
[ 461 3]]
通过循环逐步增加随机森林中树的数量,并检查混淆矩阵的准确性
这是一个系统评估随机森林分类器性能的实验方法。具体而言,该方法通过循环逐步增加森林中决策树的数量(例如每次增加10棵树,从10棵到100棵),在每轮迭代中训练模型并生成混淆矩阵来评估分类准确性,从而观察模型性能随树数量增加的变化趋势。实验会对比两种节点划分标准:基尼系数(gini)通过计算不纯度来快速划分主要类别,计算效率较高;信息熵(entropy)则通过信息增益对类别分布更敏感,可能更适合不平衡数据。该分析方法能帮助确定最优的树数量(找到性能趋于稳定的临界点)和最合适的数据划分标准,同时验证随机森林"更多决策树不一定持续提升性能"的特性,特别适用于需要平衡模型精度和计算效率的实际应用场景,如金融风控或医疗诊断等领域。
nsimu = 21
accuracy=[0]*nsimu
ntree = [0]*nsimu
for i in range(1,nsimu):
rfc = RandomForestClassifier(n_estimators=i*5,min_samples_split=10,max_depth=None,criterion='gini')
rfc.fit(X_train, y_train)
rfc_pred = rfc.predict(X_test)
cm = confusion_matrix(y_test,rfc_pred)
accuracy[i] = (cm[0,0]+cm[1,1])/cm.sum()
ntree[i]=i*5
plt.figure(figsize=(10,6))
plt.scatter(x=ntree[1:nsimu],y=accuracy[1:nsimu],s=60,c='red')
plt.title("Number of trees in the Random Forest vs. prediction accuracy (criterion: 'gini')", fontsize=18)
plt.xlabel("Number of trees", fontsize=15)
plt.ylabel("Prediction accuracy from confusion matrix", fontsize=15)
nsimu = 21
accuracy=[0]*nsimu
ntree = [0]*nsimu
for i in range(1,nsimu):
rfc = RandomForestClassifier(n_estimators=i*5,min_samples_split=10,max_depth=None,criterion='entropy')
rfc.fit(X_train, y_train)
rfc_pred = rfc.predict(X_test)
cm = confusion_matrix(y_test,rfc_pred)
accuracy[i] = (cm[0,0]+cm[1,1])/cm.sum()
ntree[i]=i*5
plt.figure(figsize=(10,6))
plt.scatter(x=ntree[1:nsimu],y=accuracy[1:nsimu],s=60,c='red')
plt.title("Number of trees in the Random Forest vs. prediction accuracy (criterion: 'entropy')", fontsize=18)
plt.xlabel("Number of trees", fontsize=15)
plt.ylabel("Prediction accuracy from confusion matrix", fontsize=15)

设置最大树深度
设置随机森林的最大树深度(max_depth)是一个重要的模型调优手段,主要用于平衡模型的复杂度和泛化能力。通过限制单棵决策树的生长深度,可以有效防止模型过拟合训练数据中的噪声或异常值,从而提高在未知数据上的预测稳定性。较浅的树(如max_depth=3-5)会生成简单的决策规则,降低模型复杂度并提升训练速度,但可能导致欠拟合;而较深的树(如max_depth=10-20)能捕捉更复杂的数据模式,但计算成本增加且可能过拟合。实际应用中,max_depth通常需要配合交叉验证来优化,同时还需考虑树的数量(n_estimators)和特征选择策略。
nsimu = 21
accuracy=[0]*nsimu
ntree = [0]*nsimu
for i in range(1,nsimu):
rfc = RandomForestClassifier(n_estimators=i*5,min_samples_split=10,max_depth=None,criterion='gini')
rfc.fit(X_train, y_train)
rfc_pred = rfc.predict(X_test)
cm = confusion_matrix(y_test,rfc_pred)
accuracy[i] = (cm[0,0]+cm[1,1])/cm.sum()
ntree[i]=i*5
plt.figure(figsize=(10,6))
plt.scatter(x=ntree[1:nsimu],y=accuracy[1:nsimu],s=60,c='red')
plt.title("Number of trees in the Random Forest vs. prediction accuracy (max depth: None)", fontsize=18)
plt.xlabel("Number of trees", fontsize=15)
plt.ylabel("Prediction accuracy from confusion matrix", fontsize=15)

nsimu = 21
accuracy=[0]*nsimu
ntree = [0]*nsimu
for i in range(1,nsimu):
rfc = RandomForestClassifier(n_estimators=i*5,min_samples_split=10,max_depth=5,criterion='gini')
rfc.fit(X_train, y_train)
rfc_pred = rfc.predict(X_test)
cm = confusion_matrix(y_test,rfc_pred)
accuracy[i] = (cm[0,0]+cm[1,1])/cm.sum()
ntree[i]=i*5
plt.figure(figsize=(10,6))
plt.scatter(x=ntree[1:nsimu],y=accuracy[1:nsimu],s=60,c='red')
plt.title("Number of trees in the Random Forest vs. prediction accuracy (max depth: 5)", fontsize=18)
plt.xlabel("Number of trees", fontsize=15)
plt.ylabel("Prediction accuracy from confusion matrix", fontsize=15)

设置最小样本分割数标准
设置随机森林的最小样本分割数(min_samples_split)是一个关键的模型调控参数,主要用于优化决策树的生长过程。该参数定义了节点在被允许继续分裂前必须包含的最小样本数,通过阻止对样本量不足的节点进行进一步分割,可有效提升模型的鲁棒性和泛化能力。较大的值(如min_samples_split=10~20)会限制树的分裂,生成更粗粒度的决策规则,降低模型复杂度并防止过拟合,尤其适用于小规模数据集;而较小的值(如默认值2)允许树生长得更深,可能捕捉更细致的数据特征,但也增加了过拟合风险。实际应用中,该参数需要与max_depth等其他树参数协同调整,通常建议通过网格搜索确定最优值。
nsimu = 21
accuracy=[0]*nsimu
ntree = [0]*nsimu
for i in range(1,nsimu):
rfc = RandomForestClassifier(n_estimators=i*5,min_samples_split=2,max_depth=None,criterion='gini')
rfc.fit(X_train, y_train)
rfc_pred = rfc.predict(X_test)
cm = confusion_matrix(y_test,rfc_pred)
accuracy[i] = (cm[0,0]+cm[1,1])/cm.sum()
ntree[i]=i*5
plt.figure(figsize=(10,6))
plt.scatter(x=ntree[1:nsimu],y=accuracy[1:nsimu],s=60,c='red')
plt.title("Number of trees in the Random Forest vs. prediction accuracy (minimum sample split: 2)", fontsize=18)
plt.xlabel("Number of trees", fontsize=15)
plt.ylabel("Prediction accuracy from confusion matrix", fontsize=15)

nsimu = 21
accuracy=[0]*nsimu
ntree = [0]*nsimu
for i in range(1,nsimu):
rfc = RandomForestClassifier(n_estimators=i*5,min_samples_split=20,max_depth=None,criterion='gini')
rfc.fit(X_train, y_train)
rfc_pred = rfc.predict(X_test)
cm = confusion_matrix(y_test,rfc_pred)
accuracy[i] = (cm[0,0]+cm[1,1])/cm.sum()
ntree[i]=i*5
plt.figure(figsize=(10,6))
plt.scatter(x=ntree[1:nsimu],y=accuracy[1:nsimu],s=60,c='red')
plt.title("Number of trees in the Random Forest vs. prediction accuracy (minimum sample split: 20)", fontsize=18)
plt.xlabel("Number of trees", fontsize=15)
plt.ylabel("Prediction accuracy from confusion matrix", fontsize=15)

随机森林 vs 决策树对比分析
现在让我们通过几个简单直观的可视化来理解随机森林相比单个决策树的优势:
让我们直接对比两种模型在相同数据上的表现:
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score
# 计算各项指标
dt_accuracy = accuracy_score(y_test, predictions)
rf_accuracy = accuracy_score(y_test, rfc_pred)
dt_precision = precision_score(y_test, predictions)
rf_precision = precision_score(y_test, rfc_pred)
dt_recall = recall_score(y_test, predictions)
rf_recall = recall_score(y_test, rfc_pred)
dt_f1 = f1_score(y_test, predictions)
rf_f1 = f1_score(y_test, rfc_pred)
# 创建性能对比图
metrics = ['Accuracy', 'Precision', 'Recall', 'F1-Score']
dt_scores = [dt_accuracy, dt_precision, dt_recall, dt_f1]
rf_scores = [rf_accuracy, rf_precision, rf_recall, rf_f1]
x = np.arange(len(metrics))
width = 0.35
fig, ax = plt.subplots(figsize=(10, 6))
bars1 = ax.bar(x - width/2, dt_scores, width, label='Decision Tree', color='lightcoral', alpha=0.8)
bars2 = ax.bar(x + width/2, rf_scores, width, label='Random Forest', color='lightblue', alpha=0.8)
ax.set_xlabel('Performance Metrics')
ax.set_ylabel('Score')
ax.set_title('Decision Tree vs Random Forest Performance Comparison')
ax.set_xticks(x)
ax.set_xticklabels(metrics)
ax.legend()
ax.set_ylim(0, 1)
# 在柱状图上添加数值标签
for bars in [bars1, bars2]:
for bar in bars:
height = bar.get_height()
ax.text(bar.get_x() + bar.get_width()/2., height + 0.01,
f'{height:.3f}', ha='center', va='bottom')
plt.grid(axis='y', alpha=0.3)
plt.tight_layout()
plt.show()
print("性能提升分析:")
for i, metric in enumerate(metrics):
improvement = rf_scores[i] - dt_scores[i]
print(f"{metric}: 随机森林比决策树提升了 {improvement:+.3f}")

特征重要性对比
比较两种模型认为哪些特征最重要:
# 获取特征重要性
dt_importance = dtree.feature_importances_
rf_importance = rfc.feature_importances_
# 创建特征重要性DataFrame
feature_importance_df = pd.DataFrame({
'Feature': X_train.columns,
'Decision_Tree': dt_importance,
'Random_Forest': rf_importance
}).sort_values('Random_Forest', ascending=False)
# 显示前10个最重要的特征
print("\nTop 10 最重要特征对比:")
print(feature_importance_df.head(10).round(4))
# 可视化前8个特征的重要性
top_features = feature_importance_df.head(8)
fig, ax = plt.subplots(figsize=(12, 6))
x = np.arange(len(top_features))
width = 0.35
bars1 = ax.bar(x - width/2, top_features['Decision_Tree'], width,
label='Decision Tree', color='lightcoral', alpha=0.8)
bars2 = ax.bar(x + width/2, top_features['Random_Forest'], width,
label='Random Forest', color='lightblue', alpha=0.8)
ax.set_xlabel('Features')
ax.set_ylabel('Importance')
ax.set_title('Feature Importance Comparison (Top 8 Features)')
ax.set_xticks(x)
ax.set_xticklabels(top_features['Feature'], rotation=45, ha='right')
ax.legend()
plt.tight_layout()
plt.show()
Top 10 最重要特征对比:
Feature Decision_Tree Random_Forest
8 revol.util 0.1463 0.1139
2 installment 0.1184 0.1138
3 log.annual.inc 0.1044 0.1078
1 int.rate 0.1092 0.1071
7 revol.bal 0.0930 0.1062
6 days.with.cr.line 0.1285 0.1060
4 dti 0.1084 0.0971
5 fico 0.0675 0.0726
9 inq.last.6mths 0.0314 0.0596
0 credit.policy 0.0253 0.0293


1776

被折叠的 条评论
为什么被折叠?



