python手动搭建transformer,并实现自回归推理

以下是添加了详细注释的代码和参数介绍:


Transformer 实现及自回归推理

本文展示了如何手动实现一个简化版的Transformer模型,并用自回归方式实现一个seq2seq任务,例如机器翻译。

导入必要的库

import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
import numpy as np

定义位置编码

Transformer 使用位置编码来捕捉序列中的位置信息。

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() * (-np.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).transpose(0, 1)self.register_buffer('pe', pe)def forward(self, x):return x + self.pe[:x.size(0), :]

参数介绍:

  • d_model: 词嵌入和位置编码的维度。
  • max_len: 序列的最大长度。

定义自注意力机制

class MultiHeadAttention(nn.Module):def __init__(self, d_model, num_heads):super(MultiHeadAttention, self).__init__()self.num_heads = num_headsself.d_model = d_modelassert d_model % num_heads == 0self.depth = d_model // num_headsself.wq = nn.Linear(d_model, d_model)self.wk = nn.Linear(d_model, d_model)self.wv = nn.Linear(d_model, d_model)self.dense = nn.Linear(d_model, d_model)def split_heads(self, x, batch_size):x = x.view(batch_size, -1, self.num_heads, self.depth)return x.permute(0, 2, 1, 3)def forward(self, v, k, q, mask):batch_size = q.size(0)q = self.split_heads(self.wq(q), batch_size)k = self.split_heads(self.wk(k), batch_size)v = self.split_heads(self.wv(v), batch_size)matmul_qk = torch.matmul(q, k.transpose(-1, -2))dk = torch.tensor(k.size(-1)).float()scaled_attention_logits = matmul_qk / torch.sqrt(dk)if mask is not None:scaled_attention_logits += (mask * -1e9)attention_weights = F.softmax(scaled_attention_logits, dim=-1)output = torch.matmul(attention_weights, v)output = output.permute(0, 2, 1, 3).contiguous()output = output.view(batch_size, -1, self.d_model)return self.dense(output)

参数介绍:

  • d_model: 词嵌入和注意力机制的维度。
  • num_heads: 注意力头的数量。

定义前馈神经网络

class FeedForward(nn.Module):def __init__(self, d_model, dff):super(FeedForward, self).__init__()self.linear1 = nn.Linear(d_model, dff)self.linear2 = nn.Linear(dff, d_model)def forward(self, x):return self.linear2(F.relu(self.linear1(x)))

参数介绍:

  • d_model: 词嵌入的维度。
  • dff: 前馈神经网络的隐藏层维度。

定义编码器层

class EncoderLayer(nn.Module):def __init__(self, d_model, num_heads, dff, dropout=0.1):super(EncoderLayer, self).__init__()self.mha = MultiHeadAttention(d_model, num_heads)self.ffn = FeedForward(d_model, dff)self.layernorm1 = nn.LayerNorm(d_model)self.layernorm2 = nn.LayerNorm(d_model)self.dropout1 = nn.Dropout(dropout)self.dropout2 = nn.Dropout(dropout)def forward(self, x, mask):attn_output = self.mha(x, x, x, mask)out1 = self.layernorm1(x + self.dropout1(attn_output))ffn_output = self.ffn(out1)out2 = self.layernorm2(out1 + self.dropout2(ffn_output))return out2

参数介绍:

  • d_model: 词嵌入和注意力机制的维度。
  • num_heads: 注意力头的数量。
  • dff: 前馈神经网络的隐藏层维度。
  • dropout: Dropout 概率。

定义编码器

class Encoder(nn.Module):def __init__(self, num_layers, d_model, num_heads, dff, input_vocab_size, max_len, dropout=0.1):super(Encoder, self).__init__()self.d_model = d_modelself.num_layers = num_layersself.embedding = nn.Embedding(input_vocab_size, d_model)self.pos_encoding = PositionalEncoding(d_model, max_len)self.enc_layers = nn.ModuleList([EncoderLayer(d_model, num_heads, dff, dropout) for _ in range(num_layers)])self.dropout = nn.Dropout(dropout)def forward(self, x, mask):seq_len = x.size(1)x = self.embedding(x)x *= torch.sqrt(torch.tensor(self.d_model, dtype=torch.float32))x = self.pos_encoding(x.permute(1, 0, 2))x = x.permute(1, 0, 2)x = self.dropout(x)for i in range(self.num_layers):x = self.enc_layers[i](x, mask)return x

