Iforest算法常用于异常检测。孤立森林算法由08年首次提出,基于孤立森林的异常检测算法11年在tkdd问世,这两篇论文的一作是小哥Fei Tony Liu和Kai Ming Ting教授,周志华是第二作者(多篇论文有合作,猜测长期合作关系)。这时Fei Tony Liu还是蒙纳士大学(Monash University )一个博士生,目前在蒙纳士大学兼职任职,主要任职于在澳大利亚印度尼西亚经济治理合作组织(数据分析顾问)。哈哈哈哈,这些是题外话
言归正传,Iforest算法自问世以来,颇受好评,从众多异常检测算法中脱颖而出的原因在于其“又快又好”的特性,接下来简单介绍其算法原理。
孤立森林,由多棵孤立树组成,一棵孤立树的目标是将一个样本与其余样本分离。孤立树本质是一棵二叉树,给定 n n n个 d d d维样本的数据 X X X,迭代地随机选择一个特征 q q q和该特征范围内的一个分割值 p p p,通过判断 q < p q<p q<p将 X X X二分为左孩子节点和右孩子节点。直到满足一下条件之一终止迭代:
这样一棵二叉树,假设所有样本都是可孤立的[无样本具有相同的值],需要 n n n个叶子节点, n − 1 n-1 n−1个内部节点,共 2 n − 1 2n-1 2n−1个节点,即内存需要和样本数量呈线性增长。
异常样本的分布显著不同于正常的大多数样本分布差异,孤立树用来检测异常的依据(假设)是:
如图所示,非异常点被切分的更频繁因此,在孤立树中路径长度(可看作是被切分的次数)越小的样本,越有可能是异常点。路径长度可理解为指从根节点出发到达代表某样本的叶子节点所在层的深度。
通过不断从全量样本中随机抽取的子样本中构造孤立树,形成孤立森林。通过样本在孤立森林中的路径长度的大小,判断该样本是否是异常点。通过归一化路径长度的值来定义样本x的异常分数:
s ( x , n ) = 2 ( − E ( h ( x ) ) c ( n ) ) s(x,n) = 2^{(-\frac{E( { h(x) })}{ c(n) } )} s(x,n)=2(−c(n)E(h(x)))
其中n表示总样本数量, E ( h ( x ) ) E(h(x)) E(h(x))是孤立森林的路径长度 h ( x ) h(x) h(x)均值, c ( n ) c(n) c(n)可看作给定样本个数 n n n的二叉树的平均搜索长度, c ( n ) = 2 H ( n − 1 ) − ( 2 ( n − 1 ) / n ) c(n) = 2H(n − 1) − (2(n − 1)/n) c(n)=2H(n−1)−(2(n−1)/n)
其中 H ( i ) H(i) H(i)是谐波数, 值为 l n ( i ) + 0.5772156649 ( 欧 拉 常 数 ) ln(i) + 0.5772156649(欧拉常数) ln(i)+0.5772156649(欧拉常数)。 E ( h ( x ) ) E(h(x)) E(h(x))越趋于0,则异常分数 s s s越趋于1。
pyod是将sklearn中大部分的异常检测方法进行封装,使得调用异常检测方法和传统的分类器等的调用习惯一致,当然你也可以直接用sklearn中的包(sklearn.ensemble.IsolationForest)
# -*- coding: utf-8 -*-
"""Example of using Isolation Forest for outlier detection
"""
# Author: Yue Zhao
# License: BSD 2 clause
from __future__ import division
from __future__ import print_function
import os
import sys
# temporary solution for relative imports in case pyod is not installed
# if pyod is installed, no need to use the following line
sys.path.append(
os.path.abspath(os.path.join(os.path.dirname("__file__"), '..')))
from pyod.models.iforest import IForest
from pyod.utils.data import generate_data
from pyod.utils.data import evaluate_print
from pyod.utils.example import visualize
if __name__ == "__main__":
contamination = 0.1 # percentage of outliers
n_train = 200 # number of training points
n_test = 100 # number of testing points
# Generate sample data
X_train, y_train, X_test, y_test = \
generate_data(n_train=n_train,
n_test=n_test,
n_features=2,
contamination=contamination,
random_state=42)
# train IForest detector
clf_name = 'IForest'
clf = IForest()
clf.fit(X_train)
# get the prediction labels and outlier scores of the training data
y_train_pred = clf.labels_ # binary labels (0: inliers, 1: outliers)
y_train_scores = clf.decision_scores_ # raw outlier scores
# get the prediction on the test data
y_test_pred = clf.predict(X_test) # outlier labels (0 or 1)
y_test_scores = clf.decision_function(X_test) # outlier scores
# evaluate and print the results
print("\nOn Training Data:")
evaluate_print(clf_name, y_train, y_train_scores)
print("\nOn Test Data:")
evaluate_print(clf_name, y_test, y_test_scores)
# visualize the results
visualize(clf_name, X_train, y_train, X_test, y_test, y_train_pred,
y_test_pred, show_figure=True, save_figure=False)
当数据不是以一个中心分布时,可能会出现伪中心
4. 我们看看孤立森林如何切分数据,左图中[4, 0]位置可能是异常点,但由于位于y轴维度的中间位置,仍然需要切分很多次,才可以将这些点“孤立”,因此其异常分数被降低,右图中左下角和右下角的样本也很难被"孤立",随机切分成两部分,仍然每部分都有很多样本,而导致伪中心点。
https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.IsolationForest.html
https://pyod.readthedocs.io/en/latest/
https://github.com/yzhao062/pyod/blob/development/docs/index.rst
https://pyod.readthedocs.io/en/latest/pyod.models.html#module-pyod.models.iforest
http://202.119.32.195/cache/7/03/cs.nju.edu.cn/c452b98b5141e44d788e2a0153a8772e/icdm08b.pdf
https://cs.nju.edu.cn/zhouzh/zhouzh.files/publication/tkdd11.pdf
https://github.com/yzhao062/pyod/blob/master/examples/iforest_example.py
https://towardsdatascience.com/outlier-detection-with-extended-isolation-forest-1e248a3fe97b
https://towardsdatascience.com/outlier-detection-with-isolation-forest-3d190448d45e