LOF 演算法全名為Local Outlier Factor,局部離群因子。Outlier[ˋaʊt͵laɪɚ] 是離群值的意思。這是用來判斷異常檢測的方法。LOF 是基於密度的經典演算法,此演算法發表於 SIGMOD 2000。
檢測到異常時,並不是全然要將其值去除,有時反而是要抓住其值。比如地震儀平時在一定範圍震動,突然發生異常大幅震盪時,並不是將其值去除,而是立馬發佈地震警告,記錄震幅並轉換震度。
安裝套件
pip install scikit-learn
產生訓練資料
首先使用 numpy 的 randn 產生 2 行 100 組標準正態分佈亂數。標準常態分佈就是大部份的值都在 0 附近,然後往正及往負慢慢減少,如下代碼及圖示。
import numpy as np import matplotlib.pyplot as plt import seaborn as sns #產生訓練資料 np.random.seed(1) inliers = np.random.randn(100, 2) sns.set_style('whitegrid') ax1=plt.subplot(1,2,1) ax1.set_xlim((-5,5)) ax1.set_ylim((0,30)) sns.histplot(inliers[:,0], kde=True) sns.set_style('whitegrid') ax2=plt.subplot(1,2,2) ax2.set_xlim((-5,5)) ax2.set_ylim((0,30)) sns.histplot(inliers[:,1], kde=True) plt.show()
分成二組
將inliers 分別加3及減3,然後將二組合併,產生分離的二群組。
import numpy as np import matplotlib.pyplot as plt np.random.seed(1) # 產生訓練資料 inliers = np.random.randn(100, 2) inliers = np.r_[inliers + 3, inliers - 3] x=inliers[:,0] y=inliers[:,1] plt.scatter(x, y, s=5, c='k') plt.xlim(-6,6) plt.ylim(-6,6) plt.show()
加入離群資料點
使用 np.random.uniform() 加入 20 點離群資料。uniform 是在指定的範圍中隨機採樣。不過這 20 點不一定會全部離群。底下的 np.r_ 會將 outliers 加到 inliers 之後。
import numpy as np import matplotlib.pyplot as plt # 產生訓練資料 np.random.seed(1) inliers = np.random.randn(100, 2) inliers = np.r_[inliers + 3, inliers - 3] outliers = np.random.uniform(low=-6, high=6, size=(20, 2)) data=np.r_[inliers, outliers] x=data[:,0] y=data[:,1] plt.scatter(x, y, s=3,c='k') plt.xlim(-6,6) plt.ylim(-6,6) plt.savefig("lof_5.jpg") plt.show()
偵測離群點
使用 LOF 產生 lof 物件,參數 n_neighbors 為 k-distance(第 k 距離),使用預設值 20,其說明請參照
K-Mean 詳解。
參數 contamination[kən͵tæməˋneʃən] 是污染,在此為異常點的比例,使用預設的 ‘auto’ 即可。
然後 lof.fit_predict(data)即可偵測每個點的離群狀況 : y_pred。y_pred為一list資料,代表每個點的狀況,1表示未離群,-1 表示離。所以可以使用 np.ones()跟 y_pred相減,讓未離群的點變成0,而離群點變成 2, 再用此資料決定圓圈的半徑。
另外 lof.negative_outlier_factor_ 為每一點的離群分數,負值愈大,代表離群的機會愈大。
在繪制點散圖時 (plt.scatter),facecolors 是圓點的填滿色,’none’ 表示為空心圓,edgecolors 則為圓的邊框顏色。
import numpy as np import matplotlib.pyplot as plt from sklearn.neighbors import LocalOutlierFactor as LOF # 產生訓練資料 #np.random.seed(1) inliers = np.random.randn(100, 2) inliers = np.r_[inliers + 3, inliers - 3] outliers = np.random.uniform(low=-6, high=6, size=(20, 2)) data=np.r_[inliers, outliers]
lof = LOF(n_neighbors=20, contamination='auto') # 使用 fit_predict 進行偵測 y_pred = lof.fit_predict(data)
scores=lof.negative_outlier_factor_ print(y_pred)
print(scores) radius=np.ones(len(y_pred)) radius-=y_pred x=data[:,0] y=data[:,1] plt.scatter(x, y, s=3,c='k') #離群點畫圓圈 plt.scatter(x, y, s=100*radius, edgecolors='r', facecolors='none') plt.xlim(-7,7) plt.ylim(-7,7) plt.savefig("lof_6.jpg") plt.show()
LOF演算法
為什麼 LOF可以這麼輕易的算出離群點呢,這有蠻複雜的演算法,目前因沒啥空檔來撰寫,請大家到網路上Google,待本人比較有空時再來撰寫。