大语言模型之一 Attention is all you need ---Transformer

大语言模型已经在很多领域大显身手,其应用包括只能写作、音乐创作、知识问答、聊天、客服、广告文案、论文、新闻、小说创作、润色、会议/文章摘要等等领域。在商业上模型即产品、服务即产品、插件即产品,任何形态的用户可触及的都可以是产品,商业付费一般都是会员制或者按次收费。当前大预言模型的核心结构是基于Transformer。

大模型之所以效果超出预期,一个很重要的原因是模型大到一定程度之后会发生质变,模型的记忆性和泛化性可以兼得。而Transformer可以令模型很大,大到在NLP领域模型可以发生质变,这使得应用得以井喷式出现在各个领域,但是也有一些问题存在需要进一步解决,这类大模型本质上是内容生成,生成的内容因符合如下三原则:
有用的(Helpful);
可信的(Honest);
无害的(Harmless)

仅仅基于Transformer框架的大预言模型(又称pretraining model)还不足以完全满足商业应用要求,业界的发展放到后续博客展开,本篇先谈谈大语言模型的核心架构Transformer。

Transformer 源于谷歌Brain 2017年针对机器翻译任务提出的,《Attention is all you need》论文详细解释了网络结构,在这个之前网络结构多采用RNN、CNN、LSTM、GRU等网络形式,这篇文章提出了一个新的核心结构-Transformer,其针对RNN网络在机器翻译上的弱点重新设计的结构,传统的Encoder-Decoder架构在建模过程中,下一个时刻的计算过程会依赖于上一个时刻的输出,而这种固有的属性就限制了传统的Encoder-Decoder模型就不能以并行的方式进行计算。

本文源码已托管到 github link地址

模型结构介绍

谷歌提出的Transformer也是包括Encoder和decoder两个部分,只是这两个部分核心是Attention结构,而非CNN、LSTM、GRU等这些结构。

对于Encoder,包含两层,一个self-attention层和一个前馈神经网络,self-attention能帮助当前节点不仅仅只关注当前的词,从而能获取到上下文的语义。Decoder也包含encoder提到的两层网络,但是在这两层中间还有一层attention层,帮助当前节点获取到当前需要关注的重点内容。
在这里插入图片描述
Transformer 模型架构
模型需要对输入的数据进行一个embedding操作(图中红色框框),Attention虽然可以提取出关注的信息,但是没有时序信息,而Position Encoding正是将时序信息转为位置信息来实现的,enmbedding结束之后加上位置编码,然后再输入到encoder层,self-attention处理完数据后把数据送给前馈神经网络(蓝色Feed Forward),前馈神经网络的计算可以并行,得到的输出会输入到下一个encoder。
请添加图片描述

  • Encoder 编码器
    • Multi-Head Attention
      • 多头自注意力机制,可以通过输入信息并行计算出查询-键-值(Query-Key-Value),来让后续的网络使用context来知道当前运算需要关注哪些信息。注意这里的计算QKV的矩阵也是网络参数的一部分,通过训练可以让网络的注意力更有效且集中。因为NLP领域都是时序上因果的,因而改进模型采用了因果多头自注意力模型。
    • Add 残差连接
      • 这里主要残差连接的主要作用是利用恒等映射来训练更深层的网络(输入和输出恒等),多头注意力和层归一化,前馈神经网络和层归一化,两部分均采用了残差连接。
    • Norm 层归一化
      • Layer Normalization 的作用是把神经网络中以样本维度为一层来进行归一化运算,以起到加快训练速度,加速收敛的作用。新的改进都是将Layer Normalization放在前面而非后面。
    • Feed Forward 前馈神经网络
      • 将通过了注意力层之后通过加权机制提取出的所关注信息,根据关注的信息在语义空间中做转换。
      • 因此MLP将Multi-Head Attention得到的向量再投影到一个更大的空间(论文里将空间放大了4倍)在那个大空间里可以更方便地提取需要的信息(使用Relu激活函数),最后再投影回token向量原来的空间。
  • Decoder
    • 和 Encoder基本一样,组成分为Masked Multi-Head Attention,Masked Encoder-Decoder Attention(这一层就是连接编码器和解码器的注意力层,后续由于GPT只用了编码器,因此删除了这一层。)和Feed Forward神经网络,三个部分的每一个部分,都有一个残差连接,后接一个Layer Normalization。下面介绍Decoder的Masked Self-Attention和Encoder-Decoder Attention两部分,
    • Masked Multi-Head Attention
      • Self-Attention的机制有一个问题,在训练过程中的完整标注数据都会暴露在 Decoder 中,这显然是不对的,我们需要对 Decoder 的输入进行一些处理,该处理被称为 Mask,将数据有选择的暴露给Decoder(在GPT中相当于遮住了后面的所有数据,由网络依次生成)。
    • Multi-Head Attention
    • Add 残差连接
    • Norm 层归一化
    • Feed Forward 前馈神经网络
  • 线性层和Softmax
    经过编码器和解码器最后是一层全连接层和SoftMax( 后面改进的大语言模型采用Gaussian Error Linear Units function)。线性层是一个简单的全连接的神经网络,它将解码器堆栈生成的向量投影到一个更大的向量,称为logits向量。Softmax层(Softmax 是用于多类分类问题的激活函数)将向量转换为概率(全部为正值,总和为1.0)。选择概率最高的单元,并生成与其关联的单词作为此时间步的输出。

