如何将 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]])
我正在尝试执行分类任务,我从 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]])