原文地址:Bert生成句向量(pytorch)
本文主要讲如何调用transformers这个包来提取一个句子的特征。
Transformers是TensorFlow 2.0和PyTorch的最新自然语言处理库
Transformers(以前称为pytorch-transformers和pytorch-pretrained-bert)提供用于自然语言理解(NLU)和自然语言生成(NLG)的最先进的模型(BERT,GPT-2,RoBERTa,XLM,DistilBert,XLNet,CTRL …) ,拥有超过32种预训练模型,支持100多种语言,并且在TensorFlow 2.0和PyTorch之间具有深厚的互操作性。
1、安装transformers库
pip install transformers
2、从transformers库中导入Bert的上面所说到的3个类
from transformers import BertModel, BertConfig,BertTokenizer
用BertTokenizer对输入文本进行处理,从预训练模型中加载tokenizer
tokenizer = BertTokenizer.from_pretrained('bert-base-chinese')
如果不想下载,可以先把bert-base-chinese-vocab.txt 下载下来加载进去。
tokenizer = BertTokenizer.from_pretrained('bert-base-chinese-vocab.txt')
需要在文本开头加上’[CLS]’,在每个句子后面加上’[SEP]’,这样输入到BertModel中才能被正确识别。
from transformers import BartModel,BertConfig, BertTokenizer
import torch
# 用BertTokenizer对输入文本进行处理,从预训练模型中加载tokenizer
tokenizer = BertTokenizer.from_pretrained('./bert-base-chinese/vocab.txt')
text = "[CLS] 今天天气不错,适合出行。 [SEP] 今天是晴天,可以出去玩。 [SEP]"
tokenized_text = tokenizer.tokenize(text) # 用tokenizer对句子分词
'''
输出;print(tokenized_text)
['[CLS]', '今', '天', '天', '气', '不', '错', ',', '适', '合', '出', '行', '。',
'[SEP]', '今', '天', '是', '晴', '天', ',', '可', '以', '出', '去', '玩', '。', '[SEP]']
'''
indexed_tokens = tokenizer.convert_tokens_to_ids(tokenized_text) #词在预训练词表中的索引列表
# print(indexed_tokens)
'''
output:print(indexed_tokens)
[101, 791, 1921, 1921, 3698, 679, 7231, 8024, 6844, 1394, 1139, 6121, 511,
102, 791, 1921, 3221, 3252, 1921, 8024, 1377, 809, 1139, 1343, 4381, 511, 102]
'''
#用来指定哪个是第一个句子,哪个是第二个句子,0的部分代表句子一, 1的部分代表句子二
segments_ids = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
#转换成PyTorch tensors
tokens_tensor = torch.tensor([indexed_tokens])
segments_tensors = torch.tensor([segments_ids])
'''
output:print(tokens_tensor)
tensor([[ 101, 791, 1921, 1921, 3698, 679, 7231, 8024, 6844, 1394, 1139, 6121,
511, 102, 791, 1921, 3221, 3252, 1921, 8024, 1377, 809, 1139, 1343,
4381, 511, 102]])
output:print(segments_tensors)
tensor([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]])
'''
tokens_tensor,segments_tensors将作为BertModel的输入。
很多时候输入文本是只有一个句子的,上面两个句子的情况只是简单提一下,下面主要是以一个句子为主。同样,先在句子前面加上’[CLS]’,后面加上’[SEP]’。一般神经网络提取文本特征是以batch为单位的,因此还需要用到一个输入input_masks,假设text是一个batch的数据。
from transformers import BartModel,BertConfig, BertTokenizer
import torch
# 用BertTokenizer对输入文本进行处理,从预训练模型中加载tokenizer
tokenizer = BertTokenizer.from_pretrained('./bert-base-chinese/vocab.txt')
texts = ["[CLS] 今天天气不错,适合出行。 [SEP]",
"[CLS] 今天是晴天,可以出去玩。 [SEP]"]
tokens, segments, input_masks = [], [], []
for text in texts:
tokenized_text = tokenizer.tokenize(text) #用tokenizer对句子分词
indexed_tokens = tokenizer.convert_tokens_to_ids(tokenized_text)#索引列表
tokens.append(indexed_tokens)
segments.append([0] * len(indexed_tokens))
input_masks.append([1] * len(indexed_tokens))
'''
output:print(tokenized_text)
['[CLS]', '今', '天', '是', '晴', '天', ',
', '可', '以', '出', '去', '玩', '。', '[SEP]']
output:print(segments)
[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
output:print(segments)
[[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]
'''
max_len = max([len(single) for single in tokens]) # 最大的句子长度
'''
# segments列表全0,因为只有一个句子1,没有句子2
# input_masks列表1的部分代表句子单词,而后面0的部分代表paddig,只是用于保持输入整齐,没有实际意义。
# 相当于告诉BertModel不要利用后面0的部分
'''
for j in range(len(tokens)):
padding = [0] * (max_len - len(tokens[j]))
tokens[j] += padding
segments[j] += padding
input_masks[j] += padding
#转换成PyTorch tensors
tokens_tensor = torch.tensor(tokens)
segments_tensors = torch.tensor(segments)
input_masks_tensors = torch.tensor(input_masks)
tokens_tensor,segments_tensors,input_masks_tensors 将作为BertModel的输入。
在BertModel后面加上一个全连接层,能够调整输出feature的维度。
class BertTextNet(nn.Module):
def __init__(self, code_length): #code_length为fc映射到的维度大小
super(TextNet, self).__init__()
modelConfig = BertConfig.from_pretrained('bert-base-chinese-config.json')
self.textExtractor = BertModel.from_pretrained(
'bert-base-chinese-pytorch_model.bin', config=modelConfig)
embedding_dim = self.textExtractor.config.hidden_size
self.fc = nn.Linear(embedding_dim, code_length)
self.tanh = torch.nn.Tanh()
def forward(self, tokens, segments, input_masks):
output=self.textExtractor(tokens, token_type_ids=segments,
attention_mask=input_masks)
text_embeddings = output[0][:, 0, :]
#output[0](batch size, sequence length, model hidden dimension)
features = self.fc(text_embeddings)
features = self.tanh(features)
return features
使用pytorch_transformers本身提供的预训练BertConfig,以及加载预训练模型。
config = BertConfig.from_pretrained('bert-base-chinese')
self.textExtractor = BertModel.from_pretrained('bert-base-chinese', config=modelConfig)
否则还是像上面模型那样加载本地下载的预训练模型。
把输入到BertModel后得到的输出output,一般是使用它的第0维信息。
outputs[0] # The last hidden-state is the first element of the output tuple
其中output[0][:,0,:]
代表下图中的C的输出向量,参考论文Bert
from transformers import BertModel,BertConfig, BertTokenizer
import torch
import torch.nn as nn
# 用BertTokenizer对输入文本进行处理,从预训练模型中加载tokenizer
# tokenizer = BertTokenizer.from_pretrained('./bert-base-chinese/vocab.txt')
#
# text = "[CLS] 今天天气不错,适合出行。 [SEP] 今天是晴天,可以出去玩。 [SEP]"
# tokenized_text = tokenizer.tokenize(text) # 用tokenizer对句子分词
#
# '''
# 输出;print(tokenized_text)
# ['[CLS]', '今', '天', '天', '气', '不', '错', ',', '适', '合', '出', '行', '。',
# '[SEP]', '今', '天', '是', '晴', '天', ',', '可', '以', '出', '去', '玩', '。', '[SEP]']
# '''
# indexed_tokens = tokenizer.convert_tokens_to_ids(tokenized_text) #词在预训练词表中的索引列表
# # print(indexed_tokens)
# '''
# output:print(indexed_tokens)
# [101, 791, 1921, 1921, 3698, 679, 7231, 8024, 6844, 1394, 1139, 6121, 511,
# 102, 791, 1921, 3221, 3252, 1921, 8024, 1377, 809, 1139, 1343, 4381, 511, 102]
# '''
# #用来指定哪个是第一个句子,哪个是第二个句子,0的部分代表句子一, 1的部分代表句子二
# segments_ids = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
#
# #转换成PyTorch tensors
# tokens_tensor = torch.tensor([indexed_tokens])
# segments_tensors = torch.tensor([segments_ids])
# '''
# output:print(tokens_tensor)
# tensor([[ 101, 791, 1921, 1921, 3698, 679, 7231, 8024, 6844, 1394, 1139, 6121,
# 511, 102, 791, 1921, 3221, 3252, 1921, 8024, 1377, 809, 1139, 1343,
# 4381, 511, 102]])
#
# output:print(segments_tensors)
# tensor([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]])
# '''
#
# # 用BertTokenizer对输入文本进行处理,从预训练模型中加载tokenizer
# tokenizer = BertTokenizer.from_pretrained('./bert-base-chinese/vocab.txt')
# texts = ["[CLS] 今天天气不错,适合出行。 [SEP]",
# "[CLS] 今天是晴天,可以出去玩。 [SEP]"]
# tokens, segments, input_masks = [], [], []
# for text in texts:
# tokenized_text = tokenizer.tokenize(text) #用tokenizer对句子分词
# indexed_tokens = tokenizer.convert_tokens_to_ids(tokenized_text)#索引列表
# tokens.append(indexed_tokens)
# segments.append([0] * len(indexed_tokens))
# input_masks.append([1] * len(indexed_tokens))
# '''
# output:print(tokenized_text)
# ['[CLS]', '今', '天', '是', '晴', '天', ',
# ', '可', '以', '出', '去', '玩', '。', '[SEP]']
#
# output:print(segments)
# [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
# [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
#
# output:print(segments)
# [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
# [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]
#
# '''
# max_len = max([len(single) for single in tokens]) # 最大的句子长度
#
# '''
# # segments列表全0,因为只有一个句子1,没有句子2
# # input_masks列表1的部分代表句子单词,而后面0的部分代表paddig,只是用于保持输入整齐,没有实际意义。
# # 相当于告诉BertModel不要利用后面0的部分
# '''
#
# for j in range(len(tokens)):
# padding = [0] * (max_len - len(tokens[j]))
# tokens[j] += padding
# segments[j] += padding
# input_masks[j] += padding
# #转换成PyTorch tensors
# tokens_tensor = torch.tensor(tokens)
# segments_tensors = torch.tensor(segments)
# input_masks_tensors = torch.tensor(input_masks)
# 1.构建模型
class BertTextNet(nn.Module):
def __init__(self, code_length): # code_length为fc映射到的维度大小
super(BertTextNet, self).__init__()
modelConfig = BertConfig.from_pretrained('./bert-base-chinese/bert_config.json')
self.textExtractor = BertModel.from_pretrained(
'./bert-base-chinese/pytorch_model.bin', config=modelConfig, from_tf=True)
embedding_dim = self.textExtractor.config.hidden_size
self.fc = nn.Linear(embedding_dim, code_length)
self.tanh = torch.nn.Tanh()
def forward(self, tokens, segments, input_masks):
output = self.textExtractor(tokens, token_type_ids=segments,
attention_mask=input_masks)
text_embeddings = output[0][:, 0, :]
# output[0](batch size, sequence length, model hidden dimension)
features = self.fc(text_embeddings)
features = self.tanh(features)
return features
textNet = BertTextNet(code_length=32)
# 2.数据处理
# 用BertTokenizer对输入文本进行处理,从预训练模型中加载tokenizer
tokenizer = BertTokenizer.from_pretrained('./bert-base-chinese/vocab.txt')
texts = ["[CLS] 今天天气不错,适合出行。 [SEP]",
"[CLS] 今天是晴天,可以出去玩。 [SEP]"]
tokens, segments, input_masks = [], [], []
for text in texts:
tokenized_text = tokenizer.tokenize(text) #用tokenizer对句子分词
indexed_tokens = tokenizer.convert_tokens_to_ids(tokenized_text)#索引列表
tokens.append(indexed_tokens)
segments.append([0] * len(indexed_tokens))
input_masks.append([1] * len(indexed_tokens))
max_len = max([len(single) for single in tokens]) # 最大的句子长度
for j in range(len(tokens)):
padding = [0] * (max_len - len(tokens[j]))
tokens[j] += padding
segments[j] += padding
input_masks[j] += padding
#转换成PyTorch tensors
tokens_tensor = torch.tensor(tokens)
segments_tensors = torch.tensor(segments)
input_masks_tensors = torch.tensor(input_masks)
# 3.提取文本特征——————
text_hashCodes = textNet(tokens_tensor,segments_tensors,input_masks_tensors) #text_hashCodes是一个32-dim文本特征
print(text_hashCodes)
在运行过程中出现如下的问题:
OSError: Unable to load weights from pytorch checkpoint file for './bert-base-chinese' at './bert-base-chinese/pytorch_model.bin'If you tried to load a PyTorch model from a TF 2.0 checkpoint, please set from_tf=True.
不知道是啥子原因,但是叫我set from_tf=True. 那我就设置吧!
self.textExtractor = BertModel.from_pretrained(
'./bert-base-chinese/pytorch_model.bin', config=modelConfig, from_tf=True)
然后接着告诉我,没有安装TensorFlow
那就装呗!装的时候又告诉我:
ERROR: Cannot uninstall 'six'. It is a distutils installed project and thus we cannot accurately determine which files belong to it which would lead to only a partial uninstall.
找了好久,找到解决办法:
pip install six --upgrade --ignore-installed six
装好了,运行,艹!
这特么貌似是h5文件加载错误,但是我特么文件都是官方的呀?于是我往上看,发现最早的一个错误:
貌似又是TensorFlow这个坑逼框架在作妖,差了一下又是啥显卡版本与TensorFlow版本不兼容什么东西!
弄一下午,害……放弃了……TensorFlow就是一个坑!!!!
借同学服务器跑出来的结果: