Table of Contents
一、異常檢測
異常定義爲偏離標準,很少發生且不遵循其餘“模式”的事件。異常的例子包括:
- 由於世界大事而導致的股市大跌
- 工廠/傳送帶上的不良物品
- 實驗室中被污染的樣品
假設我們的數據服從一個正太分佈,那麼通常異常數據位於正態分佈曲線的兩側。如下圖所示。
正如我們看到的那樣,這些事件將發生,但發生的可能性極低。從機器學習的角度來看,這使得很難檢測異常-根據定義,我們有很多“標準”事件的示例,而很少有“異常”事件的示例。因此,我們的數據集存在很大的偏差。當我們要檢測的異常可能僅在1%,0.1%或0.0001%的時間發生時,應該如何在平衡數據集中最佳地工作的機器學習算法如何工作?這時候我們就需要用到異常檢測來專門處理這類問題。
由於我們的數據集標籤存在極大的不平衡,根據定義異常數據很少發生,而我們擁有大量的正常數據。而爲了檢測異常數據,傳統的機器學習算法衍生出如孤立森林、One-class SVMs、Elliptic Envelopes和局部異常因子算法等。這裏不一一介紹,有興趣的同學可以去研究研究,這裏主要講的是如何使用深度學習來解決這個問題。
二、基於自動編碼器的異常檢測
自動編碼器是一種無監督的神經網絡,它可以:
- 接受一組輸入數據;
- 對數據進行編碼;
- 將編碼後的特徵進行解碼來重建輸入數據。
通常自動編碼器主要有兩部分組成:編碼器和解碼器。編碼器接受輸入數據並將它轉化爲特徵表示。然後,解碼器嘗試對壓縮的特徵進行重構得到輸入數據。當我們以端到端的方式訓練自動編碼器時,該網絡能夠學習到一種強大的過濾器,其甚至能夠對輸入數據去噪。
從異常檢測角度來看,自動編碼器特別之處在於其能夠重構損失,在訓練自動編碼器時通常會衡量輸入和重構數據之間的均方誤差(MSE)。如果損失越小,那麼重構的圖像越像原始數據。
假設我們在整個MNIST數據集上訓練了一個自動編碼器,然後,對自動編碼器提供一個數字,並對他進行重構。我們希望我們能夠對其進行重建與輸入數據相似,然後我們會發現這兩張圖像之間的MSE比較低。
如果,此時我們再提供一個非數字的圖像,如大象,此時這兩張圖片的MSE非常高。這是由於自動編碼器以前從未見過大象,更重要的是,從未接受過重建大象的訓練,因此 我們的MSE 很高。
此時,如果重建的MSE高,那麼我們可能會有一個異常值。
三、異常檢測Tensorflow的實現
3.1、數據加載
這裏我們使用mnist數據集,將標籤爲1的作爲正常數據,將標籤爲數據的數量1%的數量的且標籤爲3的數據作爲異常數據,並製作異常數據。具體如下:
- 加載數據集
# 加載MNIST數據集
print("[INFO] loading MNIST dataset...")
((trainX,trainY),(testX,testY))=tf.keras.datasets.mnist.load_data()
- 生成異常數據
def build_unsupervised_dataset(data,labels,validLabel=1,
anomalyLabel=3, contam=0.01, seed=42):
'''製作數據集'''
# 抓取提供的類標籤的所有 * 真正 * 索引特定的標籤,
# 然後獲取圖像的索引個標籤將成爲我們的“異常”
validIdxs=np.where(labels==validLabel)[0]
anomalyLabelIdx=np.where(labels==anomalyLabel)[0]
#隨機打亂數據
random.shuffle(validIdxs)
random.shuffle(anomalyLabelIdx)
#計算並設置異常數據的個數
i=int(len(validIdxs)*contam)
anomalyLabelIdx=anomalyLabelIdx[:i]
#提取正常數據和異常數據
validImages=data[validIdxs]
anomalyImages=data[anomalyLabelIdx]
#打包數據並進行數據打亂
images=np.vstack([validImages,anomalyImages])
return images
# 建立少量的無監督圖像數據集,污染(即異常)添加到其中
print("[INFO] creating unsupervised dataset...")
images = build_unsupervised_dataset(trainX, trainY, validLabel=1,
anomalyLabel=3, contam=0.01)
3.2、搭建自編碼模型
這裏直接上代碼:
class ConvAutoencoder:
@staticmethod
def build(width,height,depth=None,filters=(32,64),latentDim=16):
'''
構建自動編碼器模型
:param width: 圖像的寬度
:param height: 圖像的高度
:param depth: 圖像的深度
:param filters: 卷積核的尺寸
:param latentDim: 全連接的維數
:return:
'''
#定義解碼器
inputs=tf.keras.layers.Input(shape=(height,width,depth))
x=inputs
for filter in filters:
x=tf.keras.layers.Conv2D(filter,kernel_size=(3,3),strides=2,padding='same')(x)
x=tf.keras.layers.LeakyReLU(alpha=0.2)(x)
x=tf.keras.layers.BatchNormalization(axis=-1)(x)
volumeSize=tf.keras.backend.int_shape(x)
x=tf.keras.layers.Flatten()(x)
latent=tf.keras.layers.Dense(latentDim)(x)
encoder=tf.keras.Model(inputs=inputs,outputs=latent,name='encoder')
#定義編碼器
latentinputs=tf.keras.layers.Input(shape=(latentDim,))
x=tf.keras.layers.Dense(np.prod(volumeSize[1:]))(latentinputs)
x=tf.keras.layers.Reshape((volumeSize[1],volumeSize[2],volumeSize[3]))(x)
for filter in filters[::-1]:
x=tf.keras.layers.Conv2DTranspose(filter,kernel_size=(3,3),strides=2,padding='same')(x)
x=tf.keras.layers.LeakyReLU(alpha=0.2)(x)
x=tf.keras.layers.BatchNormalization(axis=-1)(x)
x=tf.keras.layers.Conv2DTranspose(depth,(3,3),padding='same')(x)
outputs=tf.keras.layers.Activation('sigmoid')(x)
decoder=tf.keras.Model(latentinputs,outputs,name='decoder')
autoencoder=tf.keras.Model(inputs,decoder(encoder(inputs)),name='autoencoder')
return (encoder,decoder,autoencoder)
autoencoder.summary()
網絡框架結構如下:
3.3、模型訓練
epochs=20
lr=1e-3
batch_size=32
#搭建模型
print("[INFO] building autoencoder...")
(encoder, decoder, autoencoder) = ConvAutoencoder.build(28, 28,1)
#搭建優化器
opt=tf.keras.optimizers.Adam(lr=lr,decay=lr/epochs)
autoencoder.compile(loss='mse',optimizer=opt,metrics=['acc'])
#訓練
H=autoencoder.fit(trainX,trainX,validation_data=(testX,testX),epochs=epochs,batch_size=batch_size)
訓練過程如下:
3.4、模型預測
主要使用自編碼重構後的圖片與原圖片計算MSE,然後根據閾值進行相應的判斷,主要實現如下:
# 加載模型
print("[INFO] loading autoencoder and image data...")
autoencoder = tf.keras.models.load_model("autoencoder.h5")
images = pickle.loads(open("mages.pickle", "rb").read())
#預測圖片
images=images.reshape(-1,28,28,1)
images=images.astype('float32')
images/=255
decoded = autoencoder.predict(images)
errors = []
for (image, recon) in zip(images, decoded):
# 計算預測和真實圖片之間的均方差1
mse = np.mean((image - recon) ** 2)
errors.append(mse)
# compute the q-th quantile of the errors which serves as our
# threshold to identify anomalies -- any data point that our model
# reconstructed with > threshold error will be marked as an outlier
thresh = np.quantile(errors, 0.999)
idxs = np.where(np.array(errors) >= thresh)[0]
print("[INFO] mse threshold: {}".format(thresh))
print("[INFO] {} outliers found".format(len(idxs)))
測試結果如下:
詳細代碼鏈接:https://github.com/kingqiuol/learning_tensorflow/tree/master/cv/Anomaly_detection
參考鏈接:
https://www.pyimagesearch.com/2020/03/02/anomaly-detection-with-keras-tensorflow-and-deep-learning/