Формы mat1 и mat2 не могут быть перемножены (100×200 и 100×9922)

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

Я пытаюсь создать языковую модель BiLSTM и испытываю некоторые проблемы.

Модель

class BiLSTM(nn.Module):
  def __init__(self, vocab_size, embedding_dim, hidden_dim, num_layers, dropout_rate, tie_weights):
    super().__init__()

    self.num_layers = num_layers
    self.hidden_dim = hidden_dim
    self.embedding_dim = embedding_dim

    self.embedding = nn.Embedding(vocab_size, embedding_dim)
    self.lstm = nn.LSTM(embedding_dim, hidden_dim, num_layers=num_layers, 
                        dropout=dropout_rate, batch_first=True, bidirectional=True)
    self.dropout = nn.Dropout(dropout_rate)
    self.linear = nn.Linear(hidden_dim*2, vocab_size)

    if tie_weights:
      # Входной и скрытый слой должны быть одного размера для связывания весов
      assert embedding_dim == hidden_dim, 'Невозможно связать веса, проверьте размеры'
      self.linear.weight = self.embedding.weight

    self.init_weights()

    self.hidden = None
    self.cell = None

  def forward(self, x, hidden):
    hidden_0 = torch.zeros(2*num_layers, batch_size, self.hidden_dim).to(device)
    cell_0 = torch.zeros(2*num_layers, batch_size, self.hidden_dim).to(device)
    output  = self.embedding(x)
    output, (h, c) = self.lstm(output, (hidden_0, cell_0))
    output = self.dropout(output)
    output = self.linear(output)
    return output, (h, c)

  def init_weights(self):
    init_range_emb = 0.1
    init_range_other = 1/math.sqrt(self.hidden_dim)
    self.embedding.weight.data.uniform_(-init_range_emb, init_range_emb)
    self.linear.weight.data.uniform_(-init_range_other, init_range_other)
    self.linear.bias.data.zero_()

  def init_hidden(self, batch_size):
        hidden = torch.zeros(self.num_layers, batch_size, self.hidden_dim)
        cell = torch.zeros(self.num_layers, batch_size, self.hidden_dim)
        return hidden, cell

  def detach_hidden(self, hidden):
        hidden, cell = hidden
        hidden = hidden.detach()
        cell = cell.detach()
        return hidden, cell

Параметры модели

vocab_size = len(vocab)
embedding_dim = 100
hidden_dim = 100
num_layers = 5
dropout_rate = 0.4
tie_weights = True
model = BiLSTM(vocab_size, embedding_dim, hidden_dim, num_layers, dropout_rate, tie_weights)
model.to(device)

Обучение:

import copy
import time

criterion = nn.CrossEntropyLoss()
lr = 20.0  # скорость обучения
optimizer = torch.optim.SGD(model.parameters(), lr=lr)
scheduler = torch.optim.lr_scheduler.StepLR(optimizer, 1.0, gamma=0.95)

def train(model: nn.Module) -> None:
    model.train()  # включить режим обучения
    total_loss = 0.
    log_interval = 200
    start_time = time.time()

    hidden = model.init_hidden(batch_size)

    num_batches = len(train_data) // bptt
    for batch, i in enumerate(range(0, train_data.size(0) - 1, bptt)):
        hidden = model.detach_hidden(hidden)
        data, targets = get_batch(train_data, i)
        seq_len = data.size(0)
        output, hidden = model(data, hidden)
        loss = criterion(output.view(-1, vocab_size), targets)

        optimizer.zero_grad()
        loss.backward()
        torch.nn.utils.clip_grad_norm_(model.parameters(), 0.5)
        optimizer.step()

        total_loss += loss.item()

        if batch % log_interval == 0 and batch > 0:
            lr = scheduler.get_last_lr()[0]
            ms_per_batch = (time.time() - start_time) * 1000 / log_interval
            cur_loss = total_loss / log_interval
            ppl = math.exp(cur_loss)
            print(f'| эпоха {epoch:3d} | {batch:5d}/{num_batches:5d} батчей | '
                  f'lr {lr:02.2f} | мс/батч {ms_per_batch:5.2f} | '
                  f'потери {cur_loss:5.2f} | ppl {ppl:8.2f}')
            total_loss = 0
            start_time = time.time()

Оценка:

