【Transformer从零开始代码实现】(一)输入部件:embedding+positionalEncoding

news2024/11/15 21:46:10

Transformer总架构图

在这里插入图片描述

输入相关组件

输入部分:

  • 源文本嵌入层+位置编码器
  • 目标文本嵌入层+位置编码器
    在这里插入图片描述

(1)Embedding

首先,需要对输入的内容进行向量化。

1)先导示例

nn.Embedding示例:

# 10代表嵌入的数不可超过10,3代表embedding的维度
embedding = nn.Embedding(10, 3)     
input = torch.LongTensor([[1, 2, 4, 5], [4, 3, 2, 9]])
# # 将[1,2,4,5]和[4, 3, 2, 9]里的四个数,各自映射为四个三维向量
embedding(input)

tensor([[[-0.0251, -1.6902,  0.7172],
                 [-0.6431,  0.0748,  0.6969],
                 [ 1.4970,  1.3448, -0.9685],
                 [-0.3677, -2.7265, -0.1685]],

                [[ 1.4970,  1.3448, -0.9685],
                 [ 0.4362, -0.4004,  0.9400],
                 [-0.6431,  0.0748,  0.6969],
                 [ 0.9124, -2.3616,  1.1151]]])

padding_idx作用是让被赋值的那个数在embedding时候各个维度里的值都变为0

# padding_idx等于几,哪个位置就全为0
embedding = nn.Embedding(10, 3, padding_idx=4)      
input = torch.LongTensor([[4, 2, 4, 5]])
embedding(input)
tensor([[[ 0.0000,  0.0000,  0.0000],
         [ 0.1535, -2.0309,  0.9315],
         [ 0.0000,  0.0000,  0.0000],
         [-0.1655,  0.9897,  0.0635]]])

2)Embedding实现

embedding输入

import torch
import torch.nn as nn
import math

# 使用torch里的模块,继承nn.Embedding
class Embedding(nn.Module):
    # vocab: 词表长度; d_model词嵌入的维度
    def __init__(self, vocab, d_model):
        super(Embedding, self).__init__()
        # lut:look-up table
        self.lut = nn.Embedding(vocab, d_model)
        self.d_model = d_model
	
	# 可理解为该层的前向传播逻辑,所有层中都会有此函数。
	# 当传给该类的实例化对象时,自动调用该类函数。
    def forward(self, x):
     	# 乘上一个根号d_model来放大lut的大小,让其和positional encoding值域范围近似
        return self.lut(x) * math.sqrt(self.d_model)

注意:
embedding里采用的L2Norm。因为各维度总和会为1,维度越大的向量归一化后单个值就会越小,因为后面还要加上positional encoding,为了保证和这个值大小差不多,需要把embedding扩大一下,就需要乘上这个权重。

示例

d_model=512
vocab=1000
x=torch.LongTensor([[100,2,421,508], [491,988,1,221]])      # 2 * 4
emb = Embedding(vocab, d_model)			# 1000 * 512
test_emb = emb(x)						# 2 * 4 * 512
print("embeding:", test_emb, "\nshape:", test_emb.shape)


embeding: tensor([[[-22.1368,   5.8489,  -4.3048,  ...,  10.1736,  -8.3588, -15.9710],
         [ 56.4712,  18.5326, -33.1404,  ...,  13.8900,  15.6980,   0.9166],
         [ -7.5599,   5.7412,   7.9309,  ..., -37.1804,   2.6838, -14.5033],
         [-31.7061,   8.6661, -12.8770,  ..., -29.6877, -37.0234, -60.4735]],
        [[  9.1579,  10.7355,  22.1405,  ...,  26.5621, -16.2131, -11.0188],
         [-26.9357, -39.1481,  27.4990,  ...,  -4.1475, -13.0475,  29.1349],
         [-52.3744, -11.6883,  12.3517,  ...,  12.9772,  -1.0818,  18.6217],
         [ 26.1174, -23.1478, -14.2219,  ...,  29.2699,  12.5628,  16.7982]]],
       grad_fn=<MulBackward0>) 
shape: torch.Size([2, 4, 512])

