【LLM学习之路】9月23日24日 第十、十一天 Attention代码解读

【LLM学习之路】9月23日24日 第十、十一天 Attention代码解读

Transformer模型大致分为三类

  • 纯 Encoder 模型(例如 BERT),又称自编码 (auto-encoding) Transformer 模型;
  • 纯 Decoder 模型(例如 GPT),又称自回归 (auto-regressive) Transformer 模型;
  • Encoder-Decoder 模型(例如 BART、T5),又称 Seq2Seq (sequence-to-sequence) Transformer 模型。

什么是 Transformer

语言模型

Transformer 模型本质上都是预训练语言模型,大都采用自监督学习 (Self-supervised learning) 的方式在大量生语料上进行训练,也就是说,训练这些 Transformer 模型完全不需要人工标注数据。

两个常用的预训练任务:

  • 因果语言建模

    • 基于句子的前 n 个词来预测下一个词,因为输出依赖于过去和当前的输入
    • 就是统计语言模型
  • 遮盖语言建模

    • 基于上下文(周围的词语)来预测句子中被遮盖掉的词语 (masked word)
    • 就是 Word2Vec 模型提出的 CBOW

这些语言模型虽然可以对训练过的语言产生统计意义上的理解,例如可以根据上下文预测被遮盖掉的词语,但是如果直接拿来完成特定任务,效果往往并不好。因此,我们通常还会采用迁移学习 (transfer learning) 方法,使用特定任务的标注语料,以有监督学习的方式对预训练模型参数进行微调 (fine-tune),以取得更好的性能。

迁移学习

将别人预训练好的模型权重通过迁移学习应用到自己的模型中,即使用自己的任务语料对模型进行“二次训练”,通过微调参数使模型适用于新任务。

迁移学习的好处:

  • 预训练时模型很可能已经见过与我们任务类似的数据集,通过微调可以激发出模型在预训练过程中获得的知识,将基于海量数据获得的统计理解能力应用于我们的任务;
  • 由于模型已经在大量数据上进行过预训练,微调时只需要很少的数据量就可以达到不错的性能;
  • 换句话说,在自己任务上获得优秀性能所需的时间和计算成本都可以很小。

例如,我们可以选择一个在大规模英文语料上预训练好的模型,使用 arXiv 语料进行微调,以生成一个面向学术/研究领域的模型。这个微调的过程只需要很少的数据:我们相当于将预训练模型已经获得的知识“迁移”到了新的领域,因此被称为迁移学习

在绝大部分情况下,我们都应该尝试找到一个尽可能接近我们任务的预训练模型,然后微调它,也就是所谓的“站在巨人的肩膀上”。

Transformer 的结构

  • **Encoder(左边):**负责理解输入文本,为每个输入构造对应的语义表示(语义特征);
  • **Decoder(右边):**负责生成输出,使用 Encoder 输出的语义表示结合其他输入来生成目标序列。

这两个模块可以根据任务的需求而单独使用:

  • **纯 Encoder 模型:**适用于只需要理解输入语义的任务,例如句子分类、命名实体识别;

  • **纯 Decoder 模型:**适用于生成式任务,例如文本生成;

  • Encoder-Decoder 模型或 **Seq2Seq 模型:**适用于需要基于输入的生成式任务,例如翻译、摘要。

注意力层

Transformer 模型的标志就是采用了注意力层 (Attention Layers) 的结构。注意力层的作用就是让模型在处理文本时,将注意力只放在某些词语上。

例如要将英文“You like this course”翻译为法语,由于法语中“like”的变位方式因主语而异,因此需要同时关注相邻的词语“You”。同样地,在翻译“this”时还需要注意“course”,因为“this”的法语翻译会根据相关名词的极性而变化。对于复杂的句子,要正确翻译某个词语,甚至需要关注离这个词很远的词。

Word2Vec 这些静态模型所解决不了的

原始结构

Transformer 模型本来是为了翻译任务而设计的。在训练过程中,Encoder 接受源语言的句子作为输入,而 Decoder 则接受目标语言的翻译作为输入。

在 Encoder 中,由于翻译一个词语需要依赖于上下文,因此注意力层可以访问句子中的所有词语;而 Decoder 是顺序地进行解码,在生成每个词语时,注意力层只能访问前面已经生成的单词。

假设翻译模型当前已经预测出了三个词语,我们会把这三个词语作为输入送入 Decoder,然后 Decoder 结合 Encoder 所有的源语言输入来预测第四个词语。

