Python数据分析案例29——自编码器监测异常值

与传统的监督学习不一样,这一篇主要是讲述自编码器模型的,是无监督学习,并且用于的任务不是分类或者回归,而是异常值的监测。


案例背景

需要从一堆网络流量特征监控的数据中寻找哪些可能是异常情况。

听着像分类问题对吧,但是和分类问题有很大不同,一是训练方式不一样,二是异常值情况通常是很少的,所以不能做分类模型,要做自监督模型。

自编码器是什么我就不多介绍了,总之原理就是把数据拿来编码压缩然后解码还原,比较重构还原出来的数据和原来的数据的差异,差的多的,误差大的,就可能是异常值。


代码实现

导入包

import pandas as pd
from pandas.plotting import scatter_matrix
import numpy as np
import pickle
import h5py
import matplotlib.pyplot as plt
import seaborn as sns
from scipy import stats

from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
import tensorflow as tf
from tensorflow.keras.models import Model, load_model
from tensorflow.keras.layers import Input, Dense
from tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping
from tensorflow.keras import regularizers
from tensorflow.keras.utils import plot_model
%matplotlib inline
sns.set(style='whitegrid', palette='muted', font_scale=1.5)
RANDOM_SEED = 42
LABELS = ['normal.', 'ipsweep.']

1.准备数据

1.1读取数据

读取,查看训练集数据

kddCupTrain = pd.read_csv('kddCupTrain.csv',header=None)
kddCupTest = pd.read_csv('kddCupTest.csv',header=None)
print("Shape of kddCupTrain: ",kddCupTrain.shape)
print("There are any missing values: ", kddCupTrain.isnull().values.any())
kddCupTrain.head(3)

 可以看到有40列特征,最后一列是标签。

查看测试集

print("Shape of kddCupTest: ",kddCupTest.shape)
print("There are any missing values: ", kddCupTest.isnull().values.any())
kddCupTest.head(3)


查看响应变量y的信息 

#将 kddCupTrain 数据集的第41列重命名为 'Class' 并且应用到原数据集中
kddCupTrain.rename(columns={41:'Class'}, inplace=True)

#使用 pandas.factorize() 函数将 'Class' 列中的值转化为数值编码,并返回编码后的结果和唯一值列表
codes,uniques=pd.factorize(kddCupTrain['Class'])

#打印出唯一的类别标签
print(uniques)
#将 'Class' 列的数值编码更新到原数据集中
kddCupTrain['Class'] = codes

#统计每个类别的样本数量并按数量从大到小排序,然后打印
count_classes = kddCupTrain['Class'].value_counts(sort = True)
print(count_classes)

画个图看看

count_classes.plot.bar(figsize=(5,3))  #画图

 

可以看到是极度不平衡的数据。

取出y

y = kddCupTrain['Class']
kddCupTrain = kddCupTrain.drop(['Class'], axis=1)

 1.2删除无用信息列

 #取值唯一的变量删除(如果有一列的值全部一样,也就是取值唯一的特征变量就可以删除了,因为每个样本没啥区别,对模型就没啥用)

for col in kddCupTrain.columns:
    if len(kddCupTrain[col].value_counts())==1:
        print(col)
        kddCupTrain.drop(col,axis=1,inplace=True)

这两列删除了

 测试集也进行删除

kddCupTest=kddCupTest[kddCupTrain.columns]
print(kddCupTrain.shape,kddCupTest.shape)


1.3训练集和测试集都进行独立热编码 

kddCupTrain=pd.get_dummies(kddCupTrain)
kddCupTest=pd.get_dummies(kddCupTest)
print(kddCupTrain.shape,kddCupTest.shape)

开通看到数据独立热编码后维度不一样,需要统一一下

统一数据维度

for col in kddCupTrain.columns:
    if col not in kddCupTest.columns:
        kddCupTest[col]=0
kddCupTest=kddCupTest[kddCupTrain.columns]
print(kddCupTrain.shape,kddCupTest.shape)

 查看训练集前三行

