Transformer模型:WordEmbedding实现

前言

最近在学Transformer,学了理论的部分之后就开始学代码的实现,这里是跟着b站的up主的视频记的笔记,视频链接:19、Transformer模型Encoder原理精讲及其PyTorch逐行实现_哔哩哔哩_bilibili


正文

首先导入所需要的包:

python 复制代码
import torch
import numpy as np
import torch.nn as nn
import torch.nn.functional as F

关于Word Embedding,这里以序列建模为例,考虑source sentence、target sentence,构建序列,序列的字符以其在词表中的索引的形式表示。

首先使用定义batch_size的大小,并且使用torch.randint()函数随机生成序列长度,这里的src是生成原本的序列,tgt是生成目标的序列。

以机器翻译实现英文翻译为中文来说,src就是英文句子,tgt就是中文句子,这也就是规定了要翻译的英文句子的长度和翻译出来的句子长度。(举个例子而已,不用纠结为什么翻译要限制句子的长度)

python 复制代码
batch_size = 2

src_len=torch.randint(2,5,(batch_size,))
tgt_len=torch.randint(2,5,(batch_size,))

将生成的src_len、tgt_len输出:

复制代码
tensor([2, 3])    生成的原序列第一个句子长度为2,第二个句子长度为3
tensor([4, 4])    生成的目标序列第一个句子长度为4,第二个句子长度为4

因为随机生成的,所以每次运行都会有新的结果,也就是生成的src和tgt两个序列,其子句的长度每次都是随机的,这里改成生成固定长度的序列:

python 复制代码
src_len = torch.Tensor([11, 9]).to(torch.int32)
tgt_len = torch.Tensor([10, 11]).to(torch.int32)

将生成的src_len、tgt_len输出,此时就固定好了序列长度了:

复制代码
tensor([11,  9], dtype=torch.int32)
tensor([10, 11], dtype=torch.int32)

接着是要实现单词索引构成的句子,首先定义单词表的大小和序列的最大长度。

python 复制代码
# 单词表大小
max_num_src_words = 10
max_num_tgt_words = 10

# 序列的最大长度
max_src_seg_len = 12
max_tgt_seg_len = 12

以生成原序列为例,使用torch.randint()生成第一个句子和第二个句子,然后放到列表中:

python 复制代码
src_seq = [torch.randint(1, max_num_src_words, (L,)) for L in src_len]
复制代码
[tensor([5, 3, 7, 5, 6, 3, 4, 3]), tensor([1, 6, 3, 1, 1, 7, 4])]

可以发现生成的两个序列长度不一样(因为我们自己定义的时候就是不一样的),在这里需要使用F.pad()函数进行padding保证序列长度一致:

python 复制代码
src_seq = [F.pad(torch.randint(1, max_num_src_words, (L,)), (0, max_src_seg_len-L)) for L in src_len]
复制代码
[tensor([8, 5, 2, 4, 6, 8, 1, 4, 0, 0, 0, 0]), tensor([5, 5, 5, 3, 7, 9, 3, 0, 0, 0, 0, 0])]

此时已经填充为同样的长度了,但是不同的句子各为一个张量,需要使用torch.cat()函数把不同句子的tensor转化为二维的tensor,在此之前需要先把每个张量变成二维的,使用torch.unsqueeze()函数:

python 复制代码
src_seq = torch.cat([torch.unsqueeze(F.pad(torch.randint(1, max_num_src_words, (L,)),(0, max_src_seg_len-L)), 0) for L in src_len])
复制代码
tensor([[9, 7, 7, 4, 7, 3, 9, 4, 7, 8, 8, 0],
        [1, 1, 5, 9, 5, 6, 2, 7, 4, 0, 0, 0]])
tensor([[3, 3, 2, 8, 3, 4, 1, 2, 9, 4, 0, 0],
        [1, 6, 3, 8, 5, 1, 5, 5, 1, 5, 3, 0]])

这里把tgt的也补充了,得到的就是src和tgt的内容各自在一个二维张量里(batch_size,max_seg_len),batch_size也就是句子数,max_seg_len也就是句子的单词数(分为src的长度跟tgt两种)。

