異常值檢測
- 背景
- 高斯分布
- 實踐
- 資料可視化
- 二維高斯分布
- 模型挑選
- 挑選異常值和可視化
- 優缺點分析
- 應用場景
- 參考文獻
背景
無論在自然界還是人類社會生産生活中,都會存在那麼一小撮不随大流的人或事物,這一小撮往往對整體影響很大,如何準确高效的把這一小部分的對象甄别出來俨然稱為一個很具挑戰性的工作。本文将帶你利用高斯分布這一有利工具來進行異常值檢測。
高斯分布
高斯分布又名正态分布,是用來描述随機變量的分布規律,這裡先給出一維高斯分布的定義
如果随機變量X服從一個數學期望值為
μ
\mu
μ、方差為
σ
2
\sigma^2
σ2的機率分布,且機率密度函數為
f
(
x
)
=
1
2
π
σ
e
−
(
x
−
μ
)
2
2
σ
2
f(x) = \frac{1}{\sqrt {2\pi} \sigma} e^{-\frac{(x-\mu)^2}{2\sigma^2}}
f(x)=2π
σ1e−2σ2(x−μ)2
則稱X服從高斯分布,記為
X
∼
N
(
μ
,
σ
2
)
X \thicksim N(μ,σ^2)
X∼N(μ,σ2)
其中數學期望值μ決定其位置,方差
σ
2
\sigma^2
σ2決定其波幅。特别的,當$μ = 0, \sigma^2 = 1 $時的高斯分布是标準正态分布(一般不稱标準高斯分布)。
下面不加證明的給出二維獨立高斯分布的機率密度函數
f
(
x
,
y
)
=
1
2
π
σ
1
σ
2
e
−
[
(
x
−
μ
1
)
2
2
σ
1
2
+
(
x
−
μ
2
)
2
2
σ
2
2
]
f(x,y ) = \frac{1}{{2\pi} \sigma_1 \sigma_2} e^{-[\frac{(x-\mu_1)^2}{2\sigma_1^2}+\frac{(x-\mu_2)^2}{2\sigma_2^2}]}
f(x,y)=2πσ1σ21e−[2σ12(x−μ1)2+2σ22(x−μ2)2]
有意思的是在三維空間中這個函數的圖像投影到平面上是一系列同心橢圓,他們的中心都在
(
u
1
,
u
2
)
(u_1, u_2)
(u1,u2)。
高斯分布是最常見的一種機率分布,科學家認為現實生活中很多現象符合高斯分布,且認為偏離高斯分布的3個标準差之外的是極小可能發生的情況,即異常情況,于是可以利用高斯分布來看資料是否是正常的還是異常的。
實踐
本次以吳恩達第八次作業為例,動手實驗異常值檢測,包括原始資料的可視化,二維高斯分布的機率密度函數及其參數估計,最佳門檻值的標明和異常值的篩選及異常資料的可視化。
資料可視化
我們一直強調如果原始資料能夠可視化,那麼毫不猶豫先可視化,可視乎之後能夠非常直覺的看到資料的大緻分布情況,異常值大概是哪些。
import math
import numpy as np
import matplotlib
import scipy.io as scio
import matplotlib.pyplot as plt
data = scio.loadmat(r"D:\項目\機器學習\吳恩達機器學習課件\CourseraML\ex8\data\ex8data1.mat") #讀取mat資料
X = data["X"] #特征
Xval = data['Xval'] #驗證集
yval = data["yval"] #驗證集對應的标簽
def dataVisual(myX): #資料可視化
plt.figure(figsize = (6, 4)) #建立畫布
plt.scatter(myX[:,0], myX[:,1], c="b", marker="x", linewidths=0.5) #散點圖
plt.xlabel("latency (ms)") #橫坐标标簽
plt.ylabel("throughput (mb/s)") #縱坐标标簽
plt.show()
從原資料的散點圖可以看到大概有那麼一些點的确跟大夥不一樣,離群性嚴重,将其圈起來便是
現在就是要設計一套模式把剛才手動圈起來的點自動識别出來。
二維高斯分布
要自動識别異常點,這時候多元高斯分布能夠為我所用,利用樣本資料來估計高斯分布的參數(
μ
,
σ
2
\mu, \sigma^2
μ,σ2),然後再計算各樣本的高斯機率值,并且還可以把高斯分布機率密度函數的投影畫出來。
def estimateGaussian(myX): #高斯分布的參數估計,即均值和方差(标準差)
m = len(myX) #樣本數
n = myX.shape[1] #特征數
mu = myX.mean(axis = 0) #每個特征的均值
sigma = myX.std(axis =0, ddof = 0) #每個特征的方差,ddof=1表示無偏估計,ddof=0表示無偏估計
return mu, sigma
def gaussianDistribution(myx, myy, mymu1, mymu2, mysigma1, mysigma2): #二維高斯機率密度函數
gaussian = (1/(2*math.pi*mysigma1*mysigma2))*math.e**(-(myx-mymu1)**2/(2*mysigma1**2) - (myy-mymu2)**2/(2*mysigma2**2) )
return gaussian
def plotContour(myX): #等值線
mu, sigma = estimateGaussian(myX) #高斯分布的估計參數
x = np.linspace(np.min(myX[:,0])-1, np.max(myX[:,0])+1, 100) #經度
y = np.linspace(np.min(myX[:,0])-1, np.max(myX[:,1])+1, 100) #緯度
xx, yy = np.meshgrid(x, y) #制作經緯網格線
zz = gaussianDistribution(xx, yy, mu[0], mu[1], sigma[0], sigma[1]) #網格點上的高斯機率值
#print(xx.shape, yy.shape, zz.shape)
cont_levels = [10 ** h for h in range(-20, 1, 3)] #指定的級别繪制輪廓線, 必須按遞增順序排列
plt.contour(xx, yy, zz, cont_levels, linewidths=0.7) #等值線
plt.scatter(myX[:,0], myX[:,1], c="b", marker="x", linewidths=0.5) #原散點圖
plt.show()
模型挑選
雖然多元高斯分布可以識别出異常值,但是怎麼具體确定哪些是異常值,哪些是正常值,并且能夠評估這種異常值自動識别的效果如何?針對第一個問題,可以設定一個門檻值,小于門檻值的斷定為異常值,反之為正常值;針對第二個問題,可以設定一個損失函數,比如f1得分,利用驗證集f1的表現情況來确定最終的門檻值。
def computeF1(y_pred, y_true): #計算f1-score,即損失函數
tp, fp, tn, fn = 0, 0, 0, 0 #初始化
for i in range(len(y_pred)):
if y_pred[i]==y_true[i]:
if y_true[i]==1:
tp +=1
else:
tn +=1
else:
if y_true[i] ==1:
fn +=1
else:
fp+=1
precision = tp/(tp+fp) if tp+fp else 0 #精确率
recall = tp/(tp+fn) if tp+fn else 0 #召回率
f1 = 2*precision*recall/(precision+recall) if precision+recall else 0 #f1得分
return f1
def selectThreshold(yval, pval): #通過驗證集來選擇最佳的門檻值
epsilons = np.linspace(np.min(pval), np.max(pval), 1000)
best_f1, best_epsilon = 0, 0 #初始化
for e in epsilons: #從極小機率值到極大機率值
y_pred = (pval < e).astype(float) #如果小于門檻值則認為是異常值
f1 = computeF1(y_pred, yval) #計算f1值
if f1 > best_f1: #如果f1值有所提升
best_f1 = f1 #更新f1
best_epsilon = e #更新epsilon
return best_f1, best_epsilon
挑選異常值和可視化
知道哪些是異常值,哪些是非異常值,就能夠把這些異常值樣本挑出來,并打上标簽。
def abnormlyVisual(myX, myepsilon, mymu, mysigma): #先挑出異常值并可視化标記出來
guassian = gaussianDistribution(myX[:,0], myX[:,1], mu[0], mu[1], sigma[0], sigma[1]) #計算高斯機率分布函數值
abnorm_points = np.array([myX[i] for i in range(len(guassian)) if guassian[i]< myepsilon]) #跳出異常值點
plt.figure() #建立畫布
plt.scatter(myX[:,0], myX[:,1], c="b", marker="x", linewidths=0.5) #原散點圖
plt.scatter(abnorm_points[:,0], abnorm_points[:,1], facecolor="w", edgecolors="r", marker="o") #異常值标注
plt.show()
大緻經過這樣幾個步驟,就完成了異常值檢測及其可視化工作了。
優缺點分析
(1) 理論通俗,操作簡單
異常值檢測主要利用了高斯分布理論來界定哪些是異常值,哪些是非異常值,理論比較通俗,操作起來也比較簡單。
(2) 不具備通用性
雖然很多現象符合高斯分布,是從人類的經驗中總結出來的規律,然而規律始終是規律,不能嚴謹證明,不能當成公理來使,現實生活中肯定還有非高斯分布的現象,這時候利用高斯分布來區分異常值和非異常值是缺乏科學性的,不符合真實情況。
應用場景
(1) 資料預處理
在資料預處理的時候需要對異常值進行剔除,正常做法是利用箱線圖的上下四分位點的1.5倍内距來框定,而箱線圖的理論基礎也是高斯分布,與異常值識别的理論基礎是一緻,是以在資料預處理的時候可以利用今天所講的異常值處理辦法;
(2) 風險控制