05阶段:NLP自然语言处理基础(2)

五.迁移学习【偏应用,写代码】

讲解fasttext工具的概念和原理;安装fasttext工具包;分析fastext模型的原理;讲解层次softmax方法的原理;讲解负采样方法的原理;基于fasttext实现词向量的训练;基于fasttext实现文本分类;基于fasttext实现词向量的迁移;讲解迁移学习有关概念;理解什么是预训练模型(Pretrained model);

掌握预训练模型的应用方式微调(Fine-tuning);掌握transformers库的三种使用方式

1 fasttext工具介绍

学习目标

  • 了解fasttext工具的作用.
  • 了解fasttext工具的优势及其原因.
  • 掌握fasttext的安装方法.

1 fasttext介绍

1.1 fasttext作用

作为NLP工程领域常用的工具包, fasttext有两大作用:

  • 进行文本分类
  • 训练词向量

1.2 fasttext工具包的优势

  • 正如它的名字, 在保持较高精度的情况下, 快速的进行训练和预测是fasttext的最大优势.

  • fasttext优势的原因:

  • fasttext工具包中内含的fasttext模型具有十分简单的网络结构.

  • 使用fasttext模型训练词向量时使用层次softmax结构, 来提升超多类别下的模型性能.

  • 由于fasttext模型过于简单无法捕捉词序特征, 因此会进行n-gram特征提取以弥补模型缺陷提升精度.

1.3 fasttext的安装

复制代码
pip install fasttext

1.4 验证安装

