使用转换器 BertModel 和 BertTokenizer 的向量表示不一致

Inconsistent vector representation using transformers BertModel and BertTokenizer

我有 BertTokenizer (tokenizer) 和 BertModel (model) 来自 transformers 图书馆。 我已经使用一些维基百科文章从头开始预训练模型,只是为了测试它是如何工作的。

模型经过预训练后,我想为给定的句子提取层向量表示。为此,我计算了 11 个隐藏(768 大小)向量的平均值。我这样做如下(line 是单个 String):

padded_sequence = tokenizer(line, padding=True)
        
indexed_tokens = padded_sequence['input_ids']
attention_mask = padded_sequence["attention_mask"]

tokens_tensor = torch.tensor([indexed_tokens])
attention_mask_tensor = torch.tensor([attention_mask])

outputs = model(tokens_tensor, attention_mask_tensor)
hidden_states = outputs[0]

line_vectorized = hidden_states[0].data.numpy().mean(axis=0)

到目前为止一切顺利。 我可以单独为每个句子做这个。但是现在我想分批,即。我有一堆句子,而不是迭代每个句子,我发送适当的张量表示来一次获取所有向量。我这样做如下(lines 是一个 list of Strings):

padded_sequences = self.tokenizer_PYTORCH(lines, padding=True)
        
indexed_tokens_list = padded_sequences['input_ids']
attention_mask_list = padded_sequences["attention_mask"]
        
tokens_tensors_list = [torch.tensor([indexed_tokens]) for indexed_tokens in indexed_tokens_list]
attention_mask_tensors_list = [torch.tensor([attention_mask ]) for attention_mask in attention_mask_list ]
        
tokens_tensors = torch.cat((tokens_tensors_list), 0)
attention_mask_tensors = torch.cat((attention_mask_tensors_list ), 0)

outputs = model(tokens_tensors, attention_mask_tensors)
hidden_states = outputs[0]

lines_vectorized = [hidden_states[i].data.numpy().mean(axis=0) for i in range(0, len(hidden_states))]

问题如下:我必须使用填充,以便我可以适当地连接令牌张量。这意味着索引标记和注意力掩码可以比之前单独评估句子的情况更大。 但是当我使用 padding 时,对于被填充的句子我得到了不同的结果

示例: 我有两句话(法语但没关系):

sentence_A = "appareil digestif un article de wikipedia l encyclopedie libre"

sentence_B = "sauter a la navigation sauter a la recherche cet article est une ebauche concernant la biologie"

当我对这两个句子单独求值时,我得到:

sentence_A:

indexed_tokens =  [10002, 3101, 4910, 557, 73, 3215, 9630, 2343, 4200, 8363, 10000]
attention_mask =  [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
line_vectorized =  [-0.9304411   0.53798294 -1.6231083 ...]

sentence_B:

indexed_tokens =  [10002, 2217, 6496, 1387, 9876, 2217, 6496, 1387, 4441, 405, 73, 6451, 3, 2190, 5402, 1387, 2971, 10000]
attention_mask =  [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
line_vectorized =  [-0.8077076   0.56028104 -1.5135447  ...]

但是当我批量评估两个句子时,我得到:

sentence_A:

indexed_tokens =  [10002, 3101, 4910, 557, 73, 3215, 9630, 2343, 4200, 8363, 10000, 10004, 10004, 10004, 10004, 10004, 10004, 10004]
attention_mask =  [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0]
line_vectorized =  [-1.0473819   0.6090186  -1.727466  ...]

sentence_B:

indexed_tokens =  [10002, 2217, 6496, 1387, 9876, 2217, 6496, 1387, 4441, 405, 73, 6451, 3, 2190, 5402, 1387, 2971, 10000]
attention_mask =  [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
line_vectorized =  [-0.8077076   0.56028104 -1.5135447  ...]

也就是说,因为sentence_B大于sentence_A,所以sentence_A被填充了,attention mask也被0填充了.索引标记现在包含额外的标记(10004,我假设是 empty)。 sentence_B 的矢量表示没有改变。但是 sentence_A 的矢量表示已更改

我想知道这是否按预期工作(我假设没有)。 我想我做错了什么,但我不知道是什么。

有什么想法吗?

当你在每批单个句子中执行时,句子的最大长度是最大标记数,但是,当你批量执行时,句子的最大长度在整个批次中保持不变,这是默认的到最长句子中的最大标记数。 1 在这种情况下的最大值表示它不是 <PAD> 标记,而 0 表示 <PAD> 标记。最好的控制方法是定义最大序列长度,截断超过最大序列长度的句子。

这可以使用另一种方法来分批标记文本(单个句子可以被视为 batch-size of 1):

tokenizer = BertTokenizer.from_pretrained("<your bert model>", do_lower_case=True)
encoding = tokenizer.batch_encode_plus(lines, return_tensors='pt',padding=True, truncation=True, max_length=50, add_special_tokens = True) ## Change the max_length to the required max length
indexed_tokens = encoding['input_ids']
attention_mask = encoding['attention_mask']