随机森林分类器(Random Forest)详解与实践(以信用卡欺诈检测为例)

一、什么是随机森林?

随机森林是一种集成学习方法(Bagging思想),它通过组合多个**决策树(Decision Trees)**来进行分类或回归,提升整体模型的准确率和鲁棒性,常用于抗噪、抗过拟合的场景。

  • 通俗理解:就像你问一群人(多个决策树)一个问题,通过投票或平均来得到更靠谱的答案。


二、工作流程:整体架构图解

训练过程:

  1. 从原始训练数据中,使用有放回抽样(Bootstrap)生成多个子数据集。

  2. 对每个子数据集训练一棵决策树,且在每个节点划分时只考虑部分特征(特征子集)。

  3. 最终通过所有树的预测结果进行投票(分类)或平均(回归)。

预测过程:

  • 分类:每棵树投票,最多票的类别为最终预测结果。

  • 回归:所有树的预测值取平均。


三、与单棵决策树的区别

特点决策树随机森林
结构单棵树多棵树
易过拟合否,抗过拟合能力强
模型稳定性对数据敏感鲁棒性更强
精度一般通常更高
可解释性差(但可以通过特征重要性解释)

四、核心技术点

1️⃣ Bagging(自助采样)

  • 从原始数据集中随机抽取子样本进行建树训练,每个样本子集大小与原始集相同(有放回),这样每棵树看到的数据都不一样,模型更具多样性。

2️⃣ 特征随机选择

  • 在每个节点分裂时,并不是使用全部特征,而是从所有特征中随机选出一部分用于划分,这进一步增加模型多样性,减少过拟合。

3️⃣ 投票机制(多数表决)

  • 所有树“投票决定”最终输出,弱学习器通过组合成为强学习器。



实战

1. 背景与问题

信用卡欺诈检测是一个典型的二分类且高度类别不平衡的问题:正样本(正常交易)远多于负样本(欺诈)。直接用原始数据训练,模型容易被多数类主导,造成对少数类(欺诈)的漏判。随机森林是基于树的集成方法,天然具备一定的抗过拟合能力和少数类鲁棒性,但在极度不平衡情况下仍需配合采样、阈值调整、评估指标等技巧来提升效果。

本文用 creditcard.csv 数据做示例(假设已有该文件),从预处理、类不平衡处理、模型训练到评估与优化,逐步展开。

 信用卡欺诈检测数据集 creditcard.csv

     


2. 导入库与基础预处理

import pandas as pd
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split, GridSearchCV, cross_val_score
from sklearn import metrics
from sklearn.preprocessing import StandardScaler
import numpy as np

2.1 加载与标准化

# 读取数据,去掉 Time 无用特征
data = pd.read_csv("creditcard.csv").drop('Time', axis=1)

# 标准化 Amount 特征(其他特征已经是 PCA 变换后的,分布可直接用)
scaler = StandardScaler()
data['Amount'] = scaler.fit_transform(data[['Amount']])

2.2 拆分特征与标签

X = data.drop('Class', axis=1)
y = data['Class']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

3. 类别不平衡处理

原始数据中欺诈样本极少(少数类),需要策略缓解偏向多数类的问题。常见方式有:

3.1 下采样(undersampling)

从多数类随机采样,和少数类数量平衡(会丢信息,但简单快速)。

# =====下采样数据 under=====
# 分离多数类和少数类
class_0 = data[data['Class'] == 0]
class_1 = data[data['Class'] == 1]

# 将多数类下采样到与少数类一样多
class_0_down = class_0.sample(len(class_1), random_state=42)

# 合并成下采样后的数据集
data_under = pd.concat([class_0_down, class_1])

# 提取特征与标签
X_under = data_under.drop("Class", axis=1)
y_under = data_under["Class"]

# 拆分训练/测试(注意这里用的是下采样后数据)
X_train_under, X_test_under, y_train_under, y_test_under = train_test_split(
    X_under, y_under, test_size=0.3, random_state=100, stratify=y_under
)

