异常检测

 
异常检测与诊断
https://zhuanlan.zhihu.com/c_1312178717184712705


【KDD20】多变量时间序列异常检测算法之USAD:对抗性训练AE
https://zhuanlan.zhihu.com/p/548441110



多变量时间序列异常检测数据集整理及标准化处理代码合集
https://zhuanlan.zhihu.com/p/544335741


时序数据造假
Current Time Series Anomaly Detection Benchmarks are Flawed and are Creating the Illusion of Progress
https://arxiv.org/abs/2009.13807
    

 

    

 

    

 
根据序列生成下一时刻的序列,判断该序列与正常序列的差异,大为异常,近似为正常 
- 滑动窗口构造数据
- 取30天/最近100的交易,第101笔为标签
- 用100笔去生成一笔 
- 在异常检测中,该方法主要思想是大部分数据是正常的,学习正常数据特征,生成正常数据,再与正常数据比较
  - 接近为正常,远离为异常 
  - 适用于正常数据较多的场景 
  - 不同于二分类的是,生成的是数据,不是类别 


如果是自监督
- 可以计算生成的数据与每个序列数据的差异
- 将这种差异作为一特征,来衡量y_out与y之间的不同 
- 这应该是利用了差分,小波变换,实际上深度学习网络应该能自己学习到这种不同

    

 
学习最近100笔X,标签为长度为100的由0或1构成向量seq
- 0-正常,1-异常
- X与y位置上一一对应 
- 依然是将X映射到seq的维度上,但损失函数是100个交叉熵的和
- 除了天,小时,分钟外,增加两笔交易之间的时差,单位为秒
- 

 

    

 


 

  

 

  

 


LSTM

 
import numpy as np
import torch
import torch.nn as nn
from sklearn.preprocessing import StandardScaler
from torch.utils.data import DataLoader, TensorDataset

# 数据加载与预处理
def load_data(file_path, seq_length=30, split_ratio=0.8):
      data = np.loadtxt(file_path)  # 形状: (时间步, 特征维度)
      scaler = StandardScaler()
      scaled_data = scaler.fit_transform(data)

      # 构建滑动窗口序列
      sequences, targets = [], []
      for i in range(len(scaled_data) - seq_length):
         seq = scaled_data[i:i+seq_length]
         label = scaled_data[i+seq_length, 0]  # 预测第一维特征的下一个时间点
         sequences.append(seq)
         targets.append(label)
      
      # 划分训练集与验证集
      split_idx = int(len(sequences) * split_ratio)
      train_data = torch.FloatTensor(sequences[:split_idx])
      train_labels = torch.FloatTensor(targets[:split_idx])
      val_data = torch.FloatTensor(sequences[split_idx:])
      val_labels = torch.FloatTensor(targets[split_idx:])
      
      return DataLoader(TensorDataset(train_data, train_labels), batch_size=64, shuffle=True), \
            DataLoader(TensorDataset(val_data, val_labels), batch_size=64)

# LSTM模型定义
class LSTMPredictor(nn.Module):
      def __init__(self, input_size=38, hidden_size=64, output_size=1):
         super().__init__()
         self.lstm = nn.LSTM(input_size, hidden_size, batch_first=True)
         self.fc = nn.Linear(hidden_size, output_size)
      
      def forward(self, x):
         out, _ = self.lstm(x)  # out形状: (batch, seq_len, hidden_size)
         return self.fc(out[:, -1, :])  # 取最后一个时间步输出

# 训练流程
def train_model(train_loader, val_loader, epochs=50):
      model = LSTMPredictor()
      criterion = nn.MSELoss()
      optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)
      
      for epoch in range(epochs):
         model.train()
         for X, y in train_loader:
            optimizer.zero_grad()
            outputs = model(X)
            loss = criterion(outputs.squeeze(), y)
            loss.backward()
            optimizer.step()
         
         # 验证阶段
         model.eval()
         val_loss = 0
         with torch.no_grad():
            for X_val, y_val in val_loader:
                  preds = model(X_val)
                  val_loss += criterion(preds.squeeze(), y_val).item()
         print(f"Epoch {epoch+1}, Val Loss: {val_loss/len(val_loader):.4f}")
      
      return model

# 示例调用
if __name__ == "__main__":
      train_loader, val_loader = load_data("machine-1-1_train.txt")
      model = train_model(train_loader, val_loader)
      
    

 

    

 

    

 
SMD 数据集包含 28 台服务器连续 5 周运行的 38 维时序指标(CPU 负载、内存使用率等),采样间隔 1 分钟。
数据按机器分组存储为machine-x-y.txt,训练集与测试集按 1:1 划分,测试集包含异常标签。
该数据集适用于多变量时序预测与异常检测任务。
    

