Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions reliability/GIP-MI-demo/GCN-Informer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# GCN-Informer

本目录包含 GCN-Informer 的训练/验证/测试代码,以及配套的 CPU / Memory / Response Time 数据集样例。

## 目录结构

- `train_gcn_informer.py`:训练/验证/测试入口(默认使用 `data/myData/2024-05-5_train_constant.csv`)
- `data/myData/`:数据集 CSV
- `models/`:模型组件(Informer + GCN)
- `utils/`:mask 与指标计算
- `checkpoints/`:默认保存 `checkpoints/gcn_informer/best_model.pt`
- `results/`:默认输出 `results/gcn_informer/test_predictions.csv`

## 安装依赖

```bash
pip install -r requirements.txt
```

## 运行

默认训练(结束后会跑 test 并导出预测 CSV):

```bash
python train_gcn_informer.py
```

指定数据文件:

```bash
python train_gcn_informer.py --data_path data/myData/2024-07-08_train_non-constant.csv
```
Binary file not shown.
Binary file not shown.
10,635 changes: 10,635 additions & 0 deletions reliability/GIP-MI-demo/GCN-Informer/data/myData/2024-05-5_aging_constant.csv

Large diffs are not rendered by default.

9,390 changes: 9,390 additions & 0 deletions reliability/GIP-MI-demo/GCN-Informer/data/myData/2024-05-5_train_constant.csv

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Empty file.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
163 changes: 163 additions & 0 deletions reliability/GIP-MI-demo/GCN-Informer/models/attn.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
import torch
import torch.nn as nn
import torch.nn.functional as F

import numpy as np

from math import sqrt
from utils.masking import TriangularCausalMask, ProbMask

class FullAttention(nn.Module):
def __init__(self, mask_flag=True, factor=5, scale=None, attention_dropout=0.1, output_attention=False):
super(FullAttention, self).__init__()
self.scale = scale
self.mask_flag = mask_flag
self.output_attention = output_attention
self.dropout = nn.Dropout(attention_dropout)

def forward(self, queries, keys, values, attn_mask):
B, L, H, E = queries.shape
_, S, _, D = values.shape
scale = self.scale or 1./sqrt(E)

scores = torch.einsum("blhe,bshe->bhls", queries, keys)
if self.mask_flag:
if attn_mask is None:
attn_mask = TriangularCausalMask(B, L, device=queries.device)

scores.masked_fill_(attn_mask.mask, -np.inf)

A = self.dropout(torch.softmax(scale * scores, dim=-1))
V = torch.einsum("bhls,bshd->blhd", A, values)

if self.output_attention:
return (V.contiguous(), A)
else:
return (V.contiguous(), None)

class ProbAttention(nn.Module):
def __init__(self, mask_flag=True, factor=5, scale=None, attention_dropout=0.1, output_attention=False):
super(ProbAttention, self).__init__()
self.factor = factor
self.scale = scale
self.mask_flag = mask_flag
self.output_attention = output_attention
self.dropout = nn.Dropout(attention_dropout)

def _prob_QK(self, Q, K, sample_k, n_top): # n_top: c*ln(L_q)
# Q [B, H, L, D]
B, H, L_K, E = K.shape
_, _, L_Q, _ = Q.shape

# calculate the sampled Q_K
K_expand = K.unsqueeze(-3).expand(B, H, L_Q, L_K, E)
index_sample = torch.randint(L_K, (L_Q, sample_k)) # real U = U_part(factor*ln(L_k))*L_q
K_sample = K_expand[:, :, torch.arange(L_Q).unsqueeze(1), index_sample, :]
Q_K_sample = torch.matmul(Q.unsqueeze(-2), K_sample.transpose(-2, -1)).squeeze(-2)

# find the Top_k query with sparisty measurement
M = Q_K_sample.max(-1)[0] - torch.div(Q_K_sample.sum(-1), L_K)
M_top = M.topk(n_top, sorted=False)[1]

# use the reduced Q to calculate Q_K
Q_reduce = Q[torch.arange(B)[:, None, None],
torch.arange(H)[None, :, None],
M_top, :] # factor*ln(L_q)
Q_K = torch.matmul(Q_reduce, K.transpose(-2, -1)) # factor*ln(L_q)*L_k

return Q_K, M_top

def _get_initial_context(self, V, L_Q):
B, H, L_V, D = V.shape
if not self.mask_flag:
# V_sum = V.sum(dim=-2)
V_sum = V.mean(dim=-2)
contex = V_sum.unsqueeze(-2).expand(B, H, L_Q, V_sum.shape[-1]).clone()
else: # use mask
assert(L_Q == L_V) # requires that L_Q == L_V, i.e. for self-attention only
contex = V.cumsum(dim=-2)
return contex

def _update_context(self, context_in, V, scores, index, L_Q, attn_mask):
B, H, L_V, D = V.shape

if self.mask_flag:
attn_mask = ProbMask(B, H, L_Q, index, scores, device=V.device)
scores.masked_fill_(attn_mask.mask, -np.inf)

