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 微调之前测试模型?

带有变压器的分类模型的 keras 模型中的错误

如何去除 GPU 内存中的变压器模型

服务模型时出现 Amazon Sagemaker ModelError

高频变压器为什么能够输出这么大的电流?

高频变压器为什么能够输出这么大的电流?