实际训练中为了加快速度,会将整个目标序列都送入 Decoder,然后在注意力层中通过 Mask 遮盖掉未来的词语来防止信息泄露。例如我们在预测第三个词语时,应该只能访问到已生成的前两个词语,如果 Decoder 能够访问到序列中的第三个(甚至后面的)词语,就相当于作弊了。

Decoder 中的第一个注意力层关注 Decoder 过去所有的输入,而第二个注意力层则是使用 Encoder 的输出

Transformer 家族

main_transformer_architectures

Encoder 分支

纯 Encoder 模型只使用 Transformer 模型中的 Encoder 模块,也被称为自编码 (auto-encoding) 模型。在每个阶段,注意力层都可以访问到原始输入句子中的所有词语,即具有**“双向 (Bi-directional)”注意力**

纯 Encoder 模型通常通过破坏给定的句子(例如随机遮盖其中的词语),然后让模型进行重构来进行预训练,最适合处理那些需要理解整个句子语义的任务,例如句子分类、命名实体识别(词语分类)、抽取式问答。

Decoder 分支

在每个阶段,对于给定的词语,注意力层只能访问句子中位于它之前的词语,即只能迭代地基于已经生成的词语来逐个预测后面的词语,因此也被称为自回归 (auto-regressive) 模型。

纯 Decoder 模型适合处理那些只涉及文本生成的任务。

Encoder-Decoder 分支

在每个阶段,

Encoder 的注意力层都可以访问初始输入句子中的所有单词,而 Decoder 的注意力层则只能访问输入中给定词语之前的词语(即已经解码生成的词语)。

Encoder-Decoder 模型适合处理那些需要根据给定输入来生成新文本的任务,例如自动摘要、翻译、生成式问答

自注意力机制

Attention

NLP 神经网络模型的本质就是对输入文本进行编码,常规的做法是首先对句子进行分词,然后将每个词语 (token) 都转化为对应的词向量 (token embeddings),这样文本就转换为一个由词语向量组成的矩阵X=(x1,x2,…,xn),其中 xi 就表示第 i 个词语的词向量,故 X∈Rn×d。

在 Transformer 模型提出之前,对 token 序列 X 的常规编码方式是通过循环网络 (RNNs) 和卷积网络 (CNNs)

之后可以用Attention 机制编码整个文本,相比 RNN 要逐步递归才能获得全局信息(因此一般使用双向 RNN),而 CNN 实际只能获取局部信息,需要通过层叠来增大感受野,Attention 机制一步到位获取了全局信息

Scaled Dot-product Attention

虽然 Attention 有许多种实现方式,但是最常见的还是 Scaled Dot-product Attention。

img

就是之前看的那一套原理

注意力代码部分

这段代码的作用是创建一个嵌入层,并将输入的单词 ID 转换为嵌入向量/词向量

# 导入必要的库
from torch import nn  # 导入PyTorch的神经网络模块,包含构建神经网络所需的类
from transformers import AutoConfig  # 导入自动配置类,用于加载模型配置
from transformers import AutoTokenizer  # 导入自动标记器类,用于处理文本数据# 定义预训练模型的检查点路径
model_ckpt = "./bert-base-uncased"  # 这里指定的是BERT模型的路径# 从预训练模型中加载标记器,以便将文本转换为模型输入
tokenizer = AutoTokenizer.from_pretrained(model_ckpt)# 定义待处理的文本字符串
text = "time flies like an arrow"  # 需要进行编码的文本示例# 使用标记器对文本进行编码,返回PyTorch张量形式的输入
# add_special_tokens=False表示不添加特殊的标记(如[CLS]和[SEP])
inputs = tokenizer(text, return_tensors="pt", add_special_tokens=True)# 打印编码后的输入ID,显示文本对应的词汇索引
print(inputs.input_ids)# 从预训练模型中加载配置,包括词汇大小和隐藏层大小等信息
config = AutoConfig.from_pretrained(model_ckpt)# 创建一个嵌入层,用于将输入ID映射到词向量(嵌入向量)
# vocab_size表示词汇表的大小,hidden_size表示嵌入向量的维度
token_emb = nn.Embedding(config.vocab_size, config.hidden_size)
#嵌入层在训练过程中承担着将离散的文本数据转化为模型可以处理的连续数值向量的任务,会学习每个词汇索引对应的词向量,这些向量将捕捉词汇的语义信息。# 打印嵌入层的参数,查看嵌入层的维度
print(token_emb)# 将输入ID转换为嵌入向量,inputs.input_ids是一个张量,表示文本的索引
inputs_embeds = token_emb(inputs.input_ids)# 打印嵌入向量的尺寸,通常为(batch_size, sequence_length, hidden_size)
print(inputs_embeds.size())
tensor([[ 2051, 10029,  2066,  2019,  8612]])
Embedding(30522, 768)
# 30522:这是词汇表的大小(vocab_size)。
# 在BERT模型中,词汇表包含30522个不同的词汇或标记。这意味着嵌入层能够处理的不同单词或标记的数量是30522。
# 768:这是嵌入向量的维度(hidden_size)。每个词汇或标记在嵌入层中都被映射为一个768维的向量。这些向量用于捕捉词汇的语义信息,768维的向量能够提供足够的空间来表示复杂的语义关系。
torch.Size([1, 5, 768])