(2)Positional Encoding

因为采用的attention注意力机制只能得到词与词之间的相关程度,但不包含词与词之间的位置信息,这就会导致只要输入的文字都相同,不论以什么顺序输入这些文字,最后都会得到同样的结果。因此,需要想办法加入词序之间的信息,就有了Positional Encoding.。

1)先导示例

nn.Dropout示例:

m = nn.Dropout(p=0.1)
input1 = torch.randn(5,6)
output = m(input1)
print(output)

tensor([[ 1.7470,  0.2384, -0.7003, -0.9112, -0.2812,  0.2167],
        [ 0.0000,  1.0510, -0.0268,  0.1691, -0.8758,  0.7029],
        [-0.5565, -0.7345,  1.5827, -0.9349,  0.2578, -0.0000],
        [-0.0000,  1.9349,  0.8612, -0.0000, -0.6373,  0.1144],
        [-1.5473, -0.6834,  1.0102, -0.6242, -0.1893, -1.5241]])

unsqueeze示例:
h.unsqueeze(k):让h在第k个维度上扩展

x = torch.tensor([1, 2, 3, 4])
y = torch.unsqueeze(x, 0)
print(y)
z = torch.unsqueeze(x, 1)
print(z)

tensor([[1, 2, 3, 4]])		# 注意这个是两个括号
tensor([[1],
        [2],
        [3],
        [4]])

2)Positional Encoding实现

对于Positional Encoding实际上分为四步:
(1)定义位置编码矩阵形状
(2)填充位置编码矩阵:
1)定义句子位置编码向量(长度为max_len的一个列向量);
2)定义一个形状变换向量(长度为d_model的一个行向量),用于与句子位置编码向量相乘后,向d_model方向扩充,将位置编码向量变为位置编码矩阵。
(3)扩展位置编码矩阵,将其变为含批数的三维矩阵
(4)与输入的x进行相加,截取和x相同形状部分,剩余部分丢弃(因每次构造的位置编码矩阵是按照最大长度构造的一个模板,因此只保留和x相同的部分)。

在这里插入图片描述

在这里插入图片描述
位置编码的计算函数
在这里插入图片描述

class PositionalEncoding(nn.Module):
    # 输入d_model:词嵌入维度、drouput:置0比率、max_len:每个句子最大长度
    def __init__(self, d_model, dropout, max_len=5000):
        super(PositionalEncoding, self).__init__()
        # 实例化nn中预定义的Dropout层
        self.dropout = nn.Dropout(p=dropout)

        # 初始化一个位置编码矩阵 [max_len, d_model]     # 当输入维度为(60 * 512)时
        pe = torch.zeros(max_len, d_model)          # 60 * 512

        # 初始化一个绝对位置矩阵 [max_len, 1],得到一个有时序信息的向量,用于后续加入到位置编码矩阵中
        position = torch.arange(0, max_len).unsqueeze(1)    # 列向量 (60, 1)

        # 实现 e^(-(2i/d_model)*ln(10000)) = e^(ln(10000)^(-(2i/d_model))) = 1/(10000)^(2i/d_model)
        # torch.arange(0, d_model, 2)将会产生从[0, d_model]中每间隔两个数取出来一个数的集合
        # 得到一个embedding对应维度的编码方式
        div_term = torch.exp(torch.arange(0, d_model, 2) * -(math.log(10000.0) / d_model))      # (1 * 256)

        # 得到偶数列数据
        pe[:, 0::2] = torch.sin(position * div_term)        # (60 * 1) * (1 * 256) ==> 60 * 256
        # 得到奇数列数据
        pe[:, 1::2] = torch.cos(position * div_term)        # (60 * 1) * (1 * 256) ==> 60 * 256
        # 拓展维度更新形状,变为输入一批数据时呈现的结构
        pe = pe.unsqueeze(0)                        # [1, 60, 512]
        '''
        向模块添加持久缓冲区。
        这通常用于注册不应被是为模型参数的缓冲区。例如,pe不是一个参数,而是一个持久状态的一部分。
        缓冲区可以使用给定的名称作为属性访问。
        
        说明:
        应该就是在内存中定义一个常量,同时,模型保存和加载的时候可以写入和读出,让其不会在梯度更新时候被再重新计算
        '''
        self.register_buffer('pe', pe)

    def forward(self, x):
        # pe[:, :x.size(1)]保证行数不变,列数与x相等。其中,行数为批数,列数为一批中的embedding个数。
        x = x + self.pe[:, :x.size(1)].detach()        # 使用detach可以避免其梯度被重复计算

        # 最后经过Dropout层后输出
        return self.dropout(x)

