当前位置: 首页 > news >正文

Transformer自然语言处理实战pdf阅读

一.第一章 欢迎来到transformer的世界

1.解码器-编码器框架

在Transformer出现之前,NLP的最新技术是LSTM等循环架构。这些架 构通过在神经网络连接使用反馈循环,允许信息从一步传播到另一 步,使其成为对文本等序列数据进行建模的理想选择。如RNN接收一些输入(可以是单词或字符),将其馈送给神经网络, 然后输出一个称为隐藏状态的向量。同时,模型通过反馈循环将信息 反馈给自身,然后在下一步中使用。

RNN发挥重要作用的一个领域是机器翻译系统,其目标是将一种语言中 的单词序列映射到另一种语言。此类任务通常使用编码器-解码器或序 列到序列架构 来解决,这些架构非常适合输入和输出都是任意长度 序列的情况。编码器将输入序列的信息编码为通常称为最后隐藏状态 的数字表示。然后编码器将该状态传给解码器,解码器生成输出序 列。

通常,编码器和解码器组件可以是能够对序列进行建模的任何类型的 神经网络架构。但这种架构的一个弱点是编码器的最终隐藏状态会产 生信息瓶颈:它必须表示整个输入序列的含义,因为解码器在生成输 出时只能靠它来读取全部内容。从而很难处理长序列,因为当序列过 长时,在将所有内容压缩为单个固定表示的过程中可能会丢失序列开 头的信息。

幸运的是,有一种方法可以摆脱这一瓶颈,就是允许解码器访问编码 器的所有隐藏状态。这种通用机制称为注意力 。它是许多现代神经 网络架构中的关键组件。了解整合了注意力机制的RNN能够帮助我们更 好地理解Transformer架构。

2.注意力机制

注意力机制的主要思想是,编码器不再像图1-3那样对整个输入序列只 生成一个状态了,而是为每个表示都生成一个状态,即解码器可以访 问编码器所有隐藏状态。但是,同时处理所有状态会给解码器带来巨 大的输入数量,因此需要一些机制来按优先级使用状态。这就是注意 力的用武之地:它让解码器在每个解码时间步为每个编码器状态分配 不同量的权重或“注意力”。

下面是引入注意力机制之前编码器-解码器结构之间的对比

尽管注意力机制能够带来更好的翻译,但在编码器和解码器中使用循 环神经网络模型仍然存在一个重大缺点:计算本质上是顺序的,不能 跨输入序列并行化。

Transformer引入了一种新的建模范式:完全放弃循环,改为使用一种 特殊形式的注意力——自注意力:允许注意力对神经网络同一层 中的所有状态进行操作。如图1-6所示,其中编码器和解码器都有自己 的自注意力机制,其输出被馈送到前馈神经网络(FF NN)。这种架 构的训练速度比循环神经网络模型快得多,从而为NLP许多最新突破铺 平了道路。

3.NLP的迁移学习

如今,计算机视觉的常见做法是使用迁移学习,即在一项任务上训练 像ResNet这样的卷积神经网络,然后在新任务上对其进行适配或微 调。迁移学习允许网络利用从原始任务中学到的知识。在架构上,这 是指将模型拆分为主体和头,其中头是指针对任务的网络。在训练期 间,主体学习来源于领域的广泛特征,并将学习到的权重用于初始化 新任务的新模型 。与传统的监督学习相比,这种方法通常会产生高 质量的模型,这些模型在各种下游任务中效果更佳,并且使用的标注 数据更少。

在计算机视觉中,模型首先在包含数百万张图像的ImageNet (https://image-net.org)等大规模数据集上进行训练。这个过程称 为预训练,其主要目的是向模型传授图像的基本特征,例如边或颜 色。然后,这些预训练模型可以在下游任务上进行微调,例如使用相 对较少的标注示例(通常每个类几百个)对花种进行分类。微调模型 通常比在相同数量的标注数据上从头开始训练的监督模型具有更高的 准确率。 尽管迁移学习成为计算机视觉的标准方法,但多年来,人们并不清楚 NLP的类似预训练过程是怎么样的。从而导致NLP应用程序通常需要大 量的标注数据才能实现高性能。即便如此,这种性能也无法与视觉领 域的成就相提并论。

紧随其后的是ULMFiT,它使用预训练LSTM模型构建了一个 可以适用于各种任务的通用框架 。 如图1-8所示,ULMFiT包括以下三个主要步骤:

预训练

对于世界上大多数语言,获得大量数字化文本语料库可能很困难,英 语更是如此。寻找解决这一问题的方法是NLP研究方向的一个活跃领 域。 最初的训练目标非常简单:根据前面的单词预测下一个单词。这类任 务称为语言建模,所生成的模型称为语言模型。这种方法的优雅之处 在于不需要标注数据,并且可以使用来自Wikipedia等来源的大量可用 文本 。

领域适配

在大规模语料库上进行预训练得出语言模型之后,下一步就是将其适 配于业务领域语料库(例如,从Wikipedia迁移到电影评论的IMDb语料 库,如图1-8所示)。这一阶段仍然使用语言建模方法,但现在模型必 须预测目标语料库中的下一个单词。

微调

这一步使用目标任务的分类层对语言模型进行微调(例如,对图1-8中 的电影评论的情感进行分类)

通过引入一个可行的NLP预训练和迁移学习框架,ULMFiT填补了令 Transformer起飞的缺失部分。2018年,发布了两类将自注意力与迁移 学习相结合的Transformer模型:

1)GPT