补充:可以看到上面三次运行出来的结果都不一样,因为三次运行的时候,每次都是随机生成,所以结果肯定不一样,第三次为什么有两个二维的tensor是因为第三次把tgt的部分也补上去了,所以就有两个二维的tensor。

接下来就是构造embedding了,这里nn.Embedding()传入了两个参数,第一个是embedding的长度,也就是单词个数+1,+1的原因是因为有个0是作为填充的,第二个参数就是embedding的维度,也就是一个单词会被映射为多少维度的向量。

然后调用forward,得到我们的src和tgt的embedding

python 复制代码
src_embedding_table = nn.Embedding(max_num_src_words+1, model_dim)
tgt_embedding_table = nn.Embedding(max_num_tgt_words+1, model_dim)
src_embedding = src_embedding_table(src_seq)   
tgt_embedding = tgt_embedding_table(tgt_seq)
python 复制代码
print(src_embedding_table.weight)    # 每一行代表一个embedding向量,第0行让给pad,从第1行到第行分配给各个单词,单词的索引是多少就取对应的行位置的向量
print(src_embedding)    # 根据src_seq,从src_embedding_table获取得到的embedding vector,三维张量:batch_size、max_seq_len、model_dim
print(tgt_embedding)

此时src_embedding_table.weight的输出内容如下,第一行为填充(0)的向量:

tensor([[-0.3412, 1.5198, -1.7252, 0.6905, -0.3832, -0.8586, -2.0788, 0.3269],

[-0.5613, 0.3953, 1.6818, -2.0385, 1.1072, 0.2145, -0.9349, -0.7091],

[ 1.5881, -0.2389, -0.0347, 0.3808, 0.5261, 0.7253, 0.8557, -1.0020],

[-0.2725, 1.3238, -0.4087, 1.0758, 0.5321, -0.3466, -0.9051, -0.8938],

[-1.5393, 0.4966, -1.4887, 0.2795, -1.6751, -0.8635, -0.4689, -0.0827],

[ 0.6798, 0.1168, -0.5410, 0.5363, -0.0503, 0.4518, -0.3134, -0.6160],

[-1.1223, 0.3817, -0.6903, 0.0479, -0.6894, 0.7666, 0.9695, -1.0962],

[ 0.9608, 0.0764, 0.0914, 1.1949, -1.3853, 1.1089, -0.9282, -0.9793] ,

[-0.9118, -1.4221, -2.4675, -0.1321, 0.7458, -0.8015, 0.5114, -0.5023],

[-1.7504, 0.0824, 2.2088, -0.4486, 0.7324, 1.8790, 1.7644, 1.2731] ,

[-0.3791, 1.9915, -1.0117, 0.8238, -2.1784, -1.2824, -0.4275, 0.3202]],

requires_grad=True)

src_embedding的输出结果如下所示,往前看src_seq的第一个句子前三个为9 7 7,往前看第9+1行与第7+1行的向量,就是现在输出的前3个向量:

