- 原文地址:Extreme Rare Event Classification using Autoencoders in Keras
- 原文做者:Chitta Ranjan
- 譯文出自:掘金翻譯計劃
- 本文永久連接:github.com/xitu/gold-m…
- 譯者:ccJia
- 校對者:lsvih
在本文中,咱們將要學習使用自編碼器搭建一個稀有事件分類器。咱們將使用來自[1]的一個現實場景稀有事件數據集。html
在稀有事件問題中,咱們面對的是一個不平衡的數據集。這表明着,相較於負樣本,咱們只有不多的正樣本標籤。典型的稀有事件問題中,正樣本在所有數據中佔比大概在 5-10% 之間。在極端稀有事件問題中,咱們只有少於 1% 的正樣本數據。好比,在咱們使用的數據集中,正樣本只有 0.6%。前端
這種極端稀有的事件在現實世界中是十分廣泛的。好比,工廠中的紙張斷裂和機器故障,在線銷售行業中的點擊或者購買。python
分類這些稀有事件是十分具備挑戰的。最近,深度學習被普遍應用於分類問題。然而,少許的正樣本限制了深度學習的應用。無論數據量有多大,正樣本數量都會限制深度學習的效果。android
這是一個合理的問題。咱們爲何不去考慮使用其餘的機器學習方法呢?ios
答案是主觀的。咱們可使用機器學習方法。爲了使它工做,咱們能夠對負樣本數據進行負採樣,使得數據接近平衡。因爲正樣本數據只有 0.6%,降採樣後的數據集大概只有原始數據集大小的 1%。一些機器學習方法,如:SVM、隨機森林等,均可以在這個數據量上工做。然而,它的準確率會受到限制。這是由於咱們不會使用剩下的 99% 的數據。git
若是數據充足,深度學習會表現的更好。它還能夠經過使用不一樣的結構來靈活的改進模型。所以,咱們準備嘗試使用深度學習方法。github
在本推文中,咱們將要學習如何使用一個簡單的全鏈接層自編碼器來搭建一個稀有事件分類器。推文的目是爲了展現一個極端稀有事件分類器的自編碼器實現。咱們將探索不一樣自編碼器結構和配置的工做留給讀者。若是有什麼有趣的發現,請分享給咱們。後端
自編碼器處理分類任務的方法相似於異常檢測。在異常檢測中,咱們學習正常過程的模式。任何與這個模式不一致的東西,咱們都認爲是異常的。對於一個稀有事件的二分類任務,咱們能夠用相似的方法使用自編碼器(延伸閱讀 [2])。框架
這是一個關於紙張斷裂的二分類標籤數據來自於造紙廠。在造紙廠,紙張斷裂是一個嚴重的問題。單次的紙張斷裂可能形成數千美金的損失,並且工廠天天至少會發生一次或屢次紙張斷裂。這致使每一年數百萬美圓的損失和工做風險。dom
因爲過程的性質,檢測中斷事件很是具備挑戰性。正如[1]中提到的,即便減小 5% 的斷裂也會給鋼廠帶來顯著的好處。
經過 15 天的收集,咱們獲得了包含 18K 行的數據。列 ‘y’ 包含了二分類標籤,1 表明斷裂。 其餘列是預測器。這裏有 124 個正樣本(~0.6%)。
從[這裏]下載數據(docs.google.com/forms/d/e/1…)下載數據。
Import the desired libraries.
%matplotlib inline
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
import numpy as np
from pylab import rcParams
import tensorflow as tf
from keras.models import Model, load_model
from keras.layers import Input, Dense
from keras.callbacks import ModelCheckpoint, TensorBoard
from keras import regularizers
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix, precision_recall_curve
from sklearn.metrics import recall_score, classification_report, auc, roc_curve
from sklearn.metrics import precision_recall_fscore_support, f1_score
from numpy.random import seed
seed(1)
from tensorflow import set_random_seed
set_random_seed(2)
SEED = 123 #used to help randomly select the data points
DATA_SPLIT_PCT = 0.2
rcParams['figure.figsize'] = 8, 6
LABELS = ["Normal","Break"]
複製代碼
注意,爲了可復現結果,咱們設置了隨機數種子。
數據處理
如今,咱們來讀取和準備數據。
df = pd.read_csv("data/processminer-rare-event-mts - data.csv")
複製代碼
這個稀有事件問題的目的是在發生斷裂前預測它。咱們嘗試提早 4 分鐘預測出斷裂。爲了創建這個模型,咱們把數據標籤提早 2 行(對應於 4 分鐘)。經過這行代碼實現 df.y=df.y.shift(-2)
。然而,在這個問題中,咱們想作的是:判斷行 n 是否會被標記爲正樣本,
讓 (n-2) 和 (n-1) 標記爲 1。這樣能夠幫助分類器學習到提早 4 分鐘預測。
刪除 n 行。由於咱們不想讓分類器學習預測正在發生的斷裂。
咱們將爲這個曲線移動開發如下 UDF。
sign = lambda x: (1, -1)[x < 0]
def curve_shift(df, shift_by):
''' 這個函數是用來偏移數據中的二分類標籤。 平移只針對標籤爲 1 的數據 舉個例子,若是偏移量爲 -2,下面的處理將會發生: 若是是 n 行的標籤爲 1,那麼 - 使 (n+shift_by):(n+shift_by-1) = 1 - 刪除第 n 行。 也就是說標籤會上移 2 行。 輸入: df 一個分類標籤列的 pandas 數據。 這個標籤列的名字是 ‘y’。 shift_by 一個整數,表示要移動的行數。 輸出: df 按照偏移量平移事後的數據。 '''
vector = df['y'].copy()
for s in range(abs(shift_by)):
tmp = vector.shift(sign(shift_by))
tmp = tmp.fillna(0)
vector += tmp
labelcol = 'y'
# 添加向量到 df
df.insert(loc=0, column=labelcol+'tmp', value=vector)
# 刪除 labelcol == 1 的行.
df = df.drop(df[df[labelcol] == 1].index)
# 丟棄 labelcol 同時將 tmp 做爲 labelcol。
df = df.drop(labelcol, axis=1)
df = df.rename(columns={labelcol+'tmp': labelcol})
# 製做二分類標籤
df.loc[df[labelcol] > 0, labelcol] = 1
return df
複製代碼
如今,咱們將數據分爲訓練集、驗證集和測試集。而後咱們將只使用標籤爲 0 的子集來訓練自編碼器。
df_train, df_test = train_test_split(df, test_size=DATA_SPLIT_PCT, random_state=SEED)
df_train, df_valid = train_test_split(df_train, test_size=DATA_SPLIT_PCT, random_state=SEED)
df_train_0 = df_train.loc[df['y'] == 0]
df_train_1 = df_train.loc[df['y'] == 1]
df_train_0_x = df_train_0.drop(['y'], axis=1)
df_train_1_x = df_train_1.drop(['y'], axis=1)
df_valid_0 = df_valid.loc[df['y'] == 0]
df_valid_1 = df_valid.loc[df['y'] == 1]
df_valid_0_x = df_valid_0.drop(['y'], axis=1)
df_valid_1_x = df_valid_1.drop(['y'], axis=1)
df_test_0 = df_test.loc[df['y'] == 0]
df_test_1 = df_test.loc[df['y'] == 1]
df_test_0_x = df_test_0.drop(['y'], axis=1)
df_test_1_x = df_test_1.drop(['y'], axis=1)
複製代碼
標準化
對於自編碼器,一般最好使用標準化數據(轉換爲高斯、均值 0 和方差 1)。
scaler = StandardScaler().fit(df_train_0_x)
df_train_0_x_rescaled = scaler.transform(df_train_0_x)
df_valid_0_x_rescaled = scaler.transform(df_valid_0_x)
df_valid_x_rescaled = scaler.transform(df_valid.drop(['y'], axis = 1))
df_test_0_x_rescaled = scaler.transform(df_test_0_x)
df_test_x_rescaled = scaler.transform(df_test.drop(['y'], axis = 1))
複製代碼
初始化
首先,咱們將初始化自編碼器框架。咱們只構建一個簡單的自編碼器。更多複雜的結構和配置留給讀者去探索。
nb_epoch = 100
batch_size = 128
input_dim = df_train_0_x_rescaled.shape[1] #num of predictor variables,
encoding_dim = 32
hidden_dim = int(encoding_dim / 2)
learning_rate = 1e-3
input_layer = Input(shape=(input_dim, ))
encoder = Dense(encoding_dim, activation="tanh", activity_regularizer=regularizers.l1(learning_rate))(input_layer)
encoder = Dense(hidden_dim, activation="relu")(encoder)
decoder = Dense(hidden_dim, activation='tanh')(encoder)
decoder = Dense(input_dim, activation='relu')(decoder)
autoencoder = Model(inputs=input_layer, outputs=decoder)
複製代碼
訓練
咱們將訓練模型,並保存它到指定文件。存儲訓練模型是節省將來分析時間的好方法。
autoencoder.compile(metrics=['accuracy'],
loss='mean_squared_error',
optimizer='adam')
cp = ModelCheckpoint(filepath="autoencoder_classifier.h5",
save_best_only=True,
verbose=0)
tb = TensorBoard(log_dir='./logs',
histogram_freq=0,
write_graph=True,
write_images=True)
history = autoencoder.fit(df_train_0_x_rescaled, df_train_0_x_rescaled,
epochs=nb_epoch,
batch_size=batch_size,
shuffle=True,
validation_data=(df_valid_0_x_rescaled, df_valid_0_x_rescaled),
verbose=1,
callbacks=[cp, tb]).history
複製代碼
分類器
接下來,咱們將展現咱們如何使用自編碼器對於稀有事件的重構偏差來作分類。
以前已經提到,若是重構偏差比較高,咱們將認定它是一次斷裂。咱們須要定一個閾值。
咱們使用驗證集來設置閾值。
valid_x_predictions = autoencoder.predict(df_valid_x_rescaled)
mse = np.mean(np.power(df_valid_x_rescaled - valid_x_predictions, 2), axis=1)
error_df = pd.DataFrame({'Reconstruction_error': mse,
'True_class': df_valid['y']})
precision_rt, recall_rt, threshold_rt = precision_recall_curve(error_df.True_class, error_df.Reconstruction_error)
plt.plot(threshold_rt, precision_rt[1:], label="Precision",linewidth=5)
plt.plot(threshold_rt, recall_rt[1:], label="Recall",linewidth=5)
plt.title('Precision and recall for different threshold values')
plt.xlabel('Threshold')
plt.ylabel('Precision/Recall')
plt.legend()
plt.show()
複製代碼
如今,咱們將對測試數據進行分類。
咱們不該該根據測試數據來估計分類閾值。這會致使過擬合。
test_x_predictions = autoencoder.predict(df_test_x_rescaled)
mse = np.mean(np.power(df_test_x_rescaled - test_x_predictions, 2), axis=1)
error_df_test = pd.DataFrame({'Reconstruction_error': mse,
'True_class': df_test['y']})
error_df_test = error_df_test.reset_index()
threshold_fixed = 0.85
groups = error_df_test.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= "Break" if name == 1 else "Normal")
ax.hlines(threshold_fixed, 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();
複製代碼
在圖 4 中,閾值線上方的橙色和藍色圓點分別表示真陽性和假陽性。正如咱們所看到的,咱們有不少假陽性。爲了更好的理解,咱們使用混淆矩陣來表示。
pred_y = [1 if e > threshold_fixed else 0 for e in error_df.Reconstruction_error.values]
conf_matrix = confusion_matrix(error_df.True_class, pred_y)
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()
複製代碼
咱們能夠預測 32 次斷裂中的 9 次。值得注意的是,這些結果是提早 2 到 4 分鐘預測的。這一比率大概是 28%,這對於造紙業來講已是一個很好的召回率了。假陽性大體是 6.3%。這並不完美,可是對於工廠而言也不壞。
該模型還能夠進一步改進,在假陽性率較小的狀況下提升召回率。咱們將在下面討論 AUC,而後討論下一個改進方法。
ROC 曲線和 AUC
false_pos_rate, true_pos_rate, thresholds = roc_curve(error_df.True_class, error_df.Reconstruction_error)
roc_auc = auc(false_pos_rate, true_pos_rate,)
plt.plot(false_pos_rate, true_pos_rate, linewidth=5, label='AUC = %0.3f'% roc_auc)
plt.plot([0,1],[0,1], linewidth=5)
plt.xlim([-0.01, 1])
plt.ylim([0, 1.01])
plt.legend(loc='lower right')
plt.title('Receiver operating characteristic curve (ROC)')
plt.ylabel('True Positive Rate')
plt.xlabel('False Positive Rate')
plt.show()
複製代碼
AUC 的結構是 0.624。
帶有註釋的代碼在這裏。 cran2367/autoencoder_classifier **Autoencoder model for rare event classification. Contribute to cran2367/autoencoder_classifier development by creating…**github.com
這是一個(多元)時間序列數據。咱們沒有考慮數據中的時間信息/模式。咱們將在下一篇推文探索是否能夠結合 RNN 進行分類。咱們將嘗試 LSTM autoencoder。
咱們研究了一個工做於造紙廠的極端稀有事件的二值數據的自編碼分類器。咱們達到了不錯的準確度。咱們的目的是展現自編碼器對於稀有事件分類問題的基礎應用。咱們以後會嘗試開發其它的方法,包括能夠結合時空特徵的 LSTM Autoencoder 來達到一個更好的效果。
下一篇關於 LSTM 自編碼的推文在這裏 LSTM Autoencoder for rare event classification.
若是發現譯文存在錯誤或其餘須要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可得到相應獎勵積分。文章開頭的 本文永久連接 即爲本文在 GitHub 上的 MarkDown 連接。
掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。