字节终面问Transformer,就很离谱...

news2024/10/1 16:30:51

Transformer 是目前 NLP 甚至是整个深度学习领域不能不提到的框架,同时大部分 LLM 也是使用其进行训练生成模型,所以 Transformer 几乎是目前每一个机器人开发者或者人工智能开发者不能越过的一个框架。

接下来本文将从顶层往下去一步步掀开 Transformer 的面纱。

01

Transformer 概述

Transformer 模型来自论文 Attention Is All You Need。

链接:https://arxiv.org/abs/1706.03762

在论文中最初是为了提高机器翻译的效率,它使用了 Self-Attention 机制和 Position Encoding 去替代 RNN。

后来大家发现 Self-Attention 的效果很好,并且在其它的地方也可以使用 Transformer 模型。并引出后面的 BERT 和 GPT 系列。

大家一般看到的 Transformer 框架如下图所示:

02

Transformer 模型概览

首先把模型看成一个黑盒,如下图所示,对于机器翻译来说,它的输入是源语言(法语)的句子,输出是目标语言(英语)的句子。

把黑盒子稍微打开一点,Transformer(或者任何的 NMT 系统)可以分成 Encoder 和 Decoder 两个部分,如下图所示。

再展开一点,Encoder 由很多结构一样的 Encoder 堆叠而成,Decoder 也是一样。如下图所示。

每一个 Encoder 的输入是下一层 Encoder 输出,最底层 Encoder 的输入是原始的输入(法语句子);Decoder 也是类似,但是最后一层 Encoder 的输出会输入给每一个 Decoder 层,这是 Attention 机制的要求。

每一层的 Encoder 都是相同的结构,它由一个 Self-Attention 层和一个前馈网络(全连接网络)组成,如下图所示。

每一层的 Decoder 也是相同的结构,它除了 Self-Attention 层和全连接层之外还多了一个 Attention 层,这个 Attention 层使得 Decoder 在解码时会考虑最后一层 Encoder 所有时刻的输出。它的结构如下图所示。

03

Transformer 流程串联

Transformer 的串流需要 tensor 的加入,输入的句子需要通过 Embedding 把它变成一个连续稠密的向量,如下图所示。

Embedding 之后的序列会输入 Encoder,首先经过 Self-Attention 层然后再经过全连接层。

我们在计算 𝑧𝑖 时需要依赖所有时刻的输入 𝑥1,…,𝑥𝑛,这是可以用矩阵运算一下子把所有的 𝑧𝑖 计算出来的。

而全连接网络的计算则完全是独立的,计算 i 时刻的输出只需要输入 𝑧𝑖 就足够了,因此很容易并行计算。

下图更加明确地表达了这一点。图中 Self-Attention 层是一个大的方框,表示它的输入是所有的 𝑥1,…,𝑥𝑛,输出是 𝑧1,…,𝑧𝑛。

而全连接层每个时刻是一个方框(但不同时刻的参数是共享的),表示计算 𝑟𝑖 只需要 𝑧𝑖。此外,前一层的输出 𝑟1,…,𝑟𝑛 直接输入到下一层。

04

Self-Attention 介绍

比如我们要翻译如下句子”The animal didn’t cross the street because it was too tired”(这个动物无法穿越马路,因为它太累了)。

这里的 it 到底指代什么呢,是 animal 还是 street?要知道具体的指代,我们需要在理解 it 的时候同时关注所有的单词,重点是 animal、street 和 tired,然后根据知识(常识)我们知道只有 animal 才能 tired,而 street 是不能 tired 的。

Self-Attention 用 Encoder 在编码一个词的时候会考虑句子中所有其它的词,从而确定怎么编码当前词。如果把 tired 换成 narrow,那么 it 就指代的是 street 了。

下图是模型的最上一层 Encoder 的 Attention 可视化图。这是 tensor2tensor 这个工具输出的内容。

我们可以看到,在编码 it 的时候有一个 Attention Head(后面会讲到)注意到了 Animal,因此编码后的 it 有 Animal 的语义。

下面我们详细的介绍 Self-Attention 是怎么计算的,首先介绍向量的形式逐个时刻计算,这便于理解,接下来我们把它写出矩阵的形式一次计算所有时刻的结果。

对于输入的每一个向量(第一层是词的 Embedding,其它层是前一层的输出),我们首先需要生成 3 个新的向量 Q、K 和 V,分别代表查询(Query)向量、Key 向量和 Value 向量。

Q 表示为了编码当前词,需要去注意(attend to)其它(其实也包括它自己)的词,我们需要有一个查询向量。

而 Key 向量可以认为是这个词的关键的用于被检索的信息,而 Value 向量是真正的内容。

具体的计算过程如下图所示。比如图中的输入是两个词”thinking”和”machines”,我们对它们进行 Embedding(这是第一层,如果是后面的层,直接输入就是向量了),得到向量 𝑥1,𝑥2。

接着我们用 3 个矩阵分别对它们进行变换,得到向量 𝑞1,𝑘1,𝑣1 和 𝑞2,𝑘2,𝑣2。比如 𝑞1=𝑥1𝑊𝑄,图中 𝑥1 的 shape 是 1x4,𝑊𝑄 是 4x3,得到的 𝑞1 是 1x3。

其它的计算也是类似的,为了能够使得 Key 和 Query 可以内积,我们要求 𝑊𝐾 和 𝑊𝑄 的 shape 是一样的,但是并不要求 𝑊𝑉 和它们一定一样(虽然实际论文实现是一样的)。

每个时刻 t 都计算出 𝑄𝑡,𝐾𝑡,𝑉𝑡 之后,我们就可以来计算 Self-Attention 了。以第一个时刻为例,我们首先计算 𝑞1 和 𝑘1,𝑘2 的内积,得到 score。

过程如下图所示:

接下来使用 softmax 把得分变成概率,注意这里把得分除以 8(𝑑𝑘) 之后再计算的 softmax,根据论文的说法,这样计算梯度时会更加稳定(stable)。

计算过程如下图所示:

接下来用 softmax 得到的概率对所有时刻的 V 求加权平均,这样就可以认为得到的向量根据 Self-Attention 的概率综合考虑了所有时刻的输入信息。

计算过程如下图所示:

这里只是演示了计算第一个时刻的过程,计算其它时刻的过程是完全一样的。

softmax 示例代码:

Python
import numpy as np
   def softmax(x):
       """Compute softmax values for each sets of scores in x."""
       # e_x = np.exp(x)  
       e_x = np.exp(x )  
       return e_x / e_x.sum()

if __name__ == '__main__':  
    
    x = np.array([-3, 2, -1, 0])
    res = softmax(x )
    print(res)                        # [0.0056533  0.83902451 0.04177257 0.11354962]
     

特别注意,以上过程是可以并行计算的。

05

Multi-Head Attention

论文还提出了 Multi-Head Attention 的概念。其实很简单,前面定义的一组 Q、K 和 V 可以让一个词 attend to 相关的词,我们可以定义多组 Q、K 和 V,它们分别可以关注不同的上下文。

计算 Q、K 和 V 的过程还是一样,不过现在变换矩阵从一组(𝑊𝑄,𝑊𝐾,𝑊𝑉)变成了多组(𝑊𝑄0,𝑊𝐾0,𝑊𝑉0) ,(𝑊𝑄1,𝑊𝐾1,𝑊𝑉1)。

如下图所示:

对于输入矩阵(time_step, num_input),每一组 Q、K 和 V 都可以得到一个输出矩阵 Z(time_step, num_features)。

如下图所示:

但是后面的全连接网络需要的输入是一个矩阵而不是多个矩阵,因此我们可以把多个 head 输出的 Z 按照第二个维度拼接起来,但是这样的特征有一些多,因此 Transformer 又用了一个线性变换(矩阵 𝑊𝑂)对它进行了压缩。

这个过程如下图所示:

上面的步骤涉及很多步骤和矩阵运算,我们用一张大图把整个过程表示出来,如下图所示。

