Pytorch 变压器模型中的大的爆炸性损失
Posted
技术标签:
【中文标题】Pytorch 变压器模型中的大的爆炸性损失【英文标题】:Large, exploding loss in Pytorch transformer model 【发布时间】:2021-11-18 06:21:01 【问题描述】:我正在尝试使用变压器模型解决序列到序列问题。数据来源于一组填字游戏。
位置编码和转换器类如下:
class PositionalEncoding(nn.Module):
def __init__(self, d_model: int, dropout: float = 0.1, max_len: int = 3000):
super().__init__()
self.dropout = nn.Dropout(p=dropout)
position = torch.arange(max_len).unsqueeze(1)
div_term = torch.exp(torch.arange(0, d_model, 2) * (-math.log(10000.0) / d_model))
pe = torch.zeros(1, max_len, d_model)
pe[0, :, 0::2] = torch.sin(position * div_term)
pe[0, :, 1::2] = torch.cos(position * div_term)
self.register_buffer('pe', pe)
def debug(self, x):
return x.shape, x.size()
def forward(self, x: Tensor) -> Tensor:
x = x + self.pe[:, :x.size(1), :]
return self.dropout(x)
class Transformer(nn.Module):
def __init__(
self,
num_tokens,
dim_model,
num_heads,
num_encoder_layers,
num_decoder_layers,
batch_first,
dropout_p,
):
super().__init__()
self.model_type = "Transformer"
self.dim_model = dim_model
self.positional_encoder = PositionalEncoding(
d_model=dim_model, dropout=dropout_p, max_len=3000
)
self.embedding = nn.Embedding.from_pretrained(vec_weights, freeze=False)#nn.Embedding(num_tokens, dim_model)
self.transformer = nn.Transformer(
d_model=dim_model,
nhead=num_heads,
num_encoder_layers=num_encoder_layers,
num_decoder_layers=num_decoder_layers,
dropout=dropout_p,
batch_first = batch_first
)
self.out = nn.Linear(dim_model, num_tokens)
def forward(self, src, tgt, tgt_mask=None, src_pad_mask=None, tgt_pad_mask=None):
src = self.embedding(src)*math.sqrt(self.dim_model)
tgt = self.embedding(tgt)*math.sqrt(self.dim_model)
src = self.positional_encoder(src)
tgt = self.positional_encoder(tgt)
transformer_out = self.transformer(src, tgt, tgt_mask=tgt_mask, src_key_padding_mask=src_pad_mask, tgt_key_padding_mask=tgt_pad_mask)
out = self.out(transformer_out)
return out
def get_tgt_mask(self, size) -> torch.tensor:
mask = torch.tril(torch.ones(size, size) == 1)
mask = mask.float()
mask = mask.masked_fill(mask == 0, float('-inf'))
mask = mask.masked_fill(mask == 1, float(0.0))
return mask
def create_pad_mask(self, matrix: torch.tensor, pad_token: int) -> torch.tensor:
return (matrix == pad_token)
输入张量是大小为 N x S 的源张量,其中 N 是批量大小,S 是源序列长度,以及大小为 N x T 的目标张量,其中 T 是目标序列长度。 S约为10,T约为5,而项目总数约为160,000-200,000,分为512的batch size。它们是torch.IntTensors,元素范围从0到V,其中V是词汇表长度。
第一层是一个嵌入层,它将输入从 N by S 带到 N by S by E,其中 E 是嵌入维度 (300),或者在目标的情况下到 N by T by E。第二层在不改变形状的情况下添加位置编码。然后两个张量都通过变换层,它输出一个 N×T×E 张量。最后,我们将这个输出通过一个线性层,它产生一个 N x T x V 输出,其中 V 是问题中使用的词汇表的大小。这里 V 约为 56,697。最频繁的标记(单词)在目标张量中出现大约 50-60 次。
transformer 类还包含实现屏蔽矩阵的函数。
然后我们创建模型并运行它(这个过程被包装在一个函数中)。
device = "cuda"
src_train, src_test = torch.utils.data.random_split(src_t, [int(0.9*len(src_t)), len(src_t)-int(0.9*len(src_t))])
src_train, src_test = src_train[:512], src_test[:512]
tgt_train, tgt_test = torch.utils.data.random_split(tgt_t, [int(0.9*len(tgt_t)), len(tgt_t)-int(0.9*len(tgt_t))])
tgt_train, tgt_test = tgt_train[:512], tgt_test[:512]
train_data, test_data = list(zip(src_train, tgt_train)), list(zip(src_test, tgt_test))
train, test = torch.utils.data.DataLoader(dataset=train_data), torch.utils.data.DataLoader(dataset=test_data)
model = Transformer(num_tokens=ntokens, dim_model=300, num_heads=2, num_encoder_layers=3, num_decoder_layers=3, batch_first = True, dropout_p=0.1).to(device)
loss_function = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=0.0000001)
n_epochs = 50
def train_model(model, optimizer, loss_function, n_epochs):
loss_value=0
for epoch in range(n_epochs):
print(f"Starting epoch epoch")
for batch, data in enumerate(train):
x, y = data
if batch%100 == 0:
print(f"Batch is batch")
batch += 1
optimizer.zero_grad()
x, y = torch.tensor(x).to(device), torch.tensor(y).to(device)
y_input, y_base = y[:, :-1], y[:, 1:]
y_input, y_base = y_input.to(device), y_base.to(device)
tgt_mask = model.get_tgt_mask(y_input.shape[1]).to(device)
pad_token = vocabulary_table[embeddings.key_to_index["/"]]
src_pad_mask = model.create_pad_mask(x, pad_token).to(device)
tgt_pad_mask = model.create_pad_mask(y_input, pad_token).to(device)
z = model(x, y_input, tgt_mask, src_pad_mask, tgt_pad_mask)
z = z.permute(0, 2, 1).to(device)
y_base = y_base.long().to(device)
loss = loss_function(z, y_base).to(device)
loss.backward()
nn.utils.clip_grad_norm_(model.parameters(), max_norm=2.0, norm_type=2)
optimizer.step()
loss_value += float(loss)
if batch%100 == 0:
print(f"For epoch epoch, batch batch the cross-entropy loss is loss_value")
#Free GPU memory.
del z
del x
del y
del y_input
del y_base
del loss
torch.cuda.empty_cache()
return model.parameters(), loss_value
基本上,我们将数据分为测试集和训练集,并使用 SGD 优化器和交叉熵损失。我们为目标张量和源张量的填充创建一个掩码矩阵,并为目标张量的未见元素创建一个掩码矩阵。然后我们执行通常的梯度更新步骤。现在,没有验证循环,因为我什至无法减少训练损失。
损失非常高,100批后达到1000多。更令人担忧的是,损失在训练期间也迅速增加,而不是减少。在我包含的代码中,我尝试剪裁渐变,降低学习率,并使用更小的样本来调试代码。
什么可能导致这种行为?
【问题讨论】:
一定是代码中的某个错误......这段代码来自哪里? 【参考方案1】:你只是在增加损失,所以自然只能增加。
loss_value += float(loss)
您应该在每个 epoch 之后将其设置为零。现在您在训练过程开始时将其设置为零一次。如果您有兴趣,这里有一个训练循环模板 (https://pytorch.org/tutorials/beginner/blitz/cifar10_tutorial.html)。这解释了不断增加的损失。为了进一步排除故障(如果需要),我会抛出一个验证循环。
【讨论】:
谢谢。已经两天了,所以我自己已经发现了错误。以上是关于Pytorch 变压器模型中的大的爆炸性损失的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Pytorch Lightning 微调之前测试模型?