局部异常因子LOF算法简介

本文详细介绍了LOF(LocalOutlierFactor)算法,一种基于密度的离群点检测方法,通过计算局部异常因子来量化数据点的异常程度。文章探讨了算法原理、关键概念、优缺点及其实现细节,包括如何处理重复点和高维数据的问题。
摘要由CSDN通过智能技术生成

0. 离群点挖掘方法

1. 背景

        在LOF之前的异常检测算法大多是基于统计方法的,或是借用了一些聚类算法用于异常点的识别(如DBSCAN, OPTICS)。但这些方法都有一些不完美的地方:

1. 基于统计的方法:通常需要假设数据服从特定的概率分布,这个假设往往是不成立的;

2. 聚类方法:通常只能给出 0/1 的判断(是不是异常点),不能量化每个数据点的异常程度(如DBSCAN, OPTICS)。

        相比较而言,基于密度的LOF(Local Outlier Factor,无监督)算法要更简单、直观。它不需要对数据的分布做太多要求,还能量化每个数据点的异常程度(outlierness)。

LOF最核心的部分是关于数据点密度的刻画。

2. 算法简介

        基于密度的离群点检测方法有一个基本假设:非离群点对象周围的密度与其邻域周围的密度类似,而离群点对象周围的密度显著不同于其邻域周围的密度。LOF 就是基于密度来判断异常点的,通过给每个数据点都分配一个依赖于邻域密度的离群因子 LOF,进而判断该数据点是否为离群点以及离群程度。如果LOF>=1,则该点为离群点,如果LOF近似1,则该点为正常数据点。可以比较精确的量化每个数据点的异常程度。

        LOF不会因为数据密度分散情况不同而错误的将正确点判定为异常点。

3. 几个基本概念

1. k-邻近距离(第k距离)

        数据点P与其最近第k个样本点之间的距离称为点P的K-邻近距离,记为k-distance(p),公式如下:d_{k}(P)=d(P,O),其中点O为距离点P最近的第k个点,且在集合中至少有不包括P在内的k个点。

2. k-距离邻域

        以点P为圆心,以k-邻近距离d_{k}(P)为半径的邻域(包括第k距离的点),称为P的第k距离邻域(不包括P),记作N_{k}(P)。因此邻域内的样本数|N_{k}(P)|>=k。

3. k-可达距离(reach-distance)

注意这里以点O为圆心,进行计算!

         O到某个样本点P的第k-可达距离:reach\_dist_{k}(P,O)=max\left \{ d_{k}(O) , d(P,O)\right \},即”点O的k-邻近距离“和“点P与O之间的直接距离”的较大值,如图下所示。

离点O最近的k个点(O第k邻域内的点),O到他们的可达距离被认为是相等的,都等于点O的k-邻近距离。

小问题:为什么用局部可达距离,而不直接用距离?

        用距离代替局部可达距离进行相应实验,效果相差不多。

为什么要定义一个可达距离呢?

        这样会导致所有p接近o的统计波动d(p,o)可以显著减少,这种平滑效果的强度可以通过参数k来控制。k值越高,同一邻域内的对象的可达距离越相似,结果越平滑。

4. k-局部可达密度(local reachablity density)

        数据点P的局部可达密度 = P的k个最近邻的平均可达距离的倒数(点P的第k邻域内点到p的平均可达距离的倒数)。距离越大,密度越小。

        可以看出:如果P是一个离群点,那么它不太可能存在于N_{k}(P)中各点的k-距离邻域内,从而导致其局部可达密度lrd偏小;

注意:

        点P的k-邻域点N_{k}(P)到P的可达距离,不是点P到其邻域点的可达距离!(k-可达距离不是对称的),且如果有重复点(存在点p的k个重复值),那么分母的可达距离之和有可能为0,这样会导致lrd变为无限大。

        局部可达密度lrd越高,越有可能属于同一簇;密度越低,越可能是离群点。即如果点P和周围邻域点是同一簇,那么可达距离越可能为较小的dk(O),导致可达距离之和较小,密度值较高;如果点P和周围邻居点较远,那么可达距离越可能取较大值d(O,P),导致密度较小,越可能是离群点;