注意:变换思路
主要的过程,就是实现对位置编码矩阵pe的填充,通过绝对位置向量positond_model维度扩充向量div_term相乘构造出和pe形状相同的矩阵,再使用三角函数作用,来保证连续且在同一比较界限范围内。

注意:position和div_term相乘思路
(1)想让position的位置信息加入到位置编码矩阵中,想法就是让[max_len, 1]变为[max_len, d_model]再加到pe中。要想做成这种变换就需要每一个[1, d_model]的向量来实现形状变化,[max_len, 1] * [1, d_model] == > [max_len, d_model]
(2)除了形状变换之外,还需要将自然数的绝对位置编码缩小,有助于后续梯度下降时候可以更快收敛,因此就有了 d i v = 1 ( 10000 ) 2 i d _ m o d e l div=\frac{1}{(10000)^\frac{2i}{d\_model}} div=(10000)d_model2i1,2i 通过torch.arange(0, d_model, 2)来实现, d _ m o d e l d\_model d_model 越大, d i v div div 越大,从而达到调控embedding数据作用。

注意:pe[:, :x.size(1)]截取原因
pe是通过max_len得到的最大长度时候的位置编码矩阵,而实际中的x并不一定会达到max_len,因此需要统一维度进行以x为标准进行切割处理。

示例

# 定义PositionalEncoding的参数
d_model = 512
dropout = 0.1
max_len = 60

# 输入之前得到的embedding
pe = PositionalEncoding(d_model, dropout, max_len)
pe_res = pe(test_emb)
print("pe_res:", pe_res, "\nshape:", pe_res.shape)


'''
embeding: tensor([[[ -4.1141, -22.1936,  -2.1720,  ...,  -1.8160,  -5.8038,   3.9030],
         [-26.2369,  35.3158, -26.7828,  ...,  18.1910, -15.5895,  -0.5780],
         [ 27.6906,   7.8964,  -3.2235,  ..., -15.7689,  -6.2119,  -5.0473],
         [-10.3022,  16.1367,  21.4265,  ...,  28.1743, -11.6089, -24.7860]],
        [[ 10.2011,  25.3486,  -8.0284,  ..., -13.7405,  -4.7748,  30.3610],
         [ 20.5093,   7.5563,  -3.9737,  ..., -32.6686, -14.8885,  -5.9809],
         [ 61.4368,   7.0695,  15.5462,  ...,  27.6469,   4.0113, -17.1434],
         [  2.5992,   9.6200, -22.2852,  ...,  29.4035,  52.7968,   3.9688]]],
       grad_fn=<MulBackward0>) 
shape: torch.Size([2, 4, 512])
'''
# 变化前后对比
pe_res: tensor([[[ -4.5712, -23.5485,  -2.4134,  ...,  -0.0000,  -6.4486,   5.4478],
         [-28.2171,  39.8401, -28.8455,  ...,  21.3233, -17.3215,   0.4689],
         [ 31.7777,   8.3114,  -2.5412,  ..., -16.4098,  -6.9019,  -4.4970],
         [-11.2900,  16.8297,  24.0796,  ...,  32.4159, -12.8984,  -0.0000]],
        [[ 11.3345,  29.2762,  -8.9204,  ..., -14.1561,  -5.3053,  34.8455],
         [ 23.7231,   8.9963,  -0.0000,  ..., -35.1873, -16.5426,  -5.5343],
         [ 69.2735,   7.3926,  18.3140,  ...,  31.8299,   4.4573, -17.9371],
         [  3.0448,   9.5888, -24.4890,  ...,  33.7817,  58.6635,   5.5209]]],
       grad_fn=<MulBackward0>) 