kddCupTrain.head(3)

测试集前三行

kddCupTest.head(3)

 


1.4数据标准化

#数据标准化
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
scaler.fit(kddCupTrain)
X_s = scaler.transform(kddCupTrain)
X_test_s = scaler.transform(kddCupTest)

print('训练数据形状:')
print(X_s.shape,y.shape)
print('测试数据形状:')
print(X_test_s.shape)

 查看数据信息(验证是不是标准化了)

print(X_s.mean())
print(X_s.std(ddof=0))


1.5将数据拆分为训练子集和验证子集 

#划分训练集和验证集
from sklearn.model_selection import train_test_split
X_train,X_val,y_train,y_val=train_test_split(X_s,y,test_size=0.2,random_state=RANDOM_SEED)

查看形状

print('Train: shape X',X_train.shape,', shape Y',y_train.shape)
print('Val: shape X',X_val.shape,', shape Y',y_val.shape)


  1.6. 分离“正常”实例

X_trainNorm = X_train[y_train == 0]
X_valNorm = X_val[y_val == 0]
print(X_trainNorm.shape,X_valNorm.shape)


2. 构建模型 

2.1.选择自编码器的架构

自己选择几层几个神经元。我这里是4个隐藏层,编码压缩从83到40到20,然后再解码从20到40到83还原。看这个层的张量形状就能看出来。

input_dim = X_trainNorm.shape[1]
layer1_dim = 40
encoder_dim = 20

input_layer = Input(shape=(input_dim, ))
encoder1 = Dense(layer1_dim, activation="relu")(input_layer)
encoder2 = Dense(encoder_dim, activation="relu")(encoder1)
decoder1 = Dense(layer1_dim, activation='relu')(encoder2)
decoder2 = Dense(input_dim, activation='linear')(decoder1)
print('input_layer: ',input_layer)
print('encoder1',encoder1)
print('encoder2',encoder2)
print('decoder1',decoder1)
print('decoder2',decoder2)

 查看模型信息。

autoencoder = Model(inputs=input_layer, outputs=decoder2)
autoencoder.summary()

 对模型进行可视化,画出来

plot_model(autoencoder, to_file='fraud_encoder1.png',show_shapes=True,show_layer_names=True)


2.2. 拟合模型 

批量大小64,训练轮数50.

nb_epoch = 50
batch_size = 64

autoencoder.compile(optimizer='adam', loss='mean_squared_error')

checkpointer = ModelCheckpoint(filepath="model.h5",verbose=0,save_best_only=True)

earlystopping = EarlyStopping(monitor='val_loss', patience=5, verbose=0) # 'patience' number of not improving epochs

history = autoencoder.fit(X_trainNorm, X_trainNorm,epochs=nb_epoch, batch_size=batch_size,shuffle=True,
                    validation_data=(X_valNorm, X_valNorm),
                    verbose=1,callbacks=[checkpointer, #tensorboard, 
                               earlystopping]).history

有早停机制,所以24轮就停下来了。

查看损失图

plt.plot(history['loss'])
plt.plot(history['val_loss'])
plt.title('model loss')
plt.ylabel('loss')
plt.xlabel('epoch')
plt.legend(['train', 'val'], loc='upper right');

 


3. 评估  

3.1. 从文件加载安装的自动编码器

autoencoder = load_model('model.h5')

3.2. 重建

对验证集数据进行评价 (下面的图都是验证集的)

重构预测

testPredictions = autoencoder.predict(X_val)
X_val.shape,testPredictions.shape

真实数据和预测重构的数据形状一样,下面计算对比他们的误差。 

 3.3. 评估

testMSE = mean_squared_error(X_val.transpose(), testPredictions.transpose(), multioutput='raw_values')
error_df = pd.DataFrame({'reconstruction_error': testMSE,'true_class': y_val})
error_df.head()

描述性统计一下

error_df.reconstruction_error.describe()

 

画图查看正常的情况重构误差的分布。 