3.2 过采样(SMOTE)(可选)

# # =====过采样SMOTE数据 over=====
# from imblearn.over_sampling import SMOTE
# oversampler = SMOTE(random_state=100)
# X_train_over, y_train_over = oversampler.fit_resample(X_train, y_train)

选择哪种采样方式取决于对“信息丢失”与“合成样本”的权衡。可以分别训练并比较。


4. 随机森林模型训练(以下以下采样数据为例)

model = RandomForestClassifier(n_estimators=100, max_depth=10, random_state=42)
model.fit(X_train_under, y_train_under)

如果用 SMOTE 过采样数据,替换为:

# model = RandomForestClassifier(n_estimators=100, max_depth=10, random_state=42)
# model.fit(X_train_over, y_train_over)

RandomForestClassifier 参数详解

1️⃣ n_estimators

  • 类型int

  • 默认值100

  • 描述:森林中树的数量,表示要训练的决策树的个数。树越多,模型越稳定,但训练和预测时间也会增加。一般来说,增加树的数量会提升模型的稳定性和准确性,直到达到一定的饱和点。

2️⃣ criterion

  • 类型str

  • 默认值'gini'

  • 可选值'gini', 'entropy'

  • 描述:衡量分裂质量的标准。常用的有:

    • 'gini':基尼不纯度(Gini Impurity)

    • 'entropy':信息增益(Information Gain)

    • 选择方法取决于任务,'gini'一般比'entropy'速度快,但有时'entropy'可能会表现得稍好。

3️⃣ max_depth

  • 类型intNone

  • 默认值None

  • 描述:每棵树的最大深度。

    • 如果为 None,树会一直分裂,直到所有叶节点的样本都属于同一类别或者所有叶节点的样本数量小于 min_samples_split

    • 设置最大深度可以防止树过深,从而避免过拟合。

4️⃣ min_samples_split

  • 类型intfloat

  • 默认值2

  • 描述:拆分内部节点所需的最小样本数。

    • 如果是 int,表示每个节点至少需要的样本数。

    • 如果是 float,则表示该节点样本数的比例(例如 0.1 表示 10% 的样本数)。

    • 增大该值可以防止过拟合,但可能导致模型欠拟合。

5️⃣ min_samples_leaf

  • 类型intfloat

  • 默认值1

  • 描述:每棵树的叶节点的最小样本数。

    • 如果是 int,表示每个叶节点至少需要的样本数。

    • 如果是 float,则表示该节点叶子样本数的比例。

    • 增大该值有助于防止过拟合。

