基于Keras的序列异常检测自编码器

2024-09-10 21:13:28 浏览数 (3)

引言

在处理长字符串序列列表,如氨基酸结构、产品序列号或用户UID时,创建一个验证流程来检测序列中的异常是一项挑战,尤其是当我们不确定序列的正确格式或结构时。异常可能是由于错误或故意为之而遵循稍微不同或不寻常的格式的字符串,或者是一个极其罕见的字符串。

在这种情况下,无监督机器学习算法,如DBScan、Isolation Forest等,通常被用来识别异常或离群值。这些算法通过识别那些相对远离其他数据点或不在大多数数据点所在区域的数据点来工作。自编码器是另一种在异常检测中表现出色的算法,尽管它们的工作原理与上述算法不同。

自编码器是一种特殊类型的神经网络,它们通过创建数据的不同表示并测量这些表示在生成预期结果方面的表现来学习。自编码器的目标是生成一个输出,这个输出是它们接收到的输入的重构。在这个过程中,自编码器学会了输入数据的格式规则,这使得它们能够作为异常检测机制。

使用自编码器进行异常检测通常包括以下两个主要步骤:

  1. 训练阶段:将数据输入自编码器,并调整它直到能够以最小误差重构预期输出。例如,如果自编码器能够成功重构大部分数据,并且重构的输出与输入足够接近,那么它就训练得很好。
  2. 检测阶段:将所有数据再次输入到训练好的自编码器中,并测量每个重构数据点的误差。这个误差是重构的数据点与实际数据点之间的“距离”。一个训练良好的自编码器学会了如何重构遵循特定格式的输入,因此,如果给自编码器一个格式错误的数据点,它可能会产生一个与输入显著不同的重构,以及一个较大的误差。

这种方法的优势在于,自编码器能够学习数据的内在结构,而不需要事先知道数据应该遵循的确切格式。这使得自编码器在处理未知或复杂格式的数据时非常有用。

代码实践

在本节中,将探讨如何使用自编码器来识别长字符串序列中的异常。自编码器是一种强大的神经网络,能够学习数据的压缩表示,并通过重构误差来识别异常。

步骤概述

  1. 生成数据:创建一组遵循特定格式的随机字符串序列,并添加一些异常。
  2. 数据预处理:将序列编码成数字并进行缩放。
  3. 构建自编码器:设计、拟合并调整自编码器。
  4. 计算误差:将序列输入到训练好的自编码器中,并计算每个数据点的误差项。
  5. 发现异常:通过找到误差项最高的数据点来识别异常。

1. 生成数据

在本节中,将创建一个函数来生成遵循特定模式的字符串序列,并在这些序列中故意引入一些异常值。这些序列将模拟具有特定格式的数据,例如产品序列号或特定类型的标识符。

首先将编写一个函数,该函数生成格式为 [4个字母A-F][1个数字0-2][3个字母QWOPZXML] 的字符串。接着,将使用此函数生成 25,000 个符合格式的序列,并添加一些故意制造的异常序列以模拟真实世界数据中的不规则情况。

代码语言:javascript复制
import random
import pandas as pd
import numpy as np

# 定义可用字符集
first_letters = 'ABCDEF'
second_numbers = '012'
last_letters = 'QWOPZXML'

def get_random_string():
    """生成一个随机字符串,格式为:[4个字母A-F][1个数字0-2][3个字母QWOPZXML]"""
    str1 = ''.join(random.choice(first_letters) for i in range(4))
    str2 = random.choice(second_numbers)
    str3 = ''.join(random.choice(last_letters) for i in range(3))
    return str1   str2   str3

# 生成25,000个符合格式的序列
random_sequences = [get_random_string() for i in range(25000)]

# 故意添加一些异常序列
random_sequences.extend(['XYDC2DCA', 'TXSX1ABC', 'RNIU4XRE', 'AABDXUEI', 'SDRAC5RF'])

# 将序列保存到 pandas DataFrame 中以便进一步处理
seqs_ds = pd.DataFrame(random_sequences)