attn = torch.softmax(scores, dim=-1) # nn.Softmax(dim=-1)(scores)

context_in[torch.arange(B)[:, None, None],
torch.arange(H)[None, :, None],
index, :] = torch.matmul(attn, V).type_as(context_in)
if self.output_attention:
attns = (torch.ones([B, H, L_V, L_V])/L_V).type_as(attn).to(attn.device)
attns[torch.arange(B)[:, None, None], torch.arange(H)[None, :, None], index, :] = attn
return (context_in, attns)
else:
return (context_in, None)

def forward(self, queries, keys, values, attn_mask):
B, L_Q, H, D = queries.shape
_, L_K, _, _ = keys.shape

queries = queries.transpose(2,1)
keys = keys.transpose(2,1)
values = values.transpose(2,1)

U_part = self.factor * np.ceil(np.log(L_K)).astype('int').item() # c*ln(L_k)
u = self.factor * np.ceil(np.log(L_Q)).astype('int').item() # c*ln(L_q)

U_part = U_part if U_part<L_K else L_K
u = u if u<L_Q else L_Q

scores_top, index = self._prob_QK(queries, keys, sample_k=U_part, n_top=u)

# add scale factor
scale = self.scale or 1./sqrt(D)
if scale is not None:
scores_top = scores_top * scale
# get the context
context = self._get_initial_context(values, L_Q)
# update the context with selected top_k queries
context, attn = self._update_context(context, values, scores_top, index, L_Q, attn_mask)

return context.transpose(2,1).contiguous(), attn


class AttentionLayer(nn.Module):
def __init__(self, attention, d_model, n_heads,
d_keys=None, d_values=None, mix=False):
super(AttentionLayer, self).__init__()