模型Pytorch实现

红色部分input&output embedding。

class Embedder(nn.Module):
    def __init__(self, vocab_size, d_model):
        super().__init__()
        self.embed = nn.Embedding(vocab_size, d_model)

    def forward(self, x):
        #[123, 0, 23, 5] -> [[..512..], [...512...], ...]
        return self.embed(x)

位置编码

如下代码所示,其值会和上面的embedding相加后输入编解码模块。

class PositionalEncoder(nn.Module):

 def __init__(self, d_model: int, max_seq_len: int = 80):
     super().__init__()
     self.d_model = d_model

     #Create constant positional encoding matrix
     pos_matrix = torch.zeros(max_seq_len, d_model)

     # for pos in range(max_seq_len):
     #     for i in range(0, d_model, 2):
     #         pe_matrix[pos, i] = math.sin(pos/1000**(2*i/d_model))
     #         pe_matrix[pos, i+1] = math.cos(pos/1000**(2*i/d_model))
     #
     # pos_matrix = pe_matrix.unsqueeze(0) # Add one dimension for batch size

     den = torch.exp(-torch.arange(0, d_model, 2) * math.log(1000) / d_model)
     pos = torch.arange(0, max_seq_len).reshape(max_seq_len, 1)
     pos_matrix[:, 1::2] = torch.cos(pos * den)
     pos_matrix[:, 0::2] = torch.sin(pos * den)
     pos_matrix = pos_matrix.unsqueeze(0)


     self.register_buffer('pe', pos_matrix) #Register as persistent buffer

 def forward(self, x):
     # x is a sentence after embedding with dim (batch, number of words, vector dimension)
     seq_len = x.size()[1]
     x = x + self.pe[:, :seq_len]
     return x

self-attention

在这里插入图片描述

## Scaled Dot-Product Attention layer
def scaled_dot_product_attention(q, k, v, mask=None, dropout=None):
    # Shape of q and k are the same, both are (batch_size, seq_len, d_k)
    # Shape of v is (batch_size, seq_len, d_v)
    attention_scores = torch.matmul(q, k.transpose(-2, -1)) / math.sqrt(q.shape[-1]) # size (bath_size, seq_len, d_k)

    # Apply mask to scores
    # <pad>
    if mask is not None:
        attention_scores = attention_scores.masked_fill(mask == 0, value=-1e9)

    # Softmax along the last dimension
    attention_weights = F.softmax(attention_scores, dim=-1)

    if dropout is not None:
        attention_weights = dropout(attention_weights)

    output = torch.matmul(attention_weights, v)
    return output

Multi-Head Attention layer

在这里插入图片描述

