我目前正在使用 vanilla
torch.nn.Transformer
执行 seq2seq 任务。
下面提供了我的实现(SimpleTransformer)。我似乎无法使我的模型输出不平凡的序列,并且损失在展平后似乎并没有缩小。也许一些更有经验的机器学习研究人员可以告诉我还可以尝试什么以及他们对这些结果的看法是什么?
任务如下:
给定一个属于定义的形式语法的句子,给我该句子的解析树表示。例如:
括号将指代某个非终结符规则(即语法对象/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)
我尝试过
我看到输出会是这样的:
(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
模型。它的性能比之前的两种型号都要好。
好吧,我想我可以解决这个问题。对于遇到同样问题的人:
什么没有解决我的问题,但可能对你有帮助:
最后是什么解决了我的问题:
采用本文中使用的学习率调度器。它将线性增加学习率,直到第 4000 步,然后使用
sqrt
函数(可以在here找到)降低学习率。
在你的优化器周围使用这个包装器:(它将根据“注意力就是你所需要的”论文更改 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 ##