Anomaly Detection異常檢測的幾種方法

異常檢測首先要先根據(jù)業(yè)務(wù)情況確定什么是異常數(shù)據(jù),再選擇合適的方法進(jìn)行算法實(shí)現(xiàn)。通常來說可以考慮如下幾種方法:

  1. PCA主成分分析
  2. Isolation Forest
  3. Autoencoder
  4. Classification

1.PCA主成分分析在上一篇文里寫過了

  1. Isolation Forest其實(shí)很簡單,可以理解為無監(jiān)督的隨機(jī)森林算法。他的基本原理是利用樹模型把數(shù)據(jù)進(jìn)行分割,一直分到只有一個獨(dú)立點(diǎn)為止。越快分割成單獨(dú)數(shù)據(jù)點(diǎn),說明這個數(shù)據(jù)越異常。
model = IsolationForest(n_jobs=8, contamination=ATTACK_FRACTION, max_features=8, n_estimators=1024)
#Define train data
train_data = get_train_data(df_full)
#Fit model
model.fit(train_data[numeric_features])

results = model.predict(X[numeric_features])
results = (results == -1)
  1. Autoencoder
    先引用一篇英文博客,寫得很全面,并且提供了代碼,下面內(nèi)容主要是搬運(yùn)工!https://medium.com/@curiousily/credit-card-fraud-detection-using-autoencoders-in-keras-tensorflow-for-hackers-part-vii-20e0c85301bd
    傳統(tǒng)的維度下降依賴于線性方法,如 PCA,找出高維數(shù)據(jù)中最大的方差的方向。通過選擇這些方向,PCA 本質(zhì)上刻畫了包含了最終信息的方向。所以我們可以找到一個較小的維度的數(shù)目來作為降維的結(jié)果。然而,PCA 方法的線性性也是導(dǎo)致自身可以抽取出的特征維度類型上的很大限制。Autoencoder通過引入神經(jīng)網(wǎng)絡(luò)天生的非線性性克服這些限制。
    Screen Shot 2018-09-04 at 10.54.30 PM.png

步驟主要是:
3.0

import pandas as pd
import numpy as np
import pickle
import matplotlib.pyplot as plt
from scipy import stats
import tensorflow as tf
import seaborn as sns
from pylab import rcParams
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, TensorBoard
from keras import regularizers

%matplotlib inline

sns.set(style='whitegrid', palette='muted', font_scale=1.5)

rcParams['figure.figsize'] = 14, 8

RANDOM_SEED = 42
LABELS = ["Normal", "Fraud"]

3.1 把data去掉label,進(jìn)行標(biāo)準(zhǔn)化處理。

from sklearn.preprocessing import StandardScaler
data = df.drop(['Time'], axis=1)
data['Amount'] = StandardScaler().fit_transform(data['Amount'].values.reshape(-1, 1))

3.2 數(shù)據(jù)切分,分成x_train, x_test, y_test. 注意在訓(xùn)練的時候只用x_train, 不需要y_train當(dāng)label, 因為這是一種無監(jiān)督的學(xué)習(xí)方法

X_train, X_test = train_test_split(data, test_size=0.2, random_state=RANDOM_SEED)
X_train = X_train[X_train.Class == 0]
X_train = X_train.drop(['Class'], axis=1)

y_test = X_test['Class']
X_test = X_test.drop(['Class'], axis=1)

X_train = X_train.values
X_test = X_test.values

3.3 建模

input_dim = X_train.shape[1]
encoding_dim = 14
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)

訓(xùn)練模型

nb_epoch = 100
batch_size = 32

autoencoder.compile(optimizer='adam', 
                    loss='mean_squared_error', 
                    metrics=['accuracy'])

checkpointer = ModelCheckpoint(filepath="model.h5",
                               verbose=0,
                               save_best_only=True)
tensorboard = TensorBoard(log_dir='./logs',
                          histogram_freq=0,
                          write_graph=True,
                          write_images=True)

history = autoencoder.fit(X_train, X_train,
                    epochs=nb_epoch,
                    batch_size=batch_size,
                    shuffle=True,
                    validation_data=(X_test, X_test),
                    verbose=1,
                    callbacks=[checkpointer, tensorboard]).history

3.4 用模型計算輸出X與輸入X相比較的誤差。這里的預(yù)測結(jié)果并不是我們最終想要的label,而是跟輸入的X矩陣一樣形狀的數(shù)據(jù)集。我們需要對這個輸出集求error集合。

predictions = autoencoder.predict(X_test)
mse = np.mean(np.power(X_test - predictions, 2), axis=1)
error_df = pd.DataFrame({'reconstruction_error': mse,
                        'true_class': y_test})
error_df.describe()

3.5 進(jìn)一步預(yù)測
模型本身并不知道如何預(yù)測新的數(shù)據(jù)是被歸為0還是1。我們通過上一步計算了重構(gòu)誤差,在這一步則需要自定義一個合理的閾值,以此為基礎(chǔ)判斷3.4步驟里由模型算出來的誤差,如果error大于閾值,則歸為1,否則歸為0。

threshold = defined_number # 這里需要主觀定義,但可以用sklearn的metrics模塊通過畫圖做到心中有數(shù)。
groups = error_df.groupby('true_class')
fig, ax = plt.subplots()

