kayiko

基于自动编码器的异常检测技术进行欺诈识别(keras)

数据集:Kaggle中使用信用卡欺诈数据:该数据集包含有在2013年9月欧洲持卡人的信用卡交易信息。

这个数据集显示了两天内发生的交易,其中在284,807次交易中有492次为欺诈数据。这样的数据集是相当不平衡的,其中正类(欺诈)数据占所有交易数据的0.172%。

数据挖掘

这虽然是一个非常不平衡的数据集,但是它也是一个很好的例子:对异常或欺诈进行识别验证。

首先,我们需要通过主成分分析法将数据集维度由30维下降到3维,并画出其对应的点状图。其中,该数据集共有32列,第一列为时间,29列为未知的数据,1列为交易金额和剩下1列为类别。需要说明的是,我们将忽略时间这一指标,因为它不是一个较为固定的指标。

def show_pca_df(df):
    x = df[df.columns[1:30]].to_numpy()
    y = df[df.columns[30]].to_numpy()

    x = preprocessing.MinMaxScaler().fit_transform(x)
    pca = decomposition.PCA(n_components=3)
    pca_result = pca.fit_transform(x)
    print(pca.explained_variance_ratio_)

    pca_df = pd.DataFrame(data=pca_result, columns=[\'pc_1\', \'pc_2\', \'pc_3\'])
    pca_df = pd.concat([pca_df, pd.DataFrame({\'label\': y})], axis=1)

    ax = Axes3D(plt.figure(figsize=(8, 8)))
    ax.scatter(xs=pca_df[\'pc_1\'], ys=pca_df[\'pc_2\'], zs=pca_df[\'pc_3\'], c=pca_df[\'label\'], s=25)
    ax.set_xlabel("pc_1")
    ax.set_ylabel("pc_2")
    ax.set_zlabel("pc_3")
    plt.show()

df = pd.read_csv(\'creditcard.csv\')

show_pca_df(df)
View Code

 

观察上图,能直观地看见有两个单独的集群,这看似是一个非常简单的任务,但是其实欺诈数据仅为黄色的点。仔细看的话,在较大的那个集群中,我们能够看见有三个黄色的点。因此,在我们保留欺诈数据的同时对正常数据进行了再次抽样。

df_anomaly = df[df[df.columns[30]] > 0]
df_normal = df[df[df.columns[30]] == 0].sample(n=df_anomaly.size, random_state=1, axis=\'index\')
df = pd.concat([ df_anomaly, df_normal])

show_pca_df(df)
View Code

 

 

 

有上图可见,正常数据较为集中,类似于一个圆盘状,而欺诈数据则较为分散。此时,我们将构建一个自动编码器,它具有3层编码器和2层解码器,具体如下:

 

 

 

 

自动编码器将我们的数据编码到一个子空间,并且在对数据进行归一化时将其解码为相应的特征。我们希望自动编码器能够学习到在归一化转换时的特征,并且在应用时这个输入和输出是类似的。而对于异常情况,由于它是欺诈数据,所以输入和输出将会明显不同。

这种方法的好处是它允许使用无监督的学习方式,毕竟在我们通常所使用的数据中,大部分的数据均为正常交易数据。并且数据的标签通常是难以获得的,而且在某些情况下完全没法使用,例如手动对数据进行标记往往存在人为认识偏差等问题。从而,在对模型进行训练的过程中,我们只使用没有标签的正常交易数据。

接下来,让我们下载数据并训练自动编码器:

import torch   
import numpy    
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.lines as lines
from sklearn import datasets, decomposition, preprocessing, model_selection
from keras import models, layers, activations, losses, optimizers, metrics
from keras.callbacks import EarlyStopping
# from tensorflow.keras import layers,Input,regularizers,Model,Sequential
from sklearn.preprocessing import StandardScaler
import seaborn as sns
from keras.utils import plot_model
df = pd.read_csv(\'SofaSofa_Anomaly.csv\')
x=df[df.columns[1:30]].to_numpy()
y=df[df.columns[30]].to_numpy()
 #准备数据
df=pd.concat([pd.DataFrame(x),pd.DataFrame({\'anomaly\':y})],axis=1)
# xx=pd.DataFrame({\'anomaly\':y})
# print(df[[True,True]])
normal_events = df[df[\'anomaly\'] == 0]
abnormal_events = df[df[\'anomaly\'] == 1]

normal_events = normal_events.loc[:, normal_events.columns != \'anomaly\']#读取整行
abnormal_events=abnormal_events.loc[:,abnormal_events.columns!=\'anomaly\'] 

scaler=preprocessing.MinMaxScaler()#标准化sklearn中的这个归一化是对列进行归一化,公式:
#        X_std = (X - X.min(axis=0)) / (X.max(axis=0) - X.min(axis=0))
#        X_scaled = X_std * (max - min) + min
scaler.fit(df.drop(\'anomaly\',1))#删除anomaly列,拟合数据
scaled_data = scaler.transform(normal_events)#标准化

train_data, test_data = model_selection.train_test_split(scaled_data, test_size=0.2)

n_features=x.shape[1]
# print(n_features)
#模型
encoder=models.Sequential(name=\'encoder\')#通过Sequential建立encoder模型
encoder.add(layer=layers.Dense(units=20,activation=activations.relu,input_shape=[n_features]))
#添加全连接层,输出维度为20,输入维度为29
encoder.add(layers.Dropout(0.1))#输出单位按比例1 / (1 - rate)进行缩放,防止过拟合
encoder.add(layer=layers.Dense(units=10, activation=activations.relu))#再添加一层全连接层
encoder.add(layer=layers.Dense(units=5, activation=activations.relu))

decoder = models.Sequential(name=\'decoder\')#建立decoder模型
decoder.add(layer=layers.Dense(units=10,activation=activations.relu,input_shape=[5]))
decoder.add(layer=layers.Dense(units=20,activation=activations.relu))
decoder.add(layers.Dropout(0.1))
decoder.add(layer=layers.Dense(units=n_features,activation=activations.sigmoid))

autoencoder=models.Sequential([encoder,decoder])#模型线性组合

autoencoder.compile(#模型编译
    loss=losses.MSE,#均方误差
    optimizer=optimizers.Adam(),#优化器
    metrics=[metrics.mean_squared_error]#衡量指标
    )
#模型训练
es = EarlyStopping(monitor=\'val_loss\', min_delta=0.00001, patience=20, restore_best_weights=True)
#提前停止(early stopping)是一种在使用诸如梯度下降之类的迭代优化方法时,可对抗过拟合的正则化方法。官方说明文档中的一句话:当监视指标停止改进时,停止训练。
# 1、monitor: 被监测的数据。z
# 2、min_delta: 在被监测的数据中被认为是提升的最小变化, 例如,小于 min_delta 的绝对变化会被认为没有提升。
# 3、patience: 在监测质量经过多少轮次没有进度时即停止。如果验证频率 
# 4、restore_best_weights: 是否从具有监测数量的最佳值的时期恢复模型权重。 如果为 False,则使用在训练的最后一步获得的模型权重。
history = autoencoder.fit(x=train_data, y=train_data, epochs=100, verbose=1, validation_data=[test_data, test_data], callbacks=[es])
#verbose: 0, 1 或 2。日志显示模式。 0 = 安静模式, 1 = 进度条, 2 = 每轮一行。
#callback:回调函数
#validation_data: 元组 (x_val,y_val) 或元组 (x_val,y_val,val_sample_weights),用来评估损失,以及在每轮结束时的任何模型度量指标。模型将不会在这个数据上进行训练。
plt.plot(history.history[\'loss\'])#history的关键字\'val_loss\', \'val_acc\', \'loss\', \'acc\'
plt.plot(history.history[\'val_loss\'])
plt.title(\'Model Loss\')
plt.ylabel(\'Loss\')
plt.xlabel(\'Epoch\')
plt.legend([\'Train\', \'Test\'], loc=\'upper left\')
plt.show()
View Code

 

 

 使用该模型,我们能够计算出正常交易时的均方根误差,并且还能知道当需要均方根误差值为95%时,阈值应该设置为多少

train_predicted_x = autoencoder.predict(x=train_data)
train_events_mse = losses.mean_squared_error(train_data, train_predicted_x)
cut_off = np.percentile(train_events_mse, 95)#当均方误差为95%时所要设置的阈值
print(\'cut_off:\', cut_off)
View Code

让我们选取100个欺诈数据和100个正常数据作为样本,结合阈值能够绘制如下图:

plot_samples = 100
#测试
# normal event
real_x = test_data[:plot_samples].reshape(plot_samples, n_features)
predicted_x = autoencoder.predict(x=real_x)
normal_events_mse = losses.mean_squared_error(real_x, predicted_x)
normal_events_df = pd.DataFrame({
    \'mse\': normal_events_mse,
    \'n\': np.arange(0, plot_samples),
    \'anomaly\': np.zeros(plot_samples)})
# abnormal event
abnormal_x = scaler.transform(abnormal_events)[:plot_samples].reshape(plot_samples, n_features)
predicted_x = autoencoder.predict(x=abnormal_x)
abnormal_events_mse = losses.mean_squared_error(abnormal_x, predicted_x)
abnormal_events_df = pd.DataFrame({
    \'mse\': abnormal_events_mse,
    \'n\': np.arange(0, plot_samples),
    \'anomaly\': np.ones(plot_samples)})
mse_df = pd.concat([normal_events_df, abnormal_events_df])
plot = sns.lineplot(x=mse_df.n, y=mse_df.mse, hue=mse_df.anomaly)

line = lines.Line2D(
    xdata=np.arange(0, plot_samples),
    ydata=np.full(plot_samples, cut_off),
    color=\'#CC2B5E\',
    linewidth=1.5,
    linestyle=\'dashed\')

plot.add_artist(line)
plt.title(\'Threshlold: {threshold}\'.format(threshold=cut_off))
plt.show()
View Code

由上图可知,与正常交易数据相比,绝大部分欺诈数据均有较高的均方根误差,从而这个方法对欺诈数据的识别似乎非常奏效。

虽然我们放弃了5%的正常交易,但仍然存在低于阈值的欺诈交易。这或许可以通过使用更好的特征提取方法来进行改进,因为一些欺诈数据与正常交易数据具有非常相似的特征。例如,对于信用卡欺诈而言,如果交易是在不同国家发生的,那么比较有价值的特征是:前一小时、前一天、前一周的交易数量。

模型可视化

 

 

转载于:https://blog.csdn.net/m0_46510245/article/details/106895419

 

发表于 2020-08-30 15:47  kayiko  阅读(547)  评论(0编辑  收藏  举报
 

分类:

技术点:

相关文章:

  • 2021-05-14
  • 2021-09-20
  • 2021-09-17
  • 2021-12-20
  • 2021-07-14
  • 2021-05-28
猜你喜欢
  • 2021-06-21
  • 2021-11-26
  • 2021-11-02
  • 2021-11-01
  • 2021-09-12
  • 2021-12-01
  • 2021-11-17
相关资源
相似解决方案