Python数据分析案例55——基于LSTM结构自编码器的多变量时间序列异常值监测

案例背景

时间序列的异常值检测是方兴未艾的话题。比如很多单变量的,一条风速,一条用电量这种做时间序列异常值检测,想查看一下哪个时间点的用电量异常。

多变量时间序列由不同变量随时间变化的序列组成,这些时间序列在实际应用中通常
来自不同的传感器或数据源。多变量时间序列异常检测是指在多个相互关联的时间序列中识
别出不符合正常模式的异常点。异常检测的应用场景包括IT 运维监控、交通流量管理、环
境检测、能源管理等。例如在空气质量检测数据中,通过监测PM2.5、二氧化碳等数据的变
化,可以及时发现污染事件,采取相应的环境保护措施。

但是如果变量很多的话,就比如网络上的某些流量他有很多条通道或者是监测的变量不止一个,他们随着时间变化都呈现了不同的趋势,而多变量的异常值检测就也可以用传统的方法来做,比如孤立森林,支持数据描述,异常因子等,使用机器学习的方法无监督进行异常值的标签预测。

但是也可以用深度学习的方法。一般来说都会用自编码器,中间的架构采用多层感知机mlp。但是今天我觉得可以用时间序列里面比较好的lstm循环神经网络去代替多层感知机结构,那么就需要把一个二维数据,也就是行代表不同的时间,列代表不同的变量给它构造为三维的循环神经网络的数据结构,放入神经网络去训练自编码器,然后再去进行解码来预测是否重构误差是否为异常值,这个其中就涉及到数据形状的一个转换的问题。以及在构建滑动窗口的时候会将数据的窗口样本量缺少,而造成前几个点无法预测的问题。今天这些问题都会在这个案例里面一一进行解答和演示。


数据介绍

任务描述:给定一段时期内的服务器性能监测数据,且是随时间记录等间隔的观测数据。
 多变量时间序列异常检测的输入表示为,其中n 表示时间戳的最大长度,k
表示输入特征数量。
 多变量时间序列异常检测任务的输出向量是 ,其中y_{i}表示第i 个时间戳
是否为异常,0 为正常,1 为异常。
数据:本数据集包含 25 个服务器在不同时间点收集的指标数据。每个指标表示服务器的
某种性能度量,例如 CPU 使用率、内存使用率等。数据采样间隔为 5 分钟,每条时间序
列代表一个指标。

train.csv:包含训练数据,数据形状 [时间步数,特征数] ,每个时间步表示一个采样时间
点,每个特征表示一个服务器指标。文件包含 26 列,第 1 列timestamp_(min)表示时间
戳,其余25 列feature_X 表示不同服务器指标数据。训练数据集中不包含异常标签。(因为是无监督)
test.csv:包含测试数据,数据形状 [时间步数,特征数]。其余含义同train.csv 文件说明。

我们需要做的就是预测test里面的全部数据点是否为异常值,给他们打上标签。

当然需要本次案例的数据和全部代码文件的同学还是可以参考:多变量异常值检测。


代码实现

我们采用新手友好的TensorFlow的keras框架,导入包

import os
import pandas as pd
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt 
import seaborn as snsplt.rcParams ['font.sans-serif'] ='SimHei'               #显示中文
plt.rcParams ['axes.unicode_minus']=False               #显示负号#from pandas.plotting import scatter_matrix
import pickle
import h5py
from scipy import statsfrom 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,Sequential
from tensorflow.keras.layers import Input, Dense,LSTM,RepeatVector, BatchNormalization,Dropout
from tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping
from keras.optimizers import Adam
from tensorflow.keras import regularizers
from tensorflow.keras.utils import plot_model
#sns.set(style='whitegrid', palette='muted', font_scale=1.5)

读取数据

data1=pd.read_csv('train.csv').set_index('timestamp_(min)').iloc[288*300:,:]  # 训练集
data1.head()

由于数据量太大,我这里就只取了288*300之后的数据,288是一天的数据量,我少取了300天的数据,因为还剩下4w多条数据,训练一个小网络是够的。

 描述性统计看看

