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表示。