复制代码
Python 3.8.12 (default) 
[GCC 7.5.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import fasttext
>>>

2 fasttext模型架构

学习目标

  • 了解fasttext的模型架构.
  • 了解fasttext模型中层次化的softmax.
  • 了解负采样.

1 Fasttext模型架构

FastText 模型架构和 Word2Vec 中的 CBOW 模型很类似, 不同之处在于, FastText 预测标签, 而 CBOW 模型预测中间词.

FastText的模型分为三层架构:

  • 输入层: 是对文档embedding之后的向量, 包含N-gram特征
  • 隐藏层: 是对输入数据的求和平均
  • 输出层: 是文档对应的label

2 层次softmax(hierarchical softmax)

  • 为了提高效率, 在fastText中计算分类标签概率的时候, 不再使用传统的softmax来进行多分类的计算, 而是使用哈夫曼树, 使用层次化的softmax来进行概率的计算.

2.1 哈夫曼树

  • 概念: 当利用n 个结点试图构建一棵树时, 如果构建的这棵树的带权路径长度最小, 称这棵树为"最优二叉树", 有时也叫"赫夫曼树"或者"哈夫曼树".
  • 特点: 权值越大的节点距离根节点也较近.

2.2 哈夫曼树相关概念

  • 二叉树: 每个节点最多有2个子树的有序树, 两个子树分别称为左子树、右子树. 有序的意思是: 树有左右之分, 不能颠倒.
  • 叶子节点: 一棵树当中没有子节点的节点称为叶子节点.
  • 路径和路径长度: 在一棵树中, 从一个节点往下可以到达孩子或孙子节点之间的通路, 称为路径. 通路中分支的数目称为路径长度.
  • 节点的权及带权路径长度: 若将树中节点赋予一个有某种含义的数值, 则这个数值称为该节点的权, 节点的带权路径长度为: 从根节点到该节点之间的路径长度与该节点的权的乘积.
  • 树的带权路径长度: 树的带权路径长度规定为所有叶子节点的带权路径长度之和, 记为WPL(weighted path length). WPL最小的二叉树就是赫夫曼树

2.3 构建哈夫曼树

假设有n个权值, 则构造出的哈夫曼树有n个叶子节点. n个权值分别设为 w1、w2、...、wn, 则哈夫曼树的构造规则为:

  • 步骤1: 将w1、w2、..., wn看成是有n 棵树的森林(每棵树仅有一个节点);
  • 步骤2: 在森林中选出两个根节点的权值最小的树合并, 作为一颗新树的左、右子树, 且新树的根节点权值为其左、右子树根节点权值之和;
  • 步骤3: 从森林中删除选取的两棵树, 并将新树加入森林;
  • 步骤4: 重复2-3步骤, 直到森林只有一颗树为止, 该树就是所求的哈夫曼树.

举例说明, 构建huffman树:

  • 假设有四个Label分别为: A~D, 统计其在语料库出现的频数:
  • 第一次合并建树:
  • 第二次合并建树:
  • 第三次合并建树:
  • 由上图可以看出权重越大, 距离根节点越近.
  • 叶子的个数为n, 构造哈夫曼树中新增的节点的个数为n-1.

2.4 哈夫曼树编码

  • 哈夫曼编码一般规定哈夫曼树中的左分支为 0, 右分支为 1, 从根节点到每个叶节点所经过的分支对应的 0 和 1 组成的序列便为该节点对应字符的编码. 这样的编码称为哈夫曼编码.
  • 上图例子中对应的编码如下:

2.5 转化为梯度计算

上图中, 红色为哈夫曼编码, 即D的哈夫曼编码为110, 那么此时如何定义条件概率P(D|context)P(D|context)​?

以D为例, 从根节点到D中间经历了3次分支, 每次分支都可以认为是进行了一次2分类, 根据哈夫曼编码, 可以把数字0对应分支认为是负类, 数字1对应的分支认为是正类.

在机器学习课程中逻辑回归中使用sigmoid函数进行2分类的过程中: 一个节点被分为正类的概率是: σ(XTθ)=1/(1+e−xTθ)σ(XTθ)=1/(1+e−xTθ), 一个节点被分为负类的概率是: 1−σ(XTθ)1−σ(XTθ), 其中θθ 就是图中非叶子节点对应的参数.

对于从根节点出发, 到达D一共经历三次分支, 将每次分类结果的概率罗列出来:

  • 第一次: P(1|X,θ1)=σ(XTθ1)P(1|X,θ1)=σ(XTθ1), 即从根节点到24节点的概率是在知道xx和θ1θ1的情况下取值为1的概率
  • 第二次: P(1|X,θ2)=σ(XTθ2)P(1|X,θ2)=σ(XTθ2)
  • 第三次: P(0|X,θ3)=1−σ(XTθ3)P(0|X,θ3)=1−σ(XTθ3)

但是我们需要求的是P(D|context)P(D|context), 它等于前3词的概率乘积, 公式如下(dwjdjw是第jj​个节点的哈夫曼编码)

在机器学习中的逻辑回归中, 我们会经常把二分类的损失函数定义为对数似然损失, 即

式子中, 求和符号表示的是使用样本的过程中, 每个label对应的概率取对数后的和, 之后求取均值.

带入前面P(Label|Context)P(Label|Context)的定义得到损失函数:

有了损失函数之后, 接下来就是对其中的X,θX,θ​进行求导, 并更新.

2.6 层次softmax的优势

  • 传统的softmax的时间复杂度为L(labels的数量), 但是使用层次化softmax之后时间复杂度的log(L) (二叉树的高度和宽度的近似), 从而在多分类的场景提高了效率.

3 负采样(negative sampling)

3.1 负采样原理

  • 当我们训练一个神经网络意味着要输入训练样本并且不断调整神经元的权重, 从而不断提高对目标的准确预测. 每当神经网络经过一个训练样本的训练, 它的权重就会进行一次调整. 比如我们利用Skip-Gram进行词向量的训练, 如果词汇量的数量为上万个, 那么我们利用softmax计算概率时, 需要对计算上万个概率值, 且每个值都需要进行反向传播更新模型参数, 这是非常消耗计算资源的, 并且实际中训练起来会非常慢.
  • 不同于原本每个训练样本更新所有的权重, 负采样每次让一个训练样本仅仅更新一小部分的权重, 这样就会降低梯度下降过程中的计算量.
  • 举例说明(负采样原理):
  • 当我们用训练样本 ( input word: "hello", output word: "man") 来训练我们的神经网络时, " hello"和"man"都是经过one-hot编码的. 如果我们的vocabulary大小为10000时, 在输出层, 我们期望对应"man"单词的那个神经元结点输出1, 其余9999个都应该输出0. 在这里, 这9999个我们期望输出为0的神经元结点所对应的单词我们称为"negative" word.
  • 当使用负采样时, 我们将随机选择一小部分的negative words(比如选5个negative words)来更新对应的权重. 我们也会对我们的"positive" word进行权重更新(在我们上面的例子中, 这个单词指的是"man").
  • 注意, 对于小规模数据集, 选择5-20个negative words会比较好, 对于大规模数据集可以仅选择2-5个negative words.
  • 假如我们的隐层-输出层拥有300 x 10000的权重矩阵. 如果使用了负采样的方法我们仅仅去更新我们的positive word-"man"的和我们选择的其他5个negative words的结点对应的权重, 共计6个输出神经元, 相当于每次只更新300×6=1800个权重. 对于3百万的权重来说, 相当于只计算了0.06%的权重, 这样计算效率就大幅度提高.

3.2 负采样的优势

  • 提高训练速度, 选择了部分数据进行计算损失, 损失计算更加简单.
  • 改进效果, 增加部分负样本, 能够模拟真实场景下的噪声情况, 能够让模型的稳健性更强.

4 小结

  • 学习了解什么是fasttext模型架构:
  • fastText 模型架构和 Word2Vec 中的 CBOW 模型很类似, 不同之处在于, fastText 预测标签, 而 CBOW 模型预测中间词.
  • 学习了层次softmax:
  • 为了提高效率, 在fastText中计算分类标签概率的时候, 不再使用传统的softmax来进行多分类的计算, 而是使用哈夫曼树, 使用层次化的softmax来进行概率的计算.
  • 哈夫曼树定义:
    • 当利用n 个结点试图构建一棵树时, 如果构建的这棵树的带权路径长度最小, 称这棵树为"最优二叉树", 有时也叫"赫夫曼树"或者"哈夫曼树".
  • 优点:
    • 传统的softmax的时间复杂度为L(labels的数量), 但是使用层次化softmax之后时间复杂度的log(L) (二叉树的高度和宽度的近似), 从而在多分类的场景提高了效率.
  • 学习了负采样:
  • 负采样原理:
    • 负采样每次让一个训练样本仅仅更新一小部分的权重, 这样就会降低梯度下降过程中的计算量.
  • 优点:
    • 提高训练速度, 选择了部分数据进行计算损失, 损失计算更加简单.
    • 改进效果, 增加部分负样本, 能够模拟真实场景下的噪声情况, 能够让模型的稳健性更强.

3 fasttext文本分类

学习目标

  • 了解什么是文本分类及其种类.
  • 掌握fasttext工具进行文本分类的过程.

1 文本分类介绍

1.1 文本分类概念

  • 文本分类的是将文档(例如电子邮件,帖子,文本消息,产品评论等)分配给一个或多个类别. 当今文本分类的实现多是使用机器学习方法从训练数据中提取分类规则以进行分类, 因此构建文本分类器需要带标签的数据.

1.2 文本分类种类

  • 二分类:
    • 文本被分类两个类别中, 往往这两个类别是对立面, 比如: 判断一句评论是好评还是差评.
  • 单标签多分类:
    • 文本被分入到多个类别中, 且每条文本只能属于某一个类别(即被打上某一个标签), 比如: 输入一个人名, 判断它是来自哪个国家的人名.
  • 多标签多分类:
    • 文本被分人到多个类别中, 但每条文本可以属于多个类别(即被打上多个标签), 比如: 输入一段描述, 判断可能是和哪些兴趣爱好有关, 一段描述中可能即讨论了美食, 又太讨论了游戏爱好.

2 文本分类的过程

  • 第一步: 获取数据
  • 第二步: 训练集与验证集的划分
  • 第三步: 训练模型
  • 第四步: 使用模型进行预测并评估
  • 第五步: 模型调优
  • 第六步: 模型保存与重加载

2.1 获取数据

数据集介绍,本案例烹饪相关的数据集, 它是由facebook AI实验室提供的演示数据集

复制代码
# 数据集在虚拟机/root/data/cooking下

# 查看数据的前10条
$ head cooking.stackexchange.txt

__label__sauce __label__cheese How much does potato starch affect a cheese sauce recipe?
__label__food-safety __label__acidity Dangerous pathogens capable of growing in acidic environments
__label__cast-iron __label__stove How do I cover up the white spots on my cast iron stove?
__label__restaurant Michelin Three Star Restaurant; but if the chef is not there
__label__knife-skills __label__dicing Without knife skills, how can I quickly and accurately dice vegetables?
__label__storage-method __label__equipment __label__bread What's the purpose of a bread box?
__label__baking __label__food-safety __label__substitutions __label__peanuts how to seperate peanut oil from roasted peanuts at home?
__label__chocolate American equivalent for British chocolate terms
__label__baking __label__oven __label__convection Fan bake vs bake
__label__sauce __label__storage-lifetime __label__acidity __label__mayonnaise Regulation and balancing of readymade packed mayonnaise and other sauces
  • 数据说明:
  • cooking.stackexchange.txt中的每一行都包含一个标签列表,后跟相应的文档, 标签列表以类似"__label__sauce __label__cheese"的形式展现, 代表有两个标签sauce和cheese, 所有标签__label__均以前缀开头,这是fastText识别标签或单词的方式. 标签之后的一段话就是文本信息.如: How much does potato starch affect a cheese sauce recipe?

2.2 训练集与验证集的划分

复制代码
# 查看数据总数
$ wc cooking.stackexchange.txt 

15404  169582 1401900 cooking.stackexchange.txt 
# 多少行 多少单词 占用字节数(多大)

# 12404条数据作为训练数据
$ head -n 12404 cooking.stackexchange.txt > cooking.train
# 3000条数据作为验证数据
$ tail -n 3000 cooking.stackexchange.txt > cooking.valid

2.3 训练模型

复制代码
# 导入fasttext
import fasttext
# 使用fasttext的train_supervised方法进行文本分类模型的训练
model = fasttext.train_supervised(input="data/cooking/cooking.train")
  • 结果输出
复制代码
# 获得结果
Read 0M words
# 不重复的词汇总数
Number of words:  14543
# 标签总数
Number of labels: 735
# Progress: 训练进度, 因为我们这里显示的是最后的训练完成信息, 所以进度是100%
# words/sec/thread: 每个线程每秒处理的平均词汇数
# lr: 当前的学习率, 因为训练完成所以学习率是0
# avg.loss: 训练过程的平均损失 
# ETA: 预计剩余训练时间, 因为已训练完成所以是0
Progress: 100.0% words/sec/thread:   60162 lr:  0.000000 avg.loss: 10.056812 ETA:   0h 0m 0s

2.4 使用模型进行预测并评估

复制代码
# 使用模型预测一段输入文本, 通过我们常识, 可知预测是正确的, 但是对应预测概率并不大
>>> model.predict("Which baking dish is best to bake a banana bread ?")
# 元组中的第一项代表标签, 第二项代表对应的概率
(('__label__baking',), array([0.06550845]))

# 通过我们常识可知预测是错误的
>>> model.predict("Why not put knives in the dishwasher?")
(('__label__food-safety',), array([0.07541209]))

# 为了评估模型到底表现如何, 我们在3000条的验证集上进行测试
>>> model.test("data/cooking/cooking.valid")
# 元组中的每项分别代表, 验证集样本数量, 精度以及召回率 
# 我们看到模型精度和召回率表现都很差, 接下来我们讲学习如何进行优化.
(3000, 0.124, 0.0541)

2.5 模型调优

1 原始数据处理:
复制代码
# 通过查看数据, 我们发现数据中存在许多标点符号与单词相连以及大小写不统一, 
# 这些因素对我们最终的分类目标没有益处, 反是增加了模型提取分类规律的难度,
# 因此我们选择将它们去除或转化

# 处理前的部分数据
__label__fish Arctic char available in North-America
__label__pasta __label__salt __label__boiling When cooking pasta in salted water how much of the salt is absorbed?
__label__coffee Emergency Coffee via Chocolate Covered Coffee Beans?
__label__cake Non-beet alternatives to standard red food dye
__label__cheese __label__lentils Could cheese "halt" the tenderness of cooking lentils?
__label__asian-cuisine __label__chili-peppers __label__kimchi __label__korean-cuisine What kind of peppers are used in Gochugaru ()?
__label__consistency Pavlova Roll failure
__label__eggs __label__bread What qualities should I be looking for when making the best French Toast?
__label__meat __label__flour __label__stews __label__braising Coating meat in flour before browning, bad idea?
__label__food-safety Raw roast beef on the edge of safe?
__label__pork __label__food-identification How do I determine the cut of a pork steak prior to purchasing it?

# 通过服务器终端进行简单的数据预处理
# 使标点符号与单词分离并统一使用小写字母
>> cat cooking.stackexchange.txt | sed -e "s/\([.\!?,'/()]\)/ \1 /g" | tr "[:upper:]" "[:lower:]" > cooking.preprocessed.txt
>> head -n 12404 cooking.preprocessed.txt > cooking.pre.train
>> tail -n 3000 cooking.preprocessed.txt > cooking.pre.valid

# 处理后的部分数据
__label__fish arctic char available in north-america
__label__pasta __label__salt __label__boiling when cooking pasta in salted water how much of the salt is absorbed ?
__label__coffee emergency coffee via chocolate covered coffee beans ?
__label__cake non-beet alternatives to standard red food dye
__label__cheese __label__lentils could cheese "halt" the tenderness of cooking lentils ?
__label__asian-cuisine __label__chili-peppers __label__kimchi __label__korean-cuisine what kind of peppers are used in gochugaru  (  )  ?
__label__consistency pavlova roll failure
__label__eggs __label__bread what qualities should i be looking for when making the best french toast ?
__label__meat __label__flour __label__stews __label__braising coating meat in flour before browning ,  bad idea ?
__label__food-safety raw roast beef on the edge of safe ?
__label__pork __label__food-identification how do i determine the cut of a pork steak prior to purchasing it ?
2 数据处理后进行训练并测试:
复制代码
# 重新训练
>>> model = fasttext.train_supervised(input="data/cooking/cooking.pre.train")
Read 0M words

# 不重复的词汇总数减少很多, 因为之前会把带大写字母或者与标点符号相连接的单词都认为是新的单词
Number of words:  8952
Number of labels: 735

# 我们看到平均损失有所下降
Progress: 100.0% words/sec/thread:   65737 lr:  0.000000 avg.loss:  9.966091 ETA:   0h 0m 0s

# 重新测试
>>> model.test("data/cooking/cooking.pre.valid")
# 我们看到精度和召回率都有所提升
(3000, 0.161, 0.06962663975782038)
3 增加训练轮数:
复制代码
# 设置train_supervised方法中的参数epoch来增加训练轮数, 默认的轮数是5次
# 增加轮数意味着模型能够有更多机会在有限数据中调整分类规律, 当然这也会增加训练时间
>>> model = fasttext.train_supervised(input="data/cooking/cooking.pre.train", epoch=25)
Read 0M words
Number of words:  8952
Number of labels: 735

# 我们看到平均损失继续下降
Progress: 100.0% words/sec/thread:   66283 lr:  0.000000 avg.loss:  7.203885 ETA:   0h 0m 0s

>>> model.test("data/cooking/cooking.pre.valid")
# 我们看到精度已经提升到了42%, 召回率提升至18%.
(3000, 0.4206666666666667, 0.1819230214790255)
4 调整学习率:
复制代码
# 设置train_supervised方法中的参数lr来调整学习率, 默认的学习率大小是0.1
# 增大学习率意味着增大了梯度下降的步长使其在有限的迭代步骤下更接近最优点
>>> model = fasttext.train_supervised(input="data/cooking/cooking.pre.train", lr=1.0, epoch=25)
Read 0M words
Number of words:  8952
Number of labels: 735

# 平均损失继续下降
Progress: 100.0% words/sec/thread:   66027 lr:  0.000000 avg.loss:  4.278283 ETA:   0h 0m 0s

>>> model.test("data/cooking/cooking.pre.valid")
# 我们看到精度已经提升到了47%, 召回率提升至20%.
(3000, 0.47633333333333333, 0.20599682860025947)
5 增加n-gram特征:
复制代码
# 设置train_supervised方法中的参数wordNgrams来添加n-gram特征, 默认是1, 也就是没有n-gram特征
# 我们这里将其设置为2意味着添加2-gram特征, 这些特征帮助模型捕捉前后词汇之间的关联, 更好的提取分类规则用于模型分类, 当然这也会增加模型训时练占用的资源和时间.
>>> model = fasttext.train_supervised(input="data/cooking/cooking.pre.train", lr=1.0, epoch=25, wordNgrams=2)
Read 0M words
Number of words:  8952
Number of labels: 735

# 平均损失继续下降
Progress: 100.0% words/sec/thread:   65084 lr:  0.000000 avg.loss:  3.189422 ETA:   0h 0m 0s

>>> model.test("data/cooking/cooking.pre.valid")
# 我们看到精度已经提升到了49%, 召回率提升至21%.
(3000, 0.49233333333333335, 0.2129162462159435)
6 修改损失计算方式:
复制代码
# 随着我们不断的添加优化策略, 模型训练速度也越来越慢
# 为了能够提升fasttext模型的训练效率, 减小训练时间
# 设置train_supervised方法中的参数loss来修改损失计算方式(等效于输出层的结构), 默认是softmax层结构
# 我们这里将其设置为'hs', 代表层次softmax结构, 意味着输出层的结构(计算方式)发生了变化, 将以一种更低复杂度的方式来计算损失.
>>> model = fasttext.train_supervised(input="data/cooking/cooking.pre.train", lr=1.0, epoch=25, wordNgrams=2, loss='hs')
Read 0M words
Number of words:  8952
Number of labels: 735
Progress: 100.0% words/sec/thread: 1341740 lr:  0.000000 avg.loss:  2.225962 ETA:   0h 0m 0s
>>> model.test("data/cooking/cooking.pre.valid")
# 我们看到精度和召回率稍有波动, 但训练时间却缩短到仅仅几秒
(3000, 0.483, 0.20887991927346114)
7 自动超参数调优:
复制代码
# 手动调节和寻找超参数是非常困难的, 因为参数之间可能相关, 并且不同数据集需要的超参数也不同, 
# 因此可以使用fasttext的autotuneValidationFile参数进行自动超参数调优.
# autotuneValidationFile参数需要指定验证数据集所在路径, 它将在验证集上使用随机搜索方法寻找可能最优的超参数.
# 使用autotuneDuration参数可以控制随机搜索的时间, 默认是300s, 根据不同的需求, 我们可以延长或缩短时间.
# 验证集路径'cooking.valid', 随机搜索600秒
>>> model = fasttext.train_supervised(input='data/cooking/cooking.pre.train', autotuneValidationFile='data/cooking/cooking.pre.valid', autotuneDuration=600)

Progress: 100.0% Trials:   38 Best score:  0.376170 ETA:   0h 0m 0s
Training again with best arguments
Read 0M words
Number of words:  8952
Number of labels: 735
Progress: 100.0% words/sec/thread:   63791 lr:  0.000000 avg.loss:  1.888165 ETA:   0h 0m 0s
8 实际生产中多标签多分类问题的损失计算方式
复制代码
# 针对多标签多分类问题, 使用'softmax'或者'hs'有时并不是最佳选择, 因为我们最终得到的应该是多个标签, 而softmax却只能最大化一个标签. 
# 所以我们往往会选择为每个标签使用独立的二分类器作为输出层结构, 
# 对应的损失计算方式为'ova'表示one vs all.
# 这种输出层的改变意味着我们在统一语料下同时训练多个二分类模型,
# 对于二分类模型来讲, lr不宜过大, 这里我们设置为0.2
>>> model = fasttext.train_supervised(input="data/cooking/cooking.pre.train", lr=0.2, epoch=25, wordNgrams=2, loss='ova')
Read 0M words
Number of words:  8952
Number of labels: 735
Progress: 100.0% words/sec/thread:   65044 lr:  0.000000 avg.loss:  7.713312 ETA:   0h 0m 0s 

# 我们使用模型进行单条样本的预测, 来看一下它的输出结果.
# 参数k代表指定模型输出多少个标签, 默认为1, 这里设置为-1, 意味着尽可能多的输出.
# 参数threshold代表显示的标签概率阈值, 设置为0.5, 意味着显示概率大于0.5的标签
>>> model.predict("Which baking dish is best to bake a banana bread ?", k=-1, threshold=0.5)

# 我看到根据输入文本, 输出了它的三个最有可能的标签
((u'__label__baking', u'__label__bananas', u'__label__bread'), array([1.00000, 0.939923, 0.592677]))

2.6 模型保存与重加载

复制代码
# 使用model的save_model方法保存模型到指定目录
# 你可以在指定目录下找到model_cooking.bin文件
>>> model.save_model("data/model/model_cooking.bin")

# 使用fasttext的load_model进行模型的重加载
>>> model = fasttext.load_model("data/model/model_cooking.bin")

# 重加载后的模型使用方法和之前完全相同
>>> model.predict("Which baking dish is best to bake a banana bread ?", k=-1, threshold=0.5)
((u'__label__baking', u'__label__bananas', u'__label__bread'), array([1.00000, 0.939923, 0.592677]))

3 小结

  • 学习了什么是文本分类:

    • 文本分类的是将文档(例如电子邮件,帖子,文本消息,产品评论等)分配给一个或多个类别. 当今文本分类的实现多是使用机器学习方法从训练数据中提取分类规则以进行分类, 因此构建文本分类器需要带标签的数据.
  • 文本分类的种类:

    • 二分类:
      • 文本被分类两个类别中, 往往这两个类别是对立面, 比如: 判断一句评论是好评还是差评.
    • 单标签多分类:
      • 文本被分入到多个类别中, 且每条文本只能属于某一个类别(即被打上某一个标签), 比如: 输入一个人名, 判断它是来自哪个国家的人名.
    • 多标签多分类:
      • 文本被分人到多个类别中, 但每条文本可以属于多个类别(即被打上多个标签), 比如: 输入一段描述, 判断可能是和哪些兴趣爱好有关, 一段描述中可能即讨论了美食, 又太讨论了游戏爱好.
  • 使用fasttext工具进行文本分类的过程:

    • 第一步: 获取数据
    • 第二步: 训练集与验证集的划分
    • 第三步: 训练模型
    • 第四步: 使用模型进行预测并评估
    • 第五步: 模型调优
    • 第六步: 模型保存与重加载

4 训练词向量

学习目标

  • 了解词向量的相关知识.
  • 掌握fasttext工具训练词向量的过程.

1 训练词向量介绍

1.1 词向量的相关知识:

  • 用向量表示文本中的词汇(或字符)是现代机器学习中最流行的做法, 这些向量能够很好的捕捉语言之间的关系, 从而提升基于词向量的各种NLP任务的效果.

1.2 练词向量的过程

  • 第一步: 获取数据
  • 第二步: 训练词向量
  • 第三步: 模型超参数设定
  • 第四步: 模型效果检验
  • 第五步: 模型的保存与重加载

2 实现步骤

2.1 数据介绍

数据集仍然使用:英语维基百科的部分网页信息

注意:原始数据集已经放在/root/data/enwik9.zip,解压后数据为/root/data/enwik9,预处理后的数据为/root/data/fil9

  • 查看预处理后的数据:

    查看前80个字符

    head -c 80 data/fil9

    输出结果为由空格分割的单词

    anarchism originated as a term of abuse first used against early working class

2.2 训练词向量

复制代码
# 代码运行在python解释器中
# 导入fasttext
>>> import fasttext
# 使用fasttext的train_unsupervised(无监督训练方法)进行词向量的训练
# 它的参数是数据集的持久化文件路径'data/fil9'
# 注意,该行代码执行耗时很长
>>> model1 = fasttext.train_unsupervised('data/fil9') 

# 可以使用以下代码加载已经训练好的模型
>>> model = fasttext.load_model("data/fil9.bin")

# 有效训练词汇量为124M, 共218316个单词
Read 124M words
Number of words:  218316
Number of labels: 0
Progress: 100.0% words/sec/thread:   53996 lr:  0.000000 loss:  0.734999 ETA:   0h 0m

查看单词对应的词向量:

复制代码
# 通过get_word_vector方法来获得指定词汇的词向量
>>> model.get_word_vector("the")

array([-0.03087516,  0.09221972,  0.17660329,  0.17308897,  0.12863874,
        0.13912526, -0.09851588,  0.00739991,  0.37038437, -0.00845221,
        ...
       -0.21184735, -0.05048715, -0.34571868,  0.23765688,  0.23726143],
      dtype=float32)

2.3 模型超参数设定

复制代码
# 在训练词向量过程中, 我们可以设定很多常用超参数来调节我们的模型效果, 如:
# 无监督训练模式: 'skipgram' 或者 'cbow', 默认为'skipgram', 在实践中,skipgram模式在利用子词方面比cbow更好.
# 词嵌入维度dim: 默认为100, 但随着语料库的增大, 词嵌入的维度往往也要更大.
# 数据循环次数epoch: 默认为5, 但当你的数据集足够大, 可能不需要那么多次.
# 学习率lr: 默认为0.05, 根据经验, 建议选择[0.01,1]范围内.
# 使用的线程数thread: 默认为12个线程, 一般建议和你的cpu核数相同.

>>> model = fasttext.train_unsupervised('data/fil9', "cbow", dim=300, epoch=1, lr=0.1, thread=8)

Read 124M words
Number of words:  218316
Number of labels: 0
Progress: 100.0% words/sec/thread:   49523 lr:  0.000000 avg.loss:  1.777205 ETA:   0h 0m 0s

2.4 模型效果检验

复制代码
# 检查单词向量质量的一种简单方法就是查看其邻近单词, 通过我们主观来判断这些邻近单词是否与目标单词相关来粗略评定模型效果好坏.

# 查找"运动"的邻近单词, 我们可以发现"体育网", "运动汽车", "运动服"等. 
>>> model.get_nearest_neighbors('sports')

[(0.8414610624313354, 'sportsnet'), (0.8134572505950928, 'sport'), (0.8100415468215942, 'sportscars'), (0.8021156787872314, 'sportsground'), (0.7889881134033203, 'sportswomen'), (0.7863013744354248, 'sportsplex'), (0.7786710262298584, 'sporty'), (0.7696356177330017, 'sportscar'), (0.7619683146476746, 'sportswear'), (0.7600985765457153, 'sportin')]


# 查找"音乐"的邻近单词, 我们可以发现与音乐有关的词汇.
>>> model.get_nearest_neighbors('music')

[(0.8908010125160217, 'emusic'), (0.8464668393135071, 'musicmoz'), (0.8444250822067261, 'musics'), (0.8113634586334229, 'allmusic'), (0.8106718063354492, 'musices'), (0.8049437999725342, 'musicam'), (0.8004694581031799, 'musicom'), (0.7952923774719238, 'muchmusic'), (0.7852965593338013, 'musicweb'), (0.7767147421836853, 'musico')]

# 查找"小狗"的邻近单词, 我们可以发现与小狗有关的词汇.
>>> model.get_nearest_neighbors('dog')

[(0.8456876873970032, 'catdog'), (0.7480780482292175, 'dogcow'), (0.7289096117019653, 'sleddog'), (0.7269964218139648, 'hotdog'), (0.7114801406860352, 'sheepdog'), (0.6947550773620605, 'dogo'), (0.6897546648979187, 'bodog'), (0.6621081829071045, 'maddog'), (0.6605004072189331, 'dogs'), (0.6398137211799622, 'dogpile')]

2.5 模型的保存与重加载

复制代码
# 使用save_model保存模型
>>> model.save_model("data/fil91.bin")

# 使用fasttext.load_model加载模型
>>> model = fasttext.load_model("data/fil91.bin")
>>> model.get_word_vector("the")

array([-0.03087516,  0.09221972,  0.17660329,  0.17308897,  0.12863874,
        0.13912526, -0.09851588,  0.00739991,  0.37038437, -0.00845221,
        ...
       -0.21184735, -0.05048715, -0.34571868,  0.23765688,  0.23726143],
      dtype=float32)

3 小结

  • 学习了词向量的相关知识:

  • 用向量表示文本中的词汇(或字符)是现代机器学习中最流行的做法, 这些向量能够很好的捕捉语言之间的关系, 从而提升基于词向量的各种NLP任务的效果.

  • 使用fasttext工具训练词向量的过程:

  • 第一步: 获取数据

  • 第二步: 训练词向量

  • 第三步: 模型超参数设定

  • 第四步: 模型效果检验

  • 第五步: 模型的保存与重加载


5 词向量迁移

学习目标

  • 了解什么是词向量迁移.
  • 了解fasttext工具中有哪些可迁移的词向量模型.
  • 掌握如何使用fasttext进行词向量模型迁移.

1 词向量迁移介绍

  • 使用在大型语料库上已经进行训练完成的词向量模型.

  • fasttext工具中可以提供的可迁移的词向量:

    • fasttext提供了157种语言的在CommonCrawl和Wikipedia语料上进行训练的可迁移词向量模型, 它们采用CBOW模式进行训练, 词向量维度为300维. 可通过该地址查看具体语言词向量模型: Word vectors for 157 languages · fastText
    • fasttext提供了294种语言的在Wikipedia语料上进行训练的可迁移词向量模型, 它们采用skipgram模式进行训练, 词向量维度同样是300维. 可通过该地址查看具体语言词向量模型: Wiki word vectors · fastText

2 使用fasttext进行词向量迁移[¶](#2 使用fasttext进行词向量迁移¶)

  • 第一步: 下载词向量模型压缩的bin.gz文件
  • 第二步: 解压bin.gz文件到bin文件
  • 第三步: 加载bin文件获取词向量
  • 第四步: 利用邻近词进行效果检验

2.1 下载词向量模型压缩的bin.gz文件[¶](#2.1 下载词向量模型压缩的bin.gz文件¶)

复制代码
# 这里我们以迁移在CommonCrawl和Wikipedia语料上进行训练的中文词向量模型为例:
# 下载中文词向量模型(bin.gz文件)
wget https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.zh.300.bin.gz

2.2 解压bin.gz文件到bin文件[¶](#2.2 解压bin.gz文件到bin文件¶)

复制代码
# 使用gunzip进行解压, 获取cc.zh.300.bin文件
gunzip cc.zh.300.bin.gz

2.3 加载bin文件获取词向量[¶](#2.3 加载bin文件获取词向量¶)

复制代码
# 加载模型
>>> model = fasttext.load_model("cc.zh.300.bin")

# 查看前100个词汇(这里的词汇是广义的, 可以是中文符号或汉字))
>>> model.words[:100]
[',', '的', '。', '</s>', '、', '是', '一', '在', ':', '了', '(', ')', "'", '和', '不', '有', '我', ',', ')', '(', '"', '"', '也', '人', '个', ':', '中', '.', '就', '他', '》', '《', '-', '你', '都', '上', '大', '!', '这', '为', '多', '与', '章', '「', '到', '」', '要', '?', '被', '而', '能', '等', '可以', '年', ';', '|', '以', '及', '之', '公司', '对', '中国', '很', '会', '小', '但', '我们', '最', '更', '/', '1', '三', '新', '自己', '可', '2', '或', '次', '好', '将', '第', '种', '她', '...', '3', '地', '對', '用', '工作', '下', '后', '由', '两', '使用', '还', '又', '您', '?', '其', '已']


# 使用模型获得'音乐'这个名词的词向量
>>> model.get_word_vector("音乐")
array([-6.81843981e-02,  3.84048335e-02,  4.63239700e-01,  6.11658543e-02,
        9.38086119e-03, -9.63955745e-02,  1.28141120e-01, -6.51574507e-02,
        ...
        3.13430429e-02, -6.43611327e-02,  1.68979481e-01, -1.95011273e-01],
      dtype=float32)    

2.4 利用邻近词进行效果检验[¶](#2.4 利用邻近词进行效果检验¶)

复制代码
# 以'音乐'为例, 返回的邻近词基本上与音乐都有关系, 如乐曲, 音乐会, 声乐等.
>>> model.get_nearest_neighbors("音乐")
[(0.6703276634216309, '乐曲'), (0.6569967269897461, '音乐人'), (0.6565821170806885, '声乐'), (0.6557438373565674, '轻音乐'), (0.6536258459091187, '音乐家'), (0.6502416133880615, '配乐'), (0.6501686573028564, '艺术'), (0.6437276005744934, '音乐会'), (0.639589250087738, '原声'), (0.6368917226791382, '音响')]


# 以'美术'为例, 返回的邻近词基本上与美术都有关系, 如艺术, 绘画, 霍廷霄(满城尽带黄金甲的美术师)等.
>>> model.get_nearest_neighbors("美术")
[(0.724744975566864, '艺术'), (0.7165924310684204, '绘画'), (0.6741853356361389, '霍廷霄'), (0.6470299363136292, '纯艺'), (0.6335071921348572, '美术家'), (0.6304370164871216, '美院'), (0.624431312084198, '艺术类'), (0.6244068741798401, '陈浩忠'), (0.62302166223526, '美术史'), (0.621710479259491, '环艺系')]


# 以'周杰伦'为例, 返回的邻近词基本上与明星有关系, 如杰伦, 周董, 陈奕迅等.
>>> model.get_nearest_neighbors("周杰伦")
[(0.6995140910148621, '杰伦'), (0.6967097520828247, '周杰倫'), (0.6859776377677917, '周董'), (0.6381043195724487, '陈奕迅'), (0.6367626190185547, '张靓颖'), (0.6313326358795166, '张韶涵'), (0.6271176338195801, '谢霆锋'), (0.6188404560089111, '周华健'), (0.6184280514717102, '林俊杰'), (0.6143589019775391, '王力宏')]

3 小结[¶](#3 小结¶)

  • 学习了什么是词向量迁移:

    • 使用在大型语料库上已经进行训练完成的词向量模型.
  • 学习了fasttext工具中可以提供的可迁移的词向量:

    • fasttext提供了157种语言的在CommonCrawl和Wikipedia语料上进行训练的可迁移词向量模型, 它们采用CBOW模式进行训练, 词向量维度为300维. 可通过该地址查看具体语言词向量模型: Word vectors for 157 languages · fastText
    • fasttext提供了294种语言的在Wikipedia语料上进行训练的可迁移词向量模型, 它们采用skipgram模式进行训练, 词向量维度同样是300维. 可通过该地址查看具体语言词向量模型: Wiki word vectors · fastText
  • 如何使用fasttext进行词向量模型迁移:

    • 第一步: 下载词向量模型压缩的bin.gz文件
    • 第二步: 解压bin.gz文件到bin文件
    • 第三步: 加载bin文件获取词向量
    • 第四步: 利用邻近词进行效果检验

6 迁移学习概念

学习目标

  • 了解迁移学习中的有关概念
  • 知道迁移学习的两种迁移方式

1 迁移学习有关概念[¶](#1 迁移学习有关概念¶)

  • 预训练模型
  • 微调

1.1 预训练模型(Pretrained model)[¶](#1.1 预训练模型(Pretrained model)¶)

  • 一般情况下预训练模型都是大型模型,具备复杂的网络结构,众多的参数量,以及在足够大的数据集下进行训练而产生的模型.。在NLP领域,预训练模型往往是语言模型。因为语言模型的训练是无监督的,可以获得大规模语料,同时语言模型又是许多典型NLP任务的基础,如机器翻译,文本生成,阅读理解等,常见的预训练模型有BERT, GPT, roBERTa, transformer-XL等。

1.2 微调(Fine-tuning)[¶](#1.2 微调(Fine-tuning)¶)

  • 根据给定的预训练模型,改变它的部分参数或者为其新增部分输出结构后,通过在小部分数据集上训练,来使整个模型更好的适应特定任务。

1.3 两种迁移方式[¶](#1.3 两种迁移方式¶)

  • 直接使用预训练模型,进行相同任务的处理,不需要调整参数或模型结构,这些模型开箱即用。但是这种情况一般只适用于普适任务, 如:fasttest工具包中预训练的词向量模型。另外,很多预训练模型开发者为了达到开箱即用的效果,将模型结构分各个部分保存为不同的预训练模型,提供对应的加载方法来完成特定目标。

  • 更加主流的迁移学习方式是发挥预训练模型特征抽象的能力,然后再通过微调的方式,通过训练更新小部分参数以此来适应不同的任务。这种迁移方式需要提供小部分的标注数据来进行监督学习。

  • 关于迁移方式的说明:

    • 直接使用预训练模型的方式, 已经在fasttext的词向量迁移中学习。接下来的迁移学习实践将主要讲解通过微调的方式进行迁移学习。

7 NLP中的常用预训练模型

学习目标

  • 了解当下NLP中流行的预训练模型.
  • 掌握如何加载和使用预训练模型.

1 当下NLP中流行的预训练模型[¶](#1 当下NLP中流行的预训练模型¶)

  • BERT
  • GPT
  • GPT-2
  • Transformer-XL
  • XLNet
  • XLM
  • RoBERTa
  • DistilBERT
  • ALBERT
  • T5
  • XLM-RoBERTa

1.1 BERT及其变体[¶](#1.1 BERT及其变体¶)

  • bert-base-uncased: 编码器具有12个隐层, 输出768维张量, 12个自注意力头, 共110M参数量, 在小写的英文文本上进行训练而得到.
  • bert-large-uncased: 编码器具有24个隐层, 输出1024维张量, 16个自注意力头, 共340M参数量, 在小写的英文文本上进行训练而得到.
  • bert-base-cased: 编码器具有12个隐层, 输出768维张量, 12个自注意力头, 共110M参数量, 在不区分大小写的英文文本上进行训练而得到.
  • bert-large-cased: 编码器具有24个隐层, 输出1024维张量, 16个自注意力头, 共340M参数量, 在不区分大小写的英文文本上进行训练而得到.
  • bert-base-multilingual-uncased: 编码器具有12个隐层, 输出768维张量, 12个自注意力头, 共110M参数量, 在小写的102种语言文本上进行训练而得到.
  • bert-large-multilingual-uncased: 编码器具有24个隐层, 输出1024维张量, 16个自注意力头, 共340M参数量, 在小写的102种语言文本上进行训练而得到.
  • bert-base-chinese: 编码器具有12个隐层, 输出768维张量, 12个自注意力头, 共110M参数量, 在简体和繁体中文文本上进行训练而得到.

1.2 GPT[¶](#1.2 GPT¶)

  • openai-gpt: 解码器具有12个隐层, 输出768维张量, 12个自注意力头, 共110M参数量, 由OpenAI在英文语料上进行训练而得到.

1.3 GPT-2及其变体[¶](#1.3 GPT-2及其变体¶)

  • gpt2: 解码器具有12个隐层, 输出768维张量, 12个自注意力头, 共117M参数量, 在OpenAI GPT-2英文语料上进行训练而得到.
  • gpt2-xl: 解码器具有48个隐层, 输出1600维张量, 25个自注意力头, 共1558M参数量, 在大型的OpenAI GPT-2英文语料上进行训练而得到.

1.4 Transformer-XL[¶](#1.4 Transformer-XL¶)

  • transfo-xl-wt103: 编码器具有18个隐层, 输出1024维张量, 16个自注意力头, 共257M参数量, 在wikitext-103英文语料进行训练而得到.

1.5 XLNet及其变体[¶](#1.5 XLNet及其变体¶)

  • xlnet-base-cased: 编码器具有12个隐层, 输出768维张量, 12个自注意力头, 共110M参数量, 在英文语料上进行训练而得到.
  • xlnet-large-cased: 编码器具有24个隐层, 输出1024维张量, 16个自注意力头, 共240参数量, 在英文语料上进行训练而得到.

1.6 XLM[¶](#1.6 XLM¶)

  • xlm-mlm-en-2048: 编码器具有12个隐层, 输出2048维张量, 16个自注意力头, 在英文文本上进行训练而得到.

1.7 RoBERTa及其变体[¶](#1.7 RoBERTa及其变体¶)

  • roberta-base: 编码器具有12个隐层, 输出768维张量, 12个自注意力头, 共125M参数量, 在英文文本上进行训练而得到.
  • roberta-large: 编码器具有24个隐层, 输出1024维张量, 16个自注意力头, 共355M参数量, 在英文文本上进行训练而得到.

1.8 DistilBERT及其变体[¶](#1.8 DistilBERT及其变体¶)

  • distilbert-base-uncased: 基于bert-base-uncased的蒸馏(压缩)模型, 编码器具有6个隐层, 输出768维张量, 12个自注意力头, 共66M参数量.
  • distilbert-base-multilingual-cased: 基于bert-base-multilingual-uncased的蒸馏(压缩)模型, 编码器具有6个隐层, 输出768维张量, 12个自注意力头, 共66M参数量.

1.9 ALBERT[¶](#1.9 ALBERT¶)

  • albert-base-v1: 编码器具有12个隐层, 输出768维张量, 12个自注意力头, 共125M参数量, 在英文文本上进行训练而得到.
  • albert-base-v2: 编码器具有12个隐层, 输出768维张量, 12个自注意力头, 共125M参数量, 在英文文本上进行训练而得到, 相比v1使用了更多的数据量, 花费更长的训练时间.

1.10 T5及其变体[¶](#1.10 T5及其变体¶)

  • t5-small: 编码器具有6个隐层, 输出512维张量, 8个自注意力头, 共60M参数量, 在C4语料上进行训练而得到.
  • t5-base: 编码器具有12个隐层, 输出768维张量, 12个自注意力头, 共220M参数量, 在C4语料上进行训练而得到.
  • t5-large: 编码器具有24个隐层, 输出1024维张量, 16个自注意力头, 共770M参数量, 在C4语料上进行训练而得到.

1.1 XLM-RoBERTa及其变体[¶](#1.1 XLM-RoBERTa及其变体¶)

  • xlm-roberta-base: 编码器具有12个隐层, 输出768维张量, 8个自注意力头, 共125M参数量, 在2.5TB的100种语言文本上进行训练而得到.
  • xlm-roberta-large: 编码器具有24个隐层, 输出1027维张量, 16个自注意力头, 共355M参数量, 在2.5TB的100种语言文本上进行训练而得到.

2 预训练模型说明[¶](#2 预训练模型说明¶)

  • 所有上述预训练模型及其变体都是以transformer为基础,只是在模型结构如神经元连接方式,编码器隐层数,多头注意力的头数等发生改变,这些改变方式的大部分依据都是由在标准数据集上的表现而定,因此,对于我们使用者而言,不需要从理论上深度探究这些预训练模型的结构设计的优劣,只需要在自己处理的目标数据上,尽量遍历所有可用的模型对比得到最优效果即可.

3 小结[¶](#3 小结¶)

  • 当下NLP中流行的预训练模型:
    • BERT
    • GPT
    • GPT-2
    • Transformer-XL
    • XLNet
    • XLM
    • RoBERTa
    • DistilBERT
    • ALBERT
    • T5
    • XLM-RoBERTa

8 Transformers库使用

学习目标

  • 了解并掌握管道方式完成基本NLP任务
  • 了解并掌握自动模型方式完成基本NLP任务
  • 了解并掌握具体模型方式完成基本NLP任务

1 了解Transformers库[¶](#1 了解Transformers库¶)

  • Huggingface总部位于纽约,是一家专注于自然语言处理、人工智能和分布式系统的创业公司。他们所提供的聊天机器人技术一直颇受欢迎,但更出名的是他们在NLP开源社区上的贡献。Huggingface一直致力于自然语言处理NLP技术的平民化(democratize),希望每个人都能用上最先进(SOTA, state-of-the-art)的NLP技术,而非困窘于训练资源的匮乏。同时Hugging Face专注于NLP技术,拥有大型的开源社区。尤其是在github上开源的自然语言处理,预训练模型库 Transformers,已被下载超过一百万次,github上超过24000个star。

  • Huggingface Transformers 是基于一个开源基于 transformer 模型结构提供的预训练语言库。它支持 Pytorch,Tensorflow2.0,并且支持两个框架的相互转换。Transformers 提供了NLP领域大量state-of-art的 预训练语言模型结构的模型和调用框架。

  • 框架支持了最新的各种NLP预训练语言模型,使用者可快速的进行模型调用,并且支持模型further pretraining 和 下游任务fine-tuning。举个例子Transformers 库提供了很多SOTA的预训练模型,比如BERT, GPT-2, RoBERTa, XLM, DistilBert, XLNet, CTRL。

  • 社区Transformer的访问地址为:https://huggingface.co/,见下图。

  • 备注
    • 1 点击 Model链接可查看、下载预训练模型。点击Datasets链接可查看、下载数据集。点击Docs链接可以阅读预训练模型的编程文档,十分方便
    • 2 SOTA(state-of-the-art)是指目前对某项任务"最好的"算法或技术

2 Transformers库三层应用结构[¶](#2 Transformers库三层应用结构¶)

  • 管道(Pipeline)方式:高度集成的极简使用方式,只需要几行代码即可实现一个NLP任务。
  • 自动模型(AutoMode)方式:可载入并使用BERTology系列模型。
  • 具体模型(SpecificModel)方式:在使用时,需要明确指定具体的模型,并按照每个BERTology系列模型中的特定参数进行调用,该方式相对复杂,但具有较高的灵活度。

3 管道方式完成多种NLP任务[¶](#3 管道方式完成多种NLP任务¶)

注意:若虚拟机中已经安装transformers,以下安装步骤不需再次执行

复制代码
# 注意在执行clone之前,要查看当前是在那个目录下,比如$HOME/nlpdev/目录下
# 克隆huggingface的transfomers文件
git clone https://github.com/huggingface/transformers.git

# 进行transformers文件夹
cd transformers

# 切换transformers到指定版本
git checkout v4.19.0

# 安装transformers包
pip install .

# 安装datasets数据库,
# 注意workon xxx虚拟机开发环境,在虚拟机开发环境下安装
pip install datasets

3.1 文本分类任务[¶](#3.1 文本分类任务¶)

  • 文本分类是指模型可以根据文本中的内容来进行分类。例如根据内容对情绪进行分类,根据内容对商品分类等。文本分类模型一般是通过有监督训练得到的。对文本内容的具体分类,依赖于训练时所使用的样本标签。

    导入工具包

    import torch
    from transformers import pipeline
    import numpy as np

    情感分类任务

    def dm01_test_classification():

    复制代码
      # 1 使用中文预训练模型chinese_sentiment
      # 模型下载地址 git clone https://huggingface.co/techthiyanes/chinese_sentiment
    
      # 2 实例化pipeline对象
      my_model = pipeline(task='sentiment-analysis', model='./chinese_sentiment')
      # my_model = pipeline(task='sentiment-analysis', model='./bert-base-chinese')
    
      # 3 文本送给模型 进行文本分类
      output = my_model('我爱北京天安门,天安门上太阳升。')
      print('output--->', output)

    结果输出

    复制代码
          output---> [{'label': 'star 5', 'score': 0.6314294338226318}]
  • pipeline函数可以自动从官网下载预训练模型,也可以加载本地的预训练模型

transformer库中预训练模型查找和下载

3.2 特征提取任务[¶](#3.2 特征提取任务¶)

  • 特征抽取任务只返回文本处理后的特征,属于预训练模型的范畴。特征抽取任务的输出结果需要和其他模型一起工作。

    特征抽取任务

    def dm02_test_feature_extraction():
    # 1 下载中文预训练模型 git clone https://huggingface.co/bert-base-chinese

    复制代码
      # 2 实例化pipeline对象 返回模型对象
      my_model = pipeline(task='feature-extraction', model='./bert-base-chinese')
    
      # 3 给模型送数据 提取语句特征
      output = my_model('人生该如何起头')
      print('output--->', type(output), np.array(output).shape)

    输出结果

    output---> <class 'list'> (1, 9, 768)

    7个字变成9个字原因: [CLS] 人 生 该 如 何 起 头 [SEP]

  • 不带任务头输出:特征抽取任务属于不带任务头输出,本bert-base-chinese模型的9个字,每个字的特征维度是768

  • 带头任务头输出:其他有指定任务类型的比如文本分类,完型填空属于带头任务输出,会根据具体任务类型不同输出不同的结果

3.3 完型填空任务[¶](#3.3 完型填空任务¶)

  • 完型填空任务又被叫做"遮蔽语言建模任务",它属于BERT模型训练过程中的子任务。下面完成一个中文场景的完型填空。

    完型填空任务

    def dm03_test_fill_mask():

    复制代码
      # 1 下载预训练模型 全词模型git clone https://huggingface.co/hfl/chinese-bert-wwm
    
      # 2 实例化pipeline对象 返回一个模型
      my_model = pipeline(task='fill-mask', model='chinese-bert-wwm')
    
      # 3 给模型送数据 做预测
      input = '我想明天去[MASK]家吃饭。'
      output = my_model(input)
    
      # 4 输出预测结果
      print('output--->', output)

    输出结果

    复制代码
      # output--->
      # [{'score': 0.34331339597702026, 'token': 1961, 'token_str': '她', 'sequence': '我 想 明 天 去 她 家 吃 饭.'},
      # {'score': 0.2533259987831116, 'token': 872, 'token_str': '你', 'sequence': '我 想 明 天 去 你 家 吃 饭.'},
      # {'score': 0.1874391734600067, 'token': 800, 'token_str': '他', 'sequence': '我 想 明 天 去 他 家 吃 饭.'},
      # {'score': 0.1273055076599121, 'token': 2769, 'token_str': '我', 'sequence': '我 想 明 天 去 我 家 吃 饭.'},
      # {'score': 0.02162978984415531, 'token': 2644, 'token_str': '您', 'sequence': '我 想 明 天 去 您 家 吃 饭.'}]

可以在官网在线查找完型填空结果

  • 阅读理解任务又称为"抽取式问答任务",即输入一段文本和一个问题,让模型输出结果。

    阅读理解任务(抽取式问答)

    def dm04_test_question_answering():

    复制代码
      # 问答语句
      context = '我叫张三,我是一个程序员,我的喜好是打篮球。'
      questions = ['我是谁?', '我是做什么的?', '我的爱好是什么?']
    
      # 1 下载模型 git clone https://huggingface.co/luhua/chinese_pretrain_mrc_roberta_wwm_ext_large
    
      # 2 实例化化pipeline 返回模型
      model = pipeline('question-answering', model='chinese_pretrain_mrc_roberta_wwm_ext_large')
    
      # 3 给模型送数据 的预测结果
      print(model(context=context, question=questions))
    
      # 输出结果
      '''
      [{'score': 1.2071758523357623e-12, 'start': 2, 'end': 4, 'answer': '张三'},
       {'score': 2.60890374192968e-06, 'start': 9, 'end': 12, 'answer': '程序员'},
       {'score': 4.1686924134864967e-08, 'start': 18, 'end': 21, 'answer': '打篮球'}]
      '''

3.5 文本摘要任务[¶](#3.5 文本摘要任务¶)

  • 摘要生成任务的输入一一段文本,输出是一段概况、简单的文字。

    文本摘要任务

    def dm05_test_summarization():

    复制代码
      # 1 下载模型 git clone https://huggingface.co/sshleifer/distilbart-cnn-12-6
    
      # 2 实例化pipline 返回模型
      my_model = pipeline(task = 'summarization', model="distilbart-cnn-12-6")
    
      # 3 准备文本 送给模型
      text = "BERT is a transformers model pretrained on a large corpus of English data " \
             "in a self-supervised fashion. This means it was pretrained on the raw texts " \
             "only, with no humans labelling them in any way (which is why it can use lots " \
             "of publicly available data) with an automatic process to generate inputs and " \
             "labels from those texts. More precisely, it was pretrained with two objectives:Masked " \
             "language modeling (MLM): taking a sentence, the model randomly masks 15% of the " \
             "words in the input then run the entire masked sentence through the model and has " \
             "to predict the masked words. This is different from traditional recurrent neural " \
             "networks (RNNs) that usually see the words one after the other, or from autoregressive " \
             "models like GPT which internally mask the future tokens. It allows the model to learn " \
             "a bidirectional representation of the sentence.Next sentence prediction (NSP): the models" \
             " concatenates two masked sentences as inputs during pretraining. Sometimes they correspond to " \
             "sentences that were next to each other in the original text, sometimes not. The model then " \
             "has to predict if the two sentences were following each other or not."
      output = my_model(text)
    
      # 4 打印摘要结果
      print('output--->', output)

    输出结果

    output---> [{'summary_text': ' BERT is a transformers model pretrained on a large corpus of English data in a self-supervised fashion . It was pretrained with two objectives: Masked language modeling (MLM) and next sentence prediction (NSP) This allows the model to learn a bidirectional representation of the sentence .'}]

3.6 NER任务[¶](#3.6 NER任务¶)

  • 实体词识别(NER)任务是NLP中的基础任务。它用于识别文本中的人名(PER)、地名(LOC)、组织(ORG)以及其他实体(MISC)等。例如:(王 B-PER) (小 I-PER) (明 I-PER) (在 O) (办 B-LOC) (公 I-LOC) (室 I-LOC)。其中O表示一个非实体,B表示一个实体的开始,I表示一个实体块的内部。

  • 实体词识别本质上是一个分类任务(又叫序列标注任务),实体词识别是句法分析的基础,而句法分析优势NLP任务的核心。`

    NER任务

    def dm06_test_ner():

    复制代码
      # 1 下载模型 git clone https://huggingface.co/uer/roberta-base-finetuned-cluener2020-chinese
    
      # 2 实例化pipeline 返回模型
      model = pipeline('ner', model='roberta-base-finetuned-cluener2020-chinese')
    
      # 3 给模型送数据 打印NER结果
      print(model('我爱北京天安门,天安门上太阳升。'))
    
      '''
      [{'entity': 'B-address', 'score': 0.8838121, 'index': 3, 'word': '北', 'start': 2, 'end': 3},
       {'entity': 'I-address', 'score': 0.83543754, 'index': 4, 'word': '京', 'start': 3, 'end': 4},
       {'entity': 'I-address', 'score': 0.4240591, 'index': 5, 'word': '天', 'start': 4, 'end': 5},
       {'entity': 'I-address', 'score': 0.7524443, 'index': 6, 'word': '安', 'start': 5, 'end': 6},
       {'entity': 'I-address', 'score': 0.6949866, 'index': 7, 'word': '门', 'start': 6, 'end': 7},
       {'entity': 'B-address', 'score': 0.65552264, 'index': 9, 'word': '天', 'start': 8, 'end': 9},
       {'entity': 'I-address', 'score': 0.5376768, 'index': 10, 'word': '安', 'start': 9, 'end': 10},
       {'entity': 'I-address', 'score': 0.510813, 'index': 11, 'word': '门', 'start': 10, 'end': 11}]
      '''

4 自动模型方式完成多种NLP任务[¶](#4 自动模型方式完成多种NLP任务¶)

4.1 文本分类任务[¶](#4.1 文本分类任务¶)

  • 文本分类是指模型可以根据文本中的内容来进行分类。例如根据内容对情绪进行分类,根据内容对商品分类等。文本分类模型一般是通过有监督训练得到的。对文本内容的具体分类,依赖于训练时所使用的样本标签。

    导入工具包

    import torch
    from transformers import AutoConfig, AutoModel, AutoTokenizer
    from transformers import AutoModelForSequenceClassification, AutoModelForMaskedLM, AutoModelForQuestionAnswering

    AutoModelForSeq2SeqLM:文本摘要

    AutoModelForTokenClassification:ner

    from transformers import AutoModelForSeq2SeqLM, AutoModelForTokenClassification

    情感分类任务

    def dm01_test_classification():

    复制代码
      # 1 加载tokenizer
      my_tokenizer = AutoTokenizer.from_pretrained('./chinese_sentiment')
    
      # 2 加载模型
      my_model = AutoModelForSequenceClassification.from_pretrained('./chinese_sentiment')
    
      # 3 文本转张量
      message = '人生该如何起头'
    
      # 3-1 return_tensors='pt' 返回是二维tensor
      msg_tensor1 = my_tokenizer.encode(text=message, return_tensors='pt', padding=True, truncation=True, max_length=20)
      print('msg_tensor1--->', msg_tensor1)
    
      # 3-2 不用return_tensors='pt'是一维列表
      msg_list2 = my_tokenizer.encode(text=message, padding=True, truncation=True, max_length=20)
      print('msg_list2--->', msg_list2)
      msg_tensor2 = torch.tensor([msg_list2])
      print('msg_tensor2--->', msg_tensor2)
    
      # 4 数据送给模型
      # 4-1
      my_model.eval()
      output1 = my_model(msg_tensor2)
      print('情感分类模型头输出outpout1--->', output1)
      # 4-2
      output2 = my_model(msg_tensor2, return_dict=False)
      print('情感分类模型头输出outpout2--->', output2)
  • AutoTokenizer、AutoModelForSequenceClassification函数可以自动从官网下载预训练模型,也可以加载本地的预训练模型

  • AutoModelForSequenceClassification类管理着分类任务,会根据参数的输入选用不同的模型。

  • AutoTokenizer的encode()函数使用return_tensors='pt'参数和不使用pt参数对文本编码的结果不同

  • AutoTokenizer的encode()函数使用padding='max_length'可以按照最大程度进行补齐,俗称打padding

  • 调用模型的forward函数输入return_dict=False参数,返回结果也不同

程序运行结果

复制代码
msg_tensor1---> tensor([[ 101,  782, 4495, 6421, 1963,  862, 6629, 1928,  102]])
msg_list2---> [101, 782, 4495, 6421, 1963, 862, 6629, 1928, 102]
msg_tensor2---> tensor([[ 101,  782, 4495, 6421, 1963,  862, 6629, 1928,  102]])
情感分类模型头输出outpout1---> SequenceClassifierOutput(loss=None, logits=tensor([[-2.7387, -1.7528,  0.2273,  2.0507,  1.4128]],
       grad_fn=<AddmmBackward>), hidden_states=None, attentions=None)
情感分类模型头输出outpout2---> (tensor([[-2.7387, -1.7528,  0.2273,  2.0507,  1.4128]],
       grad_fn=<AddmmBackward>),)

#注1:101代表[CLS] 102代表[SEP]

4.2 特征提取任务[¶](#4.2 特征提取任务¶)

  • 特征抽取任务只返回文本处理后的特征,属于预训练模型的范畴。特征抽取任务的输出结果需要和其他模型一起工作。

    特征提取任务-不带任务输出头的任务

    def dm02_test_feature_extraction():
    # 1 加载tokenizer
    my_tokenizer = AutoTokenizer.from_pretrained(pretrained_model_name_or_path='./bert-base-chinese')

    复制代码
      # 2 加载模型
      my_model = AutoModel.from_pretrained(pretrained_model_name_or_path = './bert-base-chinese')
    
      # 3 文本转张量
      message = ['你是谁', '人生该如何起头']
      msgs_tensor = my_tokenizer.encode_plus(text=message, return_tensors='pt', truncation=True, pad_to_max_length=True, max_length=30)
      print('msgs_tensor--->', msgs_tensor)
    
      # 4 给模型送数据提取特征
      my_model.eval()
      output = my_model(**msgs_tensor)
      print('不带模型头输出output--->', output)
      print('outputs.last_hidden_state.shape--->', output.last_hidden_state.shape)  # torch.Size([1, 30, 768])
      print('outputs.pooler_output.shape--->', output.pooler_output.shape)  # torch.Size([1, 768])
  • 不带任务头输出:特征抽取任务属于不带任务头输出,本bert-base-chinese模型的9个字,每个字的特征维度是768

  • 带头任务头输出:其他有指定任务类型的比如文本分类,完型填空属于带头任务输出,会根据具体任务类型不同输出不同的结果

程序运行结果

复制代码
msgs_tensor---> 
# 1 input_ids对两个句子text2id以后的结果,
# 101表示段落开头,第一个102代表第一个句子结束,第二个102点第二个句子结束
# 后面的0表示 按照编码要求pad_to_max_length=True和max_length=30补充pad零
{'input_ids': tensor([[ 101,  872, 3221, 6443,  102,  782, 4495, 6421, 1963,  862, 6629, 1928,
          102,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
            0,    0,    0,    0,    0,    0]]), 
# 2 token_type_ids表示段落标志0代表第一个句子,1代表第二个句子
 'token_type_ids': tensor([[0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
         0, 0, 0, 0, 0, 0]]), 
# 3 attention_mask表示注意力机制的掩码数据,1表示有真实数据,0表示是pad数据需要掩码
 'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
         0, 0, 0, 0, 0, 0]])}

# 1 last_hidden_state表示最后一个隐藏层的数据 [1,30,768]
# 2 pooler_output表示池化,也就是对最后一个隐藏层再进行线性变换以后平均池化的结果。分类时候使用。
不带模型头输出output---> BaseModelOutputWithPoolingAndCrossAttentions(
  last_hidden_state=tensor([[[ 0.7001,  0.4651,  0.2427,  ...,  0.5753, -0.4330,  0.1878],
         [ 0.4017,  0.1123,  0.4482,  ..., -0.2614, -0.2649, -0.1497],
         [ 1.2000, -0.4859,  1.1970,  ...,  0.7543, -0.2405, -0.2627],
         ...,
         [ 0.2074,  0.4022, -0.0448,  ..., -0.0849, -0.0766, -0.2134],
         [ 0.0879,  0.2482, -0.2356,  ...,  0.2967, -0.2357, -0.5138],
         [ 0.4944,  0.1340, -0.2387,  ...,  0.2375, -0.1011, -0.3314]]],
       grad_fn=<NativeLayerNormBackward>), 
  pooler_output=tensor([[ 0.9996,  1.0000,  0.9995,  0.9412,  0.8629,  0.9592, -0.8144, -0.9654,
          0.9892, -0.9997,  1.0000,  0.9998, -0.1187, -0.9373,  0.9999, -1.0000,
         ...,
         -0.9967,  1.0000,  0.8626, -0.9993, -0.9704, -0.9993, -0.9971,  0.8522]],
       grad_fn=<TanhBackward>), 
  hidden_states=None, past_key_values=None, attentions=None, cross_attentions=None)

outputs.last_hidden_state.shape---> torch.Size([1, 30, 768])
outputs.pooler_output.shape---> torch.Size([1, 768])

4.3 完型填空任务[¶](#4.3 完型填空任务¶)

  • 完型填空任务又被叫做"遮蔽语言建模任务",它属于BERT模型训练过程中的子任务。下面完成一个中文场景的完型填空。

    完型填空任务

    def dm03_test_fill_mask():

    复制代码
      # 1 加载tokenizer
      modename = "chinese-bert-wwm"
      # modename = "bert-base-chinese"
      my_tokenizer = AutoTokenizer.from_pretrained(modename)
    
      # 2 加载模型
      my_model = AutoModelForMaskedLM.from_pretrained(modename)
    
      # 3 文本转张量
      input = my_tokenizer.encode_plus('我想明天去[MASK]家吃饭.', return_tensors='pt')
      print('input--->', input)
    
      # 4 给模型送数据提取特征
      my_model.eval()
      output = my_model(**input)
      print('output--->', output) 
      print('output.logits--->', output.logits.shape) # [1,12,21128]
    
      # 5 取概率最高
      mask_pred_idx = torch.argmax(output.logits[0][6]).item()
      print('打印概率最高的字:', my_tokenizer.convert_ids_to_tokens([mask_pred_idx]))

程序运行结果

复制代码
# 1 input_ids 对句子text2id以后的结果 
# 2 token_type_ids 句子分段信息
# 3 attention_mask 句子掩码信息
input---> {'input_ids': tensor([[ 101, 2769, 2682, 3209, 1921, 1343,  103, 2157, 1391, 7649,  119,  102]]), 'token_type_ids': tensor([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]), 'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]])}

# 1 logits表示MASK预测的结果,也是一种分类概率
# 2 output.logits的分类形状 [1, 12, 21128]
# 3 通过 my_tokenizer.convert_ids_to_tokens()函数完成id2text的操作
output---> MaskedLMOutput(loss=None, logits=tensor([[[ -9.9017,  -9.6006,  -9.8032,  ...,  -7.9744,  -7.7402,  -8.2912],
         [-14.3878, -15.0353, -14.7893,  ..., -10.0437, -10.5279,  -9.7544],
         [-14.2215, -14.1145, -14.5770,  ...,  -6.3246,  -4.1784,  -4.6072],
         ...,
         [-14.6938, -16.8133, -15.1296,  ...,  -9.2327,  -8.1931, -15.2430],
         [-10.8649, -11.4887, -11.5731,  ...,  -6.5378,  -0.8715,  -5.3870],
         [-11.8495, -11.8358, -12.0314,  ...,  -8.4242,  -6.2741,  -8.2787]]],
       grad_fn=<AddBackward0>), hidden_states=None, attentions=None)
output.logits---> torch.Size([1, 12, 21128])
打印概率最高的字: ['她']

4.4 阅读理解任务[¶](#4.4 阅读理解任务¶)

  • 阅读理解任务又称为"抽取式问答任务",即输入一段文本和一个问题,让模型输出结果。

    阅读理解任务(抽取式问答)

    def dm04_test_question_answering():

    复制代码
      # 1 加载tokenizer
      my_tokenizer = AutoTokenizer.from_pretrained('./chinese_pretrain_mrc_roberta_wwm_ext_large')
    
      # 2 加载模型
      my_model = AutoModelForQuestionAnswering.from_pretrained('./chinese_pretrain_mrc_roberta_wwm_ext_large')
    
      # 3 文本转张量
      # 文字中的标点符号如果是中文的话,会影响到预测结果 也可以去掉标点符号
      context = '我叫张三 我是一个程序员 我的喜好是打篮球'
      questions = ['我是谁?', '我是做什么的?', '我的爱好是什么?']
    
      # 4 给模型送数据 模型做抽取式问答
      my_model.eval()
      for question in questions:
          input = my_tokenizer.encode_plus(question, context, return_tensors='pt')
          print('input--->', input)
          output = my_model(**input)
          print('output--->', output)
          start, end = torch.argmax(output.start_logits), torch.argmax(output.end_logits) +1
          answer =  my_tokenizer.convert_ids_to_tokens(input['input_ids'][0][start:end] )
          print('question:', question, 'answer:', answer)

程序运行结果:

复制代码
# input_ids表示text2id后结果 # token_type_ids表示句子分段信息 # attention_mask表示句子attention掩码信息
input---> {'input_ids': tensor([[ 101, 2769, 3221, 6443, 8043,  102, 2769, 1373, 2476,  676, 2769, 3221,
          671,  702, 4923, 2415, 1447, 2769, 4638, 1599, 1962, 3221, 2802, 5074,
         4413,  102]]), 'token_type_ids': tensor([[0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
         1, 1]]), 'attention_mask': tensor([[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]])}

# start_logits end_logits分布表示从原文中抽取答案的位置概率
# 比如:start_logits的最大值代表句子答案最可能开始的位置
# 比如:end_logits的最大值代表句子答案可能结束的位置
output---> QuestionAnsweringModelOutput(loss=None, start_logits=tensor([[ -1.9978, -11.4788, -12.6324, -11.8324, -12.4148, -11.9371,  -2.7246,
          -6.6402,   3.9131,  -2.9533,  -7.0866,  -9.5696,  -4.2775,  -8.9042,
           0.5753,  -6.9468,  -7.0469,  -8.5334, -11.3796,  -9.3905, -11.0242,
         -11.1047,  -5.7124,  -2.7293,  -7.5896, -12.6013]],
       grad_fn=<CopyBackwards>), end_logits=tensor([[ -1.3483, -12.0141, -11.6312, -11.6629, -11.9607, -12.0039,  -4.6118,
          -7.4034,  -2.3499,   4.7159,  -7.2880,  -9.5317,  -6.6742,  -6.0915,
          -7.0023,  -4.9691,   1.4515,  -7.8329,  -9.0895, -10.3742,  -8.7482,
          -9.8567,  -7.2930,  -5.8163,  -1.7323, -12.2525]],
       grad_fn=<CopyBackwards>), hidden_states=None, attentions=None)

question: 我是谁? answer: ['张', '三']
question: 我是做什么的? answer: ['程', '序', '员']
question: 我的爱好是什么? answer: ['打', '篮', '球']

4.5 文本摘要任务[¶](#4.5 文本摘要任务¶)

  • 摘要生成任务的输入一一段文本,输出是一段概况、简单的文字。

    文本摘要任务

    def dm05_test_summarization():
    text = "BERT is a transformers model pretrained on a large corpus of English data "
    "in a self-supervised fashion. This means it was pretrained on the raw texts "
    "only, with no humans labelling them in any way (which is why it can use lots "
    "of publicly available data) with an automatic process to generate inputs and "
    "labels from those texts. More precisely, it was pretrained with two objectives:Masked "
    "language modeling (MLM): taking a sentence, the model randomly masks 15% of the "
    "words in the input then run the entire masked sentence through the model and has "
    "to predict the masked words. This is different from traditional recurrent neural "
    "networks (RNNs) that usually see the words one after the other, or from autoregressive "
    "models like GPT which internally mask the future tokens. It allows the model to learn "
    "a bidirectional representation of the sentence.Next sentence prediction (NSP): the models"
    " concatenates two masked sentences as inputs during pretraining. Sometimes they correspond to "
    "sentences that were next to each other in the original text, sometimes not. The model then "
    "has to predict if the two sentences were following each other or not."

    复制代码
      # 1 加载tokenizer
      my_tokenizer = AutoTokenizer.from_pretrained(pretrained_model_name_or_path="distilbart-cnn-12-6")
    
      # 2 加载模型
      my_model = AutoModelForSeq2SeqLM.from_pretrained(pretrained_model_name_or_path='distilbart-cnn-12-6')
    
      # 3 文本转张量
      input = my_tokenizer([text], return_tensors='pt')
      # print('input--->', input)
    
      # 4 送给模型做摘要
      my_model.eval()
      output = my_model.generate(input.input_ids)
      print('output--->', output)
    
      # 5 处理摘要结果
      # 5-1 decode 的 skip_special_tokens 参数可以去除 token 前面的特殊字符
      print([my_tokenizer.decode(g, skip_special_tokens=True, clean_up_tokenization_spaces=False) for g in output])
    
      # 5-2 convert_ids_to_tokens 函数只能将 ids 还原为 token
      # print(my_tokenizer.convert_ids_to_tokens(output[0]))

程序运行结果:

复制代码
output---> tensor([[    2,     0, 11126,   565,    16,    10,  7891,   268,  1421, 11857,
         26492,    15,    10,   739, 42168,     9,  2370,   414,    11,    10,
          1403,    12, 16101, 25376,  2734,   479,    85,    21, 11857, 26492,
            19,    80, 10366,    35, 31755,   196,  2777, 19039,    36, 10537,
           448,    43,     8,   220,  3645, 16782,    36,   487,  4186,    43,
            20,  3092, 10146, 26511,  1626,    80, 24397, 11305,    25, 16584,
           148, 11857, 32155,   479,  7411,    51, 20719,     7, 11305,    14,
            58,   220,     7,   349,    97,    11,     5,  1461,  2788,     6,
          2128,    45,   479,     2]])

['BERT is a transformers model pretrained on a large corpus of English data in a self-supervised fashion . It was pretrained with two objectives: Masked language modeling (MLM) and next sentence prediction (NSP) The models concatenates two masked sentences as inputs during pretraining . Sometimes they correspond to sentences that were next to each other in the original text, sometimes not .']

4.6 NER任务[¶](#4.6 NER任务¶)

  • 实体词识别(NER)任务是NLP中的基础任务。它用于识别文本中的人名(PER)、地名(LOC)、组织(ORG)以及其他实体(MISC)等。例如:(王 B-PER) (小 I-PER) (明 I-PER) (在 O) (办 B-LOC) (公 I-LOC) (室 I-LOC)。其中O表示一个非实体,B表示一个实体的开始,I表示一个实体块的内部。

  • 实体词识别本质上是一个分类任务(又叫序列标注任务),实体词识别是句法分析的基础,而句法分析优势NLP任务的核心。`

    NER任务

    def dm06_test_ner():
    # 1 加载tokenizer 加载模型 加载配置文件
    # https://huggingface.co/uer/roberta-base-finetuned-cluener2020-chinese
    my_tokenizer = AutoTokenizer.from_pretrained('roberta-base-finetuned-cluener2020-chinese')
    my_model = AutoModelForTokenClassification.from_pretrained('roberta-base-finetuned-cluener2020-chinese')
    config = AutoConfig.from_pretrained('roberta-base-finetuned-cluener2020-chinese')

    复制代码
      # 2 数据张量化
      inputs = my_tokenizer.encode_plus('我爱北京天安门,天安门上太阳升', return_tensors='pt')
      print('inputs--->', inputs.input_ids.shape, inputs.input_ids) # torch.Size([1, 17])
    
      # 3 送入模型 预测ner概率 每个字预测的标签概率
      my_model.eval()
      logits = my_model(inputs.input_ids).logits
      print('logits--->', logits.shape)           # torch.Size([1, 17, 32])
    
      # 4 对预测数据 进行显示
      input_tokens = my_tokenizer.convert_ids_to_tokens(inputs.input_ids[0])
      print('input_tokens--->', input_tokens)
      outputs = []
    
      for token, value in zip(input_tokens, logits[0]):
    
          if token in my_tokenizer.all_special_tokens:
              continue
    
          # 获得每个字预测概率最大的标签索引
          idx = torch.argmax(value).item()
    
          # 打印索引对应标签
          outputs.append((token, config.id2label[idx]))
    
      print(outputs)

程序运行结果

复制代码
inputs---> torch.Size([1, 17]) tensor([[ 101, 2769, 4263, 1266,  776, 1921, 2128, 7305, 8024, 1921, 2128, 7305,
          677, 1922, 7345, 1285,  102]])

logits---> torch.Size([1, 17, 32])

input_tokens---> ['[CLS]', '我', '爱', '北', '京', '天', '安', '门', ',', '天', '安', '门', '上', '太', '阳', '升', '[SEP]']

[('我', 'O'), ('爱', 'O'), ('北', 'B-address'), ('京', 'I-address'), ('天', 'I-address'), ('安', 'I-address'), ('门', 'I-address'), (',', 'O'), ('天', 'B-address'), ('安', 'I-address'), ('门', 'I-address'), ('上', 'O'), ('太', 'O'), ('阳', 'O'), ('升', 'O')]

5 具体模型方式完成NLP任务[¶](#5 具体模型方式完成NLP任务¶)

5.1 完型填空任务[¶](#5.1 完型填空任务¶)

  • 完型填空任务又被叫做"遮蔽语言建模任务",它属于BERT模型训练过程中的子任务。下面完成一个中文场景的完型填空。

    具体模型完型填空任务

    def dm01_test_bert_fill_mask():

    复制代码
      # 1 加载tokenizer
      modename = "bert-base-chinese"
      my_tokenizer = BertTokenizer.from_pretrained(modename)
    
      # 2 加载模型
      my_model = BertForMaskedLM.from_pretrained(modename)
    
      # 3 文本转张量
      input = my_tokenizer.encode_plus('我想明天去[MASK]家吃饭', return_tensors='pt')
      print('input--->', input)
    
      # 4 给模型送数据提取特征
      output = my_model(**input)
      print('output--->', output)
      print('output.logits--->', output.logits.shape)  # [1,11,21128]
    
      # 5 取概率最高
      mask_pred_idx = torch.argmax(output.logits[0][6]).item()
      print('打印概率最高的字:', my_tokenizer.convert_ids_to_tokens([mask_pred_idx]))

程序运行结果

复制代码
# input_ids表示text2id后结果 # token_type_ids表示句子分段信息 # attention_mask表示句子attention掩码信息
input---> {'input_ids': tensor([[ 101, 2769, 2682, 3209, 1921, 1343,  103, 2157, 1391, 7649,  102]]), 'token_type_ids': tensor([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]), 'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]])}

output---> MaskedLMOutput(loss=None, logits=tensor([[[ -8.1771,  -8.1008,  -8.1191,  ...,  -6.8355,  -6.9482,  -6.9834],
         [ -8.2775,  -8.1251,  -8.1655,  ...,  -6.8471,  -7.4265,  -6.1365],
         [-14.1093, -13.1037, -14.6324,  ...,  -6.0959,  -3.7550,  -5.7456],
         ...,
         [-16.2103, -16.7243, -15.9876,  ...,  -5.9727,  -8.2757,  -7.3852],
         [-13.5615, -13.7670, -13.4497,  ...,  -7.8282,  -4.9095,  -9.1699],
         [-10.3200, -10.1068, -10.4439,  ...,  -6.6468,  -7.0597,  -7.5027]]],
       grad_fn=<AddBackward0>), hidden_states=None, attentions=None)

output.logits---> torch.Size([1, 11, 21128])

6 小结[¶](#6 小结¶)

  • 了解Huggingface Transformers
    • Huggingface Transformers 是基于一个开源基于 transformer 模型结构提供的预训练语言。框架支持了最新的各种NLP预训练语言模型,使用者可快速的进行模型调用,并且支持模型further pretraining 和 下游任务fine-tuning
  • 使用管道方式完成多种NLP任务
    • 文本分类任务sentiment-analysis、特征提取任务feature-extraction、完型填空任务fill-mask、阅读理解任务question-answering、文本摘要任务summarization、NER任务ner
  • 使用自动模型方式完成NLP任务
    • 对Aoto系列模型类进行使用AutoModel、AutoTokenizer、AutoModelForSequenceClassification、AutoModelForMaskedLM、AutoModelForQuestionAnswering、AutoModelForSeq2SeqLM、AutoModelForTokenClassification
  • 使用具体模型方式完成NLP任务
    • 使用具体模型比如BertTokenizer等进行具体NLP任务开发

9 迁移学习实践

学习目标

  • 了解并掌握迁移学习-中文分类任务开发
  • 了解并掌握迁移学习-中文填空任务开发
  • 了解通过微调脚本微调后模型的使用方法

1 通过微调方式进行迁移学习的两种类型[¶](#1 通过微调方式进行迁移学习的两种类型¶)

  • 类型一: 直接加载预训练模型进行输入文本的特征表示, 后接自定义网络进行微调输出结果
  • 类型二: 使用指定任务类型的微调脚本微调预训练模型, 后接带有输出头的预定义网络输出结果
  • 说明: 所有类型的实战演示, 都将针对中文文本进行

2 迁移学习-中文分类[¶](#2 迁移学习-中文分类¶)

1 任务介绍[¶](#1 任务介绍¶)

  • 直接加载预训练模型进行输入文本的特征表示, 后接自定义网络进行微调输出结果

2 数据介绍[¶](#2 数据介绍¶)

  • 数据文件有三个train.csv,test.csv,validation.csv,数据样式都是一样的。

    label,text
    1,选择珠江花园的原因就是方便,有电动扶梯直接到达海边,周围餐馆、食廊、商场、超市、摊位一应俱全。酒店装修一般,但还算整洁。 泳池在大堂的屋顶,因此很小,不过女儿倒是喜欢。 包的早餐是西式的,还算丰富。 服务吗,一般
    1,15.4寸笔记本的键盘确实爽,基本跟台式机差不多了,蛮喜欢数字小键盘,输数字特方便,样子也很美观,做工也相当不错
    0,房间太小。其他的都一般。。。。。。。。。
    0,"1.接电源没有几分钟,电源适配器热的不行. 2.摄像头用不起来. 3.机盖的钢琴漆,手不能摸,一摸一个印. 4.硬盘分区不好办."
    1,"今天才知道这书还有第6卷,真有点郁闷:为什么同一套书有两种版本呢?当当网是不是该跟出版社商量商量,单独出个第6卷,让我们的孩子不会有所遗憾。"

  • 通过huggingface的datasets工具,加载信息文件信息如下

    加载训练集
    dataset_train---> Dataset({
    features: ['label', 'text'],
    num_rows: 9600
    })
    {'label': [1, 1, 0], 'text': ['选择珠江花园的原因就是方便,有电动扶梯直接到达海边,周围餐馆、食廊、商场、超市、摊位一应俱全。酒店装修一般,但还算整洁。 泳池在大堂的屋顶,因此很小,不过女儿倒是喜欢。 包的早餐是西式的,还算丰富。 服务吗,一般', '15.4寸笔记本的键盘确实爽,基本跟台式机差不多了,蛮喜欢数字小键盘,输数字特方便,样子也很美观,做工也相当不错', '房间太小。其他的都一般。。。。。。。。。']}

    加载测试集
    my_dataset_test---> Dataset({
    features: ['label', 'text'],
    num_rows: 1200
    })
    {'label': [1, 0, 0], 'text': ['这个宾馆比较陈旧了,特价的房间也很一般。总体来说一般', '怀着十分激动的心情放映,可是看着看着发现,在放映完毕后,出现一集米老鼠的动画片!开始还怀疑是不是赠送的个别现象,可是后来发现每张DVD后面都有!真不知道生产商怎么想的,我想看的是猫和老鼠,不是米老鼠!如果厂家是想赠送的话,那就全套米老鼠和唐老鸭都赠送,只在每张DVD后面添加一集算什么??简直是画蛇添足!!', '还稍微重了点,可能是硬盘大的原故,还要再轻半斤就好了。其他要进一步验证。贴的几种膜气泡较多,用不了多久就要更换了,屏幕膜稍好点,但比没有要强多了。建议配赠几张膜让用用户自己贴。']}

    加载验证集
    my_dataset_validation---> Dataset({
    features: ['label', 'text'],
    num_rows: 1200
    })
    {'label': [1, 1, 0], 'text': ['這間酒店環境和服務態度亦算不錯,但房間空間太小不宣容納太大件行李且房間格調還可以~~ 中餐廳的廣東點心不太好吃要改善之但算價錢平宜可接受~~ 西餐廳格調都很好但吃的味道一般且令人等得太耐了要改善之~~', '<荐书> 推荐所有喜欢<红楼>的红迷们一定要收藏这本书,要知道当年我听说这本书的时候花很长时间去图书馆找和借都没能如愿,所以这次一看到当当有,马上买了,红迷们也要记得备货哦!', '商品的不足暂时还没发现,京东的订单处理速度实在.......周二就打包完成,周五才发货...']}

  • 通过huggingface的datasets工具,加载代码如下

    def dm_file2dataset():

    复制代码
      # 实例化数据源对象my_dataset_train
      print('\n加载训练集')
      my_dataset_train = load_dataset('csv', data_files='./mydata1/train.csv', split='train')
      print('dataset_train--->', my_dataset_train)
      print(my_dataset_train[0:3])
    
      # 实例化数据源对象my_dataset_test
      print('\n加载测试集')
      my_dataset_test = load_dataset('csv', data_files='./mydata1/test.csv', split='train')
      print('my_dataset_test--->', my_dataset_test)
      print(my_dataset_test[0:3])
    
      print('\n加载验证集')
      # 实例化数据源对象my_dataset_train
      my_dataset_validation = load_dataset('csv', data_files='./mydata1/validation.csv', split="train")
      print('my_dataset_validation--->', my_dataset_validation)
      print(my_dataset_validation[0:3])
  • 导入工具包和辅助工具实例化对象

    导入工具包

    import torch
    from datasets import load_dataset
    from transformers import BertTokenizer, BertModel
    from transformers import AdamW
    import time

    加载字典和分词工具 实例化分词工具

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

    加载预训练模型 实例化预训练模型

    my_model_pretrained = BertModel.from_pretrained('bert-base-chinese')

3 数据预处理[¶](#3 数据预处理¶)

对持久化文件中数据进行处理,以满足模型训练要求。

  • 数据预处理和相关测试函数
复制代码
# 数据集处理自定义函数
def collate_fn1(data):

    # data传过来的数据是list eg: 批次数8,8个字典
    # [{'text':'xxxx','label':0} , {'text':'xxxx','label':1}, ...]
    sents = [i['text'] for i in data]
    labels = [i['label'] for i in data]

    # 编码text2id 对多句话进行编码用batch_encode_plus函数
    data = my_tokenizer.batch_encode_plus(batch_text_or_text_pairs=sents,
                                   truncation=True,
                                   padding='max_length',
                                   max_length=500,
                                   return_tensors='pt',
                                   return_length=True)

    # input_ids:编码之后的数字
    # attention_mask:是补零的位置是0,其他位置是1
    input_ids = data['input_ids']
    attention_mask = data['attention_mask']
    token_type_ids = data['token_type_ids']
    labels = torch.LongTensor(labels)

    # 返回text2id信息 掩码信息 句子分段信息 标签y
    return input_ids, attention_mask, token_type_ids, labels


# 测试数据
def dm01_test_dataset():

    # 实例化数据源 通过训练文件
    dataset_train = load_dataset('csv', data_files='./mydata1/train.csv', split="train")
    print('dataset_train--->', dataset_train)

    # 实例化数据迭代器 mydataloader
    mydataloader = torch.utils.data.DataLoader(dataset_train,
                                               batch_size=8,
                                               collate_fn=collate_fn1,
                                               shuffle=True,
                                               drop_last=True)
    print('mydataloader--->', len(mydataloader))

    # 调整数据迭代器对象数据返回格式
    for i, (input_ids, attention_mask, token_type_ids, labels) in enumerate(mydataloader):
        print(len(mydataloader))
        print(input_ids.shape, attention_mask.shape, token_type_ids.shape, labels)
        # 打印句子text2id后的信息
        print('input_ids', input_ids)
        # 打印句子attention掩码信息
        print('attention_mask', attention_mask)
        # 打印句子分段信息
        print('token_type_ids', token_type_ids)
        # 打印目标y信息
        print('labels', labels)
        break
  • 程序运行效果
复制代码
# 显示训练集字段和样本数目
dataset_train---> Dataset({
    features: ['label', 'text'],
    num_rows: 9600
})
mydataloader---> 1200

# 显示处理后送给模型的数据信息
torch.Size([8, 500]) torch.Size([8, 500]) torch.Size([8, 500]) tensor([1, 1, 0, 0, 1, 0, 0, 0])

# 句子text2id后的信息
input_ids tensor([[ 101, 6848, 2885,  ...,    0,    0,    0],
        [ 101, 8115,  119,  ...,    0,    0,    0],
        [ 101, 2791, 7313,  ...,    0,    0,    0],
        ...,
        [ 101, 3322, 1690,  ...,    0,    0,    0],
        [ 101, 1457, 1457,  ...,    0,    0,    0],
        [ 101, 6821, 3315,  ...,    0,    0,    0]])

# 句子注意力机制掩码信息
attention_mask tensor([[1, 1, 1,  ..., 0, 0, 0],
        [1, 1, 1,  ..., 0, 0, 0],
        [1, 1, 1,  ..., 0, 0, 0],
        ...,
        [1, 1, 1,  ..., 0, 0, 0],
        [1, 1, 1,  ..., 0, 0, 0],
        [1, 1, 1,  ..., 0, 0, 0]])

# 句子分段信息
token_type_ids tensor([[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, 0, 0],
        [0, 0, 0,  ..., 0, 0, 0]])

# 句子的标签信息
labels tensor([1, 1, 0, 0, 1, 0, 0, 0])

4 自定义下游任务网络模型[¶](#4 自定义下游任务网络模型¶)

自定义单层的全连接网络作为微调网络。根据实际经验, 自定义的微调网络参数总数应大于0.5倍的训练数据量, 小于10倍的训练数据量, 这样有助于模型在合理的时间范围内收敛

  • 自定义下游任务网络模型
复制代码
# 定义下游任务模型
class MyModel(torch.nn.Module):
    def __init__(self):
        super().__init__()

        # 定义全连接层
        self.fc = torch.nn.Linear(768, 2)

    def forward(self, input_ids, attention_mask, token_type_ids):

        # 预训练模型不训练 只进行特征抽取 [8,500] ---> [8,768]
        with torch.no_grad():
            out = my_model_pretrained(input_ids=input_ids,
                       attention_mask=attention_mask,
                       token_type_ids=token_type_ids)

        # 下游任务模型训练 数据经过全连接层 [8,768] --> [8,2]
        out = self.fc(out.last_hidden_state[:, 0])

        # 数据进行softmax归一化 分类概率值
        out = out.softmax(dim=1)

        return out
  • 模型测试
复制代码
# 下游任务模型输入和输出测试
def dm02_test_mymodel():

    # 实例化数据源 通过训练文件
    dataset_train = load_dataset('csv', data_files='./mydata1/train.csv', split="train")
    # print('dataset_train--->', dataset_train)

    # 实例化数据迭代器 mydataloader
    mydataloader = torch.utils.data.DataLoader(dataset_train,
                                               batch_size=8,
                                               collate_fn=collate_fn1,
                                               shuffle=False,
                                               drop_last=True)
    # print('mydataloader--->', len(mydataloader))

    # 实例化下游任务模型
    mymodel = MyModel()
    print('mymodel--->', mymodel)

    # 调整数据迭代器对象数据返回格式
    for i, (input_ids, attention_mask, token_type_ids, labels) in enumerate(mydataloader):
        print(len(mydataloader))
        # print(input_ids.shape, attention_mask.shape, token_type_ids.shape, labels)

        # 数据送给模型
        y_out = mymodel(input_ids, attention_mask, token_type_ids)
        print('y_out---->', y_out.shape, y_out)
        break
  • 输出效果
复制代码
# 模型信息打印
mymodel---> MyModel(
  (fc): Linear(in_features=768, out_features=2, bias=True)
)

# 模型运算后分类结果展示
y_out----> torch.Size([8, 2]) tensor([[0.4062, 0.5938],
        [0.2788, 0.7212],
        [0.3671, 0.6329],
        [0.2496, 0.7504],
        [0.2995, 0.7005],
        [0.2566, 0.7434],
        [0.2537, 0.7463],
        [0.3832, 0.6168]], grad_fn=<SoftmaxBackward>)

5 模型训练[¶](#5 模型训练¶)

复制代码
# 模型训练
def dm03_train_model():

    # 实例化下游任务模型my_model
    my_model = MyModel()

    # 实例化优化器my_optimizer
    my_optimizer = AdamW(my_model.parameters(), lr=5e-4)

    # 实例化损失函数my_criterion
    my_criterion = torch.nn.CrossEntropyLoss()

    # 实例化数据源对象my_dataset_train
    my_dataset_train = load_dataset('csv', data_files='./mydata1/train.csv', split="train")
    print('dataset_train--->', my_dataset_train)

    # 不训练预训练模型 只让预训练模型计算数据特征 不需要计算梯度
    for param in my_model_pretrained.parameters():
        param.requires_grad_(False)

    # 设置训练参数
    epochs = 3

    # 设置模型为训练模型
    my_model.train()

    # 外层for循环 控制轮数
    for eporch_idx in range(epochs):

        # 每次轮次开始计算时间
        starttime = (int)(time.time())
        # 实例化数据迭代器对象my_dataloader
        my_dataloader = torch.utils.data.DataLoader(my_dataset_train,
                                                    batch_size=8,
                                                    collate_fn=collate_fn1,
                                                    shuffle=True,
                                                    drop_last=True)

        # 内层for循环 控制迭代次数
        for i, (input_ids, attention_mask, token_type_ids, labels) in enumerate(my_dataloader, start=1):

            # 给模型喂数据 [8,500] --> [8,2]
            my_out = my_model(input_ids=input_ids,
                        attention_mask=attention_mask,
                        token_type_ids=token_type_ids)

            # 计算损失
            my_loss = my_criterion(my_out, labels)

            # 梯度清零
            my_optimizer.zero_grad()

            # 反向传播
            my_loss.backward()

            # 梯度更新
            my_optimizer.step()

            # 每5次迭代 算一下准确率
            if i % 5 == 0:
                out = my_out.argmax(dim=1) # [8,2] --> (8,)
                accuracy = (out == labels).sum().item() / len(labels)
                print('轮次:%d 迭代数:%d 损失:%.6f 准确率%.3f 时间%d' \
                      %(eporch_idx, i, my_loss.item(), accuracy, (int)(time.time())-starttime))

        # 每个轮次保存模型
        torch.save(my_model.state_dict(), './my_model_%d.bin' % (eporch_idx + 1))
  • 模型训练效果输出
复制代码
轮次:0 迭代数:5 损失:0.735494 准确率0.250 时间40
轮次:0 迭代数:10 损失:0.614211 准确率0.875 时间81
轮次:0 迭代数:15 损失:0.635408 准确率0.750 时间119
轮次:0 迭代数:20 损失:0.575522 准确率1.000 时间157
轮次:0 迭代数:25 损失:0.661196 准确率0.625 时间196
轮次:0 迭代数:30 损失:0.546462 准确率0.875 时间234
轮次:0 迭代数:35 损失:0.609517 准确率0.875 时间272
轮次:0 迭代数:40 损失:0.529246 准确率1.000 时间310
轮次:0 迭代数:45 损失:0.474820 准确率1.000 时间348
轮次:0 迭代数:50 损失:0.540127 准确率0.875 时间387
轮次:0 迭代数:55 损失:0.575326 准确率0.625 时间426
# 从以上的训练输出效果来看,预训练模型是十分强大的,只需要短短的几次迭代,就可以让准确率上88%

6 模型评估[¶](#6 模型评估¶)

复制代码
# 模型测试
def dm04_evaluate_model():

    # 实例化数据源对象my_dataset_test
    print('\n加载测试集')
    my_dataset_test = load_dataset('csv', data_files='./mydata1/test.csv', split='train')
    print('my_dataset_test--->', my_dataset_test)
    # print(my_dataset_test[0:3])

    # 实例化下游任务模型my_model
    path = './my_model_3.bin'
    my_model = MyModel()
    my_model.load_state_dict(torch.load(path))
    print('my_model-->', my_model)

    # 设置下游任务模型为评估模式
    my_model.eval()

    # 设置评估参数
    correct = 0
    total = 0

    # 实例化化dataloader
    my_loader_test = torch.utils.data.DataLoader(my_dataset_test,
                                              batch_size=8,
                                              collate_fn=collate_fn1,
                                              shuffle=True,
                                              drop_last=True)

    # 给模型送数据 测试预测结果
    for i, (input_ids, attention_mask, token_type_ids,
            labels) in enumerate(my_loader_test):

        # 预训练模型进行特征抽取
        with torch.no_grad():
            my_out = my_model(input_ids=input_ids,
                        attention_mask=attention_mask,
                        token_type_ids=token_type_ids)

        # 贪心算法求预测结果
        out = my_out.argmax(dim = 1)

        # 计算准确率
        correct += (out == labels).sum().item()
        total += len(labels)

        # 每5次迭代打印一次准确率
        if i % 5 == 0:
            print(correct / total, end=" ")
            print(my_tokenizer.decode(input_ids[0], skip_special_tokens=True), end=" ")
            print('预测值 真实值:', out[0].item(), labels[0].item())
  • 输出效果:
复制代码
0.875 我 没 有 收 到 这 本 书 , 我 明 明 和 另 外 一 本 一 起 买 的 , 服 务 也 不 好 。 至 少 应 该 让 我 知 道 这 个 情 况 在 ! 预测值 真实值: 0 0
0.8125 也 许 这 不 算 一 个 很 好 的 理 由, 但 是 我 之 所 以 喜 欢 读 书 而 不 是 看 网 上 的 资 料 什 么 的, 就 是 喜 欢 闻 着 书 香. 这 本 书 可 能 是 印 刷 的 油 墨 不 好 还 是 什 么 原 因, 感 觉 臭 臭 的 不 好 闻. 里 面 是 一 些 关 于 中 式 英 语 的 小 趣 闻, 有 些 小 乐 趣, 但 感 觉 对 于 有 浓 重 中 式 思 维 习 惯 说 英 说 的 人 来 说 才 比 较 有 点 用 处. 预测值 真实值: 1 0
0.8409090909090909 1. 有 急 事 出 去 , 要 们 童 叫 出 租 车 , 他 们 就 叫 酒 店 里 的 黑 车 , 价 格 是 普 通 出 租 价 的 两 倍 。 你 提 出 不 要 酒 店 的 黑 车 时 , 他 们 就 告 诉 你 外 面 拦 不 到 出 租 车 , 我 们 自 己 走 出 去 时 , 外 面 出 租 车 随 时 可 以 拦 到 。 住 店 期 间 不 止 一 次 发 生 。 预测值 真实值: 0 0
0.828125 这 本 书 中 的 图 片 很 让 人 触 动 , 比 如 为 劳 拉 祈 祷 的 校 友 , 惠 特 尼 和 马 特 的 笑 容 , 很 动 人 。 预测值 真实值: 1 1
0.8511904761904762 这 是 我 住 过 的 最 差 的 酒 店 , 房 间 气 味 难 闻 , 刚 打 了 灭 蚊 药 水 , 换 了 三 个 房 间 还 是 如 此 , 服 务 员 说 : 住 久 了 就 习 惯 了 , 每 个 宾 馆 都 有 自 己 的 味 道 。 考 ! 我 又 不 是 来 体 验 生 活 的 。 周 围 环 境 复 杂 , 脏 乱 差 。 预测值 真实值: 0 0

3 迁移学习-中文填空[¶](#3 迁移学习-中文填空¶)

1 任务介绍[¶](#1 任务介绍¶)

复制代码
# 输入一句话,MASK一个字,训练模型进行填空
[CLS] 选 择 珠 江 花 园 的 原 因 就 是 方 便 , 有 [MASK] 动 扶 梯 直 接 到 达 海 边 , 周 围 餐 馆 [SEP]
# 本句MASK的字为"电"

2 数据介绍[¶](#2 数据介绍¶)

  • 数据文件有三个train.csv,test.csv,validation.csv,数据样式都是一样的。

    label,text
    1,选择珠江花园的原因就是方便,有电动扶梯直接到达海边,周围餐馆、食廊、商场、超市、摊位一应俱全。酒店装修一般,但还算整洁。 泳池在大堂的屋顶,因此很小,不过女儿倒是喜欢。 包的早餐是西式的,还算丰富。 服务吗,一般
    1,15.4寸笔记本的键盘确实爽,基本跟台式机差不多了,蛮喜欢数字小键盘,输数字特方便,样子也很美观,做工也相当不错
    0,房间太小。其他的都一般。。。。。。。。。
    0,"1.接电源没有几分钟,电源适配器热的不行. 2.摄像头用不起来. 3.机盖的钢琴漆,手不能摸,一摸一个印. 4.硬盘分区不好办."
    1,"今天才知道这书还有第6卷,真有点郁闷:为什么同一套书有两种版本呢?当当网是不是该跟出版社商量商量,单独出个第6卷,让我们的孩子不会有所遗憾。"

  • 导入工具包和辅助工具实例化对象

    import torch
    from torch.utils.data import DataLoader
    from datasets import load_dataset
    from transformers import BertTokenizer, BertModel
    from transformers import AdamW
    import time

    加载字典和分词工具

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

    加载预训练模型

    my_model_pretrained = BertModel.from_pretrained('bert-base-chinese')

3 数据预处理[¶](#3 数据预处理¶)

对持久化文件中数据进行处理,以满足模型训练要求。

  • 数据预处理和相关测试函数
复制代码
# 数据集处理自定义函数
def collate_fn2(data):
    sents = [i['text'] for i in data]

    # 文本数值化
    data = my_tokenizer.batch_encode_plus(batch_text_or_text_pairs=sents,
                                   truncation=True,
                                   padding='max_length',
                                   max_length=32,
                                   return_tensors='pt',
                                   return_length=True)

    # input_ids 编码之后的数字
    # attention_mask 是补零的位置是0,其他位置是1
    input_ids = data['input_ids']
    attention_mask = data['attention_mask']
    token_type_ids = data['token_type_ids']

    # 把第16个词固定替换为mask
    labels = input_ids[:, 16].reshape(-1).clone()  # 取出数据8句话 在第16个位置clone出来 做标签
    input_ids[:, 16] = my_tokenizer.get_vocab()[my_tokenizer.mask_token]
    labels = torch.LongTensor(labels)

    # tmpa = input_ids[:, 16]
    # print('tmpa--->', tmpa, tmpa.shape)       # torch.Size([8]
    # print('labels-->', labels.shape, labels)  # torch.Size([8]

    return input_ids, attention_mask, token_type_ids, labels


# 数据源 数据迭代器 测试
def dm01_test_dataset():

    # 生成数据源dataset对象
    dataset_train_tmp = load_dataset('csv', data_files='./mydata1/train.csv', split="train")
    # print('dataset_train_tmp--->', dataset_train_tmp)

    # 按照条件过滤数据源对象
    my_dataset_train = dataset_train_tmp.filter(lambda x: len(x['text']) > 32)
    # print('my_dataset_train--->', my_dataset_train)
    # print('my_dataset_train[0:3]-->', my_dataset_train[0:3])

    # 通过dataloader进行迭代
    mydataloader = DataLoader(my_dataset_train, batch_size=8, collate_fn=collate_fn2, shuffle=True, drop_last=True)
    print('mydataloader--->', mydataloader)

    # 不训练,不需要计算梯度
    for param in my_model_pretrained.parameters():
        param.requires_grad_(False)

    # 调整数据迭代器对象数据返回格式
    for i, (input_ids, attention_mask, token_type_ids, labels) in enumerate(mydataloader):

        print(input_ids.shape, attention_mask.shape, token_type_ids.shape, labels)

        print('\n第1句mask的信息')
        print(my_tokenizer.decode(input_ids[0]))
        print(my_tokenizer.decode(labels[0]))

        print('\n第2句mask的信息')
        print(my_tokenizer.decode(input_ids[1]))
        print(my_tokenizer.decode(labels[1]))
        break
  • 程序运行效果
复制代码
mydataloader---> <torch.utils.data.dataloader.DataLoader object at 0x7fbe28611cd0>
torch.Size([8, 32]) torch.Size([8, 32]) torch.Size([8, 32]) tensor([3160, 1905, 8152, 7415, 7231, 1331, 2360, 5831])

第1句mask的信息
[CLS] 文 章 总 体 不 错 , 不 过 太 多 的 历 史 资 [MASK] 重 复 , 感 觉 没 有 步 步 好 看 了 , 作 [SEP]
料

第2句mask的信息
[CLS] 第 一 散 热 一 般 。 。 。 用 了 一 会 托 腕 [MASK] 就 发 烫 了? 第 二 液 晶 屏 和 主 机 部 [SEP]
处
迁移学习 中文填空 End

4 自定义下游任务网络模型[¶](#4 自定义下游任务网络模型¶)

  • 自定义下游任务网络模型
复制代码
# 定义下游任务模型
class MyModel(torch.nn.Module):
    def __init__(self):
        super().__init__()
        # 定义全连接层
        self.decoder = torch.nn.Linear(768, my_tokenizer.vocab_size, bias=False)
        # 设置全连接层偏置为零
        self.decoder.bias = torch.nn.Parameter(torch.zeros(my_tokenizer.vocab_size))

    def forward(self, input_ids, attention_mask, token_type_ids):
        # 预训练模型不进行训练
        with torch.no_grad():
            out = my_model_pretrained(input_ids=input_ids,
                             attention_mask=attention_mask,
                             token_type_ids=token_type_ids)

        # 下游任务进行训练 形状[8,768] ---> [8, 21128]
        out = self.decoder(out.last_hidden_state[:, 16])

        # 返回
        return out
  • 模型测试
复制代码
# 模型输入和输出测试
def dm02_test_mymodel():
    # 生成数据源dataset对象
    dataset_train_tmp = load_dataset('csv', data_files='./mydata1/train.csv', split="train")
    # print('dataset_train_tmp--->', dataset_train_tmp)

    # 按照条件过滤数据源对象
    my_dataset_train = dataset_train_tmp.filter(lambda x: len(x['text']) > 32)
    # print('my_dataset_train--->', my_dataset_train)
    # print('my_dataset_train[0:3]-->', my_dataset_train[0:3])

    # 通过dataloader进行迭代
    mydataloader = DataLoader(my_dataset_train, batch_size=8, collate_fn=collate_fn2, shuffle=True, drop_last=True)
    print('mydataloader--->', mydataloader)

    # 不训练,不需要计算梯度
    for param in my_model_pretrained.parameters():
        param.requires_grad_(False)

    # 实例化下游任务模型
    mymodel = MyModel()

    # 调整数据迭代器对象数据返回格式
    for i, (input_ids, attention_mask, token_type_ids, labels) in enumerate(mydataloader):

        print(input_ids.shape, attention_mask.shape, token_type_ids.shape, labels)

        print('\n第1句mask的信息')
        print(my_tokenizer.decode(input_ids[0]))
        print(my_tokenizer.decode(labels[0]))

        print('\n第2句mask的信息')
        print(my_tokenizer.decode(input_ids[1]))
        print(my_tokenizer.decode(labels[1]))

        # 给模型喂数据 [8,768] ---> [8,21128] 填空就是分类 21128个单词中找一个单词
        myout = mymodel(input_ids, attention_mask, token_type_ids)
        print('myout--->', myout.shape, myout)
        break
  • 输出效果
复制代码
mydataloader---> <torch.utils.data.dataloader.DataLoader object at 0x7fd7c0765c10>
torch.Size([8, 32]) torch.Size([8, 32]) torch.Size([8, 32]) tensor([ 702, 6381,  857, 8024, 2218, 1961, 3175, 2141])

第1句mask的信息
[CLS] 很 重, 背 出 去 很 累. 好 象 屏 幕 上 有 [MASK] 亮 点, 不 过 很 小 ( 屏 本 来 就 很 小 [SEP]
个

第2句mask的信息
[CLS] 我 于 11 月 22 日 订 购 了 《 杜 拉 拉 升 职 [MASK] 》 并 已 通 过 银 行 付 款 , 为 什 么 订 [SEP]
记
myout---> torch.Size([8, 21128]) tensor([[-0.3201,  0.3877,  0.1041,  ...,  0.2262,  0.5397,  0.5053],
        [ 0.0626,  0.1335,  0.7057,  ..., -0.6277, -0.2287, -0.0532],
        [ 0.3807,  0.2024,  0.0514,  ..., -0.0113,  0.3084,  0.4678],
        ...,
        [ 0.3452,  0.1774,  0.0127,  ..., -0.3960,  0.2417, -0.0260],
        [-0.4155,  0.2038,  0.2512,  ..., -0.4112, -0.1052,  0.3574],
        [ 0.3464,  0.3439,  0.6628,  ..., -0.1706,  0.1020,  0.4141]],
       grad_fn=<AddmmBackward>)

5 模型训练[¶](#5 模型训练¶)

复制代码
# 模型训练 - 填空
def dm03_train_model():

    # 实例化数据源对象my_dataset_train
    dataset_train_tmp = load_dataset('csv', data_files='./mydata1/train.csv', split="train")
    my_dataset_train = dataset_train_tmp.filter(lambda x: len(x['text']) > 32)
    print('my_dataset_train--->', my_dataset_train)

    # 实例化下游任务模型my_model
    my_model = MyModel()

    # 实例化优化器my_optimizer
    my_optimizer = AdamW(my_model.parameters(), lr=5e-4)

    # 实例化损失函数my_criterion
    my_criterion = torch.nn.CrossEntropyLoss()

    # 不训练预训练模型 只让预训练模型计算数据特征 不需要计算梯度
    for param in my_model_pretrained.parameters():
        param.requires_grad_(False)

    # 设置训练参数
    epochs = 3

    # 设置模型为训练模型
    my_model.train()

    # 外层for循环 控制轮数
    for eporch_idx in range(epochs):

        # 实例化数据迭代器对象my_dataloader
        my_dataloader = torch.utils.data.DataLoader(my_dataset_train,
                                                    batch_size=8,
                                                    collate_fn=collate_fn2,
                                                    shuffle=True,
                                                    drop_last=True)
        starttime = (int)(time.time())
        # 内层for循环 控制迭代次数
        for i, (input_ids, attention_mask, token_type_ids, labels) in enumerate(my_dataloader, start=1):
            # 给模型喂数据 [8,32] --> [8,21128]
            my_out = my_model(input_ids=input_ids,
                        attention_mask=attention_mask,
                        token_type_ids=token_type_ids)

            # 计算损失
            my_loss = my_criterion(my_out, labels)

            # 梯度清零
            my_optimizer.zero_grad()

            # 反向传播
            my_loss.backward()

            # 梯度更新
            my_optimizer.step()

            # 每5次迭代 算一下准确率
            if i % 20 == 0:
                out = my_out.argmax(dim=1) # [8,21128] --> (8,)
                accuracy = (out == labels).sum().item() / len(labels)
                print('轮次:%d 迭代数:%d 损失:%.6f 准确率%.3f 时间%d' \
                      %(eporch_idx, i, my_loss.item(), accuracy, (int)(time.time())-starttime))

        # 每个轮次保存模型
        torch.save(my_model.state_dict(), './my_model_mask_%d.bin' % (eporch_idx + 1))
  • 模型训练效果输出
复制代码
轮次:2 迭代数:680 损失:0.324525 准确率0.875 时间370
轮次:2 迭代数:700 损失:0.776103 准确率0.750 时间381
轮次:2 迭代数:720 损失:0.681674 准确率0.875 时间392
轮次:2 迭代数:740 损失:0.654384 准确率0.750 时间402
轮次:2 迭代数:760 损失:0.612616 准确率0.875 时间413
轮次:2 迭代数:780 损失:0.918874 准确率0.625 时间424
轮次:2 迭代数:800 损失:0.640087 准确率0.750 时间435
轮次:2 迭代数:820 损失:0.410612 准确率1.000 时间446
轮次:2 迭代数:840 损失:0.395016 准确率1.000 时间457
轮次:2 迭代数:860 损失:0.313001 准确率0.875 时间468
轮次:2 迭代数:880 损失:1.534165 准确率0.750 时间479
轮次:2 迭代数:900 损失:0.384014 准确率0.875 时间490
轮次:2 迭代数:920 损失:0.386283 准确率1.000 时间501
轮次:2 迭代数:940 损失:1.168535 准确率0.750 时间512
轮次:2 迭代数:960 损失:0.568617 准确率0.875 时间522
轮次:2 迭代数:980 损失:1.178597 准确率0.750 时间533
轮次:2 迭代数:1000 损失:0.534274 准确率0.875 时间544
轮次:2 迭代数:1020 损失:0.371301 准确率1.000 时间555
轮次:2 迭代数:1040 损失:0.106059 准确率1.000 时间566
轮次:2 迭代数:1060 损失:1.153722 准确率0.750 时间577
轮次:2 迭代数:1080 损失:0.352150 准确率1.000 时间588
轮次:2 迭代数:1100 损失:1.242567 准确率0.625 时间598
轮次:2 迭代数:1120 损失:0.548795 准确率0.875 时间609
# 只需要3个轮次,就可以填空准确率达到88%以上

6 模型评估[¶](#6 模型评估¶)

复制代码
# 模型测试:填空
def dm04_evaluate_model():

    # 实例化数据源对象my_dataset_test
    print('\n加载测试集')
    my_dataset_tmp = load_dataset('csv', data_files='./mydata1/test.csv', split='train')
    my_dataset_test = my_dataset_tmp.filter(lambda x: len(x['text']) > 32)
    print('my_dataset_test--->', my_dataset_test)
    # print(my_dataset_test[0:3])

    # 实例化下游任务模型my_model
    path = './my_model_mask_3.bin'
    my_model = MyModel()
    my_model.load_state_dict(torch.load(path))
    print('my_model-->', my_model)

    # 设置下游任务模型为评估模式
    my_model.eval()

    # 设置评估参数
    correct = 0
    total = 0

    # 实例化化dataloader
    my_loader_test = torch.utils.data.DataLoader(my_dataset_test,
                                              batch_size=8,
                                              collate_fn=collate_fn2,
                                              shuffle=True,
                                              drop_last=True)

    # 给模型送数据 测试预测结果
    for i, (input_ids, attention_mask, token_type_ids,
            labels) in enumerate(my_loader_test):

        with torch.no_grad():
            my_out = my_model(input_ids=input_ids,
                        attention_mask=attention_mask,
                        token_type_ids=token_type_ids)

        out = my_out.argmax(dim=1)
        correct += (out == labels).sum().item()
        total += len(labels)

        if i % 25 == 0:
            print(i+1, my_tokenizer.decode(input_ids[0]))
            print('预测值:', my_tokenizer.decode(out[0]), '\t真实值:', my_tokenizer.decode(labels[0]))
            print(correct / total)
  • 输出效果:
复制代码
26 [CLS] 大 堂 是 喧 闹 的 , 房 间 地 毯 是 脏 的 , [MASK] 室 淋 浴 龙 头 是 坏 的 , 毛 巾 是 黄 颜 [SEP]
预测值: 寝  真实值: 浴
0.62
51 [CLS] 外 观 漂 亮 , 13 寸 黄 金 比 例 , 悬 浮 键 [MASK] 设 计 , 噪 音 很 低 , 和 我 公 司 发 的 [SEP]
预测值: 盘  真实值: 盘
0.6625
76 [CLS] 某 些 酒 店 人 员 对 待 顾 客 不 诚 恳 。 说 [MASK] 你 换 房 间 , 骗 你 说 是 价 钱 贵 的 房 [SEP]
预测值: 让  真实值: 给
0.68
101 [CLS] 看 这 本 书 有 种 感 觉 就 是 作 者 在 苦 口 [MASK] 心 的 告 戒 读 者 如 何 如 何 , 为 了 证 [SEP]
预测值: 婆  真实值: 婆
0.6775
126 [CLS] 房 间 很 舒 适 也 很 干 净 , 周 围 虽 然 不 [MASK] 闹 但 很 安 静 , 离 虹 桥 机 场 很 静 , [SEP]
预测值: 吵  真实值: 热
0.681

4 迁移学习-微调脚本中文分类[¶](#4 迁移学习-微调脚本中文分类¶)

1 数据介绍[¶](#1 数据介绍¶)

  • 使用文本二分类的任务类型SST-2的微调脚本微调中文预训练模型, 后接带有分类输出头的预定义网络输出结果. 目标是判断句子的情感倾向

  • 准备中文酒店评论的情感分析语料, 语料样式与SST-2数据集相同, 标签0代表差评, 标签1好评

  • 语料存放在与glue_data/同级目录cn_data/下, 其中的SST-2目录包含train.csv和dev.csv

  • train.csv

    sentence label
    早餐不好,服务不到位,晚餐无西餐,早餐晚餐相同,房间条件不好,餐厅不分吸烟区.房间不分有无烟房. 0
    去的时候 ,酒店大厅和餐厅在装修,感觉大厅有点挤.由于餐厅装修本来该享受的早饭,也没有享受(他们是8点开始每个房间送,但是我时间来不及了)不过前台服务员态度好! 1
    有很长时间没有在西藏大厦住了,以前去北京在这里住的较多。这次住进来发现换了液晶电视,但网络不是很好,他们自己说是收费的原因造成的。其它还好。 1
    非常好的地理位置,住的是豪华海景房,打开窗户就可以看见栈桥和海景。记得很早以前也住过,现在重新装修了。总的来说比较满意,以后还会住 1
    交通很方便,房间小了一点,但是干净整洁,很有香港的特色,性价比较高,推荐一下哦 1
    酒店的装修比较陈旧,房间的隔音,主要是卫生间的隔音非常差,只能算是一般的 0
    酒店有点旧,房间比较小,但酒店的位子不错,就在海边,可以直接去游泳。8楼的海景打开窗户就是海。如果想住在热闹的地带,这里不是一个很好的选择,不过威海城市真的比较小,打车还是相当便宜的。晚上酒店门口出租车比较少。 1
    位置很好,走路到文庙、清凉寺5分钟都用不了,周边公交车很多很方便,就是出租车不太爱去(老城区路窄爱堵车),因为是老宾馆所以设施要陈旧些, 1
    酒店设备一般,套房里卧室的不能上网,要到客厅去。 0

  • dev.csv

    sentence label
    房间里有电脑,虽然房间的条件略显简陋,但环境、服务还有饭菜都还是很不错的。如果下次去无锡,我还是会选择这里的。 1
    我们是5月1日通过携程网入住的,条件是太差了,根本达不到四星级的标准,所有的东西都很陈旧,卫生间水龙头用完竟关不上,浴缸的漆面都掉了,估计是十年前的四星级吧,总之下次是不会入住了。 0
    离火车站很近很方便。住在东楼标间,相比较在九江住的另一家酒店,房间比较大。卫生间设施略旧。服务还好。10元中式早餐也不错,很丰富,居然还有青菜肉片汤。 1
    坐落在香港的老城区,可以体验香港居民生活,门口交通很方便,如果时间不紧,坐叮当车很好呀!周围有很多小餐馆,早餐就在中远后面的南北嚼吃的,东西很不错。我们定的大床房,挺安静的,总体来说不错。前台结账没有银联! 1
    酒店前台服务差,对待客人不热情。号称携程没有预定。感觉是客人在求他们,我们一定得住。这样的宾馆下次不会入住! 0
    价格确实比较高,而且还没有早餐提供。 1
    是一家很实惠的酒店,交通方便,房间也宽敞,晚上没有电话骚扰,住了两次,有一次住501房间,洗澡间排水不畅通,也许是个别问题.服务质量很好,刚入住时没有调好宽带,服务员很快就帮忙解决了. 1
    位置非常好,就在西街的街口,但是却闹中取静,环境很清新优雅。 1
    房间应该超出30平米,是HK同级酒店中少有的大;重装之后,设备也不错. 1

  • 语料在服务器上存放路径展示

2 运行代码[¶](#2 运行代码¶)

在run_glue.py同级目录执行以下命令

复制代码
# 定义DATA_DIR: 微调数据所在路径, 这里我们使用glue_data中的数据作为微调数据
# export DATA_DIR="/root/data/glue_data" # 目前版本暂不需要

# 定义SAVE_DIR: 模型的保存路径, 我们将模型保存在当前目录的bert_finetuning_test文件中
export SAVE_DIR="./bert_finetuning_sst2_test/"

# 使用python运行微调脚本
# --model_name_or_path: 选择具体的模型或者变体, 中文语料上微调, 选择bert-base-uncased
# --task_name: 它将代表对应的任务类型, 如MRPC代表句子对二分类任务
# --do_train: 使用微调脚本进行训练
# --do_eval: 使用微调脚本进行验证
# --max_seq_length: 输入句子的最大长度, 超过则截断, 不足则补齐
# --learning_rate: 学习率
# --num_train_epochs: 训练轮数
# --output_dir $SAVE_DIR: 训练后的模型保存路径
# --overwrite_output_dir: 再次训练时将清空之前的保存路径内容重新写入

# 注1 虚拟机中执行以下命令耗时较长,建议在有GPU的主机上执行
# 注2 该命令已在虚拟机执行,再次执行会覆盖缓存的模型

python run_glue_forcnsst2.py \
  --task_name sst2 \
  --model_name_or_path bert-base-chinese \
  --do_train \
  --do_eval \
  --train_file "/Users/xxxxxx/transformers/examples/pytorch/text-classification/cn_data/SST-2/train.csv" \
  --validation_file "/Users/xxxxxx/transformers/examples/pytorch/text-classification/cn_data/SST-2/dev.csv" \
  --max_seq_length 128 \
  --learning_rate 2e-5 \
  --num_train_epochs 1.0 \
  --output_dir "bert-base-uncased-finetuning" \
  --overwrite_output_dir

# 注3 本命令已形成mycnsst2.sh shell文件,使用命令直接执行即可。比如: sh mycnnsst2.sh
  • 检验效果
复制代码
# 找到mycnnsst2.sh的文件路径,比如:/Users/xxxxxx/transformers/examples/pytorch/text-classification
(nlp382) xxxxxx@bogon text-classification % sh mycnsst2.sh

# 最终打印模型的验证结果, 准确率高达0.88.

[INFO|trainer.py:2463] 2022-05-15 11:50:30,323 >> ***** Running Evaluation *****
[INFO|trainer.py:2465] 2022-05-15 11:50:30,323 >>   Num examples = 1000
[INFO|trainer.py:2468] 2022-05-15 11:50:30,324 >>   Batch size = 8
100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 125/125 [04:06<00:00,  1.99s/it]05/15/2022 11:54:39 - INFO - datasets.metric - Removing /Users/xxxxxx/.cache/huggingface/metrics/glue/sst2/default_experiment-1-0.arrow
100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 125/125 [04:06<00:00,  1.97s/it]
***** eval metrics *****
  epoch                   =        1.0
  eval_accuracy           =      0.885
  eval_loss               =     0.3328
  eval_runtime            = 0:04:08.99
  eval_samples            =       1000
  eval_samples_per_second =      4.016
  eval_steps_per_second   =      0.502

3 查看模型文件内容[¶](#3 查看模型文件内容¶)

复制代码
-rw-r--r--  1        1390  5 15 11:54 README.md
-rw-r--r--  1         389  5 15 11:54 all_results.json
-rw-r--r--  1         216  5 15 11:54 eval_results.json
-rw-r--r--  1         590  5 15 11:50 trainer_state.json
-rw-r--r--  1         193  5 15 11:50 train_results.json
-rw-r--r--  1        3119  5 15 11:50 training_args.bin
-rw-r--r--  1      439390  5 15 11:50 tokenizer.json
-rw-r--r--  1      109540  5 15 11:50 vocab.txt
-rw-r--r--  1         112  5 15 11:50 special_tokens_map.json
-rw-r--r--  1         322  5 15 11:50 tokenizer_config.json
-rw-r--r--  1   409150611  5 15 11:50 pytorch_model.bin
-rw-r--r--  1        1005  5 15 11:50 config.json

4 使用本地微调模型[¶](#4 使用本地微调模型¶)

复制代码
import torch
from transformers import AutoTokenizer, AutoModelForSequenceClassification, AutoModel

def  dm01_use_model():

    # 0 找到自己预训练模型的路径
    mymodelname = '/Users/xxxx/transformers/examples/pytorch/text-classification/bert-base-uncased-finetuning'
    print(mymodelname)

    # 1 本地加载预训练模型的tokenizer
    tokenizer = AutoTokenizer.from_pretrained(mymodelname)

    # 2 本地加载 预训练模型 带分类模型头
    model = AutoModelForSequenceClassification.from_pretrained(mymodelname)
    # model = AutoModelForSequenceClassification.from_pretrained(model_checkpoint, num_labels=2)

    # 3 默认情况下 加载预训练模型,不带头
    # model2 = AutoModel.from_pretrained(mymodelname)

    text = "早餐不好,服务不到位,晚餐无西餐,早餐晚餐相同,房间条件不好"
    index = tokenizer.encode(text)
    tokens_tensor = torch.tensor([index])

    # 使用评估模式
    with torch.no_grad():
        # 使用模型预测获得结果
        result = model(tokens_tensor)
        print('result.logits--->', result.logits)

    predicted_label = torch.argmax(result.logits).item()
    print('预测标签为>', predicted_label)

    text1 = "房间应该超出30平米,是HK同级酒店中少有的大;重装之后,设备也不错."
    index = tokenizer.encode(text1)
    tokens_tensor = torch.tensor([index])

    # 使用评估模式
    with torch.no_grad():
        # 使用模型预测获得结果
        result = model(tokens_tensor)
        print('result.logits--->', result.logits)

    predicted_label = torch.argmax(result[0]).item()
    print('预测标签为>', predicted_label)
  • 输出效果:
复制代码
输入文本为: 早餐不好,服务不到位,晚餐无西餐,早餐晚餐相同,房间条件不好
预测标签为: 0

输入文本为: 房间应该超出30平米,是HK同级酒店中少有的大;重装之后,设备也不错.
预测标签为: 1

5 小结[¶](#5 小结¶)

  • 学习了迁移学习方式中文分类任务开发
    • 数据预处理、数据源封装、数据迭代器的使用
    • 搭建中文分类任务下游任务模型,并进行模型训练、模型预测
  • 学习了迁移学习方式中文填空任务开发
    • 数据预处理、数据源封装、数据迭代器的使用
    • 搭建中文填空下游任务模型,并进行模型训练、模型预测
  • 学习了迁移学习方式中文句子关系任务开发
    • 数据预处理、数据源封装、数据迭代器的使用
    • 搭建中文句子关系任务模型,并进行模型训练、模型预测

10 NLP中的标准数据集(拓展资料)

学习目标

  • 了解NLP中GLUE标准数据集合的相关知识.
  • 掌握GLUE标准数据集合的下载方式, 数据样式及其对应的任务类型.

1 GLUE数据集合介绍[¶](#1 GLUE数据集合介绍¶)

1.1 数据集合介绍[¶](#1.1 数据集合介绍¶)

GLUE由纽约大学, 华盛顿大学, Google联合推出, 涵盖不同NLP任务类型, 截止至2020年1月其中包括11个子任务数据集, 成为衡量NLP研究发展的衡量标准.

  • CoLA 数据集
  • SST-2 数据集
  • MRPC 数据集
  • STS-B 数据集
  • QQP 数据集
  • MNLI 数据集
  • SNLI 数据集
  • QNLI 数据集
  • RTE 数据集
  • WNLI 数据集
  • diagnostics数据集(官方未完善)

1.2 数据集合路径[¶](#1.2 数据集合路径¶)

数据集在虚拟机/root/data/glue_data下

另外这GLUE的11个数据集的下载地址HuggingFace地址

2 GLUE子数据集的样式及其任务类型[¶](#2 GLUE子数据集的样式及其任务类型¶)

2.1 CoLA数据集文件样式[¶](#2.1 CoLA数据集文件样式¶)

  • 数据集释义:CoLA(The Corpus of Linguistic Acceptability,语言可接受性语料库)纽约大学发布的有关语法的数据集

  • 本质: 是对一个给定句子,判定其是否语法正确的单个句子的**文本二分类任务**.

    • CoLA/
      • dev.tsv
      • original/
      • test.tsv
      • train.tsv
  • 文件样式说明:

  • 在使用中常用到的文件是train.tsv, dev.tsv, test.tsv, 分别代表训练集, 验证集和测试集. 其中train.tsv与dev.tsv数据样式相同, 都是带有标签的数据, 其中test.tsv是不带有标签的数据.

  • train.tsv数据样式:

复制代码
...
gj04    1       She coughed herself awake as the leaf landed on her nose.
gj04    1       The worm wriggled onto the carpet.
gj04    1       The chocolate melted onto the carpet.
gj04    0   *   The ball wriggled itself loose.
gj04    1       Bill wriggled himself loose.
bc01    1       The sinking of the ship to collect the insurance was very devious.
bc01    1       The ship's sinking was very devious.
bc01    0   *   The ship's sinking to collect the insurance was very devious.
bc01    1       The testing of such drugs on oneself is too risky.
bc01    0   *   This drug's testing on oneself is too risky.
...
  • train.tsv数据样式说明:

  • train.tsv中的数据内容共分为4列, 第一列数据, 如gj04, bc01等代表每条文本数据的来源即出版物代号; 第二列数据, 0或1, 代表每条文本数据的语法是否正确, 0代表不正确, 1代表正确; 第三列数据, '', 是作者最初的正负样本标记, 与第二列意义相同, ''表示不正确; 第四列即是被标注的语法使用是否正确的文本句子.

  • test.tsv数据样式:

复制代码
index   sentence
0   Bill whistled past the house.
1   The car honked its way down the road.
2   Bill pushed Harry off the sofa.
3   the kittens yawned awake and played.
4   I demand that the more John eats, the more he pay.
5   If John eats more, keep your mouth shut tighter, OK?
6   His expectations are always lower than mine are.
7   The sooner you call, the more carefully I will word the letter.
8   The more timid he feels, the more people he interviews without asking questions of.
9   Once Janet left, Fred became a lot crazier.
...
  • test.tsv数据样式说明:

  • test.tsv中的数据内容共分为2列, 第一列数据代表每条文本数据的索引; 第二列数据代表用于测试的句子.

  • CoLA数据集的任务类型:

  • 二分类任务

  • 评估指标为: MCC(马修斯相关系数, 在正负样本分布十分不均衡的情况下使用的二分类评估指标)

2.2 SST-2数据集文件样式[¶](#2.2 SST-2数据集文件样式¶)

  • 数据集释义:SST-2(The Stanford Sentiment Treebank,斯坦福情感树库),单句子分类任务,包含电影评论中的句子和它们情感的人类注释.

  • 本质:句子级别的**二分类任务**

    • SST-2/
      - dev.tsv
      - original/
      - test.tsv
      - train.tsv
  • 文件样式说明:

  • 在使用中常用到的文件是train.tsv, dev.tsv, test.tsv, 分别代表训练集, 验证集和测试集. 其中train.tsv与dev.tsv数据样式相同, 都是带有标签的数据, 其中test.tsv是不带有标签的数据.

  • train.tsv数据样式:

复制代码
sentence    label
hide new secretions from the parental units     0
contains no wit , only labored gags     0
that loves its characters and communicates something rather beautiful about human nature    1
remains utterly satisfied to remain the same throughout     0
on the worst revenge-of-the-nerds clichés the filmmakers could dredge up    0
that 's far too tragic to merit such superficial treatment  0
demonstrates that the director of such hollywood blockbusters as patriot games can still turn out a small , personal film with an emotional wallop .    1
of saucy    1
a depressed fifteen-year-old 's suicidal poetry     0
...
  • train.tsv数据样式说明:

  • train.tsv中的数据内容共分为2列, 第一列数据代表具有感情色彩的评论文本; 第二列数据, 0或1, 代表每条文本数据是积极或者消极的评论, 0代表消极, 1代表积极.

  • test.tsv数据样式:

复制代码
index   sentence
0   uneasy mishmash of styles and genres .
1   this film 's relationship to actual tension is the same as what christmas-tree flocking in a spray can is to actual snow : a poor -- if durable -- imitation .
2   by the end of no such thing the audience , like beatrice , has a watchful affection for the monster .
3   director rob marshall went out gunning to make a great one .
4   lathan and diggs have considerable personal charm , and their screen rapport makes the old story seem new .
5   a well-made and often lovely depiction of the mysteries of friendship .
6   none of this violates the letter of behan 's book , but missing is its spirit , its ribald , full-throated humor .
7   although it bangs a very cliched drum at times , this crowd-pleaser 's fresh dialogue , energetic music , and good-natured spunk are often infectious .
8   it is not a mass-market entertainment but an uncompromising attempt by one artist to think about another .
9   this is junk food cinema at its greasiest .
...
  • test.tsv数据样式说明:

    • test.tsv中的数据内容共分为2列, 第一列数据代表每条文本数据的索引; 第二列数据代表用于测试的句子.
  • SST-2数据集的任务类型:

  • 二分类任务

  • 评估指标为: ACC

2.3 MRPC数据集文件样式[¶](#2.3 MRPC数据集文件样式¶)

  • 数据集释义:MRPC(The Microsoft Research Paraphrase Corpus,微软研究院释义语料库),相似性和释义任务,是从在线新闻源中自动抽取句子对语料库,并人工注释句子对中的句子是否在语义上等效。

  • 本质:句子级别的**二分类任务**

    • MRPC/
      - dev.tsv
      - test.tsv
      - train.tsv
      • dev_ids.tsv
      • msr_paraphrase_test.txt
      • msr_paraphrase_train.txt
  • 文件样式说明:

  • 在使用中常用到的文件是train.tsv, dev.tsv, test.tsv, 分别代表训练集, 验证集和测试集. 其中train.tsv与dev.tsv数据样式相同, 都是带有标签的数据, 其中test.tsv是不带有标签的数据.

  • train.tsv数据样式:

复制代码
Quality #1 ID   #2 ID   #1 String   #2 String
1   702876  702977  Amrozi accused his brother , whom he called " the witness " , of deliberately distorting his evidence . Referring to him as only " the witness " , Amrozi accused his brother of deliberately distorting his evidence .
0   2108705 2108831 Yucaipa owned Dominick 's before selling the chain to Safeway in 1998 for $ 2.5 billion .   Yucaipa bought Dominick 's in 1995 for $ 693 million and sold it to Safeway for $ 1.8 billion in 1998 .
1   1330381 1330521 They had published an advertisement on the Internet on June 10 , offering the cargo for sale , he added .   On June 10 , the ship 's owners had published an advertisement on the Internet , offering the explosives for sale .
0   3344667 3344648 Around 0335 GMT , Tab shares were up 19 cents , or 4.4 % , at A $ 4.56 , having earlier set a record high of A $ 4.57 . Tab shares jumped 20 cents , or 4.6 % , to set a record closing high at A $ 4.57 .
1   1236820 1236712 The stock rose $ 2.11 , or about 11 percent , to close Friday at $ 21.51 on the New York Stock Exchange .   PG & E Corp. shares jumped $ 1.63 or 8 percent to $ 21.03 on the New York Stock Exchange on Friday .
1   738533  737951  Revenue in the first quarter of the year dropped 15 percent from the same period a year earlier .   With the scandal hanging over Stewart 's company , revenue the first quarter of the year dropped 15 percent from the same period a year earlier .
0   264589  264502  The Nasdaq had a weekly gain of 17.27 , or 1.2 percent , closing at 1,520.15 on Friday .    The tech-laced Nasdaq Composite .IXIC rallied 30.46 points , or 2.04 percent , to 1,520.15 .
1   579975  579810  The DVD-CCA then appealed to the state Supreme Court .  The DVD CCA appealed that decision to the U.S. Supreme Court .
...
  • train.tsv数据样式说明:

  • train.tsv中的数据内容共分为5列, 第一列数据, 0或1, 代表每对句子是否具有相同的含义, 0代表含义不相同, 1代表含义相同. 第二列和第三列分别代表每对句子的id, 第四列和第五列分别具有相同/不同含义的句子对.

  • test.tsv数据样式:

复制代码
index   #1 ID   #2 ID   #1 String   #2 String
0   1089874 1089925 PCCW 's chief operating officer , Mike Butcher , and Alex Arena , the chief financial officer , will report directly to Mr So . Current Chief Operating Officer Mike Butcher and Group Chief Financial Officer Alex Arena will report to So .
1   3019446 3019327 The world 's two largest automakers said their U.S. sales declined more than predicted last month as a late summer sales frenzy caused more of an industry backlash than expected . Domestic sales at both GM and No. 2 Ford Motor Co. declined more than predicted as a late summer sales frenzy prompted a larger-than-expected industry backlash .
2   1945605 1945824 According to the federal Centers for Disease Control and Prevention ( news - web sites ) , there were 19 reported cases of measles in the United States in 2002 .   The Centers for Disease Control and Prevention said there were 19 reported cases of measles in the United States in 2002 .
3   1430402 1430329 A tropical storm rapidly developed in the Gulf of Mexico Sunday and was expected to hit somewhere along the Texas or Louisiana coasts by Monday night . A tropical storm rapidly developed in the Gulf of Mexico on Sunday and could have hurricane-force winds when it hits land somewhere along the Louisiana coast Monday night .
4   3354381 3354396 The company didn 't detail the costs of the replacement and repairs .   But company officials expect the costs of the replacement work to run into the millions of dollars .
5   1390995 1391183 The settling companies would also assign their possible claims against the underwriters to the investor plaintiffs , he added . Under the agreement , the settling companies will also assign their potential claims against the underwriters to the investors , he added .
6   2201401 2201285 Air Commodore Quaife said the Hornets remained on three-minute alert throughout the operation . Air Commodore John Quaife said the security operation was unprecedented .
7   2453843 2453998 A Washington County man may have the countys first human case of West Nile virus , the health department said Friday .  The countys first and only human case of West Nile this year was confirmed by health officials on Sept . 8 .
...
  • test.tsv数据样式说明:

    • test.tsv中的数据内容共分为5列, 第一列数据代表每条文本数据的索引; 其余列的含义与train.tsv中相同.
  • MRPC数据集的任务类型:

  • 句子对二分类任务

  • 评估指标为: ACC和F1

2.4 STS-B数据集文件样式[¶](#2.4 STS-B数据集文件样式¶)

  • 数据集释义: STSB(The Semantic Textual Similarity Benchmark,语义文本相似性基准测试)

  • 本质: 回归任务/句子对的文本五分类任务

    • STS-B/
      - dev.tsv
      - test.tsv
      - train.tsv
      • LICENSE.txt
      • readme.txt
      • original/
  • 文件样式说明:

  • 在使用中常用到的文件是train.tsv, dev.tsv, test.tsv, 分别代表训练集, 验证集和测试集. 其中train.tsv与dev.tsv数据样式相同, 都是带有标签的数据, 其中test.tsv是不带有标签的数据.

  • train.tsv数据样式:

复制代码
index   genre   filename    year    old_index   source1 source2 sentence1   sentence2   score
0   main-captions   MSRvid  2012test    0001    none    none    A plane is taking off.  An air plane is taking off. 5.000
1   main-captions   MSRvid  2012test    0004    none    none    A man is playing a large flute. A man is playing a flute.   3.800
2   main-captions   MSRvid  2012test    0005    none    none    A man is spreading shreded cheese on a pizza.   A man is spreading shredded cheese on an uncooked pizza.    3.800
3   main-captions   MSRvid  2012test    0006    none    none    Three men are playing chess.Two men are playing chess.  2.600
4   main-captions   MSRvid  2012test    0009    none    none    A man is playing the cello.A man seated is playing the cello.   4.250
5   main-captions   MSRvid  2012test    0011    none    none    Some men are fighting.  Two men are fighting.   4.250
6   main-captions   MSRvid  2012test    0012    none    none    A man is smoking.   A man is skating.   0.500
7   main-captions   MSRvid  2012test    0013    none    none    The man is playing the piano.   The man is playing the guitar.  1.600
8   main-captions   MSRvid  2012test    0014    none    none    A man is playing on a guitar and singing.   A woman is playing an acoustic guitar and singing.  2.200
9   main-captions   MSRvid  2012test    0016    none    none    A person is throwing a cat on to the ceiling.   A person throws a cat on the ceiling.   5.000
...
  • train.tsv数据样式说明:

  • train.tsv中的数据内容共分为10列, 第一列数据是数据索引; 第二列代表每对句子的来源, 如main-captions表示来自字幕; 第三列代表来源的具体保存文件名, 第四列代表出现时间(年); 第五列代表原始数据的索引; 第六列和第七列分别代表句子对原始来源; 第八列和第九列代表相似程度不同的句子对; 第十列代表句子对的相似程度由低到高, 值域范围是0, 5.

  • test.tsv数据样式:

复制代码
index   genre   filename    year    old_index   source1 source2 sentence1   sentence2
0   main-captions   MSRvid  2012test    0024    none    none    A girl is styling her hair. A girl is brushing her hair.
1   main-captions   MSRvid  2012test    0033    none    none    A group of men play soccer on the beach.    A group of boys are playing soccer on the beach.
2   main-captions   MSRvid  2012test    0045    none    none    One woman is measuring another woman's ankle.   A woman measures another woman's ankle.
3   main-captions   MSRvid  2012test    0063    none    none    A man is cutting up a cucumber. A man is slicing a cucumber.
4   main-captions   MSRvid  2012test    0066    none    none    A man is playing a harp.    A man is playing a keyboard.
5   main-captions   MSRvid  2012test    0074    none    none    A woman is cutting onions.  A woman is cutting tofu.
6   main-captions   MSRvid  2012test    0076    none    none    A man is riding an electric bicycle.    A man is riding a bicycle.
7   main-captions   MSRvid  2012test    0082    none    none    A man is playing the drums. A man is playing the guitar.
8   main-captions   MSRvid  2012test    0092    none    none    A man is playing guitar.    A lady is playing the guitar.
9   main-captions   MSRvid  2012test    0095    none    none    A man is playing a guitar.  A man is playing a trumpet.
10  main-captions   MSRvid  2012test    0096    none    none    A man is playing a guitar.  A man is playing a trumpet.
...
  • test.tsv数据样式说明:

  • test.tsv中的数据内容共分为9列, 含义与train.tsv前9列相同.

  • STS-B数据集的任务类型:

  • 句子对多分类任务/句子对回归任务

  • 评估指标为: Pearson-Spearman Corr

2.5 QQP数据集文件样式[¶](#2.5 QQP数据集文件样式¶)

  • 数据集释义: QQP(The Quora Question Pairs, Quora问题对数集),相似性和释义任务,是社区问答网站Quora中问题对的集合。

  • 本质: 句子对的**二分类任务**

    • QQP/
      - dev.tsv
      - original/
      - test.tsv
      - train.tsv
  • 文件样式说明:

  • 在使用中常用到的文件是train.tsv, dev.tsv, test.tsv, 分别代表训练集, 验证集和测试集. 其中train.tsv与dev.tsv数据样式相同, 都是带有标签的数据, 其中test.tsv是不带有标签的数据.

  • train.tsv数据样式:

复制代码
id  qid1    qid2    question1   question2   is_duplicate
133273  213221  213222  How is the life of a math student? Could you describe your own experiences?Which level of prepration is enough for the exam jlpt5?  0
402555  536040  536041  How do I control my horny emotions? How do you control your horniness?  1
360472  364011  490273  What causes stool color to change to yellow?    What can cause stool to come out as little balls?   0
150662  155721  7256    What can one do after MBBS? What do i do after my MBBS ?    1
183004  279958  279959  Where can I find a power outlet for my laptop at Melbourne Airport? Would a second airport in Sydney, Australia be needed if a high-speed rail link was created between Melbourne and Sydney?   0
119056  193387  193388  How not to feel guilty since I am Muslim and I'm conscious we won't have sex together?  I don't beleive I am bulimic, but I force throw up atleast once a day after I eat something and feel guilty. Should I tell somebody, and if so who? 0
356863  422862  96457   How is air traffic controlled?  How do you become an air traffic controller?0
106969  147570  787 What is the best self help book you have read? Why? How did it change your life?    What are the top self help books I should read? 1
...
  • train.tsv数据样式说明:

  • train.tsv中的数据内容共分为6列, 第一列代表文本数据索引; 第二列和第三列数据分别代表问题1和问题2的id; 第四列和第五列代表需要进行'是否重复'判定的句子对; 第六列代表上述问题是/不是重复性问题的标签, 0代表不重复, 1代表重复.

  • test.tsv数据样式:

复制代码
id  question1   question2
0   Would the idea of Trump and Putin in bed together scare you, given the geopolitical implications?   Do you think that if Donald Trump were elected President, he would be able to restore relations with Putin and Russia as he said he could, based on the rocky relationship Putin had with Obama and Bush?
1   What are the top ten Consumer-to-Consumer E-commerce online?    What are the top ten Consumer-to-Business E-commerce online?
2   Why don't people simply 'Google' instead of asking questions on Quora?  Why do people ask Quora questions instead of just searching google?
3   Is it safe to invest in social trade biz?   Is social trade geniune?
4   If the universe is expanding then does matter also expand?  If universe and space is expanding? Does that mean anything that occupies space is also expanding?
5   What is the plural of hypothesis?   What is the plural of thesis?
6   What is the application form you need for launching a company?  What is the application form you need for launching a company in Austria?
7   What is Big Theta? When should I use Big Theta as opposed to big O? Is O(Log n) close to O(n) or O(1)?
8   What are the health implications of accidentally eating a small quantity of aluminium foil?What are the implications of not eating vegetables?
...
  • test.tsv数据样式说明:

  • test.tsv中的数据内容共分为3列, 第一列数据代表每条文本数据的索引; 第二列和第三列数据代表用于测试的问题句子对.

  • QQP数据集的任务类型:

  • 句子对二分类任务

  • 评估指标为: ACC/F1

2.6 (MNLI/SNLI)数据集文件样式[¶](#2.6 (MNLI/SNLI)数据集文件样式¶)

  • 数据集释义:

  • MNLI(The Multi-Genre Natural Language Inference Corpus, 多类型自然语言推理数据库)

  • 本质: 句子对的**三分类任务**

    • (MNLI/SNLI)/
      • dev_matched.tsv
      • dev_mismatched.tsv
      • original/
      • test_matched.tsv
      • test_mismatched.tsv
      • train.tsv
  • 文件样式说明:

  • 在使用中常用到的文件是train.tsv, dev_matched.tsv, dev_mismatched.tsv, test_matched.tsv, test_mismatched.tsv分别代表训练集, 与训练集一同采集的验证集, 与训练集不是一同采集验证集, 与训练集一同采集的测试集, 与训练集不是一同采集测试集. 其中train.tsv与dev_matched.tsv和dev_mismatched.tsv数据样式相同, 都是带有标签的数据, 其中test_matched.tsv与test_mismatched.tsv数据样式相同, 都是不带有标签的数据.

  • train.tsv数据样式:

复制代码
index   promptID    pairID  genre   sentence1_binary_parse  sentence2_binary_parse  sentence1_parse sentence2_parse sentence1   sentence2   label1  gold_label
0   31193   31193n  government  ( ( Conceptually ( cream skimming ) ) ( ( has ( ( ( two ( basic dimensions ) ) - ) ( ( product and ) geography ) ) ) . ) )  ( ( ( Product and ) geography ) ( ( are ( what ( make ( cream ( skimming work ) ) ) ) ) . ) )   (ROOT (S (NP (JJ Conceptually) (NN cream) (NN skimming)) (VP (VBZ has) (NP (NP (CD two) (JJ basic) (NNS dimensions)) (: -) (NP (NN product) (CC and) (NN geography)))) (. .)))  (ROOT (S (NP (NN Product) (CC and) (NN geography)) (VP (VBP are) (SBAR (WHNP (WP what)) (S (VP (VBP make) (NP (NP (NN cream)) (VP (VBG skimming) (NP (NN work)))))))) (. .)))   Conceptually cream skimming has two basic dimensions - product and geography.   Product and geography are what make cream skimming work.    neutral neutral
1   101457  101457e telephone   ( you ( ( know ( during ( ( ( the season ) and ) ( i guess ) ) ) ) ( at ( at ( ( your level ) ( uh ( you ( ( ( lose them ) ( to ( the ( next level ) ) ) ) ( if ( ( if ( they ( decide ( to ( recall ( the ( the ( parent team ) ) ) ) ) ) ) ) ( ( the Braves ) ( decide ( to ( call ( to ( ( recall ( a guy ) ) ( from ( ( triple A ) ( ( ( then ( ( a ( double ( A guy ) ) ) ( ( goes up ) ( to ( replace him ) ) ) ) ) and ) ( ( a ( single ( A guy ) ) ) ( ( goes up ) ( to ( replace him ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ( You ( ( ( ( lose ( the things ) ) ( to ( the ( following level ) ) ) ) ( if ( ( the people ) recall ) ) ) . ) )   (ROOT (S (NP (PRP you)) (VP (VBP know) (PP (IN during) (NP (NP (DT the) (NN season)) (CC and) (NP (FW i) (FW guess)))) (PP (IN at) (IN at) (NP (NP (PRP$ your) (NN level)) (SBAR (S (INTJ (UH uh)) (NP (PRP you)) (VP (VBP lose) (NP (PRP them)) (PP (TO to) (NP (DT the) (JJ next) (NN level))) (SBAR (IN if) (S (SBAR (IN if) (S (NP (PRP they)) (VP (VBP decide) (S (VP (TO to) (VP (VB recall) (NP (DT the) (DT the) (NN parent) (NN team)))))))) (NP (DT the) (NNPS Braves)) (VP (VBP decide) (S (VP (TO to) (VP (VB call) (S (VP (TO to) (VP (VB recall) (NP (DT a) (NN guy)) (PP (IN from) (NP (NP (RB triple) (DT A)) (SBAR (S (S (ADVP (RB then)) (NP (DT a) (JJ double) (NNP A) (NN guy)) (VP (VBZ goes) (PRT (RP up)) (S (VP (TO to) (VP (VB replace) (NP (PRP him))))))) (CC and) (S (NP (DT a) (JJ single) (NNP A) (NN guy)) (VP (VBZ goes) (PRT (RP up)) (S (VP (TO to) (VP (VB replace) (NP (PRP him)))))))))))))))))))))))))))) (ROOT (S (NP (PRP You)) (VP (VBP lose) (NP (DT the) (NNS things)) (PP (TO to) (NP (DT the) (JJ following) (NN level))) (SBAR (IN if) (S (NP (DT the) (NNS people)) (VP (VBP recall))))) (. .))) you know during the season and i guess at at your level uh you lose them to the next level if if they decide to recall the the parent team the Braves decide to call to recall a guy from triple A then a double A guy goes up to replace him and a single A guy goes up to replace him You lose the things to the following level if the people recall.    entailment  entailment
2   134793  134793e fiction ( ( One ( of ( our number ) ) ) ( ( will ( ( ( carry out ) ( your instructions ) ) minutely ) ) . ) )   ( ( ( A member ) ( of ( my team ) ) ) ( ( will ( ( execute ( your orders ) ) ( with ( immense precision ) ) ) ) . ) )   (ROOT (S (NP (NP (CD One)) (PP (IN of) (NP (PRP$ our) (NN number)))) (VP (MD will) (VP (VB carry) (PRT (RP out)) (NP (PRP$ your) (NNS instructions)) (ADVP (RB minutely)))) (. .))) (ROOT (S (NP (NP (DT A) (NN member)) (PP (IN of) (NP (PRP$ my) (NN team)))) (VP (MD will) (VP (VB execute) (NP (PRP$ your) (NNS orders)) (PP (IN with) (NP (JJ immense) (NN precision))))) (. .)))  One of our number will carry out your instructions minutely.    A member of my team will execute your orders with immense precision.    entailment  entailment
3   37397   37397e  fiction ( ( How ( ( ( do you ) know ) ? ) ) ( ( All this ) ( ( ( is ( their information ) ) again ) . ) ) ) ( ( This information ) ( ( belongs ( to them ) ) . ) )  (ROOT (S (SBARQ (WHADVP (WRB How)) (SQ (VBP do) (NP (PRP you)) (VP (VB know))) (. ?)) (NP (PDT All) (DT this)) (VP (VBZ is) (NP (PRP$ their) (NN information)) (ADVP (RB again))) (. .)))   (ROOT (S (NP (DT This) (NN information)) (VP (VBZ belongs) (PP (TO to) (NP (PRP them)))) (. .)))    How do you know? All this is their information again.   This information belongs to them.   entailment  entailment
...
  • train.tsv数据样式说明:

  • train.tsv中的数据内容共分为12列, 第一列代表文本数据索引; 第二列和第三列数据分别代表句子对的不同类型id; 第四列代表句子对的来源; 第五列和第六列代表具有句法结构分析的句子对表示; 第七列和第八列代表具有句法结构和词性标注的句子对表示, 第九列和第十列代表原始的句子对, 第十一和第十二列代表不同标准的标注方法产生的标签, 在这里,他们始终相同, 一共有三种类型的标签, neutral代表两个句子既不矛盾也不蕴含, entailment代表两个句子具有蕴含关系, contradiction代表两个句子观点矛盾.

  • test_matched.tsv数据样式:

复制代码
index   promptID    pairID  genre   sentence1_binary_parse  sentence2_binary_parse  sentence1_parse sentence2_parse sentence1   sentence2
0   31493   31493   travel  ( ( ( ( ( ( ( ( Hierbas , ) ( ans seco ) ) , ) ( ans dulce ) ) , ) and ) frigola ) ( ( ( are just ) ( ( a ( few names ) ) ( worth ( ( keeping ( a look-out ) ) for ) ) ) ) . ) )    ( Hierbas ( ( is ( ( a name ) ( worth ( ( looking out ) for ) ) ) ) . ) )   (ROOT (S (NP (NP (NNS Hierbas)) (, ,) (NP (NN ans) (NN seco)) (, ,) (NP (NN ans) (NN dulce)) (, ,) (CC and) (NP (NN frigola))) (VP (VBP are) (ADVP (RB just)) (NP (NP (DT a) (JJ few) (NNS names)) (PP (JJ worth) (S (VP (VBG keeping) (NP (DT a) (NN look-out)) (PP (IN for))))))) (. .))) (ROOT (S (NP (NNS Hierbas)) (VP (VBZ is) (NP (NP (DT a) (NN name)) (PP (JJ worth) (S (VP (VBG looking) (PRT (RP out)) (PP (IN for))))))) (. .)))    Hierbas, ans seco, ans dulce, and frigola are just a few names worth keeping a look-out for.    Hierbas is a name worth looking out for.
1   92164   92164   government  ( ( ( The extent ) ( of ( the ( behavioral effects ) ) ) ) ( ( would ( ( depend ( in ( part ( on ( ( the structure ) ( of ( ( ( the ( individual ( account program ) ) ) and ) ( any limits ) ) ) ) ) ) ) ) ( on ( accessing ( the funds ) ) ) ) ) . ) )    ( ( Many people ) ( ( would ( be ( very ( unhappy ( to ( ( loose control ) ( over ( their ( own money ) ) ) ) ) ) ) ) ) . ) )   (ROOT (S (NP (NP (DT The) (NN extent)) (PP (IN of) (NP (DT the) (JJ behavioral) (NNS effects)))) (VP (MD would) (VP (VB depend) (PP (IN in) (NP (NP (NN part)) (PP (IN on) (NP (NP (DT the) (NN structure)) (PP (IN of) (NP (NP (DT the) (JJ individual) (NN account) (NN program)) (CC and) (NP (DT any) (NNS limits)))))))) (PP (IN on) (S (VP (VBG accessing) (NP (DT the) (NNS funds))))))) (. .))) (ROOT (S (NP (JJ Many) (NNS people)) (VP (MD would) (VP (VB be) (ADJP (RB very) (JJ unhappy) (PP (TO to) (NP (NP (JJ loose) (NN control)) (PP (IN over) (NP (PRP$ their) (JJ own) (NN money)))))))) (. .))) The extent of the behavioral effects would depend in part on the structure of the individual account program and any limits on accessing the funds. Many people would be very unhappy to loose control over their own money.
2   9662    9662    government  ( ( ( Timely access ) ( to information ) ) ( ( is ( in ( ( the ( best interests ) ) ( of ( ( ( both GAO ) and ) ( the agencies ) ) ) ) ) ) . ) )    ( It ( ( ( is ( in ( ( everyone 's ) ( best interest ) ) ) ) ( to ( ( have access ) ( to ( information ( in ( a ( timely manner ) ) ) ) ) ) ) ) . ) )   (ROOT (S (NP (NP (JJ Timely) (NN access)) (PP (TO to) (NP (NN information)))) (VP (VBZ is) (PP (IN in) (NP (NP (DT the) (JJS best) (NNS interests)) (PP (IN of) (NP (NP (DT both) (NNP GAO)) (CC and) (NP (DT the) (NNS agencies))))))) (. .))) (ROOT (S (NP (PRP It)) (VP (VBZ is) (PP (IN in) (NP (NP (NN everyone) (POS 's)) (JJS best) (NN interest))) (S (VP (TO to) (VP (VB have) (NP (NN access)) (PP (TO to) (NP (NP (NN information)) (PP (IN in) (NP (DT a) (JJ timely) (NN manner))))))))) (. .)))   Timely access to information is in the best interests of both GAO and the agencies. It is in everyone's best interest to have access to information in a timely manner.
3   5991    5991    travel  ( ( Based ( in ( ( the ( Auvergnat ( spa town ) ) ) ( of Vichy ) ) ) ) ( , ( ( the ( French government ) ) ( often ( ( ( ( proved ( more zealous ) ) ( than ( its masters ) ) ) ( in ( ( ( suppressing ( civil liberties ) ) and ) ( ( drawing up ) ( anti-Jewish legislation ) ) ) ) ) . ) ) ) ) ) ( ( The ( French government ) ) ( ( passed ( ( anti-Jewish laws ) ( aimed ( at ( helping ( the Nazi ) ) ) ) ) ) . ) )   (ROOT (S (PP (VBN Based) (PP (IN in) (NP (NP (DT the) (NNP Auvergnat) (NN spa) (NN town)) (PP (IN of) (NP (NNP Vichy)))))) (, ,) (NP (DT the) (JJ French) (NN government)) (ADVP (RB often)) (VP (VBD proved) (NP (JJR more) (NNS zealous)) (PP (IN than) (NP (PRP$ its) (NNS masters))) (PP (IN in) (S (VP (VP (VBG suppressing) (NP (JJ civil) (NNS liberties))) (CC and) (VP (VBG drawing) (PRT (RP up)) (NP (JJ anti-Jewish) (NN legislation))))))) (. .))) (ROOT (S (NP (DT The) (JJ French) (NN government)) (VP (VBD passed) (NP (NP (JJ anti-Jewish) (NNS laws)) (VP (VBN aimed) (PP (IN at) (S (VP (VBG helping) (NP (DT the) (JJ Nazi)))))))) (. .))) Based in the Auvergnat spa town of Vichy, the French government often proved more zealous than its masters in suppressing civil liberties and drawing up anti-Jewish legislation.   The French government passed anti-Jewish laws aimed at helping the Nazi.
...
  • test_matched.tsv数据样式说明:

  • test_matched.tsv中的数据内容共分为10列, 与train.tsv的前10列含义相同.

  • (MNLI/SNLI)数据集的任务类型:

  • 句子对多分类任务

  • 评估指标为: ACC

2.7 (QNLI/RTE/WNLI)数据集文件样式[¶](#2.7 (QNLI/RTE/WNLI)数据集文件样式¶)

  • 数据集释义:

  • QNLI(Qusetion-answering NLI,问答自然语言推断),自然语言推断任务。QNLI是从另一个数据集The Stanford Question Answering Dataset(斯坦福问答数据集, SQuAD 1.0)[3](https://zhuanlan.zhihu.com/p/135283598#ref_3 "3]")转换而来的.

  • RTE(The Recognizing Textual Entailment datasets,识别文本蕴含数据集),自然语言推断任务,它是将一系列的年度文本蕴含挑战赛的数据集进行整合合并而来的.

  • WNLI(Winograd NLI,Winograd自然语言推断),自然语言推断任务,数据集来自于竞赛数据的转换。

  • 本质: QNLI是二分类任务. RTE是二分类任务. WNLI是二分类任务.

  • QNLI, RTE, WNLI三个数据集的样式基本相同.

    • (QNLI/RTE/WNLI)/
      - dev.tsv
      - test.tsv
      - train.tsv
  • 文件样式说明:

  • 在使用中常用到的文件是train.tsv, dev.tsv, test.tsv, 分别代表训练集, 验证集和测试集. 其中train.tsv与dev.tsv数据样式相同, 都是带有标签的数据, 其中test.tsv是不带有标签的数据.

  • QNLI中的train.tsv数据样式:

复制代码
index   question    sentence    label
0   When did the third Digimon series begin?    Unlike the two seasons before it and most of the seasons that followed, Digimon Tamers takes a darker and more realistic approach to its story featuring Digimon who do not reincarnate after their deaths and more complex character development in the original Japanese. not_entailment
1   Which missile batteries often have individual launchers several kilometres from one another?    When MANPADS is operated by specialists, batteries may have several dozen teams deploying separately in small sections; self-propelled air defence guns may deploy in pairs.    not_entailment
2   What two things does Popper argue Tarski's theory involves in an evaluation of truth?   He bases this interpretation on the fact that examples such as the one described above refer to two things: assertions and the facts to which they refer.   entailment
3   What is the name of the village 9 miles north of Calafat where the Ottoman forces attacked the Russians?    On 31 December 1853, the Ottoman forces at Calafat moved against the Russian force at Chetatea or Cetate, a small village nine miles north of Calafat, and engaged them on 6 January 1854.  entailment
4   What famous palace is located in London?    London contains four World Heritage Sites: the Tower of London; Kew Gardens; the site comprising the Palace of Westminster, Westminster Abbey, and St Margaret's Church; and the historic settlement of Greenwich (in which the Royal Observatory, Greenwich marks the Prime Meridian, 0° longitude, and GMT).  not_entailment
5   When is the term 'German dialects' used in regard to the German language?   When talking about the German language, the term German dialects is only used for the traditional regional varieties.   entailment
6   What was the name of the island the English traded to the Dutch in return for New Amsterdam?    At the end of the Second Anglo-Dutch War, the English gained New Amsterdam (New York) in North America in exchange for Dutch control of Run, an Indonesian island.  entailment
7   How were the Portuguese expelled from Myanmar?  From the 1720s onward, the kingdom was beset with repeated Meithei raids into Upper Myanmar and a nagging rebellion in Lan Na.  not_entailment
8   What does the word 'customer' properly apply to?    The bill also required rotation of principal maintenance inspectors and stipulated that the word "customer" properly applies to the flying public, not those entities regulated by the FAA. entailment
...
  • RTE中的train.tsv数据样式:
复制代码
index   sentence1   sentence2   label
0   No Weapons of Mass Destruction Found in Iraq Yet.   Weapons of Mass Destruction Found in Iraq.  not_entailment
1   A place of sorrow, after Pope John Paul II died, became a place of celebration, as Roman Catholic faithful gathered in downtown Chicago to mark the installation of new Pope Benedict XVI.Pope Benedict XVI is the new leader of the Roman Catholic Church. entailment
2   Herceptin was already approved to treat the sickest breast cancer patients, and the company said, Monday, it will discuss with federal regulators the possibility of prescribing the drug for more breast cancer patients.  Herceptin can be used to treat breast cancer.   entailment
3   Judie Vivian, chief executive at ProMedica, a medical service company that helps sustain the 2-year-old Vietnam Heart Institute in Ho Chi Minh City (formerly Saigon), said that so far about 1,500 children have received treatment.   The previous name of Ho Chi Minh City was Saigon.entailment
4   A man is due in court later charged with the murder 26 years ago of a teenager whose case was the first to be featured on BBC One's Crimewatch. Colette Aram, 16, was walking to her boyfriend's house in Keyworth, Nottinghamshire, on 30 October 1983 when she disappeared. Her body was later found in a field close to her home. Paul Stewart Hutchinson, 50, has been charged with murder and is due before Nottingham magistrates later.  Paul Stewart Hutchinson is accused of having stabbed a girl.    not_entailment
5   Britain said, Friday, that it has barred cleric, Omar Bakri, from returning to the country from Lebanon, where he was released by police after being detained for 24 hours. Bakri was briefly detained, but was released.   entailment
6   Nearly 4 million children who have at least one parent who entered the U.S. illegally were born in the United States and are U.S. citizens as a result, according to the study conducted by the Pew Hispanic Center. That's about three quarters of the estimated 5.5 million children of illegal immigrants inside the United States, according to the study. About 1.8 million children of undocumented immigrants live in poverty, the study found.  Three quarters of U.S. illegal immigrants have children.    not_entailment
7   Like the United States, U.N. officials are also dismayed that Aristide killed a conference called by Prime Minister Robert Malval in Port-au-Prince in hopes of bringing all the feuding parties together.  Aristide had Prime Minister Robert Malval  murdered in Port-au-Prince.  not_entailment
8   WASHINGTON --  A newly declassified narrative of the Bush administration's advice to the CIA on harsh interrogations shows that the small group of Justice Department lawyers who wrote memos authorizing controversial interrogation techniques were operating not on their own but with direction from top administration officials, including then-Vice President Dick Cheney and national security adviser Condoleezza Rice. At the same time, the narrative suggests that then-Defense Secretary Donald H. Rumsfeld and then-Secretary of State Colin Powell were largely left out of the decision-making process. Dick Cheney was the Vice President of Bush. entailment
  • WNLI中的train.tsv数据样式:
复制代码
index   sentence1   sentence2   label
0   I stuck a pin through a carrot. When I pulled the pin out, it had a hole.   The carrot had a hole.  1
1   John couldn't see the stage with Billy in front of him because he is so short.  John is so short.   1
2   The police arrested all of the gang members. They were trying to stop the drug trade in the neighborhood.   The police were trying to stop the drug trade in the neighborhood.  1
3   Steve follows Fred's example in everything. He influences him hugely.   Steve influences him hugely.    0
4   When Tatyana reached the cabin, her mother was sleeping. She was careful not to disturb her, undressing and climbing back into her berth.   mother was careful not to disturb her, undressing and climbing back into her berth. 0
5   George got free tickets to the play, but he gave them to Eric, because he was particularly eager to see it. George was particularly eager to see it.    0
6   John was jogging through the park when he saw a man juggling watermelons. He was very impressive.   John was very impressive.   0
7   I couldn't put the pot on the shelf because it was too tall.    The pot was too tall.   1
8   We had hoped to place copies of our newsletter on all the chairs in the auditorium, but there were simply not enough of them.   There were simply not enough copies of the newsletter.  1
  • (QNLI/RTE/WNLI)中的train.tsv数据样式说明:

  • train.tsv中的数据内容共分为4列, 第一列代表文本数据索引; 第二列和第三列数据代表需要进行'是否蕴含'判定的句子对; 第四列数据代表两个句子是否具有蕴含关系, 0/not_entailment代表不是蕴含关系, 1/entailment代表蕴含关系.

  • QNLI中的test.tsv数据样式:

复制代码
index   question    sentence
0   What organization is devoted to Jihad against Israel?   For some decades prior to the First Palestine Intifada in 1987, the Muslim Brotherhood in Palestine took a "quiescent" stance towards Israel, focusing on preaching, education and social services, and benefiting from Israel's "indulgence" to build up a network of mosques and charitable organizations.
1   In what century was the Yarrow-Schlick-Tweedy balancing system used?    In the late 19th century, the Yarrow-Schlick-Tweedy balancing 'system' was used on some marine triple expansion engines.
2   The largest brand of what store in the UK is located in Kingston Park?  Close to Newcastle, the largest indoor shopping centre in Europe, the MetroCentre, is located in Gateshead.
3   What does the IPCC rely on for research?    In principle, this means that any significant new evidence or events that change our understanding of climate science between this deadline and publication of an IPCC report cannot be included.
4   What is the principle about relating spin and space variables?  Thus in the case of two fermions there is a strictly negative correlation between spatial and spin variables, whereas for two bosons (e.g. quanta of electromagnetic waves, photons) the correlation is strictly positive.
5   Which network broadcasted Super Bowl 50 in the U.S.?    CBS broadcast Super Bowl 50 in the U.S., and charged an average of $5 million for a 30-second commercial during the game.
6   What did the museum acquire from the Royal College of Science?  To link this to the rest of the museum, a new entrance building was constructed on the site of the former boiler house, the intended site of the Spiral, between 1978 and 1982.
7   What is the name of the old north branch of the Rhine?  From Wijk bij Duurstede, the old north branch of the Rhine is called Kromme Rijn ("Bent Rhine") past Utrecht, first Leidse Rijn ("Rhine of Leiden") and then, Oude Rijn ("Old Rhine").
8   What was one of Luther's most personal writings?    It remains in use today, along with Luther's hymns and his translation of the Bible.
...
  • (RTE/WNLI)中的test.tsv数据样式:

    index sentence1 sentence2
    0 Maude and Dora had seen the trains rushing across the prairie, with long, rolling puffs of black smoke streaming back from the engine. Their roars and their wild, clear whistles could be heard from far away. Horses ran away when they came in sight. Horses ran away when Maude and Dora came in sight.
    1 Maude and Dora had seen the trains rushing across the prairie, with long, rolling puffs of black smoke streaming back from the engine. Their roars and their wild, clear whistles could be heard from far away. Horses ran away when they came in sight. Horses ran away when the trains came in sight.
    2 Maude and Dora had seen the trains rushing across the prairie, with long, rolling puffs of black smoke streaming back from the engine. Their roars and their wild, clear whistles could be heard from far away. Horses ran away when they came in sight. Horses ran away when the puffs came in sight.
    3 Maude and Dora had seen the trains rushing across the prairie, with long, rolling puffs of black smoke streaming back from the engine. Their roars and their wild, clear whistles could be heard from far away. Horses ran away when they came in sight. Horses ran away when the roars came in sight.
    4 Maude and Dora had seen the trains rushing across the prairie, with long, rolling puffs of black smoke streaming back from the engine. Their roars and their wild, clear whistles could be heard from far away. Horses ran away when they came in sight. Horses ran away when the whistles came in sight.
    5 Maude and Dora had seen the trains rushing across the prairie, with long, rolling puffs of black smoke streaming back from the engine. Their roars and their wild, clear whistles could be heard from far away. Horses ran away when they came in sight. Horses ran away when the horses came in sight.
    6 Maude and Dora had seen the trains rushing across the prairie, with long, rolling puffs of black smoke streaming back from the engine. Their roars and their wild, clear whistles could be heard from far away. Horses ran away when they saw a train coming. Maude and Dora saw a train coming.
    7 Maude and Dora had seen the trains rushing across the prairie, with long, rolling puffs of black smoke streaming back from the engine. Their roars and their wild, clear whistles could be heard from far away. Horses ran away when they saw a train coming. The trains saw a train coming.
    8 Maude and Dora had seen the trains rushing across the prairie, with long, rolling puffs of black smoke streaming back from the engine. Their roars and their wild, clear whistles could be heard from far away. Horses ran away when they saw a train coming. The puffs saw a train coming.
    ...

  • (QNLI/RTE/WNLI)中的test.tsv数据样式说明:

  • test.tsv中的数据内容共分为3列, 第一列数据代表每条文本数据的索引; 第二列和第三列数据代表需要进行'是否蕴含'判定的句子对.

  • (QNLI/RTE/WNLI)数据集的任务类型:

  • 句子对二分类任务

  • 评估指标为: ACC

3 小结[¶](#3 小结¶)

  • 学习了GLUE数据集合的介绍:

    • GLUE由纽约大学, 华盛顿大学, Google联合推出, 涵盖不同NLP任务类型, 截止至2020年1月其中包括11个子任务数据集, 成为衡量NLP研究发展的衡量标准.
  • GLUE数据集合包含以下数据集:

    • CoLA 数据集
    • SST-2 数据集
    • MRPC 数据集
    • STS-B 数据集
    • QQP 数据集
    • MNLI 数据集
    • SNLI 数据集
    • QNLI 数据集
    • RTE 数据集
    • WNLI 数据集

六.Bert系列模型【纯概念】

1 BERT模型介绍

学习目标

  • 了解什么是BERT

  • 掌握BERT的架构

  • 掌握BERT的预训练任务

思考题:Bert模型的架构以及每一部分的作用?

思考题:Bert模型两大预训练任务,并谈一谈你的理解?

1 BERT简介[¶](#1 BERT简介¶)

BERT是2018年10月由Google AI研究院提出的一种预训练模型.

  • BERT的全称是Bidirectional Encoder Representation from Transformers.
  • BERT在机器阅读理解顶级水平测试SQuAD1.1中表现出惊人的成绩: 全部两个衡量指标上全面超越人类, 并且在11种不同NLP测试中创出SOTA表现. 包括将GLUE基准推高至80.4% (绝对改进7.6%), MultiNLI准确度达到86.7% (绝对改进5.6%). 成为NLP发展史上的里程碑式的模型成就.

2 BERT的架构[¶](#2 BERT的架构¶)

总体架构: 如下图所示, 最左边的就是BERT的架构图, 可以很清楚的看到BERT采用了Transformer Encoder block进行连接, 因为是一个典型的双向编码模型.

从上面的架构图中可以看到, 宏观上BERT分三个主要模块.

  • 最底层黄色标记的Embedding模块.
  • 中间层蓝色标记的Transformer模块.
  • 最上层绿色标记的预微调模块.

2.1 Embedding模块[¶](#2.1 Embedding模块¶)

BERT中的该模块是由三种Embedding共同组成而成, 如下图

  • Token Embeddings 是词嵌入张量, 第一个单词是CLS标志, 可以用于之后的分类任务.

  • Segment Embeddings 是句子分段嵌入张量, 是为了服务后续的两个句子为输入的预训练任务.

  • Position Embeddings 是位置编码张量, 此处注意和传统的Transformer不同, 不是三角函数计算的固定位置编码, 而是通过学习得出来的.

  • 整个Embedding模块的输出张量就是这3个张量的直接加和结果.

2.2 双向Transformer模块[¶](#2.2 双向Transformer模块¶)

BERT中只使用了经典Transformer架构中的Encoder部分, 完全舍弃了Decoder部分. 而两大预训练任务也集中体现在训练Transformer模块中.

2.3 预微调模块[¶](#2.3 预微调模块¶)

  • 经过中间层Transformer的处理后, BERT的最后一层根据任务的不同需求而做不同的调整即可.
  • 比如对于sequence-level的分类任务, BERT直接取第一个CLS token 的final hidden state, 再加一层全连接层后进行softmax来预测最终的标签.
  • 对于不同的任务, 微调都集中在预微调模块, 几种重要的NLP微调任务架构图展示如下
  • 从上图中可以发现, 在面对特定任务时, 只需要对预微调层进行微调, 就可以利用Transformer强大的注意力机制来模拟很多下游任务, 并得到SOTA的结果. (句子对关系判断, 单文本主题分类, 问答任务(QA), 单句贴标签(NER))

  • 若干可选的超参数建议如下:

复制代码
Batch size: 16, 32
Learning rate (Adam): 5e-5, 3e-5, 2e-5
Epochs: 3, 4

3 BERT的预训练任务[¶](#3 BERT的预训练任务¶)

BERT包含两个预训练任务:

  • 任务一: Masked LM (带mask的语言模型训练)
  • 任务二: Next Sentence Prediction (下一句话预测任务)

3.1 任务一: Masked LM[¶](#3.1 任务一: Masked LM¶)

带mask的语言模型训练

  • 关于传统的语言模型训练, 都是采用left-to-right, 或者left-to-right + right-to-left结合的方式, 但这种单向方式或者拼接的方式提取特征的能力有限. 为此BERT提出一个深度双向表达模型(deep bidirectional representation). 即采用MASK任务来训练模型.

  • 1: 在原始训练文本中, 随机的抽取15%的token作为参与MASK任务的对象.

  • 2: 在这些被选中的token中, 数据生成器并不是把它们全部变成MASK, 而是有下列3种情况.

  • 2.1: 在80%的概率下, 用MASK标记替换该token, 比如my dog is hairy -> my dog is MASK

  • 2.2: 在10%的概率下, 用一个随机的单词替换token, 比如my dog is hairy -> my dog is apple

  • 2.3: 在10%的概率下, 保持该token不变, 比如my dog is hairy -> my dog is hairy

  • 3: 模型在训练的过程中, 并不知道它将要预测哪些单词? 哪些单词是原始的样子? 哪些单词被遮掩成了MASK? 哪些单词被替换成了其他单词? 正是在这样一种高度不确定的情况下, 反倒逼着模型快速学习该token的分布式上下文的语义, 尽最大努力学习原始语言说话的样子. 同时因为原始文本中只有15%的token参与了MASK操作, 并不会破坏原语言的表达能力和语言规则.

3.2 任务二: Next Sentence Prediction[¶](#3.2 任务二: Next Sentence Prediction¶)

下一句话预测任务

  • 在NLP中有一类重要的问题比如QA(Quention-Answer), NLI(Natural Language Inference), 需要模型能够很好的理解两个句子之间的关系, 从而需要在模型的训练中引入对应的任务. 在BERT中引入的就是Next Sentence Prediction任务. 采用的方式是输入句子对(A, B), 模型来预测句子B是不是句子A的真实的下一句话.

  • 1: 所有参与任务训练的语句都被选中作为句子A.

  • 1.1: 其中50%的B是原始文本中真实跟随A的下一句话. (标记为IsNext, 代表正样本)

  • 1.2: 其中50%的B是原始文本中随机抽取的一句话. (标记为NotNext, 代表负样本)

  • 2: 在任务二中, BERT模型可以在测试集上取得97%-98%的准确率.

4 小结[¶](#4 小结¶)

  • 学习了什么是BERT.

    • BERT是一个基于Transformer Encoder的预训练语言模型.
    • BERT在11种NLP测试任务中创出SOAT表现.
  • 学习了BERT的结构.

    • 最底层的Embedding模块, 包括Token Embeddings, Segment Embeddings, Position Embeddings.
    • 中间层的Transformer模块, 只使用了经典Transformer架构中的Encoder部分.
    • 最上层的预微调模块, 具体根据不同的任务类型来做相应的处理.
  • 学习了BERT的两大预训练任务.

    • MLM任务(Masked Language Model), 在原始文本中随机抽取15%的token参与任务.
      • 在80%概率下, 用MASK替换该token.
      • 在10%概率下, 用一个随机的单词替换该token.
      • 在10%概率下, 保持该token不变.
    • NSP任务(Next Sentence Prediction), 采用的方式是输入句子对(A, B), 模型预测句子B是不是句子A的真实的下一句话.
      • 其中50%的B是原始文本中真实跟随A的下一句话.(标记为IsNext, 代表正样本)
      • 其中50%的B是原始文本中随机抽取的一句话. (标记为NotNext, 代表负样本)
  • 本节常见问答

    • 说一说Bert模型的架构以及每一部分的作用?
    • 说一说Bert模型两大预训练任务,并谈一谈你的理解?

2 BERT模型特点

学习目标

  • 理解BERT模型的优点和原因.
  • 理解BERT模型的缺点和原因.
  • 理解在MLM任务中采用80%, 10%, 10%策略的原因.
  • 掌握利用BERT处理长文本的任务如何构造训练样本.

思考题:BERT模型的优点和缺点?

思考题:BERT的MLM任务中为什么采用了80%, 10%, 10%的策略?

思考题:长文本预测任务如果想用BERT来实现, 要如何构造训练样本?

1 BERT模型优缺点[¶](#1 BERT模型优缺点¶)

1.1 BERT的优点[¶](#1.1 BERT的优点¶)

  • 通过预训练, 加上Fine-tunning, 在11项NLP任务上取得最优结果.

  • BERT的根基源于Transformer, 相比传统RNN更加高效, 可以并行化处理同时能捕捉长距离的语义和结构依赖.

  • BERT采用了Transformer架构中的Encoder模块, 不仅仅获得了真正意义上的bidirectional context, 而且为后续微调任务留出了足够的调整空间.

1.2 BERT的缺点[¶](#1.2 BERT的缺点¶)

  • BERT模型过于庞大, 参数太多, 不利于资源紧张的应用场景, 也不利于上线的实时处理.

  • BERT目前给出的中文模型中, 是以字为基本token单位的, 很多需要词向量的应用无法直接使用. 同时该模型无法识别很多生僻词, 只能以UNK代替.

  • BERT中第一个预训练任务MLM中, MASK标记只在训练阶段出现, 而在预测阶段不会出现, 这就造成了一定的信息偏差, 因此训练时不能过多的使用MASK, 否则会影响模型的表现.

  • 按照BERT的MLM任务中的约定, 每个batch数据中只有15%的token参与了训练, 被模型学习和预测, 所以BERT收敛的速度比left-to-right模型要慢很多(left-to-right模型中每一个token都会参与训练).

2 BERT的MLM任务[¶](#2 BERT的MLM任务¶)

2.1 BERT的MLM任务中为什么采用了80%, 10%, 10%的策略?[¶](#2.1 BERT的MLM任务中为什么采用了80%, 10%, 10%的策略?¶)

  • 首先, 如果所有参与训练的token被100%的MASK, 那么在fine-tunning的时候所有单词都是已知的, 不存在MASK, 那么模型就只能根据其他token的信息和语序结构来预测当前词, 而无法利用到这个词本身的信息, 因为它们从未出现在训练过程中, 等于模型从未接触到它们的信息, 等于整个语义空间损失了部分信息. 采用80%的概率下应用MASK, 既可以让模型去学着预测这些单词, 又以20%的概率保留了语义信息展示给模型.

  • 保留下来的信息如果全部使用原始token, 那么模型在预训练的时候可能会偷懒, 直接照抄当前token信息. 采用10%概率下random token来随机替换当前token, 会让模型不能去死记硬背当前的token, 而去尽力学习单词周边的语义表达和远距离的信息依赖, 尝试建模完整的语言信息.

  • 最后再以10%的概率保留原始的token, 意义就是保留语言本来的面貌, 让信息不至于完全被遮掩, 使得模型可以"看清"真实的语言面貌.

3 BERT处理长文本的方法[¶](#3 BERT处理长文本的方法¶)

首选要明确一点, BERT预训练模型所接收的最大sequence长度是512.

那么对于长文本(文本长度超过512的句子), 就需要特殊的方式来构造训练样本. 核心就是如何进行截断.

  • head-only方式: 这是只保留长文本头部信息的截断方式, 具体为保存前510个token (要留两个位置给CLSSEP).
  • tail-only方式: 这是只保留长文本尾部信息的截断方式, 具体为保存最后510个token (要留两个位置给CLSSEP).
  • head+only方式: 选择前128个token和最后382个token (文本总长度在800以内), 或者前256个token和最后254个token (文本总长度大于800).

4 小结[¶](#4 小结¶)

  • 学习了BERT模型的3个优点:

    • 在11个NLP任务上取得SOAT成绩.
    • 利用了Transformer的并行化能力以及长语句捕捉语义依赖和结构依赖.
    • BERT实现了双向Transformer并为后续的微调任务留出足够的空间.
  • 学习了BERT模型的4个缺点:

    • BERT模型太大, 太慢.
    • BERT模型中的中文模型是以字为基本token单位的, 无法利用词向量, 无法识别生僻词.
    • BERT模型中的MLM任务, MASK标记在训练阶段出现, 预测阶段不出现, 这种偏差会对模型有一定影响.
    • BERT模型的MLM任务, 每个batch只有15%的token参与了训练, 造成大量文本数据的"无用", 收敛速度慢, 需要的算力和算时都大大提高.
  • 学习了长文本处理如果要利用BERT的话, 需要进行截断处理.

    • 第一种方式就是只保留前面510个token.
    • 第二种方式就是只保留后面510个token.
    • 第三种方式就是前后分别保留一部分token, 总数是510.
  • BERT中MLM任务中的MASK是以一种显示的方式告诉模型"这个词我不告诉你, 你自己从上下文里猜", 非常类似于同学们在做完形填空. 如果MASK以外的部分全部都用原始token, 模型会学习到"如果当前词是MASK, 就根据其他词的信息推断这个词; 如果当前词是一个正常的单词, 就直接照抄". 这样一来, 到了fine-tunning阶段, 所有单词都是正常单词了, 模型就会照抄所有单词, 不再提取单词之间的依赖关系了.

  • BERT中MLM任务以10%的概率填入random token, 就是让模型时刻处于"紧张情绪"中, 让模型搞不清楚当前看到的token是真实的单词还是被随机替换掉的单词, 这样模型在任意的token位置就只能把当前token的信息和上下文信息结合起来做综合的判断和建模. 这样一来, 到了fine-tunning阶段, 模型也会同时提取这两方面的信息, 因为模型"心理很紧张", 它不知道当前看到的这个token, 所谓的"正常单词"到底有没有"提前被动过手脚".


3 BERT系列模型介绍

BERT系列模型


学习目标

  • 了解不同类型的BERT系列模型.
  • 掌握BERT系列模型之间的区别和联系.

1 AlBERT模型[¶](#1 AlBERT模型¶)


学习目标

  • 了解AlBERT模型的架构.
  • 掌握AlBERT模型的优化点.

1.1 AlBERT模型的架构[¶](#1.1 AlBERT模型的架构¶)

  • AlBERT模型发布于ICLR 2020会议, 是基于BERT模型的重要改进版本. 是谷歌研究院和芝加哥大学共同发布的研究成果.
  • 论文全称<< A Lite BERT For Self-Supervised Learning Of Language Representations >>.
  • 从模型架构上看, AlBERT和BERT基本一致, 核心模块都是基于Transformer的强大特征提取能力.

  • 在本篇论文中, 首先对比了过去几年预训练模型的主流操作思路.
  • 第一: 大规模的语料.
  • 第二: 更深的网络, 更多的参数.
  • 第三: 多任务训练.


1.2 AlBERT模型的优化点[¶](#1.2 AlBERT模型的优化点¶)

  • 相比较于BERT模型, AlBERT的出发点即是希望降低预训练的难度, 同时提升模型关键能力. 主要引入了5大优化.
  • 第一: 词嵌入参数的因式分解.
  • 第二: 隐藏层之间的参数共享.
  • 第三: 去掉NSP, 增加SOP预训练任务.
  • 第四: 去掉dropout操作.
  • 第五: MLM任务的优化.

  • 第一: 词嵌入参数的因式分解.
  • AlBERT的作者认为, 词向量只记录了少量的词汇本身的信息, 更多的语义信息和句法信息包含在隐藏层中. 因此词嵌入的维度不一定非要和隐藏层的维度一致.

  • 具体做法就是通过因式分解来降低嵌入矩阵的参数:
  • BERT: embedding_dim * vocab_size = hidden_size * vocab_size, 其中embedding_dim=768, vocab_size大约为30000左右的级别, 大约等于30000 * 768 = 23040000(2300万).
  • AlBERT: vocab_size * project + project * hidden_size, 其中project是因式分解的中间映射层维度, 一般取128, 参数总量大约等于30000 * 128 + 128 * 768 = 482304(48万).

  • 第二: 隐藏层之间的参数共享.
  • 在BERT模型中, 无论是12层的base, 还是24层的large模型, 其中每一个Encoder Block都拥有独立的参数模块, 包含多头注意力子层, 前馈全连接层. 非常重要的一点是, 这些层之间的参数都是独立的, 随着训练的进行都不一样了!

  • 那么为了减少模型的参数量, 一个很直观的做法便是让这些层之间的参数共享, 本质上只有一套Encoder Block的参数!
  • 在AlBERT模型中, 所有的多头注意力子层, 全连接层的参数都是分别共享的, 通过这样的方式, AlBERT属于Block的参数量在BERT的基础上, 分别下降到原来的1/12, 1/24.

  • 第三: 去掉NSP, 增加SOP预训练任务.
  • BERT模型的成功很大程度上取决于两点, 一个是基础架构采用Transformer, 另一个就是精心设计的两大预训练任务, MLM和NSP. 但是BERT提出后不久, 便有研究人员对NSP任务提出质疑, 我们也可以反思一下NSP任务有什么问题?

  • 在AlBERT模型中, 直接舍弃掉了NSP任务, 新提出了SOP任务(Sentence Order Prediction), 即两句话的顺序预测, 文本中正常语序的先后两句话A, B作为正样本, 则B, A作为负样本.
  • 增加了SOP预训练任务后, 使得AlBERT拥有了更强大的语义理解能力和语序关系的预测能力.

  • 第四: 去掉dropout操作.
  • 原始论文中提到, 在AlBERT训练达到100万个batch_size时, 模型依然没有过拟合, 作者基于这个试验结果直接去掉了Dropout操作, 竟然意外的发现AlBERT对下游任务的效果有了进一步的提升. 这是NLP领域第一次发现dropout对大规模预训练模型会造成负面影响, 也使得AlBERT v2.0版本成为第一个不使用dropout操作而获得优异表现的主流预训练模型

  • 第五: MLM任务的优化.
  • segments-pair的优化:
    • BERT为了加速训练, 前90%的steps使用了长度为128个token的短句子, 后10%的steps才使用长度为512个token的长句子.
    • AlBERT在90%的steps中使用了长度为512个token的长句子, 更长的句子可以提供更多上下文信息, 可以显著提升模型的能力.
  • Masked-Ngram-LM的优化:
    • BERT的MLM目标是随机mask掉15%的token来进行预测, 其中的token早已分好, 一个个算.
    • AlBERT预测的是Ngram片段, 每个片段长度为n (n=1,2,3), 每个Ngram片段的概率按照公式分别计算即可. 比如1-gram, 2-gram, 3-gram的概率分别为6/11, 3/11, 2/11.

  • AlBERT系列中包含一个albert-tiny模型, 隐藏层仅有4层, 参数量1.8M, 非常轻巧. 相比较BERT, 其训练和推理速度提升约10倍, 但精度基本保留, 语义相似度数据集LCQMC测试集达到85.4%, 相比于bert-base仅下降1.5%, 非常优秀.

2 RoBERTa模型[¶](#2 RoBERTa模型¶)


学习目标

  • 掌握RoBERTa模型的架构.
  • 理解RoBERTa模型的优化点.

2.1 RoBERTa模型的架构[¶](#2.1 RoBERTa模型的架构¶)

  • 原始论文<< RoBERTa: A Robustly Optimized BERT Pretraining Approach >>, 由FaceBook和华盛顿大学联合于2019年提出的模型.

  • 从模型架构上看, RoBERTa和BERT完全一致, 核心模块都是基于Transformer的强大特征提取能力. 改进点主要集中在一些训练细节上.

    • 第1点: More data
    • 第2点: Larger batch size
    • 第3点: Training longer
    • 第4点: No NSP
    • 第5点: Dynamic masking
    • 第6点: Byte level BPE

2.2 RoBERTa模型的优化点[¶](#2.2 RoBERTa模型的优化点¶)

  • 针对于上面提到的7点细节, 一一展开说明:

  • 第1点: More data (更大的数据量)

    • 原始BERT的训练语料采用了16GB的文本数据.
    • RoBERTa采用了160GB的文本数据.
      • 1: Books Corpus + English Wikipedia (16GB): BERT原文使用的之数据.
      • 2: CC-News (76GB): 自CommonCrawl News数据中筛选后得到数据, 约含6300万篇新闻, 2016年9月-2019年2月.
      • 3: OpenWebText (38GB): 该数据是借鉴GPT2, 从Reddit论坛中获取, 取点赞数大于3的内容.
      • 4: Storie (31GB): 同样从CommonCrawl获取, 属于故事类数据, 而非新闻类.

  • 第2点: Larger batch size (更大的batch size)
    • BERT采用的batch size等于256.
    • RoBERTa的训练在多种模式下采用了更大的batch size, 从256一直到最大的8000.

  • 第3点: Training longer (更多的训练步数)
    • RoBERTa的训练采用了更多的训练步数, 让模型充分学习数据中的特征.

  • 第4点: No NSP (去掉NSP任务)
    • 从2019年开始, 已经有越来越多的证据表明NSP任务对于大型预训练模型是一个负面作用, 因此在RoBERTa中直接取消掉NSP任务.
    • 论文作者进行了多组对照试验:
      • 1: Segment + NSP (即BERT模式). 输入包含两部分, 每个部分是来自同一文档或者不同文档的segment(segment是连续的多个句子), 这两个segment的token总数少于512, 预训练包含MLM任务和NSP任务.
      • 2: Sentence pair + NSP (使用两个连续的句子 + NSP, 并采用更大的batch size). 输入也是包含两部分, 每个部分是来自同一个文档或者不同文档的单个句子, 这两个句子的token 总数少于512. 由于这些输入明显少于512个tokens, 因此增加batch size的大小, 以使tokens总数保持与SEGMENT-PAIR + NSP相似, 预训练包含MLM任务和NSP任务.
      • 3: Full-sentences (如果输入的最大长度为512, 那么尽量选择512长度的连续句子; 如果跨越document, 就在中间加上一个特殊分隔符, 比如SEP; 该试验没有NSP). 输入只有一部分(而不是两部分), 来自同一个文档或者不同文档的连续多个句子, token总数不超过512. 输入可能跨越文档边界, 如果跨文档, 则在上一个文档末尾添加文档边界token, 预训练不包含NSP任务.
      • 4: Document-sentences (和情况3一样, 但是步跨越document; 该实验没有NSP). 输入只有一部分(而不是两部分), 输入的构造类似于Full-sentences, 只是不需要跨越文档边界, 其输入来自同一个文档的连续句子, token总数不超过512. 在文档末尾附近采样的输入可以短于512个tokens, 因此在这些情况下动态增加batch size大小以达到与Full-sentecens相同的tokens总数, 预训练不包含NSP任务.

  • 总的来说, 实验结果表明1 < 2 < 3 < 4.
    • 真实句子过短的话, 不如拼接成句子段.
    • 没有NSP任务更优.
    • 不跨越document更优.

  • 第5点: Dynamic masking (采用动态masking策略)
    • 原始静态mask: 即BERT版本的mask策略, 准备训练数据时, 每个样本只会进行一次随机mask(因此每个epoch都是重复的), 后续的每个训练步都采用相同的mask方式, 这是原始静态mask.
    • 动态mask: 并没有在预处理的时候执行mask, 而是在每次向模型提供输入时动态生成mask, 所以到底哪些tokens被mask掉了是时刻变化的, 无法提前预知的.

  • 第6点: Byte level BPE (采用字节级别的Encoding)

    • 基于char-level: 原始BERT的方式, 在中文场景下就是处理一个个的汉字.
    • 基于bytes-level: 与char-level的区别在于编码的粒度是bytes, 而不是unicode字符作为sub-word的基本单位.
  • 当采用bytes-level的BPE之后, 词表大小从3万(原始BERT的char-level)增加到5万. 这分别为BERT-base和BERT-large增加了1500万和2000万额外的参数. 之前有研究表明, 这样的做法在有些下游任务上会导致轻微的性能下降. 但论文作者相信: 这种统一编码的优势会超过性能的轻微下降.


3 MacBert模型[¶](#3 MacBert模型¶)


3.1 学习目标[¶](#3.1 学习目标¶)

  • 掌握MacBert模型的架构.
  • 掌握MacBert模型的优化点.

3.2 MacBert模型的架构[¶](#3.2 MacBert模型的架构¶)

  • MacBert模型由哈工大NLP实验室于2020年11月提出, 2021年5月发布应用, 是针对于BERT模型做了优化改良后的预训练模型.

  • << Revisiting Pre-trained Models for Chinese Natural Language Processing >>, 通过原始论文题目也可以知道, MacBert是针对于中文场景下的BERT优化.

  • MacBert模型的架构和BERT大部分保持一致, 最大的变化有两点:

    • 第一点: 对于MLM预训练任务, 采用了不同的MASK策略.
    • 第二点: 删除了NSP任务, 替换成SOP任务.

3.3 MacBert模型的优化点[¶](#3.3 MacBert模型的优化点¶)

  • 第一点: 对于MLM预训练任务, 采用了不同的MASK策略.
    • 1: 使用了全词masked以及n-gram masked策略来选择tokens如何被遮掩, 从单个字符到4个字符的遮掩比例分别为40%, 30%, 20%, 10%
    • 2: 原始BERT模型中的MASK出现在训练阶段, 但没有出现在微调阶段, 这会造成exposure bias的问题. 因此在MacBert中提出使用类似的单词来进行masked. 具体来说, 使用基于Word2Vec相似度计算包训练词向量, 后续利用这里面找近义词的功能来辅助mask, 比如以30%的概率选择了一个3-gram的单词进行masked, 则将在Word2Vec中寻找3-gram的近义词来替换, 在极少数情况下, 当没有符合条件的相似单词时, 策略会进行降级, 直接使用随机单词进行替换.
    • 3: 使用15%的百分比对输入单词进行MASK, 其中80%的概率下执行策略2(即替换为相似单词), 10%的概率下替换为随机单词, 10%的概率下保留原始单词不变.

  • 第二点: 删除了NSP任务, 替换成SOP任务.

  • 第二点优化是直接借鉴了AlBERT模型中提出的SOP任务.


  • 在NLP著名的难任务阅读理解中, MacBert展现出非常优秀的表现.


4 SpanBERT模型[¶](#4 SpanBERT模型¶)


学习目标

  • 掌握SpanBERT模型的架构.
  • 掌握SpanBERT模型的优化点.

4.1 SpanBERT模型的架构[¶](#4.1 SpanBERT模型的架构¶)

  • 论文的主要贡献有3点:
    • 1: 提出了更好的Span Mask方案, 再次展示了随机遮掩连续一段tokens比随机遮掩单个token要好.
    • 2: 通过加入了Span Boundary Objective(SBO)训练任务, 增强了BERT的性能, 特别在一些和Span适配的任务, 如抽取式问答.
    • 3: 用实验数据获得了和XLNet一致的结果, 发现去除掉NSP任务, 直接用连续一长句训练效果更好.

  • SpanBERT的架构图如下:
  • 架构图中可以清晰的展示论文的核心贡献点:
    • Span Masking
    • Span Boundary Objective

4.2 Span Masking[¶](#4.2 Span Masking¶)

  • 关于创新的MASK机制, 一般来说都是相对于原始BERT的基准进行改进. 对于BERT, 训练时会随机选取整句中的最小输入单元token来进行遮掩, 中文场景下本质上就是进行字级别的MASK. 但是这种方式会让本来应该有强相关的一些连在一起的字词, 在训练时被割裂开了.

  • 那么首先想到的做法: 既然能遮掩字, 那么能不能直接遮掩整个词呢? 这就是BERT-WWM模型的思想.

    原始输入: 使用语言模型来预测下一个词的概率.

    原始BERT: 使用语言[MASK]型来[MASK]测下一个词的[MASK]率.

    BERT-WWM: 使用语言[MASK][MASK]来[MASK][MASK]下一个词的[MASK][MASK].


  • 引申: 百度著名的ERNIE模型中, 直接引入命名实体(Named Entity)的外部知识, 进行整个实体的遮掩, 进行训练.

  • 综合上面所说, 会更自然的想到, 既然整词的MASK, 那么如果拥有词的边界信息会不会让模型的能力更上一层楼呢? SpanBERT给出的是肯定的回答!!!

  • 论文中关于span的选择, 走了这样一个流程:

    • 第一步: 根据几何分布, 先随机选择一个**span长度**.
    • 第二步: 再根据均匀分布随机选择这一段的**起始位置**.
    • 第三步: 最后根据前两步的start和length直接进行MASK.

  • 结论: 论文中详细论证了按照上述算法进行MASK, 随机被遮掩的文本平均长度等于3.8

4.3 Span Boundary Objective(SBO)[¶](#4.3 Span Boundary Objective(SBO)¶)

  • SBO任务是本篇论文最核心的创新点, 希望通过增加这个预训练任务, 可以让被遮掩的Span Boundary的词向量, 能够学习到Span内部的信息.

  • 具体的做法: 在训练时取Span前后边界的两个词, 需要注意这两个词不在Span内, 然后用这两个词向量加上Span中被MASK掉的词的位置向量, 来预测原词.

  • 更详细的操作如下, 即将词向量和位置向量进行拼接, 经过GeLU激活和LayerNorm处理, 连续经过两个全连接层, 得到最终的输出张量:


  • 最后预测Span中原词的时候会得到一个损失, 这就是SBO任务的损失; 再将其和BERT自身的MLM任务的损失进行加和, 共同作为SpanBERT的目标损失函数进行训练:

4.4 NSP任务反思[¶](#4.4 NSP任务反思¶)

  • 为什么选择Single Sentence而不是BERT的Two Sentence?
    • 1: 训练文本的长度更大, 可以学会长程依赖.
    • 2: 对于NSP的负样本, 基于另一个主题文档的句子来预测单词, 会给MLM任务引入很大的噪声.
    • 3: AlBERT模型已经给出了论证, 因为NSP任务太简单了.

4 ELMo模型介绍

学习目标

  • 了解什么是ELMo.
  • 掌握ELMo的架构.
  • 掌握ELMo的预训练任务.
  • 了解ELMo的效果和成绩.
  • 了解ELMo的优缺点.

1 ELMo简介[¶](#1 ELMo简介¶)

ELMo是2018年3月由华盛顿大学提出的一种预训练模型.

  • ELMo的全称是Embeddings from Language Models.
  • ELMo模型的提出源于论文<< Deep Contextualized Word Representations >>.
  • ELMo模型提出的动机源于研究人员认为一个好的预训练语言模型应该能够包含丰富的句法和语义信息, 并且能够对多义词进行建模. 而传统的词向量(2013年的word2vec, 2014年的GloVe)都是上下文无关的, 也就是固定的词向量. 最典型的例子就是"apple"在不同的语境下, 应该可以表示水果或公司, 但是固定的词向量显然无法做到这一点. 因此研究团队利用新的语言模型训练一个上下文相关的预训练模型, 成为ELMo, 并在6个NLP任务上获得提升.

2 ELMo的架构[¶](#2 ELMo的架构¶)

2.1 总体架构[¶](#2.1 总体架构¶)

从上面的架构图中可以看到, 宏观上ELMo分三个主要模块.

  • 最底层黄色标记的Embedding模块.
  • 中间层蓝色标记的两部分双层LSTM模块.
  • 最上层绿色标记的词向量表征模块.

2.2 Embedding模块[¶](#2.2 Embedding模块¶)

ELMo最底层的词嵌入采用CNN对字符级进行编码, 本质就是获得一个静态的词嵌入向量作为网络的底层输入.

2.3 两部分的双层LSTM模块[¶](#2.3 两部分的双层LSTM模块¶)

  • 这是整个ELMo中最重要的部分, 架构中分成左侧的前向LSTM网络, 和右侧的反向LSTM网络.
  • ELMo的做法是我们只预训练一个Language Model, 而word embedding是通过输入的句子实时给出的, 这样单词的嵌入向量就包含了上下文的信息, 也就彻底改变了Word2Vec和GloVe的静态词向量的做法.
  • ELMo的这一模块分为左右两部分, 本质上就是一个双向LM, 对于左半部分, 给定了N个tokens(t1, t2, ..., tN), Language Model通过前面k-1个位置的token序列来计算第k个token出现的概率, 构成前向双层LSTM模型.
  • 同理, 对于架构中的右半部分, 给定了N个tokens(t(k+1), t(k+2), ..., t(N)), Language Model通过后面N-k个位置的token序列来计算第k个token出现的概率, 构成后向双层LSTM模型.
  • ELMo在训练过程中的目标函数就是最大化下面的公式:

2.4 词向量表征模块[¶](#2.4 词向量表征模块¶)

  • 因为ELMo是个语言模型, 对于每个token, 通过一个L层的双向LSTM网络可以计算出2L+1个表示向量如下:
  • 从上面的公式可以清楚的看到, 有3个不同的组成部分, 第一个就是对token直接进行CNN编码的结果, 也是ELMo最底层模块的输出; 第二个就是前向LSTM的输出结果, 每一层都会有一个输出, 总共L层就会有L个输出; 第三个就是后向LSTM的输出结果, 每一层都会有一个输出, 总共L层就会有L个输出; 综合三部分的输出加在一起, 就是2L+1个输出向量.

  • 通过整个网络, 每一个token得到了2L+1个表示向量, 但是我们希望每一个token能对应一个向量. 最简单的做法就是取最上层的输出结果作为token的表示向量, 更通用的做法是加入若干参数来融合所有层的信息, 如下所示:

  • 上式的意思是对于2L+1个向量, 每一个前面都加上一个权重稀疏, 然后直接融合成一个向量, 最后再乘一个系数作为最终该token的词向量.

  • 原始论文中提到最前面的那个系数, 在不同任务中取不同的值效果会有较大的差异, 需要注意在SQuAD中设置为0.01取得的效果要好于设置为1.

  • 原始论文中在进行底层token编码时, 用CNN形成了一个512维的列向量, 也就是初始嵌入维度等于512. 中间层使用了双层的LSTM分别进行前向编码和后向编码, 每层的单个LSTM输入维度是512, 输出维度也是512, 保持一致. 因为是双向编码并且分左右两部分, 所以每层的输出维度是512*2=1024, 最后进行权重融合后的向量维度就是1024.

3 ELMo的预训练任务[¶](#3 ELMo的预训练任务¶)

3.1 ELMo的本质思想[¶](#3.1 ELMo的本质思想¶)

  • 首先用一个语言模型学好一个单词的word embedding, 此时是无法区分多义词的, 但没关系. 当实际使用word embedding的时候, 该单词已经具备了特定的上下文信息, 这个时候可以根据上下文单词的语义去调整单词的word embedding表示, 这样经过调整后得到的word embedding向量就可以准确的表达单词在当前上下文中的真实含义了, 也就自然的解决了多义词问题.
  • 结论就是ELMo模型是个根据当前上下文对word embedding动态调整的语言模型.

3.2 ELMo的预训练采用了典型的两阶段过程[¶](#3.2 ELMo的预训练采用了典型的两阶段过程¶)

  • 第一阶段: 利用语言模型进行预训练.

  • 第二阶段: 在做下游任务时, 从预训练网络中提取对应单词的网络各层的word embedding作为新特征补充到下游任务中.

  • 第一阶段: 语言模型预训练.

  • 再次回到ELMo的总体架构图, 网络结构采用了双层双向LSTM.

  • 目前语言模型训练的任务目标是根据单词Wi的上下文去正确预测单词Wi, Wi之前的单词序列context-before称为上文, Wi之后的单词序列context-after称为下文.

  • 架构图上左侧的前向双层LSTM代表正方向编码器, 输入的是从左向右顺序的除了预测单词Wi之外的上文context-before; 右侧的反向双层LSTM代表反方向编码器, 输入的是从右向左的逆序的下文context-after;

  • 每个编码器的深度都是L=2, 即双层LSTM叠加.

  • 使用上述的网络结构利用大量语料做语言模型任务就能预训练好这个网络. 当输入一个新句子S_new时, 句子中每个单词都能得到对应的3个embedding向量: 1-最底层的单词的word embedding. 2-中间第一层双向LSTM中对应单词位置的embedding, 这层编码对应单词的句法信息更多一些. 3-中间第二层双向LSTM中对应单词位置的embedding, 这层编码对应单词的语义信息更多一些.

  • ELMo的预训练过程不仅仅学会了单词的word embedding, 还学习了一个双层双向的LSTM网络, 这两者后续都会用到, 是整个ELMo预训练的两大产出结果.

  • 第二阶段: 下游任务的调整.
  • 比如我们的下游任务是QA问题.
  • 对于问句X, 可以先将句子X作为预训练好的ELMo网络的输入, 这样X中每个单词在ELMo中都能获得3个对应的embedding向量. 之后赋给这3个向量各自一个权重a, 这个权重a既可以是学习得来的也可以是最简单的平均分布赋值, 然后把3个向量加权求和, 整个成一个词向量. 最后将整合后的词向量作为X在自己任务的那个网络结构中对应单词的输入, 以此作为新特征补充进下游任务中. 对于回答Y可以同样处理.

  • 因为ELMo给下游提供的是每个单词的特征形式, 所以这一类预训练方法被称为"Feature-based Pre-Training".

4 ELMo模型的效果[¶](#4 ELMo模型的效果¶)

ELMo对于多义词问题的解决结果:

  • 前面提到静态的word embedding无法解决多义词的问题, 那么ELMo引入上下文动态语义调整后的embedding word可以解决多义词问题吗? 答案正如上图所示, 而且比我们期待的解决效果要更好.

  • 上图中的例子, 对于GloVe训练出来的word embedding来说, 多义词比如play, 根据它的embedding找出最接近其语义的单词, 发现结果集合几乎全部都在体育领域, 这很明显是因为训练数据中包含play的语句中体育领域的数量明显占多数导致的.

  • 再来看使用ELMo后的效果, 根据上下文动态调整后的embedding word不仅仅能找出对应于"play":"演出"的相同语义的句子, 而且还可以保证找出的句子中的play对应的词性也是相同的, 这真的是超出期待之外的惊喜!

  • 原始论文中提到ELMo的试验效果, 在6个NLP主流任务中性能都有不同幅度的提升, 最高的提升达到25%, 任务的覆盖范围很广, 包含句子语义关系判断, 分类任务, 阅读理解等等.

5 ELMo的待改进点[¶](#5 ELMo的待改进点¶)

ELMo在传统静态word embedding方法(Word2Vec, GloVe)的基础上提升了很多, 但是依然存在缺陷, 有很大的改进余地.

  • 第一点: 一个很明显的缺点在于特征提取器的选择上, ELMo使用了双向双层LSTM, 而不是现在横扫千军的Transformer, 在特征提取能力上肯定是要弱一些的. 设想如果ELMo的提升提取器选用Transformer, 那么后来的BERT的反响将远不如当时那么火爆了.
  • 第二点: ELMo选用双向拼接的方式进行特征融合, 这种方法肯定不如BERT一体化的双向提取特征好.

6 小结[¶](#6 小结¶)

  • 学习了什么是ELMo.

    • ELMo是2018年3月由华盛顿大学提出的一种预训练语言模型.
    • ELMo在6种NLP测试任务中有很大的提升表现.
  • 学习了ELMo的结构.

    • ELMo架构总体上采用了双向双层LSTM的结构.
    • 最底层的Embedding模块.
    • 中间层的双向双层LSTM模块.
    • 最上层的特征融合模块.
  • 学习了ELMo的预训练任务.

    • ELMo的本质思想就是根据当前上下文对word embedding进行动态调整的语言模型.
    • ELMo的预训练是一个明显的两阶段过程.
      • 第一阶段: 利用语言模型进行预训练, 得到基础静态词向量和双向双层LSTM网络.
      • 第二阶段: 在拥有上下文的环境中, 将上下文输入双向双层LSTM中, 得到动态调整后的word embedding, 等于将单词融合进了上下文的语义, 可以更准确的表达单词的真实含义.
  • 学习了ELMo的效果.

    • 经过与GloVe静态词向量的对比, 明显可以看出ELMo的词向量可以更好的表达真实语义, 更好的解决多义词的问题.
  • 学习了ELMo的待改进点.

    • ELMo的特征提取器没有选用更强大的Transformer, 在提取特征上肯定弱于现在的最优结果.

5 GPT模型介绍

学习目标

  • 了解什么是GPT.
  • 掌握GPT的架构.
  • 掌握GPT的预训练任务.

1 GPT介绍[¶](#1 GPT介绍¶)

  • GPT是OpenAI公司提出的一种语言预训练模型.

  • OpenAI在论文<< Improving Language Understanding by Generative Pre-Training >>中提出GPT模型.

  • OpenAI后续又在论文<< Language Models are Unsupervised Multitask Learners >>中提出GPT2模型.

  • GPT和GPT2模型结构差别不大, 但是GPT2采用了更大的数据集进行训练.

  • OpenAI GPT模型是在Google BERT模型之前提出的, 与BERT最大的区别在于GPT采用了传统的语言模型方法进行预训练, 即使用单词的上文来预测单词, 而BERT是采用了双向上下文的信息共同来预测单词.

  • 正是因为训练方法上的区别, 使得GPT更擅长处理自然语言生成任务(NLG), 而BERT更擅长处理自然语言理解任务(NLU).

2 GPT的架构[¶](#2 GPT的架构¶)

  • 看三个语言模型的对比架构图, 中间的就是GPT:
  • 从上图可以很清楚的看到GPT采用的是单向Transformer模型, 例如给定一个句子u1, u2, ..., un, GPT在预测单词ui的时候只会利用u1, u2, ..., u(i-1)的信息, 而BERT会同时利用上下文的信息u1, u2, ..., u(i-1), u(i+1), ..., un.

  • 作为两大模型的直接对比, BERT采用了Transformer的Encoder模块, 而GPT采用了Transformer的Decoder模块. 并且GPT的Decoder Block和经典Transformer Decoder Block还有所不同, 如下图所示:

  • 如上图所示, 经典的Transformer Decoder Block包含3个子层, 分别是Masked Multi-Head Attention层, encoder-decoder attention层, 以及Feed Forward层. 但是在GPT中取消了第二个encoder-decoder attention子层, 只保留Masked Multi-Head Attention层, 和Feed Forward层.

  • 作为单向Transformer Decoder模型, GPT利用句子序列信息预测下一个单词的时候, 要使用Masked Multi-Head Attention对单词的下文进行遮掩(look ahead mask), 来防止未来信息的提前泄露. 例如给定一个句子包含4个单词A, B, C, D, GPT需要用A预测B, 用A, B预测C, 用A, B, C预测D. 很显然的就是当要预测B时, 需要将B, C, D遮掩起来.

  • 具体的遮掩操作是在slef-attention进行softmax之前进行的, 一般的实现是将MASK的位置用一个无穷小的数值-inf来替换, 替换后执行softmax计算得到新的结果矩阵. 这样-inf的位置就变成了0. 如上图所示, 最后的矩阵可以很方便的做到当利用A预测B的时候, 只能看到A的信息; 当利用A, B预测C的时候, 只能看到A, B的信息.

  • 注意: 对比于经典的Transformer架构, 解码器模块采用了6个Decoder Block; GPT的架构中采用了12个Decoder Block.

3 GPT训练过程[¶](#3 GPT训练过程¶)

GPT的训练也是典型的两阶段过程:

  • 第一阶段: 无监督的预训练语言模型.
  • 第二阶段: 有监督的下游任务fine-tunning.

3.1 无监督的预训练语言模型[¶](#3.1 无监督的预训练语言模型¶)

给定句子U = u1, u2, ..., un, GPT训练语言模型时的目标是最大化下面的似然函数:

有上述公式可知, GPT是一个单向语言模型, 假设输入张量用h0表示, 则计算公式如下:

其中Wp是单词的位置编码, We是单词本身的word embedding. Wp的形状是max_seq_len, embedding_dim, We的形状是vocab_size, embedding_dim.

得到输入张量h0后, 要将h0传入GPT的Decoder Block中, 依次得到ht:

最后通过得到的ht来预测下一个单词:

3.2 有监督的下游任务fine-tunning[¶](#3.2 有监督的下游任务fine-tunning¶)

GPT经过预训练后, 会针对具体的下游任务对模型进行微调. 微调采用的是有监督学习, 训练样本包括单词序列x1, x2, ..., xn和label y. GPT微调的目标任务是根据单词序列x1, x2, ..., xn预测标签y.

其中WyWy表示预测输出的矩阵参数, 微调任务的目标是最大化下面的函数:

综合两个阶段的目标任务函数, 可知GPT的最终优化函数为:

4 小结[¶](#4 小结¶)

  • 学习了什么是GPT.

    • GPT是OpenAI公司提出的一种预训练语言模型.
    • 本质上来说, GPT是一个单向语言模型.
  • 学习了GPT的架构.

    • GPT采用了Transformer架构中的解码器模块.
    • GPT在使用解码器模块时做了一定的改造, 将传统的3层Decoder Block变成了2层Block, 删除了encoder-decoder attention子层, 只保留Masked Multi-Head Attention子层和Feed Forward子层.
    • GPT的解码器总共是由12个改造后的Decoder Block组成的.
  • 学习了GPT的预训练任务.

    • 第一阶段: 无监督的预训练语言模型. 只利用单词前面的信息来预测当前单词.
    • 第二阶段: 有监督的下游任务fine-tunning.

6 BERT GPT ELMo模型的对比

学习目标

  • 理解BERT, GPT, ELMo相互间的不同点.
  • 理解BERT, GPT, ELMo相互比较下的各自优点和缺点.

1 BERT, GPT, ELMo之间的不同点[¶](#1 BERT, GPT, ELMo之间的不同点¶)

  • 关于特征提取器:

    • ELMo采用两部分双层双向LSTM进行特征提取, 然后再进行特征拼接来融合语义信息.
    • GPT和BERT采用Transformer进行特征提取.
    • 很多NLP任务表明Transformer的特征提取能力强于LSTM, 对于ELMo而言, 采用1层静态token embedding + 2层LSTM, 提取特征的能力有限.
  • 单/双向语言模型:

    • 三者之中, 只有GPT采用单向语言模型, 而ELMo和BERT都采用双向语言模型.
    • ELMo虽然被认为采用了双向语言模型, 但实际上是左右两个单向语言模型分别提取特征, 然后进行特征拼接, 这种融合特征的能力比BERT一体化的融合特征方式弱.
    • 三者之中, 只有ELMo没有采用Transformer. GPT和BERT都源于Transformer架构, GPT的单向语言模型采用了经过修改后的Decoder模块, Decoder采用了look-ahead mask, 只能看到context before上文信息, 未来的信息都被mask掉了. 而BERT的双向语言模型采用了Encoder模块, Encoder只采用了padding mask, 可以同时看到context before上文信息, 以及context after下文信息.

2 BERT, GPT, ELMo各自的优点和缺点[¶](#2 BERT, GPT, ELMo各自的优点和缺点¶)

  • ELMo:

  • 优点:

    • 从早期的Word2Vec预训练模型的最大缺点出发, 进行改进, 这一缺点就是无法解决多义词的问题.
    • ELMo根据上下文动态调整word embedding, 可以解决多义词的问题.
  • 缺点:

    • ELMo使用LSTM提取特征的能力弱于Transformer.
    • ELMo使用向量拼接的方式融合上下文特征的能力弱于Transformer.
  • GPT:

  • 优点:

    • GPT使用了Transformer提取特征, 使得模型能力大幅提升.
  • 缺点:

    • GPT只使用了单向Decoder, 无法融合未来的信息.
  • BERT:

  • 优点:

    • BERT使用了双向Transformer提取特征, 使得模型能力大幅提升.
    • 添加了两个预训练任务, MLM + NSP的多任务方式进行模型预训练.
  • 缺点:

    • 模型过于庞大, 参数量太多, 需要的数据和算力要求过高, 训练好的模型应用场景要求高.
    • 更适合用于语言嵌入表达, 语言理解方面的任务, 不适合用于生成式的任务.

3 小结[¶](#3 小结¶)

学习了BERT, GPT, ELMo之间的区别: * 三者所选取的特征提取器不同. * BERT采用的是Transformer架构中的Encoder模块. * GPT采用的是Transformer架构中的Decoder模块. * ELMo采用的双层双向LSTM模块.

  • 三者所采用的语言模型单/双向不同.
    • BERT采用的是最彻底的双向语言模型, 可以同时关注context before和context after.
    • GPT采用的是单向语言模型, 即Transformer中的Decoder, 由于采用了mask机制, 所以未来信息context after都不可见.
    • ELMo表面上被认为是双向语言模型, 但实际上是左右两个单向LSTM模型分别提取特征, 在进行简单的拼接融合.

七.Transformer精选问答(拓展资料)

1 Transformer 各子模块作用

学习目标

  • 掌握Encoder模块的结构和作用
  • 掌握Decoder模块的结构和作用
  • 掌握其他模块的结构和作用

思考题:Transformer的结构是什么样的? 各个子模块各有什么作用?

1 Encoder模块[¶](#1 Encoder模块¶)

1.1 Encoder模块的结构和作用:[¶](#1.1 Encoder模块的结构和作用:¶)

  • 经典的Transformer结构中的Encoder模块包含6个Encoder Block.
  • 每个Encoder Block包含一个多头自注意力层, 和一个前馈全连接层.

1.2 关于Encoder Block[¶](#1.2 关于Encoder Block¶)

  • 在Transformer架构中, 6个一模一样的Encoder Block层层堆叠在一起, 共同组成完整的Encoder, 因此剖析一个Block就可以对整个Encoder的内部结构有清晰的认识.

1.3 多头自注意力层(self-attention)[¶](#1.3 多头自注意力层(self-attention)¶)

首先来看self-attention的计算规则图:

  • 上述attention可以被描述为将query和key-value键值对的一组集合映射到输出, 输出被计算为values的加权和, 其中分配给每个value的权重由query与对应key的相似性函数计算得来. 这种attention的形式被称为Scaled Dot-Product Attention, 对应的数学公式形式如下:
  • 所谓的多头self-attention层, 则是先将Q, K, V经过参数矩阵进行映射, 再做self-attention, 最后将结果拼接起来送入一个全连接层即可.

上述的多头self-attention, 对应的数学公式形式如下:

  • 前馈全连接层模块

    • 前馈全连接层模块, 由两个线性变换组成, 中间有一个Relu激活函数, 对应的数学公式形式如下:
  • 注意: 原版论文中的前馈全连接层, 输入和输出的维度均为d_model = 512, 层内的连接维度d_ff = 2048, 均采用4倍的大小关系.

  • 前馈全连接层的作用: 单纯的多头注意力机制并不足以提取到理想的特征, 因此增加全连接层来提升网络的能力.

1.4 Decoder模块[¶](#1.4 Decoder模块¶)

  • Decoder模块的结构和作用:

    • 经典的Transformer结构中的Decoder模块包含6个Decoder Block.
    • 每个Decoder Block包含三个子层.
      • 一个多头self-attention层
      • 一个Encoder-Decoder attention层
      • 一个前馈全连接层
  • Decoder Block中的多头self-attention层

    • Decoder中的多头self-attention层与Encoder模块一致, 但需要注意的是Decoder模块的多头self-attention需要做look-ahead-mask, 因为在预测的时候"不能看见未来的信息", 所以要将当前的token和之后的token全部mask.
  • Decoder Block中的Encoder-Decoder attention层

    • 这一层区别于自注意力机制的Q = K = V, 此处矩阵Q来源于Decoder端经过上一个Decoder Block的输出, 而矩阵K, V则来源于Encoder端的输出, 造成了Q != K = V的情况.
    • 这样设计是为了让Decoder端的token能够给予Encoder端对应的token更多的关注.
  • Decoder Block中的前馈全连接层

    • 此处的前馈全连接层和Encoder模块中的完全一样.
  • Decoder Block中有2个注意力层的作用: 多头self-attention层是为了拟合Decoder端自身的信息, 而Encoder-Decoder attention层是为了整合Encoder和Decoder的信息.

1.5 Add & Norm模块[¶](#1.5 Add & Norm模块¶)

  • Add & Norm模块接在每一个Encoder Block和Decoder Block中的每一个子层的后面. 具体来说Add表示残差连接, Norm表示LayerNorm.

    • 对于每一个Encoder Block, 里面的两个子层后面都有Add & Norm.
    • 对于每一个Decoder Block, 里面的三个子层后面都有Add & Norm.
    • 具体的数学表达形式为: LayerNorm(x + Sublayer(x)), 其中Sublayer(x)为子层的输出.
  • Add残差连接的作用: 和其他神经网络模型中的残差连接作用一致, 都是为了将信息传递的更深, 增强模型的拟合能力. 试验表明残差连接的确增强了模型的表现.

  • Norm的作用: 随着网络层数的额增加, 通过多层的计算后参数可能会出现过大, 过小, 方差变大等现象, 这会导致学习过程出现异常, 模型的收敛非常慢. 因此对每一层计算后的数值进行规范化可以提升模型的表现.

1.6 位置编码器Positional Encoding[¶](#1.6 位置编码器Positional Encoding¶函数和余弦函数来编码位置信息, 如)函数和余弦函数来编码位置信息, 如

  • Transformer中直接采用正弦函数和余弦函数来编码位置信息, 如下图所示:
  • 需要注意: 三角函数应用在此处的一个重要的优点, 因为对于任意的PE(pos+k), 都可以表示为PE(pos)的线性函数, 大大方便计算. 而且周期性函数不受序列长度的限制, 也可以增强模型的泛化能力.

2 小结[¶](#2 小结¶)

  • Encoder模块

    • 经典的Transformer架构中的Encoder模块包含6个Encoder Block.
    • 每个Encoder Block包含两个子模块, 分别是多头自注意力层, 和前馈全连接层.
      • 多头自注意力层采用的是一种Scaled Dot-Product Attention的计算方式, 实验结果表明, Mul ti-head可以在更细致的层面上提取不同head的特征, 比单一head提取特征的效果更佳.
      • 前馈全连接层是由两个全连接层组成, 线性变换中间增添一个Relu激活函数, 具体的维度采用4倍关系, 即多头自注意力的d_model=512, 则层内的变换维度d_ff=2048.
  • Decoder模块

    • 经典的Transformer架构中的Decoder模块包含6个Decoder Block.
    • 每个Decoder Block包含3个子模块, 分别是多头自注意力层, Encoder-Decoder Attention层, 和前馈全连接层.
      • 多头自注意力层采用和Encoder模块一样的Scaled Dot-Product Attention的计算方式, 最大的 区别在于需要添加look-ahead-mask, 即遮掩"未来的信息".
      • Encoder-Decoder Attention层和上一层多头自注意力层最主要的区别在于Q != K = V, 矩阵Q来源于上一层Decoder Block的输出, 同时K, V来源于Encoder端的输出.
      • 前馈全连接层和Encoder中完全一样.
  • Add & Norm模块

    • Add & Norm模块接在每一个Encoder Block和Decoder Block中的每一个子层的后面.
    • 对于每一个Encoder Block, 里面的两个子层后面都有Add & Norm.
    • 对于每一个Decoder Block, 里面的三个子层后面都有Add & Norm.
    • Add表示残差连接, 作用是为了将信息无损耗的传递的更深, 来增强模型的拟合能力.
    • Norm表示LayerNorm, 层级别的数值标准化操作, 作用是防止参数过大过小导致的学习过程异常, 模型收敛特别慢的问题.
  • 位置编码器Positional Encoding

    • Transformer中采用三角函数来计算位置编码.
    • 因为三角函数是周期性函数, 不受序列长度的限制, 而且这种计算方式可以对序列中不同位置的编码的重要程度同等看待.
  • 本节常见问答

    • Transformer的结构是什么样的? 各个子模块各有什么作用?

2 Transformer Decoder模块

学习目标

  • 掌握Transformer结构中的Decoder端的输入张量特点和含义.
  • 掌握Decoder在训练阶段的输入是什么.
  • 掌握Decoder在预测阶段的输入是什么.

思考题:Transformer结构中的Decoder端具体输入是什么? 在训练阶段和预测阶段一致吗?

1 Decoder端的输入解析[¶](#1 Decoder端的输入解析¶)

1.1 Decoder端的架构[¶](#1.1 Decoder端的架构¶)

Transformer原始论文中的Decoder模块是由N=6个相同的Decoder Block堆叠而成, 其中每一个Block是由3个子模块构成, 分别是多头self-attention模块, Encoder-Decoder attention模块, 前馈全连接层模块.

  • 6个Block的输入不完全相同:
    • 最下面的一层Block接收的输入是经历了MASK之后的Decoder端的输入 + Encoder端的输出.
    • 其他5层Block接收的输入模式一致, 都是前一层Block的输出 + Encoder端的输出.

1.2 Decoder在训练阶段的输入解析[¶](#1.2 Decoder在训练阶段的输入解析¶)

  • 从第二层Block到第六层Block的输入模式一致, 无需特殊处理, 都是固定操作的循环处理.
  • 聚焦在第一层的Block上: 训练阶段每一个time step的输入是上一个time step的输入加上真实标签序列向后移一位. 具体来说, 假设现在的真实标签序列等于"How are you?", 当time step=1时, 输入张量为一个特殊的token, 比如"SOS"; 当time step=2时, 输入张量为"SOS How"; 当time step=3时, 输入张量为"SOS How are", 以此类推...
  • 注意: 在真实的代码实现中, 训练阶段不会这样动态输入, 而是一次性的把目标序列全部输入给第一层的Block, 然后通过多头self-attention中的MASK机制对序列进行同样的遮掩即可.

1.3 Decoder在预测阶段的输入解析[¶](#1.3 Decoder在预测阶段的输入解析¶)

  • 同理于训练阶段, 预测时从第二层Block到第六层Block的输入模式一致, 无需特殊处理, 都是固定操作的循环处理.
  • 聚焦在第一层的Block上: 因为每一步的输入都会有Encoder的输出张量, 因此这里不做特殊讨论, 只专注于纯粹从Decoder端接收的输入. 预测阶段每一个time step的输入是从time step=0, input_tensor="SOS"开始, 一直到上一个time step的预测输出的累计拼接张量. 具体来说:
    • 当time step=1时, 输入的input_tensor="SOS", 预测出来的输出值是output_tensor="What";
    • 当time step=2时, 输入的input_tensor="SOS What", 预测出来的输出值是output_tensor="is";
    • 当time step=3时, 输入的input_tensor="SOS What is", 预测出来的输出值是output_tensor="the";
    • 当time step=4时, 输入的input_tensor="SOS What is the", 预测出来的输出值是output_tensor="matter";
    • 当time step=5时, 输入的input_tensor="SOS What is the matter", 预测出来的输出值是output_tensor="?";
    • 当time step=6时, 输入的input_tensor="SOS What is the matter ?", 预测出来的输出值是output_tensor="EOS", 代表句子的结束符, 说明解码结束, 预测结束.

2 小结[¶](#2 小结¶)

  • 在Transformer结构中的Decoder模块的输入, 区分于不同的Block, 最底层的Block输入有其特殊的地方. 第二层到第六层的输入一致, 都是上一层的输出和Encoder的输出.

  • 最底层的Block在训练阶段, 每一个time step的输入是上一个time step的输入加上真实标签序列向后移一位. 具体来看, 就是每一个time step的输入序列会越来越长, 不断的将之前的输入融合进来.

  • 最底层的Block在训练阶段, 真实的代码实现中, 采用的是MASK机制来模拟输入序列不断添加的过程.

  • 最底层的Block在预测阶段, 每一个time step的输入是从time step=0开始, 一直到上一个time step的预测值的累积拼接张量. 具体来看, 也是随着每一个time step的输入序列会越来越长. 相比于训练阶段最大的不同是这里不断拼接进来的token是每一个time step的预测值, 而不是训练阶段每一个time step取得的groud truth值.


3 Self attention机制详解

学习目标

  • 掌握self-attention的机制和原理.
  • 掌握为什么要使用三元组(Q, K, V)来计算self-attention.
  • 理解softmax函数的输入是如何影响输出分布的.
  • 理解softmax函数反向传播进行梯度求导的数学过程.
  • 理解softmax函数出现梯度消失的原因.
  • 理解self-attention计算规则中归一化的原因.

思考题1: Transformer中一直强调的self-attention是什么? 为什么能发挥如此大的作用? 计算的时候如果不使用三元组(Q, K, V), 而仅仅使用(Q, V)或者(K, V)或者(V)行不行?

思考题2:self-attention公式中的归一化有什么作用? 为什么要添加scaled?

1 Self-attention的机制和原理[¶](#1 Self-attention的机制和原理¶)

self-attention是一种通过自身和自身进行关联的attention机制, 从而得到更好的representation来表达自身.

self-attention是attention机制的一种特殊情况,在self-attention中, Q=K=V, 序列中的每个单词(token)都和该序列中的其他所有单词(token)进行attention规则的计算.

attention机制计算的特点在于, 可以直接跨越一句话中不同距离的token, 可以远距离的学习到序列的知识依赖和语序结构.

  • 从上图中可以看到, self-attention可以远距离的捕捉到语义层面的特征(its的指代对象是Law).

  • 应用传统的RNN, LSTM, 在获取长距离语义特征和结构特征的时候, 需要按照序列顺序依次计算, 距离越远的联系信息的损耗越大, 有效提取和捕获的可能性越小.

  • 但是应用self-attention时, 计算过程中会直接将句子中任意两个token的联系通过一个计算步骤直接联系起来,

关于self-attention为什么要使用(Q, K, V)三元组而不是其他形式:

  • 首先一条就是从分析的角度看, 查询Query是一条独立的序列信息, 通过关键词Key的提示作用, 得到最终语义的真实值Value表达, 数学意义更充分, 完备.
  • 这里不使用(K, V)或者(V)没有什么必须的理由, 也没有相关的论文来严格阐述比较试验的结果差异, 所以可以作为开放性问题未来去探索, 只要明确在经典self-attention实现中用的是三元组就好.

self-attention公式中的归一化有什么作用? 为什么要添加scaled?

2 Self-attention中的归一化概述[¶](#2 Self-attention中的归一化概述¶)

  • 训练上的意义: 随着词嵌入维度d_k的增大, q * k 点积后的结果也会增大, 在训练时会将带有饱和区间的激活函数(比如:sigmoid激活函数、tanh激活函数、逻辑回归softmax)推入梯度非常小的区域, 可能出现梯度消失的现象, 造成模型收敛困难.

  • 数学上的意义: 假设q和k的统计变量是满足标准正态分布的独立随机变量, 意味着q和k满足均值为0, 方差为1. 那么q和k的点积结果就是均值为0, 方差为d_k, 为了抵消这种方差被放大d_k倍的影响, 在计算中主动将点积缩放1/sqrt(d_k), 这样点积后的结果依然满足均值为0, 方差为1.

3 softmax的梯度变化[¶](#3 softmax的梯度变化¶)

这里我们分3个步骤来解释softmax的梯度问题:

  • 第一步: softmax函数的输入分布是如何影响输出的.
  • 第二步: softmax函数在反向传播的过程中是如何梯度求导的.
  • 第三步: softmax函数出现梯度消失现象的原因.

3.1 softmax函数的输入分布是如何影响输出的[¶](#3.1 softmax函数的输入分布是如何影响输出的¶)

  • 对于一个输入向量x, softmax函数将其做了一个归一化的映射, 首先通过自然底数e将输入元素之间的差距先"拉大", 然后再归一化为一个新的分布. 在这个过程中假设某个输入x中最大的元素下标是k, 如果输入的数量级变大(就是x中的每个分量绝对值都很大), 那么在数学上会造成y_k的值非常接近1.

  • 具体用一个例子来演示, 假设输入的向量x = a, a, 2a, 那么随便给几个不同数量级的值来看看对y3产生的影响

    a = 1时, y3 = 0.5761168847658291 # e^2 / (e^1 + e^1 + e^2))
    a = 10时, y3 = 0.9999092083843412 # e^20 / (e^10 + e^10 + e^20))
    a = 100时, y3 = 1.0 # e^200 / (e^100 + e^100 + e^200))

  • 采用一段实例代码将a在不同取值下, 对应的y3全部画出来, 以曲线的形式展示:
复制代码
from math import exp
from matplotlib import pyplot as plt
import numpy as np 
f = lambda x: exp(x * 2) / (exp(x) + exp(x) + exp(x * 2))
x = np.linspace(0, 100, 100)
y_3 = [f(x_i) for x_i in x]
plt.plot(x, y_3)
plt.show()
  • 得到如下的曲线:
  • 从上图可以很清楚的看到输入元素的数量级对softmax最终的分布影响非常之大

  • 结论: 在输入元素的数量级较大时,softmax函数几乎将全部的概率分布都分配给了最大值分量所对应的标签。通俗的讲:数据的方差变大(离散程度变大),最大值强占了所有概率。

3.2 softmax函数在反向传播的过程中是如何梯度求导的[¶](#3.2 softmax函数在反向传播的过程中是如何梯度求导的¶)

softmax函数在反向传播中容易梯度消失,所以要看一看softmax函数在反向传播中是如何求导的。

首先定义神经网络的输入和输出:

反向传播就是输出端的损失函数对输入端求偏导的过程, 这里要分两种情况, 第一种如下所示:

第二种如下所示:

经过对两种情况分别的求导计算, 可以得出最终的结论如下:

把抽象的数学公式,映射成矩阵表示,见3.3节表示(i=j时,两个矩阵对角线 - 对角线 ;i!=j时,对应位置相减)。

3.3 softmax函数出现梯度消失现象的原因[¶](#3.3 softmax函数出现梯度消失现象的原因¶)

  • 根据第二步中softmax函数的求导结果, 可以将最终的结果以矩阵形式展开如下:
  • 根据第一步中的讨论结果, 当输入x的分量值较大时, softmax函数会将大部分概率分配给最大的元素, 假设最大元素是x1, 那么softmax的输出分布将产生一个接近one-hot的结果张量y_ = 1, 0, 0,..., 0, 此时结果矩阵变为:
  • 结论: 综上可以得出, 所有的梯度都消失为0(接近于0), 参数几乎无法更新, 模型收敛困难.

4 维度与点积大小的关系[¶](#4 维度与点积大小的关系¶)

  • 针对为什么维度会影响点积的大小, 原始论文中有这样的一点解释如下:

    To illustrate why the dot products get large, assume that the components of q and k
    are independent random variables with mean 0 and variance 1. Then their doct product,
    q*k = (q1k1+q2k2+......+q(d_k)k(d_k)), has mean 0 and variance d_k.

  • 我们分两步对其进行一个推导, 首先就是假设向量q和k的各个分量是相互独立的随机变量, X = q_i, Y = k_i, X和Y各自有d_k个分量, 也就是向量的维度等于d_k, 有E(X) = E(Y) = 0, 以及D(X) = D(Y) = 1.

  • 可以得到E(XY) = E(X)E(Y) = 0 * 0 = 0

  • 同理, 对于D(XY)推导如下:

  • 根据期望和方差的性质, 对于互相独立的变量满足下式:
  • 上述公式,简读为:和的期望,等于期望的和;和的方差等于方差的和

  • 根据上面的公式, 可以很轻松的得出q*k的均值为E(qk) = 0, D(qk) = d_k.

  • 所以方差越大, 对应的qk的点积就越大, 这样softmax的输出分布就会更偏向最大值所在的分量.

  • 一个技巧就是将点积除以sqrt(d_k), 将方差在数学上重新"拉回1", 如下所示:

  • 最终的结论: 通过数学上的技巧将方差控制在1, 也就有效的控制了点积结果的发散, 也就控制了对应的梯度消失的问题!

5 小结[¶](#5 小结¶)

  • self-attention机制的重点是使用三元组(Q, K, V)参与规则运算, 这里面Q=K=V.

  • self-attention最大的优势是可以方便有效的提取远距离依赖的特征和结构信息, 不必向RNN那样依次计算产生传递损耗.

  • 关于self-attention采用三元组的原因, 经典实现的方式数学意义明确, 理由充分, 至于其他方式的可行性暂时没有论文做充分的对比试验研究.

  • 学习了softmax函数的输入是如何影响输出分布的.

    • softmax函数本质是对输入的数据分布做一次归一化处理, 但是输入元素的数量级对softmax最终的分布影响非常之大.
    • 在输入元素的数量级较大时, softmax函数几乎将全部的概率分布都分配给了最大值分量所对应的标签.
  • 学习了softmax函数在反向传播的过程中是如何梯度求导的.

    • 具体的推导过程见讲义正文部分, 注意要分两种情况讨论, 分别处理.
  • 学习了softmax函数出现梯度消失现象的原因.

    • 结合第一步, 第二步的结论, 可以很清楚的看到最终的梯度矩阵接近于零矩阵, 这样在进行参数更新的时候就会产生梯度消失现象.
  • 学习了维度和点积大小的关系推导.

    • 通过期望和方差的推导理解了为什么点积会造成方差变大.
    • 理解了通过数学技巧除以sqrt(d_k)就可以让方差恢复成1.

4 Multi head Attention详解

学习目标

  • 掌握Transformer中应用多头注意力的原因.
  • 掌握Transformer中多头注意力的计算方式.

思考题:Transformer为什么需要进行Multi-head Attention? Multi-head Attention的计算过程是什么?

1 采用Multi-head Attention的原因[¶](#1 采用Multi-head Attention的原因¶)

  • 原始论文中提到进行Multi-head Attention的原因是将模型分为多个头, 可以形成多个子空间, 让模型去关注不同方面的信息, 最后再将各个方面的信息综合起来得到更好的效果.

  • 多个头进行attention计算最后再综合起来, 类似于CNN中采用多个卷积核的作用, 不同的卷积核提取不同的特征, 关注不同的部分, 最后再进行融合.

  • 直观上讲, 多头注意力有助于神经网络捕捉到更丰富的特征信息.

2 Multi-head Attention的计算方式[¶](#2 Multi-head Attention的计算方式¶)

  • Multi-head Attention和单一head的Attention唯一的区别就在于, 其对特征张量的最后一个维度进行了分割, 一般是对词嵌入的embedding_dim=512进行切割成head=8, 这样每一个head的嵌入维度就是512/8=64, 后续的Attention计算公式完全一致, 只不过是在64这个维度上进行一系列的矩阵运算而已.

  • 在head=8个头上分别进行注意力规则的运算后, 简单采用拼接concat的方式对结果张量进行融合就得到了Multi-head Attention的计算结果.

3 小结[¶](#3 小结¶)

  • 学习了Transformer架构采用Multi-head Attention的原因.

    • 将模型划分为多个头, 分别进行Attention计算, 可以形成多个子空间, 让模型去关注不同方面的信息特征, 更好的提升模型的效果.
    • 多头注意力有助于神经网络捕捉到更丰富的特征信息.
  • 学习了Multi-head Attention的计算方式.

    • 对特征张量的最后一个维度进行了分割, 一般是对词嵌入的维度embedding_dim进行切割, 切割后的计算规则和单一head完全一致.
    • 在不同的head上应用了注意力计算规则后, 得到的结果张量直接采用拼接concat的方式进行融合, 就得到了Multi-head Attention的结果张量.

5 Transformer优势

学习目标

  • 掌握Transformer相比于RNN/LSTM的优势和背后的原因.
  • 掌握Transformer架构的并行化是如何进行的.
  • 理解为什么采用这样的方式可以实现Transformer的并行化.
  • 掌握Transformer可以替代seq2seq的核心原因.

思考题:transformer架构的并行化是如何进行的? 具体体现在哪里?

思考题:Transformer相比于RNN/LSTM有什么优势? 为什么?

思考题:为什么说Transformer可以代替seq2seq?

1 Transformer的并行计算[¶](#1 Transformer的并行计算¶)

对于Transformer比传统序列模型RNN/LSTM具备优势的第一大原因就是强大的并行计算能力.

  • 对于RNN来说, 任意时刻t的输入是时刻t的输入x(t)和上一时刻的隐藏层输出h(t-1), 经过运算后得到当前时刻隐藏层的输出h(t), 这个h(t)也即将作为下一时刻t+1的输入的一部分. 这个计算过程是RNN的本质特征, RNN的历史信息是需要通过这个时间步一步一步向后传递的. 而这就意味着RNN序列后面的信息只能等到前面的计算结束后, 将历史信息通过hidden state传递给后面才能开始计算, 形成链式的序列依赖关系, 无法实现并行.
  • 对于Transformer结构来说, 在self-attention层, 无论序列的长度是多少, 都可以一次性计算所有单词之间的注意力关系, 这个attention的计算是同步的, 可以实现并行.

2 Transformer架构的并行化过程[¶](#2 Transformer架构的并行化过程¶)

2.1 Transformer架构中Encoder的并行化[¶](#2.1 Transformer架构中Encoder的并行化¶)

首先Transformer的并行化主要体现在Encoder模块上.

  • 上图最底层绿色的部分, 整个序列所有的token可以并行的进行Embedding操作, 这一层的处理是没有依赖关系的.

  • 上图第二层土黄色的部分, 也就是Transformer中最重要的self-attention部分, 这里对于任意一个单词比如x1, 要计算x1对于其他所有token的注意力分布, 得到z1. 这个过程是具有依赖性的, 必须等到序列中所有的单词完成Embedding才可以进行. 因此这一步是不能并行处理的. 但是从另一个角度看, 我们真实计算注意力分布的时候, 采用的都是矩阵运算, 也就是可以一次性的计算出所有token的注意力张量, 从这个角度看也算是实现了并行, 只是矩阵运算的"并行"和词嵌入的"并行"概念上不同而已.

  • 上图第三层蓝色的部分, 也就是前馈全连接层, 对于不同的向量z之间也是没有依赖关系的, 所以这一层是可以实现并行化处理的. 也就是所有的向量z输入Feed Forward网络的计算可以同步进行, 互不干扰.

2.2 Transformer架构中Decoder的并行化

其次Transformer的并行化也部分的体现在Decoder模块上.

  • Decoder模块在训练阶段采用了并行化处理. 其中Self-Attention和Encoder-Decoder Attention两个子层的并行化也是在进行矩阵乘法, 和Encoder的理解是一致的. 在进行Embedding和Feed Forward的处理时, 因为各个token之间没有依赖关系, 所以也是可以完全并行化处理的, 这里和Encoder的理解也是一致的.

  • Decoder模块在预测阶段基本上不认为采用了并行化处理. 因为第一个time step的输入只是一个"SOS", 后续每一个time step的输入也只是依次添加之前所有的预测token.

  • 注意: 最重要的区别是训练阶段目标文本如果有20个token, 在训练过程中是一次性的输入给Decoder端, 可以做到一些子层的并行化处理. 但是在预测阶段, 如果预测的结果语句总共有20个token, 则需要重复处理20次循环的过程, 每次的输入添加进去一个token, 每次的输入序列比上一次多一个token, 所以不认为是并行处理.

3 Transformer的特征抽取能力

对于Transformer比传统序列模型RNN/LSTM具备优势的第二大原因就是强大的特征抽取能力.

  • Transformer因为采用了Multi-head Attention结构和计算机制, 拥有比RNN/LSTM更强大的特征抽取能力, 这里并不仅仅由理论分析得来, 而是大量的试验数据和对比结果, 清楚的展示了Transformer的特征抽取能力远远胜于RNN/LSTM.
  • 注意: 不是越先进的模型就越无敌, 在很多具体的应用中RNN/LSTM依然大有用武之地, 要具体问题具体分析.

4 为什么说Transformer可以代替seq2seq?

4.1 seq2seq的两大缺陷

  • seq2seq架构的第一大缺陷是将Encoder端的所有信息压缩成一个固定长度的语义向量中, 用这个固定的向量来代表编码器端的全部信息. 这样既会造成信息的损耗, 也无法让Decoder端在解码的时候去用注意力聚焦哪些是更重要的信息.

  • seq2seq架构的第二大缺陷是无法并行, 本质上和RNN/LSTM无法并行的原因一样.

4.2 Transformer的改进

  • Transformer架构同时解决了seq2seq的两大缺陷, 既可以并行计算, 又应用Multi-head Attention机制来解决Encoder固定编码的问题, 让Decoder在解码的每一步可以通过注意力去关注编码器输出中最重要的那些部分.

5 小结

  • 学习了Transformer相比于RNN/LSTM的优势和原因.

    • 第一大优势是并行计算的优势.
    • 第二大优势是特征提取能力强.
  • 学习了Transformer架构中Encoder模块的并行化机制.

    • Encoder模块在训练阶段和测试阶段都可以实现完全相同的并行化.
    • Encoder模块在Embedding层, Feed Forward层, Add & Norm层都是可以并行化的.
    • Encoder模块在self-attention层, 因为各个token之间存在依赖关系, 无法独立计算, 不是真正意义上的并行化.
    • Encoder模块在self-attention层, 因为采用了矩阵运算的实现方式, 可以一次性的完成所有注意力张量的计算, 也是另一种"并行化"的体现.
  • 学习了Transformer架构中Decoder模块的并行化机制.

    • Decoder模块在训练阶段可以实现并行化.
    • Decoder模块在训练阶段的Embedding层, Feed Forward层, Add & Norm层都是可以并行化的.
    • Decoder模块在self-attention层, 以及Encoder-Decoder Attention层, 因为各个token之间存在依赖关系, 无法独立计算, 不是真正意义上的并行化.
    • Decoder模块在self-attention层, 以及Encoder-Decoder Attention层, 因为采用了矩阵运算的实现方式, 可以一次性的完成所有注意力张量的计算, 也是另一种"并行化"的体现.
    • Decoder模块在预测计算不能并行化处理.
  • 学习了seq2seq架构的两大缺陷.

    • 第一个缺陷是Encoder端的所有信息被压缩成一个固定的输出张量, 当序列长度较长时会造成比较严重的信息损耗.
    • 第二个缺陷是无法并行计算.
  • 学习了Transformer架构对seq2seq两大缺陷的改进.

    • Transformer应用Multi-head Attention机制让编码器信息可以更好的展示给解码器.
    • Transformer可以实现Encoder端的并行计算.