ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

AI-For-Beginners 课程精讲:从注意力机制到 Transformer 与 BERT 的完整实战指南

AI-For-Beginners 课程精讲:从注意力机制到 Transformer 与 BERT 的完整实战指南 教程人工智能机器学习深度学习【免费下载链接】AI-For-Beginners12 Weeks, 24 Lessons, AI for All!项目地址https://gitcode.com/GitHub_Trending/ai/AI-For-Beginners点击查看免费下载注意力机制与 Transformer 是当前自然语言处理NLP领域最核心的技术基石也是本课程 NLP 章节 中承上启下的关键一课。本文以 第 18 课《注意力机制与 Transformer》 为主体结合课程配套的 PyTorch 笔记本 与 TensorFlow 笔记本 源码级细节系统讲解注意力机制如何解决 RNN 的序列建模缺陷、Transformer 如何通过位置编码与多头自注意力实现并行化训练以及如何用预训练 BERT 以迁移学习方式完成真实文本分类任务。读完本文你将掌握注意力矩阵的含义、Transformer 两大核心组件位置编码与多头自注意力的底层实现思路并能够独立在 PyTorch、TensorFlow 和 HuggingFace 三种框架下搭建或微调 Transformer 模型。从机器翻译说起序列到序列任务与 RNN 的局限NLP 领域最重要的任务之一是机器翻译它是 Google Translate 这类工具的基础。在本课程的语境下我们关注的不只是翻译而是更一般的序列到序列sequence-to-sequence任务也被称为句子转换sentence transduction——即把一条输入序列映射为一条输出序列。在 RNN 时代序列到序列任务由两个循环网络协作完成编码器encoder把输入序列压缩成一个隐藏状态hidden state解码器decoder把这个隐藏状态展开unroll成翻译结果。然而这种一条隐藏状态扛到底的架构存在两个先天缺陷长句记忆困难编码器网络的最终状态难以记住句子的开头部分导致模型在长句上的翻译质量明显下降权重均等序列中所有单词对结果的影响被一视同仁。但现实中输入序列里特定的词往往对后续输出的影响远大于其他词。这两个问题正是注意力机制诞生的直接动因。此外从本课程 第 16 课 RNN 可知循环网络的本质是逐步传递上下文这也为后面的并行化困境埋下了伏笔。注意力机制为每个输出动态加权输入上下文**注意力机制Attention Mechanism**提供了一种方法在 RNN 生成每一个输出预测时对每个输入向量的上下文影响力进行加权。它的实现方式是在输入 RNN 的中间状态与输出 RNN 之间建立捷径连接shortcut。具体来说当生成输出符号 $y_t$ 时模型不再只依赖编码器的最终状态而是把所有输入隐藏状态 $h_i$ 都纳入计算并为每个状态分配一个权重系数 $\alpha_{t,i}$。这就是经典论文中提出的**加性注意力additive attention**结构最早见于 [Bahdanau et al., 2015]该论文也是 Transformer 出现前序列学习最重要的奠基工作之一注意力矩阵 ${\alpha_{i,j}}$ 的每个元素代表输出序列中某个词的生成在多大程度上受到输入序列中某个特定词的影响。下图展示了一个真实的注意力对齐示例来自 RNNsearch-50 模型即引入注意力机制的翻译模型可以看到输出词-输入词之间的对应关系被清晰地刻画出来注意力机制几乎支撑起了当下 NLP 的最先进水平state of the art。但它的引入也带来一个新问题模型参数量大幅增加进而引发 RNN 的扩展性scaling危机。RNN 难以扩展的关键约束在于模型的循环本质使得训练时的批处理batch与并行化非常困难——序列中的每个元素都必须按顺序依次处理无法简单地并行计算。课程配套的动画直观展示了编码器-解码器加注意力的工作方式正是注意力机制被广泛采用 RNN 并行化受限这对矛盾直接催生了我们今天熟知并广泛使用的 Transformer 系列模型从 BERT 到 Open-GPT3 皆是其产物。Transformer 模型摆脱 RNN 的顺序束缚Transformer 的核心思想之一就是绕开 RNN 的顺序特性构建一个在训练时高度可并行化的模型。这个目标通过两大设计实现位置编码positional encoding显式地把 token 的相对位置信息注入模型用自注意力机制self-attention取代 RNN或 CNN来捕捉模式——这正是那篇提出 Transformer 的论文标题Attention is all you need的含义所在。位置编码 / 位置嵌入Positional Encoding / Embedding为什么需要位置编码思路可以梳理为四步使用 RNN 时token 的相对位置由步数天然隐式表示因此无需显式建模一旦切换到注意力机制模型本身对顺序没有概念必须显式知道 token 在序列中的相对位置为了获得位置编码我们在 token 序列旁边附加一条位置序列即 $0, 1, 2, \dots$ 的自然数序列把每个 token 的位置整数转换成向量后与 token 的嵌入embedding向量混合。把整数位置转成向量有两条主流路线可训练嵌入trainable embedding与 token 嵌入类似为位置也配备一个嵌入层。这是本课采用的方案——分别对 token 和位置施加嵌入层得到两个维度相同的嵌入向量然后直接相加固定位置编码函数fixed position encoding function即原论文提出的三角函数式固定编码方案。位置嵌入的最终结果是每个 token 的表示向量中同时编码了原始 token 本身和它在序列中的位置两类信息这一思路在配套的 TensorFlow 笔记本 中有完整的可运行实现。因为 Keras 没有内置 Transformer 层课程自行实现了TokenAndPositionEmbedding层class TokenAndPositionEmbedding(keras.layers.Layer): def __init__(self, maxlen, vocab_size, embed_dim): super(TokenAndPositionEmbedding, self).__init__() self.token_emb keras.layers.Embedding(input_dimvocab_size, output_dimembed_dim) self.pos_emb keras.layers.Embedding(input_dimmaxlen, output_dimembed_dim) self.maxlen maxlen def call(self, x): maxlen self.maxlen positions tf.range(start0, limitmaxlen, delta1) positions self.pos_emb(positions) x self.token_emb(x) return x positions该层由两个Embedding层组成一个嵌入 token一个嵌入位置位置由tf.range(0, maxlen)生成的自然数序列构成。两者维度相同的嵌入向量相加后得到形状为maxlen × embed_dim的带位置的输入表示。多头自注意力让词与词之间相互看见Transformer 捕捉序列模式的利器是自注意力self-attention——本质上是把注意力作用在输入与输出同为同一序列上。自注意力让模型能够考虑句子内部的上下文看清哪些词彼此相关。例如它能够识别出代词it等**共指coreference**关系具体指向哪些词并据此理解整句语境课程进一步指出Transformer 中使用的是多头注意力Multi-Head Attention目的是赋予网络同时捕捉多种不同类型依赖关系的能力例如长程long-term与短程short-term词关系共指关系与其他类型的关系不同注意力头可以分别学习不同的词间关系从而提升下游 NLP 任务的效果。在 TensorFlow 笔记本 中Transformer 块TransformerBlock的完整实现如下它把注意力、前馈网络、层归一化与残差连接组合在一起class TransformerBlock(keras.layers.Layer): def __init__(self, embed_dim, num_heads, ff_dim, rate0.1): super(TransformerBlock, self).__init__() self.att keras.layers.MultiHeadAttention(num_headsnum_heads, key_dimembed_dim, nameattn) self.ffn keras.Sequential( [keras.layers.Dense(ff_dim, activationrelu), keras.layers.Dense(embed_dim),] ) self.layernorm1 keras.layers.LayerNormalization(epsilon1e-6) self.layernorm2 keras.layers.LayerNormalization(epsilon1e-6) self.dropout1 keras.layers.Dropout(rate) self.dropout2 keras.layers.Dropout(rate) def call(self, inputs, training): attn_output self.att(inputs, inputs) attn_output self.dropout1(attn_output, trainingtraining) out1 self.layernorm1(inputs attn_output) ffn_output self.ffn(out1) ffn_output self.dropout2(ffn_output, trainingtraining) return self.layernorm2(out1 ffn_output)代码要点解析MultiHeadAttention(inputs, inputs)中两个inputs相同即 Q/K/V 都来自同一序列实现自注意力注意力输出先过Dropout再与原始输入做残差相加residual connection随后经LayerNormalization归一化归一化结果送入两层的前馈网络FFNDense(ff_dim, relu)Dense(embed_dim)再次残差相加并归一化。值得注意的是这里的LayerNormalization与计算机视觉章节讲过的BatchNormalization不同它独立地对每个训练样本归一化上一层的输出将其拉回 $[-1, 1]$ 的量级因此非常适合序列数据这种变长、批大小敏感的场景。编码器-解码器注意力两处注意力的分工在 Transformer 中注意力出现在两个位置各司其职输入文本内部的自注意力用于在源文本内捕捉模式编码器与解码器之间的注意力层用于完成序列翻译即让解码器在生成每个目标词时聚焦到源文本中相关的部分。编码器-解码器注意力与本节开头介绍的 RNN 注意力机制非常相似——只是被嵌入到了完全可并行化的 Transformer 架构中。下面的动画直观解释了 Transformer 模型中这种逐位置评估-对齐的执行过程由于每个输入位置都被独立映射到每个输出位置Transformer 比 RNN 拥有强得多的并行能力这使人们能够训练出远比 RNN 更大、更具表达力的语言模型——这是现代大规模语言模型能够存在的架构前提。BERT用迁移学习榨干预训练模型的潜力BERTBidirectional Encoder Representations from Transformers双向编码器 Transformer 表示是一个体量巨大的多层 Transformer 网络BERT-base12 层BERT-large24 层。其训练分两个阶段无监督预训练在海量文本语料Wikipedia 书籍上进行任务是在句子中预测被掩码masked的词有监督微调fine-tuning利用预训练阶段获得的对语言的深刻理解在其他数据集上进行下游任务适配。这一预训练 微调的整体流程被称为迁移学习transfer learning它让 BERT 乃至今天的大语言模型得以以相对低廉的成本适配各种具体任务课程明确指出Transformer 架构存在大量变体——BERT、DistilBERT、BigBird、OpenGPT3 等都可以被微调HuggingFace 的 transformers 库 为其中许多架构同时提供了 PyTorch 与 TensorFlow 实现是本课程实战部分的主要工具。实战一用 PyTorch 微调 BERT 完成文本分类PyTorch 笔记本 演示了如何用预训练 BERT 解决课程一贯使用的AG News 新闻主题分类任务4 个类别World、Sports、Business、Sci/Tech数据集加载与词表构建逻辑见课程工具模块 torchnlp.py。第一步加载数据与 BERT 专用分词器。由于使用预训练模型必须配套使用其专属分词器import torch import torchtext from torchnlp import * import transformers train_dataset, test_dataset, classes, vocab load_dataset() vocab_len len(vocab) # 从模型名加载联网自动下载 bert_model bert-base-uncased # 或从本地目录加载目录需含分词器参数、config.json、权重文件等 # bert_model ./bert tokenizer transformers.BertTokenizer.from_pretrained(bert_model) MAX_SEQ_LEN 128 PAD_INDEX tokenizer.convert_tokens_to_ids(tokenizer.pad_token) UNK_INDEX tokenizer.convert_tokens_to_ids(tokenizer.unk_token)tokenizer.encode可以直接把文本转成 BERT 词表对应的 id 序列例如tokenizer.encode(PyTorch is a great framework for NLP)会输出以[CLS]id 101开头、[SEP]id 102结尾的整数列表。第二步定制批处理。因为 BERT 使用自己的编码函数需要实现一个与之前padify类似的填充函数pad_bert对一个批内的样本逐个用tokenizer.encode编码取批内最大长度l再通过torch.nn.functional.pad右端补零保证同批张量形状一致def pad_bert(b): v [tokenizer.encode(x[1]) for x in b] l max(map(len, v)) return ( torch.LongTensor([t[0] for t in b]), torch.stack([torch.nn.functional.pad(torch.tensor(t), (0, l-len(t)), modeconstant, value0) for t in v]) ) train_loader torch.utils.data.DataLoader(train_dataset, batch_size8, collate_fnpad_bert, shuffleTrue) test_loader torch.utils.data.DataLoader(test_dataset, batch_size8, collate_fnpad_bert)第三步加载分类头模型。使用BertForSequenceClassification它会自动带上适配分类任务的最终分类器因此加载时会提示分类器权重未初始化、需要训练——这正是我们要做的微调model transformers.BertForSequenceClassification.from_pretrained(bert_model, num_labels4).to(device)第四步微调训练。关键实践点BERT 已充分预训练因此必须用很小的学习率笔记本中取lr2e-5以免破坏已学到的权重。训练循环中模型一次前向同时返回loss和网络输出out用loss.backward()做反向传播用out.argmax(dim1)与标签对比计算准确率optimizer torch.optim.Adam(model.parameters(), lr2e-5) report_freq 50 iterations 500 # 想训练更久就调大这个数 model.train() i, c 0, 0 acc_loss, acc_acc 0, 0 for labels, texts in train_loader: labels labels.to(device) - 1 # 把标签归一到 0-3 texts texts.to(device) loss, out model(texts, labelslabels)[:2] labs out.argmax(dim1) acc torch.mean((labs labels).type(torch.float32)) optimizer.zero_grad() loss.backward() optimizer.step() acc_loss loss acc_acc acc i 1; c 1 if i % report_freq 0: print(fLoss {acc_loss.item()/c}, Accuracy {acc_acc.item()/c}) c 0; acc_loss 0; acc_acc 0 iterations - 1 if not iterations: break笔记本中记录的示例训练日志显示损失从约 1.13 快速降至 0.26 左右准确率从 0.585 一路提升到 0.92 以上——这正是BERT 已经很好理解语言结构只需微调最终分类器的直观体现。第五步评估。评估循环与训练循环几乎一致但必须调用model.eval()切换到评估模式笔记本示例中最终测试准确率约为 0.905。评估代码片段model.eval() iterations 100 acc 0 i 0 for labels, texts in test_loader: labels labels.to(device) - 1 texts texts.to(device) _, out model(texts, labelslabels)[:2] labs out.argmax(dim1) acc torch.mean((labs labels).type(torch.float32)) i 1 if i iterations: break print(fFinal accuracy: {acc.item()/i})注意事项来自课程与笔记本BERT 模型很大整个训练耗时较长且需要较强算力GPU最好不止一块。笔记本中使用的bert-base-uncased还属于最小的预训练 BERT 之一更大的模型通常能带来更好的效果。若在本地运行本课程可先按 5-NLP 章节说明 安装依赖PyTorch 侧依赖清单见 requirements-pytorch.txt该章节同时给出了 GPU 内存管理的建议例如在 TensorFlow 中开启显存按需增长physical_devices tf.config.list_physical_devices(GPU) if len(physical_devices)0: tf.config.experimental.set_memory_growth(physical_devices[0], True)实战二TensorFlow 手写 Transformer 与 BERT 微调TensorFlow 笔记本 提供了另一条完整路径依赖清单见 requirements-tf.txt含tensorflow、tensorflow_datasets、tensorflow_text、transformers等。1. 从零搭建小规模 Transformer 分类器。数据使用tfds.load(ag_news_subset)。完成前述TokenAndPositionEmbedding与TransformerBlock两个自定义层后即可组装完整模型embed_dim 32 # 每个 token 的嵌入维度 num_heads 2 # 注意力头数 ff_dim 32 # 前馈网络隐藏层维度 maxlen 256 vocab_size 20000 model keras.models.Sequential([ keras.layers.experimental.preprocessing.TextVectorization(max_tokensvocab_size, output_sequence_lengthmaxlen, input_shape(1,)), TokenAndPositionEmbedding(maxlen, vocab_size, embed_dim), TransformerBlock(embed_dim, num_heads, ff_dim), keras.layers.GlobalAveragePooling1D(), keras.layers.Dropout(0.1), keras.layers.Dense(20, activationrelu), keras.layers.Dropout(0.1), keras.layers.Dense(4, activationsoftmax) ])该模型仅约 66 万参数笔记本model.summary()显示 Total params: 659,592用 Adam 优化器在 AG News 子集上即可训练到不错的准确率示例日志 val_acc ≈ 0.91。它也是理解小数据小模型与大模型迁移学习差异的最佳对照。2. 用 TensorFlow Hub 接入预训练 BERT。关键实践点必须使用与原模型训练时完全相同的向量化器。BERT 向量化器返回三个分量input_word_ids输入句子的 token 编号序列input_mask标记序列中哪些位置是真实输入、哪些是 padding类似Masking层的掩码input_type_ids用于语言建模任务允许在同一序列中指定两个输入句子。加载 BERT 特征提取器后它会返回多个有用的输出pooled_output整句的智能语义嵌入等价于我们手写模型中的GlobalAveragePooling1D输出、sequence_output最后一个 Transformer 层的输出以及encoder_outputs所有 Transformer 层的输出列表。然后可以用 Keras 函数式 API 定义端到端分类模型并把 BERT 权重固定为不可训练、只训练最后的分类器这样训练参数仅 516 个模型总计约 478 万参数inp keras.Input(shape(), dtypetf.string) x vectorizer(inp) x bert(x) x keras.layers.Dropout(0.1)(x[pooled_output]) out keras.layers.Dense(4, activationsoftmax)(x) model keras.models.Model(inp, out) bert.trainable False3. 解冻 BERT 权重做完整微调。冻结权重训练效果有限笔记本示例 val_acc ≈ 0.79课程接着演示了解冻全部权重、用AdamW 优化器 warmup的规范微调策略——这是 Transformer 微调的标准做法from official.nlp import optimization bert.trainable True epochs 3 opt optimization.create_optimizer( init_lr3e-5, num_train_stepsepochs * len(ds_train), num_warmup_steps0.1 * epochs * len(ds_train), optimizer_typeadamw) model.compile(losssparse_categorical_crossentropy, metrics[acc], optimizeropt) model.fit(ds_train.map(tupelize).batch(128), validation_datads_test.map(tupelize).batch(128))笔记本示例显示解冻后单轮训练 val_acc 提升至约 0.82由于 BERT 特征提取计算量大即使可训练参数不多训练依然较慢课程建议可尝试训练 5–10 个 epoch 以获得更好效果。4. HuggingFace 路线TF/PyTorch 通用。笔记本最后展示了更简洁的 HuggingFace 用法BertTokenizer.from_pretrained(bert-base-uncased)加载分词器TFBertForSequenceClassification.from_pretrained(bert_model, num_labels4)加载模型其summary()显示约 1.095 亿参数其中分类器仅 3076 个可训练参数。数据管线通过tf.py_function调用分词器配合paddingmax_length, max_lengthMAX_SEQ_LEN, truncationTrue完成定长编码之后即可用常规model.fit训练。课后实践挑战与作业课程的收尾环节给你留下了充分的动手空间挑战Challenge尝试不同的 Transformer 架构与超参数组合观察它们对下游任务的影响作业Assignment完整要求见 assignment.md——使用 HuggingFace 提供的脚本在其平台上的各类模型中挑选一个进行实验尝试官方数据集或从本课程中挑选一个数据集甚至从 Kaggle 导入自己的数据观察模型能否生成有趣文本并产出一份记录实验发现与分析结论的笔记本。总结通过本课你掌握了 NLP 工具箱中两大关键武器注意力机制通过加权输入上下文解决 RNN 编码器-解码器记不住句子开头、词权均等的缺陷其注意力矩阵 ${\alpha_{i,j}}$ 可视化地刻画了输入与输出词之间的对齐关系Transformer 架构以位置编码注入顺序信息以多头自注意力替代 RNN 捕捉模式配合编码器-解码器注意力完成翻译类任务从而获得 RNN 无法企及的并行化训练能力。在此基础上BERT 等预训练模型 迁移学习微调的范式让你能够以很小的训练成本获得当前 NLP 的最佳实践效果。正如 PyTorch 笔记本 的结论所指出的Transformer 模型是当下 NLP 的 SOTA 方案在做自定义 NLP 解决方案时它应当是你最先尝试的技术路线而理解 RNN 等基础原理依然是构建更高级神经模型不可或缺的功底。除文本分类外同样的 BERT 技术栈还可以迁移到实体抽取NER、问答QA等更丰富的任务上——这正是整个 5-NLP 章节 为你铺好的进阶之路。赞分享教程人工智能机器学习深度学习【免费下载链接】AI-For-Beginners12 Weeks, 24 Lessons, AI for All!项目地址https://gitcode.com/GitHub_Trending/ai/AI-For-Beginners点击查看免费下载相关推荐AI for Beginners 第 18 课从注意力机制到 Transformer 与 BERT 的完整实战指南AI for Beginners 第 18 课从注意力机制到 Transformer 与 BERT 的完整实战指南 导读 本文基于 AI for Beginn教程人工智能机器学习深度学习AI-For-Beginners 课程详解注意力机制与 Transformer 模型原理及 BERT 实战AI For Beginners 课程详解注意力机制与 Transformer 模型原理及 BERT 实战 注意力机制Attention Mechanism教程人工智能机器学习深度学习AI-For-Beginners 课程第 18 课注意力机制与 Transformer 架构实战指南AI For Beginners 课程第 18 课注意力机制与 Transformer 架构实战指南 本文基于 AI For Beginners https:教程人工智能机器学习深度学习创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表