5. k-局部异常因子LOF

        根据局部可达密度的定义,如果一个数据点跟其他点比较疏远的话,那么显然它的局部可达密度就小。但LOF算法衡量一个数据点的异常程度,并不是看它的绝对局部密度,而是看它跟周围邻近的数据点的相对密度。这样做的好处是可以允许数据分布不均匀、密度不同的情况。局部异常因子即是用“局部相对密度”来定义的。数据点p的局部相对密度(局部异常因子)为点P邻居们的平均局部可达密度跟数据点P的局部可达密度的比值。

表示点P的邻域点N_{k}(P)的平均局部可达密度与点P的局部可达密度之比。LOF主要通过计算一个数值score来反映一个样本的异常程度。该数值表示:一个样本点周围样本点所处位置的平均密度比上该样本点所在位置的密度。

如果该比值接近1,说明P与其邻域点密度差不多,P可能和邻域属于同一簇;

如果该比值越小于1,说明P的密度高于其邻域点密度,P为密集点(内点);

如果该比值越大于1,说明P处的密度小于其邻域点密度,P越可能是异常点;

4. LOF算法流程

1. 对于每个数据点,计算它与其它所有点的距离,并按从近到远排序;

2. 对于每个数据点,找到它的k近邻节点,计算LOF得分;

1. 计算点P的局部可达密度;

2. 计算点P的k邻域内所有点局部可达密度的平均值;

3. 通过第二步计算结果与第一步计算结果的比值,得到LOF分数。

3. 如果LOF值越大,说明越异常,反之如果越小,说明越趋于正常。

具体案例参考:异常检测算法LOF - 知乎

圈圈圆圆圈圈,圈懵了多少人,异常检测算法LOF概念详解、可视化、实战案例分析 - 知乎

5. LOF优缺点

5.1 优点

1. 检测思想比较简单。算法简单易懂、容易实现,只需计算距离,且参数只有邻居个数K。

2. LOF同时考虑了数据集的局部和全局属性。异常值不是按绝对值确定的,而是相对于它们的邻域点密度确定的;

2. 当数据集中存在不同密度的不同集群时,LOF表现良好,比较适用于中等高维的数据集。

5.2 缺点

1. LOF算法中关于局部可达密度的定义其实暗含了一个假设不存在 >=k 个重复的点。当这样的重复点存在时,这些点的平均可达距离为零,局部可达密度就变为无穷大,会给计算带来一些麻烦。在实际应用时,为了避免这样的情况出现,可以把 k-distance 改为 k-distinct-distance,不考虑重复的情况。或者还可以考虑给可达距离都加一个很小的值,避免可达距离等于零。

若有k个或以上的点跟O重合,即到O的距离是0,则ρ无法计算,要排除这种情况。或者,k-distance都加上一个很小的值,避免ρ无法计算。

 2. 因需要计算距离,运算开销大,不适合高维、大数据。  

       LOF 需要计算数据点两两之间的距离,造成整个算法时间复杂度高,为O(n^2)。为了提高算法效率,后续有算法尝试改进。FastLOF算法先将整个数据随机的分成多个子集,然后在每个子集里计算LOF值。对于那些LOF异常得分<=1 的,从数据集里剔除,剩下的在下一轮寻找更合适的 nearest-neighbor,并更新 LOF 值。

6. API简介

        局部离群点因子为每个样本的异常分数,主要通过每个点P和其邻域点的密度来判断该点是否为异常点,如果点P的密度越低,越可能被认为是异常点。而密度是通过点之间的距离计算的,距离越远,密度越低,距离越近,密度越高。因为LOF的密度是通过点的第k邻域来计算,而不是全局计算,因此得名“局部”异常因子。

6.1 构造函数

lof = LocalOutlierFactor(n_neighbors=20,  # k值,默认=20,似乎实践很好,knn算法中的k默认为 5。
    contamination='auto',  # 样本中异常值占比,默认为0.1,取值0.1~0.5
    algorithm='auto',  # 如ball_tree、kd_tree、brute(暴力搜索)
    leaf_size=30,
    metric='minkowski',  # 距离计算标准,距离标准图例
    p=2,  # 使用的距离度量函数(1:欧氏距离, 2:曼哈顿距离)
    metric_params=None,  
    novelty=False,  # 是否用于奇异值检测
    n_jobs=None)  # 并行作业数,-1时为CPU核心数

algorithm:找到最近的k个样本

1. kd_tree:依次对K维坐标轴,以中值切分,每一个节点是超矩形,适用于低维(<20时效率最高);

2. ball_tree:以质心c和半径r分割样本空间,每一个节点是超球体,适用于高维(kd_tree高维效率低);

