前言
本文介绍异常检测的常用方法之基于相似度的方法,该方法属于传统方法之一。
基于相似度的方法可以分为基于密度的方法和基于距离的方法两种。
一、基于距离的方法
【适用于各个集群的密度较为均匀的情况】该方法基于最近邻距离来定义异常值。
-
前提假设-------异常点的 k k k 近邻距离要远大于正常点。
-
嵌套循环法-------
(1). 第一层循环遍历每个数据,
(2). 第二层循环进行异常判断,需要计算当前点与其他点的距离。
一旦已识别出多于 k k k 个数据点与当前点的距离在 D D D 之内,则将该点自动标记为非异常值。特点: 这样计算的时间复杂度为 O ( N 2 ) O(N^{2}) O(N2),当数据量比较大时,这样计算是及不划算的。
因此,需要修剪方法以加快距离计算。
1. 单元格方法
在基于单元格的技术中,数据空间被划分为单元格,单元格的宽度是阈值D和数据维数的函数。具体地说,每个维度被划分成宽度最多为
D
2
⋅
d
\frac{D}{{2 \cdot \sqrt d }}
2⋅dD 单元格。
2. 索引方法
基于索引的方法利用多维索引结构(如 R \mathrm{R} R 树、 k − d k-d k−d 树)来搜索每个数据对象 A A A 在半径 D D D 范围 内的相邻点。
二、基于密度的方法
【适应密度不同的集群情况】基于密度的方法也是属于基于距离的方式,只是该类方法中使用的距离不再是 k k k近邻距离,而是对距离的概念进行了扩展:
-
k-距离(k-distance( p ) ):以对象o为中心,对数据集D中的所有点到o的距离进行排序,距离对象o第k近的点p与o之间的距离就是k-距离。
-
k-邻域(k-distance neighborhood): 就是以对象o为圆心、k-距离为半径围成的圆形区域。
-
可达距离(reachability distance):
-
局部可达密度(local reachability density):
-
局部异常因子:
基于密度的算法主要有:
局部离群因子(LocalOutlierFactor,LOF),以及LOCI、CLOF等基于LOF的改进算法。
其中,LOF采用的就是局部异常因子距离作为度量的一种检测方式
三、LOF方法演示
1.引入库
代码如下(示例):
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
warnings.filterwarnings('ignore')
import ssl
ssl._create_default_https_context = ssl._create_unverified_context
2.生成数据
数据分布如下图所示:
代码如下:
'''生成Toy example'''
x_train,x_test,y_train,y_test=data.generate_data(n_train=1000, n_test=500, n_features=2, contamination=0.1, train_only=False, offset=10, behaviour='new', random_state=None)
#分离出训练数据中的集群点(正常点)和离群点(异常点)
x_train_in = []
x_train_out = []
for i in range(x_train.shape[0]):
if(y_train[i]==0):
x_train_in.append(x_train[i])
if (y_train[i] == 1):
x_train_out.append(x_train[i])
x_train_in = np.array(x_train_in)
x_train_out = np.array(x_train_out)
#展示生成数据
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus']=False
plt.title('构造数据集 (LOF)')
plt.scatter(x_train_in[:,0],x_train_in[:,1], color='b', s=5, label='集群点')
plt.scatter(x_train_out[:,0],x_train_out[:,1], color='red', s=5, label='离群点')
plt.axis('tight')
legend = plt.legend(loc='upper left')
legend.legendHandles[0]._sizes = [10]
legend.legendHandles[1]._sizes = [20]
plt.show()
3. LOF 模型检测
代码如下:
'''模型检测'''
clf = LOF()
LOF.fit(x_train)
# 返回训练数据X_train上的异常标签和异常分值
y_train_pred = clf.labels_
# 用训练好的clf来预测未知数据中的异常值
y_test_pred = clf.predict(x_test)
4. 结果展示
plt.title('局部离群因子检测 (LOF)')
# 以标准化之后的局部离群值为半径画圆,以圆的大小直观表示出每个数据点的离群程度
# 找出构造离群值与实际离群值不同的点
n_errors = y_train_pred != y_train
X_pred = np.c_[x_train,n_errors]
# 实际离群值有正有负,转化为正数并保留其差异性(不是直接取绝对值)
X_scores_nor = (y_train_scores.max() - y_train_scores) / (y_train_scores.max() - y_train_scores.min())
X_pred = np.c_[X_pred,X_scores_nor]
X_pred = pd.DataFrame(X_pred,columns=['x','y','pred','scores'])
X_pred_same = X_pred[X_pred['pred'] == False]
X_pred_different = X_pred[X_pred['pred'] == True]
plt.scatter(X_pred_same.values[:,0], X_pred_same.values[:, 1],s=1000 * X_pred_same.values[:, 3], edgecolors='c',facecolors='none', label='标签一致')
plt.scatter(X_pred_different.values[:, 0], X_pred_different.values[:, 1],s=1000 * X_pred_different.values[:, 3], edgecolors='violet',facecolors='none', label='标签不同')
plt.scatter(x_train_in[:,0], x_train_in[:,1], color='b', s=5, label='集群点')
plt.scatter(x_train_out[:,0], x_train_out[:,1],color='red', s=5,label='离群点')
plt.axis('tight')
legend = plt.legend(loc='upper left')
legend.legendHandles[0]._sizes = [10]
legend.legendHandles[1]._sizes = [20]
plt.show()
总结
本文简单介绍了基于相似度的异常检测方法的两种类别:基于距离和基于密度的方法。
- 基于距离的方法更适合样本密度均匀的数据
- 基于相似度的方法能适应样本密度不均匀的情况
本文重点演示了利用pyod 进行LOF异常检测的代码实现。
至此,该教程已经学完了异常检测的传统方法的三个大类:
- 基于统计学的方法
- 线性模型
- 基于距离的方法