如何使用注意掩码计算 mean/max 的 HuggingFace Transformers BERT 令牌嵌入?
How to compute mean/max of HuggingFace Transformers BERT token embeddings with attention mask?
我正在使用 HuggingFace Transformers BERT 模型,我想使用 mean
或 [= 计算句子中标记的摘要向量(a.k.a。嵌入) 16=] 函数。复杂的是有些标记是 [PAD]
,所以我想在计算平均值或最大值时忽略这些标记的向量。
这是一个例子。我最初实例化一个 BertTokenizer
和一个 BertModel
:
import torch
import transformers
from transformers import AutoTokenizer, AutoModel
transformer_name = 'bert-base-uncased'
tokenizer = AutoTokenizer.from_pretrained(transformer_name, use_fast=True)
model = AutoModel.from_pretrained(transformer_name)
然后我将一些句子输入分词器并输出 input_ids
和 attention_mask
。值得注意的是,attention_mask
值为 0 意味着令牌是一个 [PAD]
我可以忽略。
sentences = ['Deep learning is difficult yet very rewarding.',
'Deep learning is not easy.',
'But is rewarding if done right.']
tokenizer_result = tokenizer(sentences, max_length=32, padding=True, return_attention_mask=True, return_tensors='pt')
input_ids = tokenizer_result.input_ids
attention_mask = tokenizer_result.attention_mask
print(input_ids.shape) # torch.Size([3, 11])
print(input_ids)
# tensor([[ 101, 2784, 4083, 2003, 3697, 2664, 2200, 10377, 2075, 1012, 102],
# [ 101, 2784, 4083, 2003, 2025, 3733, 1012, 102, 0, 0, 0],
# [ 101, 2021, 2003, 10377, 2075, 2065, 2589, 2157, 1012, 102, 0]])
print(attention_mask.shape) # torch.Size([3, 11])
print(attention_mask)
# tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
# [1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0],
# [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0]])
现在,我调用 BERT 模型来获取 768-D 令牌嵌入(顶层隐藏状态)。
model_result = model(input_ids, attention_mask=attention_mask, return_dict=True)
token_embeddings = model_result.last_hidden_state
print(token_embeddings.shape) # torch.Size([3, 11, 768])
所以在这一点上,我有:
- [3,11,768] 矩阵中的标记嵌入:3 个句子,11 个标记,每个标记的 768-D 向量。
- [3, 11] 矩阵中的注意力掩码:3 个句子,11 个标记。 1 值表示非
[PAD]
.
如何计算向量上的 mean
/ max
以获得有效的非 [PAD]
标记?
我尝试使用注意力掩码作为掩码,然后调用 torch.max()
,但我没有得到正确的尺寸:
masked_token_embeddings = token_embeddings[attention_mask==1]
print(masked_token_embeddings.shape) # torch.Size([29, 768] <-- WRONG. SHOULD BE [3, 11, 768]
pooled = torch.max(masked_token_embeddings, 1)
print(pooled.values.shape) # torch.Size([29]) <-- WRONG. SHOULD BE [3, 768]
我真正想要的是一个形状为 [3, 768] 的张量。也就是说,3个句子中的每一个都有一个768维的向量。
对于max
,你可以乘以attention_mask
:
pooled = torch.max((token_embeddings * attention_mask.unsqueeze(-1)), axis=1)
对于 mean
,您可以沿轴求和并沿该轴除以 attention_mask
:
mean_pooled = token_embeddings.sum(axis=1) / attention_mask.sum(axis=-1).unsqueeze(-1)
亚历克斯是对的。
Look on hidden states for strings that go into tokenizer. For different strings, padding will have different embeddings.
因此,为了正确合并嵌入,您需要忽略那些填充向量。
假设您想从 BERT 的最后 4 层中获取嵌入(因为它会产生最佳分类结果):
#iterate over the last 4 layers and get embeddings for
#strings without having embeddings from PAD tokens
m = []
for i in range(len(hidden_states[0])):
m.append([hidden_states[j+9][i,:,:][tokens["attention_mask"][i] !=0] for j in range(4)])
#average over all tokens embeddings
means = []
for i in range(len(hidden_states[0])):
means.append(torch.stack(m[i]).mean(dim=1))
#stack embeddings for all strings
pooled = torch.stack(means).reshape(-1,1,3072)
我正在使用 HuggingFace Transformers BERT 模型,我想使用 mean
或 [= 计算句子中标记的摘要向量(a.k.a。嵌入) 16=] 函数。复杂的是有些标记是 [PAD]
,所以我想在计算平均值或最大值时忽略这些标记的向量。
这是一个例子。我最初实例化一个 BertTokenizer
和一个 BertModel
:
import torch
import transformers
from transformers import AutoTokenizer, AutoModel
transformer_name = 'bert-base-uncased'
tokenizer = AutoTokenizer.from_pretrained(transformer_name, use_fast=True)
model = AutoModel.from_pretrained(transformer_name)
然后我将一些句子输入分词器并输出 input_ids
和 attention_mask
。值得注意的是,attention_mask
值为 0 意味着令牌是一个 [PAD]
我可以忽略。
sentences = ['Deep learning is difficult yet very rewarding.',
'Deep learning is not easy.',
'But is rewarding if done right.']
tokenizer_result = tokenizer(sentences, max_length=32, padding=True, return_attention_mask=True, return_tensors='pt')
input_ids = tokenizer_result.input_ids
attention_mask = tokenizer_result.attention_mask
print(input_ids.shape) # torch.Size([3, 11])
print(input_ids)
# tensor([[ 101, 2784, 4083, 2003, 3697, 2664, 2200, 10377, 2075, 1012, 102],
# [ 101, 2784, 4083, 2003, 2025, 3733, 1012, 102, 0, 0, 0],
# [ 101, 2021, 2003, 10377, 2075, 2065, 2589, 2157, 1012, 102, 0]])
print(attention_mask.shape) # torch.Size([3, 11])
print(attention_mask)
# tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
# [1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0],
# [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0]])
现在,我调用 BERT 模型来获取 768-D 令牌嵌入(顶层隐藏状态)。
model_result = model(input_ids, attention_mask=attention_mask, return_dict=True)
token_embeddings = model_result.last_hidden_state
print(token_embeddings.shape) # torch.Size([3, 11, 768])
所以在这一点上,我有:
- [3,11,768] 矩阵中的标记嵌入:3 个句子,11 个标记,每个标记的 768-D 向量。
- [3, 11] 矩阵中的注意力掩码:3 个句子,11 个标记。 1 值表示非
[PAD]
.
如何计算向量上的 mean
/ max
以获得有效的非 [PAD]
标记?
我尝试使用注意力掩码作为掩码,然后调用 torch.max()
,但我没有得到正确的尺寸:
masked_token_embeddings = token_embeddings[attention_mask==1]
print(masked_token_embeddings.shape) # torch.Size([29, 768] <-- WRONG. SHOULD BE [3, 11, 768]
pooled = torch.max(masked_token_embeddings, 1)
print(pooled.values.shape) # torch.Size([29]) <-- WRONG. SHOULD BE [3, 768]
我真正想要的是一个形状为 [3, 768] 的张量。也就是说,3个句子中的每一个都有一个768维的向量。
对于max
,你可以乘以attention_mask
:
pooled = torch.max((token_embeddings * attention_mask.unsqueeze(-1)), axis=1)
对于 mean
,您可以沿轴求和并沿该轴除以 attention_mask
:
mean_pooled = token_embeddings.sum(axis=1) / attention_mask.sum(axis=-1).unsqueeze(-1)
亚历克斯是对的。 Look on hidden states for strings that go into tokenizer. For different strings, padding will have different embeddings.
因此,为了正确合并嵌入,您需要忽略那些填充向量。
假设您想从 BERT 的最后 4 层中获取嵌入(因为它会产生最佳分类结果):
#iterate over the last 4 layers and get embeddings for
#strings without having embeddings from PAD tokens
m = []
for i in range(len(hidden_states[0])):
m.append([hidden_states[j+9][i,:,:][tokens["attention_mask"][i] !=0] for j in range(4)])
#average over all tokens embeddings
means = []
for i in range(len(hidden_states[0])):
means.append(torch.stack(m[i]).mean(dim=1))
#stack embeddings for all strings
pooled = torch.stack(means).reshape(-1,1,3072)