异常检测算法LOF(local outlier factor)出自2000年的SIGMOD论文《LOF: Identifying Density-Based Local Outliers》,它定义了Local outlier factor来评估一个对象的异常程度。
为了理解LOF是如何计算的,先要知道以下几个定义:
- k-近邻距离(k-distance):点p的k-近邻距离,记作k-distance§,是距离p第k近的点与p之间的距离。
- k-近邻邻居(k-distance neighborhood): 给定了p的k-近邻距离,p的k-近邻邻居包括了与p点距离小于k-近邻距离的点。记为KaTeX parse error: Undefined control sequence: \textbackslash at position 41: …p)= \{ q \in D \̲t̲e̲x̲t̲b̲a̲c̲k̲s̲l̲a̲s̲h̲ ̲\{p\} | d(p, q)…,如果没有歧义时, N k-distance(p) ( p ) N_{\text{k-distance(p)}}(p) Nk-distance(p)(p)会记为 N k ( p ) N_k(p) Nk(p)。
- 可达距离(reachability distance): 在给定k的情况下,点p到点o的可达距离为o的k-邻近距离与点p和点o距离中的最大值,即 reach-dist k ( p , o ) = m a x ( k-distance ( o ) , d ( p , o ) ) \text{reach-dist}_k(p, o) = max(\text{k-distance}(o), d(p, o)) reach-distk(p,o)=max(k-distance(o),d(p,o))。在论文的图2示意了k=4时的可达距离,图中的 p 2 p_2 p2点与o点距离较远,所以两者间的可达距离为实际距离;而图中的 p 1 p_1 p1点与o很近,所以它们的可达距离为点o的k-近邻距离。所以使用可达距离后,对所有与点o距离较近的点p的距离 d ( p , o ) d(p,o) d(p,o)的统计波动(statistical flactuation)可以显著减少,这个平滑效应是由参数k来控制的,k越大,同一个邻域内里的点的可达距离越相似。
- 局部可达密度(local reachability density): 点p的局部可达密度记作下式,MinPts定义了最小的点个数,也就是点p的局部可达密度是点p的MinPts个最近邻居的平均可达距离的倒数。当有超过MinPts个与p一样坐标的点时,局部可达密度为无穷大,所以为了简化处理,LOF假设没有重复点(如果要处理重复点的情形,可定义k-近邻距离时必须要包括k个坐标不一样的点。或者先将数据集去重处理)
lrd M i n P t s ( p ) = 1 / ( ∑ o ∈ N M i n P t s ( p ) reach-dist M i n P t s ( p , o ) ∣ N M i n P t s ( p ) ∣ ) \text{lrd}_{MinPts}(p) = 1/ \left( \frac{\sum_{o \in N_{MinPts}(p)} \text{reach-dist}_{MinPts}(p, o) } {|N_{MinPts}(p)|} \right) lrdMinPts(p)=1/(∣NMinPts(p)∣∑o∈NMinPts(p)reach-distMinPts(p,o))
- 局部异常因子(local outlier factor): 点p的局部可达因子定义为下式,它是p的MinPts近邻邻居的平均局部可达密度与点p的局部可达密度的比值。 如果p的局部可达密度越低,它的MinPts近邻邻居的局部可达密度越高,p的LOF值越大。
L O F M i n P t s ( p ) = ∑ o ∈ N M i n P t s ( p ) l r d M i n P t s ( o ) l r d M i n P t s ( p ) ∣ N M i n P t s ( p ) ∣ = ∑ o ∈ N M i n P t s ( p ) l r d M i n P t s ( o ) ∣ N M i n P t s ( p ) ∣ / l r d ( p ) LOF_{MinPts}(p) = \frac {\sum_{o \in N_{MinPts}(p)} \frac{lrd_{MinPts}(o)}{lrd_{MinPts}(p)} } {|N_{MinPts}(p)|} = \frac {\sum_{o \in N_{MinPts}(p)} {lrd_{MinPts}(o)} } {|N_{MinPts}(p)|}/lrd(p) LOFMinPts(p)=∣NMinPts(p)∣∑o∈NMinPts(p)lrdMinPts(p)lrdMinPts(o)=∣NMinPts(p)∣∑o∈NMinPts(p)lrdMinPts(o)/lrd(p)
论文用了一定的篇幅去说明LOF的上下界。根据局部异常因子的定义,如果数据点 p 的 LOF 得分在1附近,表明数据点p的局部密度跟它的邻居们差不多;如果数据点 p 的 LOF 得分小于1,表明数据点p处在一个相对密集的区域,不像是一个异常点;如果数据点 p 的 LOF 得分远大于1,表明数据点p跟其他点比较疏远,很有可能是一个异常点。
但LOF的值并不是随着MinPts的改变而单调变化的,所以LOF作者的建议是选择一个范围内的MinPts,将这个范围的MinPts的上下界分别记为MinPtsUB和MinPtsLB。再将这个范围内计算出的最大LOF值作为点p的最终LOF值: max { LOF M i n P t s ( p ) ∣ M i n P t s L B ≤ M i n P t s ≤ M i n P t s U B } \max \{\text{LOF}_{MinPts}(p) | MinPtsLB \le MinPts \le MinPtsUB \} max{LOFMinPts(p)∣MinPtsLB≤MinPts≤MinPtsUB}。关于MinPtsUB和MinPtsLB的选取,论文有如下建议:
- 为了去掉不想要的统计波动,MinPtsLB至少为10。
- MinPtsLB可以看作一个类簇里需要包含的最小数目的点的个数;一般根据应用场景来选取,在作者实验的数据集,一般10-20个就挺好的。
- MinPtsUB选取为最大的可能被看做为异常点的集合的大小。
Scikit Learn 里实现了LOF, 使用样例:
import numpy as np
from sklearn.neighbors import LocalOutlierFactor
X = [[-1.1], [0.2], [101.1], [0.3]]
clf = LocalOutlierFactor(n_neighbors=2)
clf.fit_predict(X)
clf.negative_outlier_factor_