bert介绍和使用

原文链接:https://blog.csdn.net/weixin_46425692/article/details/108890831?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522163175571916780274115633%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=163175571916780274115633&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2alltop_click~default-3-108890831.pc_search_result_control_group&utm_term=BERT&spm=1018.2226.3001.4187

1.前言

bert是非常出名的预训练模型,它在很少的数据也能有很好的表现。
在我们将要引出bert模型时,先来简单diss其他常见模型的缺点吧!!

  1. diss Word2vec
    bert介绍和使用
    word2vec 不能解决一词多义,也不能解决OOV问题,生成的句子和文档向量也差强人意
  2. diss RNN
    bert介绍和使用
    最出名的缺点是:不能并行,训练速度太慢了
  3. diss CNN
    bert介绍和使用
    虽然可以并行,但太适用于分类任务了,用在其他NLP任务上,效果还不如RNN好
  4. diss seq2seq
    很适合做翻译任务,但encoder和decoder仍然用的RNN,不能并行,所以不能用太深层网络;并且语义向量也只能记住靠近它的一些单词
    bert介绍和使用
  5. diss Attention
    仍然用的RNN结构,不能用多层,否则速度很慢
    bert介绍和使用
    总结:要想改善模型,则不能使用
  • word2vec
  • RNN 及其变种
    所以出现了如下发展路径:
    bert介绍和使用

2. 简单了解Transformer

这里只是简单描述什么是Transformer,至于详细的理解敬请期待 ^^
Transformer 本质仍然是 seq2seq 模型,只不过它叠加了多层的 encoder和decoder
bert介绍和使用
其中每个encoder 和 decoder 结构如下,核心是 self-Attention 机制
bert介绍和使用

3. Bert 模型简介

3.1 bert模型结构

bert介绍和使用
根据这张图:bert训练的两个主要任务是

  • 预测被 [mask] 的单词, 相当于完形填空
  • 预测输入的两个句子是否相邻

这两个任务就对应了两个损失,因此 bert 是最小化这两个损失函数的和来训练模型的。
bert介绍和使用

3.2 bert的输入

bert介绍和使用
bert将输入句子转化为词向量,是经过了3 个Embedding 的加和, 即
input_embed = Token_embed + Sentence_embed + Position_embed.
源码是:
bert介绍和使用
调用模型的话,我们只需要输入:

1) input_ids:一个形状为[batch_size, sequence_length]的 torch.LongTensor,在词汇表中包含单词的token索引, 注意 在句子首尾分别加了 [cls] 和 [sep] 的 索引

2) segment_ids :形状[batch_size, sequence_length]的可选 torch.LongTensor,在0, 1中选择token类型索引。类型0对应于句子A,类型1对应于句子B。如 [0,0,0,0,0,1,1,1,1,1], 0代表第一个句子A, 1代表第二个句子B,默认全为0

3) input_mask:一个可选的 torch.LongTensor,形状为[batch_size, sequence_length],索引在0, 1中选择。0 是 padding 的位置,1是没有padding的字

用其他博客的图:
其中input_ids = seq_ids
segment_ids = token_type_ids
input_mask = mask
bert介绍和使用

3.3 bert的下游任务

bert介绍和使用

  • 加一个线性层就可以分类了

3.4 使用bert提取Embedding向量

可以看到每个Encoder 的隐层输出我们都可以当成是Ebedding, 但哪一层的Embedding更能代表上下文信息呢??bert介绍和使用
第一层效果通常不太好,最后4层隐层输出的拼接效果是最好的
bert介绍和使用

4. Bert学习路径

推荐学习路径:
bert介绍和使用

5.体验bert操作

bert介绍和使用

5.1 准备下载模型

我们需要这3个模型:
bert介绍和使用
vocab.txt 分字表:
bert介绍和使用
贴心的我,已经帮大家整理好了,这里用到是中文bert模型。
bert介绍和使用
链接:https://pan.baidu.com/s/1phZVYv2G-GZXnkWzAhtdeQ
提取码:gjd1
复制这段内容后打开百度网盘手机App,操作更方便哦
下载完之后:再 pip install pytorch_pretrained_bert

5.2 代码

import torch
from pytorch_pretrained_bert import BertTokenizer, BertModel, BertConfig, BertForMaskedLM

import torch.nn as nn

  • 1
  • 2
  • 3
  • 4
# 设置bert模型路径
import os
bert_path = r"D:\Bert\bert-base-chinese"
bert_config_path = os.path.join(bert_path, r"bert_config.json")
bert_vocab_path = os.path.join(bert_path, r"vocab.txt")
bert_model_path = os.path.join(bert_path, r"pytorch_model.bin")

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
# 加载模型配置
bert_config = BertConfig.from_json_file(bert_config_path)