参数介绍:

  • num_layers: 编码器层的数量。
  • d_model: 词嵌入和注意力机制的维度。
  • num_heads: 注意力头的数量。
  • dff: 前馈神经网络的隐藏层维度。
  • input_vocab_size: 输入词汇表大小。
  • max_len: 序列的最大长度。
  • dropout: Dropout 概率。

定义解码器层

class DecoderLayer(nn.Module):def __init__(self, d_model, num_heads, dff, dropout=0.1):super(DecoderLayer, self).__init__()self.mha1 = MultiHeadAttention(d_model, num_heads)self.mha2 = MultiHeadAttention(d_model, num_heads)self.ffn = FeedForward(d_model, dff)self.layernorm1 = nn.LayerNorm(d_model)self.layernorm2 = nn.LayerNorm(d_model)self.layernorm3 = nn.LayerNorm(d_model)self.dropout1 = nn.Dropout(dropout)self.dropout2 = nn.Dropout(dropout)self.dropout3 = nn.Dropout(dropout)def forward(self, x, enc_output, look_ahead_mask, padding_mask):attn1 = self.mha1(x, x, x, look_ahead_mask)attn1 = self.dropout1(attn1)out1 = self.layernorm1(attn1 + x)attn2 = self.mha2(enc_output, enc_output, out1, padding_mask)attn2 = self.dropout2(attn2)out2 = self.layernorm2(attn2 + out1)ffn_output = self.ffn(out2)ffn_output = self.dropout3(ffn_output)out3 = self.layernorm3(ffn_output + out2)return out3

参数介绍:

  • d_model: 词嵌入和注意力机制的维度。
  • num_heads: 注意力头的数量。
  • dff: 前馈神经网络的隐藏层维度。
  • dropout: Dropout 概率。

定义解码器

class Decoder(nn.Module):def __init__(self, num_layers, d_model, num_heads, dff, target_vocab_size, max_len, dropout=0.1):super(Decoder, self).__init__()self.d_model = d_modelself.num_layers = num_layersself.embedding = nn.Embedding(target_vocab_size, d_model)self.pos_encoding = PositionalEncoding(d_model, max_len)self.dec_layers = nn.ModuleList([DecoderLayer(d_model, num_heads, dff, dropout) for _ in range(num_layers)])self.dropout = nn.Dropout(dropout)def forward(self, x, enc_output, look_ahead_mask, padding_mask):seq_len = x.size(1)attention_weights = {}x = self.embedding(x)x *= torch.sqrt(torch.tensor(self.d_model, dtype=torch.float32))x = self.pos_encoding(x.permute(1, 0, 2))x = x.permute(1, 0, 2)x = self.dropout(x)for i in range(self.num_layers):x = self.dec_layers[i](x, enc_output, look_ahead_mask, padding_mask)return x

参数介绍:

  • num_layers: 解码器层的数量。
  • d_model: 词嵌入和注意力机制的维度。
  • num_heads: 注意力头的数量。
  • dff: 前馈神经网络的隐藏层维度。
  • target_vocab_size: 目标词汇表大小。
  • max_len: 序列的最大长度。
  • dropout: Dropout 概率。

定义Transformer模型

class Transformer(nn.Module):def __init__(self, num_layers, d_model, num_heads, dff, input_vocab_size, target_vocab_size, pe_input, pe_target, dropout=0.1):super(Transformer, self).__init__()self.encoder = Encoder(num_layers, d_model, num_heads, dff, input_vocab_size, pe_input, dropout)self.decoder = Decoder(num_layers, d_model, num_heads, dff, target_vocab_size, pe_target, dropout)self.final_layer = nn.Linear(d_model, target_vocab_size)def forward(self, inp, tar, enc_padding_mask, look_ahead_mask, dec_padding_mask):enc_output = self.encoder(inp, enc_padding_mask)dec_output = self.decoder(tar, enc_output, look_ahead_mask, dec_padding_mask)final_output = self.final_layer(dec_output)return final_output

