如何将 BERT 的最后 4 个连接的隐藏层馈送到 FC 层

How to fed last 4 concatenated hidden layers of BERT to FC layers

我正在尝试执行分类任务,我从 BERT 获得了最后 4 层并将它们连接起来。

out = model(...)
out=torch.cat([out['hidden_states'][-i] for i in range(1,5)],dim=-1)

现在的形状是 (12,200,768*4)batch,max_length,concatenation layer 但是对于全连接层我们需要有二维。因此,一种方法是像 torch.mean((12,200,768*4),dim=1) 那样取平均值并得到 (12,768*4) 的输出。 但是我很困惑什么是原始的 BERT 方法

没有“原始”的 BERT 方法用于连接隐藏层的分类。您有多种选择可以继续,我将只描述对您的方法的评论,并在下面提出替代方案。

初步:

import torch.nn as nn
from transformers import BertTokenizerFast, BertModel

t = BertTokenizerFast.from_pretrained("bert-base-cased")
m = BertModel.from_pretrained("bert-base-cased")
fc = nn.Linear(768, 5)

s = ["This is a random sentence", "This is another random sentence with more words"]

i = t(s, padding=True,return_tensors="pt")

with torch.no_grad():
  o = m(**i, output_hidden_states=True)

print(i)

首先,你应该看看你的输入:

#print(I)
{'input_ids': 
tensor([[ 101, 1188, 1110,  170, 7091, 5650,  102,    0,    0,    0],
        [ 101, 1188, 1110, 1330, 7091, 5650, 1114, 1167, 1734,  102]]), 
'token_type_ids': 
tensor([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]), 
'attention_mask': 
tensor([[1, 1, 1, 1, 1, 1, 1, 0, 0, 0],
        [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]])
}

你在这里应该注意的是,较短的句子被填充了。这是相关的,因为简单地将平均值与 torch.mean 合并,将导致同一句子的不同句子嵌入,具体取决于填充标记的数量。当然,模型会在经过充分训练后学会在某种程度上处理该问题,但是您应该使用更多 sophisticated mean function 立即删除填充标记:

def mean_pooling(model_output, attention_mask):
    input_mask_expanded = attention_mask.unsqueeze(-1).expand(model_output.size()).float()
    return torch.sum(model_output * input_mask_expanded, 1) / torch.clamp(input_mask_expanded.sum(1), min=1e-9)


o_mean = [mean_pooling(o.hidden_states[-x],i.attention_mask) for x in range(1,5)]
#we want a tensor and not a list
o_mean = torch.stack(o_mean, dim=1)
#we want only one tensor per sequence
o_mean = torch.mean(o_mean,dim=1)

print(o_mean.shape)
with torch.no_grad():
  print(fc(o_mean))

输出:

torch.Size([2, 768])
tensor([[ 0.0677, -0.0261, -0.3602,  0.4221,  0.2251],
        [-0.0328, -0.0161, -0.5209,  0.5825,  0.2405]])

这些操作非常昂贵,人们经常使用称为 cls 池的方法作为具有可比性能的更便宜的替代方法:

#We only use the cls token (i.e. first token of the sequence)
#id 101
o_cls = [o.hidden_states[-x][:, 0] for x in range(1,5)]
#we want a tensor and not a list
o_cls = torch.stack(o_cls, dim=1)
#we want only one tensor per sequence
o_cls = torch.mean(o_cls,dim=1)
print(o_cls.shape)
with torch.no_grad():
  print(fc(o_cls))

输出:

torch.Size([2, 768])
tensor([[-0.3731,  0.0473, -0.4472,  0.3804,  0.4057],
        [-0.3468,  0.0685, -0.5885,  0.4994,  0.4182]])