# 加载中文词库
bert_vocab = BertTokenizer(vocab_file= bert_vocab_path)

# 加载模型
bert_model = BertModel.from_pretrained(bert_path) # 这个函数的输入要求a path or url to a pretrained model archive containing:
#. bert_config.json a configuration file for the model
#. pytorch_model.bin a PyTorch dump of a BertForPreTraining instance

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

bert介绍和使用

#  Tokenize input
text = "乌兹别克斯坦议会立法院主席获连任"
# 利用bert自带的分字工具分字
tokenized_text = bert_vocab.tokenize(text)   # list
#print("type of tokenized:", type(tokenized_text))

# 在句子首尾分别加上[CLS],[SEP]. [CLS] 只有一个,而[SEP]在每句话的末尾,当输入两个句子时,就有两个[SEP]
tokenized_text = ["[CLS]"] + tokenized_text + ["[SEP]"]

#将 字 转化为词库中的索引
input_ids = bert_vocab.convert_tokens_to_ids(tokenized_text) # list, len:18
#print(“type of input_ids :”, type(input_ids), len(input_ids))
## input_ids.shape : [batch, sequence_lenth]

# Define sentence A and B indices ,由于输入只有一个句子,那么就全定义为0
segment_ids = [0] * len(input_ids)

# input_mask,由于没有填充padding, 就全定义为1
input_mask = [1] * len(input_ids)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
# Bert 的输入 只支持 长整型,因此必须转化为 torch.long tensor 形式
input_ids = torch.LongTensor(input_ids)
print("input_ids: ", input_ids.size())  # input_ids:  torch.Size([18])

segment_ids = torch.tensor([segment_ids], dtype = torch.long)

input_mask = torch.tensor([input_mask], dtype = torch.long)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
# bert 模型调用
all_encoder_layer, pooled_output = bert_model(input_ids, input_mask,segment_ids)
## all_encoder_layers: 一个包含 12 个 transfomer encoder 层输出的列表list
### 每一层的输出大小是: [batch, seq_lenth, hiddern_size], 对于bert-base 是12, bert-large是24
print("all_encoder_layer:\n ", type(all_encoder_layer), len(all_encoder_layer), all_encoder_layer[0].size())

## pooled_output: 最后一个 transfomer encoder ,且输入句子的第一个字[CLS]位置上的隐层输出
### 大小:[batch, hidden_size]
### 这个输出就可以看做是输入句子的语义信息了
print(“pooled_output: \n”, type(pooled_output), len(pooled_output), pooled_output.size())

######################################
all_encoder_layer:
<class ‘list’> 12 torch.Size([1, 18, 768])
pooled_output:
<class ‘torch.Tensor’> 1 torch.Size([1, 768])

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  1. 如果我们想要得到输入句子的 embedding 表示,那么我们只要 all_encoder_layer 的最后几层向量

  2. 如果我们想要做分类,那么只要Pooled_output 向量,因为它代表了整个输入句子的语义信息

考虑padding时,代码有什么变化??

为什么要padding呢,因为我们训练时候是用batch_size样本的,但是每个样本即句子的长度都不一致,因此我们必须设置一个最大句子的长度,当其他句子小于此长度时就padding,大于此长度就截断

# padding
# 设置最长句子长度
max_seq_lenth = 300

new_text = “乌兹别克斯坦议会立法院主席获连任”
tokenized_text = bert_vocab.tokenize(new_text) # list
tokenized_text = ["[CLS]"] + tokenized_text + ["[SEP]"] # list
input_ids = bert_vocab.convert_tokens_to_ids(tokenized_text) # list
input_mask = [1] * len(input_ids)

# define padding, 填充的地方置为0,未填充的地方是 1
padding = [0] * (max_seq_lenth - len(input_ids))

input_ids += padding # 300
input_mask += padding

# input_ids 后面填充部分都要置0

# 这里只有一个输入句子,因此可以不用segment_ids,因为默认就有全0的segment_ids

#转化为 torch.LongTensor 形式
input_ids = torch.tensor([input_ids], dtype = torch.long) #300
input_mask = torch.tensor([input_mask], dtype = torch.long) # 300
print("padding intput_ids: ", input_ids.size()) # 300

## bert_model 的测试模式,不用梯度更新
bert_model.eval()
with torch.no_grad():
all_encoder_layer, pooled_output = bert_model(input_ids, attention_mask= input_mask)
print("padding all_encoder_layers: ", all_encoder_layer[0].shape)
print("padding pooled_output : ", pooled_output.size())

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

bert介绍和使用
bert介绍和使用

上一篇:前端向后端传数组的三种方式(第一种简洁,第二种更简洁-推荐,第三种较繁琐)


下一篇:python测试开发django-126.bootstrap-table表格内操作按钮(修改/删除) 功能实现