随机森林与孤立森林

本文详细介绍了随机森林与孤立森林两种机器学习算法。随机森林是一种集成学习方法,用于分类和回归,通过随机抽取样本和特征来减少过拟合。而孤立森林则用于异常检测,通过构建决策树来隔离样本,异常点通常在较少的分裂步骤中被分离。文中还提供了两种算法的Python代码实现,并展示了如何计算特征重要性和利用Out-of-Bag分数评估模型性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

随机森林

随机森林是一种以决策树(常用CART树)为基学习器的bagging算法。

  • 回归问题结果:各学习器的均值
  • 分类问题结果:
    • 硬投票:基学习器预测频率最高的类别为最终结果(原论文采用方法)
    • 软投票:通过各基学习器的结果概率分布计算样本属于某个类别的平均概率,然后选择概率分布最高的类别结果(sklearn.ensemble .RandomForestClassifier采用方法)

随机性

  • 样本随机:bootstrap抽样导致的训练集随机性
  • 特征随机:每个节点随机选取特征子集进行不纯度计算的随机性
  • 节点分割随机(可选):使用随机分割点选取时产生的随机性(此时的随机森林又被称为**Extremely Randomized Trees**),这种方法可以进一步减少方差,但是会增大偏差

特征重要性

  • Gini系数:随机森林可以通过计算各特征gini系数均值对特征重要性进行排序。但这种方法得出的重要性排序与特征取值范围以及预测类别数等因素相关[Strobl 2007],例如取值范围大的连续变量较离散变量可能会具有更高的得分。
  • permutation importance:sklearn中还可以用sklearn.inspection.permutation_importance计算模型中变量重要性,评价指标选择参考Metrics and scoring

out-of-bag(oob) score

sklearn随机森林模型中有一个oob_score参数,用于计算oob样本(没有被采样到的样本)在不包含该样本基学习器上的预测结果平均得分。

  • 回归问题:r2_score
    R 2 = 1 − ∑ i ( y i − y ^ i ) 2 ∑ i ( y i − y ‾ ) 2 R^2=1-\frac{\sum \limits_i(y_i-\hat y_i)^2}{\sum \limits_i(y_i-\overline{y})^2} R2=1i(yiy)2i(yiy^i)2
    M S E = ∑ i m ( y i − y ^ i ) 2 m MSE=\frac{\sum \limits_i^m(y_i-\hat y_i)^2}{m} MSE=mim(yiy^i)2
    和MSE相比, R 2 R^2 R2无量纲,而且消除了原始数据离散程度的影响。
  • 分类问题:accuracy_score

TRTE

  • 定义:Totally Random Trees Embedding(TRTE)是一种非监督数据升维方法。它能够基于每个样本在各个决策树上的叶节点位置,得到一种基于森林的样本特征嵌入。
  • 举例:假设现在有4棵树且每棵树有4个叶子节点,我们依次对它们进行从0至15的编号,记样本𝑖在4棵树叶子节点的位置编号为[0,7,8,14],样本𝑗的编号为[1,7,9,13],此时这两个样本的嵌入向量即为[1,0,0,0,0,0,0,1,1,0,0,0,0,0,1,0]和[0,1,0,0,0,0,0,1,0,1,0,0,0,1,0,0]。假设样本𝑘对应的编号为[0,6,8,14],那么其对应嵌入向量([1,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0])的距离应当和样本𝑖较近,而离样本𝑗较远,即两个样本在不同树上分配到相同的叶子结点次数越多,则越接近。
  • 应用:提取样本隐式特征,度量样本间距离
  • 由于闵氏距离度量两个嵌入向量之间的距离时,计算同一位置变量差异,故与叶子节点的编号顺序无关。

代码

from sklearn.datasets import make_classification
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
RANDOM_STATE = 42
X, y = make_classification(n_samples=1000, n_features=25,
                           n_clusters_per_class=1, n_informative=15,
                           random_state=RANDOM_STATE)
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.3, random_state=RANDOM_STATE)

clf = RandomForestClassifier(n_estimators=3,
                             max_depth=10,
                             random_state=RANDOM_STATE)
clf.fit(X_train, y_train)

results = clf.predict(X_test)

孤立森林

目的

