Pytorch变压器模型爆炸损失大



我正试图用变压器模型解决序列到序列的问题。数据来源于一组填字游戏。

位置编码和转换器类如下:

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 × S的源张量,其中N为批大小,S为源序列长度;输入张量为大小为N × T的目标张量,其中T为目标序列长度。S约为10件,T约为5件,总件数约为16 -20万件,分批大小为512件。他们是火炬。张量,元素范围从0到V,其中V为词汇表长度。

第一层是一个嵌入层,它将输入从N × S到N × S × E,其中E是嵌入维数(300),或者在目标的情况下是N × T × E。第二层在不改变形状的情况下添加位置编码。然后两个张量都经过变压器层,它输出一个N × T × E张量。最后,我们通过一个线性层传递这个输出,它产生一个N × T × V的输出,其中V是问题中使用的词汇表的大小。这里V大约是56697。最频繁的标记(单词)在目标张量中出现约50-60次。

变压器类还包含实现屏蔽矩阵的函数。

然后我们创建模型并运行它(这个过程被封装在一个函数中)。

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多。更令人担忧的是,在训练过程中,这种损失也会迅速增加,而不是减少。在我所包含的代码中,我尝试剪切梯度,降低学习率,并使用更小的样本来调试代码。

是什么导致了这种行为?

你只是在增加你的损失,所以它自然只会增加。

loss_value += float(loss)

你应该在每个epoch之后将其设置为0。在训练开始的时候,你把它设为0。如果你感兴趣,这里有一个训练循环模板(https://pytorch.org/tutorials/beginner/blitz/cifar10_tutorial.html)。这就解释了损失增加的原因。为了进一步排除故障(如果需要),我会抛出一个验证循环。

最新更新