6️⃣ max_features

  • 类型int, float, str, 或 None

  • 默认值'auto'(即 sqrt

  • 描述:用于分裂节点时考虑的最大特征数。可以有以下几种选择:

    • 'auto':等同于 'sqrt',即每次分裂时,随机选择特征的平方根个数。

    • 'sqrt':每次分裂时,随机选择特征的平方根个数(默认)。

    • 'log2':每次分裂时,随机选择特征的对数个数。

    • int:指定特定数量的特征。

    • float:指定特征数量的比例(例如 0.5 表示 50% 的特征)。

    • None:表示考虑所有特征。

7️⃣ bootstrap

  • 类型bool

  • 默认值True

  • 描述:是否使用自助采样(Bootstrap)法。

    • 如果为 True,表示通过有放回的采样生成训练数据集。

    • 如果为 False,则表示不使用采样,每棵树都使用原始数据集进行训练。

8️⃣ oob_score

  • 类型bool

  • 默认值False

  • 描述:是否使用袋外样本(Out-of-Bag samples)来评估模型的泛化能力。

    • 如果为 True,则使用未被选中的样本来评估模型,通常用于估计模型的性能,而无需额外的验证集。

9️⃣ n_jobs

  • 类型intNone

  • 默认值None

  • 描述:并行处理的作业数。

    • -1 表示使用所有可用核心来训练和预测。

    • 如果是正整数,表示使用的 CPU 核心数。

🔟 random_state

  • 类型intRandomState

  • 默认值None

  • 描述:控制随机性,保证结果可复现。

    • 通过设定随机数种子,确保每次生成相同的结果(比如数据抽样、树的构建顺序)。

1️⃣1️⃣ class_weight

  • 类型dict, list, balancedNone

  • 默认值None

  • 描述:为每个类指定权重,可以帮助处理类别不平衡问题。

    • 'balanced':自动调整权重,使得较小类别的样本对损失的贡献更大。

    • None:每个类的权重都为 1。

    • 可以使用 {class_label: weight} 的形式指定每个类别的权重。

1️⃣2️⃣ max_samples

  • 类型intfloat

  • 默认值None

  • 描述:如果启用了自助采样(bootstrap=True),则此参数可以指定每棵树使用的样本数的比例或数量。

1️⃣3️⃣ warm_start

  • 类型bool

  • 默认值False

  • 描述:如果为 True,则在每次调用 .fit() 时,会在上次的基础上继续训练,而不是重新训练。

1️⃣4️⃣ verbose

  • 类型int

  • 默认值0

  • 描述:控制训练过程中的输出信息,越大输出信息越详细。

1️⃣5️⃣ n_estimators

  • 类型int

  • 默认值100

  • 描述:森林中树的数量,表示训练的决策树的数量,通常需要通过交叉验证来选择最合适的值。


5. 评估指标与报告

评估指标不能只看 Accuracy,F1、ROC-AUC、PR-AUC、混淆矩阵、阈值敏感度要结合看

# =====评估指标与分类报告=====
# 评估指标不能只看 Accuracy,F1、ROC-AUC、PR-AUC、混淆矩阵、阈值敏感度要结合看
y_pred = model.predict(X_test)
score = metrics.accuracy_score(y_test, y_pred)
print("分类模型准确率:", score)

print("分类报告:")
print(metrics.classification_report(y_test, y_pred))

conf_mat = metrics.confusion_matrix(y_test, y_pred)
print("混淆矩阵:\n", conf_mat)

结果:

5.1、整体准确率

        准确率(Accuracy) = 0.9746

        说明模型预测对了约 97.46% 的样本,但由于数据高度不平衡(0 类远多于 1 类),高准确率不代表模型好 —— 需要看分类报告和混淆矩阵。


5.2、分类报告分析

指标类别 0(非欺诈)类别 1(欺诈)
precision1.000.06
recall0.970.97
f1-score0.990.12
support5686498
类别 0(非欺诈)
  • 精确率(1.00):预测为非欺诈的几乎都是对的。

  • 召回率(0.97):97% 的真实非欺诈样本被正确识别。

类别 1(欺诈)
  • 精确率(0.06):只有 6% 的预测为欺诈是对的,误报较多。(宁杀错不放过)

  • 召回率(0.97):97% 的真实欺诈样本被模型成功找出,说明模型对欺诈样本的识别能力很强。

  • F1 分数(0.12):由于精确率太低,F1 分数偏低。

5.3、混淆矩阵分析

  • 真正例(TP) = 95:成功识别的欺诈。

  • 假正例(FP) = 1445:将非欺诈误报为欺诈(误报)。

  • 假负例(FN) = 3:漏掉的真实欺诈。

  • 真正负例(TN) = 55419:正确识别的非欺诈。

重点:
  • 误报(FP)比较多(1445),虽然召回率高,但可能带来业务上的麻烦。

  • 漏报(FN)极少(3),模型对欺诈“抓得住”。


6. 特征重要性

# =====特征重要性=====
importance = model.feature_importances_   #这个属性保存了模型中特征的重要性
im_df = pd.DataFrame(importance,columns=["importance"])
clos = data.columns
clos_1 = clos.values
clos_2 = clos_1.tolist()
clos = clos_2[0:-1]    #0从第一个元素开始,-1表示结束索引(倒数第一个元素),但「左闭右开」,最后取到倒数第二个
im_df['clos'] = clos
im_df = im_df.sort_values(by=['importance'],ascending=False)[:10]
print(im_df.head(10))

# 可视化
import matplotlib.pyplot as plt
plt.yticks(range(len(im_df)),im_df.clos)
plt.barh(range(len(im_df)),im_df['importance'])
plt.show()

结果:


7. 小结

  • 随机森林通过多棵决策树的投票/平均机制提升稳定性,天然防过拟合,但对极度不平衡敏感。

  • 处理不平衡:下采样、SMOTE 过采样、类权重、阈值调整各有利弊,应通过验证比较。

  • 评估指标不能只看 Accuracy,F1、ROC-AUC、PR-AUC、混淆矩阵、阈值敏感度要结合看。

  • 超参数调优和特征重要性可以进一步挖掘模型性能与解释性。


8. 提升建议

  1. 评估指标补充:ROC AUC 与 PR AUC

  2. 使用 class_weight="balanced":直接让模型在不采样的条件下内部调整惩罚。

  3.  决策阈值优化:自定义阈值(非默认 0.5)

  4. 阈值校准:用 CalibratedClassifierCV 做概率校准,提升概率可信度(尤其对阈值敏感时)。

  5. 简单交叉验证(了解泛化)

  6. 超参数调优:网格搜索 + 交叉验证


完整代码(随机森林+下采样/过采样+评估)

import pandas as pd
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn import metrics
from sklearn.preprocessing import StandardScaler

data = pd.read_csv("creditcard.csv").drop('Time', axis=1)
scaler = StandardScaler()
data['Amount'] = scaler.fit_transform(data[['Amount']])

X = data.drop('Class', axis=1)
y = data['Class']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# # =====过采样数据 SMOTE=====
# from imblearn.over_sampling import SMOTE
# oversampler = SMOTE(random_state=100)
# X_train_over, y_train_over = oversampler.fit_resample(X_train, y_train)
#
# model = RandomForestClassifier(n_estimators=100, max_depth=10, random_state=42)
# model = model.fit(X_train_over, y_train_over)

# =====下采样数据 under=====
class_0 = data[data['Class']==0]
class_1 = data[data['Class']==1]
class_0 = class_0.sample(len(class_1))
data_under = pd.concat([class_0, class_1])

X_under = data_under.drop("Class", axis=1)
y_under = data_under.Class
X_train_under, X_test_under, y_train_under, y_test_under = train_test_split(X_under, y_under, test_size=0.3, random_state=100)

model = RandomForestClassifier(n_estimators=100, max_depth=10, random_state=42)
model = model.fit(X_train_under, y_train_under)

# =====评估指标与分类报告=====
# 评估指标不能只看 Accuracy,F1、ROC-AUC、PR-AUC、混淆矩阵、阈值敏感度要结合看
y_pred = model.predict(X_test)
score = metrics.accuracy_score(y_test, y_pred)
print("分类模型准确率:", score)

print("分类报告:")
print(metrics.classification_report(y_test, y_pred))

conf_mat = metrics.confusion_matrix(y_test, y_pred)
print("混淆矩阵:\n", conf_mat)



# =====特征重要性=====
importance = model.feature_importances_   #这个属性保存了模型中特征的重要性
im_df = pd.DataFrame(importance,columns=["importance"])
clos = data.columns
clos_1 = clos.values
clos_2 = clos_1.tolist()
clos = clos_2[0:-1]    #0从第一个元素开始,-1表示结束索引(倒数第一个元素),但「左闭右开」,最后取到倒数第二个
im_df['clos'] = clos
im_df = im_df.sort_values(by=['importance'],ascending=False)[:10]
print("特征重要性排行:")
print(im_df.head(10))

import matplotlib.pyplot as plt
plt.yticks(range(len(im_df)),im_df.clos)
plt.barh(range(len(im_df)),im_df['importance'])
plt.show()

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