Вопрос или проблема
У меня есть модель прогнозирования многомерного временного ряда, изначально использующая расширенную временную свертку для извлечения временных зависимостей, которую я пытался заменить на многоголовое самовнимание с остаточным соединением, но когда я стараюсь обучить её на наборе данных PEMSD4, я получаю следующую ошибку: RuntimeError: mat1 and mat2 shapes cannot be multiplied (131072×12 and 64×32). Вот модель:
import torch
import torch.nn as nn
import torch.nn.functional as F
from graph_constuct_fgwn_2 import graph_constructor
from layer_mtgnn import *
import numpy as np
from DSTAGNN_my import MultiHeadAttention, ScaledDotProductAttention, Embedding
class dnconv(nn.Module):
def __init__(self):
super(dnconv, self).__init__()
def forward(self, x, A):
if len(A.size()) == 2:
A = A.unsqueeze(0).repeat(x.shape[0], 1, 1)
x = torch.einsum('nvw, ncwl->ncvl', [A, x])
return x.contiguous()
class linear(nn.Module):
def __init__(self, c_in, c_out):
super(linear, self).__init__()
self.mlp = torch.nn.Conv2d(c_in, c_out, kernel_size=(1, 1), padding=(0, 0), stride=(1, 1), bias=True)
def forward(self, x):
return self.mlp(x)
class gcn_modify(nn.Module):
def __init__(self, c_in, c_out, dropout, support_len=3, order=2):
super(gcn_modify, self).__init__()
self.nconv = dnconv()
c_in = (order * support_len + 1) * c_in
self.mlp = linear(c_in, c_out)
self.dropout = dropout
self.order = order
def forward(self, x, support):
out = [x]
x1 = self.nconv(x, support)
out.append(x1)
for k in range(2, self.order + 1):
x2 = self.nconv(x1, support)
out.append(x2)
x1 = x2
h = torch.cat(out, dim=1)
h = self.mlp(h)
h = F.dropout(h, self.dropout, training=self.training)
return h
class gwnet(nn.Module):
def __init__(self, device, num_nodes, dropout=0.3, gcn_bool=True, addaptadj=True, seq_length=12,
in_dim=1, out_dim=12, residual_channels=32, dilation_channels=32, skip_channels=64, end_channels=128,
layers=2, embed_dim=10, dropout_ingc=0.5, eta=1, gamma=0.001,
m=0.9, highway=False, batch_size=64, tc_dropout=0.1, n_heads=8, d_model=64):
super(gwnet, self).__init__()
self.dropout = dropout
self.layers = layers
self.gcn_bool = gcn_bool
self.addaptadj = addaptadj
self.filter_convs = nn.ModuleList()
self.gate_convs = nn.ModuleList()
self.residual_convs = nn.ModuleList()
self.skip_convs = nn.ModuleList()
self.bn = nn.ModuleList()
self.gconv = nn.ModuleList()
self.gconv_1 = nn.ModuleList()
self.norm = nn.ModuleList()
self.attention_layers = nn.ModuleList()
self.d_model = d_model
self.n_heads = n_heads
self.start_conv = nn.Conv2d(in_channels=in_dim,
out_channels=residual_channels,
kernel_size=(1, 1))
self.seq_length = seq_length
self.receptive_field = layers * 4 + 1
self.temporal_embedding = Embedding(seq_length, d_model, residual_channels, 'T')
for i in range(layers):
self.attention_layers.append(
MultiHeadAttention(device, d_model, d_model // n_heads, d_model // n_heads, n_heads, num_nodes)
)
self.residual_convs.append(nn.Conv2d(in_channels=d_model,
out_channels=residual_channels,
kernel_size=(1, 1)))
if self.seq_length > self.receptive_field:
self.skip_convs.append(nn.Conv2d(in_channels=d_model,
out_channels=skip_channels,
kernel_size=(1, self.seq_length - self.receptive_field + 1)))
else:
self.skip_convs.append(nn.Conv2d(in_channels=d_model,
out_channels=skip_channels,
kernel_size=(1, 1)))
if self.gcn_bool:
self.gconv.append(gcn_modify(d_model, residual_channels, dropout, support_len=1, order=2))
self.gconv_1.append(gcn_modify(d_model, residual_channels, dropout, support_len=1, order=2))
if self.seq_length > self.receptive_field:
self.norm.append(LayerNorm((residual_channels, num_nodes, self.seq_length - self.receptive_field + 1),
elementwise_affine=True))
else:
self.norm.append(LayerNorm((residual_channels, num_nodes, 1),
elementwise_affine=True))
self.end_conv_1 = nn.Conv2d(in_channels=skip_channels,
out_channels=end_channels,
kernel_size=(1, 1),
bias=True)
self.end_conv_2 = nn.Conv2d(in_channels=end_channels,
out_channels=out_dim,
kernel_size=(1, 1),
bias=True)
self.skip0 = nn.Conv2d(in_channels=in_dim, out_channels=skip_channels, kernel_size=(1, 1), bias=True)
self.skipE = nn.Conv2d(in_channels=residual_channels, out_channels=skip_channels, kernel_size=(1, 1), bias=True)
self.idx = torch.arange(num_nodes).to(device)
self.graph_construct = graph_constructor(num_nodes, embed_dim, device, seq_length, eta=eta, in_dim=in_dim,
gamma=gamma, dropout=dropout_ingc, m=m, batch_size=batch_size)
def forward(self, input, pred_time_embed=None):
in_len = input.size(3)
if in_len < self.receptive_field:
x = nn.functional.pad(input, [self.receptive_field - in_len, 0, 0, 0])
else:
x = input
new_supports = None
gl_loss = None
dy_adj = None
adj_norm = None
if self.gcn_bool:
adp, resolution_static, node_embed, gl_loss_from, dy_nodeEmbed, adj_norm = self.graph_construct(input)
gl_loss = gl_loss_from
new_supports = resolution_static
dy_adj = adp
skip = self.skip0(F.dropout(x, self.dropout, training=self.training))
x = self.start_conv(x)
# Применить временное встраивание
x = x.permute(0, 3, 2, 1) # [batch_size, seq_length, num_nodes, channels]
x = self.temporal_embedding(x, x.size(0))
x = x.permute(0, 3, 2, 1) # [batch_size, channels, num_nodes, seq_length]
for i in range(self.layers):
residual = x
# Применить внимание вместо расширенной свертки
x = x.permute(0, 3, 2, 1) # [batch_size, seq_length, num_nodes, channels]
x, _ = self.attention_layers[i](x, x, x, None)
x = x.permute(0, 3, 2, 1) # [batch_size, channels, num_nodes, seq_length]
x = self.residual_convs[i](x)
x = x + residual[:, :, :, -x.size(3):]
x = self.norm[i](x, self.idx)
s = self.skip_convs[i](x)
skip = s + skip
if self.gcn_bool:
x_1 = self.gconv_1[i](x, dy_adj)
x = x_1
skip = self.skipE(x) + skip
x = F.relu(skip)
x = F.relu(self.end_conv_1(x))
x = self.end_conv_2(x)
return x, gl_loss, None, dy_adj
def make_model(DEVICE, num_of_d, nb_block, in_channels, K,
nb_chev_filter, nb_time_filter, time_strides, adj_mx, adj_pa,
adj_TMD, num_for_predict, len_input, num_of_vertices, d_model, d_k, d_v, n_heads):
L_tilde = scaled_Laplacian(adj_mx)
cheb_polynomials = [torch.from_numpy(i).type(torch.FloatTensor).to(DEVICE) for i in cheb_polynomial(L_tilde, K)]
model = gwnet(DEVICE, num_of_vertices, dropout=0.3, gcn_bool=True, addaptadj=True,
in_dim=in_channels, out_dim=num_for_predict, residual_channels=nb_chev_filter,
dilation_channels=nb_chev_filter, skip_channels=nb_chev_filter * 8,
end_channels=nb_chev_filter * 16, layers=nb_block, seq_length=len_input,
embed_dim=10, dropout_ingc=0.5, eta=1, gamma=0.001, m=0.9,
batch_size=64, tc_dropout=0.1, n_heads=n_heads, d_model=d_model)
for p in model.parameters():
if p.dim() > 1:
nn.init.xavier_uniform_(p)
else:
nn.init.uniform_(p)
return model
Вот код для извлечения пространственных зависимостей и построения графа:
import torch
from torch import nn
from layer_module import *
from torch.nn.utils import weight_norm
class calc_adj_5_withLoss_1(nn.Module):
def __init__(self, node_dim, heads, head_dim, nodes=207, eta=1,
gamma=0.001, dropout=0.5, n_clusters=5):
super(calc_adj_5_withLoss_1, self).__init__()
self.D = heads * head_dim # node_dim #
self.heads = heads
self.dropout = dropout
self.eta = eta
self.gamma = gamma
self.head_dim = head_dim
self.node_dim = node_dim
self.nodes = nodes
self.query = fc_layer(in_channels=node_dim, out_channels=self.D, need_layer_norm=False)
self.key = fc_layer(in_channels=node_dim, out_channels=self.D, need_layer_norm=False)
self.value = fc_layer(in_channels=node_dim, out_channels=self.D, need_layer_norm=False)
self.mlp = nn.Conv2d(in_channels=self.heads, out_channels=self.heads, kernel_size=(1, 1), bias=True)
self.bn = nn.LayerNorm(node_dim)
self.bn1 = nn.LayerNorm(node_dim)
self.w = nn.Parameter(torch.zeros(size=(nodes, node_dim)))
nn.init.xavier_uniform_(self.w.data, gain=1.414)
self.attn_static = nn.LayerNorm(nodes)
self.skip_norm = nn.LayerNorm(nodes)
self.attn_norm = nn.LayerNorm(nodes)
self.linear_norm = nn.LayerNorm(nodes)
self.attn_linear = nn.Parameter(torch.zeros(size=(nodes, nodes)))
nn.init.xavier_uniform_(self.attn_linear.data, gain=1.414)
self.attn_linear_1 = nn.Parameter(torch.zeros(size=(nodes, nodes)))
nn.init.xavier_uniform_(self.attn_linear_1.data, gain=1.414)
self.static_inf_norm = nn.LayerNorm(nodes)
self.attn_norm_1 = nn.LayerNorm(nodes)
self.attn_norm_2 = nn.LayerNorm(nodes)
def forward(self, nodevec1, input_gc, nodevec_dyStatic, batch_size=64):
node_orginal = nodevec1
nodevec1 = self.bn(nodevec1)
static_graph_inf = self.static_inf_norm(torch.mm(nodevec_dyStatic, nodevec_dyStatic.transpose(1, 0)))
batch_size, nodes, node_dim = batch_size, self.nodes, self.node_dim
nodevec1_1 = torch.einsum('bnd, nl -> bnl', nodevec1, self.w) + nodevec1
skip_atten = torch.einsum('bnd,bdm->bnm', nodevec1_1, nodevec1_1.transpose(-1, -2))
skip_atten = self.skip_norm(skip_atten)
nodevec1 = nodevec1.unsqueeze(1).transpose(1, -1)
query = self.query(nodevec1)
key = self.key(nodevec1)
value = self.value(nodevec1)
key = key.squeeze(-1).contiguous().view(batch_size, self.heads, self.head_dim, nodes)
query = query.squeeze(-1).contiguous().view(batch_size, self.heads, self.head_dim, nodes).transpose(-1, -2)
# attention = batch_size, heads, nodes, nodes
attention = torch.einsum('bhnd, bhdu-> bhnu', [query, key])
attention /= (self.head_dim ** 0.5)
attention = F.dropout(attention, self.dropout, training=self.training)
attention = self.mlp(attention) + attention
resolution = self.attn_norm(torch.sum(attention, dim=1)) + skip_atten
resolution1 = F.relu(torch.einsum('bnm, ml->bnl', [self.linear_norm(resolution), self.attn_linear]))
resolution1 = torch.einsum('bnm, ml -> bnl', [resolution1, self.attn_linear_1])
relation2 = self.attn_norm_1(resolution1 + resolution)
relation2 = F.dropout(relation2, self.dropout, training=self.training)
static_graph_inf = static_graph_inf.unsqueeze(0).repeat(batch_size, 1, 1)
relation3 = self.attn_norm_2(relation2) + static_graph_inf
relation4 = F.softmax(F.relu(relation3), dim=2)
gl_loss = None
if self.training:
gl_loss = self.graph_loss_orginal(input_gc, relation4, self.eta, self.gamma)
node_model = value
return relation4, static_graph_inf, node_orginal, gl_loss, node_model, relation3
def static_graph(self, nodevec):
resolution_static = torch.mm(nodevec, nodevec.transpose(1, 0))
resolution_static = F.softmax(F.relu(self.attn_static(resolution_static)), dim=1)
return resolution_static
def graph_loss_orginal(self, input, adj, eta=1, gamma=0.001): # 0.0001
B, N, D = input.shape
x_i = input.unsqueeze(2).expand(B, N, N, D)
x_j = input.unsqueeze(1).expand(B, N, N, D)
dist_loss = torch.pow(torch.norm(x_i - x_j, dim=3), 2) * adj
dist_loss = torch.sum(dist_loss, dim=(1, 2))
f_norm = torch.pow(torch.norm(adj, dim=(1, 2)), 2)
gl_loss = dist_loss + gamma * f_norm
return gl_loss
class graph_constructor(nn.Module):
def __init__(self, nodes, dim, device, time_step, cout=16, heads=4, head_dim=8,
eta=1, gamma=0.0001, dropout=0.5, m=0.9, batch_size=64, in_dim=2, is_add1=True):
super(graph_constructor, self).__init__()
self.embed1 = nn.Embedding(nodes, dim)
self.m = m
self.embed2 = nn.Embedding(nodes, dim)
for param in self.embed2.parameters():
param.requires_grad = False
for para_static, para_w in zip(self.embed2.parameters(), self.embed1.parameters()):
para_static.data = para_w.data
self.heads = heads
self.head_dim = head_dim
self.out_channel = cout
self.device = device
self.dim = dim
self.nodes = nodes
self.time_step = time_step
if is_add1:
time_length = time_step + 1
else:
time_length = time_step
self.trans_Merge_line = nn.Conv2d(in_dim, dim, kernel_size=(1, time_length), bias=True) # cout
self.gate_Fusion_1 = gatedFusion_1(self.dim, device)
self.calc_adj = calc_adj_5_withLoss_1(node_dim=dim, heads=heads, head_dim=head_dim, nodes=nodes,
eta=eta, gamma=gamma, dropout=dropout)
self.dim_to_channels = nn.Parameter(torch.zeros(size=(heads * head_dim, cout * time_step)))
nn.init.xavier_uniform_(self.dim_to_channels.data, gain=1.414)
self.skip_norm = nn.LayerNorm(time_step)
self.time_norm = nn.LayerNorm(dim)
def forward(self, input):
"""
input = batch_size, 2, nodes ,time_step
"""
batch_size, nodes, time_step = input.shape[0], self.nodes, self.time_step
time_node = input
# time_node_2 = b, n, dim
time_node = self.time_norm(self.trans_Merge_line(time_node).squeeze(-1).transpose(1, 2))
idx = torch.arange(self.nodes).to(self.device)
nodevec1 = self.embed1(idx)
nodevec_orginal = nodevec1
nodevec1 = self.gate_Fusion_1(batch_size, nodevec1, time_node) + nodevec1 # time_node #
adj = self.calc_adj(nodevec1, time_node, nodevec_orginal, batch_size)
return adj
А вот код для многоголового самовнимания:
import torch
import torch.nn as nn
import torch.nn.functional as F
import numpy as np
class ScaledDotProductAttention(nn.Module):
def __init__(self, d_k, num_of_d):
super(ScaledDotProductAttention, self).__init__()
self.d_k = d_k
self.num_of_d = num_of_d
def forward(self, Q, K, V, attn_mask, res_att):
scores = torch.matmul(Q, K.transpose(-1, -2)) / np.sqrt(self.d_k) + res_att
if attn_mask is not None:
scores.masked_fill_(attn_mask, -1e9)
attn = F.softmax(scores, dim=-1)
context = torch.matmul(attn, V)
return context, scores
class MultiHeadAttention(nn.Module):
def __init__(self, DEVICE, d_model, d_k, d_v, n_heads, num_of_d):
super(MultiHeadAttention, self).__init__()
self.d_model = d_model
self.d_k = d_k
self.d_v = d_v
self.n_heads = n_heads
self.num_of_d = num_of_d
self.DEVICE = DEVICE
self.W_Q = nn.Linear(d_model, d_k * n_heads, bias=False)
self.W_K = nn.Linear(d_model, d_k * n_heads, bias=False)
self.W_V = nn.Linear(d_model, d_v * n_heads, bias=False)
self.fc = nn.Linear(n_heads * d_v, d_model, bias=False)
def forward(self, input_Q, input_K, input_V, attn_mask, res_att=None):
residual, batch_size = input_Q, input_Q.size(0)
Q = self.W_Q(input_Q).view(batch_size, self.num_of_d, -1, self.n_heads, self.d_k).transpose(2, 3)
K = self.W_K(input_K).view(batch_size, self.num_of_d, -1, self.n_heads, self.d_k).transpose(2, 3)
V = self.W_V(input_V).view(batch_size, self.num_of_d, -1, self.n_heads, self.d_v).transpose(2, 3)
if attn_mask is not None:
attn_mask = attn_mask.unsqueeze(1).repeat(1, self.n_heads, 1, 1)
context, res_attn = ScaledDotProductAttention(self.d_k, self.num_of_d)(Q, K, V, attn_mask, res_att)
context = context.transpose(2, 3).reshape(batch_size, self.num_of_d, -1, self.n_heads * self.d_v)
output = self.fc(context)
return nn.LayerNorm(self.d_model).to(self.DEVICE)(output + residual), res_attn
class Embedding(nn.Module):
def __init__(self, nb_seq, d_Em, num_of_features, Etype):
super(Embedding, self).__init__()
self.nb_seq = nb_seq
self.Etype = Etype
self.num_of_features = num_of_features
self.pos_embed = nn.Embedding(nb_seq, d_Em)
self.norm = nn.LayerNorm(num_of_features)
self.linear = nn.Linear(d_Em, num_of_features)
def forward(self, x, batch_size):
if self.Etype == 'T':
pos = torch.arange(self.nb_seq, dtype=torch.long).to(x.device)
pos = pos.unsqueeze(0).unsqueeze(0).expand(batch_size, self.num_of_features, self.nb_seq)
pos_embedding = self.pos_embed(pos)
pos_embedding = pos_embedding.permute(0, 1, 3, 2)
pos_embedding = self.linear(pos_embedding)
embedding = x + pos_embedding
else:
pos = torch.arange(self.nb_seq, dtype=torch.long).to(x.device)
pos = pos.unsqueeze(0).expand(batch_size, self.nb_seq)
pos_embedding = self.pos_embed(pos)
pos_embedding = self.linear(pos_embedding.transpose(1, 2)).transpose(1, 2)
embedding = x + pos_embedding
Emx = self.norm(embedding)
return Emx
def make_model(DEVICE, num_of_d, nb_block, in_channels, K,
nb_chev_filter, nb_time_filter, time_strides, adj_mx, adj_pa,
adj_TMD, num_for_predict, len_input, num_of_vertices, d_model, d_k, d_v, n_heads):
L_tilde = scaled_Laplacian(adj_mx)
cheb_polynomials = [torch.from_numpy(i).type(torch.FloatTensor).to(DEVICE) for i in cheb_polynomial(L_tilde, K)]
model = DSTAGNN_submodule(DEVICE, num_of_d, nb_block, in_channels,
K, nb_chev_filter, nb_time_filter, time_strides, cheb_polynomials,
adj_pa, adj_TMD, num_for_predict, len_input, num_of_vertices, d_model, d_k, d_v, n_heads)
for p in model.parameters():
if p.dim() > 1:
nn.init.xavier_uniform_(p)
else:
nn.init.uniform_(p)
return model
Набор данных PEMSD4 имеет форму: (16992, 307, 1). Я нормализую набор данных стандартной нормализацией и разделяю данные следующим образом:
Обучающая выборка: (10173, 12, 307, 1)
Валидационная выборка: (3375, 12, 307, 1)
Тестовая выборка: (3375, 12, 307, 1)
Буду признателен за любые советы по исправлению этой ошибки.
Ответ или решение
Ошибка, с которой вы столкнулись при обучении модели для многовариантного временного ряда, связанная с формами матриц mat1
и mat2
, не позволяющими выполнить умножение (размеры 131072×12 и 64×32), может быть вызвана несколькими факторами в вашем коде и модели. Рассмотрим основные причины и возможные решения, чтобы помочь вам устранить эту проблему.
1. Понимание ошибки
Ошибка RuntimeError: mat1 and mat2 shapes cannot be multiplied (131072x12 and 64x32)
указывает, что вы пытаетесь выполнить матричное умножение между двумя тензорами, где количество столбцов первого тензора (12) не совпадает с количеством строк второго тензора (64). Для корректного выполнения матричного умножения размерности должны быть совместимыми: число столбцов первого тензора должно совпадать с числом строк второго.
2. Входные данные и размерности
Вы упомянули, что форма вашего входного набора данных такова:
- Тренировочный набор: (10173, 12, 307, 1)
- Валидационный набор: (3375, 12, 307, 1)
- Тестовый набор: (3375, 12, 307, 1)
Разберём структуру входных данных:
- Здесь
(10173, 12, 307, 1)
означает, что у вас есть 10173 примера, 12 временных шагов, 307 признаков и 1 канал.
3. Модель и размеры тензоров
Ваша модель включает слои вниманием и графовые сверточные слои. В частности, в классе MultiHeadAttention
вы создаете матрицы Q
, K
, и V
с использованием линейных слоев. Обратите внимание, что размерности, используемые в этих слоях, должны соответствовать друг другу.
4. Проверка слоев
Чтобы устранить ошибку, следите за тем, как данные проходят через разные слои. Вот несколько шагов, которые могут помочь:
-
Линейнные слои: Убедитесь, что линейные слои, определенные в
MultiHeadAttention
, создают выходы правильной размерности. Например, размер выходного тензора из слояW_Q
должен оставаться совместимым с размерностью входного тензора. -
Переупорядочивание тензоров: Метод
transpose
изменяет порядок измерений тензора. Убедитесь, что вы правильно переупорядочиваете тензоры перед операцией матричного умножения. Например, после примененияtranspose
входной размер должен соответствовать ожидаемым. -
Аггрегация результатирующих тензоров: Ваша модель может терять информацию о размерности, если вы случайно используете функции агрегации. Убедитесь, что перед проведением операций со слоями (например,
self.skip_convs[i](x)
) данные имеют правильные размеры.
5. Иннвераризм выходных данных
Имеет смысл добавить к коду проверки выводных размерностей после каждого важного слоя. Это упростит отладку и позволит убедиться, что данные приобретают ожидаемые формы.
def forward(self, input_Q, input_K, input_V, attn_mask, res_att=None):
#...
print("Q shape:", Q.shape) # Вывод размера тензора Q
print("K shape:", K.shape) # Вывод размера тензора K
print("V shape:", V.shape) # Вывод размера тензора V
#...
Заключение
Чтобы решить проблему с несовпадением размеров матриц, вам нужно внимательно отслеживать преобразования и размеры тензоров в вашей модели. Убедитесь, что каждое преобразование не приводит к изменению размерностей, которые могут привести к ошибке в конце. Проанализируйте каждый слой и проверьте свой входной и выходной размеры с помощью отладочных выводов или встроенного механизма отладки.
Следуя этим рекомендациям, вы сможете устранить ошибку и продолжить успешную разработку своей модели прогнозирования временных рядов.