Bert生成句向量(pytorch)

 

本文主要讲如何调用transformers这个包来提取一个句子的特征。

Transformers是TensorFlow 2.0和PyTorch的最新自然语言处理库

Transformers(以前称为pytorch-transformers和pytorch-pretrained-bert)提供用于自然语言理解(NLU)和自然语言生成(NLG)的最先进的模型(BERTGPT-2,RoBERTaXLMDistilBertXLNet,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中才能被正确识别。

text = "[CLS] 今天天气不错,适合出行。 [SEP] 今天是晴天,可以出去玩。 [SEP]"
tokenized_text = tokenizer.tokenize(text) #用tokenizer对句子分词
indexed_tokens = tokenizer.convert_tokens_to_ids(tokenized_text)#词在预训练词表中的索引列表
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]
#用来指定哪个是第一个句子,哪个是第二个句子,0的部分代表句子一, 1的部分代表句子二

#转换成PyTorch tensors
tokens_tensor = torch.tensor([indexed_tokens])
segments_tensors = torch.tensor([segments_ids])

tokens_tensor,segments_tensors将作为BertModel的输入。

输入文本是一个sentence

很多时候输入文本是只有一个句子的,上面两个句子的情况只是简单提一下,下面主要是以一个句子为主。同样,先在句子前面加上’[CLS]’,后面加上’[SEP]’。一般神经网络提取文本特征是以batch为单位的,因此还需要用到一个输入input_masks,假设text是一个batch的数据。

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
#segments列表全0,因为只有一个句子1,没有句子2
#input_masks列表1的部分代表句子单词,而后面0的部分代表paddig,只是用于保持输入整齐,没有实际意义。
#相当于告诉BertModel不要利用后面0的部分
    
#转换成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(BertTextNet, 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

3、完整代码

import torch
from pytorch_transformers import BertModel, BertConfig, BertTokenizer
from torch import nn

# 自己下载模型相关的文件,并指定路径
config_path = 'model/bert/bert_config.json'
model_path = 'model/bert/pytorch_model.bin'
vocab_path = 'model/bert/vocab.txt'


# ——————构造模型——————
class BertTextNet(nn.Module):
    def __init__(self,code_length):
        super(BertTextNet, self).__init__()

        modelConfig = BertConfig.from_pretrained(config_path)
        self.textExtractor = BertModel.from_pretrained(
            model_path, 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


textNet = BertTextNet(code_length=32)

# ——————输入处理——————
tokenizer = BertTokenizer.from_pretrained(vocab_path)

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
# segments列表全0,因为只有一个句子1,没有句子2
# input_masks列表1的部分代表句子单词,而后面0的部分代表paddig,只是用于保持输入整齐,没有实际意义。
# 相当于告诉BertModel不要利用后面0的部分

# 转换成PyTorch tensors
tokens_tensor = torch.tensor(tokens)
segments_tensors = torch.tensor(segments)
input_masks_tensors = torch.tensor(input_masks)

# ——————提取文本特征——————
text_hashCodes = textNet(tokens_tensor, segments_tensors, input_masks_tensors)  # text_hashCodes是一个32-dim文本特征
print(text_hashCodes)

 

  • 9
    点赞
  • 58
    收藏
    觉得还不错? 一键收藏
  • 16
    评论
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值