参数介绍:

  • num_layers: 编码器和解码器层的数量。
  • d_model: 词嵌入和注意力机制的维度。
  • num_heads: 注意力头的数量。
  • dff: 前馈神经网络的隐藏层维度。
  • input_vocab_size: 输入词汇表大小。
  • target_vocab_size: 目标词汇表大小。
  • pe_input: 输入序列的最大长度。
  • pe_target: 目标序列的最大长度。
  • dropout: Dropout 概率。

创建掩码

def create_padding_mask(seq):seq = torch.eq(seq, 0)return seq[:, None, None, :]def create_look_ahead_mask(size):mask = torch.triu(torch.ones((size, size)), 1)return mask

自回归推理

实现一个简化的自回归推理过程:

def generate_text(model, input_sequence, start_token, max_length, target_vocab_size):generated = [start_token]model.eval()enc_padding_mask = create_padding_mask(input_sequence)with torch.no_grad():enc_output = model.encoder(input_sequence, enc_padding_mask)for _ in range(max_length):dec_input = torch.tensor(generated).unsqueeze(0)look_ahead_mask = create_look_ahead_mask(dec_input.size(1))dec_padding_mask = create_padding_mask(dec_input)with torch.no_grad():output = model.decoder(dec_input, enc_output, look_ahead_mask, dec_padding_mask)output = model.final_layer(output)next_token = torch.argmax(output[:, -1, :], dim=-1).item()generated.append(next_token)if next_token == eos_token:breakreturn generated

参数介绍:

  • model: 训练好的Transformer模型。
  • input_sequence: 输入的序列张量。
  • start_token: 生成序列的开始标记。
  • max_length: 生成序列的最大长度。
  • target_vocab_size: 目标词汇表大小。

使用示例

创建一个简单的模型并进行文本生成:

input_vocab_size = 1000  # 输入词汇表大小
target_vocab_size = 1000  # 目标词汇表大小
max_len = 50  # 序列最大长度
num_layers = 2  # 编码器和解码器层的数量
d_model = 512  # 词嵌入和注意力机制的维度
num_heads = 8  # 注意力头的数量
dff = 2048  # 前馈神经网络的隐藏层维度# 创建Transformer模型
transformer = Transformer(num_layers, d_model, num_heads, dff, input_vocab_size, target_vocab_size, max_len, max_len)# 输入序列,假设输入序列为[1, 2, 3, 4, 0, 0, 0]
input_sequence = torch.tensor([[1, 2, 3, 4, 0, 0, 0]])# 假设开始标记为1,结束标记为2
start_token = 1
eos_token = 2# 生成序列
generated_sequence = generate_text(transformer, input_sequence, start_token, max_length=20, target_vocab_size=target_vocab_size)
print("Generated sequence:", generated_sequence)

以上代码展示了一个简化的Transformer模型的实现,包括位置编码、自注意力机制、前馈神经网络、编码器层、解码器层、编码器和解码器整体的实现,以及一个基本的自回归推理过程。你可以根据需要进行调整和扩展。


关于mask的解释

以下关于掩码函数 create_padding_maskcreate_look_ahead_mask 的详细介绍以及示例。

create_padding_mask

该函数用于生成填充掩码,以忽略序列中的填充值(通常是0)。在Transformer模型中,填充掩码用于屏蔽掉填充值在计算注意力时的影响。

代码实现
def create_padding_mask(seq):seq = torch.eq(seq, 0)  # 查找填充值(假设填充值为0),返回一个布尔张量return seq[:, None, None, :]  # 扩展维度以适配注意力机制中的广播
示例

假设我们有一个输入序列,其中0是填充值:

seq = torch.tensor([[7, 6, 0, 0, 0], [1, 2, 3, 0, 0]])padding_mask = create_padding_mask(seq)
print(padding_mask)
输出
tensor([[[[False, False,  True,  True,  True]]],[[[False, False, False,  True,  True]]]])

在输出中,True 表示填充值的位置,这些位置将在计算注意力时被忽略。

create_look_ahead_mask

该函数用于生成前瞻掩码,以确保解码器中的每个位置只能看到该位置之前的序列,不能看到未来的信息。在自回归生成中,前瞻掩码用于防止解码器在生成下一个标记时看到未来的标记。

代码实现
def create_look_ahead_mask(size):mask = torch.triu(torch.ones((size, size)), 1)  # 生成上三角矩阵,主对角线以上的元素为1return mask  # 返回前瞻掩码
示例

