首頁 > 軟體

8種Python異常檢測演演算法總結

2023-11-01 10:00:28

一、異常檢測簡介

異常檢測是通過資料探勘方法發現與資料集分佈不一致的異常資料,也被稱為離群點、異常值檢測等等。

1.1 異常檢測適用的場景

異常檢測演演算法適用的場景特點有:(1)無標籤或者類別極不均衡;(2)異常資料跟樣本中大多數資料的差異性較大;(3)異常資料在總體資料樣本中所佔的比例很低。常見的應用案例如:

金融領域:從金融資料中識別”欺詐使用者“,如識別信用卡申請欺詐、信用卡盜刷、信貸欺詐等;安全領域:判斷流量資料波動以及是否受到攻擊等等;電商領域:從交易等資料中識別”惡意買家“,如羊毛黨、惡意刷屏團伙;生態災難預警:基於天氣指標資料,判斷未來可能出現的極端天氣;醫療監控:從醫療裝置資料,發現可能會顯示疾病狀況的異常資料;

1.2 異常檢測存在的挑戰

異常檢測是熱門的研究領域,但由於異常存在的未知性、異質性、特殊性及多樣性等複雜情況,整個領域仍有較多的挑戰:

1)最具挑戰性的問題之一是難以實現高異常檢測召回率。由於異常非常罕見且具有異質性,因此很難識別所有異常。

2)異常檢測模型要提高精確度(precision)往往要深度結合業務特徵,否則效果不佳,且容易導致對少數群體產生演演算法偏見。

二、異常檢測方法

按照訓練集是否包含異常值可以劃分為異常值檢測(outlier detection)及新穎點檢測(novelty detection),新穎點檢測的代表方法如one class SVM。

按照異常類別的不同,異常檢測可劃分為:異常點檢測(如異常消費使用者),上下文異常檢測(如時間序列異常),組異常檢測(如異常團伙)。

按照學習方式的不同,異常檢測可劃分為:有監督異常檢測(Supervised Anomaly Detection)、半監督異常檢測(Semi-Supervised Anomaly Detection)及無監督異常檢測(Unsupervised Anomaly Detection)。現實情況的異常檢測問題,由於收集異常標籤樣本的難度大,往往是沒有標籤的,所以無監督異常檢測應用最為廣泛。

無監督異常檢測按其演演算法思想大致可分為如下下幾類:

2.1 基於聚類的方法

基於聚類的異常檢測方法通常依賴下列假設,1)正常資料範例屬於資料中的一個簇,而異常資料範例不屬於任何簇;2)正常資料範例靠近它們最近的簇質心,而異常資料離它們最近的簇質心很遠;3)正常資料範例屬於大而密集的簇,而異常資料範例要麼屬於小簇,要麼屬於稀疏簇;通過將資料歸分到不同的簇中,異常資料則是那些屬於小簇或者不屬於任何一簇或者遠離簇中心的資料。

  • 將距離簇中心較遠的資料作為異常點:這類方法有 SOM、K-means、最大期望( expectation maximization,EM)及基於語意異常因子( semantic anomaly factor)演演算法等;
  • 將聚類所得小簇資料作為異常點:代表方法有K-means聚類;
  • 將不屬於任何一簇作為異常點:代表方法有 DBSCAN、ROCK、SNN 聚類。

2.2 基於統計的方法

基於統計的方法依賴的假設是資料集服從某種分佈( 如正態分佈、泊松分佈及二項式分佈等) 或概率模型,通過判斷某資料點是否符合該分佈/模型( 即通過小概率事件的判別) 來實現異常檢測。根據概率模型可分為:

1)引數方法,由已知分佈的資料中估計模型引數( 如高斯模型) ,其中最簡單的引數異常檢測模型就是假設樣本服從一元正態分佈,當資料點與均值差距大於兩倍或三倍方差時,則認為該點為異常;

2)非引數方法,在資料分佈未知時,可繪製直方圖通過檢測資料是否在訓練集所產生的直方圖中來進行異常檢測。還可以利用資料的變異程度( 如均差、標準差、變異係數、四分位數間距等) 來發現資料中的異常點資料。

2.3 基於深度的方法

該方法將資料對映到 k 維空間的分層結構中,並假設異常值分佈在外圍,而正常資料點靠近分層結構的中心(深度越高)。

半空間深度法( ISODEPTH 法) ,通過計算每個點的深度,並根據深度值判斷異常資料點。

最小橢球估計 ( minimum volume ellipsoid estimator,MVE)法。根據大多數資料點( 通常為 > 50% ) 的概率分佈模型擬合出一個實線橢圓形所示的最小橢圓形球體的邊界,不在此邊界範圍內的資料點將被判斷為異常點。

