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

news2024/9/24 21:28:18

【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 nn

class 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.coloradmin.cn/o/2161480.html

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

相关文章

多颜色绘制语义分割/变化检测结果图

在论文绘图时,传统的二元语义分割结果图颜色单一(下图左),所以论文中常根据混淆矩阵类别使用多颜色进行绘制(下图右),可以看到,结果的可视化效果更好。 以下是绘制代码: …

同等学力申硕英语网课如何选择

很多考生想知道同等学力申硕英语网课如何选择,小编告诉大家,首先明确自己的学习目标和需求是为了提高口语、阅读、写作还是听力能力? 只有明确了自己的学习目标和需求,才能更好地选择适合自己的课程和平台。 二、选择知名品牌和口碑良好的平…

UE5地图白屏/过曝/非常亮の解决方法

今天遇到一个问题 , 新建项目 , 打开虚幻第三人称地图的默认关卡 , 发现白屏 , 啥也看不见 猜测可能是虚幻编辑器的bug , 造成白屏的原因应该是场景过曝了 记录一下解决方案 第一种解决方法 找到场景中的 后期处理体积 (PostProcessVolume) 直接删掉 或者找到 细节面板中 -…

衍射的角谱理论

一、单色平面波与本征函数 不考虑夫琅禾费近似, 则相干光场在给定二平面间的传播过程就是通过一个二维线性空不变系统。 上式函数是这个系统的本征函数,表示振幅为1的平面波在xy平面上的复振幅分布,空间频率分量 = cos / , = cos / 与平面波的传播方向相联系, 空间…

java和mysql命名规则不一样,每次在mybatis中起别名太麻烦?来看看如何设置自动自动映射!

简介: 在 Java 开发中,当使用 MyBatis 框架连接 Java 代码与 MySQL 数据库时,常常会遇到 Java 和 MySQL 命名规则不一致的问题,这使得每次在 MyBatis 中为查询结果起别名变得繁琐。本教程将深入探讨如何设置自动映射,以…

java节假日工具类,判断一个日期是否是法定节假日

java节假日工具类,判断一个日期是否是法定节假日 1.HolidayUtil工具类2.工具类生成的日期json文件3.结果展示 无需链接数据库,无需手写节假日集合列表 1.HolidayUtil工具类 import com.alibaba.fastjson.JSONObject; import com.fasterxml.jackson.data…

DataGemma:谷歌大模型

诸神缄默不语-个人CSDN博文目录 DataGemma是谷歌出的大模型,是gemma 2的升级版,主要亮点是基于检索解决幻觉问题。 在huggingface和kaggle上均可下载模型权重。 检索数据源是Google’s Data Commons知识图谱。 官方博客:https://blog.googl…

Centos redis下载安装以及redis manager连接详细教程

一、redis下载以安装: 1.切换到home目录下 cd /home(/是根目录,./是当前目录 ../是父目录) 2.执行wget http://download.redis.io/releases/redis-5.0.2.tar.gz(这里,不知道为什么安装6开头的版本在make的…

定制智慧科技展厅方案:哪些细节是成功的秘诀?

随着数字科技浪潮的迅猛推进,智慧科技展厅跃升为科技成果展示与技术对话的前沿阵地。其策划与实施方案因而显得尤为关键。在此过程中,精雕细琢每一环节,确保创意与技术的无缝对接,成为不可或缺的要点。现在,让我们深入…

CS创世8GB SD NAND的低功耗特性

在电子设备不断追求低功耗的今天,CS创世半导体的8GB SD NAND芯片以其低功耗特性脱颖而出。这款芯片的读写电流仅为15mA,相较于同类产品,其功耗显著降低,这不仅延长了设备的使用时间,还减少了对电池的依赖。这种低功耗特…

HDMI20协议解析_Audio_Sample

HDMI20协议解析_Audio_Sample 1.版本说明 日期作者版本说明202409XX风释雪初始版本 2.概述 当通过HDMI传输音频信号时,Audio_Sample是必须要传输的数据包之一; 通过前端硬件或软件收到PCM原始音频数据后,需要通过Audio_Sample packet发送给…

跟着B战学习JAVA面试八股文

学习链接:https://www.bilibili.com/video/BV1gm411S7EX/?spm_id_from333.337.search-card.all.click&vd_sourceefbaa07876b231ae3225ba8999116807 创建线程的几种方式? 继承Thread类实现Runnable接口实现Callable接口通过线程池来创建线程 为什么…

fiddler抓包09_过滤站点请求

课程大纲 1、 界面 Fiddler列表默认显示所有请求,可以设置过滤,按照规则只显示指定的请求。 界面介绍: “Use Filters”:过滤功能开关。勾选,开启过滤;反之不开启。 “Hosts”:根据站点&#xf…

免费ppt模板哪里找?职场必备这些利器

一眨眼,9月份的尾声渐近,无论是学生还是职场人士,都开始准备着新一轮的演讲和报告。在这个忙碌的时期,一份精美的PPT模板能够大幅提升你的工作效率,让你的演示更加引人入胜。 不用担心高昂的版权费用,市场…

LPDDR4芯片学习(一)——基础知识与引脚定义

一、基础知识 01 dram基本存储单元 当需要将一位数据存储到DRAM中时,晶体管会充电或放电电容。充电的电容表示逻辑高(1),放电的电容表示逻辑低(0)。由于电容会随着时间泄漏电荷,因此需要定期刷…

接口测试Postman关联,断言,前置,参数化用法

一、Postman下载 我们直接搜索Postman官网下载即可 Postman API Platformhttps://www.postman.com/ 二、使用 下载安装完成后我们需要登录注册,按照Postman的指示进行注册登录,不登陆可能有些功能无法使用 登陆完成我们就可以开始对接口进行测试了 …

用于体积医学图像分割的跨视角差异依赖网络|文献速递--基于多模态-半监督深度学习的病理学诊断与病灶分割

Title 题目 Cross-view discrepancy-dependency network for volumetric medical image segmentation 用于体积医学图像分割的跨视角差异依赖网络 01 文献速递介绍 医学图像分割的目标是通过为每个像素分配语义类别,从原始图像中描绘出受试者的解剖结构&#x…

Python项目的质量保证

首先来打个不恰当比喻,你在市场上购买苹果时,挑选最好的苹果相当简单。你可以通过触摸它们来挑选,选择最好的颜色、成熟度和没有可见的伤疤。这个过程称为质量控制——你只选择满足你要求的优质产品。当分拣站里有大量苹果时,事情…

利用F.interpolate()函数进行插值操作

函数简介 功能: 利用插值方法,对输入的张量数组进行上\下采样操作,换句话说就是科学合理地改变数组的尺寸大小,尽量保持数据完整。 torch.nn.functional.interpolate(input, sizeNone, scale_factorNone, modenearest, align_c…

Node.JS有什么用?给谁用?怎么学?通俗易懂,超级详细!

现在,nodejs主要是前端的小伙伴来用的。前端小伙伴也不用说去怎么学node,而是把node当做是一个环境。我们利用这个环境去搭建上层的一些应用,去使用一些工具。就像学习Windows一样,我们没有必要深入了解Windows的每一个细节&#…