3. brute:暴力搜索;

4. auto:通过 fit() 拟合,选择最适合的算法;如果数据稀疏,拟合后会自动选择brute,参数失效。

leaf_size:kd_tree和ball_tree中叶子大小,影响构建、查询、存储,根据实际数据而定。

      树叶中可以有多于一个的数据点,算法在达到叶子时在其中执行暴力搜索即可。如果leaf size 趋向于N(训练数据的样本数量),算法其实就是 brute force了。如果leaf size 太小了,趋向于1,那查询的时候 遍历树的时间就会大大增加。

其他函数

decision_function(X)# 预测未知数据的异常程度,返回值为原始分数。分数越高,异常程度越高
fit(X[, y])  # 训练函数
fit_predict(X[, y])  # 训练&预测
get_params([deep]) # 获取模型参数
kneighbors([X, n_neighbors, return_distance])  # 查找点的K临近点
kneighbors_graph([X, n_neighbors, mode])  # 计算X中点的k临近点的(加权)图
predict([X])  # 结果预测
score_samples(X)  # 与X的局部异常值因子相反
set_params(**params)  # 设置此估算器的参数。

6.2 在一组数中找异常点

from sklearn.neighbors import LocalOutlierFactor
X = [[-1.1], [0.2], [100.1], [0.3]]
lof = LocalOutlierFactor(n_neighbors=2)
res = lof.fit_predict(X)
print(res)
# [ 1  1 -1  1]
print(lof.negative_outlier_factor_) # 返回-lof值
# [ -0.98214286  -1.03703704 -72.64219576  -0.98214286]
 

属性negative_outlier_factor_:和LOF相反的值,值越小,越有可能是异常值。

6.3 Outlier detection -- 异常值检测

  当训练数据中包含离群点,通过相关算法找到训练数据的中心模式,忽视训练样本的其他异常点。(模型训练时要匹配训练数据的中心样本,忽略偏差观测值,从而检测出异常值)。

        LOF既可以做奇异值检测,也可以做异常值检测。

neighbors.LocalOutlierFactor 算法用于奇异值检测时,需要将算法中novelty参数的值设置为True,即 lof = LocalOutlierFactor(novelty=True)。并且没有fit_predict(),此时需要用predict(), decision_function() 和 score_samples() 用于新的观测数据而不是训练数据。

X_inliers = 0.3 * np.random.randn(100, 2)
X_inliers = np.r_[X_inliers + 2, X_inliers - 2]  # 正常点
X_outliers = np.random.uniform(low=-4, high=4, size=(20, 2))  # 异常点
X = np.r_[X_inliers, X_outliers]  # 混合
ground_truth = np.ones(len(X), dtype=int)
ground_truth[-len(X_outliers):] = -1

lof = LocalOutlierFactor(n_neighbors=20, contamination=0.1)
res = lof.fit_predict(X)
y_pred = lof.fit_predict(X)
n_errors = (y_pred != ground_truth).sum()
X_scores = lof.negative_outlier_factor_

plt.title('Locla Outlier Factor (LOF)')
plt.scatter(X[:, 0], X[:, 1], color='k', s=3., label='Data points')
radius = (X_scores.max() - X_scores) / (X_scores.max() - X_scores.min())
plt.scatter(X[:, 0], X[:, 1], s=1000*radius, edgecolors='r',
            facecolors='none', label='Outlier scores')
plt.axis('tight')
plt.xlim((-5, 5))
plt.ylim((-5, 5))
plt.xlabel("prediction errors: %d" %(n_errors))
legend = plt.legend(loc='upper left')
legend.legendHandles[0]._sizes = [10]
legend.legendHandles[1]._sizes = [20]
plt.show()

6.4 Novelty detection -- 奇异值检测

        当训练数据中没有离群点,只含有positive(正常)的数据,我们的目标是用训练好的模型去检测另外发现的新样本。通过算法学习其pattern,之后用于检测未曾看到过新数据是否属于这个pattern,如果属于,该新数据是positive,否则negative,即奇异值。

# 生成网格坐标点
xx, yy = np.meshgrid(np.linspace(-5, 5, 500), np.linspace(-5, 5, 500))
X = 0.3 * np.random.randn(100, 2)
X_train = np.r_[X + 2, X - 2]
X = 0.3 * np.random.randn(20, 2)
X_test = np.r_[X + 2, X - 2]