复制代码
tensor([[[-1.7504,  0.0824,  2.2088, -0.4486,  0.7324,  1.8790,  1.7644, 1.2731],
         [ 0.9608,  0.0764,  0.0914,  1.1949, -1.3853,  1.1089, -0.9282, -0.9793],
         [ 0.9608,  0.0764,  0.0914,  1.1949, -1.3853,  1.1089, -0.9282, -0.9793],
         [-1.5393,  0.4966, -1.4887,  0.2795, -1.6751, -0.8635, -0.4689, -0.0827],
         [ 0.9608,  0.0764,  0.0914,  1.1949, -1.3853,  1.1089, -0.9282, -0.9793],
         [-0.2725,  1.3238, -0.4087,  1.0758,  0.5321, -0.3466, -0.9051, -0.8938],
         [-1.7504,  0.0824,  2.2088, -0.4486,  0.7324,  1.8790,  1.7644, 1.2731],
         [-1.5393,  0.4966, -1.4887,  0.2795, -1.6751, -0.8635, -0.4689, -0.0827],
         [ 0.9608,  0.0764,  0.0914,  1.1949, -1.3853,  1.1089, -0.9282, -0.9793],
         [-0.9118, -1.4221, -2.4675, -0.1321,  0.7458, -0.8015,  0.5114, -0.5023],
         [-0.9118, -1.4221, -2.4675, -0.1321,  0.7458, -0.8015,  0.5114, -0.5023],
         [-0.3412,  1.5198, -1.7252,  0.6905, -0.3832, -0.8586, -2.0788, 0.3269]],

        [[-0.5613,  0.3953,  1.6818, -2.0385,  1.1072,  0.2145, -0.9349, -0.7091],
         [-0.5613,  0.3953,  1.6818, -2.0385,  1.1072,  0.2145, -0.9349, -0.7091],
         [ 0.6798,  0.1168, -0.5410,  0.5363, -0.0503,  0.4518, -0.3134, -0.6160],
         [-1.7504,  0.0824,  2.2088, -0.4486,  0.7324,  1.8790,  1.7644, 1.2731],
         [ 0.6798,  0.1168, -0.5410,  0.5363, -0.0503,  0.4518, -0.3134, -0.6160],
         [-1.1223,  0.3817, -0.6903,  0.0479, -0.6894,  0.7666,  0.9695, -1.0962],
         [ 1.5881, -0.2389, -0.0347,  0.3808,  0.5261,  0.7253,  0.8557, -1.0020],
         [ 0.9608,  0.0764,  0.0914,  1.1949, -1.3853,  1.1089, -0.9282, -0.9793],
         [-1.5393,  0.4966, -1.4887,  0.2795, -1.6751, -0.8635, -0.4689, -0.0827],
         [-0.3412,  1.5198, -1.7252,  0.6905, -0.3832, -0.8586, -2.0788, 0.3269],
         [-0.3412,  1.5198, -1.7252,  0.6905, -0.3832, -0.8586, -2.0788, 0.3269],
         [-0.3412,  1.5198, -1.7252,  0.6905, -0.3832, -0.8586, -2.0788, 0.3269]]], grad_fn=<EmbeddingBackward>)

同理tgt_embedding的输出结果如下所示:

复制代码
tensor([[[-1.3681, -0.1619, -0.3676,  0.4312, -1.3842, -0.6180,  0.3685, 1.6281],
         [-1.3681, -0.1619, -0.3676,  0.4312, -1.3842, -0.6180,  0.3685, 1.6281],
         [-2.6519, -0.8566,  1.2268,  2.6479, -0.2011, -0.1394, -0.2449, 1.0309],
         [-0.8919,  0.5235, -3.1833,  0.9388, -0.6213, -0.5146,  0.7913, 0.5126],
         [-1.3681, -0.1619, -0.3676,  0.4312, -1.3842, -0.6180,  0.3685, 1.6281],
         [-0.4984,  0.2948, -0.2804, -1.1943, -0.4495,  0.3793, -0.1562, -1.0122],
         [ 0.8976,  0.5226,  0.0286,  0.1434, -0.2600, -0.7661,  0.1225, -0.7869],
         [-2.6519, -0.8566,  1.2268,  2.6479, -0.2011, -0.1394, -0.2449, 1.0309],
         [ 2.2026,  1.8504, -0.6285, -0.0996, -0.0994, -0.0828,  0.6004, -0.3173],
         [-0.4984,  0.2948, -0.2804, -1.1943, -0.4495,  0.3793, -0.1562, -1.0122],
         [ 0.3637,  0.4256,  0.7674,  1.4321, -0.1164, -0.6032, -0.8182, -0.6119],
         [ 0.3637,  0.4256,  0.7674,  1.4321, -0.1164, -0.6032, -0.8182, -0.6119]],

        [[ 0.8976,  0.5226,  0.0286,  0.1434, -0.2600, -0.7661,  0.1225, -0.7869],
         [-1.0356,  0.8212,  1.0538,  0.4510,  0.2734,  0.3254,  0.4503, 0.1694],
         [-1.3681, -0.1619, -0.3676,  0.4312, -1.3842, -0.6180,  0.3685, 1.6281],
         [-0.8919,  0.5235, -3.1833,  0.9388, -0.6213, -0.5146,  0.7913, 0.5126],
         [-0.4783, -1.5936,  0.5033,  0.3483, -1.3354,  1.4553, -1.1344, -1.9280],
         [ 0.8976,  0.5226,  0.0286,  0.1434, -0.2600, -0.7661,  0.1225, -0.7869],
         [-0.4783, -1.5936,  0.5033,  0.3483, -1.3354,  1.4553, -1.1344, -1.9280],
         [-0.4783, -1.5936,  0.5033,  0.3483, -1.3354,  1.4553, -1.1344, -1.9280],
         [ 0.8976,  0.5226,  0.0286,  0.1434, -0.2600, -0.7661,  0.1225, -0.7869],
         [-0.4783, -1.5936,  0.5033,  0.3483, -1.3354,  1.4553, -1.1344, -1.9280],
         [-1.3681, -0.1619, -0.3676,  0.4312, -1.3842, -0.6180,  0.3685, 1.6281],
         [ 0.3637,  0.4256,  0.7674,  1.4321, -0.1164, -0.6032, -0.8182, -0.6119]]], grad_fn=<EmbeddingBackward>)