def evaluate(model: nn.Module, eval_data: Tensor) -> float:
    model.eval()  # включить режим оценки
    total_loss = 0.
    with torch.no_grad():
        for i in range(0, eval_data.size(0) - 1, bptt):
            data, targets = get_batch(eval_data, i)
            seq_len = data.size(0)
            output = model(data)
            output_flat = output.view(-1, vocab_size)
            total_loss += seq_len * criterion(output_flat, targets).item()
    return total_loss / (len(eval_data) - 1)

Блок обучения:

best_val_loss = float('inf')
epochs = 50
best_model = None

for epoch in range(1, epochs + 1):
    epoch_start_time = time.time()
    train(model)
    val_loss = evaluate(model, val_data)
    val_ppl = math.exp(val_loss)
    elapsed = time.time() - epoch_start_time
    print('-' * 89)
    print(f'| конец эпохи {epoch:3d} | время: {elapsed:5.2f}с | '
          f'потери на валидации {val_loss:5.2f} | ppl на валидации {val_ppl:8.2f}')
    print('-' * 89)

    if val_loss < best_val_loss:
        best_val_loss = val_loss
        best_model = copy.deepcopy(model)

    scheduler.step()

Моя проблема в том, что я получаю следующую ошибку.

RuntimeError                              Traceback (most recent call last)
<ipython-input-345-453c3f2a9cad> in <cell line: 5>()
      5 for epoch in range(1, epochs + 1):
      6     epoch_start_time = time.time()
----> 7     train(model)
      8     val_loss = evaluate(model, val_data)
      9     val_ppl = math.exp(val_loss)

4 frames
<ipython-input-343-16d7ac1074e2> in train(model)
     20         data, targets = get_batch(train_data, i)
     21         seq_len = data.size(0)
---> 22         output, hidden = model(data, hidden)
     23         loss = criterion(output.view(-1, vocab_size), targets)
     24 

/usr/local/lib/python3.9/dist-packages/torch/nn/modules/module.py in _call_impl(self, *args, **kwargs)
   1499                 or _global_backward_pre_hooks or _global_backward_hooks):
   1500
   1501     return forward_call(*args, **kwargs)
   1502                 or _global_backward_pre_hooks или _global_backward_hooks):
   1503 
   1504 # Вызывайте только в том случае, если jit не используется
   1505 full_backward_hooks, non_full_backward_hooks = [], []

/usr/local/lib/python3.9/dist-packages/torch/nn/modules/linear.py in forward(self, input)
    112 
    113     def forward(self, input: Tensor) -> Tensor:
----> 114         return F.linear(input, self.weight, self.bias)
    115 
    116     def extra_repr(self) -> str:

RuntimeError: размеры mat1 и mat2 не могут быть перемножены (100x200 и 100x9922)

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

Любая помощь будет полезной, спасибо.

ИЗМЕНЕНИЕ

Функция get_batch возвращает

(tensor([[9893,    0,  171,    1,    1,  180,    4,  795,   13,   26],
        [9894, 1192,   58,    0, 9325,  190,  140,    4,    2,    0],
        [9896,    2,  109, 5437,   20,   12, 1022,    5,    6,    4],
        [9897,    2,   52, 1446,    5,  474,    9,   38,  192,    5],
        [9898,    3, 2850,   30, 3489,  655,   10,  212, 3915,  300],
        [9902,    1, 2150,    1, 1791, 1534,  141,   12,    4, 2853],
        [9903, 2526,    9,  161,   40,   17,    7,    5,  339,    4],
        [9904,   42,   10,   14,  775, 1931,  151, 1786, 1281,  893],
        [9905,   32, 1574,    2, 2088,  569,  154,   19,  799, 3353],
        [9906,  222,   43,    9,  342,  499,  290,    1,    0, 9253]],
       device="cuda:0"), tensor([9894, 1192,   58,    0, 9325,  190,  140,    4,    2,    0, 9896,    2,
         109, 5437,   20,   12, 1022,    5,    6,    4, 9897,    2,   52, 1446,
           5,  474,    9,   38,  192,    5, 9898,    3, 2850,   30, 3489,  655,
          10,  212, 3915,  300, 9902,    1, 2150,    1, 1791, 1534,  141,   12,
           4, 2853, 9903, 2526,    9,  161,   40,   17,    7,    5,  339,    4,
        9904,   42,   10,   14,  775, 1931,  151, 1786, 1281,  893, 9905,   32,
        1574,    2, 2088,  569,  154,   19,  799, 3353, 9906,  222,   43,    9,
         342,  499,  290,    1,    0, 9253, 9908,    6,  388,   31,   28,    0,
         559,  835,    7,   30], device="cuda:0"))