孤立森林。上述兩種基於深度的基礎模型隨著特徵維度k的增加,其時間複雜性呈指數增長,通常適用於維度k≤3 時,而孤立森林通過改變計算深度的方式,也可以適用於高維的資料。

孤立森林演演算法是基於 Ensemble 的異常檢測方法,因此具有線性的時間複雜度。且精準度較高,在處理巨量資料時速度快,所以目前在工業界的應用範圍比較廣。其基本思想是:通過樹模型方法隨機地切分樣本空間,那些密度很高的簇要被切很多次才會停止切割(即每個點都單獨存在於一個子空間內),但那些分佈稀疏的點(即異常點),大都很早就停到一個子空間內了。演演算法步驟為:

1)從訓練資料中隨機選擇 Ψ 個樣本,以此訓練單棵樹。

2)隨機指定一個q維度(attribute),在當前節點資料中隨機產生一個切割點p。p切割點產生於當前節點資料中指定q維度的最大值和最小值之間。

3)在此切割點的選取生成了一個超平面,將當前節點資料空間切分為2個子空間:把當前所選維度下小於 p 的點放在當前節點的左分支,把大於等於 p 的點放在當前節點的右分支;

4)在節點的左分支和右分支節點遞迴步驟 2、3,不斷構造新的葉子節點,直到葉子節點上只有一個資料(無法再繼續切割) 或樹已經生長到了所設定的高度 。(設定單顆樹的最大高度是因為異常資料記錄都比較少,其路徑長度也比較低,而我們也只需要把正常記錄和異常記錄區分開來,因此只需要關心低於平均高度的部分就好,這樣演演算法效率更高。)

5)由於每顆樹訓練的切割特徵空間過程是完全隨機的,所以需要用 ensemble 的方法來使結果收斂,即多建立幾棵樹,然後綜合計算每棵樹切分結果的平均值。對於每個樣本 x,通過下面的公式計算綜合的異常得分s。

h(x) 為 x 在每棵樹的高度,c(Ψ) 為給定樣本數 Ψ 時路徑長度的平均值,用來對樣本 x 的路徑長度 h(x) 進行標準化處理。

2.4 基於分類模型

代表方法是One class SVM,其原理是尋找一個超平面將樣本中的正例圈出來,預測就是用這個超平面做決策,在圈內的樣本就認為是正樣本。由於核函數計算比較耗時,在海量資料的場景用的並不多。

依賴的假設是:正常資料範例位於密集的鄰域中,而異常資料範例附近的樣例較為稀疏。可以繼續細分為 基於密度/鄰居:

基於密度,該方法通過計算資料集中各資料區域的密度,將密度較低區域作為離群區域。經典的方法為:區域性離群因子( local outlier factor,LOF) 。LOF 法與傳統異常點非彼即此定義不同,將異常點定義局域是異常點,為每個資料賦值一個代表相對於其鄰域的 LOF 值,LOF 越大,說明其鄰域密度較低,越有可能是異常點。但在 LOF 中難以確定最小近鄰域,且隨著資料維度的升高,計算複雜度和時間複雜度增加。

基於距離,其基本思想是通過計算比較資料與近鄰資料集合的距離來檢測異常,正常資料點與其近鄰資料相似,而異常資料則有別於近鄰資料。

2.5 基於偏差的方法

當給定一個資料集時,可通過基於偏差法找出與整個資料集特徵不符的點,並且資料集方差會隨著異常點的移除而減小。該方法可分為逐個比較資料點的序列異常技術和 OLAP 資料立方體技術。目前該方法實際應用較少。

2.6 基於重構的方法

代表方法為PCA。PCA在異常檢測方面的做法,大體有兩種思路:一種是將資料對映到低維特徵空間,然後在特徵空間不同維度上檢視每個資料點跟其它資料的偏差;另外一種是將資料對映到低維特徵空間,然後由低維特徵空間重新對映回原空間,嘗試用低維特徵重構原始資料,看重構誤差的大小。

2.7 基於神經網路的方法

代表方法有自動編碼器( autoencoder,AE) ,長短期記憶神經網路(LSTM)等。

LSTM可用於時間序列資料的異常檢測:利用歷史序列資料訓練模型,檢測與預測值差異較大的異常點。

Autoencoder異常檢測 Autoencoder本質上使用了一個神經網路來產生一個高維輸入的低維表示。Autoencoder與主成分分析PCA類似,但是Autoencoder在使用非線性啟用函數時克服了PCA線性的限制。演演算法的基本上假設是異常點服從不同的分佈。根據正常資料訓練出來的Autoencoder,能夠將正常樣本重建還原,但是卻無法將異於正常分佈的資料點較好地還原,導致其基於重構誤差較大。當重構誤差大於某個閾值時,將其標記為異常值。

