Формы mat1 и mat2 в Pytorch не могут быть умножены (131072×12 и 64×32)

Вопрос или проблема

У меня есть модель прогнозирования многомерного временного ряда, изначально использующая расширенную временную свертку для извлечения временных зависимостей, которую я пытался заменить на многоголовое самовнимание с остаточным соединением, но когда я стараюсь обучить её на наборе данных 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. Проверка слоев

Чтобы устранить ошибку, следите за тем, как данные проходят через разные слои. Вот несколько шагов, которые могут помочь:

  1. Линейнные слои: Убедитесь, что линейные слои, определенные в MultiHeadAttention, создают выходы правильной размерности. Например, размер выходного тензора из слоя W_Q должен оставаться совместимым с размерностью входного тензора.

  2. Переупорядочивание тензоров: Метод transpose изменяет порядок измерений тензора. Убедитесь, что вы правильно переупорядочиваете тензоры перед операцией матричного умножения. Например, после применения transpose входной размер должен соответствовать ожидаемым.

  3. Аггрегация результатирующих тензоров: Ваша модель может терять информацию о размерности, если вы случайно используете функции агрегации. Убедитесь, что перед проведением операций со слоями (например, 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
    #...

Заключение

Чтобы решить проблему с несовпадением размеров матриц, вам нужно внимательно отслеживать преобразования и размеры тензоров в вашей модели. Убедитесь, что каждое преобразование не приводит к изменению размерностей, которые могут привести к ошибке в конце. Проанализируйте каждый слой и проверьте свой входной и выходной размеры с помощью отладочных выводов или встроенного механизма отладки.

Следуя этим рекомендациям, вы сможете устранить ошибку и продолжить успешную разработку своей модели прогнозирования временных рядов.

Оцените материал
Добавить комментарий

Капча загружается...