词向量是指将词语映射到一个连续向量空间中形成的表示。其定义可以概括为以下几点:

  1. 数值表示:每个词用一个固定维度的实数向量表示,通常为高维向量(例如100维、300维等)。
  2. 语义关系:词向量能够捕捉到词与词之间的语义和语法关系,类似的词在向量空间中靠得更近,而不相似的词则距离较远。
  3. 训练方法:词向量可以通过不同的模型训练得到,如Word2Vec、GloVe、FastText等,或者通过深度学习模型(如BERT、GPT等)自动生成。
  4. 应用广泛:词向量广泛应用于自然语言处理任务,如文本分类、情感分析、机器翻译等,作为输入特征提高模型性能。

计算注意力分数α

import torch  # 导入PyTorch库
from math import sqrt  # 导入平方根函数# 将输入的词向量作为查询(Q)、键(K)和值(V)
Q = K = V = inputs_embeds  # 使用相同的嵌入向量作为 Q、K 和 V,这里是简化了实际会成三个不同的权重矩阵# 获取键(K)的最后一维的大小,通常是词向量的维度
dim_k = K.size(-1)# 计算注意力分数:使用批量矩阵乘法(bmm)计算 Q 和 K 的点积
# K.transpose(1, 2) 将 K 的最后两个维度交换,以便进行矩阵乘法
# 用词向量的维度的平方根进行缩放,以防分数过大
scores = torch.bmm(Q, K.transpose(1, 2)) / sqrt(dim_k)# 打印注意力分数的尺寸,形状为 (batch_size, sequence_length, sequence_length)
print(scores.size())
torch.Size([1, 5, 5])

根据查询QKV的值计算注意力输出

掩码会被用来调整注意力分数,屏蔽掉不需要的部分

归一化得到最终的输出,反应词与词之间的相关性

import torch  # 导入PyTorch库
import torch.nn.functional as F  # 导入PyTorch的函数式API
from math import sqrt  # 导入平方根函数def scaled_dot_product_attention(query, key, value, query_mask=None, key_mask=None, mask=None):"""计算缩放点积注意力机制的函数。参数:- query: 查询张量,形状为 (batch_size, seq_length, dim_k)- key: 键张量,形状为 (batch_size, seq_length, dim_k)- value: 值张量,形状为 (batch_size, seq_length, dim_v)- query_mask: 可选的查询掩码,形状为 (batch_size, seq_length)- key_mask: 可选的键掩码,形状为 (batch_size, seq_length)- mask: 可选的综合掩码,形状为 (batch_size, seq_length, seq_length)返回:- 输出张量,形状为 (batch_size, seq_length, dim_v)"""# 获取查询张量的最后一维的大小,即键的维度dim_k = query.size(-1)# 计算注意力分数:使用批量矩阵乘法计算查询和键的点积# K 的转置用于适应矩阵乘法# 用词向量的维度的平方根进行缩放,以防分数过大scores = torch.bmm(query, key.transpose(1, 2)) / sqrt(dim_k)# 如果提供了查询掩码和键掩码,则计算综合掩码if query_mask is not None and key_mask is not None:mask = torch.bmm(query_mask.unsqueeze(-1), key_mask.unsqueeze(1))# 如果有掩码,将对应位置的注意力分数设置为负无穷if mask is not None:scores = scores.masked_fill(mask == 0, -float("inf"))# 计算注意力权重,使用 softmax 函数归一化分数weights = F.softmax(scores, dim=-1)# 将注意力权重与值张量相乘,得到最终输出return torch.bmm(weights, value)  # 返回的形状为 (batch_size, seq_length, dim_v)

多头注意力机制

所谓的“多头” (Multi-head),其实就是多做几次 Scaled Dot-product Attention,然后把结果拼接。

映射到特征空间,可以通过乘权重矩阵映射到高维的空间,这样就可以更好的表示信息