# generate some abnormal novel observations
X_outliers = np.random.uniform(low=-4, high=4, size=(20, 2))
lof = LocalOutlierFactor(n_neighbors=20, contamination=0.1, novelty=True)
lof.fit(X_train)
y_pred_test = lof.predict(X_test)
y_pred_outliers = lof.predict(X_outliers)

n_error_test = y_pred_test[y_pred_test == -1].size
n_error_outliers = y_pred_outliers[y_pred_outliers == 1].size
Z = lof.decision_function(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)

plt.title('Novelty Detection with LOF')
plt.contourf(xx, yy, Z, levels=np.linspace(Z.min(), 0, 7), cmap=plt.cm.PuBu)
a = plt.contour(xx, yy, Z, levels=[0], linewidths=2, colors='darkred')
plt.contourf(xx, yy, Z, levels=[0, Z.max()], colors='palevioletred')
s = 40
b1 = plt.scatter(X_train[:, 0], X_train[:, 1], c='white', s=s, edgecolors='k')
b2 = plt.scatter(X_test[:, 0], X_test[:, 1], c='blueviolet', s=s, edgecolors='k')
c = plt.scatter(X_outliers[:, 0], X_outliers[:, 1], c='gold', s=s, edgecolors='k')
plt.axis('tight')
plt.xlim((-5, 5))
plt.ylim((-5, 5))
plt.legend([a.collections[0], b1, b2, c],
           ["learned frontier", "training observations", "new regular observations", "new abnormal observations"],
           loc='upper left',
           prop=matplotlib.font_manager.FontProperties(size=11))