На первый взгляд, мне интересно, не является ли это чем-то простым: согласно документации по nn.LSTM, batch_first – если True, то входные и выходные тензоры предоставляются в виде (batch, seq, feature) вместо (seq, batch, feature). Обратите внимание, что это не относится к скрытым или ячеистым состояниям. См. разделы Входные/Выходные данные ниже для получения дополнительных сведений. По умолчанию: False. Если вы установите это значение на False, то размеры вашего батча должны быть 200×100, и это должно сработать (возможно?).

Ответ или решение

Ошибка RuntimeError: mat1 and mat2 shapes cannot be multiplied (100x200 and 100x9922) указывает на проблему с размерами тензоров, которые пытаются перемножиться, а именно при выполнении операции линейного преобразования в вашей модели.

Ваша модель BiLSTM имеет следующие ключевые компоненты:

  1. Входной тензор (output) из LSTM имеет форму (batch_size, seq_len, hidden_dim * 2). Поскольку LSTM является двунаправленным, скрытый размер умножается на 2.
  2. Линейный слой ожидает входные данные в форме (batch_size, seq_len, in_features), где in_features равен hidden_dim * 2.

Поэтому, если ваш скрытый размер (hidden_dim) равен 100, тогда размерность выходного тензора после LSTM будет (batch_size, seq_len, 200). Эта форма корректно передается в линейный слой, если vocab_size – это количество классов, для которых вы хотите предсказать вероятности.

На данный момент у вас всегда происходит умножение с неправильными размерами — 100x200 и 100x9922. Давайте посмотрим, как это можно исправить:

Возможные источники проблемы:

  1. Неправильная инициализация скрытых состояний. Убедитесь, что вы правильно инициализируете скрытые состояния. Они должны иметь форму (num_layers * num_directions, batch_size, hidden_dim) для LSTM.

    hidden_0 = torch.zeros(2 * self.num_layers, x.size(0), self.hidden_dim).to(device)
    cell_0 = torch.zeros(2 * self.num_layers, x.size(0), self.hidden_dim).to(device)

    Убедитесь, что ваше скрытое состояние правильно передается и не имеет фиксированного batch_size, если вы рассчитываете его по входным данным.

  2. Неправильная форма данных. При вызове вашей модели в методе train, убедитесь, что тензор data имеет форму (batch_size, seq_len), если вы используете batch_first=True. Однако, если у вас seq_len идет в первой размерности, тогда необходимо установить batch_first=False в LSTM-слое.

  3. Проверка возвращаемых данных в get_batch(). Убедитесь, что ваши data и targets имеют соответствующие размеры. Из вашего редактированного примера видно, что вы получаете data как тензор формы (seq_len, batch_size), что может привести к неправильной работе модели, если у вас batch_first=True.

Рекомендации по исправлению

  1. Если вы хотите оставить batch_first=True, измените get_batch() так, чтобы он возвращал данные в формате (batch_size, seq_len).

  2. Либо поменяйте параметр batch_first=False в LSTM, чтобы оставить get_batch() как есть, но тогда убедитесь, что все ваши данные имеют правильную форму.

Итоговый код

Вот как может выглядеть исправленный код вашего метода forward:

def forward(self, x, hidden):
    batch_size = x.size(0)
    hidden_0 = torch.zeros(2 * self.num_layers, batch_size, self.hidden_dim).to(device)
    cell_0 = torch.zeros(2 * self.num_layers, batch_size, self.hidden_dim).to(device)

    output = self.embedding(x)
    output, (h, c) = self.lstm(output, (hidden_0, cell_0))
    output = self.dropout(output)
    output = self.linear(output.view(-1, output.size(2)))  # Измените output на (batch_size * seq_len, hidden_dim * 2)
    return output, (h, c)

Также нужно внимательно следить за формами тензоров, которые вы передаете в каждую стадию процесса. Четкое соответствие размеров — это ключ к разрешению большинства ошибок матричных операций в PyTorch.

Если у вас возникнут дополнительные вопросы или нужна помощь, не стесняйтесь обращаться!

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

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