# Multi-Head Attention layer
class MultiHeadAttention(nn.Module):
    def __init__(self, n_heads, d_model, dropout=0.1):
        super().__init__()

        self.n_heads = n_heads
        self.d_model = d_model
        self.d_k = self.d_v = d_model // n_heads

        # self attention linear layers
        #Linear layers for q, k, v vectors generation in different heads
        self.q_linear_layers = []
        self.k_linear_layers = []
        self.v_linear_layers = []
        for i in range(n_heads):
            self.q_linear_layers.append(nn.Linear(d_model, self.d_k))
            self.k_linear_layers.append(nn.Linear(d_model, self.d_k))
            self.v_linear_layers.append(nn.Linear(d_model, self.d_v))

        self.dropout = nn.Dropout(dropout)
        self.out = nn.Linear(n_heads*self.d_v, d_model)

    def forward(self, q, k, v, mask=None):
        multi_head_attention_outputs = []
        for q_linear, k_linear, v_linear in zip(self.q_linear_layers,
                                                self.k_linear_layers,
                                                self.v_linear_layers):
            new_q = q_linear(q) # size: (batch_size, seq_len, d_k)
            new_k = q_linear(k) # size: (batch_size, seq_len, d_k)
            new_v = q_linear(v) # size: (batch_size, seq_len, d_v)

            # Scaled Dot-Product attention
            head_v = scaled_dot_product_attention(new_q, new_k, new_v, mask, self.dropout) # (batch_size, seq_len,
            multi_head_attention_outputs.append(head_v)

        # Concat
        # import pdb; pdb.set_trace()
        concat = torch.cat(multi_head_attention_outputs, -1) # (batch_size, seq_len, n_heads*d_v)

        # Linear layer to recover to original shap
        output = self.out(concat) # (batch_size, seq_len, d_model)

        return output

翻译实例

这里github链接
以英语到法语的翻译实例展示Transformer这篇文章所述网络模型结构和其用法。Python安装版本信息如下:

Python 3.7.16
torch==2.0.1
torchdata==0.6.1
torchtext==0.15.2
spacy==3.6.0
numpy==1.25.2
pandas
times
portalocker==2.7.0

数据处理

分词和词映射为张量化的数字

使用torchtext提供的工具比较方便创建一个便于处理迭代的语音翻译模型的数据集,首先是从原始文本分词、构建词汇表以及标记为数字化张量。尽管torchtext提供了基本的英语分词支持,但是这里的翻译中除了英语还有法语,因而使用了分词python库Spacy。

首先是创建环境,接下来是下载英语和法语的分词器,因为这是一个很小的例子,因而使用新闻的spacy语言处理模型即可:

#python3 -m spacy download en_core_web_sm
#python3 -m spacy download fr_core_news_sm

如下图所示:
请添加图片描述

接下来是将数据进行分词,然后将词映射为张量化的数字

#Data processing
import spacy
from torchtext.data.utils import get_tokenizer
from collections import Counter
import io
from torchtext.vocab import vocab

src_data_path = 'data/english.txt'
trg_data_path = 'data/french.txt'

en_tokenizer = get_tokenizer('spacy', language='en_core_web_sm')
fr_tokenizer = get_tokenizer('spacy', language='fr_core_news_sm')


def build_vocab(filepath, tokenizer):
    counter = Counter()
    with io.open(filepath, encoding="utf8") as f:
        for string_ in f:
            counter.update(tokenizer(string_))
    return vocab(counter, specials=['<unk>', '<pad>', '<bos>', '<eos>'])

en_vocab = build_vocab(src_data_path, en_tokenizer)
fr_vocab = build_vocab(trg_data_path, fr_tokenizer)

def data_process(src_path, trg_path):
    raw_en_iter = iter(io.open(src_path, encoding="utf8"))
    raw_fr_iter = iter(io.open(trg_path, encoding="utf8"))
    data = []
    for (raw_en, raw_fr) in zip (raw_en_iter, raw_fr_iter):
        en_tensor_ = torch.tensor([en_vocab[token] for token in en_tokenizer(raw_en)], dtype=torch.long)
        fr_tensor_ = torch.tensor([fr_vocab[token] for token in fr_tokenizer(raw_fr)], dtype= torch.long)
        data.append((en_tensor_, fr_tensor_))
    return data

train_data = data_process(src_data_path, trg_data_path)

DataLoader

DataLoader是torch.utils.data提供的方法,其将数据集和采样器组合在一起,为给定的数据集提供可迭代的对象。DataLoader 支持单进程或多进程加载、自定义加载顺序和可选的自动批处理(合并)和内存固定的映射式和可迭代式数据集。
collate_fn(可选),它将样本列表合并以形成张量的小批量。在使用映射样式数据集的批量加载时使用。

#Train transformer
d_model= 512
n_heads = 8
N = 6
src_vocab_size = len(en_vocab.vocab)
trg_vocab_size = len(fr_vocab.vocab)