from torch import nnclass AttentionHead(nn.Module):def __init__(self, embed_dim, head_dim):super().__init__()#下面就是映射到特征空间的过程,是通过线性变化乘权重矩阵实现的self.q = nn.Linear(embed_dim, head_dim)self.k = nn.Linear(embed_dim, head_dim)self.v = nn.Linear(embed_dim, head_dim)def forward(self, query, key, value, query_mask=None, key_mask=None, mask=None):attn_outputs = scaled_dot_product_attention(self.q(query), self.k(key), self.v(value), query_mask, key_mask, mask)return attn_outputs

定义了一个多头注意力机制的类 MultiHeadAttention

class MultiHeadAttention(nn.Module):def __init__(self, config):super().__init__()#从配置中获取嵌入维度embed_dim = config.hidden_size #注意力头数量num_heads = config.num_attention_heads#获取注意力头的维度head_dim = embed_dim // num_heads#创建注意力头 创建多个 AttentionHead 实例self.heads = nn.ModuleList([AttentionHead(embed_dim, head_dim) for _ in range(num_heads)])self.output_linear = nn.Linear(embed_dim, embed_dim)维度一致也是需要线性变化,可以通过权重 调整输入特征的重要性,帮助模型学习更有效的表示def forward(self, query, key, value, query_mask=None, key_mask=None, mask=None):x = torch.cat([h(query, key, value, query_mask, key_mask, mask) for h in self.heads], dim=-1)x = self.output_linear(x)return x

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/149388.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

基于python的django微博内容网络分析系统,实现文本划分词结构

本项目旨在开发一个基于Python的Django框架的微博内容网络分析系统,聚焦于微博文本的分词处理、名词提取和主成分分析。该系统通过数据收集与预处理、分词及结构化文本分析,为舆情监测、话题分析和用户行为研究提供了一体化的解决方案。 主要功能包括&a…

lkhgjfjghkbhjk

📢博客主页:https://blog.csdn.net/2301_779549673 📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正! 📢本文由 JohnKi 原创,首发于 CSDN🙉 📢未来很长&#…

LED驱动电路

LED驱动电路简介 摘要: LED照明是今年来快速兴起发展的一种新型光源,它的许多良好特点使得它的应用面越来越广。LED的单向导电特性使人一般认为应该用直流驱动,但是对直流恒压和限流的装置在保证比较好的限流特性时,自身功耗是很…

路政通 | OPENAIGC开发者大赛高校组AI创新之星奖

在第二届拯救者杯OPENAIGC开发者大赛中,涌现出一批技术突出、创意卓越的作品。为了让这些优秀项目被更多人看到,我们特意开设了优秀作品报道专栏,旨在展示其独特之处和开发者的精彩故事。 无论您是技术专家还是爱好者,希望能带给…

深圳某局联想SR850服务器黄灯 不开机维修

深圳 福田区1台Lenovo Thinksystem SR850 四路服务器黄灯问题现场处理。 1:型号:联想SR850 机架式2U服务器 2:故障:能通电,开机按钮快闪,随后叹号警告灯常亮 3:用户自行折腾无果后找到我们tech …

QT客户端发送HTTP请求此时服务器到底收到了哪些数据?

一个Http请求包括 请求行 请求头 空行 请求体 下面是示例: 1,2,3,4分别代表上面的四个部分,我只是做了一些解析,具体可以结合代码 1. post / HTTP/1.1 2.GET请求头包括Host(主机名),user-agent(客户端标识符)&am…

C++类和对象(中)【下篇】

&#x1f31f;个人主页&#xff1a;落叶 &#x1f31f;当前专栏: C专栏 目录 赋值运算符重载 运算符重载 赋值运算符重载 日期类实现 运算符重载<和运算符重载 运算符重载进行复用 运算符重载< 运算符重载> 运算符重载> 运算符重载! 获取某年某月的天数…

解决方案 | 镭速助力动漫游戏行业突破跨网文件交换瓶颈

在数字化浪潮推动下&#xff0c;动漫游戏行业蓬勃发展。随着高清技术的普及和云游戏的兴起&#xff0c;动漫游戏行业对动画的画质要求越来越高&#xff0c;数据量呈现爆炸式增长。然而&#xff0c;行业内的跨网文件交换难题也日益凸显&#xff0c;成为制约行业发展的瓶颈。 行业…

pyqt瀑布流布局

最近研究瀑布流布局&#xff0c;发现都是收费的&#xff0c;所以只能自己写算法写布局。 所以啥都不说直接上代码 ImageLabel 参考 pyqt5 QLabel显示网络图片或qfluentwidgets官网 代码 import math import sys from pathlib import Pathfrom PyQt5.Qt import * from qflue…

