【问题标题】:How to increase batch size in GPT2 training for translation task?如何在翻译任务的 GPT2 训练中增加批量大小?
【发布时间】:2021-07-30 07:59:09
【问题描述】:

我正在开发一个代码来使用预训练的GPT2 模型来完成机器翻译任务。我的数据的 word-to-id 的长度是 91,我为我的模型开发了以下代码:

import torch
from torch.utils.data import DataLoader
from transformers.models.gpt2.modeling_gpt2 import GPT2Model

# data preparation code

def batch_sequences(x, y, env):
    """
    Take as input a list of n sequences (torch.LongTensor vectors) and return
    a tensor of size (slen, n) where slen is the length of the longest
    sentence, and a vector lengths containing the length of each sentence.
    """
    lengths_x = torch.LongTensor([len(s) + 2 for s in x])
    lengths_y = torch.LongTensor([len(s) + 2 for s in y])
    max_length = max(lengths_x.max().item(), lengths_y.max().item())
    sent_x = torch.LongTensor(
        max_length, lengths_x.size(0)).fill_(env.pad_index)
    sent_y = torch.LongTensor(
        max_length, lengths_y.size(0)).fill_(env.pad_index)
    assert lengths_x.min().item() > 2
    assert lengths_y.min().item() > 2

    sent_x[0] = env.eos_index
    for i, s in enumerate(x):
        sent_x[1:lengths_x[i] - 1, i].copy_(s)
        sent_x[lengths_x[i] - 1, i] = env.eos_index

    sent_y[0] = env.eos_index
    for i, s in enumerate(y):
        sent_y[1:lengths_y[i] - 1, i].copy_(s)
        sent_y[lengths_y[i] - 1, i] = env.eos_index

    return sent_x, sent_y, max_length

def collate_fn(elements):
    """
    Collate samples into a batch.
    """
    x, y = zip(*elements)
    x = [torch.LongTensor([env.word2id[w]
                          for w in seq if w in env.word2id]) for seq in x]
    y = [torch.LongTensor([env.word2id[w]
                          for w in seq if w in env.word2id]) for seq in y]
    x, y, length = batch_sequences(x, y, env)
    return (x, length), (y, length), torch.LongTensor(nb_ops)

loader = DataLoader(data, batch_size=1, shuffle=False, collate_fn=collate_fn)
gpt2 = GPT2Model.from_pretrained('gpt2')
in_layer = nn.Embedding(len(env.word2id), 768)
out_layer = nn.Linear(768, len(env.word2id))

parameters = list(gpt2.parameters()) + list(in_layer.parameters()) + list(out_layer.parameters())
optimizer = torch.optim.Adam(parameters)
loss_fn = nn.CrossEntropyLoss()
for layer in (gpt2, in_layer, out_layer):
    layer.train()

accuracies = list()
n_epochs = 5
for i in range(n_epochs):
    for (x, x_len), (y, y_len) in loader:

        x = x.to(device=device)
        y = y.to(device=device)

        embeddings = in_layer(x.reshape(1, -1))
        hidden_state = gpt2(inputs_embeds=embeddings).last_hidden_state[:, :]
        logits = out_layer(hidden_state)[0]
        loss = loss_fn(logits, y.reshape(-1))
        accuracies.append(
            (logits.argmax(dim=-1) == y.reshape(-1)).float().mean().item())

        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        if len(accuracies) % 500 == 0:
            accuracy = sum(accuracies[-50:]) / len(accuracies[-50:])
            print(f'Samples: {len(accuracies)}, Accuracy: {accuracy}')

当批量大小为 1 时,此代码运行良好。但它太慢了。我想将批量大小从 1 增加到 32,但我遇到了一些尺寸兼容性问题。如何在不出错的情况下增加批量大小?

我的数据由一对句子组成,第一个是第一语言的句子,第二个是第二语言的翻译。

例如,假设 x.shape 是 (batch_size, 12)(意味着我们有长度为 12 的“batch_size”句子作为输入,y.shape 也是 (batch_size, 12)(翻译)。而且我们还有长度为 90 的 word-to-id 字典,将句子中的每个单词与其索引匹配)

【问题讨论】:

  • 您能分享一下您遇到的错误吗?对于有同样问题的人搜索这个问题会很方便。
  • @Jindřich 上面的代码工作正常,因为批量大小为 1(查看代码中的部分 'embeddings = in_layer(x.reshape(1, -1))')。我的问题是我无法将批量大小从 1 增加到更多(比如 32)并且我得到形状兼容性错误,这表明我没有正确实现它。我在上面的问题末尾添加了一段关于我想要的形状。

标签: nlp pytorch gpt-2


【解决方案1】:

这个问题可以使用填充来解决。我们需要两个特殊符号:

  • 输入中的代码0 (x) 将表示不应翻译的“空白”标记。
  • 输出中的代码-100 (y) 将表示不应参与损失计算的“空白”令牌。 nn.CrossEntropyLoss() 是 programmed 忽略此值(通过参数 ignore_index)。

大小为 3 的批次可能如下所示:

x:
[[1, 2, 3, 0, 0],
[ 4, 5, 6, 7, 8],
[ 9, 8, 0, 0, 0]]
y:
[[1, 2,    3, -100, -100],
[ 4, 5,    6,    7,    8],
[ 9, 8, -100, -100, -100]]

您可以使用以下代码生成它:

def pad_sequences(batch, pad_value=0):
    n = max(len(v) for v in batch)
    return torch.tensor([v + [pad_value] * (n - len(v)) for v in batch])

但是,我觉得您的问题陈述存在问题。如果您执行机器翻译,那么您的输入和输出可以有不同的长度,但您的架构只允许 x 和 y 具有相同的长度。如果要支持不同长度的x和y,我建议改用T5等seq2seq架构。

另一个问题是 GPT 是自回归的,所以如果 y 与 x 完全对齐,那么我们不能在生成 y 的左侧部分时使用 x 的后缀。因此,如果您希望 x 和 y 完美对齐,但仍想在生成 x 时使用有关 x 的完整信息,我建议您使用双向编码器,例如 BERT。

【讨论】:

  • 感谢您的回答。我知道如何批量创建数据。我的问题在我训练模型的最后一部分。您能告诉我应该如何更改该部分,以便它可以针对每个批次大小运行吗?
猜你喜欢
  • 2020-05-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-17
  • 2020-03-17
  • 2018-07-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多