用于连续特征数据的异常检测

基本原理及步骤

  • 基本思想
    多次随机选取特征和对应的分割点以分开空间中样本点,那么异常点很容易在较早的几次分割中就已经与其他样本隔开,正常点由于较为紧密故需要更多的分割次数才能将其分开。

  • 异常值判断指标
    假设样本数为n,

    • 树中的平均路径长度 c ( n ) = 2 H n − 1 − 2 ( n − 1 ) n c(n) = 2H_{n-1}-\frac{2(n-1)}{n} c(n)=2Hn1n2(n1)
    • 样本x分派到叶子节点的路径长度 h x = ∑ p = 1 x 1 p h_x=\sum_{p=1}^x\frac{1}{p} hx=p=1xp1
    • 异常程度指标 s ( x , n ) = 2 − E h x c ( n ) s(x,n)=2^{-\frac{\mathbb{E}h_x}{c(n)}} s(x,n)=2c(n)Ehx

    E h x → 0 \mathbb{E}h_x\rightarrow0 Ehx0时, s ( x , n ) → 1 s(x,n)\rightarrow1 s(x,n)1
    E h x → n − 1 \mathbb{E}h_x\rightarrow n-1 Ehxn1时, s ( x , n ) → 0 s(x,n)\rightarrow0 s(x,n)0
    E h x → c ( n ) \mathbb{E}h_x\rightarrow c(n) Ehxc(n)时, s ( x , n ) → 1 2 s(x,n)\rightarrow\frac{1}{2} s(x,n)21

  • 最大树高
    因为
    lim ⁡ n → ∞ H n − log ⁡ n = γ \lim_{n\to\infty} H_n-\log n = \gamma nlimHnlogn=γ
    其中, γ ≈ 0.5772 \gamma\approx0.5772 γ0.5772为欧拉常数, log ⁡ n \log n logn c ( n ) c(n) c(n)数量级相同。
    故,最大树高可设为 log ⁡ n \log n logn

  • 算法步骤

    • step1:样本、特征随机采样;
    • step2:对每个特征值随机确定分割点并进行样本分割;
    • step3:重复step2直至达到最大树高或者该节点只有1个样本;
    • step4:计算样本在每棵树上被分配到叶子的路径+ c ( T . s i z e ) c(T.size) c(T.size)(由于限制了最大树高,故需补充计算当前叶子节点上样本数对应可生长的平均路径值);
    • step5:计算 s ( x , n ) s(x,n) s(x,n)

代码

  • python库:[sklearn.ensemble.IsolationForest]https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.IsolationForest.html#sklearn.ensemble.IsolationForest)
  • 示例
import matplotlib.pyplot as plt
import numpy as np
from sklearn.ensemble import IsolationForest as sklearnIF


def cal_h(n):
    if n>=10000:
        hn = (np.log(n) + 0.5772)
    else:
        hn = 0
        for i in range(1,n+1):
            hn += 1/i
    return hn

class Node:

    def __init__(self, depth):
        self.depth = depth
        self.left = None
        self.right = None
        self.feature = None
        self.pivot = None


class Tree:

    def __init__(self, max_height):
        self.root = Node(0)
        self.max_height = max_height
        self.c = None

    def _build(self, node, X,):
        if X.shape[0] == 1:
            return
        if node.depth+1 > self.max_height:
            node.depth += self._c(X.shape[0])
            return
        node.feature = np.random.randint(X.shape[1])
        pivot_min = X[:, node.feature].min()
        pivot_max = X[:, node.feature].max()
        node.pivot = np.random.uniform(pivot_min, pivot_max)
        node.left, node.right = Node(node.depth+1), Node(node.depth+1)
        self._build(node.left, X[X[:, node.feature]<node.pivot])
        self._build(node.right, X[X[:, node.feature]>=node.pivot])

    def build(self, X):
        self.c = self._c(X.shape[0])
        self._build(self.root, X)

    def _c(self, n):
        if n == 1:
            return 0
        else:
            return 2 * (cal_h(n-1) - (n-1)/n)

    def _get_h_score(self, node, x):
        if node.left is None and node.right is None:
            return node.depth
        if x[node.feature] < node.pivot:
            return self._get_h_score(node.left, x)
        else:
            return self._get_h_score(node.right, x)

    def get_h_score(self, x):
        return self._get_h_score(self.root, x)


