BERT | (4)Bert生成句向量_pytorch

原文地址: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之间具有深厚的互操作性。

 

Bert

1、安装transformers库

pip install transformers

2、从transformers库中导入Bert的上面所说到的3个类

 from transformers import  BertModel, BertConfig,BertTokenizer

1、文本处理

用BertTokenizer对输入文本进行处理,从预训练模型中加载tokenizer

tokenizer = BertTokenizer.from_pretrained('bert-base-chinese')

如果不想下载,可以先把bert-base-chinese-vocab.txt 下载下来加载进去。

tokenizer = BertTokenizer.from_pretrained('bert-base-chinese-vocab.txt')

 

输入文本是两个sentence

需要在文本开头加上’[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的输入。

 

输入文本是一个sentence

很多时候输入文本是只有一个句子的,上面两个句子的情况只是简单提一下,下面主要是以一个句子为主。同样,先在句子前面加上’[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的输入。

2、构建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

BERT | (4)Bert生成句向量_pytorch_第1张图片

3、完整代码

 

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就是一个坑!!!!

借同学服务器跑出来的结果:

BERT | (4)Bert生成句向量_pytorch_第2张图片

 

 

你可能感兴趣的:(#,BERT,python,bert)