我们已经学习了 Transformer 的 Self-Attention 机制,下面我们通过一个具体的例子来看看不同的 Attention Head 到底学习到了什么样的语义。

从上面两图的对比也能看出使用多个 Head 的好处——每个 Head(在数据的驱动下)学习到不同的语义。

06

位置编码(Positional Encoding)

我们的目的是用 Self-Attention 替代 RNN,RNN 能够记住过去的信息,这可以通过 Self-Attention“实时”的注意相关的任何词来实现等价(甚至更好)的效果。

RNN 还有一个特定就是能考虑词的顺序(位置)关系,一个句子即使词完全是相同的但是语义可能完全不同,比如”北京到上海的机票”与”上海到北京的机票”,它们的语义就有很大的差别。

我们上面的介绍的 Self-Attention 是不考虑词的顺序的,如果模型参数固定了,上面两个句子的北京都会被编码成相同的向量。

但是实际上我们可以期望这两个北京编码的结果不同,前者可能需要编码出发城市的语义,而后者需要包含目的城市的语义。

而 RNN 是可以(至少是可能)学到这一点的。当然 RNN 为了实现这一点的代价就是顺序处理,很难并行。

为了解决这个问题,我们需要引入位置编码,也就是 t 时刻的输入,除了 Embedding 之外(这是与位置无关的),我们还引入一个向量,这个向量是与 t 有关的,我们把 Embedding 和位置编码向量加起来作为模型的输入。

这样的话如果两个词在不同的位置出现了,虽然它们的 Embedding 是相同的,但是由于位置编码不同,最终得到的向量也是不同的。

位置编码有很多方法,其中需要考虑的一个重要因素就是需要它编码的是相对位置的关系。

比如两个句子:”北京到上海的机票”和”你好,我们要一张北京到上海的机票”。

显然加入位置编码之后,两个北京的向量是不同的了,两个上海的向量也是不同的了,但是我们期望 Query(北京1)Key(上海1)却是等于Query(北京2)Key(上海2)的。具体的编码算法我们在代码部分再介绍。

位置编码加入后的模型如下图所示:

一个具体的位置编码的例子如下图所示:

07

残差和归一化

每个 Self-Attention 层都会加一个残差连接,然后是一个 LayerNorm 层,如下图所示。

下图展示了更多细节:输入 𝑥1,𝑥2 经 self-attention 层之后变成 𝑧1,𝑧2,然后和残差连接的输入 𝑥1,𝑥2 加起来,然后经过 LayerNorm 层输出给全连接层。

全连接层也是有一个残差连接和一个 LayerNorm 层,最后再输出给上一层。

Decoder 和 Encoder 是类似的,如下图所示,区别在于它多了一个 Encoder-Decoder Attention 层,这个层的输入除了来自 Self-Attention 之外还有 Encoder 最后一层的所有时刻的输出。

Encoder-Decoder Attention 层的 Query 来自前面一层,而 Key 和 Value 则来自 Encoder 的输出。

此外在解码器的编码器-解码器注意力层中,掩码的使用非常关键,以确保解码器在生成每个目标词时只能使用到源语言句子的信息和它之前已经生成的目标词的信息。

08

Pytorch 实现 Transformer

代码如下:

Python
import torch
import torch.nn as nn
import math