2. 数据预处理

在本阶段,将字符串序列转换为数字表示,并进行缩放,以准备用于训练自编码器。

首先,构建字符索引,将字符映射到整数,以便将字符串序列编码为数值序列。

代码语言:javascript复制
# 构建字符索引,用于将序列编码为数字
char_index = '0abcdefghijklmnopqrstuvwxyz'
char_index  ='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
char_index  = '123456789'
char_index  = '().,-/ =&$?@#!*:;_[]|%⸏{}"''   ' '  '\'

char_to_int = dict((c, i) for i, c in enumerate(char_index))
int_to_char = dict((i, c) for i, c in enumerate(char_index))

from keras.preprocessing.sequence import pad_sequences
import numpy as np

# 定义函数将字符串序列编码为数值序列
def encode_sequence_list(seqs, feat_n=0):
    encoded_seqs = []
    for seq in seqs:
        encoded_seq = [char_to_int[c] for c in seq]
        encoded_seqs.append(encoded_seq)
    if feat_n > 0:
        encoded_seqs.append(np.zeros(feat_n))
    return pad_sequences(encoded_seqs, padding='post')

# 定义函数将数值序列解码回字符串序列
def decode_sequence_list(seqs):
    decoded_seqs = []
    for seq in seqs:
        decoded_seq = [int_to_char[i] for i in seq]
        decoded_seqs.append(decoded_seq)
    return decoded_seqs
    
# 将字符串序列编码为数值序列
encoded_seqs = encode_sequence_list(random_sequences)
# 打乱序列以确保数据的随机性
np.random.shuffle(encoded_seqs)

# 查看编码后数组的形状
print(encoded_seqs.shape)
# 输出: (25005, 8)

现在,我们有一个形状为 (25005, 8) 的数组,其中每个字符串序列被编码为一个包含8个数字的序列,每个数字代表一个字符。在将数据输入自编码器之前,将使用 MinMaxScaler 对数据进行缩放,这有助于提高神经网络的训练效率和性能。

代码语言:javascript复制
# 使用 MinMaxScaler 对数据进行缩放
from sklearn.preprocessing import StandardScaler, MinMaxScaler

scaler = MinMaxScaler()
scaled_seqs = scaler.fit_transform(encoded_seqs)

# 将数据分割为训练集和测试集
X_train = scaled_seqs[:20000]
X_test = scaled_seqs[20000:]

3. 构建自编码器

在本节中,将设计、构建并训练一个自编码器模型,用于学习数据的有效表示,并识别异常值。自编码器由编码器和解码器两部分组成,编码器将输入数据压缩成较低维度的表示,而解码器则尝试从这个表示中重构原始数据。

代码语言:javascript复制
from keras.models import Model, load_model
from keras.layers import Input, Dense, Dropout
from keras.callbacks import ModelCheckpoint, TensorBoard
from keras import regularizers


# 定义输入数据的维度
input_dim = X_train.shape[1]  # 特征数量
encoding_dim = 8  # 编码层维度
hidden_dim = int(encoding_dim / 2)  # 隐藏层维度

# 定义训练参数
nb_epoch = 30
batch_size = 128
learning_rate = 0.1

# 构建输入层
input_layer = Input(shape=(input_dim, ))

# 构建编码器
encoder = Dense(encoding_dim, activation="tanh", activity_regularizer=regularizers.l1(10e-5))(input_layer)
encoder = Dense(hidden_dim, activation="relu")(encoder)

# 构建解码器
decoder = Dense(encoding_dim, activation='relu')(encoder)
decoder = Dense(input_dim, activation='tanh')(decoder)

# 构建完整的自编码器模型
autoencoder = Model(inputs=input_layer, outputs=decoder)

# 编译模型
autoencoder.compile(optimizer='adam', loss='mse')

# 定义回调函数
checkpointer = ModelCheckpoint(filepath='autoencoder.h5', verbose=1, save_best_only=True)
tensorboard = TensorBoard(log_dir='./logs', histogram_freq=1)