假设我们有一个序列长度为5:

size = 5
look_ahead_mask = create_look_ahead_mask(size)
print(look_ahead_mask)
输出
tensor([[0., 1., 1., 1., 1.],[0., 0., 1., 1., 1.],[0., 0., 0., 1., 1.],[0., 0., 0., 0., 1.],[0., 0., 0., 0., 0.]])

在输出中,1 表示被掩盖的位置,这些位置在计算注意力时将被屏蔽。

综合示例

结合以上两种掩码,假设我们有以下输入序列:

seq = torch.tensor([[7, 6, 0, 0, 0], [1, 2, 3, 0, 0]])
size = seq.size(1)padding_mask = create_padding_mask(seq)
look_ahead_mask = create_look_ahead_mask(size)print("Padding Mask:\n", padding_mask)
print("Look Ahead Mask:\n", look_ahead_mask)
输出
Padding Mask:tensor([[[[False, False,  True,  True,  True]]],[[[False, False, False,  True,  True]]]])
Look Ahead Mask:tensor([[0., 1., 1., 1., 1.],[0., 0., 1., 1., 1.],[0., 0., 0., 1., 1.],[0., 0., 0., 0., 1.],[0., 0., 0., 0., 0.]])

在实际使用中,编码器使用 padding_mask 来屏蔽填充值的影响,解码器则同时使用 look_ahead_maskpadding_mask 来屏蔽未来标记和填充值的影响。

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

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

相关文章

【区分vue2和vue3下的element UI DatePicker 日期选择器组件,分别详细介绍属性,事件,方法如何使用,并举例】

在 Vue 2 中,我们通常使用 Element UI 的 DatePicker 日期选择器组件。然而,如前所述,Element UI 并没有官方支持 Vue 3 的版本。但有一个叫做 Element Plus 的库,它是 Element UI 的 Vue 3 版本,并提供了类似的 DateP…

TypeScript学习(二):数据类型

/** *一.typescript类型学习 */ //1.1 number类型 let a:number; a123; //这么写会报错,a"hello,world"; console.log(a); //1.2 string类型 let b:string; b"hello,world"; console.log(b); //1.3 boolean类型 let c:booleantrue; c…

华为OD刷题C卷 - 每日刷题 13(图像物体的边界,英文输入法)

1、(图像物体的边界): 这段代码是解决“图像物体的边界”的问题。它提供了一个Java类Main,其中包含main方法和getResult方法,以及一个内部UnionFindSet类,用于计算像素1代表的物体的边界个数。 main方法首…

C#操作MySQL从入门到精通(11)——对查询数据使用正则表达式过滤

前言 对于之前提到的使用匹配、比较、通配符等过滤方式能解决大部分的项目问题,但是有时候也会遇到一些比较复杂的过滤需求,这时候就需要正则表达式来实现了,正则表达式使用regexp这个关键字来实现。 本次测试的数据库表的内容如下: 1、基本字符匹配(包含某些字符) 匹…

嵌入式之存储基本知识

系列文章目录 嵌入式之存储基本知识 嵌入式之存储基本知识 系列文章目录一、RAM与ROM二、DRAM和SRAM三、SDRAM(DRAM的一种)四、DDR 一、RAM与ROM RAM(随机存取存储器)和ROM(只读存储器)是两种不同类型的计…

揭秘VVIC API:开启高效数据交互的密钥,你的项目就差这一步

VVIC API接口概述 VVIC API提供了对VVIC服务的数据访问和操作功能。通过此API,开发者可以集成VVIC服务到他们的应用程序中,实现数据同步、用户认证、资源管理等功能。 点击获取key和secret API端点示例 用户认证 方法:POSTURL:/…

【JS】JavaScript编程语言-(Object)对象属性标志与对象属性描述符(2024-06-05)

我们知道,对象可以存储属性。 到目前为止,属性对我们来说只是一个简单的“键值”对。但对象属性实际上是更灵活且更强大的东西:其他配置选项。 1、属性标志 对象属性(properties),除 value 外&#xff0c…

Nvidia Jetson/Orin +FPGA+AI大算力边缘计算盒子:无人机自主飞行软件平台

案例简介 北京泛化智能科技有限公司(gi)所主导开发的 Generalized Autonomy Aviation System (GAAS) 是为无人机以及城市空中交通 (UAM, Urban Air Mobility) 所设计的开源无人机自主飞行框架。通过 SLAM、路径规划和 Global Optimization Graph 等功能…