d_keys = d_keys or (d_model//n_heads)
d_values = d_values or (d_model//n_heads)

self.inner_attention = attention
self.query_projection = nn.Linear(d_model, d_keys * n_heads)
self.key_projection = nn.Linear(d_model, d_keys * n_heads)
self.value_projection = nn.Linear(d_model, d_values * n_heads)
self.out_projection = nn.Linear(d_values * n_heads, d_model)
self.n_heads = n_heads
self.mix = mix

def forward(self, queries, keys, values, attn_mask):
B, L, _ = queries.shape
_, S, _ = keys.shape
H = self.n_heads

queries = self.query_projection(queries).view(B, L, H, -1)
keys = self.key_projection(keys).view(B, S, H, -1)
values = self.value_projection(values).view(B, S, H, -1)

out, attn = self.inner_attention(
queries,
keys,
values,
attn_mask
)
if self.mix:
out = out.transpose(2,1).contiguous()
out = out.view(B, L, -1)

return self.out_projection(out), attn
51 changes: 51 additions & 0 deletions reliability/GIP-MI-demo/GCN-Informer/models/decoder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import torch
import torch.nn as nn
import torch.nn.functional as F

class DecoderLayer(nn.Module):
def __init__(self, self_attention, cross_attention, d_model, d_ff=None,
dropout=0.1, activation="relu"):
super(DecoderLayer, self).__init__()
d_ff = d_ff or 4*d_model
self.self_attention = self_attention
self.cross_attention = cross_attention
self.conv1 = nn.Conv1d(in_channels=d_model, out_channels=d_ff, kernel_size=1)
self.conv2 = nn.Conv1d(in_channels=d_ff, out_channels=d_model, kernel_size=1)
self.norm1 = nn.LayerNorm(d_model)
self.norm2 = nn.LayerNorm(d_model)
self.norm3 = nn.LayerNorm(d_model)
self.dropout = nn.Dropout(dropout)
self.activation = F.relu if activation == "relu" else F.gelu

def forward(self, x, cross, x_mask=None, cross_mask=None):
x = x + self.dropout(self.self_attention(
x, x, x,
attn_mask=x_mask
)[0])
x = self.norm1(x)

x = x + self.dropout(self.cross_attention(
x, cross, cross,
attn_mask=cross_mask
)[0])

y = x = self.norm2(x)
y = self.dropout(self.activation(self.conv1(y.transpose(-1,1))))
y = self.dropout(self.conv2(y).transpose(-1,1))

return self.norm3(x+y)

class Decoder(nn.Module):
def __init__(self, layers, norm_layer=None):
super(Decoder, self).__init__()
self.layers = nn.ModuleList(layers)
self.norm = norm_layer

def forward(self, x, cross, x_mask=None, cross_mask=None):
for layer in self.layers:
x = layer(x, cross, x_mask=x_mask, cross_mask=cross_mask)

if self.norm is not None:
x = self.norm(x)

return x
131 changes: 131 additions & 0 deletions reliability/GIP-MI-demo/GCN-Informer/models/embed.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import torch
import torch.nn as nn
import torch.nn.functional as F

import math


class PositionalEmbedding(nn.Module):
def __init__(self, d_model, max_len=5000):
super(PositionalEmbedding, self).__init__()
# Compute the positional encodings once in log space.
pe = torch.zeros(max_len, d_model).float()
pe.require_grad = False

position = torch.arange(0, max_len).float().unsqueeze(1)
div_term = (torch.arange(0, d_model, 2).float() * -(math.log(10000.0) / d_model)).exp()

pe[:, 0::2] = torch.sin(position * div_term)
pe[:, 1::2] = torch.cos(position * div_term)

pe = pe.unsqueeze(0)
self.register_buffer('pe', pe)

def forward(self, x):
return self.pe[:, :x.size(1)]


class TokenEmbedding(nn.Module):
def __init__(self, c_in, d_model):
super(TokenEmbedding, self).__init__()
# torch.__version__ can be like "2.1.0+cu121"; avoid lexicographic compare (e.g. "1.10" vs "1.5").
version_core = torch.__version__.split("+", 1)[0]
major_minor = version_core.split(".", 2)[:2]
try:
major, minor = (int(major_minor[0]), int(major_minor[1]))
except Exception:
major, minor = (0, 0)
padding = 1 if (major, minor) >= (1, 5) else 2
self.tokenConv = nn.Conv1d(in_channels=c_in, out_channels=d_model,
kernel_size=3, padding=padding, padding_mode='circular')
for m in self.modules():
if isinstance(m, nn.Conv1d):
nn.init.kaiming_normal_(m.weight, mode='fan_in', nonlinearity='leaky_relu')

def forward(self, x):
x = self.tokenConv(x.permute(0, 2, 1)).transpose(1, 2)
return x


class FixedEmbedding(nn.Module):
def __init__(self, c_in, d_model):
super(FixedEmbedding, self).__init__()

w = torch.zeros(c_in, d_model).float()
w.require_grad = False

position = torch.arange(0, c_in).float().unsqueeze(1)
div_term = (torch.arange(0, d_model, 2).float() * -(math.log(10000.0) / d_model)).exp()

w[:, 0::2] = torch.sin(position * div_term)
w[:, 1::2] = torch.cos(position * div_term)

self.emb = nn.Embedding(c_in, d_model)
self.emb.weight = nn.Parameter(w, requires_grad=False)

def forward(self, x):
return self.emb(x).detach()


class TemporalEmbedding(nn.Module):
def __init__(self, d_model, embed_type='fixed', freq='t'):
super(TemporalEmbedding, self).__init__()
self.freq = freq
# 根据频率定义输入特征维度(与 create_time 的输出维度匹配)
self.input_features = 5 # second_sin, second_cos, minute_sin, minute_cos
# 使用线性层将时间特征映射到 d_model
self.linear = nn.Linear(self.input_features, d_model)

def forward(self, x):
# x 的形状为 (batch_size, seq_len, input_features)
return self.linear(x)


# class TemporalEmbedding(nn.Module):
# def __init__(self, d_model, embed_type='fixed', freq='t'):
# super(TemporalEmbedding, self).__init__()
#
# minute_size = 60;
# hour_size = 24;
# second_size = 60
#
# Embed = FixedEmbedding if embed_type == 'fixed' else nn.Embedding
# self.minute_embed = Embed(minute_size, d_model)
# self.hour_embed = Embed(hour_size, d_model)
# self.second_embed = Embed(second_size, d_model)
#
# def forward(self, x):
# x = x.long()
# second_x = self.second_embed(x[:, :, 0])
# minute_x = self.minute_embed(x[:, :, 1]) if hasattr(self, 'minute_embed') else 0.
# hour_x = self.hour_embed(x[:, :, 2])
# return second_x + minute_x + hour_x

class TimeFeatureEmbedding(nn.Module):
def __init__(self, d_model, embed_type='timeF', freq='h'):
super(TimeFeatureEmbedding, self).__init__()

freq_map = {'h': 4, 't': 5, 's': 6, 'm': 1, 'a': 1, 'w': 2, 'd': 3, 'b': 3}
d_inp = freq_map[freq]
self.embed = nn.Linear(d_inp, d_model)

def forward(self, x):
return self.embed(x)


class DataEmbedding(nn.Module):
def __init__(self, c_in, d_model, embed_type='fixed', freq='h', dropout=0.1):
super(DataEmbedding, self).__init__()
self.value_embedding = TokenEmbedding(c_in=c_in, d_model=d_model)
self.position_embedding = PositionalEmbedding(d_model=d_model)
# 统一使用 TemporalEmbedding
self.temporal_embedding = TemporalEmbedding(d_model=d_model, freq=freq)
self.dropout = nn.Dropout(p=dropout)

def forward(self, x, x_mark):
x1 = self.value_embedding(x)
x2 = self.position_embedding(x)
x3 = self.temporal_embedding(x_mark) # x_mark 来自 create_time 的输出
x = x1 + x2 + x3
x = x1 + x2 + x3
return self.dropout(x)
Loading