fig = plt.figure(figsize=(5,3),dpi=128)
ax = fig.add_subplot(111)
normal_error_df = error_df[(error_df['true_class']== 0) & (error_df['reconstruction_error'] < 10)]
ax.hist(normal_error_df.reconstruction_error.values, bins=10);

 

 可以看到误差基本都是0附近,说明正常情况下的重构误差都很小。

查看异常值的重构误差分布

fig = plt.figure(figsize=(5,3),dpi=128)
ax = fig.add_subplot(111)
fraud_error_df = error_df[(error_df['true_class']== 1) & (error_df['reconstruction_error'] < 10)]
ax.hist(fraud_error_df.reconstruction_error.values, bins=10);

  可以看到误差分布没那么极端了,有很多样本重构后有较大的误差,说明异常情况下的重构误差都会偏大。

计算AUC值

from sklearn.metrics import (confusion_matrix, auc, roc_curve, cohen_kappa_score, accuracy_score)
fpr, tpr, thresholds = roc_curve(error_df.true_class, error_df.reconstruction_error)
roc_auc = auc(fpr, tpr)

plt.figure(figsize=(7,4),dpi=128)
plt.title('Receiver Operating Characteristic')
plt.plot(fpr, tpr, label='AUC = %0.4f'% roc_auc)
plt.legend(loc='lower right')
plt.plot([0,1],[0,1],'r--')
plt.xlim([-0.001, 1])
plt.ylim([0, 1.001])
plt.ylabel('True Positive Rate')
plt.xlabel('False Positive Rate')
plt.show();

 3.4 预测效果

threshold = normal_error_df.reconstruction_error.quantile(q=0.995)
threshold

找到误差5%的分位数水平作为阈值,重构误差大于这个值就认为是异常情况。

筛序一下极端情况,方便画图

error_df=error_df[error_df['reconstruction_error']<3500]
groups = error_df.groupby('true_class')
fig, ax = plt.subplots()
for name, group in groups:
    if name == 1:
        MarkerSize = 7 ; Color = 'orangered' ; Label = 'Fraud' ; Marker = 'd'
    else:
        MarkerSize = 3.5 ; Color = 'b' ; Label = 'Normal' ; Marker = 'o'
        
    ax.plot(group.index, group.reconstruction_error, linestyle='',color=Color,label=Label,ms=MarkerSize,marker=Marker)
    
ax.hlines(threshold, ax.get_xlim()[0], ax.get_xlim()[1], colors="r", zorder=100, label='Threshold')
ax.legend(loc='upper left', bbox_to_anchor=(0.95, 1))
plt.title("Probabilities of fraud for different classes")
plt.ylabel("Reconstruction error")  ;   plt.xlabel("Data point index")
plt.show()

可以看到,大于阈值的情况,有一些事正常的,也有异常的,分类没那么准确。

画混淆矩阵来进一步观察

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)
print(conf_matrix)

plt.figure(figsize=(5, 5),dpi=108)
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()

 

 计算科恩指标和准确率 

cohen_kappa_score(error_df.true_class, y_pred),accuracy_score(error_df.true_class, y_pred)

准确率还是高达98的,但是由于样本的极度不平衡,科恩指标较小。


4.测试集的预测,创建提交 

预测重构

testPredictions = autoencoder.predict(X_test_s)
X_test_s.shape,testPredictions.shape

计算误差

testMSE = mean_squared_error(X_test_s.transpose(), testPredictions.transpose(), multioutput='raw_values')
result_df = pd.DataFrame({'reconstruction_error': testMSE}) 

画个图看看

result_df.plot.box()

 有很多极大值,说明可能这些误差大的样本就是异常情况。

储存,就可以提交了

result_df.to_csv('result.csv')

(如果需要变成分类的结果(是否为异常值),就按照上面算出阈值,然后加一个判断映射为分类变量就行)

猜你喜欢

转载自blog.csdn.net/weixin_46277779/article/details/131101630