Bert获取词向量的过程
参考博客:https://blog.csdn.net/u011984148/article/details/99921480
1.把我们要获取词向量的句子进行分词处理,再根据模型中的vocab.txt获取每个词的对应的索引。
token初始化
tokenized_text = tokenizer.tokenize(marked_text) print (tokenized_text) ['[CLS]', 'after', 'stealing', 'money', 'from', 'the', 'bank', 'vault', ',', 'the', 'bank', 'robber', 'was', 'seen', 'fishing', 'on', 'the', 'mississippi', 'river', 'bank', '.', '[SEP]']
通过分词后的token获取词表中对应的索引
indexed_tokens = tokenizer.convert_tokens_to_ids(tokenized_text) for tup in zip(tokenized_text, indexed_tokens): print (tup) ('[CLS]', 101) ('after', 2044) ('stealing', 11065) ('money', 2769) ('from', 2013) ('the', 1996) ('bank', 2924) ('vault', 11632) (',', 1010) ('the', 1996) ('bank', 2924) ('robber', 27307) ('was', 2001) ('seen', 2464) ('fishing', 5645) ('on', 2006) ('the', 1996) ('mississippi', 5900) ('river', 2314) ('bank', 2924) ('.', 1012) ('[SEP]', 102)
2.生成句子的位置编码,bert以一个或者两个句子作为输入,要对句子进行处理,获取他们的位置编码。
segments_ids = [1] * len(tokenized_text) print (segments_ids) [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
3.
接下来,我们需要将数据转换为torch张量并调用BERT模型。BERT PyTorch接口要求数据使用torch张量而不是Python列表,所以我们在这里转换列表——这不会改变形状或数据。
eval()将我们的模型置于评估模式,而不是训练模式。在这种情况下,评估模式关闭了训练中使用的dropout正则化。
调用 from_pretrained
将从网上获取模型。当我们加载 bert-base-uncased
时,我们会在日志中看到打印的模型定义。该模型是一个12层的深度神经网络!
# Convert inputs to PyTorch tensors tokens_tensor = torch.tensor([indexed_tokens]) segments_tensors = torch.tensor([segments_ids]) # Load pre-trained model (weights) model = BertModel.from_pretrained('bert-base-uncased') # Put the model in "evaluation" mode, meaning feed-forward operation. model.eval()
4.接下来,让我们获取网络的隐藏状态。encode_layers隐藏状态是一个四维的张量,分别是隐藏层的层数、batch(批处理的大小)、一个输入的句子(一个样本),句子中每个token的编码(768维的向量)
torch.no_grad禁用梯度计算,节省内存,并加快计算速度(我们不需要梯度或反向传播,因为我们只是运行向前传播)。
# Predict hidden states features for each layer with torch.no_grad(): encoded_layers, _ = model(tokens_tensor, segments_tensors)
print ("Number of layers:", len(encoded_layers)) layer_i = 0 print ("Number of batches:", len(encoded_layers[layer_i])) batch_i = 0 print ("Number of tokens:", len(encoded_layers[layer_i][batch_i])) token_i = 0 print ("Number of hidden units:", len(encoded_layers[layer_i][batch_i][token_i])) Number of layers: 12 Number of batches: 1 Number of tokens: 22 Number of hidden units: 768
5.对获取的隐藏状态进行重构,获取的token_embeddings是一个三维的张量,是一个样本中每个token在神经网络每一层对应的编码,对应的维度分别是句子中的token个数,神经网络的层数,每个token对应的编码。
[# tokens, # layers, # features] # Convert the hidden state embeddings into single token vectors # Holds the list of 12 layer embeddings for each token # Will have the shape: [# tokens, # layers, # features] token_embeddings = [] # For each token in the sentence... for token_i in range(len(tokenized_text)): # Holds 12 layers of hidden states for each token hidden_layers = [] # For each of the 12 layers... for layer_i in range(len(encoded_layers)): # Lookup the vector for `token_i` in `layer_i` vec = encoded_layers[layer_i][batch_i][token_i] hidden_layers.append(vec) token_embeddings.append(hidden_layers) # Sanity check the dimensions: print ("Number of tokens in sequence:", len(token_embeddings)) print ("Number of layers per token:", len(token_embeddings[0])) Number of tokens in sequence: 22 Number of layers per token: 12
6.构建词向量和句向量,词向量是根据神经网络的最后四层来进行构建的,因为最后四层的效果最好,可以使用拼接的方式,也可以使用求和取平均的方式来获取词向量的编码。句向量是是对每个token的倒数第二个隐藏层求平均,生成一个768长度的向量。具体代码见上面博客连接。
总结,通过预训练模型来获取词向量,就是先对句子分词,根据token获取词表中对应的索引,之后再获取位置编码,将它们输入到模型之中,(模型可以设置成评估模式,关闭训练过程中的正则化,同时去掉反向传播)模型就会返回每个隐藏层中的token的编码,之后在对其进行处理,比如用最后四层的token编码来计算最终的token表示。