使用Scikit-learn包的随机森林嵌入方法处理数据

举报
yd_37369233 发表于 2026/07/31 11:08:11 2026/07/31
【摘要】 引言在机器学习中,特征工程往往比算法选择更能决定模型的上限。当数据呈现非线性结构时,传统的线性模型常常力不从心。本文将介绍Scikit-learn中一个相对冷门却非常强大的工具——随机森林嵌入(Random Forest Embedding),它能够将原始数据映射到高维稀疏空间,从而让线性模型也能捕捉非线性关系。 什么是随机森林嵌入?随机森林嵌入的核心思想来自Scikit-learn的Ra...

引言

在机器学习中,特征工程往往比算法选择更能决定模型的上限。当数据呈现非线性结构时,传统的线性模型常常力不从心。本文将介绍Scikit-learn中一个相对冷门却非常强大的工具——随机森林嵌入(Random Forest Embedding),它能够将原始数据映射到高维稀疏空间,从而让线性模型也能捕捉非线性关系。

什么是随机森林嵌入?

随机森林嵌入的核心思想来自Scikit-learn的RandomTreesEmbedding模块。它的工作流程如下:

  1. 首先在训练集上拟合一个树的集成模型(可以是完全随机树、随机森林或梯度提升树)
  2. 集成中的每棵树的每个叶子节点都被分配一个固定的特征索引
  3. 每个样本经过每棵树的决策路径后,会落入一个叶子节点
  4. 最终采用独热编码(one-hot) 的方式,将样本编码为一个高维稀疏向量——被激活的叶子位置为1,其余为0

最终每个样本的编码中,1的个数等于森林中树的数量。编码后的特征维度约为 n_estimators * 2 ** max_depth

这种嵌入本质上是将原始数据通过树的结构转化为一个有监督的、稀疏的、高维的分类编码。映射过程完全无监督且非常高效

为什么需要嵌入?

原始数据可能是低维但非线性的(比如同心圆分布),线性分类器难以直接处理。通过随机森林嵌入,数据被映射到高维空间后,原本线性不可分的问题变得可分。在高维稀疏空间中,线性分类器往往能取得优异的精度。

实战案例一:无监督嵌入 + 朴素贝叶斯

首先来看一个无监督的嵌入案例。我们使用Scikit-learn官方示例中的同心圆数据集:

import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_circles
from sklearn.ensemble import RandomTreesEmbedding, ExtraTreesClassifier
from sklearn.decomposition import TruncatedSVD
from sklearn.naive_bayes import BernoulliNB

# 生成同心圆数据集(非线性可分)
X, y = make_circles(factor=0.5, random_state=0, noise=0.05)

# 使用RandomTreesEmbedding进行无监督嵌入
hasher = RandomTreesEmbedding(
    n_estimators=10,      # 树的数量
    random_state=0, 
    max_depth=3           # 每棵树的最大深度
)
X_transformed = hasher.fit_transform(X)

# 使用截断SVD降维到2维以便可视化
svd = TruncatedSVD(n_components=2)
X_reduced = svd.fit_transform(X_transformed)

# 在嵌入后的数据上训练朴素贝叶斯分类器
nb = BernoulliNB()
nb.fit(X_transformed, y)

# 作为对比,直接在原始数据上训练ExtraTreesClassifier
trees = ExtraTreesClassifier(max_depth=3, n_estimators=10, random_state=0)
trees.fit(X, y)

上述代码中,RandomTreesEmbedding将原始2维数据映射到了高维稀疏空间,TruncatedSVD再将高维数据降维回2维以便观察嵌入效果。可以看到,原本同心圆分布的数据在嵌入后变得线性可分。

RandomTreesEmbedding的主要参数包括:

参数 说明 默认值
n_estimators 森林中树的数量 10
max_depth 每棵树的最大深度 5
min_samples_split 内部节点再划分所需最小样本数 2
min_samples_leaf 叶子节点最少样本数 1
random_state 随机种子 None

实战案例二:有监督嵌入 + 逻辑回归(Pipeline方式)

Scikit-learn的RandomTreesEmbedding是一个标准的transformer,可以无缝集成到Pipeline中。以下示例展示了如何将随机森林嵌入与逻辑回归组合使用:

from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomTreesEmbedding
from sklearn.model_selection import train_test_split
from sklearn.pipeline import make_pipeline
from sklearn.metrics import roc_curve

# 生成分类数据集
X, y = make_classification(n_samples=80000)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.5)

# 【重要】为避免过拟合,将训练集再拆分为两部分:
# 一部分用于训练嵌入(树模型),另一部分用于训练线性模型
X_train, X_train_lr, y_train, y_train_lr = train_test_split(
    X_train, y_train, test_size=0.5
)

# 构建Pipeline:随机树嵌入 → 逻辑回归
rt = RandomTreesEmbedding(max_depth=3, n_estimators=10, random_state=0)
rt_lm = LogisticRegression(solver='lbfgs', max_iter=1000)
pipeline = make_pipeline(rt, rt_lm)

# 训练并预测
pipeline.fit(X_train, y_train)
y_pred_rt = pipeline.predict_proba(X_test)[:, 1]
fpr_rt_lm, tpr_rt_lm, _ = roc_curve(y_test, y_pred_rt)

实战案例三:有监督嵌入(手动方式,使用随机森林)

如果你想使用有监督的随机森林(而非完全随机树)来做嵌入,可以借助RandomForestClassifierapply()方法获取每个样本的叶子索引,再配合OneHotEncoder进行独热编码:

from sklearn.ensemble import RandomForestClassifier
from sklearn.preprocessing import OneHotEncoder
from sklearn.linear_model import LogisticRegression

# 训练随机森林(有监督)
rf = RandomForestClassifier(max_depth=3, n_estimators=10)
rf.fit(X_train, y_train)

# 获取每个样本在每棵树中的叶子索引
X_train_leaves = rf.apply(X_train)        # shape: (n_samples, n_estimators)
X_train_lr_leaves = rf.apply(X_train_lr)
X_test_leaves = rf.apply(X_test)

# 对叶子索引进行独热编码
rf_enc = OneHotEncoder(categories='auto')
rf_enc.fit(X_train_leaves)

# 在编码后的特征上训练逻辑回归
rf_lm = LogisticRegression(solver='lbfgs', max_iter=1000)
rf_lm.fit(rf_enc.transform(X_train_lr_leaves), y_train_lr)

# 预测
y_pred_rf_lm = rf_lm.predict_proba(rf_enc.transform(X_test_leaves))[:, 1]

rf.apply(X)返回一个形状为 (n_samples, n_estimators) 的数组,表示每个样本在每棵树中落入的叶子节点索引。

注意事项

1. 避免过拟合

这是最关键的一点:训练嵌入的树模型和使用嵌入特征的线性模型,应该使用不同的数据子集。如果叶子总数接近训练样本数,树模型可能会过拟合,导致嵌入后的特征缺乏泛化能力。上述代码中通过将训练集拆分为两部分来解决这个问题。

2. 适用场景

  • 数据呈现非线性结构,但你想使用线性模型(如逻辑回归、线性SVM)
  • 需要处理高维稀疏数据,BernoulliNB等模型在嵌入后表现优异
  • 作为特征工程的一种手段,提升基线模型的表现

3. 计算开销

嵌入后的特征维度为 n_estimators * 2 ** max_depth。当树的数量和深度较大时,特征维度会急剧膨胀,需要注意内存和计算开销。

【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。