shape: torch.Size([2, 4, 512])

可以看出加入位置信息后,对于每列都会有一定的数值上的变化影响。

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

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

相关文章

即插即用篇 | YOLOv8 引入反向残差注意力模块 iRMB | 《ICCV 2023 最新论文》

论文地址:https://arxiv.org/abs/2301.01146 代码地址:https://github.com/zhangzjn/EMO 本论文着重于开发现代、高效、轻量级的模型,用于进行密集预测,同时在参数、FLOPs和性能之间进行权衡。倒置残差块(IRB)作为轻量级CNN的基础设施,但在基于注意力的研究中尚未找到对…

目标检测中的评价指标

目标检测中的评价指标 将检测目标分为正样本和负样本。 真阳性&#xff08;true positives , TP&#xff09; : 正样本被正确识别为正样本。 假阳性&#xff08;false positives, FP&#xff09;: 负样本被错误识别为正样本。 假阴性&#xff08;false negatives, FN&#…

技术分享 | app自动化测试(Android)-- 参数化用例

参数化是自动化测试的一种常用技巧&#xff0c;可以将测试代码中的某些输入使用参数来代替。以百度搜索功能为例&#xff0c;每次测试搜索场景&#xff0c;都需要测试不同的搜索内容&#xff0c;在这个过程里面&#xff0c;除了数据在变化&#xff0c;测试步骤都是重复的&#…

Java附件和base64相互转换

1 文件转base64 声明&#xff1a;我用的是Hutool的Base64下的api package cn.hutool.core.codec; 首先找一张图片 很简单&#xff0c;直接使用Base64的encode方法就可以拿到文件的base64码&#xff1a; File file new File("D:\\Tools\\Images\\北极熊.jpg");String…

软件测试面试题汇总,(测试技术+人力资源+进阶规划)含2023面试题和答案总结

什么是兼容性测试&#xff1f;单元测试的策略有哪些&#xff1f;当开发人员说不是BUG时&#xff0c;你如何应付&#xff1f;等&#xff0c;尾部有最新BAT的Python高级自动化工程师面试题目和答案福利&#xff0c;想要的就快来领走吧&#xff01;&#xff08;领取方式见文末&…

【机器学习3】有监督学习经典分类算法

1 支持向量机 在现实世界的机器学习领域&#xff0c; SVM涵盖了各个方面的知识&#xff0c; 也是面试题目中常见的基础模型。 SVM的分类结果仅依赖于支持向量&#xff0c;对于任意线性可分的两组点&#xff0c;它 们在SVM分类的超平面上的投影都是线性不可分的。 2逻辑回归 …

安装 MinGW

实际上是将 GCC&#xff08;C语言编译器&#xff09; 移植到了 Windows 平台下。 1、网上下载 下载安装器 mingw-get-setup.exe&#xff0c;路径https://osdn.net/projects/mingw/ 2、打开点击install 3、选择路径continue 4、文件加载完成之后选择continue 5、勾选这两个 6…

数据结构与算法之美学习笔记:17 | 跳表:为什么Redis一定要用跳表来实现有序集合?

目录 前言如何理解“跳表”&#xff1f;用跳表查询到底有多快&#xff1f;跳表是不是很浪费内存&#xff1f;高效的动态插入和删除跳表索引动态更新解答开篇内容小结 前言 本节课程思维导图&#xff1a; 二分查找底层依赖的是数组随机访问的特性&#xff0c;所以只能用数组来实…

润色论文Prompt

你好&#xff0c;我现在开始写论文了&#xff0c;我希望你可以扮演帮我润色论文的角色我写的论文是关于xxxxx领域的xxxxx&#xff0c;我希望你能帮我检查段落中语句的逻辑、语法和拼写等问题我希望你能帮我检查以下段落中语句的逻辑、语法和拼写等问题同时提供润色版本以符合学…

freeswich学习

写在前面 因为所在部分主要负责公司客服业务&#xff0c;需要了解freeswich相关内容&#xff0c;所以这里将学习内容记录下。 1&#xff1a;安装freesswich freeswich是一个实现了软交换协议的开源软件&#xff0c;可以对对接运营上的通话线路&#xff0c;实现拨打电话。 安…