class IsolationForest:

    def __init__(self, n_estimators=100, max_samples=256):
        self.n_estimator = n_estimators
        self.max_samples = max_samples
        self.trees = []

    def fit(self, X):
        for tree_id in range(self.n_estimator):
            random_X = X[np.random.randint(0, X.shape[0], self.max_samples)]
            tree = Tree(np.log(random_X.shape[0]))
            tree.build(X)
            self.trees.append(tree)

    def predict(self, X):
        result = []
        for x in X:
            h = 0
            for tree in self.trees:
                h += tree.get_h_score(x) / tree.c
            score = np.power(2, - h/len(self.trees))
            result.append(score)
        return np.array(result)


if __name__ == "__main__":

    rng = np.random.RandomState(42)

    # Generate train data
    X = 0.3 * rng.randn(1000, 2)
    X_train = np.r_[X + 2, X - 2]
    # Generate some regular novel observations
    X = 0.3 * rng.randn(50, 2)
    X_normal = np.r_[X + 2, X - 2]
    # Generate some abnormal novel observations
    X_outliers = rng.uniform(low=-4, high=4, size=(20, 2))
    X_test = np.vstack([X_normal,X_outliers])

    IF = IsolationForest()
    IF.fit(X_train)
    res = IF.predict(X_test)
    result = sklearnIF(max_samples=100, random_state=rng).fit(X_train).predict(X_test)

    abnormal_X = X_test[res > np.quantile(res, 0.90)]
    abnormal_X2 = X_test[result ==-1]

    b1 = plt.scatter(X_test[:, 0], X_test[:, 1], s=5)
    b2 = plt.scatter(
        abnormal_X[:, 0], abnormal_X[:, 1],
        s=50, edgecolors="Red", facecolor="none"
    )
    b3 = plt.scatter(
        abnormal_X2[:, 0], abnormal_X2[:, 1],
        s=30, edgecolors="green", facecolor="none"
    )
    plt.legend([b1, b2, b3],
           ["test observations",
            "class IsolationForest outliers", "sklearnIF outliers"],
           loc="upper left")
    plt.show()
  • 结果
    选取0.95分位数作为异常值判断时,自己建立的孤立森林结果(红圈)和sklearn库结果(绿圈)如下图所示:
    在这里插入图片描述

[参考]:
DataWhale树模型与集成学习

