异常检测——基于相似度的方法

异常检测——基于相似度的方法

Github
主要内容包括:

  • 基于距离的度量
  • 基于密度的度量

1.基于距离的度量

基于距离的度量去解决问题的最简单方法是使用嵌套循环。 第一层循环遍历每个数据,第二层循环进行异常判断,需要计算当前点与其他点的距离,一旦已识别出多于 k k k 个数据点与当前点的距离在 D D D 之内,则将该点自动标记为非异常值。 这样计算的时间复杂度为 O ( N 2 ) O(N^{2}) O(N2),当数据量比较大时,这样计算是及不划算的。 因此,需要修剪方法以加快距离计算。

1.1基于单元格的方法

基于单元格的技术中,数据空间被划分为单元格,单元格的宽度是阈值D和数据维数d的函数。具体地说,每个维度被划分成宽度最多为 D 2 ⋅ d \frac{D}{ {2 \cdot \sqrt d }} 2d D 单元格。在给定的单元以及相邻的单元中存在的数据点满足某些特性,这些特性可以让数据被更有效的处理。
在这里插入图片描述

二维情况为例,此时网格间的距离为 D 2 ⋅ d \frac{D}{ {2 \cdot \sqrt d }} 2d D

网格单元的数量基于数据空间的分区,并且与数据点的数量无关。这是决定该方法在低维数据上的效率的重要因素,在这种情况下,网格单元的数量可能不多。
此方法不适用于更高维度的数据。

对于给定的单元格,其 L 1 L_{1} L1 邻居被定义为通过最多1个单元间的边界可从该单元到达的单元格的集合。请注意,在一个角上接触的两个单元格也是 L 1 L_{1} L1 邻居。 L 2 L_{2} L2 邻居是通过跨越2个或3个边界而获得的那些单元格。 上图中显示了标记为 X X X的特定单元格及其 L 1 L_{1} L1 L 2 L_{2} L2 邻居集。 显然,内部单元具有8个 L 1 L_{1} L1 邻居和40个 L 2 L_{2} L2 邻居。 然后,可以立即观察到以下性质:

- 单元格中两点之间的距离最多为 D / 2 D/2 D/2
- 一个点与 L 1 L_{1} L1 邻接点之间的距离最大为 D D D
- 一个点与它的 L r Lr Lr 邻居(其中 r r r > 2)中的一个点之间的距离至少为 D D D
- 无法直接得出结论的是 L 2 L_{2} L2 中的单元格。 这表示特定单元中数据点的不确定性区域。

无法直接得出结论的L2需要执行距离计算。 同时,可以定义许多规则,以便立即将部分数据点确定为异常值或非异常值。 规则如下:

1. 如果一个单元格中包含超过 k k k 个(数据点及其 L 1 L_{1} L1 邻居),那么这些数据点都不是异常值。
2. 如果单元 A A A 及其相邻 L 1 L_{1} L1 L 2 L_{2} L2 中包含少于 k k k 个数据点,则单元A中的所有点都是异常值。

第一步是将部分数据点直接标记为非异常值(如果由于第一个规则而导致它们的单元格包含 k k k 个点以上)。 此外,此类单元格的所有相邻单元格仅包含非异常值。 为了充分利用第一条规则的修剪能力,确定每个单元格及其 L 1 L_{1} L1 邻居中点的总和。 如果总数大于 k k k ,则所有这些点也都标记为非离群值。

利用第二条规则的继续进行修剪。 对于包含至少一个数据点的每个单元格 A A A,计算其中的点数及其 L 1 L_{1} L1 L 2 L_{2} L2 邻居的总和。 如果该数字不超过 k k k,则将单元格 A A A 中的所有点标记为离群值。 此时,许多单元可能被标记为异常值或非异常值。

此时仍未标记为异常值或非异常值的单元格中的数据点需要明确计算其 k k k 最近邻距离。即使对于这样的数据点,通过使用单元格结构也可以更快地计算出 k k k 个最近邻的距离。
  考虑到目前为止尚未被标记为异常值或非异常值的单元格 A A A。这样的单元可能同时包含异常值和非异常值。
  单元格 A A A 中数据点的不确定性主要存在于该单元格的 L 2 L_{2} L2 邻居中的点集。无法通过规则知道 A A A L 2 L_{2} L2 邻居中的点是否在阈值距离 D D D 内,为了确定单元 A A A 中数据点与其 L 2 L_{2} L2 邻居中的点集在阈值距离 D D D 内的点数,需要进行显式距离计算。
  对于那些在 L 1 L_{1} L1 L 2 L_{2} L2 中不超过 k k k 个且距离小于 D D D 的数据点,则声明为异常值。
  需要注意,仅需要对单元 A A A 中的点到单元 A A A L 2 L_{2} L2邻居中的点执行显式距离计算。这是因为已知 L 1 L_{1} L1 邻居中的所有点到 A A A 中任何点的距离都小于 D D D,并且已知 L r Lr Lr ( r > 2 ) (r> 2) (r>2) 的所有点与 A A A上任何点的距离至少为 D D D。因此,可以在距离计算中实现额外的节省。

1.2 基于索引的方法

对于一个给定数据集,基于索引的方法利用多维索引结构(如 R \mathrm{R} R 树、 k − d k-d kd 树)来搜索每个数据对象 A A A 在半径 D D D 范围 内的相邻点。设 M M M 是一个异常值在其 D D D -邻域内允许含有对象的最多个数,若发现某个数据对象 A A A D D D -邻域内出现 M + 1 M+1 M+1 甚至更多个相邻点, 则判定对象 A A A 不是异常值。该算法时间复杂度在最坏情况下为 O ( k N 2 ) , O\left(k N^{2}\right), O(kN2), 其中 k k k 是数据集维数, N N N 是数据集包含对象的个数。该算法在数据集的维数增加时具有较好的扩展性,但是时间复杂度的估算仅考虑了搜索时间,而构造索引的任务本身就需要密集复杂的计算量。

