上一篇介紹:一文讀懂層次聚類(Python代碼)
本篇介紹一個經典的異常檢測算法:局部離群因子(Local Outlier Factor),簡稱LOF算法。
背景Local Outlier Factor(LOF)是基於密度的經典算法(Breuning et. al. 2000), 文章發表於 SIGMOD 2000, 到目前已經有 3000+ 的引用。
在 LOF 之前的異常檢測算法大多是基於統計方法的,或者是借用了一些聚類算法用於異常點的識別(比如 ,DBSCAN,OPTICS)。這些方法都有一些不完美的地方:
基於統計的方法:通常需要假設數據服從特定的概率分布,這個假設往往是不成立的。聚類方法:通常只能給出 0/1 的判斷(即:是不是異常點),不能量化每個數據點的異常程度。相比較而言,基於密度的LOF算法要更簡單、直觀。它不需要對數據的分布做太多要求,還能量化每個數據點的異常程度(outlierness)。
下面開始正式介紹LOF算法。
LOF 算法首先,基於密度的離群點檢測方法有一個基本假設:非離群點對象周圍的密度與其鄰域周圍的密度類似,而離群點對象周圍的密度顯著不同於其鄰域周圍的密度。
什麼意思呢?看下面圖片感受下。
集群 C1 包含了 400 多個點,集群 C2 包含 100 個點。C1 和 C2 都是一類集群點,區別是 C1 位置比較集中,或者說密度比較大。而像 o1、o2點均為異常點,因為基於我們的假設,這兩個點周圍的密度顯著不同於周圍點的密度。
LOF 就是基於密度來判斷異常點的,通過給每個數據點都分配一個依賴於鄰域密度的離群因子 LOF,進而判斷該數據點是否為離群點。 如果
那麼什麼是LOF呢?
了解LOF前,必須先知道一下3個基本概念,因為LOF是基於這幾個概念而來的。
1. k鄰近距離
在距離數據點
點
比如上圖中,距離點
這裡的距離計算可以採用歐式距離、漢明距離、馬氏距離等等。比如用歐式距離的計算公式如下:
這裡的重點是找到第
2. k距離領域
以點
還是上圖所示,假設k=4,那麼點 1-6 均是鄰域範圍內的點。
3. 可達距離
這個可達距離大家需要留意點,點
這裡計算
4. 局部可達密度
先給出公式,然後再說明密度的含義。
數據點
5. 局部異常因子
根據局部可達密度的定義,如果一個數據點跟其他點比較疏遠的話,那麼顯然它的局部可達密度就小。但LOF算法衡量一個數據點的異常程度,並不是看它的絕對局部密度,而是看它跟周圍鄰近的數據點的相對密度。
這樣做的好處是可以允許數據分布不均勻、密度不同的情況。局部異常因子即是用局部相對密度來定義的。數據點
LOF算法流程了解了 LOF 的定義以後,整個算法也就顯而易見了:
對於每個數據點,計算它與其它所有點的距離,並按從近到遠排序;
對於每個數據點,找到它的 k-nearest-neighbor,計算 LOF 得分;
如果LOF值越大,說明越異常,反之如果越小,說明越趨於正常。
LOF優缺點優點
LOF 的一個優點是它同時考慮了數據集的局部和全局屬性。異常值不是按絕對值確定的,而是相對於它們的鄰域點密度確定的。當數據集中存在不同密度的不同集群時,LOF表現良好,比較適用於中等高維的數據集。
缺點
LOF算法中關於局部可達密度的定義其實暗含了一個假設,即:不存在大於等於 k 個重複的點。
當這樣的重複點存在的時候,這些點的平均可達距離為零,局部可達密度就變為無窮大,會給計算帶來一些麻煩。在實際應用時,為了避免這樣的情況出現,可以把 k-distance 改為 k-distinct-distance,不考慮重複的情況。或者,還可以考慮給可達距離都加一個很小的值,避免可達距離等於零。
另外,LOF 算法需要計算數據點兩兩之間的距離,造成整個算法時間複雜度為
Python 實現 LOF有兩個庫可以計算LOF,分別是PyOD和Sklearn,下面分別介紹。
使用pyod自帶的方法生成200個訓練樣本和100個測試樣本的數據集。正態樣本由多元高斯分布生成,異常樣本是使用均勻分布生成的。
訓練和測試數據集都有 5 個特徵,10% 的行被標記為異常。並且在數據中添加了一些隨機噪聲,讓完美分離正常點和異常點變得稍微困難一些。
from pyod.utils.data import generate_data
import numpy as np
X_train, y_train, X_test, y_test = \
generate_data(n_train=200,
n_test=100,
n_features=5,
contamination=0.1,
random_state=3)
X_train = X_train * np.random.uniform(0, 1, size=X_train.shape)
X_test = X_test * np.random.uniform(0,1, size=X_test.shape)PyOD
下面將訓練數據擬合了 LOF 模型並將其應用於合成測試數據。
在 PyOD 中,有兩個關鍵方法:decision_function 和 predict。
decision_function:返回每一行的異常分數predict:返回一個由 0 和 1 組成的數組,指示每一行被預測為正常 (0) 還是異常值 (1)from pyod.models.lof import LOF
clf_name = 'LOF'
clf = LOF()
clf.fit(X_train)
test_scores = clf.decision_function(X_test)
roc = round(roc_auc_score(y_test, test_scores), ndigits=4)
prn = round(precision_n_scores(y_test, test_scores), ndigits=4)
print(f'{clf_name} ROC:{roc}, precision @ rank n:{prn}')
>> LOF ROC:0.9656, precision @ rank n:0.8可以通過 LOF 模型方法查看 LOF 分數的分布。在下圖中看到正常數據(藍色)的分數聚集在 1.0 左右。離群數據點(橙色)的得分均大於 1.0,一般高於正常數據。
Sklearn
在scikit-learn中實現 LOF 進行異常檢測時,有兩種模式選擇:異常檢測模式 (novelty=False) 和 novelty檢測模式 (novelty=True)。
在異常檢測模式下,只有fit_predict生成離群點預測的方法可用。可以使用negative_outlier_factor_屬性檢索訓練數據的異常值分數,但無法為未見過的數據生成分數。模型會根據contamination參數(默認值為 0.1)自動選擇異常值的閾值。
import matplotlib.pyplot as plt
detector = LOF()
scores = detector.fit(X_train).decision_function(X_test)
sns.distplot(scores[y_test==0], label="inlier scores")
sns.distplot(scores[y_test==1], label="outlier scores").set_title("Distribution of Outlier Scores from LOF Detector")
plt.legend()
plt.xlabel("Outlier score")在novelty檢測模式下,只有decision_function用於生成異常值可用。fit_predict方法不可用,但predict方法可用於生成異常值預測。
clf = LocalOutlierFactor(novelty=True)
clf = clf.fit(X_train)
test_scores = clf.decision_function(X_test)
test_scores = -1*test_scores
roc = round(roc_auc_score(y_test, test_scores), ndigits=4)
prn = round(precision_n_scores(y_test, test_scores), ndigits=4)
print(f'{clf_name} ROC:{roc}, precision @ rank n:{prn}')該模式下模型的異常值分數被反轉,異常值的分數低於正常值。
實戰系列的全部完整代碼可見:https://github.com/xiaoyusmd/PythonDataScience
原創不易,歡迎點讚、在看。
關於LOF的原論文可以在公眾號回覆:LOF 獲取。
參考:
https://pub.towardsai.net/an-in-depth-guide-to-local-outlier-factor-lof-for-outlier-detection-in-python-5a6f128e5871https://zhuanlan.zhihu.com/p/28178476https://mp.weixin.qq.com/s/SHYNjtqCo-Ue3lezdVk1jAhttps://www.cnblogs.com/xzydn/p/14408758.html