# 位置编码模块
class PositionalEncoding(nn.Module):
    def __init__(self, d_model, max_len=5000):
        super(PositionalEncoding, self).__init__()       
        pe = torch.zeros(max_len, d_model)       
        position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)       
        div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model))        
        pe[:, 0::2] = torch.sin(position * div_term)      
        pe[:, 1::2] = torch.cos(position * div_term)      
        pe = pe.unsqueeze(0)      
        self.register_buffer('pe', pe)          
        
    def forward(self, x):
        x = x + self.pe[:x.size(0), :
        return x

# Transformer模型
class TransformerModel(nn.Module):   
    def __init__(self, ntoken, d_model, nhead, d_hid, nlayers, dropout=0.5):    
        super(TransformerModel, self).__init__()
        self.model_type = 'Transformer'
        self.pos_encoder = PositionalEncoding(d_model)
        self.encoder = nn.Embedding(ntoken, d_model)
        self.transformer = nn.Transformer(d_model, nhead, d_hid, nlayers, dropout)
        self.decoder = nn.Linear(d_model, ntoken)
        self.init_weights()
        self.dropout = nn.Dropout(dropout) 
        
    def generate_square_subsequent_mask(self, sz):  
       `# 生成后续掩码,用于防止位置信息泄露        
       mask = (torch.triu(torch.ones(sz, sz)) == 1).transpose(0, 1)        
       mask = mask.float().masked_fill(mask == 0, float('-inf')).masked_fill(mask == 1, float(0.0))        
       return mask  
       
    def init_weights(self):  
        # 初始化权重
        initrange = 0.1
        self.encoder.weight.data.uniform_(-initrange, initrange)`        
        self.decoder.bias.data.zero_()
        self.decoder.weight.data.uniform_(-initrange, initrange)
       
    def forward(self, src, src_mask)
        # 前向传播      
        src = self.encoder(src) * math.sqrt(self.d_model)        
        src = self.pos_encoder(src)        
        output = self.transformer(src, src, src_key_padding_mask=src_mask)       
        output = self.decoder(output)        
        return output

# 示例使用
ntokens = 1000  # 词汇表大小
d_model = 512  # 嵌入维度
nhead = 8  # 多头注意力中的头数
d_hid = 2048  # 前馈网络模型的维度
nlayers = 6  # 层数
dropout = 0.2  # dropout比率

model = TransformerModel(ntokens, d_model, nhead, d_hid, nlayers, dropout)

# 示例输入
src = torch.randint(0, ntokens, (10, 32))  # (序列长度, 批量大小)
src_mask = model.generate_square_subsequent_mask(10)  # 创建掩码

output = model(src, src_mask)
print(output)

09

推理过程

在 Transformer 模型的机器翻译任务中,解码器生成第一个翻译后的词(通常称为第一个目标词)的过程如下:

1、起始符号: 在解码器的输入序列的开始位置,通常会添加一个特殊的起始符号,如(Start Of Sentence)。这个符号告诉模型翻译过程的开始。

2、初始化隐藏状态: 解码器的隐藏状态通常初始化为零向量或从编码器的最后一层的输出中获得。这个隐藏状态在生成序列的每一步中都会更新。

3、第一次迭代: 在第一次迭代中,解码器的输入只包含起始符号。

解码器通过以下步骤生成第一个词:

  • 将起始符号 通过嵌入层转换为嵌入向量。

  • 将这个嵌入向量与编码器的输出一起输入到解码器的第一个注意力层。

  • 在自注意力层中,使用因果掩码(Look-ahead Mask)确保解码器只能关注到当前位置和之前的词(在这个例子中只有 )。

  • 在编码器-解码器注意力层中,解码器可以查看整个编码器的输出,因为这是第一次迭代,解码器需要获取关于整个源语言句子的信息。

  • 经过解码器的前馈网络后,输出层会生成一个概率分布,表示下一个可能的词。

  • 选择概率最高的词作为第一个翻译后的词,或者使用贪婪策略、束搜索(Beam Search)等解码策略来选择词。

4、后续迭代: 一旦生成了第一个词,它就会被添加到解码器的输入序列中,与一起作为下一步的输入。

在后续的迭代中,解码器会继续生成下一个词,直到遇到结束符号或达到最大序列长度。

在训练阶段,目标序列的真实词(包括和)会用于计算损失函数,并通过反向传播更新模型的权重。在推理阶段,解码器使用上述过程逐步生成翻译,直到生成完整的句子。

如何学习AI大模型?

作为一名热心肠的互联网老兵,我决定把宝贵的AI知识分享给大家。 至于能学习到多少就看你的学习毅力和能力了 。我已将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。

这份完整版的大模型 AI 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费

一、全套AGI大模型学习路线

AI大模型时代的学习之旅:从基础到前沿,掌握人工智能的核心技能!

img

二、640套AI大模型报告合集

这套包含640份报告的合集,涵盖了AI大模型的理论研究、技术实现、行业应用等多个方面。无论您是科研人员、工程师,还是对AI大模型感兴趣的爱好者,这套报告合集都将为您提供宝贵的信息和启示。

img

三、AI大模型经典PDF籍

随着人工智能技术的飞速发展,AI大模型已经成为了当今科技领域的一大热点。这些大型预训练模型,如GPT-3、BERT、XLNet等,以其强大的语言理解和生成能力,正在改变我们对人工智能的认识。 那以下这些PDF籍就是非常不错的学习资源。

img

四、AI大模型商业化落地方案

img

作为普通人,入局大模型时代需要持续学习和实践,不断提高自己的技能和认知水平,同时也需要有责任感和伦理意识,为人工智能的健康发展贡献力量。

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

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

相关文章

只写CURD后台管理的Java后端要如何提升自己

你是否工作3~5年后,发现日常只做了CURD的简单代码。 你是否每次面试就会头疼,自己写的代码,除了日常CURD简历上毫无亮点可写 抱怨过苦恼过也后悔过,但是站在现在的时间点回想以前,发现有很多事情我们是可以做的更好的。…

宁夏众智科技OA办公系统存在SQL注入漏洞

漏洞描述 宁夏众智科技OA办公系统存在SQL注入漏洞 漏洞复现 POC POST /Account/Login?ACTIndex&CLRHome HTTP/1.1 Host: Content-Length: 45 Cache-Control: max-age0 Origin: http://39.105.48.206 Content-Type: application/x-www-form-urlencoded Upgrade-Insecur…

【C语言指南】数据类型详解(上)——内置类型

💓 博客主页:倔强的石头的CSDN主页 📝Gitee主页:倔强的石头的gitee主页 ⏩ 文章专栏:《C语言指南》 期待您的关注 目录 引言 1. 整型(Integer Types) 2. 浮点型(Floating-Point …

C++ 游戏开发

C游戏开发 C 是一种高效、灵活且功能强大的编程语言,因其性能和控制能力而在游戏开发中被广泛应用。许多著名的游戏引擎,如 Unreal Engine、CryEngine 和 Godot 等,都依赖于 C 进行核心开发。本文将详细介绍 C 在游戏开发中的应用&#xff0…

【机器学习】ID3、C4.5、CART 算法

目录 常见的决策树算法 1. ID3 2. C4.5 3. CART 决策树的优缺点 优点: 缺点: 决策树的优化 常见的决策树算法 1. ID3 ID3(Iterative Dichotomiser 3)算法使用信息增益作为特征选择的标准。它是一种贪心算法,信…

Ubuntu开机进入紧急模式处理

文章目录 Ubuntu开机进入紧急模式处理一、问题描述二、解决办法参考 Ubuntu开机进入紧急模式处理 一、问题描述 Ubuntu开机不能够正常启动,自动进入紧急模式(You are in emergency mode)。具体如下所示: 二、解决办法 按CtrlD进…

基于SpringBoot+Vue的智能宾馆预定系统

作者:计算机学姐 开发技术:SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等,“文末源码”。 专栏推荐:前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏:…

基于人工智能的实时健身训练分析系统:深蹲姿态识别与动作评估

关于深度实战社区 我们是一个深度学习领域的独立工作室。团队成员有:中科大硕士、纽约大学硕士、浙江大学硕士、华东理工博士等,曾在腾讯、百度、德勤等担任算法工程师/产品经理。全网20多万粉丝,拥有2篇国家级人工智能发明专利。 社区特色…

前端css样式设置元素的绝对定位和相对定位,要注意宽度和高度的设置

vue3子div position absolute,父div positon relative 。如果不设置子div的 width 和height,那么子div中如果数据变长,子div相对父div位置会变化。子div数据超过&#xff0c;显示... 如何实现 <template><div class"parent"><div class"child&q…

[含文档+PPT+源码等]精品大数据项目-基于Django实现的高校图书馆智能推送系统的设计与实现

大数据项目——基于Django实现的高校图书馆智能推送系统的设计与实现背景&#xff0c;可以从以下几个方面进行详细阐述&#xff1a; 一、信息技术的发展背景 随着信息技术的飞速发展和互联网的广泛普及&#xff0c;大数据已经成为现代社会的重要资源。在大数据背景下&#xf…

深入计算机语言之C++:C到C++的过度

&#x1f511;&#x1f511;博客主页&#xff1a;阿客不是客 &#x1f353;&#x1f353;系列专栏&#xff1a;从C语言到C语言的渐深学习 欢迎来到泊舟小课堂 &#x1f618;博客制作不易欢迎各位&#x1f44d;点赞⭐收藏➕关注 一、什么是C C&#xff08;c plus plus&#xff…

stm32单片机学习 - MDK仿真调试

1 进行环境配置 点击 Options for Target&#xff0c;也就是我们俗称的魔法棒。 将"C/C"中的Optimization选项选为Level 0(-O0) 作用:优化等级调为0级,便于调试时分析代码 勾选"Debug"中的Load Application at Starup 和 Run to main() 选项 作用:Load…

DBeaver详细安装与使用教程-免费的数据库管理工具

一、简介 二、安装教程 三、使用教程 1. 连接MySQL数据库 2. 查看表数据 3. 查看表属性 3. SQL编辑器和控制台 4.在DBeaver中设置定时备份数据库 一、简介 dbeaver是一款的数据库连接工具&#xff0c;免费&#xff0c;跨平台。 官网&#xff1a;DBeaver Community | …

前端——切换轮播图

学完前端js小知识后&#xff0c;动手操作的一个简单图片轮播图。 <!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><meta name"keywords" content"关键词信息"><meta name"des…

数据中心交换机与普通交换机之间的区别到底在哪里?

号主&#xff1a;老杨丨11年资深网络工程师&#xff0c;更多网工提升干货&#xff0c;请关注公众号&#xff1a;网络工程师俱乐部 上午好&#xff0c;我的网工朋友。 数据中心交换被设计用来满足数据中心特有的高性能、高可靠性和可扩展性需求。 与此同时&#xff0c;普通交换机…

ACL(Access Control List)访问控制列表

目录 ACL 访问控制列表 ACL分类 ACL的组成 ACL匹配机制 ACL调用方式 实验配置 不允许PC1访问PC4 只允许PC1访问PC4 高级ACL 基本概念 实验配置 限制ping 实验配置 限制DNS 基于时间的ACL 实验配置 ACL 访问控制列表 根据一系列不同的规则&#xff0c;设备根据这…

使用Python实现Auto.js的自动输入图形验证码

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

【unity进阶知识6】Resources的使用,如何封装一个Resources资源管理器

文章目录 一、Unity资源加载的几种方式1、Inspector窗口拖拽2、Resources3、AssetBundle4、Addressables&#xff08;可寻址资源系统&#xff09;5、AssetDatabase 二、准备三、同步加载Resources资源1、Resources.Load同步加载单个资源1.1、基本加载1.2、加载指定类型的资源1.…

详解JavaScript中把函数作为值

8.4 作为值的函数 JS中函数不仅是一种语法&#xff0c;也是值&#xff0c;也就是说&#xff0c;可以将函数赋值给变量&#xff0c;存储在对象的属性或者数组的元素中&#xff0c;作为参数传入另外一个函数等。 来看一个函数&#xff1a;function square(x){return x*x;} 定义…

深度学习之入门书籍

自学深度学习&#xff0c;书籍很重要。 从我个人来说&#xff0c;我不太习惯英译版本&#xff0c;或者那些牛人说的&#xff0c;直接读英文&#xff0c;我是水平不够。只讲自己的经验。牛人绕道。 推荐书籍: 深度学习:从入门到精通&#xff0c;这本书不错。把基础的深度学习的…