BATH_SIZE = 32
PAD_IDX = en_vocab['<pad>']
BOS_IDX = en_vocab['<bos>']
EOS_IDX = en_vocab['<eos>']

from torch.nn.utils.rnn import pad_sequence
from torch.utils.data import DataLoader

def generate_batch(data_batch):
    en_batch, fr_batch = [], []
    for (en_item, fr_item) in data_batch:
        en_batch.append(torch.cat([torch.tensor([BOS_IDX]), en_item, torch.tensor([EOS_IDX])], dim=0))
        fr_batch.append(torch.cat([torch.tensor([BOS_IDX]), fr_item, torch.tensor([EOS_IDX])], dim=0))
    en_batch = pad_sequence(en_batch, padding_value=PAD_IDX)
    fr_batch = pad_sequence(fr_batch, padding_value=PAD_IDX)
    return en_batch, fr_batch

train_iter = DataLoader(train_data, batch_size=BATH_SIZE, shuffle=True, collate_fn=generate_batch)

训练的输出如下:
请添加图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

synchronized 底层是如何实现的 ?

目录 1. synchronized 底层是如何实现的 2. 监视器的执行流程 1. synchronized 底层是如何实现的 synchronized 底层是通过 JVM 内置的 Monitor 监视器实现的。 以下代码&#xff0c;查看它运行时的字节码文件&#xff1a; public class SynchronizedMonitorDemo {public …

pycharm中配置conda

安装好pycharm和conda后&#xff0c;打开pycharm&#xff1a;

贴吧照片和酷狗音乐简单爬取

爬取的基本步骤 很简单&#xff0c;主要是两大步 向url发起请求 这里注意找准对应资源的url&#xff0c;如果对应资源不让程序代码访问&#xff0c;这里可以伪装成浏览器发起请求。 解析上一步返回的源代码&#xff0c;从中提取想要的资源 这里解析看具体情况&#xff0c;一…

Azure添加网络接口

添加网络接口的意义 在 Azure 上&#xff0c;为虚拟机添加网络接口的意义包括以下几个方面&#xff1a; 扩展网络带宽&#xff1a;通过添加多个网络接口&#xff0c;可以增加虚拟机的网络带宽&#xff0c;提高网络传输速度和数据吞吐量。实现网络隔离&#xff1a;每个网络接口…

科东软件受邀参加第五届国产嵌入式操作系统技术与产业发展论坛

8月12日&#xff0c;第五届国产嵌入式操作系统技术与产业发展论坛暨嵌入式系统联谊会主题讨论会&#xff08;总第29次&#xff09;在杭州成功举行。这次论坛的主题是“面向异构多核智能芯片的混合关键系统研究与应用”&#xff0c;上午是“嵌入式异构多核智能芯片产业发展”的主…

Python中的字符串与字符编码

Hello&#xff0c;这里是Token_w的博客&#xff0c;欢迎您的到来 今天文章讲解的是Python中的字符串与字符编码&#xff0c;其中有基础的理论知识讲解&#xff0c;也有实战中的应用讲解&#xff0c;希望对你有所帮助 整理不易&#xff0c;如对你有所帮助&#xff0c;希望能得到…

uniapp-----封装接口

系列文章目录 uniapp-----封装接口 uniapp-----分包 文章目录 系列文章目录 uniapp-----封装接口 uniapp-----分包 文章目录 前言 一、技术 二、封装步骤 1.准备 ​编辑 2.代码填充 request.js&#xff1a; api.js&#xff1a; min.js 页面使用 总结 前言 uni…

Java反序列化漏洞笔记

前言 作为Java安全方面的盲对Java反序列化各种链方面了解的并不多&#xff0c;但是这些链条又极为重要&#xff0c;有助于更好的理解各种漏洞的产出和原理&#xff0c;因此以下笔记开始从底慢慢学起。 为什么会产生安全问题&#xff1f; 服务器反序列化数据时&#xff0c;客…

【数学建模】--灰色关联分析

系统分析: 一般的抽象系统&#xff0c;如社会系统&#xff0c;经济系统&#xff0c;农业系统&#xff0c;生态系统&#xff0c;教育系统等都包含有许多种因素&#xff0c;多种因素共同作用的结果决定了该系统的发展态势。人们常常希望知道在众多的因素中&#xff0c;哪些是主要…

数据结构--最短路径 Dijkstra算法