仅使用Transformer架构的解码器部分,以及与ULMFiT相同的语言建模 方法。GPT在BookCorpus1上进行预训练,该语料库由7000本来自冒 险、幻想和浪漫等各种流派的图书组成。

2)BERT

仅使用Transformer架构的编码器部分,以及一种称为掩码语言建模的 特殊形式的语言建模。掩码语言建模的目的是预测文本中随机掩码的 单词。如,给定一个句子,如 “I looked at my[MASK]and saw that[MASK]was late.”,该 模型需要预测[MASK]所掩码的单词最有可能的候选者。BERT在 BookCorpus和英语Wikipedia上进行预训练。

但是由于各个研究实验室在不同的框架(PyTorch或TensorFlow)上发 布模型,而这些模型无法兼容于其他框架,NLP从业者将这些模型移植 到他们自己的应用程序中经常会遇到困难。于是 Hugging FaceTransformers库(https://oreil.ly/Z79jF)这个能够 跨50多个架构的统一API应运而生。该库催化了Transformer研究的爆 炸式增长,并迅速渗透到NLP从业者中,从而可以轻松地将 Transformer模型集成到当今的许多实际应用中。

4.Hugging Face Transformers库:提供规范化接口

将新颖的机器学习架构应用于新任务可能是一项复杂的任务,通常涉 及以下步骤:

1.将模型架构付诸代码实现(通常基于PyTorch或TensorFlow)。

2.从服务器加载预训练权重(如果有的话)。

3.预处理输入并传给模型,然后应用一些针对具体任务的后处理。

4.实现数据加载器并定义损失函数和优化器来训练模型。

5.Transformer应用概览

5.1文本分类

 在本章中,我 们将从pipeline开始,pipeline把将原始文本转换为微调模型的一组 预测所需的所有步骤都抽象出来。 我们可以通过调用Hugging Face Transformers库的pipeline()函数 并提供我们感兴趣的任务名称来实例化pipeline:

本例的预测结果是模型非常确信输入文本具有正面情感

5.2命名实体识别

现在我们能够预测客户反馈的情感,但你经常还想知道反馈是否与特 定项目或服务有关。在NLP中,产品、地点和人等现实世界的对象称为 命名实体,从文本中提取它们称为命名实体识别(NER)。我们可以通 过加载以下pipeline并将我们的客户评论提供给它来应用NER:

你可以看到pipeline检测出所有实体,并且还为每个实体分配了一个 类别,例如ORG(组织)、LOC(位置)或PER(人)。在这里,我们使 用aggregation strategy参数根据模型的预测对单词进行分组。例 如,实体“Optimus Prime”由两个单词组成,但被分配到一个类 别:MISC(杂项)。score列告诉我们模型对其识别的实体的信心(概 率)。我们可以看到,它对“Decepticons”和第一个“Megatron”最 没有信心(概率最低),两者都未能作为一个实体分在同一组。

看到上表word列中那些奇怪的哈希符号(#)了吗?这些符号是模型的 词元分析器生成的,词元分析器将单词划分成称为词元的原子单元。 我们将在第2章讲述词元分析器的详细信息。

现在我们已经把文本中的所有命名实体提取出来了,但有时我们想问 更有针对性的问题。于是我们就需要问答pipeline。

5.3问答

我们通过下面的context参数把问答的上下文(即本节开头提到的文 本)提供给模型,通过question参数把问题提供给模型。模型将返回 回答。现在我们看看当询问客户反馈的相关问题时,会得到什么回 答:

我们可以看到,除了回答之外,pipeline还返回回答在上下文中的位 置,即start和end(就像NER标注一样)。我们将在第7章研究好几种 类型的问答,但本节这种特殊的类型称为提取式问答,因为答案是直 接从文本提取出来的。

5.4 文本摘要

文本摘要的目标是输入一段长文本,然后生成包含所有相关事实的简 短版本。这是一项比前面所讲任务复杂得多的任务,因为它需要模型 生成连贯的文本。我们通过以下熟悉的模式实例化文本摘要:

得出的摘要还不错!尽管其只是简单地复制粘贴部分原始文本,但该 模型能够捕捉到问题的根源,并正确识别“Bumblebee”(出现在输入 文本的末尾)是投诉者。

5.5 文本翻译

我们看到,模型同样生成了一个非常好的翻译,正确使用了德语的代 词,如“Ihrem”和“Sie”。在以上示例代码中,我们还展示了如何 通过model参数指定模型来替换默认模型,从而可以为你的应用程序选 择最佳模型,你可以在Hugging Face Hub上找到数千种语言对模 型。接下来我们看看最后一个应用,然后再开始介绍Hugging Face生 态系统。

假设你希望通过自动完成功能来撰写答复,从而更快地回复客户。你 可以按如下方式使用text-generation pipeline:

现在我们已经讲述了Transformer模型一些很酷的应用,你可能想知道 训练在哪里进行。我们在本章使用的所有模型都是公开可用的,并且 已经针对以上任务微调过了。一般来说,无论如何,你都会想根据自 己的数据微调模型,在后面的章节中,你将学习如何做到这一点。 但是,训练模型只是任何NLP项目的一小部分——能够有效地处理数 据、与同事共享结果,以及使你的工作可重复也是项目的关键组成部 分。幸运的是,围绕着Hugging Face Transformers库有一个由一整 套工具组成的大型生态系统,从而可以支持大部分现代机器学习工作 流程。我们一起看看这个生态系统吧。

5.6 Hugging Face生态系统

以Hugging Face Transformers库为基础,Hugging Face已经迅速 发展成一个完整的生态系统,它由许多库和工具组成,以加速你的NLP 和机器学习项目。Hugging Face生态系统主要由一系列库和Hub组 成,如图1-9所示。库提供相关代码,而Hub提供预训练的模型权重、 数据集、度量指标脚本等。

除了预训练模型之外,Hub还托管了用于度量指标的数据集和脚本,使 你可以重现已发布的结果或为应用程序利用其他数据。

Hub还提供Models(模型)和Datasets(数据集)卡片,用于记录模型 和数据集相关内容,以帮助你就它们是否适合你做出明智的决定。Hub 最酷的功能之一是,你可以直接通过各种特定于任务的交互式小部件 试用任何模型,如图1-11所示。

Hugging Face Tokenizers库

我们在本章看到的每个pipeline示例背后都有一个词元化 (tokenization)步骤,该步骤将原始文本拆分为称为词元的更小部 分。我们将在第2章详细介绍这个过程,但现在只需要知道词元可以是 单词、单词的一部分或只是标点符号等字符就足够了。Transformer模 型是在这些词元的数字表示上训练的,因此正确执行这一步对于整个 NLP项目非常重要!

Hugging Face Datasets库

Hugging Face Datasets库(https://oreil.ly/959YT)通过为可在 Hub(https://oreil.ly/Rdhcu)上找到的数千个数据集提供标准界面 来简化这个过程。它还提供智能缓存(因此你不需要每次运行代码时 都重复预处理工作),并通过利用称为内存映射的特殊机制来突破内 存限制,该机制将文件的内容存储到虚拟内存中,并使多个进程能够 更有效地修改文件。该库还可以与Pandas和NumPy等流行框架进行互操 作,因此你不必离开自己喜欢的数据整理工具。

Hugging Face Accelerate库

如果你曾经不得不用PyTorch编写自己的训练脚本,那么在尝试将笔记 本电脑上运行的代码移植到组织集群上运行的代码时,你可能会遇到 一些麻烦。Hugging Face Accelerate库 (https://oreil.ly/iRfDe)为常规训练操作增加了一个抽象层以负 责训练基础设施所需的所有逻辑。这可以通过在必要时简化基础架构 的更改来加速工作流。

Transformer的主要挑战

语言

NLP研究以英语为主。有一些支持其他语言的模型,但很难找到稀有或 资源少的语言的预训练模型。我们将在第4章探讨多语言Transformer 及其执行零样本学习跨语言迁移的能力。

数据可用性

尽管我们可以通过迁移学习来显著减少模型所需的标注训练数据量, 但与人类执行任务所需的量相比,依然差很多。我们将在第9章探讨如 何处理几乎没有标注数据可用的场景。

处理长文本

自注意力在段落长度的文本上效果非常好,但是在处理整个文档这样 长度的文本时,将变得非常昂贵。第11章将讨论缓解这种情况的方 法。

不透明度

与其他深度学习模型一样,Transformer在很大程度上是不透明的。人 们很难或不可能解开模型做出某种预测的“原因”。当需要通过这些 模型来做出关键决策时,这是一个特别艰巨的挑战。我们将在第2章和 第4章探讨一些探测Transformer模型误差的方法。

二.第二章,文本分类

现在想象一下,你是一名数据科学家,需要构建一个系统,可以自动 识别人们在Twitter上对你公司产品表达的情感状态,例如愤怒或喜 悦。在本章中,我们将使用一种名为DistilBERT 的BERT变体来解决 这个任务。该模型的主要优点是,在实现与BERT相当的性能的同时, 体积更小、效率更高。这使我们能够在几分钟内训练一个分类器,如 果你想训练一个更大的BERT模型,则只需更改预训练模型的 checkpoint。checkpoint对应于加载到给定Transformer架构中的权重 集。

1.数据集

为了构建我们的情感检测器,我们将使用一篇论文 中提供的数据 集,该论文探讨了英语推文所表示的情感。与大多数情感分析数据集 只涉及“正面”和“负面”这两种极性不同的是,这个数据集包含六 种基本情感:anger(愤怒)、disgust(厌恶)、fear(恐惧)、joy (喜悦)、sadness(悲伤)和surprise(惊讶)。我们的任务是训练 一个模型,将给定推文按其中一种情感进行分类。

我们将使用Hugging Face Datasets库从Hugging Face Hub (https://oreil.ly/959YT)下载数据。我们可以使用 list_datasets()函数查看Hub上有哪些可用数据集。

我们可以看到每个数据集都有一个名称,因此让我们使用 load_dataset()函数加载情感数据集:

我们可以看到它类似于Python字典,每个键对应不同的数据集分割

以上代码返回一个Dataset类的实例。Dataset对象是 Hugging Face Datasets库的核心数据结构之一,在本书中我们将探 索它的许多特性。

在本例中,text列的数据类型为字符串,而label列是一个特殊的 ClassLabel对象,它包含有关类名称及其映射到整数的信息。

如果我的数据集不在Hub上那该怎么办? 在本书的大部分示例中,我们将使用Hugging Face Hub库下载数据 集。但在许多情况下,你会发现自己处理的数据要么存储在你的笔记 本电脑上,要么存储在你的组织的远程服务器上。Datasets提供了多 个加载脚本来处理本地和远程数据集。最常见数据格式的示例参见表 2-1。

data_files参数可以为文件路径或者url

从Datasets到DataFrame

尽管Hugging Face Datasets库提供了许多底层的功能供我们切分和 处理数据,但我们通常将Dataset对象转换为Pandas DataFrame,这 样我们就可以使用高级API来进行可视化,这样做将非常方便。 Hugging Face Datasets库提供了set_format()方法,该方法允许我 们更改数据集的输出格式以进行转换。请注意,它不会改变底层的数 据格式(Arrow表),并且你可以随时切换到另一种格式。

查看类分布

对于处理文本分类问题,检查类的样本分布无论何时都是一个好主 意。相对于类平衡的数据集来说,一个类分布不平衡的数据集可能需 要在训练损失和度量指标方面采取不同的处理方法。 我们可以使用Pandas和Matplotlib快速地可视化类分布:

我们可以看到数据集严重不平衡;joy和sadness类频繁出现,大约是 love和surprise类的5~10倍。处理不平衡数据的方法包括: ●随机对少数类进行过采样(oversample)。 ●随机对多数类进行欠采样(undersample)。 ●收集更多来自未被充分表示的类的标注数据。 为了保持本章简单,我们不使用以上的任何方法,而将使用原始的、 不平衡的类。如果你想了解更多关于这些采样技术的内容,我们建议 你查看Imbalanced-learn库(https://oreil.ly/5XBhb)。需要注意 的是,在将数据集分割成训练/测试集之前不要应用采样方法,否则会 在两者之间造成大量泄漏!

2.将文本转换成词元

像DistilBERT这样的Transformer模型不能接收原始字符串作为输入。 它假定文本已经被词元化并编码为数字向量。词元化 (tokenization)是指将字符串分解为给模型使用的原子单元的步 骤。词元化的策略有好几种,具体哪种最佳通常需要从语料库中学 习。在讨论DistilBERT使用的Tokenizer之前,我们先讨论两种极端情 况:字符词元化和单词词元化。

1)字符词元化 最简单的词元化方案是按每个字符单独馈送到模型中。在Python中, str对象实际上是一组数据,这使我们可以用一行代码快速实现字符词 元化:

这是一个很好的开始,但我们还没有完成任务。我们的模型希望把每 个字符转换为一个整数,有时这个过程被称为数值化 (numericalization)。一个简单的方法是用一个唯一的整数来编码 每个唯一的词元(在这里为字符):

我们可以看到,我们得到了一个包括了每个字符到一个唯一性整数的 映射,即词元分析器的词表。我们现在可以使用token2idx将词元化的 文本转换成一个整数列表:

现在,每个词元都已映射到唯一的数字标识符(因此称为 input_ids)。最后一步是将input_ids转换为独热向量(onehot vector)的二维张量。在机器学习中,独热向量常常用于编码分 类数据(包括有序和无序数据)。

我们可以为每个类别创建一个新列,在 该类别为true时分配1,否则分配0。在Pandas中,可以使用 get_dummies()函数实现这点:

以上DataFrame中的每行是一个独热向量,一整行只有一个1,其他都 是0。现在,看看我们的input_ids,我们有类似的问题:这些元素的 取值之间引入了虚假的顺序关系。因为这个关系是虚假的,所以对两 个ID进行加减是一个没意义的操作。 如果我们将input_ids改成独热编码,结果就很容易解释:“热”的两 个条目表明是相同的两个词元。在PyTorch中,我们可以使用 one_hot()函数对input_ids进行独热编码:

本例共有38个输入词元,我们得到了一个20维的独热向量,因为我们 的词表包含了20个唯一字符。

2)单词词元化

与字符词元化相比,单词词元化将文本细分为单词,并将每个单词映 射到一个整数。单词词元化使模型跳过从字符学习单词的步骤,从而 降低训练过程的复杂性。

词表太大是一个问题,因为它导致了神经网络需要大量的参数。举例 来说,假设词表中有100万个唯一词项,并按照大多数NLP架构中的标 准步骤将100万维输入向量压缩到第一层神经网络中的1000维向量。这 样就导致了第一层的权重矩阵将包含100万×1千=10亿个权重。这已经 可以与最大的GPT-2模型 看齐了,该模型总计拥有大约15亿个参数!

3)子词词元化(既可 以保留输入信息又能保留文本结构)

子词词元化背后的基本思想是将字符和单词词元化的优点结合起来。 一方面,我们希望将生僻单词拆分成更小的单元,以使模型能够处理 复杂单词和拼写错误。另一方面,我们希望将常见单词作为唯一实体 保留下来,以便我们将输入长度保持在可管理的范围内。子词词元化 (以及单词词元化)是使用统计规则和算法从预训练语料库中学习 的。

在NLP中常用的子词词元化算法有几种,我们先从WordPiece 算法开 始,这是BERT和DistilBERT词元分析器使用的算法。了解WordPiece如 何工作最简单的方法是看它的运行过程。 Hugging Face Transformers库提供了一个很方便的AutoTokenizer 类,它能令你快速加载与预训练模型相关联的词元分析器——只需要 提供模型在Hub上的ID或本地文件路径,然后调用它的 from_pretrained()方法即可。我们先加载DistilBERT的词元分析器:

from transformers import AutoTokenizer
model_ckpt="distilbert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(model_ckpt)

AutoTokenizer类是“auto”类的一种(https://oreil.ly/h4YPz), 其任务是根据checkpoint的名称自动检索模型的配置、预训练权重或 词表。使用以上代码的优点是可以快速切换模型,但是你也可以手动 加载特定类。

和字符词元化一样,我们可以看到,单词映射成input_ids字段中的唯 一整数。

中的唯 一整数。我们将在2.2.4节中讨论attention mask字段的作用。现在 我们有了input_ids,我们可以通过使用词元分析器的 convert_ids_to_tokens()方法将它们转换回词元:

我们可以观察到三件事情。首先,序列的开头和末尾多了一些特殊的 词元:[CLS]和[SEP]。这些词元具体因模型而异,它们的主要作用是 指示序列的开始和结束。其次,词元都小写了,这是该checkpoint的 特性。最后,我们可以看到tokenizing和NLP都被拆分为两个词元,这 是有道理的,因为它们不是常用的单词。##前缀中的##izing和##p意 味着前面的字符串不是空白符,将带有这个前缀的词元转换回字符串 时,应当将其与前一个词元合并。AutoTokenizer类有一个 convert_tokens_to_string()方法可以做到这一点,所以让我们将它 应用到我们的词元:

对整个数据集进行词元化

我们将使用DatasetDict对象的map()方法来对整个语料库进行词元 化。

1)将我们的样本进行词元化的处理函 数:

这个函数将词元分析器应用于一个批量样本。padding=True表示以零 填充样本,以达到批量中最长样本的长度,truncation=True表示将样 本截断为模型的最大上下文大小。

此外,除了将编码后的推文返回为input_ids外,词元分析器还返回一 系列attention_mask数组。这是因为我们不希望模型被额外的填充词 元所困惑:注意力掩码(attention mask)允许模型忽略输入的填充 部分。图

3.训练文本分类器

如第1章所述,像DistilBERT这样的模型被预训练用于预测文本序列中 的掩码单词。然而,这些语言模型不能直接用于文本分类,我们需要 稍微修改它们。为了理解需要做哪些修改,我们来看一下基于编码器 的模型(如DistilBERT)的架构,如图2-4所示。

在DistilBERT的情况下,它在猜测掩码词元。

首先,文本会被词元化并表示为称为词元编码的独热向量。词元编码 的维度由词元分析器词表的大小决定,通常包括两万到二十万个唯一 性词元。接下来,这些词元编码会被转换为词元嵌入,即存在于低维 空间中的向量。然后,这些词元嵌入会通过编码器块层传递,以产生 每个输入词元的隐藏状态。对于语言建模的预训练目标 ,每个隐藏 状态都被馈送到一个层,该层预测掩码输入词元。对于分类任务,我 们将语言建模层替换为分类层。

实际上,PyTorch在实现中跳过了为词元编码创建独热向量的步骤,因 为将矩阵与独热向量相乘等同于从矩阵中选择一列。这可以通过直接 从矩阵中获取词元ID对应的列来完成。当我们使用nn.Embedding类 时,我们将在第3章中看到这一点。

我们有以下两种选择来基于Twitter数据集进行模型训练:

特征提取

我们将隐藏状态用作特征,只需训练分类器,而无须修改预训练模 型。

微调

我们对整个模型进行端到端的训练,这样还会更新预训练模型的参 数。 接下来我们将讲述基于DistilBERT的以上两种选择,以及这两种选择 的权衡取舍。

使用Transformer作为特征提取器

使用Transformer作为特征提取器相当简单。如图2-5所示,我们在训 练期间冻结主体的权重,并将隐藏状态用作分类器的特征。这种方法 的优点是,我们可以快速训练一个小型或浅层模型。这样的模型可以 是神经分类层或不依赖于梯度的方法,例如随机森林。这种方法特别 适用于没有GPU的场景,因为隐藏状态只需要预计算一次。

加载预训练模型

我们将使用Hugging Face Transformers库中另一个很方便的自动类 AutoModel。与AutoTokenizer类似,AutoModel具有 from_pretrained()方法,可用于加载预训练模型的权重。现在我们使 用该方法来加载DistilBERT checkpoint:

这里我们使用PyTorch来检查GPU是否可用(即代码 torch.cuda.is available()),然后将PyTorch的nn.Module.to()方 法与模型加载器链接起来(即代码to(device))。这确保了如果有 GPU,模型将在GPU上运行。如果没有,模型将在CPU上运行,不过这样 可能会慢很多。

AutoModel类将词元编码转换为嵌入向量,然后将它们馈送到编码器栈 中以返回隐藏状态。我们看一下如何从语料库中提取这些状态。

提取最终隐藏状态

作为预热,我们检索一个字符串的最终隐藏状态。我们需要做的第一 件事是对字符串进行编码并将词元转换为PyTorch张量。可以通过向词 元分析器提供return_tensors=”pt”参数来实现。具体如下:

我们可以看到,生成的张量形状为[batch size,n_tokens]。现在我 们已经将编码作为张量获取,最后一步是将它们放置在与模型相同的 设备上,并按以下方式传输入:

这里我们使用了torch.no_grad()上下文管理器来禁用梯度的自动计 算。这对推理很有用,因为它减少了计算的内存占用。根据模型配 置,输出可以包含多个对象,例如隐藏状态、损失或注意力,这些对 象以类似于Python中的namedtuple的形式排列。在我们的示例中,模 型输出是一个BaseModelOutput实例,并且包含了其属性名,我们可以 通过这些属性名来获取其详情。我们看到,我们的模型只有一个属 性,即last_hidden_state(最终隐藏状态)

现在我们知道如何针对单个字符串获取最终隐藏状态。我们通过创建 一个新的hidden_state列来对整个数据集执行相同的操作,以存储所 有这些向量。就像我们在词元分析器中所做的那样,我们将使用 DatasetDict的map()方法一次性提取所有隐藏状态。我们需要做的第 一件事是将先前的步骤封装在一个处理函数中:

这个函数和我们之前的逻辑的唯一不同在于最后一步,即将最终的隐 藏状态作为NumPy数组放回CPU。当我们使用批量输入时,map()方法要 求处理函数返回Python或NumPy对象。 由于我们的模型期望输入张量,下一步需要将input_ids和 attention_mask列转换为torch格式,具体如下:

现在我们已经得到了与每个推文相关联的隐藏状态,下一步是基于它 们训练一个分类器。为了做到这一点,我们需要一个特征矩阵,我们 来看一下。

创建特征矩阵

可视化训练集

由于在768维度中可视化隐藏状态是个艰难的任务,因此我们将使用强 大的UMAP算法将向量投影到2D平面上 。由于UMAP在特征缩放到[0, 1]区间内时效果最佳,因此我们将首先应用一个MinMaxScaler,然后 使用umap-learn库的UMAP实现来缩放隐藏状态:

训练一个简单的分类器

我们已经看到,不同情感的隐藏状态是不同的,尽管其中一些情感并 没有明显的界限。现在让我们使用这些隐藏状态来训练一个逻辑回归 模型(使用Scikit-learn)。训练这样一个简单的模型速度很快,而 且不需要GPU:

从准确率上看,我们的模型似乎只比随机模型稍微好一点,但由于我 们处理的是一个不平衡的多分类数据集,它实际上会显著地表现更 好。我们可以通过将其与简单基准进行比较来检查我们的模型是否良 好。在Scikit-learn中,有一个DummyClassifier可以用于构建具有简 单启发式的分类器,例如始终选择多数类或始终选择随机类。在这种 情况下,表现最佳的启发式是始终选择最常见的类,这会产生约35%的 准确率:

因此,使用DistilBERT嵌入的简单分类器明显优于我们的基线。我们 可以通过查看分类器的混淆矩阵来进一步研究模型的性能,该矩阵告 诉我们真实标注和预测标注之间的关系:

这里我们可以看到,anger和fear最常与sadness混淆,这与我们可视 化嵌入时所观察到的一致。此外,love和surprise经常与joy混淆。 接下来我们将探究微调方法,这种方法可以带来更好的分类效果。但 是,重要的是要注意,微调需要更多的计算资源,比如GPU,而你的组 织可能没有GPU。在这种情况下,基于特征的方法可以是传统机器学习 和深度学习之间的一个很好的折中方案。

微调Transformer模型

现在我们探讨如何进行端到端的Transformer模型微调。在使用微调方 法时,我们不使用隐藏状态作为固定特征,而是如图2-6所示那样进行 训练。这要求分类头是可微的,这就是为什么这种方法通常使用神经 网络进行分类。

训练用作分类模型输入的隐藏状态将有助于我们避免使用可能不适合 分类任务的数据的问题。相反,初始隐藏状态在训练过程中适配,以 降低模型损失并提高其性能。

加载预训练模型

定义性能指标

为了在训练期间监控指标,我们需要为Trainer定义一个 compute_metrics()函数。该函数接收一个EvalPrediction对象(这是 一个具有predictions和label_ids属性的命名元组),并需要返回一 个将每个指标名称映射到其值的字典。对于我们的应用,我们将计算 模型的F1分数和准确率:

有了数据集和度量指标后,在定义Trainer类之前,我们只需要处理最 后两件事情: 1.登录我们的Hugging Face Hub账户。从而让我们能够将我们的微 调模型推送到Hub上,并与社区分享它。 2.定义训练运行的所有超参数。

登录hugging-face

from huggingface_hub import notebook_login

notebook_login()

训练模型

我们将使用TrainingArguments类来定义训练参数。此类存储了大量信 息,从而为训练和评估提供细粒度的控制。最重要的参数是 output_dir,它是存储训练过程中所有工件的位置。以下是 TrainingArguments的完整示例:

 我们可以看到我们的模型在验证集上的F1分数约为92%,这比基于特征 的方法有了显著的提升! 我们可以通过计算混淆矩阵来更详细地查看训练指标。为了可视化混 淆矩阵,我们首先需要获取验证集上的预测结果。Trainer类的predict()方法返回了几个有用的对象,我们可以用它们进行评估

predict()方法的输出是一个PredictionOutput对象,它包含了 predictions和label_ids的数组,以及我们传给训练器的度量指标。 我们可以通过以下方式访问验证集上的度量指标:

它还包含了每个类别的原始预测值。我们可以使用np.argmax()进行贪 婪解码预测,然后会得到预测标注,并且结果格式与前面的基于特征 的方法相同,以便我们进行比较:

 我们可以基于这个预测结果再次绘制混淆矩阵:

可见,与前面的基于特征的方法相比,微调方法的结果更接近于理想 的对角线混淆矩阵。love类别仍然经常与joy混淆,这点逻辑上也讲得 过去。surprise也经常被错误地识别为joy,或者与fear混淆。总体而 言,模型的性能似乎非常不错,但在我们结束之前,让我们深入了解 模型可能会犯的错误的类型。

储存和共享模型

下载自己的模型

使用自己的模型预测

4.本章小结

祝贺你,现在你知道了如何训练一个用于分类推文情感的Transformer
模型!我们已经讨论了基于特征和微调的两种互补方法,并研究了它
们的优势和劣势。
然而,这只是使用Transformer模型构建实际应用程序的第一步,我们
还有很多工作要做。以下是在NLP旅程中可能遇到的挑战清单:
我的老板希望我的模型昨天就上线了!
在大多数应用程序中,你并不希望你的模型闲置,你希望将它用于预
测!当模型被推送到Hub时,会自动创建一个推理端点,可以使用HTTP
请求调用它。如果你想了解更多信息,建议查看推理API的文档
(https://oreil.ly/XACF5)。
我的用户需要更快的预测!
我们已经讨论了解决此问题的一种方法:使用DistilBERT。在第8章
中,我们将深入研究知识蒸馏(DistilBERT创建的过程)以及其他加
速Transformer模型的技巧。
你的模型是否也可以执行X任务?
正如我们在本章中所提到的,Transformer模型非常多才多艺。在本书
的其余部分中,我们将使用相同的基本架构探索一系列任务,例如问
答和命名实体识别。
我的文本不是英语!
事实证明,Transformer模型能够支持多语言,在第4章中,我们将使
用它们同时处理多种语言。
我没有标注数据!
如果可用的标注数据非常少,则可能不能进行微调。在第9章中,我们
将探讨一些应对这种情况的技术。
现在我们已经了解了如何训练和共享Transforme。在第3章中,我们将
探索如何从头开始实现我们自己的Transformer模型。

第三章.Transformer架构剖析

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Jvm是如何处理异常的
  • 【ESP32接入国产大模型之豆包】
  • 2024年自动驾驶SLAM面试题及答案(更新中)
  • docker文件挂载和宿主主机文件的关系
  • 【IoTDB 线上小课 05】时序数据文件 TsFile 三问“解密”!
  • 2024在线PHP加密网站源码
  • 代码随想录算法训练营第二十天|二叉树 part7
  • 香薰学习笔记
  • 云计算的三种服务模式
  • c#,NumSharp 中的 NDArray属性说明
  • BUUCTF逆向wp [MRCTF2020]Xor
  • Web开发:一个可拖拽的模态框(HTML、CSS、JavaScript)
  • 代码随想录算法训练营第47天
  • wkhtmltopdf 工具安装与使用
  • excel系列(三) - 利用 easyexcel 快速实现 excel 文件导入导出
  • php的引用
  • 《Java编程思想》读书笔记-对象导论
  • 【108天】Java——《Head First Java》笔记(第1-4章)
  • CSS 提示工具(Tooltip)
  • css的样式优先级
  • ES6之路之模块详解
  • ng6--错误信息小结(持续更新)
  • nginx 配置多 域名 + 多 https
  • Python_网络编程
  • Ruby 2.x 源代码分析:扩展 概述
  • Zepto.js源码学习之二
  • 包装类对象
  • 从零开始的webpack生活-0x009:FilesLoader装载文件
  • 浮动相关
  • 爬虫进阶 -- 神级程序员:让你的爬虫就像人类的用户行为!
  • 配置 PM2 实现代码自动发布
  • 前端_面试
  • 适配mpvue平台的的微信小程序日历组件mpvue-calendar
  • 再次简单明了总结flex布局,一看就懂...
  • 带你开发类似Pokemon Go的AR游戏
  • 说说我为什么看好Spring Cloud Alibaba
  • ​​​​​​​Installing ROS on the Raspberry Pi
  • ​力扣解法汇总946-验证栈序列
  • # 20155222 2016-2017-2 《Java程序设计》第5周学习总结
  • # AI产品经理的自我修养:既懂用户,更懂技术!
  • # 数据结构
  • #我与Java虚拟机的故事#连载03:面试过的百度,滴滴,快手都问了这些问题
  • (10)ATF MMU转换表
  • (2.2w字)前端单元测试之Jest详解篇
  • (BAT向)Java岗常问高频面试汇总:MyBatis 微服务 Spring 分布式 MySQL等(1)
  • (HAL库版)freeRTOS移植STMF103
  • (JSP)EL——优化登录界面,获取对象,获取数据
  • (MTK)java文件添加简单接口并配置相应的SELinux avc 权限笔记2
  • (附源码)spring boot车辆管理系统 毕业设计 031034
  • (附源码)ssm基于web技术的医务志愿者管理系统 毕业设计 100910
  • (实测可用)(3)Git的使用——RT Thread Stdio添加的软件包,github与gitee冲突造成无法上传文件到gitee
  • (四)JPA - JQPL 实现增删改查
  • .gitignore文件—git忽略文件
  • .Net Core 生成管理员权限的应用程序
  • .NET Core使用NPOI导出复杂,美观的Excel详解