plt.xlabel("errors novel regular:%d/40; errors novel abnormal: %d/40" %(n_error_test, n_error_outliers))
plt.show()

  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: LOF局部异常因子)是一种用于异常检测的算法,它可以用来识别数据集中的离群点。该算法的主要思想是通过比较每个数据点与其邻居数据点之间的密度来判断其异常程度。 以下是一个示例的LOF局部异常因子算法的MATLAB代码: ```matlab function lof = LOF(data, k) n = size(data, 1); % 数据点的数量 % 计算每个点的k距离 k_dist = zeros(n, 1); for i = 1:n distance = sqrt(sum((data - repmat(data(i, :), n, 1)).^2, 2)); k_dist(i) = sort(distance, 'ascend')(k + 1); % 对距离从小到大排序,取第k+1个值 end % 计算每个点的局部可达密度(LRD) lrd = zeros(n, 1); for i = 1:n neighbors = find(sqrt(sum((data - repmat(data(i, :), n, 1)).^2, 2)) <= k_dist(i)); sum_density = sum(k_dist(neighbors)); lrd(i) = length(neighbors) / sum_density; end % 计算每个点的LOFlof = zeros(n, 1); for i = 1:n neighbors = find(sqrt(sum((data - repmat(data(i, :), n, 1)).^2, 2)) <= k_dist(i)); lrd_ratio = sum(lrd(neighbors)) / lrd(i); lof(i) = lrd_ratio / length(neighbors); end end ``` 在这个代码中,输入参数`data`是一个n×d的矩阵,其中n是数据点的数量,d是每个数据点的维度。`k`是每个数据点的邻居数量。 算法首先计算每个点的k距离,即与该点距离第k近的点的距离。然后,通过计算每个点的邻居数据点的密度之和得到局部可达密度(LRD)。最后,通过将局部可达密度的比率与邻居数量计算得到LOF值。 该代码返回一个n×1的向量`lof`,其中每个元素是相应数据点的LOF值。LOF值越大,表示该数据点越异常。 ### 回答2: LOF局部异常因子)是一种用于检测数据集中离群点的算法。它通过比较每个数据点的局部密度与其邻居数据点的局部密度来计算异常因子。该算法的MATLAB代码如下: ```matlab function LOF = local_outlier_factor(data, k) [m,n] = size(data); % 获取数据集的大小 LOF = zeros(m, 1); % 初始化异常因子向量 for i=1:m % 找到数据点i的k个最近邻居 neighbors = knnsearch(data, data(i,:), 'K', k+1); % 最近邻的索引(包括自身) neighbors = neighbors(2:end); % 去除自身 % 计算每个邻居的局部可达密度 lrd_i = 0; % 数据点i的局部可达密度 for j=1:length(neighbors) lrd_n = local_reachability_density(data, neighbors(j), k); % 邻居的局部可达密度 lrd_i = lrd_i + lrd_n; end lrd_i = lrd_i / k; % 取平均值 % 计算数据点i的局部异常因子 lof_i = 0; % 数据点i的局部异常因子 for j=1:length(neighbors) lrd_n = local_reachability_density(data, neighbors(j), k); % 邻居的局部可达密度 lof_n = lrd_n / lrd_i; % 邻居的局部异常因子 lof_i = lof_i + lof_n; end lof_i = lof_i / k; % 取平均值 LOF(i) = lof_i; % 存储数据点i的局部异常因子 end end function lrd = local_reachability_density(data, idx, k) [m,n] = size(data); % 获取数据集的大小 idx_neighbors = knnsearch(data, data(idx,:), 'K', k+1); % 数据点idx的最近邻索引(包括自身) idx_neighbors = idx_neighbors(2:end); % 去除自身 % 计算数据点idx的k个最近邻居的可达距离 reach_dist = zeros(1, k); for i=1:k dist = norm(data(idx,:) - data(idx_neighbors(i),:)); reach_dist(i) = max([dist, k_distance(data, idx_neighbors(i), k)]); end % 计算数据点idx的局部可达密度 lrd = k / sum(reach_dist); end function k_dist = k_distance(data, idx, k) [m,n] = size(data); % 获取数据集的大小 dist = zeros(m, 1); % 存储数据点idx与其他数据点的距离 for i=1:m dist(i) = norm(data(idx,:) - data(i,:)); % 计算距离 end % 找到数据点idx的第k个最近距离 k_dist = min(nth_element(dist, k+1)); end ``` 该代码首先定义了一个`local_outlier_factor`函数,该函数接受一个数据集以及`k`,计算每个数据点的局部异常因子,并将结果存储在`LOF`向量中。其次,定义了一个`local_reachability_density`函数和一个`k_distance`函数,分别用于计算局部可达密度和第`k`个最近距离。 使用该代码,可以传入一个数据集和`k`的值来计算每个数据点的局部异常因子。结果中的值越大,表示对应数据点越是异常。 ### 回答3: LOF局部异常因子算法是一种用于异常检测的机器学习算法。该算法通过计算每个样本点周围样本点的密度来确定其异常程度。 以下是LOF算法的简化版MATLAB代码示例: ```matlab function LOF_scores = LOF(data, k) % data为输入数据,每行代表一个样本 % k为k邻近的数目 [n, m] = size(data); % n为样本数量,m为特征数目 LOF_scores = zeros(n, 1); % 初始化LOF得分数组 for i = 1:n distances = sqrt(sum((repmat(data(i,:), n, 1) - data).^2, 2)); % 计算样本点与其他点的欧氏距离 [sorted_dist, idx] = sort(distances); % 按距离排序 k_distances = sorted_dist(2:k+1); % 获取k个最近邻距离 k_nearest_points = data(idx(2:k+1), :); % 获取k个最近邻的样本点 average_local_reachability = 0; % 平均局部可达密度 for j = 1:k distances_j = sqrt(sum((repmat(k_nearest_points(j,:), k, 1) - k_nearest_points).^2, 2)); % 计算k近邻点之间的欧氏距离 reachability_distances = max([distances_j, k_distances], [], 2); % 计算k近邻点的可达距离 local_reachability_density = 1 / (sum(reachability_distances) / k); % 计算局部可达密度 average_local_reachability = average_local_reachability + local_reachability_density; % 累加局部可达密度 end average_local_reachability = average_local_reachability / k; % 计算平均局部可达密度 LOF_scores(i) = average_local_reachability / (sum(k_distances) / k); % 计算LOF得分 end LOF_scores = LOF_scores / max(LOF_scores); % 标准化LOF得分 end ``` 以上代码中,首先通过计算样本点之间的欧氏距离,找出每个样本点的k个最近邻距离和对应的样本点。然后,计算每个样本点的k近邻点之间的欧氏距离,并计算k近邻点的可达距离。通过累加所有k近邻点的可达距离,计算局部可达密度。最后,将每个样本点的局部可达密度除以其k个最近邻距离的平均值,得到LOF得分,即该样本点的异常程度。 需要注意的是,以上代码是一种简化版的LOF算法实现,可能存在一些优化和改进的空间。在实际应用中,可以根据具体的数据和需求进行相应的调整和改进。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值