data1.describe()

可以看到数据基本上都是最大值是一和最小值是零都已经进行了标准化,因此可以直接放入神经网络训练,而不需要进行额外的归一化或者是标准化。

查看前12个特征的时间序列走势图。

data1.iloc[:,:12].plot(figsize=(18,5))

查看后面13个特征的时间序列走势图。

data1.iloc[:,12:].plot(figsize=(18,5))

 

可以看到数据的范围都是在零跟一之间,并且都是有一定的周期性和季节性的波动。  而有的特征波动性挺大的可能就是异常点。

数据准备

时间序列需要构建滑动窗口特征,因为要构造为三维数据,所以我们会损失几个样本点。但是这样少了几个样本的特征之后,前几个样本的标签就没有办法预测了,因此我们可以用均值进行填充。保证数据的x的长度和y的长度是一模一样的。

window_size=288
def get_traindata(data1,    window_size = window_size):# 使用滑动窗口生成时间序列数据# 计算均值填充mean_values = data1.iloc[:, :].mean()padding_data = pd.DataFrame([mean_values] * (window_size - 1))# 在数据前面添加均值填充的数据df_padded = pd.concat([padding_data, data1], ignore_index=True)# 提取特征数据X = df_padded.iloc[:, :].to_numpy()# 构建滑动窗口X_windows = []for i in range(len(data1)):X_windows.append(X[i:i + window_size])X_train = np.array(X_windows) ; X= data1.to_numpy()return X_train,XX_train,X=get_traindata(data1, window_size = 288)
X_train.shape,X.shape

可以看到原来读取的数据是4.6万条,现在还是4.6万条。

测试集也是一样处理

data2=pd.read_csv('test.csv').set_index('timestamp_(min)')#.iloc[:1440,:]
X1_train,X1=get_traindata( data2, window_size = 288)
X1_train.shape,X1.shape


异常值监测

自编码器

我们首先来构造神经网络的架构层

model = Sequential()
model.add(LSTM(16, activation='relu', input_shape=(window_size, X.shape[1]), return_sequences=False))
model.add(BatchNormalization())  # 添加批量归一化层
model.add(Dropout(0.1))  # 添加Dropout层以减少过拟合的风险
model.add(RepeatVector(window_size))
model.add(LSTM(16, activation='relu'))
model.add(BatchNormalization())
model.add(Dropout(0.1))  # 再次添加Dropout层
model.add(Dense(X.shape[1]))
model.compile(optimizer=Adam(learning_rate=0.003), loss='mse')

查看模型信息

model.summary()

因为我安装的tf版本比较高,是不支持GPU的,因此我选择把神经元的数量都改小一点,总共的参数也就5000多个,不怎么多。

可以清楚的看到神经网络的架构主要是数据进入过lstm层,然后再经过层批量归一化,防止梯度消失。然后再经过一个丢包层进行一定的正则化,然后再经过时间的一个向量重复层将它恢复为三维的数据,然后再经过lstm层,再经过成批量归一化,然后再经过一个丢包层,最后用多层感知机进行输出。输出的维度跟x的变量数量是一样的。因为是自编码器嘛,相当于是三维的数据输入,二维的数据输出。相当于是自己用自己进行训练和监督,重构这个误差。

模型训练:

# 训练模型
history =model.fit(X_train, X, epochs=4, batch_size=512, shuffle=False,verbose=1).history

我就训练了四轮,因为每一轮都还有点久的。而且四轮基本上已经收敛了。

产看损失变化图

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');

# 计算重构误差


reconstructed_X = model.predict(X_train)
print(reconstructed_X.shape)
mse = np.mean(np.power(X - reconstructed_X, 2), axis=1)
mse

最后得到一个误差列,相当于是每一个样本的误差数值。

我们将其转化为数据框,方便进行存储和后面的筛选

testMSE = mean_squared_error(X.transpose(), reconstructed_X.transpose(), multioutput='raw_values')
error_df = pd.DataFrame({'reconstruction_error': testMSE,'true_value': data1['feature_24']})
print(error_df.shape)
error_df.head()

对误差画个直方图看看。