关键实现细节

 

数据标准化
使用StandardScaler对每个特征维度进行 Z-score 标准化,消除量纲差异对模型训练的影响。
滑动窗口构造
将时序数据转换为监督学习格式,设定seq_length=30表示用过去 30 分钟的数据预测下一时刻值。
模型结构
LSTM 层捕获时序依赖关系,全连接层将隐状态映射到预测值。输入维度 38 对应数据特征数,输出维度 1 表示单步预测。
训练策略
采用均方误差(MSE)作为损失函数,Adam 优化器动态调整学习率,分批次训练提升收敛效率。
   

扩展方向

 

多任务预测:修改输出层维度,同时预测多个特征值
异常检测:结合预测误差与测试集标签计算阈值,识别偏离正常模式的异常点
模型优化:引入注意力机制或堆叠 LSTM 层以提升复杂模式捕捉能力
该方案可直接应用于服务器指标预测任务,通过调整超参数(如序列长度、隐层维度)适配不同场景需求。


 


 
import pandas as pd
import numpy as np
import torch
import torch.nn as nn
from sklearn.preprocessing import MinMaxScaler

# 生成模拟时序数据
def generate_time_series(start_date, num_steps, freq='D'):
      timestamp = pd.date_range(start=start_date, periods=num_steps, freq=freq)
      trend = np.linspace(0, 5, num_steps)
      seasonality = 10 * np.sin(np.linspace(0, 10*np.pi, num_steps))
      noise = np.random.normal(0, 1, num_steps)
      value = trend + seasonality + noise
      
      return pd.DataFrame({
         'timestamp': timestamp,
         'value': value.astype(np.float32),
         'feature1': np.random.uniform(0, 10, num_steps),  # 附加随机特征
         'feature2': np.random.randint(0, 5, num_steps)
      }).set_index('timestamp')

# 参数配置
SEQ_LENGTH = 7  # 滑动窗口长度
BATCH_SIZE = 16
HIDDEN_SIZE = 32
EPOCHS = 50

# 生成数据
df = generate_time_series('2025-01-01', 100)
scaler = MinMaxScaler()
scaled_data = scaler.fit_transform(df.values)

# 构建训练集
def create_sequences(data, seq_length):
      sequences = []
      targets = []
      for i in range(len(data)-seq_length):
         sequences.append(data[i:i+seq_length])
         targets.append(data[i+seq_length, 0])  # 预测主特征
      return torch.FloatTensor(sequences), torch.FloatTensor(targets)

X, y = create_sequences(scaled_data, SEQ_LENGTH)
train_size = int(0.8 * len(X))
train_X, test_X = X[:train_size], X[train_size:]
train_y, test_y = y[:train_size], y[train_size:]

# 定义RNN模型
class TimeSeriesRNN(nn.Module):
      def __init__(self, input_size, hidden_size, output_size):
         super().__init__()
         self.rnn = nn.RNN(input_size, hidden_size, batch_first=True)
         self.fc = nn.Linear(hidden_size, output_size)

      def forward(self, x):
         out, _ = self.rnn(x)
         return self.fc(out[:, -1, :])

model = TimeSeriesRNN(input_size=3, hidden_size=HIDDEN_SIZE, output_size=1)
criterion = nn.MSELoss()
optimizer = torch.optim.Adam(model.parameters())

# 训练过程
for epoch in range(EPOCHS):
      model.train()
      outputs = model(train_X)
      loss = criterion(outputs.squeeze(), train_y)
      
      optimizer.zero_grad()
      loss.backward()
      optimizer.step()
      
      if (epoch+1) % 10 == 0:
         print(f'Epoch {epoch+1}, Loss: {loss.item():.4f}')

# 预测演示
model.eval()
with torch.no_grad():
      test_pred = model(test_X).numpy()
      test_actual = test_y.numpy()
      
# 可视化对比(需matplotlib)
import matplotlib.pyplot as plt
plt.plot(test_actual, label='Actual')
plt.plot(test_pred, label='Predicted')
plt.legend()
plt.show()


 
单变量时序特征
- 这里实际上只有一个变量,另外一个变量是随机噪声
- 预测就是取这个真实数据变量的下一个值作为label 
-
 

 

  

 
y1 y2 y3 y4 y5 y6 y7 y8 
0   0  0  0  0  1  0  0 
x1 x2 x3 x4 x5 x6 x7 x8 
对于异常检测来说,可以增加一个维度,就是标签的维度,这样就可以按这种方式进行预测了

但这并不合适
- 这种方式生成是的,或者说学习的是序列本身的规律,并不学习异常的规律
  - 异常数据比例千分之一不到,在深度学习中被自动冲消... 

