面向自然语言处理的迁移学习(三)(3)https://developer.aliyun.com/article/1522492
10.2 多任务微调
在第四章的第三部分中,我们介绍了多任务学习的概念,其中模型被训练执行各种任务,而不仅仅是一个任务。结果模型通常对新场景更具一般性,并且可以实现更好的转移和性能。毫不奇怪,这个想法再次出现在预训练 NLP 语言模型的适应策略的背景下,微调在多个任务上的模型观察到更加健壮和有效。⁷
我们在这里讨论这个想法提供了一个很好的机会来介绍通用语言理解评估(GLUE)数据集,⁸这是一个包含几个人类语言推理代表性任务数据的集合。这个数据集包括检测句子相似性、问题相似性、释义、情感分析和问题回答等任务。在本节中,我们演示了如何快速利用 transformers 库对我们讨论的各种基于变压器的预训练模型在 GLUE 数据集的各种任务上进行微调。这个练习还演示了如何按类似方式微调来自 BERT 系列的模型,以解决 GLUE 中包含的重要问题类别之一的自定义数据集。
我们还演示了顺序适应——将总体所需的转移实验过程分解成更简单、更易管理的步骤的过程。考虑一个假设的情景,即基于语言的工具在西非和东非之间无法完成转移——首先它可能在西非和中非之间成功转移,然后在中非和东非之间成功转移。这与多任务微调的想法相关,因为它本质上是按顺序进行的,一步接一步进行。与通常理解的多任务微调方法不同,顺序适应首先在一个任务上进行微调,然后再在另一个任务上进行微调。
在本节中,我们通过在 GLUE 数据集的几个任务上对一些预训练的基于变压器的语言模型进行多任务微调和顺序适应来演示。具体来说,我们关注的是一个被称为Quora 问题对(QQP)任务的问题相似度任务,以及用于衡量一对句子之间相似性的语义文本相似性基准(SST-B)任务。
10.2.1 通用语言理解数据集(GLUE)
通用语言理解数据集(GLUE)旨在提供一系列多样的自然语言理解任务的具有挑战性的基准数据集。这些任务被选中,以代表多年来在自然语言处理领域研究人员之间达成的一种关于什么构成有趣、具有挑战性和相关问题的隐含共识。在表 10.1 中,我们总结了数据集中可用任务和每个任务的数据计数。
表 10.1 原始通用语言理解数据集(GLUE)中提供的任务、描述和数据计数列表
任务名称 | 数据量 | 描述 |
语言可接受性语料库(CoLA) | 训练 8,500,测试 1,000 | 确定一个英语句子是否符合语法规范 |
斯坦福情感树库(SST2) | 训练 67,000,测试 1800 | 检测给定句子的情感-积极或消极 |
Microsoft Research Paraphrase Corpus (MRPC) | 3,700 train, 1,700 test | 确定一个句子是否是另一个句子的释义 |
Semantic Textual Similarity Benchmark (STS-B) | 7,000 train, 1,400 test | 预测一对句子之间的相似度分数,范围在 1 到 5 之间 |
Quora Question Pairs (QQP) | 3,640,000 train, 391,000 test | 确定一对 Quora 问题是否语义上等同 |
Multi-Genre Natural Language Inference (MultiNLI) | 393,000 train, 20,000 test | 确定一个前提句子是暗示/蕴含还是与一个假设句子相矛盾 |
Question-Answering Natural Language Inference (QNLI) | 105,000 train, 5,400 test | 检测上下文句子是否包含对问题的答案 |
Recognizing Textual Entailment (RTE) | 2,500 train, 3,000 test | 测量前提和假设之间的文本蕴含关系,类似于 MultiNLI |
Winograd Schema Challenge (WNLI) | 634 train, 146 test | 确定模棱两可的代词指的是一组可能选项中的哪一个名词 |
从表中可以看出,原始的 GLUE 数据集涵盖了各种任务,并且可用的数据量不同。这是为了鼓励不同任务之间的知识共享,这也是我们在本章节中探讨的多任务微调理念的核心。接下来我们简要描述表中的各项任务。
前两个任务——Corpus of Linguistic Acceptability (CoLA) 和 Stanford Sentiment Treebank (SST2)——是单句任务。前者试图确定一个给定的英文句子是否语法正确,而后者试图检测句子中表达的情感是积极还是消极。
以下三项任务——Microsoft Research Paraphrase Corpus (MRPC)、Semantic Textual Similarity Benchmark (STS-B) 和 Quora Question Pairs (QQP)——被归类为相似性任务。这些任务涉及以各种方式比较两个句子。MRPC 试图检测一个句子是否是另一个句子的释义,即是否表达了相同的概念。STS-B 在连续范围 1 到 5 之间测量一对句子的相似度。QQP 试图检测一个 Quora 问题是否等同于另一个。
剩下的四个任务被分类为推理任务。多体裁自然语言推理(MultiNLI)任务试图确定给定的句子是否暗示另一个句子或是否与之矛盾――它衡量蕴涵。问答自然语言推理(QNLI)任务类似于我们讨论并在第八章中用于说明问答的 SQuAD⁹数据集。提醒一下,该数据集由上下文段落、对其提出的问题以及答案在上下文段落中的开始和结束位置指示符组成,如果存在的话。QNLI 基本上将这个想法转化为一个句对任务,通过将每个上下文句子与问题配对,并尝试预测答案是否在该上下文句子中。识别文本蕴涵(RTE)任务类似于 MultiNLI,因为它衡量两个句子之间的蕴涵关系。最后,Winograd Schema Challenge(WNLI)数据集试图检测一个含糊指代词在句子中指代可用选项中的哪个名词。
自 GLUE 成立以来,还引入了另一个名为 SuperGLUE¹⁰的数据集。这个新版本是必要的,因为最近的现代方法在 GLUE 的许多部分几乎达到了完美的性能。SuperGLUE 的开发是为了更具挑战性,因此为比较方法提供更多的“动态范围”。我们在这里关注 GLUE,但我们认为在您成为 NLP 专家时,牢记 SuperGLUE 的存在是很重要的。
接下来,我们将以 QQP 和 STS-B GLUE 任务做一些实验,作为本节的说明性示例。首先,在下一小节中,我们展示如何对我们提出的任何任务中的一个任务进行微调预训练的 BERT。我们强调,虽然在这种情况下,我们使用 STS-B 作为示例微调任务,但对于任何呈现的任务,相同的步骤序列直接适用。我们还提醒您,此练习是为了准备您在自己的自定义数据集上对 BERT 进行微调,该数据集来自我们提出的任何任务类别。
10.2.2 在单个 GLUE 任务上进行微调
在本小节中,我们看到如何快速微调 transformers 家族中的预训练模型,以处理 GLUE 基准集中的任务。回想一下,BERT 是在“填空”和“下一个句子预测”目标上进行预训练的。在这里,我们进一步微调这个预训练的 BERT 来处理 GLUE 数据上的 STS-B 相似性任务。这个练习作为如何在 GLUE 的任何其他任务以及属于这些重要问题类别之一的任何自定义数据集上进行操作的示例。
我们要做的第一件事是克隆 transformers 存储库,并使用以下代码安装必要的要求:
!git clone --branch v3.0.1 https:/ /github.com/huggingface/transformers ❶ !cd transformers !pip install -r transformers/examples/requirements.txt ❷ !pip install transformers==3.0.1 ❸
❶ 克隆(指定版本的)transformers 存储库
❷ 安装必要的要求
❸ 为了可重现性,固定 transformers 版本
请注意在我们的 Kaggle 笔记本中忽略依赖冲突消息——这些消息与我们在此处使用的库无关,只要您复制我们的笔记本而不是从头开始创建一个新的。
接下来,按以下方式下载 GLUE 数据:
!mkdir GLUE !python transformers/utils/download_glue_data.py --data_dir GLUE --tasks all❶
❶ 下载所有任务的 GLUE 数据
这会创建一个名为 GLUE 的目录,其中包含一个子目录,该子目录以每个 GLUE 任务命名,并包含该任务的数据。我们可以按以下方式查看 GLUE/STS-B 中包含的内容:
!ls GLUE/STS-B
这产生了以下输出:
LICENSE.txt dev.tsv original readme.txt test.tsv train.tsv
此外,我们可以用以下方式查看一部分 STS-B 训练数据:
!head GLUE/STS-B/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 shreddedcheese 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
在继续之前,我们注意到,为了使用这里讨论的脚本来在您自己的自定义数据上对模型进行精细调优,您只需要将您的数据转换为所示格式并指定脚本所在的位置即可!
要在 STS-B GLUE 任务上对“vanilla” bert-base-cased
BERT checkpoint 进行三轮训练——批量大小为 32,最大输入序列长度为 256,学习率为 2e-5——我们执行以下命令:
%%time ❶ !python transformers/examples/text-classification/run_glue.py --model_name_or_path bert-base-cased --task_name STS-B --do_train --do_eval --data_dir GLUE/STS-B/ --max_seq_length 256 --per_gpu_train_batch_size 32 --learning_rate 2e-5 --num_train_epochs 3.0 --output_dir /tmp/STS-B/
❶ 这是 Jupyter 笔记本中计时的“魔法”命令。
这一操作执行时间不超过 10 分钟。请注意,在代码中,我们指定了输出目录为 /tmp/STS-B/。该文件夹包含了经过精细调优的模型和评估结果。然后,为了查看所取得的性能,我们只需执行以下命令将结果打印到屏幕上:
!cat /tmp/STS-B/eval_results_sts-b.txt
这产生以下输出:
eval_loss = 0.493795601730334 eval_pearson = 0.8897041761974835 eval_spearmanr = 0.8877572577691144 eval_corr = 0.888730716983299
这些代表了用于此问题的度量标准的最终数值,即皮尔逊相关系数和斯皮尔曼相关系数。不深入细节,这些系数衡量了数据集中提供的真实相似度与我们在测试集上精细调优模型获得的相似度之间的相关性。这些系数的较高值表明了更好的模型,因为它们与真实结果的关联更大。我们看到对于这两个系数都达到了接近 89% 的性能。在撰写本文时(2020 年 10 月初),当前的 GLUE 排行榜¹¹显示,在全球范围内,排名前 20 的性能大约在 87% 到 93% 之间变化。这些排名靠前的性能也在 GLUE 的其他任务上表现良好,尽管我们目前只对一个任务进行了精细调优。但我们可以快速取得如此接近最新技术水平的性能仍然令人印象深刻。请注意从表 10.1 中得知,用于此任务的训练数据量仅为 7,000 个样本。
在下一小节中,我们将进一步在另一个任务——Quora 问题对(QQP)上对模型进行精细调优,并进一步阐明多任务学习和顺序适应的概念。
10.2.3 顺序适应
在本小节中,我们将看到在 STS-B 任务上进行微调之前,在 Quora 问答对(QQP)任务上进行微调是否会产生更好的性能。请回顾表 10.1,其中 QQP 有 364,000 个训练样本,而 STS-B 有 7,000 个样本。显然,QQP 具有更多的数据。首先在 QQP 上训练可以被解释为应用一种顺序适应多任务学习策略来处理一个低资源的场景,其中训练数据量不理想:只有 7,000 个样本。
我们开始本练习,假设已经克隆了 transformers 存储库,已安装了必要的要求,并已下载了 GLUE 数据,如前一小节所示。现在,要做的下一件事是在 QQP GLUE 任务上对“普通”的bert-base-cased
BERT 检查点进行微调,一次迭代,批处理大小为 32,最大输入序列长度为 256,学习率为 2e-5。请注意,这次我们只使用一个迭代,而不是前一小节中的三个,因为训练数据现在要大得多。现在每个迭代(涉及一次通过训练集)涵盖了 364,000 个样本,我们认为这已足够。我们使用以下代码:
!python transformers/examples/text-classification/run_glue.py --model_name_or_path bert-base-cased --task_name QQP --do_train --do_eval --data_dir GLUE/QQP/ --max_seq_length 256 --per_gpu_train_batch_size 32 --learning_rate 2e-5 --num_train_epochs 1 --output_dir /tmp/QQP/
训练时长约为 2 小时 40 分钟。与以前一样,我们可以检查 QQP 任务上的性能如下:
!cat /tmp/QQP/eval_results_qqp.txt
这达到了以下性能:
eval_loss = 0.24864352908579548 eval_acc = 0.8936433341578036 eval_f1 = 0.8581700639883898 eval_acc_and_f1 = 0.8759066990730967 epoch = 1.0
然后我们可以按以下方式加载 QQP 微调的模型:
from transformers import BertForSequenceClassification, BertConfig ❶ qqp_model = BertForSequenceClassification.from_pretrained("/tmp/QQP") ❷
❶ 初始化为我们的微调模型检查点
❷ 这次使用序列分类,因为这是问题的形式
在加载了微调模型之后,让我们提取其编码器,以便我们可以在后续模型中使用它,然后可以进一步在 STS-B 任务上进行微调。请注意,这类似于我们在第四章中分析的硬参数共享场景。我们在图 10.1 中说明了这种情况。
图 10.1 我们在本节探讨的硬参数共享多任务学习场景。模型首先在 QQP 上进行微调,这是一个数据丰富的场景,然后是 STS-B,这是一个资源稀缺的场景。这个实验的顺序性质将其分类为顺序适应。
图中清楚地显示了任务之间共享的编码器。编码器被提取并用于初始化一个模型,以进行在 STS-B 上的微调,代码片段如下:
shared_encoder = getattr(qqp_model, "bert") ❶ configuration = BertConfig() configuration.vocab_size = qqp_model.config.vocab_size ❷ configuration.num_labels = 1 ❸ stsb_model = BertForSequenceClassification(configuration) ❹ setattr(stsb_model, "bert", shared_encoder) ❺
❶ 获取经过微调的 QQP 模型编码器
❷ 确保 STS-B 配置的词汇量和输出大小设置一致
❸ STS-B 是一个回归问题,只需要一个输出;QQP 是一个二元分类任务,因此有两个输出。
❹ 用与 QQP 类似的设置初始化 STS-B 模型
设置其编码器为 QQP 编码器
将初始化的 STS-B 模型保存以供进一步微调,方法如下:
stsb_model.save_pretrained("/tmp/STSB_pre")
确保从 QQP 模型中获取了词汇表,如下所示:
!cp /tmp/QQP/vocab.txt /tmp/STSB_pre
现在,使用与前一小节相同的设置,在 STS-B 上微调先前微调的 QQP 模型,操作如下:
!python transformers/examples/text-classification/run_glue.py --model_name_or_path /tmp/STSB_pre --task_name STS-B --do_train --do_eval --data_dir GLUE/STS-B/ --max_seq_length 256 --per_gpu_train_batch_size 32 --learning_rate 2e-5 --num_train_epochs 3 --output_dir /tmp/STS-B/
这三个训练时代只需大约七分半钟即可执行,只有 7,000 个训练集大小。像往常一样,我们使用以下内容来检查获得的性能:
!cat /tmp/STS-B/eval_results_sts-b.txt
观察到以下性能:
eval_loss = 0.49737201514158474 eval_pearson = 0.8931606380447263 eval_spearmanr = 0.8934618150816026 eval_corr = 0.8933112265631644 epoch = 3.0
我们已经取得了比之前的小节更好的表现,只是在 STS-B 上进行微调。那里的eval_corr
约为 88.9%,而我们在这里达到了 89.3%。因此,连续适应的多任务学习实验被证明具有益处,并导致性能的可衡量提高。
在下一节中,我们将探讨是否可以比我们在这里做得更加高效地将模型微调到新的情况。我们将研究在预训练语言模型的层之间引入称为适应模块或适配器的方法,以适应新情况。这种方法很有前途,因为引入的参数数量非常少,可以有效地预训练和分享 NLP 社区。
10.3 适配器
我们探索的下一个适应策略是使用所谓的适配模块或适配器。它们背后的关键思想如图 10.2 所示,介绍了它们作为第七章中图 7.6 中香草变压器编码器中的附加层。
图 10.2 在“香草”变压器编码器的图 7.6 中新引入的适配器层
如图所示,这些适配器是预训练神经网络层之间仅有几个参数的新引入模块。为了将修改后的模型微调到新任务上,只需要训练这些少量的额外参数——原始网络的权重保持不变。与微调整个模型相比,通常仅添加每个任务 3-4%的额外参数,几乎没有性能损失。¹² 实际上,这些额外参数相当于大约 1 兆字节的磁盘空间,这在现代标准下非常低。
这些适配器是模块化的,允许易于扩展和研究人员之间的经验共享。实际上,一个名为 AdapterHub¹³的项目是在我们使用的 transformers 库上构建的,旨在成为共享这些模块的中央存储库。在该部分中,我们将使用此项目构建在斯坦福情感树库(SST2)任务上微调的 BERT 模型。这相当于我们在先前的小节中微调 STS-B GLUE 子集所做的事情,将使您迅速了解适配器框架所提供的优势与我们之前所做的有何不同。
让我们按以下方式安装 AdapterHub 库:
pip install adapter-transformers
导入所需类并仅使用三行代码加载所需的适配器,如下所示:
from transformers import BertForSequenceClassification, BertTokenizer model = BertForSequenceClassification.from_pretrained("bert-base-uncased") ❶ model.load_adapter("sentiment/sst-2@ukp") ❷
❶要微调的检查点
❷ 任务特定的适配器选择规范
适配器和使用说明在 AdapterHub 网站上列出 ¹⁴ 这就是我们要对 BERT 检查点适应 SST2 情感分类任务所做的一切。将这与上一节的微调步骤进行比较,就可以显而易见地看出适配器方法的实用性。我们无需进行微调,只需加载附加的模块即可继续前进!
请注意,在我们的代码中,我们使用了bert-base-uncased
检查点,并且我们要加载的适配器是在 UKP 句子论证挖掘语料库上进行了微调 ¹⁵,这是因为目前 AdapterHub 存储库中只有部分可用的内容。AdapterHub 是一个早期项目,我们预计随着时间的推移会提供更多的适配器。在撰写本文的 2020 年 10 月,已经有接近 200 个适配器可用。¹⁶
作为本节和本章的最终行动,让我们通过以下代码片段来确信我们构建的模型实际上作为情感分类引擎运行。我们使用以下两个句子的情感进行比较:“那是非常出色的贡献,好!”和“那对环境非常糟糕。”
import torch tokenizer = BertTokenizer.from_pretrained("bert-base-uncased") ❶ tokensA = tokenizer.tokenize("That was an amazing contribution, good!") ❷ input_tensorA = torch.tensor([tokenizer.convert_tokens_to_ids(tokensA)]) tokensB = tokenizer.tokenize("That is bad for the environment.") ❸ input_tensorB = torch.tensor([tokenizer.convert_tokens_to_ids(tokensB)]) outputsA = model(input_tensorA,adapter_names=['sst-2']) ❹ outputsB = model(input_tensorB,adapter_names=['sst-2']) ❺ print("The prediction for sentence A - That was an amazing contribution, good! - is:") print(torch.nn.functional.softmax(outputsA[0][0])) ❻ print("The prediction for sentence B - That is very bad for the environment. - is:") print(torch.nn.functional.softmax(outputsB[0][0])) ❼
❶ 使用常规预训练的分词器
❷ 句子 A
❸ 句子 B
❹ 进行了 A 的预测
❺ 进行了 B 的预测
❻ 显示了句子 A 的预测概率
❼ 显示了句子 B 的预测概率
这产生了以下输出:
The prediction for sentence A - That was an amazing contribution, good! - is: tensor([0.0010, 0.9990], grad_fn=<SoftmaxBackward>) The prediction for sentence B - That is very bad for the environment. - is: tensor([0.8156, 0.1844], grad_fn=<SoftmaxBackward>)
所示的预测可以被解释为一对概率,第一个指示了输入是“负面”的概率,第二个是“正面”的概率。我们看到句子“那是非常出色的贡献,好!”有 99.9%的强烈正面概率。另一方面,句子“那对环境非常糟糕。”则是负面的,概率为 81.6%。这当然是合理的,并验证了我们的实验。
第十一章:结论
本章包括
- 总结本书涵盖的重要概念
- 总结相关的重要新兴概念
- 考虑自然语言处理中关于迁移学习方法的局限性以及环境和伦理考虑
- 展望自然语言处理中的迁移学习未来
- 跟上该领域的最新发展
在前面的章节中,我们涵盖了大量的材料——我们希望它们既具信息性又引人入胜。这一结论性章节试图对我们所做的一切进行有意义的总结,并展望该领域的未来和新兴的研究趋势。由于该领域的多产产出和快速发展的性质,我们当然没有涵盖每一个有影响力的架构或有前途的研究方向。为了减轻这一点,我们对我们在本书中没有机会涵盖的各种研究趋势进行了简要讨论,尽可能与已涵盖的材料进行联系和框架化。
在本章中,我们还试图通过涉及一些传统上没有受到太多关注的新兴问题,例如伦理考虑和各种模型的环境影响,提供更广泛的背景。这些与对这些模型的局限性的认识密切相关,我们在本章中尽可能突出这一点。
至关重要的是,我们讨论了在这样一个快速发展的领域中保持最新的各种提示。强调掌握了本书内容后,您现在只是开始您在该领域的旅程。所提出的工具和技能会随时间变化,并且它们的每个独特应用可能需要您的创造力或者尚未开发的新技术。在这样一个快速发展的领域中保持竞争优势确实是一次旅程,而不是一个目的地。我们鼓励读者对正在进行的研究保持探究的态度,并在某种程度上继续为其发展做出贡献。
让我们通过概述关键概念来开始这一最后一章。
11.1 关键概念概述
迁移学习旨在利用不同设置中的先前知识——无论是不同的任务、语言还是领域——来帮助解决手头的问题。它受到人类学习方式的启发,因为我们通常不会从头开始学习任何给定的问题,而是建立在可能相关的先前知识上。使没有实质计算资源的从业者能够达到最先进的性能被认为是向民主化获得正在进行的技术革命成果的重要一步。作为更具体的动机,考虑一下训练不同大小的 BERT 模型的代表性成本,如图 11.1 所示。¹
图 11.1 BERT 不同规模的训练成本。展示了两种代表性成本——单次运行和包括超参数调整在内的整个训练过程。15 亿参数的最大规模单次运行成本为,而所有优化步骤计算下的成本为80k,而所有优化步骤计算下的成本为80k,而所有优化步骤计算下的成本为1.6 百万!
正如图所示,最大规模的 BERT 训练成本可能高达数百万美元。迁移学习确实可以让您在几小时内,最坏情况下花费几美元用于微调,将这些宝贵的知识重复利用于您的个人计算项目中。
在计算机视觉中推广的迁移学习最近开始被自然语言处理(NLP)社区大量使用。而计算机视觉涉及教计算机如何理解和处理图像和视频,NLP 则考虑如何处理人类语音,无论是文本还是语音音频。在本书中,我们关注的是文本。我们特别感兴趣的一些 NLP 任务包括文档分类、机器翻译和问答。
尽管在历史上,这些任务最初是通过试图为每种情况制定固定规则来解决的——这种范式现在被称为符号 AI——但机器学习现在已成为主导趋势。计算机不再为每种可能的情况明确编程,而是通过看到许多这种相应的输入-输出对的示例来训练计算机将输入与输出信号相关联。传统上用于学习适当的输入-输出关系的方法包括决策树、随机森林、诸如 SVM 的核方法和神经网络。神经网络最近已成为解决感知问题(即计算机视觉和 NLP)的表示学习方法的首选。因此,这是我们在本书中探讨的最重要的方法类别。
在深入研究现代 NLP 的迁移学习方法之前,我们进行了一项关于传统机器学习方法的回顾性实验。具体来说,我们采用了以下方法:
- 逻辑回归
- 支持向量机
- 随机森林
- 梯度提升机
以解决两个重要问题:电子邮件垃圾检测和互联网电影数据库(IMDB)电影评论分类。为了将文本转换为数字,我们使用了词袋模型。该模型简单地计算了每封电子邮件中包含的单词标记的频率,从而将其表示为这些频率计数的向量。
现代自然语言处理(NLP)方法学主要集中在将文本部分(词语、子词、句子等)向量化上,采用诸如 word2vec 和 sent2vec 之类的技术。然后将得到的数值向量进一步处理,作为传统机器学习方法的特征,例如用于随机森林分类。
正如本书第一章所概述的,这一重要的自然语言处理研究子领域起源于 20 世纪 60 年代的信息检索术语向量模型。这在预训练的浅层神经网络技术方面达到了高潮,包括以下内容:
- fastText
- GloVe
- word2vec,在 2010 年代中期推出了几个变体,包括连续词袋(CBOW)和 Skip-Gram
CBOW 和 Skip-Gram 都来自于训练用于各种目标的浅层神经网络。Skip-Gram 试图预测滑动窗口中任何目标词附近的单词,而 CBOW 试图预测给定邻居的目标词。GloVe,代表“全局向量”,试图通过将全局信息合并到嵌入中来扩展 word2vec。它优化了嵌入,使得单词之间的余弦乘积反映它们共同出现的次数,从而使得结果向量更具可解释性。fastText 技术试图通过对字符 n-gram(而不是单词 n-gram)重复 Skip-Gram 方法来增强 word2vec,从而能够处理以前未见过的单词。这些预训练嵌入的每个变体都有其优点和缺点。作为这类方法的数值演示,我们使用 fastText 词嵌入来重新访问 IMDB 电影分类示例,那里将词袋模型替换为 fastText 以将文本转化为数字。
几种技术受 word2vec 的启发,试图将较大的文本部分嵌入到向量空间中,以便在诱导向量空间中含义类似的文本部分彼此靠近。这使得可以在这些文本部分上进行算术运算,以进行关于类比、组合含义等推理。这样的方法包括以下内容:
- 段落向量,或doc2vec,利用了从预训练词嵌入中摘要单词的连接(而不是平均)。
- Sent2vec扩展了 word2vec 的经典连续词袋(CBOW)—其中一个浅层神经网络被训练以从其上下文中的滑动窗口中预测一个词—到通过优化词和词 n-gram 的嵌入来对句子进行准确的平均表示。
作为这类方法的数值演示,我们使用了一个基于 fastText 而非词袋模型的 sent2vec 的实现来执行 IMDB 电影分类实验。
一些作者², ³, ⁴提出了各种分类系统,将迁移学习方法归类到不同的组别中。粗略地说,分类是基于迁移是否发生在不同的语言、任务或数据领域之间。通常,这些分类类型对应着以下内容:
- 跨语言学习
- 多任务学习
- 领域自适应
我们进行了一系列多任务迁移学习实验,使用了 IMDB 分类和电子邮件垃圾邮件检测这些熟悉的任务来说明这个概念。为了通过示例说明领域自适应,我们使用了自动编码器来调整一个在 IMDB 电影评论分类上训练的模型,以适应亚马逊图书评论的领域。这个练习还允许我们说明了零-shot 迁移学习的一个实例,即在亚马逊图书评论领域不需要微调就可以开始提供有价值的结果。
序列到序列建模的进展为诸如机器翻译之类的任务带来了革命。该设置中的编码器和解码器最初是循环神经网络(RNNs)。由于输入序列过长的问题,发展了一种称为注意力的技术,允许输出仅关注输入的相关部分。尽管最初这与 RNNs 结合使用,但它发展成为了使用自注意力构建编码器和解码器的技术。自注意力与最初的注意力公式不同,因为它寻求序列的部分与同一序列的其他部分之间的关联,而不是两个不同输入和输出序列的部分之间的关联。自注意力取代注意力的架构被称为 变压器,它在并行计算架构上比早期基于 RNN 的序列到序列模型更具可伸缩性。这种改进的可扩展性推动了它在竞争架构中的广泛采用。我们使用了一个预训练的英文到加纳语 Twi 的翻译变压器模型来探索这一重要架构的效能和其他特性。
NLP 的迁移学习的早期探索侧重于与计算机视觉的类比,而计算机视觉在此方面已经成功使用了一段时间。其中一个模型——SIMOn——采用了字符级卷积神经网络(CNNs)结合双向 LSTM 用于结构语义文本分类。SIMOn 代表 本体建模的语义推理。它是在 DARPA 的数据驱动模型发现(D3M)⁵ 计划中开发的,该计划是为了自动化数据科学家面临的一些典型任务。它展示了与计算机视觉中使用的方法直接类似的 NLP 迁移学习方法。该模型学到的特征还被证明对无监督学习任务也有用,并且在社交媒体语言数据上表现良好,这些数据可能有些特殊,与维基百科和其他大型基于书籍的数据集上的语言非常不同。列类型分类被用作该建模框架的说明性示例。
作为提醒,计算机视觉中微调的启发式大致如下:
- 随着目标域中的数据越来越多,阈值从输出中移动(并朝向输入)。在阈值和输出之间的参数被解冻并进行训练,而其余参数保持不变。这是由于增加的数据量可以有效地用于训练更多的参数,而否则无法完成。
- 另外,阈值的移动必须远离输出并朝向输入,因为这样可以保留编码靠近输入的通用特征的参数,同时重新训练更靠近输出的层,这些层编码源域特定特征。
- 此外,当源和目标高度不同时,一些更具体的参数/层可以完全丢弃。
早期嵌入方法(如 word2vec)的一个主要弱点是消歧义 - 区分一个词的各种用法,这些用法根据上下文可能有不同的含义。这些词在技术上被称为同形异义词,例如,duck(姿势)与 duck(鸟)和 fair(集会)与 fair(公平)。来自语言模型的嵌入 - 在流行的Sesame Street角色之后缩写为 ELMo - 是最早尝试开发单词的上下文化嵌入的方法之一,使用双向长短期记忆网络(bi-LSTMs)。ELMo 可以说是与正在进行的 NLP 迁移学习革命相关联的最流行的早期预训练语言模型之一。它与 SIMOn 具有许多架构相似之处,后者由字符级 CNNs 和 bi-LSTMs 组成。这个模型中一个词的嵌入取决于其上下文,ELMo 通过被训练来预测单词序列中的下一个单词来实现这一点。大量数据集,如维基百科和各种图书数据集,被用来训练这个模型。我们将 ELMo 应用于一个说明性的示例问题,即假新闻检测,作为一个实际演示。
通用语言模型微调(ULMFiT)进一步提出了一种方法,为任何特定任务微调基于神经网络的语言模型。该框架介绍并演示了一些关键的技术和概念,以更有效地适应预训练语言模型的新设置。这些包括区分性微调和渐进解冻。区分性微调规定,因为语言模型的不同层包含不同类型的信息,它们应该以不同的速率进行微调。渐进解冻描述了一种逐渐地以渐进方式微调更多参数的过程,目的是减少过拟合的风险。ULMFiT 框架还包括在适应过程中以独特方式改变学习率的创新。我们使用 fast.ai 库对这些概念进行了数值上的说明。
- OpenAI 的生成式预训练变压器(GPT)修改了变压器的编码器-解码器架构,以实现 NLP 的可微调语言模型。它丢弃了编码器,保留了解码器及其自注意子层。它是以因果建模目标进行训练的——预测序列中的下一个词。它特别适用于文本生成。我们展示了如何使用 Hugging Face 的 transformers 库快速使用预训练的 GPT-2 模型进行文本生成,该库在本书中早已介绍过。
- 从转换器的双向编码器表示(BERT)可以说是相反的,通过保留编码器并丢弃解码器来修改转换器架构,还依赖于掩码词语,然后需要准确预测作为训练度量的这些词语。更具体地说,它是以掩码建模目标训练的——填补空白。此外,它还通过下一个句子预测任务进行训练——确定给定句子是否是目标句子之后的一个合理的跟随句子。虽然不适用于文本生成,但该模型在其他一般语言任务(如分类和问题回答)上表现非常好。我们将其应用于问题回答和文档分类的两个重要应用。文档分类用例是垃圾邮件检测。我们还展示了它在填补空白和检测一个句子是否是另一个句子的合理的下一个句子方面的应用。
- mBERT 模型,代表“多语言 BERT”,实际上是同时在 100 多种语言上预训练的 BERT。自然地,这个模型特别适合跨语言迁移学习。我们展示了多语言预训练权重检查点如何有助于为原本未包含在多语言训练语料库中的语言创建 BERT 嵌入。BERT 和 mBERT 都是由 Google 创建的。
- 在所有这些基于语言模型的方法——ELMo、ULMFiT、GPT 和 BERT 中——都显示出生成的嵌入可以用相对较少的标记数据点进行特定下游 NLP 任务的微调。这解释了 NLP 社区对语言模型的关注:它验证了它们诱导的假设集通常是有用的。
我们还介绍了一些关于已覆盖的深度 NLP 迁移学习建模架构的适应策略。换句话说,针对预训练架构,如 ELMo、BERT 或 GPT,如何更有效地进行迁移学习?我们专注于参数效率,目标是在尽可能减少参数的情况下产生一个性能损失最小的模型。这样做的目的是让模型更小、更容易存储,从而更容易在智能手机等设备上部署。或者,智能的适应策略可能仅仅是为了在一些困难的转移情况下达到可接受的性能水平。我们介绍的适应策略有:
- 我们探索的第一种适应策略是前面提到的 ULMFiT 技术,即逐步解冻和区分微调,使用的是 fast.ai 库。
- 然后我们探索了被称为知识蒸馏的模型压缩方法,因为它最近在 NLP 领域显赫一时。这个过程本质上试图通过显著更小的学生模型来模拟更大教师模型的输出。特别是,我们使用 transformers 库中知识蒸馏方法 DistilBERT 的实现来证明通过这种方式可以将 BERT 模型的大小减少一半以上。
- 我们接触到的下一个适应策略围绕着两个想法,旨在创建更有利于更大词汇量和更长输入长度的基于变压器的语言模型。第一种方法涉及巧妙的因式分解,或者将一个更大的权重矩阵分解为两个较小的矩阵,允许你增加一个维度而不影响另一个维度。第二种想法涉及跨所有层共享参数。这两种策略是 ALBERT(A Lite BERT)方法的基础。我们使用 transformers 库中的实现来亲身体验这一方法。
因此,我们基于多任务学习的想法,即模型被训练以同时执行多种任务,并产生更具有普遍性的模型。当面临转移场景时,我们没有足够的训练数据来在给定任务上进行微调时,为什么不在多个任务上进行微调?讨论这个想法给我们提供了一个很好的机会来介绍通用语言理解评估(GLUE)数据集,这是一组代表人类语言推理的几项任务的数据集,例如检测句子之间的相似性,问题之间的相似性,释义,情感分析和问题回答。我们展示了如何使用这个数据集快速利用 transformers 库进行多任务微调。这个练习还演示了如何类似地在来自这些重要问题类别之一的自定义数据集上微调来自 BERT 系列的模型。
我们还建立在领域自适应的思想上,特别是源域和目标域的相似性对于迁移学习的有效性起着至关重要的作用。更大的相似性通常意味着一般情况下更容易进行迁移学习。当源域和目标域之间相差太大时,可能无法在单一步骤中执行该过程。在这种情况下,可能会使用顺序自适应的想法将所需的整体迁移分解为更简单、更易管理的步骤。举例来说,我们首先将一个“填空”目标预训练的 BERT 逐步适应到一个低资源的句子相似度检测场景中,再适应到一个数据丰富的问题相似度场景中。实验中两个场景的数据都来自 GLUE 数据集。
我们探讨的最终适应策略是使用所谓的适应模块或适配器。这些是仅在预训练神经网络的层之间具有少量参数的新引入的模块。对于新任务微调这个修改过的模型只需要训练这几个额外的参数。原始网络的权重保持不变。与微调整个模型相比,通常只添加了 3-4% 的额外参数时往往几乎没有性能损失。这些适配器也是模块化的,并且容易在研究人员之间共享。我们使用了 AdapterHub 框架来加载其中一些适配器,并展示它们如何用于将通用 BERT 模型适应为在情感分类任务上表现良好的模型。
面向自然语言处理的迁移学习(三)(5)https://developer.aliyun.com/article/1522494