实际想要把文本句子嵌入到Embedding中,需要先根据自己的词典,将文本信息转化为每个词在词典中的位置,然后第0个位置依旧要让给Padding,得到索引然后构建Batch再去构造Embedding,以索引为输入得到每个样本的Embedding。

代码

python 复制代码
import torch
import numpy as np
import torch.nn as nn
import torch.nn.functional as F

# 句子数
batch_size = 2

# 单词表大小
max_num_src_words = 10
max_num_tgt_words = 10

# 序列的最大长度
max_src_seg_len = 12
max_tgt_seg_len = 12

# 模型的维度
model_dim = 8

# 生成固定长度的序列
src_len = torch.Tensor([11, 9]).to(torch.int32)
tgt_len = torch.Tensor([10, 11]).to(torch.int32)
print(src_len)
print(tgt_len)

#单词索引构成的句子
src_seq = torch.cat([torch.unsqueeze(F.pad(torch.randint(1, max_num_src_words, (L,)),(0, max_src_seg_len-L)), 0) for L in src_len])
tgt_seq = torch.cat([torch.unsqueeze(F.pad(torch.randint(1, max_num_tgt_words, (L,)),(0, max_tgt_seg_len-L)), 0) for L in tgt_len])
print(src_seq)
print(tgt_seq)

# 构造embedding
src_embedding_table = nn.Embedding(max_num_src_words+1, model_dim)
tgt_embedding_table = nn.Embedding(max_num_tgt_words+1, model_dim)
src_embedding = src_embedding_table(src_seq)  
tgt_embedding = tgt_embedding_table(tgt_seq)
print(src_embedding_table.weight)    
print(src_embedding)    
print(tgt_embedding)

参考

torch.randint --- PyTorch 2.3 documentation

torch.nn.functional.pad --- PyTorch 2.3 文档

F.pad 的理解_domain:luyixian.cn-CSDN博客

嵌入 --- PyTorch 2.3 文档

相关推荐
m0_6038887113 分钟前
什么是上采样什么是下采样
人工智能·深度学习·计算机视觉
TSINGSEE14 分钟前
人员抽烟AI检测算法在智慧安防领域的创新应用,助力监控智能化
人工智能·算法·视频编解码·安防视频监控·视频监控管理平台
一枚游戏干饭人15 分钟前
【运营攻略】怎样进行游戏产品的定位
人工智能·游戏·语音识别
Python极客之家32 分钟前
基于机器学习的乳腺癌肿瘤智能分析预测系统
人工智能·python·机器学习·毕业设计·xgboost·可视化分析
嵌入式杂谈40 分钟前
深入理解AI大模型:参数、Token、上下文窗口、上下文长度和温度
人工智能
范范08251 小时前
自然语言处理入门:从基础概念到实战项目
人工智能·自然语言处理
_feivirus_1 小时前
神经网络_使用TensorFlow预测气温
人工智能·神经网络·算法·tensorflow·预测气温
deflag1 小时前
第T1周:Tensorflow实现mnist手写数字识别
人工智能·python·机器学习·分类·tensorflow
aimmon1 小时前
深度学习之微积分预备知识点
人工智能·深度学习·线性代数·微积分
不是很强 但是很秃1 小时前
秃姐学AI系列之:实战Kaggle比赛:狗的品种识别(ImageNet Dogs)
图像处理·人工智能·pytorch·深度学习·神经网络·计算机视觉·分类