erlang学习:Linux命令学习4

顺序控制语句学习 if&#xff0c;else对文件操作 判断一个文件夹是否存在&#xff0c;如果存在则进行删除&#xff0c;如果不存在则创建该文件夹&#xff0c;并复制一份该脚本后&#xff0c;删除该脚本 if [ -d "/erlangtest/testdir"]; then echo "删除文件夹…

JavaWeb--小白笔记07-2:超链接以及servlet对表单数据的完整处理

一.超链接 Html使用标签<a></a>来设置超链接&#xff0c;<a>有一个属性href"" 必须加进去&#xff0c;里面就是链接地址 注意&#xff1a;链接里必须包含https://前缀 <a></a>里面可以是一个字&#xff0c;一个词或者一副图...点击…

27 C 语言标准库 <stdio.h> 中的两个重要字符串函数:sprintf、sscanf

目录 1 sprintf 1.1 函数原型 1.2 功能说明 1.3 案例演示 1.4 注意事项 2 sscanf 2.1 函数原型 2.2 功能说明 2.3 案例演示 2.4 注意事项 1 sprintf 1.1 函数原型 sprintf 函数是 C 语言标准库中的一个函数&#xff0c;用于将格式化的数据写入字符串。其函数原型定义…

【软件测试】详解测试中常用的几种测试方法

目录 一、集成测试二、 系统测试三、验收测试四、回归测试 总结 一、集成测试 术语 集成测试是继组件测试之后的又一个层次。集成测试假定交给这个层次的测试对象已经经过了组件测试&#xff0c;并且任何组件内部的缺陷都已经尽可能地被纠正。 集成 开发人员、测试人员和专…

【裸机装机系列】14.kali(ubuntu)-linux装机在分区时采用manual手动形式该怎么做

推荐阅读&#xff1a; 1.kali(ubuntu)-为什么弃用ubuntu&#xff0c;而选择基于debian的kali操作系统 如果在装机的时候选则了manual手动模式&#xff0c;可以根据以下步骤一步步做: 1> 在“partition disks”这个地方选择了manual,也就是手动自己分区的方式 点击"c…

大模型框架 LangChain 介绍

文章目录 langchain介绍安装依赖大模型类别千帆大模型案例常见问题 langchain介绍 是一个开源大语言模型框架&#xff0c;本身不提供大模型算法&#xff0c;只提供对接大模型算法平台的接口&#xff08;模型包裹器&#xff09;&#xff1b;langchain官网v0.2&#xff0c;内部涉…

828华为云征文 | 云服务器Flexus X实例,Docker集成搭建斗地主

828华为云征文 | 云服务器Flexus X实例&#xff0c;Docker集成搭建斗地主 华为云端口放行 服务器放行对应端口8078 Docker安装并配置镜像加速 1、购买华为云 Flexus X 实例 Flexus云服务器X实例-华为云 (huaweicloud.com) 2、docker安装 yum install -y docker-ce3、验证 Dock…

问请问请问2312123213123

&#x1f4e2;博客主页&#xff1a;https://blog.csdn.net/2301_779549673 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01; &#x1f4e2;本文由 JohnKi 原创&#xff0c;首发于 CSDN&#x1f649; &#x1f4e2;未来很长&#…

后台管理系统开箱即用的组件库!!【送源码】

今天给大家推荐几款的后台管理系统开箱即用的组件库&#xff0c;基于ElementUI二次封装&#xff0c;开发必备 Headless UI Headless UI 是一款出色的前端组件库&#xff0c;专为与 Tailwind CSS 集成而设计。一组完全无样式、完全可访问的 UI 组件&#xff0c;可以自由的引入…

使用Docker和cpolar在Linux服务器上搭建DashDot监控面板

使用Docker和cpolar在Linux服务器上搭建DashDot监控面板 前言环境准备安装Docker下载Dashdot镜像 部署DashDot应用本地访问DashDot服务安装cpolar内网穿透固定DashDot公网地址结语 前言 在这个数字化飞速发展的时代&#xff0c;服务器作为支撑各种应用和服务的基础设施&#xf…

Unity进阶之C#知识补充

概述 Unity跨平台的基本原理 了解.Net相关知识 Unity跨平台的基本原理&#xff08;Mono&#xff09; Unity跨平台的基本原理&#xff08;IL2CPP&#xff09; IL2CPP 模式可能存在的问题处理 报错的话就去下载 用到的测试类 C#版本和Unity的关系 C#各版本新功能和语法 C# 1~4 功…