# 训练模型
history = autoencoder.fit(X_train, X_train,
                          epochs=nb_epoch,
                          batch_size=batch_size,
                          shuffle=True,
                          validation_data=(X_test, X_test),
                          verbose=1,
                          callbacks=[checkpointer, tensorboard]).history

训练完成后,可以检查自编码器的性能,特别是它在最小化重构误差方面的表现。这通常通过观察训练和验证损失随时间的变化来完成。

4. 计算误差并找出异常

在这一步骤中,将使用训练好的自编码器来计算数据集中每个样本的重构误差,这将帮助我们识别异常值。首先,我们需要对整个数据集进行编码和缩放,然后使用自编码器模型进行预测,最后计算每个样本的均方误差(MSE)。

代码语言:javascript复制
# 对所有数据进行编码
encoded_seqs = encode_sequence_list(seqs_ds.iloc[:,0])
# 缩放数据
scaler = MinMaxScaler()
scaled_data = scaler.fit_transform(encoded_seqs)
# 使用自编码器进行预测
predicted = autoencoder.predict(scaled_data)
# 计算均方误差(MSE)
mse = np.mean(np.power(scaled_data - predicted, 2), axis=1)
# 将MSE添加到DataFrame中
seqs_ds['MSE'] = mse

将误差项存储在数据帧中后,可以看到自动编码器构造每个输入数据的程度。

5. 分析结果

在利用自编码器进行异常检测的过程中,确定合适的阈值是关键步骤。这个阈值将帮助我们区分正常数据和异常数据。由于我们的数据集中只有极小比例的数据是异常的(在本例中为0.02%),需要选择一个高百分位数作为阈值,以确保只有极少数的数据点被标记为异常。

一旦我们确定了阈值,就可以在数据集中添加一个新列,用于标记那些超过阈值的异常值。

最后,可以检查数据集中的异常值,确认它们是否与我们预先注入的异常值相匹配。

代码语言:javascript复制
['XYDC2DCA', 'TXSX1ABC','RNIU4XRE','AABDXUEI','SDRAC5RF']
代码语言:javascript复制
# 计算阈值
threshold = np.percentile(seqs_ds['MSE'], 99.98)

# 标记异常值
seqs_ds['MSE_Outlier'] = (seqs_ds['MSE'] > threshold).astype(int)

# 检查异常值
outliers = seqs_ds[seqs_ds['MSE_Outlier'] == 1]
print(outliers)

在本例中,发现了6个异常值,其中5个是预先注入的“真实”异常值。这表明我们的自编码器模型在识别异常方面表现良好。通过这种方法,可以有效地识别和处理数据集中的异常值,从而提高数据质量并为进一步的分析和决策提供支持。

总结

在本教程中,利用自编码器算法,通过以下步骤成功识别并处理了数据集中的异常值:

  1. 数据生成:生成了25,000个符合特定模式的字符串序列,并掺入了5个异常值,以模拟真实场景中的异常数据。
  2. 数据预处理:将字符串序列转化为数值形式,并进行了缩放处理,以便适配神经网络模型。
  3. 模型构建与训练:设计并训练了一个包含编码器和解码器的自编码器模型,使其能够学习并重构正常数据模式。
  4. 误差分析与异常识别:基于自编码器的重构误差,设定阈值来识别数据中的异常值。
  5. 结果评估:对模型的异常检测结果进行了评估,确认其能准确识别预设的异常。

这一过程不仅验证了自编码器在异常检测上的有效性,还为处理含异常值的数据集提供了一套实用的方法论,适用于金融欺诈检测、网络安全监控和工业质量控制等多个领域。同时,也强调了设定合适阈值的重要性,并指出了根据数据特性调整模型参数的必要性,以提高模型的精确度和稳定性。

参考

  • https://towardsdatascience.com/a-keras-based-autoencoder-for-anomaly-detection-in-sequences-75337eaed0e5
  • https://github.com/a-agmon/experiments/blob/master/Sequence_Anomaly_Detection-NN.ipynb

0 人点赞