编程未来规划笔记

编程思考 Python 自动化办公、深度学习、自然语言处理&#xff08;调用各种库&#xff09; Html Css 写网页 学习不要怕忘 为什么学的快、忘得快 Google、写代码、放文档 高度提炼 学什么&#xff1b;存在的意义是什么 更好的拓展性&#xff1b;可维护性 实践 原理 顶层设计…

建材行业微信小程序制作全攻略

随着移动互联网的发展&#xff0c;微信小程序成为各行各业推广和服务的新方式。对于建材行业来说&#xff0c;制作一个微信小程序商城能够提供更方便快捷的购买途径&#xff0c;提升用户体验。下面将为大家介绍建材行业微信小程序制作的全攻略。 第一步&#xff1a;注册登录账号…

人工智能一种现代的方法 第四章 非经典搜索 上(局部搜索)

文章目录 人工智能一种现代的方法 第四章 非经典搜索 上前言4.1 局部搜索4.1.1 爬山法4.1.2 爬山法变形4.1.3模拟退火搜索4.1.4 局部束搜索4.1.5 遗传算法 4.2 连续空间的局部搜索4.2.1 梯度下降4.2.2 约束优化 小结 人工智能一种现代的方法 第四章 非经典搜索 上 前言 在第三…

ASUS华硕灵耀X2 Duo UX481FA(FL,FZ)_UX4000F工厂模式原装出厂Windows10系统

下载链接&#xff1a;https://pan.baidu.com/s/1sRHKBOyc3zu1v0qw4dSASA?pwd7nb0 提取码&#xff1a;7nb0 带有ASUS RECOVERY恢复功能、自带所有驱动、出厂主题壁纸、系统属性专属LOGO标志、Office办公软件、MyASUS华硕电脑管家等预装程序所需要工具&#xff1a;16G或以上…

2023 年最佳 Android 数据恢复软件工具

Android 数据恢复软件将使您能够从 Android 智能手机中检索所有已删除的文件。您需要此类软件的原因是由于不同情况下会丢失数据。例如&#xff0c;病毒攻击会导致数据损坏和文件丢失。 Android 数据恢复软件工具清单 以下是十个最佳 Android 数据恢复软件工具&#xff0c;用于…

有哪些你直呼好用的科研效率神器?

今天来分享几款科研免费小工具&#xff0c;帮你读懂外刊、追踪文献、搞定翻译、解除限制……甚至轻松制作PPT。一身好装备&#xff0c;提高科研效率。 一、 浏览器插件 1.1 easyScholar 一款助力科研的浏览器扩展 一款助力科研的浏览器扩展 - easyScholar | 显示期刊等级\SC…

从0到1的Springcloud Alibaba项目,一篇入门!!!

1、新建项目 我们用maven管理项目 第一步&#xff1a;选择maven 第二步&#xff1a;项目命名&#xff0c;项目路径 第三步&#xff1a;进入项目&#xff0c;把src文件夹删掉&#xff08;不删也没事&#xff0c;主要是用不到这个文件夹&#xff09; 2、引入项目依赖 在父项目…

matlab 读写ENVI标准数据

本博客主要讲解如何读、生成ENVI标准格式的数据。主要分为四部分&#xff1a;读取ENVI头文件、读取ENVI数据、写入ENVI头文件、生成ENVI标准数据&#xff0c;最后附加讲解了本人写的生成hdr文本文件代码。此外&#xff0c;文中还具体介绍写代码的一些思路。 一、读取ENVI头文件…

【数据结构】单链表之--无头单向非循环链表

前言&#xff1a;前面我们学习了动态顺序表并且模拟了它的实现&#xff0c;今天我们来进一步学习&#xff0c;来学习单链表&#xff01;一起加油各位&#xff0c;后面的路只会越来越难走需要我们一步一个脚印&#xff01; &#x1f496; 博主CSDN主页:卫卫卫的个人主页 &#x…

kafka可视化工具

Offset Explorer kafka可视化工具