完整代碼及其數據,請移步小編的GitHub
傳送門:請點擊我
如果點擊有誤:https://github.com/LeBron-Jian/MachineLearningNote
在數據挖掘方面,經常需要在做特征工程和模型訓練之前對數據進行清洗,剔除無效數據和異常數據。異常檢測也是數據挖掘的一個方向,用於反作弊,偽基站,金融欺詐等領域。
在之前已經學習了異常檢測算法One Class SVM和 isolation Forest算法,博文如下:
Python機器學習筆記:異常點檢測算法——One Class SVM
Python機器學習筆記:異常點檢測算法——Isolation Forest
下面學習一個新的異常檢測算法:Local Outlier Factor
前言:異常檢測算法
異常檢測方法,針對不同的數據形式,有不同的實現方法。常用的有基於分布的方法,在上下 α 分位點之外的值認為是異常值(例如下圖),對於屬性值常用此類方法。基於距離的方法,適用於二維或高維坐標體系內異常點的判別。例如二維平面坐標或經緯度空間坐標下異常點識別,可用此類方法。
下面要學習一種基於距離的異常檢測算法,局部異常因子 LOF算法(Local Outlier Factor)。此算法可以在中等高維數據集上執行異常值檢測。
Local Outlier Factor(LOF)是基於密度的經典算法(Breuning et,al 2000),文章發表與SIGMOD 2000 ,到目前已經有 3000+引用。在LOF之前的異常檢測算法大多數是基於統計方法的,或者是借用了一些聚類算法用於異常點的識別(比如:DBSCAN,OPTICS),但是基於統計的異常檢測算法通常需要假設數據服從特定的概率分布,這個假設往往是不成立的。而聚類的方法通常只能給出0/1的判斷(即:是不是異常點),不能量化每個數據點的異常程度。相比較而言,基於密度的LOF算法要更簡單,直觀。它不需要對數據的分布做太多要求,還能量化每個數據點的異常程度(outlierness)。
在學習LOF之前,可能需要了解一下KMeans算法,這里附上博文:
Python機器學習筆記:K-Means算法,DBSCAN算法
1,LOF(Local Outlier Factor)算法理論
(此處地址:https://blog.csdn.net/wangyibo0201/article/details/51705966/)
1.1 LOF算法介紹
LOF是基於密度的算法,其最核心的部分是關於數據點密度的刻畫。如果對 distanced-based 或者 density-based 的聚類算法有些印象,你會發現 LOF中用來定義密度的一些概念和K-Means算法一些概念很相似。
首先用視覺直觀的感受一下,如下圖,對於C1集合的點,整體間距,密度,分散情況較為均勻一致。可以認為是同一簇;對於C2集合點,同樣可認為是一簇。o1, o2點相對孤立,可以認為是異常點或離散點。現在的問題是,如何實現算法的通用性,可以滿足C1 和 C2 這種密度分散情況迥異的集合的異常點識別。LOF可以實現我們的目標,LOF不會因為數據密度分散情況不同而錯誤的將正確點判定為異常點。
1.2 LOF 算法步驟
下面介紹 LOF算法的相關定義:
(1) d(p, o) :兩點 P 和 O 之間的距離
(2) K-distance:第 k 距離
在距離數據點 p 最近的 幾個點中,第 k 個最近的點跟點 p 之間的距離稱為點 p的K-鄰近距離,記為 K-distance(p)。
對於點 p 的第 k 距離 dk(p) 定義如下:
dk(p) = d(p, o) 並且滿足:
(a)在集合中至少有不包括 p 在內的 k 個點 o ∈ C{x≠p},滿足d(p,o') ≤ d(p,o)
(b)在集合中最多不包括 p 在內的 k-1 個點 o ∈ C{x≠p},滿足d(p,o') ≤ d(p,o)
p 的第 k 距離,也就是距離 p 第 k 遠的點的距離,不包括 P,如下圖所示:
(3) k-distance neighborhood of p:第 k 距離鄰域
點 p 的第 k 距離鄰域 Nk(p) 就是 p 的第 k距離即以內的所有點,包括第 k 距離。
因此 p 的 第 k 鄰域點的個數 |Nk(p)| >=k
(4) reach-distance:可達距離
可達距離(Reachablity distance):可達距離的定義跟K-鄰近距離是相關的,給定參數k時,數據點 p 到 數據點o的可達距離 reach-dist(p, o)為數據點 o 的 K-鄰近距離和數據點 p與點o 之間的直接距離的最大值。
點 o 到 點 p 的第 k 可達距離定義為:
也就是,點 o 到點 p 的第 k 可達距離,至少是 o 的第 k 距離,或者為 o, p之間的真實距離。這也意味着,離點 o 最近的 k 個點, o 到他們的可達距離被認為是相等,且都等於 dk(o)。如下圖所示, o1 到 p 的第 5 可達距離為 d(p, o1),o2 到 p 的第5可達距離為 d5(o2)
(5) local reachablity density:局部可達密度
局部可達密度(local reachablity density):局部可達密度的定義是基於可達距離的,對於數據點 p,那些跟 點 p的距離小於等於 K-distance(p) 的數據點稱為它的 K-nearest-neighbor,記為Nk(p),數據點p的局部可達密度為它與鄰近的數據點的平均可達距離的倒數。
點 p 的局部可達密度表示為:
表示點 p 的第 k 鄰域內點到 p 的平均可達距離的倒數。
注意:是 p 的鄰域點 Nk(p)到 p的可達距離,不是 p 到 Nk(p) 的可達距離,一定要弄清楚關系。並且,如果有重復點,那么分母的可達距離之和有可能為0,則會導致 ird 變為無限大,下面還會繼續提到這一點。
這個值的含義可以這樣理解,首先這代表一個密度,密度越高,我們認為越可能屬於同一簇,密度越低,越可能是離群點,如果 p 和 周圍鄰域點是同一簇,那么可達距離越可能為較小的 dk(o),導致可達距離之和較小,密度值較高;如果 p 和周圍鄰居點較遠,那么可達距離可能都會取較大值 d(p, o),導致密度較小,越可能是離群點。
(6) local outlier factor:局部離群因子
Local Outlier Factor:根據局部可達密度的定義,如果一個數據點根其他點比較疏遠的話,那么顯然它的局部可達密度就小。但LOF算法衡量一個數據點的異常程度,並不是看他的絕對局部密度,而是它看跟周圍鄰近的數據點的相對密度。這樣做的好處是可以允許數據分布不均勻,密度不同的情況。局部異常因子既是用局部相對密度來定義的。數據點 p 的局部相對密度(局部異常因子)為點 p 的鄰居們的平均局部可達密度跟數據點 p 的局部可達密度的比值。
點 p 的局部離群因子表示為:
表示點 p 的鄰域點 Nk(p) 的局部可達密度與點 p的局部可達密度之比的平均數。
LOF 主要通過計算一個數值 score 來反映一個樣本的異常程度。這個數值的大致意思是:一個樣本點周圍的樣本點所處位置的平均密度比上該樣本點所在位置的密度。如果這個比值越接近1,說明 p 的其鄰域點密度差不多, p 可能和鄰域同屬一簇;如果這個比值越小於1,說明 p 的密度高於其鄰域點目睹,p 為密度點;如果這個比值越大於1,說明 p 的密度小於其鄰域點密度, p 越可能是異常點。
所以了解了上面LOF一大堆定義,我們在這里簡單整理一下此算法:
- 1,對於每個數據點,計算它與其他所有點的距離,並按從近到遠排序
- 2,對於每個數據點,找到它的K-Nearest-Neighbor,計算LOF得分
1.3 算法應用
LOF 算法中關於局部可達密度的定義其實暗含了一個假設,即:不存在大於等於k個重復的點。當這樣的重復點存在的時候,這些點的平均可達距離為零,局部可達密度就變為無窮大,會給計算帶來一些麻煩。在實際應用中,為了避免這樣的情況出現,可以把 K-distance改為 K-distinct-distance,不考慮重復的情況。或者,還可以考慮給可達距離都加一個很小的值,避免可達距離等於零。
LOF算法需要計算數據點兩兩之間的距離,造成整個算法時間復雜度為 O(n**2)。為了提高算法效率,后續有算法嘗試改進。FastLOF(Goldstein, 2012)先將整個數據隨機的分成多個子集,然后在每個子集里計算 LOF值。對於那些LOF異常得分小於等於1的。從數據集里剔除,剩下的在下一輪尋找更合適的 nearest-neighbor,並更新LOF值。這種先將數據粗略分為多個部分,然后根據局部計算結果將數據過濾減少計算量的想法,並不罕見。比如,為了改進 K-Means的計算效率,Canopy Clustering算法也采用過比較相似的做法。
2,LOF算法應用(sklearn實現)
2.1 sklearn 中LOF庫介紹
Unsupervised Outlier Detection using Local Outlier Factor (LOF)。
The anomaly score of each sample is called Local Outlier Factor. It measures the local deviation of density of a given sample with respect to its neighbors. It is local in that the anomaly score depends on how isolated the object is with respect to the surrounding neighborhood. More precisely, locality is given by k-nearest neighbors, whose distance is used to estimate the local density. By comparing the local density of a sample to the local densityes of its neighbors, one can identify samples that have s substantially lower density than their neighbors. These are considered outliers.
局部離群點因子為每個樣本的異常分數,主要是通過比較每個點 p 和其鄰域點的密度來判斷該點是否為異常點,如果點p的密度越低,越可能被認定是異常點。至於密度,是通過點之間的距離計算的,點之間的距離越遠,密度越低,距離越近,密度越高。而且,因為LOF對密度的是通過點的第 k 鄰域來計算,而不是全局計算,因此得名 “局部”異常因子。
Sklearn中LOF在 neighbors 里面,其源碼如下:
LOF的中主要參數含義:
- n_neighbors:設置k,default=20
- contamination:設置樣本中異常點的比例,default=auto
LOF的主要屬性:
補充一下這里的 negative_outlier_factor_:和LOF相反的值,值越小,越有可能是異常值。(LOF的值越接近1,越有可能是正常樣本,LOF的值越大於1,則越有可能是異常樣本)
LOF的主要方法:
2.2 LOF算法實戰
實例1:在一組數中找異常點
代碼如下:
import numpy as np from sklearn.neighbors import LocalOutlierFactor as LOF X = [[-1.1], [0.2], [100.1], [0.3]] clf = LOF(n_neighbors=2) res = clf.fit_predict(X) print(res) print(clf.negative_outlier_factor_) ''' 如果 X = [[-1.1], [0.2], [100.1], [0.3]] [ 1 1 -1 1] [ -0.98214286 -1.03703704 -72.64219576 -0.98214286] 如果 X = [[-1.1], [0.2], [0.1], [0.3]] [-1 1 1 1] [-7.29166666 -1.33333333 -0.875 -0.875 ] 如果 X = [[0.15], [0.2], [0.1], [0.3]] [ 1 1 1 -1] [-1.33333333 -0.875 -0.875 -1.45833333] '''
我們可以發現,隨着數字的改變,它的異常點也在變,無論怎么變,都是基於鄰域密度比來衡量。
實例2:Outlier detection
(outlier detection:當訓練數據中包含離群點,模型訓練時要匹配訓練數據的中心樣本,忽視訓練樣本的其他異常點)
The Local Outlier Factor(LOF) algorithm is an unsupervised anomaly detection method which computes the local density deviation of a given data point with respect to its neighbors. It considers as outliers the samples that have a substantially lower density than their neighbors.
This example shows how to use LOF for outlier detection which is the default use case of this estimator in sklearn。Note that when LOF is used for outlier detection it has no predict, decision_function and score_samples methods.
The number of neighbors considered(parameter n_neighbors)is typically set 1) greater than the minimum number of samples a cluster has to contain, so that other samples can be local outliers relative to this cluster , and 2) smaller than the maximum number of close by samples that can potentially be local outliers. In practice, such informations are generally not available and taking n_neighbors=20 appears to work well in general.
鄰居的數量考慮(參數 n_neighbors通常設置為:
- 1) 大於一個集群包含最小數量的樣本,以便其他樣本可以局部離群
- 2) 小於附加的最大數量樣本,可以局部離群值
在實踐中,這種信息一般是不可用的,n_neighbors=20 似乎實踐很好。
代碼:
#_*_coding:utf-8_*_ import numpy as np from sklearn.neighbors import LocalOutlierFactor as LOF import matplotlib.pyplot as plt # generate train data X_inliers = 0.3 * np.random.randn(100, 2) X_inliers = np.r_[X_inliers + 2, X_inliers - 2] # generate some outliers X_outliers = np.random.uniform(low=-4, high=4, size=(20, 2)) X = np.r_[X_inliers, X_outliers] n_outliers = len(X_outliers) # 20 ground_truth = np.ones(len(X), dtype=int) ground_truth[-n_outliers:] = -1 # fit the model for outlier detection clf = LOF(n_neighbors=20, contamination=0.1) # use fit_predict to compute the predicted labels of the training samples y_pred = clf.fit_predict(X) n_errors = (y_pred != ground_truth).sum() X_scores = clf.negative_outlier_factor_ plt.title('Locla Outlier Factor (LOF)') plt.scatter(X[:, 0], X[:, 1], color='k', s=3., label='Data points') # plot circles with radius proportional to thr outlier scores 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()
結果如下:
這個圖可能有點復雜。這樣我們將異常點設置為2個,則執行效果:
實例3:Novelty detection
(novelty detection:當訓練數據中沒有離群點,我們的目的是用訓練好的模型去檢測另外發現的新樣本。)
The Local Outlier Factor(LOF) algorithm is an unsupervised anomaly detection method which computes the local density deviation of a given data point with respect to its neighbors. It considers as outliers the samples that have a substantially lower density than their neighbors.
This example shows how to use LOF for novelty detection .Note that when LOF is used for novelty detection you MUST not use no predict, decision_function and score_samples on the training set as this would lead to wrong result. you must only use these methods on new unseen data(which are not in the training set)
The number of neighbors considered(parameter n_neighbors)is typically set 1) greater than the minimum number of samples a cluster has to contain, so that other samples can be local outliers relative to this cluster , and 2) smaller than the maximum number of close by samples that can potentially be local outliers. In practice, such informations are generally not available and taking n_neighbors=20 appears to work well in general.
代碼如下:
#_*_coding:utf-8_*_ import numpy as np from sklearn.neighbors import LocalOutlierFactor as LOF import matplotlib.pyplot as plt import matplotlib # np.meshgrid() 生成網格坐標點 xx, yy = np.meshgrid(np.linspace(-5, 5, 500), np.linspace(-5, 5, 500)) # generate normal (not abnormal) training observations X = 0.3*np.random.randn(100, 2) X_train = np.r_[X+2, X-2] # generate new normal (not abnormal) observations 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)) # fit the model for novelty detection (novelty=True) clf = LOF(n_neighbors=20, contamination=0.1, novelty=True) clf.fit(X_train) # do not use predict, decision_function and score_samples on X_train # as this would give wrong results but only on new unseen data(not # used in X_train , eg: X_test, X_outliers or the meshgrid) y_pred_test = clf.predict(X_test) y_pred_outliers = clf.predict(X_outliers) ''' ### contamination=0.1 X_test: [ 1 1 1 1 1 1 1 1 1 1 1 1 1 1 -1 1 1 -1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 -1 1 1 -1 1 1] ### contamination=0.01 X_test: [1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1] y_pred_outliers: [-1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1] ''' n_error_test = y_pred_test[y_pred_test == -1].size n_error_outliers = y_pred_outliers[y_pred_outliers == 1].size # plot the learned frontier, the points, and the nearest vectors to the plane Z = clf.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()
效果如下:
對上面模型進行調參,並設置異常點個數為2個,則效果如下:
參考地址:
https://scikit-learn.org/stable/modules/generated/sklearn.neighbors.LocalOutlierFactor.html?highlight=lof
https://blog.csdn.net/YE1215172385/article/details/79766906
https://blog.csdn.net/bbbeoy/article/details/80301211