使用 pytorch 对 Transformer seq2seq 任务进行训练的困难

问题描述 投票:0回答:1

我目前正在使用 vanilla

torch.nn.Transformer
执行 seq2seq 任务。 下面提供了我的实现(SimpleTransformer)。我似乎无法使我的模型输出不平凡的序列,并且损失在展平后似乎并没有缩小。也许一些更有经验的机器学习研究人员可以告诉我还可以尝试什么以及他们对这些结果的看法是什么?

任务:

任务如下:

给定一个属于定义的形式语法的句子,给我该句子的解析树表示。例如:

  • 我喜欢苹果。 -> (S (NP I) (VP 像苹果))
  • 约翰说他喜欢苹果 -> (S (NP John) (VP says (S (NP he) (VP 喜欢苹果))))

括号将指代某个非终结符规则(即语法对象/POS)。

每个左括号和右括号类型都是一个标记。 (例如,S-开、S-闭、NP-开、NP-闭……将是单独的标记)。文本将使用字节对编码进行标记。

训练参数

我使用交叉熵损失、AdamW (0.9, 0.98)、放大的 e-4 和 e-5 的学习率来训练我的模型,最大标记大小为 512,批量大小为 4(因为 GPU 内存不是“不够大,无法容纳更多),数据集包含超过 3-4 个时期的约 70 000 个例句。

结果

我的型号:

class SimpleTransformer(nn.Module):
def __init__(self, vocab_size: int, ntokens=512, d_model=512, num_layers=6, bidirectional=False, device="cpu"):
    super().__init__()
    self.d_model = d_model
    self.src_embed = nn.Embedding(vocab_size, self.d_model)
    self.tgt_embed = nn.Embedding(vocab_size, self.d_model)
    self.positional_encoder = PositionalEncoding(d_model=self.d_model, max_len=ntokens)
    self.model = Transformer(d_model=self.d_model, batch_first=True, num_encoder_layers=num_layers,
                             num_decoder_layers=num_layers)
    self.bidirectional = bidirectional
    self.generator = Generator(hidden_size=self.d_model, vocab_size=vocab_size) # Just a fc layer
    self.device = device

def forward(self, in_ids, l_ids, in_masks, l_masks):
    in_ids = self.src_embed(in_ids.long()) * math.sqrt(self.d_model)  # scale by sqrt of dmodel
    in_ids = self.positional_encoder(in_ids)

    l_ids = self.tgt_embed(l_ids.long()) * math.sqrt(self.d_model)
    l_ids = self.positional_encoder(l_ids)

    # Create Masks
    src_seq_len = in_ids.size(1)
    tgt_seq_len = l_ids.size(1)
    src_mask = torch.zeros(src_seq_len, src_seq_len, device=self.device).type(torch.bool)
    if not self.bidirectional:
        tgt_mask = torch.triu(torch.full((tgt_seq_len, tgt_seq_len), float('-inf'), device=self.device), diagonal=1)
    else:
        tgt_mask = torch.zeros(tgt_seq_len, tgt_seq_len, device=self.device).type(torch.bool)
    in_masks = in_masks == 0.0 # in_masks will mask special pad_tokens
    l_masks = l_masks == 0.0 # l_masks will mask special pad_tokens

    out = self.model(src=in_ids, tgt=l_ids,
                     src_mask=src_mask, tgt_mask=tgt_mask,
                     src_key_padding_mask=in_masks,
                     tgt_key_padding_mask=l_masks)
    return self.generator(out)

我尝试过

  • 层数 = 6, 3, 1,
  • d_型号=512
  • 前馈调暗:2048(默认)
  • 位置编码:sin/cos 或无

我看到输出会是这样的:

(s (s (s (s (s (s (s (s (s (s (s (s (s (s (s (s (s (s (s (s (s (s (s (s (s (s (s (s ...

无论输入什么...我只是不知道任务是否很难处理,或者我是否在过程中的某个地方犯了错误。我无法让我的模型输出不平凡的模式。我在机器学习行业还是个新手,所以这肯定可能是某个地方的愚蠢错误。也许我训练太早停止了或者某些超参数是错误的?

比较尝试

此外,我还使用

facebook/fairseq
工具包来训练同一任务的模型。性能更好,损失显着降低:

我还使用相同的训练脚本和训练数据训练了

bart-base
库中预训练的
huggingface
模型。它的性能比之前的两种型号都要好。

machine-learning pytorch nlp transformer-model formal-languages
1个回答
0
投票

好吧,我想我可以解决这个问题。对于遇到同样问题的人:

什么没有解决我的问题,但可能对你有帮助:

  • 针对高度重复的任务尝试不同的位置编码策略
  • 尝试使用渐变裁剪
  • 在更简单的任务上训练你的模型,看看任务是否太难了
  • 尝试不同的学习率
  • 尝试不同的(更简单的)标记化策略,例如按单词标记化
  • 尝试使用标签平滑
  • 使用 AdamW beta (0.9, 0.98)

最后是什么解决了我的问题:

采用本文中使用的学习率调度器。它将线性增加学习率,直到第 4000 步,然后使用

sqrt
函数(可以在here找到)降低学习率。

TLDR

在你的优化器周围使用这个包装器:(它将根据“注意力就是你所需要的”论文更改 lr)

class NoamOptim(object):
""" Optimizer wrapper for learning rate scheduling.
"""

def __init__(self, optimizer, d_model, factor=2, n_warmup_steps=4000):
    self.optimizer = optimizer
    self.d_model = d_model
    self.factor = factor
    self.n_warmup_steps = n_warmup_steps
    self.n_steps = 0

def zero_grad(self):
    self.optimizer.zero_grad()

def step(self):
    self.n_steps += 1
    lr = self.get_lr()
    for p in self.optimizer.param_groups:
        p['lr'] = lr
    self.optimizer.step()

def get_lr(self):
    return self.factor * (
            self.d_model ** (-0.5)
            * min(self.n_steps ** (-0.5), self.n_steps * self.n_warmup_steps ** (-1.5))
    )## Heading ##
© www.soinside.com 2019 - 2024. All rights reserved.