nlp - 如何在翻译任务的 GPT2 训练中增加批量大小?
问题描述
我正在开发一个代码来使用预先训练的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 的 to-id 字典,将句子中的每个单词与其索引匹配)
解决方案
这个问题可以使用填充来解决。我们需要两个特殊符号:
0
输入 ( ) 中的代码x
将表示不应翻译的“空白”标记。-100
输出 ( ) 中的代码y
将表示不应参与损失计算的“空白”令牌。nn.CrossEntropyLoss()
被编程为忽略该值(通过参数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
我建议改用 seq2seq 架构,比如 T5。
另一个问题是 GPT 是自回归的,所以如果y
与 完全对齐x
,那么我们不能x
在生成 的左侧部分时使用 的后缀y
。因此,如果您希望您的x
和y
完美对齐,但仍想使用有关x
何时生成的完整信息y
,我建议您使用双向编码器,例如 BERT。
推荐阅读
- python - UserError 在值 0 上触发
- python - 为什么按下 PyQt 按钮时 matplotlib 的计时器会加快?
- java - 空对象引用 JSONArray 和 JSONObject
- sorting - ExtJS 4.2.2:如何在组合框顶部显示所选项目?
- python - 在python中的特定字母处拆分字符串,除非后面跟着另一个字母
- sql - 从 CRM 导入带有日期的数据的问题
- db2 - 在不同 DB2 表中的 2 列之间执行 Like %。
- angular - 在角网格中,我有一个列,其中包含在线 Kendo 网格中的编辑器模板(反应形式)。它不链接模板控件(文本框)和表单
- django - Python 十进制等效 isclose 方法
- ios - 使用扩展将函数添加到现有协议而无需函数声明