可以拆解为两个模型
- 一个模型为生成模型预测正常序列,学习客户正常行为模式
- 一个模型学习上一个模型预测结果与标签之间的差异,
- 0-标记为正常,1-异常 
- 差异小就是正常,差异大就是异常 
   

 

y1 y2 y3 y4 y5 y6 y7 y8 
0   0  0  0  0  1  0  0 
x1 x2 x3 x4 x5 x6 x7 x8 

还有另外一种思路,就是直接的序列[x1,x2,...,xi,...,xn] -- [y1,y2,...,yi,...,yn]
- [batch_size,seq_len,embedding_dim] 
      |
- [batch_size,seq_len,m] 
      |
- [batch_size,seq_len,n] 
      |
- [batch_size,seq_len,1] 
   
变换的是embedding_dim维度,但在变换的过程中,却是从多个维度进行学习 
- 自身维度 
- 整句话的维度 
- y6之所以为1是因为自身的信息与整个序列的信息不符所导致的 
   - 这就要有自身信息
   - 还有自身相对于整个序列的信息 

 

  

 


OmniAnomaly

 
https://github.com/NetManAIOps/OmniAnomaly

OmniAnomaly是一种随机递归神经网络模型,
它结合了门控递归单元(GRU)和变分自编码器(VAE),
其核心思想是学习多元时间序列的正态模式,并使用重建概率进行异常判断。
    

代码下载

 
git clone https://github.com/smallcowbaby/OmniAnomaly && cd OmniAnomaly


    

数据集

 
SMD (Server Machine Dataset) is in folder ServerMachineDataset.

You can get the public datasets (SMAP and MSL) using:

wget https://s3-us-west-2.amazonaws.com/telemanom/data.zip && unzip data.zip && rm data.zip

cd data && wget https://raw.githubusercontent.com/khundman/telemanom/master/labeled_anomalies.csv
   

smap-msl

 

Anomaly Detection in Time Series Data Using LSTMs and Automatic Thresholding
https://github.com/khundman/telemanom/tree/master
    
pip install kaggle 


# make sure you have an Kaggle API key setup, then: 
kaggle datasets download -d patrickfleith/nasa-anomaly-detection-dataset-smap-msl && mv nasa-anomaly-detection-dataset-smap-msl.zip data.zip && unzip -o data.zip && rm data.zip && mv data/data tmp && rm -r data && mv tmp data

 

    

 


 

  

 


USAD

 
https://dl.acm.org/doi/pdf/10.1145/3394486.3403392

    
UnSupervised Anomaly Detection on Multivariate Time Series
多变量时间序列的无监督异常检测

 
在摘要中主要指出了本文的难题引出本文的方法。

原因出自Orange公司的IT系统的自动监测。
由于系统的整体的规模和复杂性,随着时间的推移,
用于推断正常和异常行为的测量所需的传感器数量急剧增加,使传统的基于专家的监督方法变慢或容易出错。

所以本文提出了一个非监督方法,
而前面所说的通过多传感器数据来推断系统正常或异常的问题其实就是多变量时间序列异常检测问题。

最后该方法在五种公共数据集上以及Orange公司提供的真实数据集上进行测试。

    

 

【KDD20】多变量时间序列异常检测算法之USAD:对抗性训练AE
https://zhuanlan.zhihu.com/p/548441110

    

 
https://github.com/73xuetu/usad

G:\wks\kejian\zhunbei\dl_kejian\anomaly_detection\usad 


 


 


 


 

  

 


多级小波分解网络

 
Multilevel Wavelet Decomposition Network for Interpretable Time Series Analysis
用于可解释时间序列分析的多级小波分解网络

https://blog.51cto.com/u_14344/12019166

    

 

    

 


 

  

 


时序数据集·SMD

 
数据集简介

数据集收集的是28个机器连续5周的数据,相邻两组数据间间隔一分钟。

总共收集28个机器的数据,每个机器收集38个维度(变量)的信息。

训练集与测试集的数据量是1:1的,训练集无label,测试集有label。

时间信息是隐匿的

   

    

 
数据文件中内容说明

文件名采用machine-x-y.txt的形式,其中x代表组,y是组里的index,每一个machine-x-y代表一个具体的机器。

train:包含数据的前半部分,作为训练集,无标签。

test:包含数据的后半部分,作为测试集,有标签。

test_label: 测试集标签,表明某一时间节点处是否处于异常状态。

interpretation_label:该数据集给出异常点的具体异常
    

 
链接:https://pan.baidu.com/s/1fgMz1m50vNQaECscT11DRA 

提取码:loix 


https://zhuanlan.zhihu.com/p/544335741


    

 

    

 

    

 

    

 


 

  

 


参考