<think>好的,我现在需要帮助用户了解孤立森林(Isolation Forest)的原理及实现方法。首先,用户提供了四个引用资料,我需要仔细阅读这些资料,提取关键信息,并结合自己的知识来组织回答。 首先,孤立森林的基本原理。根据引用[1][2],孤立森林类似于随机森林,但使用的是iTree,构建过程完全随机,不需要信息增益或基尼指数。其核心思想是通过随机选择特征划分值来隔离样本,异常点因为正常点差异大,会被更快地隔离到叶子节点。我需要解释清楚路径长度异常得分的关系,以及如何通过平均路径长度判断异常。 接下来是算法步骤。引用[3]提到Partial models线性时间复杂度,每棵树使用子样本训练,独立生成。需要分步骤说明树的构建过程,包括随机选择特征划分值,直到样本被孤立。然后说明森林的构建,多棵树的平均路径长度计算异常得分。 然后是算法的优势,根据引用[3][4],孤立森林不需要计算距离或密度,内存需求低,处理大数据高效,适合分布式处理。这部分要强调其高效性适用场景。 实现方法部分,用户可能需要Python代码示例。我需要参考引用中的描述,写出一个简单的iForest实现,包括iTree的构建路径长度计算。需要注意递归终止条件,比如树的高度限制或样本数,避免过深。预测函数需要遍历所有树计算平均路径。 最后是相关问题,根据用户提供的示例,我需要生成孤立森林相关的问题,可能涉及参数调优、传统方法比较、应用场景、处理高维数据等。 在写作过程中,要确保所有数学公式用$...$,独立公式用$$...$$,比如异常得分公式。同时检查LaTeX语法是否正确,如指数、希腊字母等。回答结构要清晰,分点说明原理、步骤、优势、实现,并附上代码相关问题。引用资料在适当位置标注,比如在提到内存需求时引用[4],提到部分模型时引用[3]。 需要特别注意用户提到的系统级指令,比如行内公式的格式、独立公式单独成段,中文回答,生成相关问题,引用标识自然添加。同时用户希望回答结构清晰,逐步解决问题,保证真实可靠。因此,在解释原理时,分核心思想算法步骤,每个部分简明扼要,结合引用内容。代码部分用Python,确保语法正确,注释清晰。 现在整理思路,先写原理,再步骤,优势,实现,相关问题。在实现部分,代码需要展示iTree的结构,构建函数,路径计算,以及整合成森林的过程。可能需要注意递归构建树时的终止条件,比如样本数<=1或达到高度限制。预测时,对每个样本计算在所有树中的路径长度,然后转换为异常得分。 最后检查是否符合所有用户要求,包括引用标识的正确位置,代码格式,问题生成是否相关。确保回答逻辑连贯,易于理解,覆盖用户所需的信息。</think>### 孤立森林(Isolation Forest)原理及实现 #### 一、算法原理 孤立森林(Isolation Forest)是一种基于**树结构**的无监督异常检测算法,其核心思想是通过**随机划分特征空间**快速隔离异常点。正常数据需要更多划分步骤才能被“孤立”,而异常点因分布稀疏,往往在较短的路径长度内即可被隔离[^3]。 异常得分公式为: $$s(x,n) = 2^{-\frac{E(h(x))}{c(n)}}$$ 其中: - $E(h(x))$ 是样本$x$在所有树中的平均路径长度; - $c(n)$ 是标准化因子,样本量$n$相关[^3]。 #### 二、算法步骤 1. **构建iTree**: - 随机选择一个特征划分值,递归地将样本划分为左右子树; - 终止条件:样本数$\leq 1$或达到树高度限制[^2]。 2. **构建森林**: - 生成多棵iTree(通常$t=100$),每棵树仅使用子样本(默认$ψ=256$)[^4]。 3. **计算异常得分**: - 若$s(x) \approx 1$,判定为异常;若$s(x) \approx 0$,则为正常。 #### 三、算法优势 1. **高效性**:时间复杂度为$O(n)$,适合大规模数据; 2. **无需距离计算**:直接通过树结构隔离样本,避免密度估计[^2]; 3. **内存友好**:子采样策略减少内存占用。 --- #### 四、Python实现示例 ```python import numpy as np class iTree: def __init__(self, height_limit): self.height_limit = height_limit self.left = None self.right = None self.split_feature = None self.split_value = None def build_itree(data, current_height, height_limit): if current_height >= height_limit or len(data) <= 1: return iTree(current_height) tree = iTree(current_height) tree.split_feature = np.random.randint(data.shape[1]) min_val = np.min(data[:, tree.split_feature]) max_val = np.max(data[:, tree.split_feature]) tree.split_value = np.random.uniform(min_val, max_val) left_data = data[data[:, tree.split_feature] < tree.split_value] right_data = data[data[:, tree.split_feature] >= tree.split_value] tree.left = build_itree(left_data, current_height + 1, height_limit) tree.right = build_itree(right_data, current_height + 1, height_limit) return tree def path_length(x, tree, current_path=0): if tree.left is None and tree.right is None: return current_path + _c(len(x)) # _c(n)为标准化函数 if x[tree.split_feature] < tree.split_value: return path_length(x, tree.left, current_path + 1) else: return path_length(x, tree.right, current_path + 1) class IsolationForest: def __init__(self, n_trees=100, sample_size=256): self.n_trees = n_trees self.sample_size = sample_size self.trees = [] def fit(self, X): max_height = np.ceil(np.log2(self.sample_size)) for _ in range(self.n_trees): sub_sample = X[np.random.choice(X.shape[0], self.sample_size, replace=False)] self.trees.append(build_itree(sub_sample, 0, max_height)) def anomaly_score(self, X): scores = [] for x in X: avg_path = np.mean([path_length(x, tree) for tree in self.trees]) scores.append(2 ** (-avg_path / _c(self.sample_size))) return np.array(scores) ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值