for name, group in groups:
    ax.plot(group.index, group.reconstruction_error, marker='o', ms=3.5, linestyle='',
            label= "Fraud" if name == 1 else "Normal")
ax.hlines(threshold, ax.get_xlim()[0], ax.get_xlim()[1], colors="r", zorder=100, label='Threshold')
ax.legend()
plt.title("Reconstruction error for different classes")
plt.ylabel("Reconstruction error")
plt.xlabel("Data point index")
plt.show();
Screen Shot 2018-09-04 at 11.22.34 PM.png

當(dāng)然也可以畫出混淆矩陣來量化模型效果:

y_pred = [1 if e > threshold else 0 for e in error_df.reconstruction_error.values]
conf_matrix = confusion_matrix(error_df.true_class, y_pred)

plt.figure(figsize=(12, 12))
sns.heatmap(conf_matrix, xticklabels=LABELS, yticklabels=LABELS, annot=True, fmt="d");
plt.title("Confusion matrix")
plt.ylabel('True class')
plt.xlabel('Predicted class')
plt.show()

另外,以上幾種方法,都可能會面臨數(shù)據(jù)不平衡的問題,即class imbalance. 比如訓(xùn)練集中的異常數(shù)據(jù)比例及少,這種時候機(jī)器學(xué)習(xí)的模型可能算出來的Accuracy很好,但是實(shí)際上卻是無效的。這種情況下有兩個層面的解決:

數(shù)據(jù)層面的考慮:

  1. 擴(kuò)大數(shù)據(jù)集。
  2. 采樣數(shù)據(jù)。
    隨機(jī)過采樣(Over-Sampling)通過隨機(jī)復(fù)制少數(shù)類來增加其實(shí)例數(shù)量。缺點(diǎn)是過擬合,優(yōu)點(diǎn)是不會有信息丟失,表現(xiàn)好于下采樣。這里面又有升級版的SMOTE(Synthetic Minority Over-sampling Technique)方法。
    隨機(jī)欠采樣(Under-Sampling)通過隨機(jī)地消除占多數(shù)的類的樣本來平衡類分布,直到多數(shù)類和少數(shù)類的實(shí)例實(shí)現(xiàn)平衡,目標(biāo)才算達(dá)成。優(yōu)點(diǎn)是針對大數(shù)據(jù)量可以解決存儲問題,提升運(yùn)行時間。缺點(diǎn)是去掉了一些信息,結(jié)果可能不精確。

模型層面的考慮:

  1. sklearn grid search調(diào)參的時候避免采用默認(rèn)的accuracy score做指標(biāo),而改為F或precision。關(guān)于如何自定義模型評價指標(biāo)可以看官網(wǎng)http://scikit-learn.org/stable/modules/model_evaluation.html。
  2. 修改現(xiàn)有的分類算法,使其適用于不平衡數(shù)據(jù)集。主要考慮bagging和boosting模型。
    bagging: 一個最簡單的集成方法就是不斷從多數(shù)類中抽取樣本,使得每個模型的多數(shù)類樣本數(shù)量和少數(shù)類樣本數(shù)量都相同,最后將這些模型集成起來。集成方法的主要目的是提高單個分類器的性能。該方法從原始數(shù)據(jù)中構(gòu)建幾個兩級分類器,然后整合它們的預(yù)測。
    bagging關(guān)注在減小方差/克服過擬合,比如RF每一個樹都是很深的深度,所以每一個樹都有低偏差。投票之后,樹越多,就可以讓方差也越低。優(yōu)點(diǎn)是在嘈雜的數(shù)據(jù)環(huán)境下,它性能優(yōu)于boosting。缺點(diǎn)是bagging只在基分類器效果很好時才有效,錯誤的分類可能會進(jìn)一步降低表現(xiàn)。

boosting的基分類器是弱學(xué)習(xí)器,即預(yù)測準(zhǔn)確度僅略好于平均水平,每一個分類器方差低但偏差高。每一輪迭代都在上一個弱分關(guān)器的基礎(chǔ)上調(diào)整權(quán)重,以達(dá)到總體集成分類器可以更好的fit樣本,所以其目標(biāo)是使總體的偏差降低。
按發(fā)明先后時間來介紹Adboost, Gradient Boosting, XGBoost:
Adboost最早,可以泛化得很好,適合分類問題且不易過擬合但是對嗓聲數(shù)據(jù)敏感。
GBDT里的樹都是回歸樹,不能是分類樹,因為它的核心是每一棵樹學(xué)的是之前所有樹結(jié)論和的殘差,這個殘差就是一個加預(yù)測值后能得真實(shí)值的累加量。那么哪里體現(xiàn)了Gradient呢?并沒有用到求導(dǎo)的梯度,只是用殘差作為全局最優(yōu)的絕對方向,這就是Gradient。GBDT的缺點(diǎn)是不好擬合。
XGboost是GB的更先進(jìn)的實(shí)現(xiàn),它對目標(biāo)函數(shù)做了二階導(dǎo),并使用正則化和特征分塊存儲并行處理。優(yōu)點(diǎn)是速度快。

參考:更全的具體問題和解決方法在這篇博文里有很好的闡述:https://flystarhe.github.io/docs-2014/algorithm/imbalanced-classification/readme/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容