error_df['reconstruction_error'].plot.hist()

可以看到直方图是一个很明显的右偏分布,它存在很多极大值,这些极大的异常值可能就是所谓的异常点。

我们选择95%作为分位数及95%以下的数据是作为正常值,5%的数据作为异常点,我们去找到这个切割阈值的分位数。

threshold = error_df.reconstruction_error.quantile(q=0.95)
threshold

 

然后我们按这个阈值标记为正常值和异常值,查看其数量。

mlp_label=np.where( error_df.reconstruction_error.to_numpy()>threshold,1,0)
error_df['pred_class']=mlp_label
error_df['pred_class'].value_counts()

我们对重构误差进行一定的可视化。

groups = error_df.groupby('pred_class')
fig, ax = plt.subplots(figsize=(10,3),dpi=128)
for name, group in groups:if name == 1:MarkerSize = 3 ; Color = 'orangered' ; Label = 'Fraud' ; Marker = 'd'else:MarkerSize = 3 ; 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()

X轴是代表不同的样本,y轴代表重构的误差,我们把阈值直线也画在了上面,可以清楚的看到重构误差大的一些点,可能就被认为是异常点。

我们把最后一个特征的序列图进行可视化,并在上面标记异常值的点。

def plot_Abnormal(error_df,mode='Autoencoder'):plt.figure(figsize=(14, 4),dpi=128)plt.plot(error_df.index, error_df["true_value"], label='Power')# Plotting the pred_class scatter plot for points where pred_class is 1pred_class_1 = error_df[error_df["pred_class"] == 1]plt.scatter(pred_class_1.index, pred_class_1["true_value"], color='orange', label='Abnormal value')# Adding labels and legendplt.xlabel('Time')plt.ylabel('Power')plt.title(mode)plt.legend()plt.grid(True)plt.show()
plot_Abnormal(error_df,mode='Autoencoder')

可以看到这些较高或者较低的值被标为了黄色的点就是异常值,,由于这是单个特征,所以说不是很明显。只是有的比较异常的位置被标为了异常值,因为异常值的重构误差是所谓的特征一起决定的。但是在图表上只能对于一个特征,一个特征的可视化,这里就只可视化的这一个来观察一下。目前感觉效果是还可以的。

测试集上预测

我们对测试集的数据同样进行重构误差的预测计算,然后将其装入数据框查看。

# 计算重构误差
reconstructed_X1 = model.predict(X1_train)
print(reconstructed_X1.shape)
testMSE1 = mean_squared_error(X1.transpose(), reconstructed_X1.transpose(), multioutput='raw_values')
error_df1 = pd.DataFrame({'reconstruction_error': testMSE1,'true_value': data2['feature_24']})
print(error_df1.shape)
error_df1.head()

error_df1['reconstruction_error'].plot.hist()

 

可以看到分布跟训练集是一致的,基本上都是一个右偏分布,存在较多的较大的异常值,这些异常值可能就是异常点。

同样我们找到95%的分位的分位数作为阈值。

threshold1 = error_df1.reconstruction_error.quantile(q=0.95)
threshold1

同样我们查看其划分出来的正常值和异常值的数量对比。

mlp_label1=np.where( error_df1.reconstruction_error.to_numpy()>threshold1,1,0)
error_df1['pred_class']=mlp_label1
error_df1['pred_class'].value_counts()