小結:無監督異常檢測方法的要素為選擇相關的特徵以及基於合理假設選擇合適的演演算法,可以更好的發揮異常檢測效果。

三、專案實戰:信用卡反欺詐

專案為kaggle上經典的信用卡欺詐檢測,該資料集質量高,正負樣本比例非常懸殊。我們在此專案主要用了無監督的Autoencoder新穎點檢測,根據重構誤差識別異常欺詐樣本。

#!/usr/bin/env python  
# coding: utf-8  
  
import warnings  
warnings.filterwarnings("ignore")  
  
import pandas as pd  
import numpy as np  
import pickle  
import matplotlib.pyplot as plt  
plt.style.use('seaborn')  
import tensorflow as tf  
import seaborn as sns  
from sklearn.model_selection import train_test_split  
from keras.models import Model, load_model  
from keras.layers import Input, Dense  
from keras.callbacks import ModelCheckpoint  
from keras import regularizers  
from sklearn.preprocessing import StandardScaler  
from sklearn.metrics import roc_curve, auc, precision_recall_curve  
# 安利一個異常檢測Python庫 https://github.com/yzhao062/Pyod  
  
# 讀取資料 :信用卡欺詐資料集地址https://www.kaggle.com/mlg-ulb/creditcardfraud  
d = pd.read_csv('creditcard.csv')  
  
# 檢視樣本比例  
num_nonfraud = np.sum(d['Class'] == 0)  
num_fraud = np.sum(d['Class'] == 1)  
plt.bar(['Fraud', 'non-fraud'], [num_fraud, num_nonfraud], color='dodgerblue')  
plt.show()  
  
# 刪除時間列,對Amount進行標準化  
data = d.drop(['Time'], axis=1)  
data['Amount'] = StandardScaler().fit_transform(data[['Amount']])  
  
# 為無監督新穎點檢測方法,只提取負樣本,並且按照8:2切成訓練集和測試集  
mask = (data['Class'] == 0)  
X_train, X_test = train_test_split(data[mask], test_size=0.2, random_state=0)  
X_train = X_train.drop(['Class'], axis=1).values  
X_test = X_test.drop(['Class'], axis=1).values  
  
# 提取所有正樣本,作為測試集的一部分  
X_fraud = data[~mask].drop(['Class'], axis=1).values  
  
# 構建Autoencoder網路模型  
# 隱藏層節點數分別為16,8,8,16  
# epoch為5,batch size為32  
input_dim = X_train.shape[1]  
encoding_dim = 16  
num_epoch = 5  
batch_size = 32  
  
input_layer = Input(shape=(input_dim, ))  
encoder = Dense(encoding_dim, activation="tanh",   
                activity_regularizer=regularizers.l1(10e-5))(input_layer)  
encoder = Dense(int(encoding_dim / 2), activation="relu")(encoder)  
decoder = Dense(int(encoding_dim / 2), activation='tanh')(encoder)  
decoder = Dense(input_dim, activation='relu')(decoder)  
autoencoder = Model(inputs=input_layer, outputs=decoder)  
autoencoder.compile(optimizer='adam',   
                    loss='mean_squared_error',   
                    metrics=['mae'])  
  
# 模型儲存為model.h5,並開始訓練模型  
checkpointer = ModelCheckpoint(filepath="model.h5",  
                               verbose=0,  
                               save_best_only=True)  
history = autoencoder.fit(X_train, X_train,  
                          epochs=num_epoch,  
                          batch_size=batch_size,  
                          shuffle=True,  
                          validation_data=(X_test, X_test),  
                          verbose=1,   
                          callbacks=[checkpointer]).history  
  
  
# 畫出損失函數曲線  
plt.figure(figsize=(14, 5))  
plt.subplot(121)  
plt.plot(history['loss'], c='dodgerblue', lw=3)  
plt.plot(history['val_loss'], c='coral', lw=3)  
plt.title('model loss')  
plt.ylabel('mse'); plt.xlabel('epoch')  
plt.legend(['train', 'test'], loc='upper right')  
  
plt.subplot(122)  
plt.plot(history['mae'], c='dodgerblue', lw=3)  
plt.plot(history['val_mae'], c='coral', lw=3)  
plt.title('model mae')  
plt.ylabel('mae'); plt.xlabel('epoch')  
plt.legend(['train', 'test'], loc='upper right')  
  
  
# 讀取模型  
autoencoder = load_model('model.h5')  
  