【Linux】(三)—— 文件管理和软件安装

文件管理 Linux的文件管理是系统管理中的核心部分,它涉及到如何组织、访问、修改和保护文件及目录结构。 目录 文件管理基本概念常用命令查看和切换目录创建文件和目录删除文件和目录文件拷贝移动和重命名文件文件查看cat文件查看more查找文件查找文本 数据流和管道…

redsystems教程的基本使用之重置密码(忘记密码解决方法)

前言: 相信很多人都有疑惑,要是我不记得密码怎么办?如果你登录了,点击更改密码后,还是要你填写登录密码才能修改。为了解决这问题,博主通过了钻研成功搞出来了!!!&#…

DS:数与二叉树的相关概念

欢迎来到Harper.Lee的学习世界!博主主页传送门:Harper.Lee的博客主页想要一起进步的uu可以来后台找我哦! 一、树的概念及其结构 1.1 树的概念亲缘关系 树是一种非线性的数据结构,它是由n(n>0)个有限节点…

Marvelous Designer中一些棉质布料预设

Marvelous Designer中一些棉质布料预设的解释: Cotton_14_Wale_Corduroy:14条细鲸鱼纹的灯芯绒,适合制作温暖且有质感的服装。Cotton_40s_Chambray:40支精梳针织的府绸布,通常用于制作休闲衬衫。Cotton_40s_Poplin&am…

Nginx目录文件

Nginx目录文件 在 Nginx 的安装目录下,你可能会看到许多文件夹和文件。以下是对各个文件夹和文件的简要解释: conf.d: 这个文件夹通常用于存放额外的配置文件。Nginx 在启动时,会读取该文件夹下的所有配置文件,并将其内容合并到…

Face Forgery Detection by 3D Decomposition

文章目录 Face Forgery Detection by 3D Decomposition研究背景研究目标创新点方法提出问题研究过程技术贡献实验结果未来工作Face Forgery Detection by 3D Decomposition 会议:CVPR2021 作者: 研究背景 面部伪造引发关注传统面部伪造检测主要关注原始RGB图像

邮箱地址验证软件

邮箱地址验证软件是一种用于检测邮箱地址是否真实存在、有效和可送达的工具。这些软件通常服务于邮件营销、客户数据清洗或研究领域,以帮助用户提高邮件发送的成功率并减少 bounce rate(退回率)。易邮地址验证软件就是这么一款软件。 易邮件…

学Python,看一篇就够

学Python,看一篇就够 python基础注释变量标识符命名规则使用变量认识bugDebug工具打断点 数据类型输出转义字符输入输入语法输入的特点 转换数据类型pycharm交互运算符的分类赋值运算符复合赋值运算符比较运算符逻辑运算符拓展 条件语句单分支语法多分支语法拓展 if…

XFF注入【墨者靶场】

目录 XFF介绍 靶场练习 最近在复习XFF注入,这里使用墨者靶场来简单的练习一下该漏洞的利用方法 XFF介绍 X-Forwarded-For:简称XFF头,代表了HTTP的请求端真实的IP。 它被认为是客户端通过HTTP代理或者负载均衡器连接到web服务端获取源ip地…

植物大战僵尸杂交版2.0.88最新版安装包

游戏简介 游戏中独特的杂交植物更是为游戏增添了不少亮点。这些杂交植物不仅外观独特,而且拥有更强大的能力,能够帮助玩家更好地应对游戏中的挑战。玩家可以通过一定的条件和方式,解锁并培养这些杂交植物,从而不断提升自己的战斗…

http和websocket区别

HTTP和WebSocket在多个方面存在显著的区别: 1. 通信方式:HTTP协议是一种基于请求-响应模式的通信方式,客户端发送请求,服务器响应请求。这种通信方式相对简单直观,但它是单向的,即每次通信都是由客户端发起…

市政环境卫生乙级资质申请:费用详解与预算

市政环境卫生乙级资质申请的费用详解与预算如下: 一、基本成本 注册资本:根据规定,申请市政环境卫生乙级资质的企业需具备独立法人资格,且注册资本不少于100万元人民币。 二、行政与申报费用 行政性收费:向住房和城…