groups1 = error_df1.groupby('pred_class')
fig, ax = plt.subplots(figsize=(10,3),dpi=128)
for name, group in groups1:if name == 1:MarkerSize = 3 ; Color = 'orangered' ; Label = 'Fraud' ; Marker = 'd'else:MarkerSize = 3 ; Color = 'b' ; Label = 'Normal' ; Marker = 'o'ax.plot(group.index, group.reconstruction_error, linestyle='',color=Color,label=Label,ms=MarkerSize,marker=Marker)ax.hlines(threshold1, 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()

可以清楚的看到,算出来的很多点误差都还比较高,都是可能潜在的异常值。

同样对最后一个特征的序列图上,把异常点标上进行可视化。

plot_Abnormal(error_df1,mode='Autoencoder')

感觉效果还行,它在某些边缘的极大值,极小值上都标记为了异常点。至于为什么有的地方可能极大值还高一些,它没有标记为异常情况,那可能是因为别的特征一起的作用使得它们的误差并没有那么大。

最后我们将测试集的预测结果储存下来,这样就可以进行提交了。

## 储存
error_df1['pred_class'].to_csv('结果.csv')


总结

以前的自编码器基本上都是使用mlp架构,这里使用了lstm架构完成了一个数据的转换的一个思维。其实也可以进行更高维的转换,比如用二维CNN,或者用同样三维数据结构的一维卷积或者是transformer注意力机制等网络结构。或者是图神经网络等等的方法去,进行不同的这个自编码器架构的构建。

当然由于是无监督的,所以也不好评价哪种方法效果是比较好,只是说这些结构都是可以尝试的。在完成了数据的这种转换之后,神经网络的内部架构都是可以随便更改的,就像搭积木一样是很简单的。也非常容易创新。


创作不易,看官觉得写得还不错的话点个关注和赞吧,本人会持续更新python数据分析领域的代码文章~(需要定制类似的代码可私信)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/50772.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

小黄人欢乐来袭,国漫萌物大集结

最近上映的《神偷奶爸》4不知道大家有没有去看,小黄人作为该系列电影的标志性角色,继续以其呆萌可爱的形象和幽默搞怪的性格赢得了观众的喜爱。 在中国动漫中,也有许多可爱且深受观众喜爱的萌物角色。这些角色以其独特的形象、有趣的性格和与…

数据结构day6

一、思维导图 二、模拟面试 typedef定义函数指针的方式typedef int(*p)(int,int);对void*指针的理解,相关应用万能指针,可以定义形参用来接收任意类型的指针变量,也可以定义函数用来返回任意类型的指针变量例如malloc函数在堆区申请内存&…

HTTP协议和RPC协议的区别是什么

从功能层面来说,HTTP协议是一个应用层的超文本传输协议,它是万维网数据通信的一个基础,主要服务在网页端和服务端的一个数据传输上。而RPC是一个远程过程调用协议,它是定位在实现分布式应用之间的一个数据通信,屏蔽了通…

SpringBoot入门:如何新建SpringBoot项目(保姆级教程)

在本文中,我们将演示如何新建一个基本的 Spring Boot 项目。写这篇文章的时候我还是很惊讶的,因为我发现有些java的初学者,甚至工作10年的老员工居然并不会新建一个SpringBoot项目,所以特别出了一篇文章来教大家新建一个SpringBoo…

数据挖掘-数据预处理

来自🥬🐶程序员 Truraly | 田园 的博客,最新文章首发于:田园幻想乡 | 原文链接 | github (欢迎关注) 文章目录 3.3.1 数据的中心趋势平均数和加权平均数众数,中位数和均值描述数据的离散程度 &a…

VSCode | 修改编辑器注释的颜色

1 打开VsCode的设置进入settings.json 2 添加如下代码 "editor.tokenColorCustomizations": {"comments": "#17e917"},3 保存即可生效

Linux源码阅读笔记14-IO体系结构与访问设备

IO体系结构 与外设通信通常称为输入输出,一般缩写为I/O。在实现外设IO的时候,内核必须处理三个可能出现的问题: 必须根据具体的设备类型和模型,使用各种方法对硬件寻址。内核必须向用户应用程序和系统工具提供访问各种设备的方法…

hugging face 使用教程———快速入门

概述 本篇存在的意义是快速介绍hugging face使用,梳理主要部件,梳理易混淆概念。原因是:目前hugging face的使用,官方放在了3个地方(参考链接部分):使用文档、NLP教程、Transformers git的readm…

Vue3+Element Plus 实现table表格中input的验证

实现效果 html部分 <template><div class"table"><el-form ref"tableFormRef" :model"form"><el-table :data"form.detailList"><el-table-column type"selection" width"55" align&…

基于springboot+vue+uniapp的养老院系统小程序

开发语言&#xff1a;Java框架&#xff1a;springbootuniappJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#…

repo 工具安装和使用教程(windows+gitee)

repo是什么 官方的定义&#xff1a;Repo是谷歌用python脚本写的调用git的一个脚本&#xff0c;可以实现管理多个git库。 Android的源代码使用Repo 命令行工具来管理多个git仓库&#xff0c;大概有百多个。要想克隆和管理百多个 Git 仓库&#xff0c;不是一件简单的事情。Repo 命…

LoRaWAN网络中的chirpstack

目录 一、chirpstack介绍 二、网关与chirpstack之间的通信 三、NS与AS之间的通信 1、Protobuf 2、gRPC 一、chirpstack介绍 ChirpStack 是一个开源的 LoRaWAN 网络服务器&#xff0c;可用于 设置私有或公共 LoRaWAN 网络。ChirpStack 提供了一个 Web 界面 用于管理网关、设…

HBuilder X中配置vue-cli项目和UI库

目录 一.前端项目结构 二.在HBuilder X中搭建vue-cli项目 1. 安装node.js前端环境 2. HBuilder X创建一个vue-cli项目 3. vue-cli项目结构 4. 如何运行前端项目 5. 创建组件 6. 组件路由(页面跳转) 6.1 创建router目录 6.2 使用路由 6.3 在main.js中配置路由 6.4 路…

【IoTDB 线上小课 05】时序数据文件 TsFile 三问“解密”!

【IoTDB 视频小课】持续更新&#xff01;第五期来啦~ 关于 IoTDB&#xff0c;关于物联网&#xff0c;关于时序数据库&#xff0c;关于开源... 一个问题重点&#xff0c;3-5 分钟详细展开&#xff0c;为大家清晰解惑&#xff1a; IoTDB 的 TsFile 科普&#xff01; 了解了时序数…

系统移植(三)u-boot移植 ① 相关概念

文章目录 一、u-boot概念&#xff08;一&#xff09;概念&#xff08;二&#xff09;获取u-boot源码1.从u-boot官网获取2. 从 STM官网3. 开发板厂商获取 &#xff08;三&#xff09;分析u-boot源码1. u-boot源码的目录结构2. 获取make的帮助信息3. 分析README文件 &#xff08;…

讨逆猴子剪切板,浏览器复制失败?

讨逆猴子剪切板&#xff0c;复制失败&#xff1f; 问题&#xff1a;本地开发情况下可以直接复制&#xff0c;公网就不行了…触发了安全机制。 const link 内容;navigator.clipboard.writeText(link);报错&#xff1a; 解决方案&#xff1a; if (navigator.clipboard &&…

使用代理IP进行本地SEO优化:如何吸引附近的客户?

在今天竞争激烈的互联网时代&#xff0c;如何利用代理IP进行本地SEO优化并吸引附近的客户已经成为许多企业和网站面临的关键挑战。本文将探讨使用代理IP的策略和技巧&#xff0c;以帮助公司提高在本地市场的可见性和吸引力&#xff0c;从而扩大本地客户群体。 1. 代理IP在本地…

Windosw下Visual Studio2022编译FFmpeg(支持x264、x265、fdk-acc)

FFmpeg 7.0 版本移除了 6.0 之前已弃用的 API&#xff0c;无法向下兼容。所以编译的版本选择FFmpeg 6.1.1。 一、安装Visual Studio2022 可参考另外一篇文章&#xff1a;Windows安装Visual Studio2022 QT5.15开发环境_qt5.15.2 vs2022-CSDN博客 二、安装MSYS2 下载地址&…

lua 游戏架构 之 游戏 AI (四)ai_autofight_find_target

定义一个名为 ai_autofight_find_target 的类&#xff0c;继承自 ai_base 类。 lua 游戏架构 之 游戏 AI &#xff08;一&#xff09;ai_base-CSDN博客文章浏览阅读237次。定义了一套接口和属性&#xff0c;可以基于这个基础类派生出具有特定行为的AI组件。例如&#xff0c;可…

大数据-50 Redis 分布式锁 乐观锁 Watch SETNX Lua Redisson分布式锁 Java实现分布式锁

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…