数据结构–最短路径 Dijkstra算法 Dijkstra算法 计算 b e g i n 点到各个点的最短路 \color{red}计算\ begin\ 点到各个点的最短路 计算 begin 点到各个点的最短路 如果是无向图&#xff0c;可以先把无向图转化成有向图 我们需要2个数组 final[] &#xff08;标记各顶点是否已…

webpack 创建VUE项目

1、安装 node.js 下载地址&#xff1a;https://nodejs.org/en/ 下载完成以后点击安装&#xff0c;全部下一步即可 安装完成&#xff0c;输入命令验证 node -vnpm -v2.搭建VUE环境 输入命令&#xff0c;全局安装 npm install vue-cli -g安装完成后输入命令 查看 vue --ver…

宋浩线性代数笔记(五)矩阵的对角化

本章的知识点难度和重要程度都是线代中当之无愧的T0级&#xff0c;对于各种杂碎的知识点&#xff0c;多做题复盘才能良好的掌握&#xff0c;良好掌握的关键点在于&#xff1a;所谓的性质A与性质B&#xff0c;是谁推导得谁~

深入理解epoll

文章目录 概述1. epoll_create - 创建一个epoll实例2. epoll_ctl - 控制epoll实例的事件结构体介绍events取值&#xff1a;data&#xff1a; 联合体&#xff08;共用体&#xff09;&#xff1a; 3. epoll_wait - 等待事件发生伪代码总结 概述 在网络编程中&#xff0c;高效地处…

人大进仓数据库ksql命令基础

测试环境信息: 系统为银河麒麟V10 数据库为Kingbase ES V8 数据库安装目录为/opt/Kingbase/ES/V8 ksql命令位于/opt/Kingbase/ES/V8/Server/bin下 使用--help获取帮助 续上图 1.查看数据库列表 ./ksql -U system -l 2.查看数据库版本 ./ksql -V 3.连接指定的数据库tes…

浅谈SMT行业MES系统生产管理的特点

一、SMT生产车间在电子制造中起重要作用的部分&#xff0c;主要具备以下生产特点&#xff1a; 1.高密度和高速度&#xff1a; SMT生产车间中的电子元器件一般来说较为精小&#xff0c;且被紧密地排列在PCB上。这就要求SMT生产车间的机械设备具备高精度和高速度&#xff0c;确保…

新增守护进程管理、支持添加MySQL远程数据库,支持PHP版本切换,1Panel开源面板v1.5.0发布

2023年8月14日&#xff0c;现代化、开源的Linux服务器运维管理面板1Panel正式发布v1.5.0版本。 在这个版本中&#xff0c;1Panel新增了守护进程管理功能&#xff1b;支持添加MySQL远程数据库&#xff1b;支持添加FTP/S和WebDAV的SFTP服务&#xff1b;支持PHP版本切换。此外&am…

Android app专项测试之耗电量测试

前言 耗电量指标 待机时间成关注目标 提升用户体验 通过不同的测试场景&#xff0c;找出app高耗电的场景并解决 01、需要的环境准备 1、python2.7(必须是2.7&#xff0c;3.X版本是不支持的) 2、golang语言的开发环境 3、Android SDK 此三个的环境搭建这里就不详细说了&am…

LeetCode 36题:有效的数独

题目 请你判断一个 9 x 9 的数独是否有效。只需要 根据以下规则 &#xff0c;验证已经填入的数字是否有效即可。 数字 1-9 在每一行只能出现一次。数字 1-9 在每一列只能出现一次。数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。&#xff08;请参考示例图&#xff…

8.14 ARM

1.练习一 .text 文本段 .global _start 声明一个_start函数入口 _start: _start标签&#xff0c;相当于C语言中函数mov r0,#0x2mov r1,#0x3cmp r0,r1beq stopsubhi r0,r0,r1subcc r1,r1,r0stop: stop标签&#xff0c;相当于C语言中函数b stop 跳转到stop标签下的第一条…

如何给 Keycloak 用户加上“部门”、“电话”等自定义属性

Keycloak 是一款开源的用户认证和授权软件。在默认安装情况下&#xff0c;它只给新创建的用户提供了 email 属性&#xff0c;但是在许多应用场景中&#xff0c;客户都会要求给新创建的用户增加诸如“部门”、“电话”等自定义属性。 本文会介绍如何给 keycloak 中新创建的用户…