2、基于密度的度量

基于密度的算法主要有局部离群因子(LocalOutlierFactor,LOF),以及LOCI、CLOF等基于LOF的改进算法。以LOF为例来介绍和实践。

基于距离的检测适用于各个集群的密度较为均匀的情况。在下图中,离群点B容易被检出,而若要检测出较为接近集群的离群点A,则可能会将一些集群边缘的点当作离群点丢弃。而LOF等基于密度的算法则可以较好地适应密度不同的集群情况。

在这里插入图片描述

基于密度的度量值是从从距离的计算开始引入的。首先定义一个“k-距离”。

k-距离(k-distance§)
  对于数据集D中的某一个对象o,与其距离最近的k个相邻点的最远距离表示为k-distance(p),定义为给定点p和数据集D中对象o之间的距离d(p,o),满足:

  • 在集合D中至少有k个点 o’,其中 o ′ ∈ D p o'∈D{p} oDp,满足 d ( p , o ′ ) ≤ d ( p , o ) d(p,o')≤d(p,o) d(p,o)d(p,o)
  • 在集合D中最多有k-1个点o’,其中 o ′ ∈ D p o'∈D{p} oDp,满足 d ( p , o ; ) < d ( p , o ) d(p,o;)<d(p,o) d(p,o;)<d(p,o)

直观理解,就是以对象p为中心,点O是距离 P最近的第k 个点。

k-邻域(k-distance neighborhood)
  由k-距离,我们扩展到一个点的集合——到对象o的距离小于等于k-距离的所有点的集合,我们称之为k-邻域:$N_{k − d i s t a n c e ( p )}( P ) = { q ∈ D \backslash{ p } ∣ d ( p , q ) ≤ k − d i s t a n c e ( p )} $。

在二维平面上展示出来的话,对象o的k-邻域实际上就是以对象o为圆心、k-距离为半径围成的圆形区域。就是说,k-邻域已经从“距离”这个概念延伸到“空间”了。

可达距离(reachability distance)
  有了邻域的概念,我们可以按照到对象o的距离远近,将数据集D内的点按照到o的距离分为两类:

  • p i p_i pi在对象o的k-邻域内,则可达距离就是给定点p关于对象o的k-距离;
  • p i p_i pi在对象o的k-邻域外,则可达距离就是给定点p关于对象o的实际距离。

给定点p关于对象o的可达距离用数学公式可以表示为: r e a c h − d i s t k ( p , o ) = m a x k − d i s t a n c e ( o ) , d ( p , o ) r e a c h−d i s t_ k ( p , o ) = m a x {k−distance( o ) , d ( p , o )} reachdistk(p,o)=maxkdistance(o),d(p,o)
  可达距离的设定是为了通过k的设置来平滑掉k邻域内所有点的距离波动,可以减少对距离的计算次数。
  在这里插入图片描述
  这样的分类处理可以简化后续的计算,同时让得到的数值区分度更高。

局部可达密度(local reachability density)
  我们可以将“密度”直观地理解为点的聚集程度,就是说,点与点之间距离越短,则密度越大。在这里,我们使用数据集D中给定点p与对象o的k-邻域内所有点的可达距离平均值的倒数(注意,不是导数)来定义局部可达密度。
  
  给定点p的局部可达密度计算公式为:
  在这里插入图片描述

  • 计算点P的第k 邻域内的所有点到给点P PP的可达距离平均值
  • P 的局部可达密度越高,越可能与其邻域内的点 属于同一簇;密度越低,越可能是离群点。

局部异常因子:
在这里插入图片描述

表示点p的邻域 N k ( p ) N_k(p) Nk(p)内其他点的局部可达密度与点p的局部可达密度之比的平均数。如果这个比值越接近1,说明p的邻域点密度差不多,p可能和邻域同属一簇;如果这个比值小于1,说明p的密度高于其邻域点密度,p为密集点;如果这个比值大于1,说明p的密度小于其邻域点密度,p可能是异常点。(此处应该都是p,github资料给的为o应该是错误的

最终得出的LOF数值,就是我们所需要的离群点分数。在sklearn中有LocalOutlierFactor库,可以直接调用。下面来直观感受一下LOF的图像呈现效果。

LocalOutlierFactor库可以用于对单个数据集进行无监督的离群检测,也可以基于已有的正常数据集对新数据集进行新颖性检测。在这里我们进行单个数据集的无监督离群检测。

练习

使用PyOD库生成toy example并调用LOF算法

from pyod.models.lof import LOF
import pyod as po
import matplotlib.pyplot as plt

(X_train, y_train, X_test, y_test ) = po.utils.data.generate_data()


clf = LOF()

clf.fit(X_train)
y_pre = clf.labels_

X_normal = X_train[y_train == 0, :]
X_outlier = X_train[y_train == 1, :]

Xpre_normal = X_train[y_pre == 0, :]
Xpre_outlier = X_train[y_pre == 1, :]

fig = plt.figure(figsize=(20,20))
# 画图
plt.scatter(X_normal[:,0],X_normal[:,1], c = 'r',marker = '+' ,s = 120)
plt.scatter(X_outlier[:,0],X_outlier[:,1], c = 'b',marker = '+',s = 120)

plt.scatter(Xpre_normal[:,0],Xpre_normal[:,1], c = 'r',marker = '+', s=20 )
plt.scatter(Xpre_outlier[:,0],Xpre_outlier[:,1], c = 'b',marker = '+', s=20)

plt.show()

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_43720646/article/details/112974728