# 利用autoencoder重建測試集  
pred_test = autoencoder.predict(X_test)  
# 重建欺詐樣本  
pred_fraud = autoencoder.predict(X_fraud)    
  
# 計算重構MSE和MAE誤差  
mse_test = np.mean(np.power(X_test - pred_test, 2), axis=1)  
mse_fraud = np.mean(np.power(X_fraud - pred_fraud, 2), axis=1)  
mae_test = np.mean(np.abs(X_test - pred_test), axis=1)  
mae_fraud = np.mean(np.abs(X_fraud - pred_fraud), axis=1)  
mse_df = pd.DataFrame()  
mse_df['Class'] = [0] * len(mse_test) + [1] * len(mse_fraud)  
mse_df['MSE'] = np.hstack([mse_test, mse_fraud])  
mse_df['MAE'] = np.hstack([mae_test, mae_fraud])  
mse_df = mse_df.sample(frac=1).reset_index(drop=True)  
  
# 分別畫出測試集中正樣本和負樣本的還原誤差MAE和MSE  
markers = ['o', '^']  
markers = ['o', '^']  
colors = ['dodgerblue', 'coral']  
labels = ['Non-fraud', 'Fraud']  
  
plt.figure(figsize=(14, 5))  
plt.subplot(121)  
for flag in [1, 0]:  
    temp = mse_df[mse_df['Class'] == flag]  
    plt.scatter(temp.index,   
                temp['MAE'],    
                alpha=0.7,   
                marker=markers[flag],   
                c=colors[flag],   
                label=labels[flag])  
plt.title('Reconstruction MAE')  
plt.ylabel('Reconstruction MAE'); plt.xlabel('Index')  
plt.subplot(122)  
for flag in [1, 0]:  
    temp = mse_df[mse_df['Class'] == flag]  
    plt.scatter(temp.index,   
                temp['MSE'],    
                alpha=0.7,   
                marker=markers[flag],   
                c=colors[flag],   
                label=labels[flag])  
plt.legend(loc=[1, 0], fontsize=12); plt.title('Reconstruction MSE')  
plt.ylabel('Reconstruction MSE'); plt.xlabel('Index')  
plt.show()  
# 下圖分別是MAE和MSE重構誤差,其中橘黃色的點是信用欺詐,也就是異常點;藍色是正常點。我們可以看出異常點的重構誤差整體很高。  
  
# 畫出Precision-Recall曲線  
plt.figure(figsize=(14, 6))  
for i, metric in enumerate(['MAE', 'MSE']):  
    plt.subplot(1, 2, i+1)  
    precision, recall, _ = precision_recall_curve(mse_df['Class'], mse_df[metric])  
    pr_auc = auc(recall, precision)  
    plt.title('Precision-Recall curve based on %snAUC = %0.2f'%(metric, pr_auc))  
    plt.plot(recall[:-2], precision[:-2], c='coral', lw=4)  
    plt.xlabel('Recall'); plt.ylabel('Precision')  
plt.show()  
  
# 畫出ROC曲線  
plt.figure(figsize=(14, 6))  
for i, metric in enumerate(['MAE', 'MSE']):  
    plt.subplot(1, 2, i+1)  
    fpr, tpr, _ = roc_curve(mse_df['Class'], mse_df[metric])  
    roc_auc = auc(fpr, tpr)  
    plt.title('Receiver Operating Characteristic based on %snAUC = %0.2f'%(metric, roc_auc))  
    plt.plot(fpr, tpr, c='coral', lw=4)  
    plt.plot([0,1],[0,1], c='dodgerblue', ls='--')  
    plt.ylabel('TPR'); plt.xlabel('FPR')  
plt.show()  
# 不管是用MAE還是MSE作為劃分標準,模型的表現都算是很好的。PR AUC分別是0.51和0.44,而ROC AUC都達到了0.95。  
  
# 畫出MSE、MAE散點圖  
markers = ['o', '^']  
colors = ['dodgerblue', 'coral']  
labels = ['Non-fraud', 'Fraud']  
  
plt.figure(figsize=(10, 5))  
for flag in [1, 0]:  
    temp = mse_df[mse_df['Class'] == flag]  
    plt.scatter(temp['MAE'],   
                temp['MSE'],    
                alpha=0.7,   
                marker=markers[flag],   
                c=colors[flag],   
                label=labels[flag])  
plt.legend(loc=[1, 0])  
plt.ylabel('Reconstruction RMSE'); plt.xlabel('Reconstruction MAE')  
plt.show()  

到此這篇關於8種Python異常檢測演演算法總結的文章就介紹到這了,更多相關Python異常檢測演演算法內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com