自动编码器(au toencoder) 是神经网络的一种,该网络可以看作由两部分组成:一个编码器和一个生成重构的解码器)。传统上,自动编码器被用于降维或特征学习。其为非线性降维,传统的如:PCA,为线性降维。
自编码器结构如下图所示,编码器来实现维度的压缩,解码器实现维度的恢复重构。
自编码器可用于:训练深度学习网络、压缩、分类以及异常检测等,本文主要是异常检测的应用。
本文采用公开数据集,信用卡用户的异常检测。链接如下:链接:https://pan.baidu.com/s/16-OMOjF6-S-d704FgfshbA
提取码:aaaa
上图为数据集的大概。数据共有31列,其中最后一列为异常的标定,0为正常,1为异常。
首先导入所需要的库函数。
import torch
import torch.nn as nn
import numpy as np
import matplotlib.pyplot as plt
from torch.utils.data import DataLoader,TensorDataset
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
import pandas as pd
from matplotlib import font_manager
import seaborn as sns
载入数据并对数据预处理
#加载数据并预处理
df = pd.read_csv(r'C:\Users\google1\PycharmProjects\pythonProject\Y异常检测案例\creditcard.csv')
df['Amount'] = StandardScaler().fit_transform(df['Amount'].values.reshape(-1,1))
features = df.columns[:-1].tolist()
df0 = df.query('Class==0').sample(40000)
df1= df.query('Class==1')
x_nom = df0
x_nov = df1
数据共有280000多行,其中正常数据有280000行,异常数据共有500个左右,选择40000个正常数据集和所有异常数据集,并将Amount列数据标准化。
#划分训练集和测试集
x_train, x_nom_test = train_test_split(x_nom.drop(labels=['Time','Class'],axis=1), train_size = 0.85, random_state = 1)
x_test = np.concatenate([x_nom_test,x_nov.drop(labels=['Time','Class'],axis=1)],axis = 0)
y_test = np.concatenate([np.zeros(len(x_nom_test)),np.ones(len(x_nov))])
划分数据集将85%的正常数据集作为训练集,将15%的正常数据集和异常数据集作为测试集。
#做数据格式的改变到张量
x_train = np.array(x_train)
x_test = np.array(x_test)
x_train,x_test = torch.FloatTensor(x_train),torch.FloatTensor(x_test)
#将训练数据处理为数据加载器
train_set = TensorDataset(x_train)
train_loader = DataLoader(dataset=train_set, batch_size=64, shuffle=True)
处理数据格式为张量,并将其读入加载器。
encoding_dim = 12
input_dim = x_train.shape[1]
class AutoEncoder(torch.nn.Module):
def __init__(self):
super(AutoEncoder, self).__init__()
self.encoder = torch.nn.Sequential(
torch.nn.Linear(input_dim,encoding_dim),
torch.nn.ReLU(),
torch.nn.Linear(encoding_dim,4),
torch.nn.ReLU())
self.decoder = torch.nn.Sequential(
torch.nn.Linear(4,encoding_dim),
torch.nn.ReLU(),
torch.nn.Linear(encoding_dim, input_dim)
)
def forward(self, input):
output = self.encoder(input)
output = self.decoder(output)
return output
定义自编码器网络结构,这里采用最简单的自动编码器。
model = AutoEncoder()
num_epochs = 50#选用50个epoch
optimizer = torch.optim.Adam(model.parameters(), 0.001)
loss_func = nn.MSELoss()#采用均方差损失函数
all_loss = []
for epoch in range(num_epochs):
total_loss = 0
for step, (x,) in enumerate(train_loader):
x_recon = model(x)
loss = loss_func(x_recon, x)
optimizer.zero_grad()
loss.backward()
optimizer.step()
total_loss += loss.item() * len(x)
total_loss /= len(train_set)
all_loss.append(total_loss)
# print('Epoch {}/{} : loss: {:.4f}'.format(
# epoch + 1, num_epochs, loss.item()))
plt.figure(figsize=(12, 6))
plt.plot(range(num_epochs),all_loss)
plt.show()
def get_recon_err(X):
return torch.mean((model(X) - X) ** 2, dim=1).detach().numpy()
recon_err_train = get_recon_err(x_train)
recon_err_test = get_recon_err(x_test)
recon_err = np.concatenate([recon_err_train, recon_err_test])
labels = np.concatenate([np.zeros(len(recon_err_train)), y_test])
index = np.arange(0, len(labels))
sns.kdeplot(recon_err[labels == 0], shade=True)
sns.kdeplot(recon_err[labels == 1], shade=True)
plt.show()
定义其重构误差这里采用平方均值来表示误差,并将异常和正常数据的误差密度可视化
蓝色为正常数据的,橙色为正常数据
最后设置不同阈值,寻找最佳的划定阈值。
threshold = np.linspace(0, 10, 200)
acc_list = []
f1_list = []
for t in threshold:
y_pred = (recon_err_test > t).astype(np.int)
acc_list.append(accuracy_score(y_pred, y_test))
f1_list.append(f1_score(y_pred, y_test))
plt.figure(figsize=(8, 6))
plt.plot(threshold, acc_list, c='y', label='acc')
plt.plot(threshold, f1_list, c='b', label='f1')
plt.xlabel('threshold')
plt.ylabel('classification score')
plt.legend()
plt.show()
i = np.argmax(f1_list)
t = threshold[i]
score = f1_list[i]
print('threshold: %.3f, f1 score: %.3f' % (t, score))
最后编写绘制画混淆矩阵的函数,可以调用绘制出异常检测的混淆矩阵以及异常和正常点的分类。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.metrics import confusion_matrix, roc_auc_score
import seaborn as sns
from sklearn.preprocessing import MinMaxScaler
class visualization:
labels = ["Noramal", "Anomaly"]
def draw_confusion_matrix(self, y, ypred):
matrix = confusion_matrix(y, ypred)
plt.figure(figsize=(10, 8))
colors = ["orange", "green"]
sns.heatmap(matrix, xticklabels=self.labels, yticklabels=self.labels, cmap=colors, annot=True, fmt="d")
plt.title("Confusion Matrix")
plt.ylabel('Actual')
plt.xlabel('Predicted')
plt.show()
def draw_anomaly(self, y, error, threshold):
groupSDF = pd.DataFrame({'error': error,
'true': y}).groupby('true')
figure, axes = plt.subplots(figsize=(12, 8))
for name, group in groupSDF:
axes.plot(group.index, group.error, marker='x' if name == 1 else 'o', linestyle='',
color='r' if name == 1 else 'g', label="Anomaly" if name == 1 else "Normal")
axes.hlines(threshold, axes.get_xlim()[0], axes.get_xlim()[1], colors='b', zorder=100, label="Threshold")
axes.legend()
plt.title("Anomalies")
plt.ylabel("Error")
plt.xlabel("Data")
plt.show()
def draw_error(self, error, threshold):
plt.plot(error, marker='o', ms=3.5, linestyle='', label='Point')
plt.hlines(threshold, xmin=0, xmax=len(error) - 1, colors='b', zorder=100, label='Threshold')
plt.legend()
plt.title("Reconstruction error")
plt.ylabel("Error")
plt.xlabel("Data")
plt.show()
import draw viz = draw.visualization() viz.draw_confusion_matrix(y_test,y_pred) viz.draw_anomaly(y_test,recon_err_test,threshold[i])
绘制混淆矩阵和分类可视化,其中选取使f1分数最大的阈值来实现
由于数据集前面部分都是正常标签值,最后顺序的为异常值,现在将其排序打乱并重新可视化。
c = list(zip(y_test, y_pred,recon_err_test)) random.Random(100).shuffle(c) y_test, y_pred, recon_err_test = zip(*c)
将大部分异常值都分类出来,由于仅选用最简单的自编码器故其效果不是特别好,可尝试其他种类的自编密码器如VAE等。