关于这个“这是B站目前讲的最好的【Transformer实战】教程!“视频的目前可以运行的源代码GPU版本
课程链接如下:
2.1认识Transformer架构-part1_哔哩哔哩_bilibili
因为网上可以找到源代码,但是呢,代码似乎有点小错误,我自己改正后,放到了GPU上运行,
代码如下:
# 来自https://www.bilibili.com/video/BV188411H71g?p=3&vd_source=3083729582baecf3ad2c3c52876b23aa
# 我已经使用GPU修改了代码,加了几处.cuda()就行了import copy
import math
import timeimport numpy as np
import torch
import torch.nn as nn
from torch.autograd import Variablefrom pyitcast.transformer_utils import Batch
from pyitcast.transformer_utils import get_std_opt
from pyitcast.transformer_utils import LabelSmoothing
from pyitcast.transformer_utils import SimpleLossCompute
from pyitcast.transformer_utils import run_epoch
from pyitcast.transformer_utils import greedy_decode# 文本嵌入层
class Embeddings(nn.Module):def __init__(self, d_model, vocab):super(Embeddings, self).__init__()self.lut = nn.Embedding(vocab, d_model)self.d_model = d_modeldef forward(self, x):return self.lut(x) * math.sqrt(self.d_model)# 定义位置编码器,即也是一个层
class PositionalEncoding(nn.Module):def __init__(self, d_model, dropout, max_len=5000):super(PositionalEncoding, self).__init__()self.dropout = nn.Dropout(p=dropout)pe = torch.zeros(max_len, d_model)position = torch.arange(0, max_len).unsqueeze(1)div_term = torch.exp(torch.arange(0, d_model, 2) * (-(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(1)]return self.dropout(x)# 构建掩码张量
def subsequent_maxk(size):attn_shape = (1, size, size)subsequent_maxk = np.triu(np.ones(attn_shape), k=1).astype('uint8')return torch.from_numpy(1 - subsequent_maxk)# # 2.3.2注意力机制# 为下面函数重写了注意力机制,否则代码会报错
# 注意力机制代码实现
def attention(query, key, value, mask=None, dropout=None):d_k = query.size(-1)scores = torch.matmul(query, key.transpose(-2, -1)) / math.sqrt(d_k)if mask is not None:# mask = torch.zeros(1, 1, 1, 1).cuda()# print("mask.shape:", mask.shape)scores = scores.masked_fill(mask == 0, -1e9)p_attn = F.softmax(scores, dim=-1)if dropout is not None:p_attn = dropout(p_attn)return torch.matmul(p_attn, value), p_attn# # 2.3.3多头注意力机制# 实现克隆函数
def clones(module, N):return nn.ModuleList([copy.deepcopy(module) for _ in range(N)])# 实现多头注意力机制
class MultiHeadAttention(nn.Module):def __init__(self, head, embedding_dim, dropout=0.1):super(MultiHeadAttention, self).__init__()assert embedding_dim % head == 0self.d_k = embedding_dim // headself.head = headself.embedding_dim = embedding_dimself.linears = clones(nn.Linear(embedding_dim, embedding_dim), 4)self.attn = Noneself.dropout = nn.Dropout(p=dropout)def forward(self, query, key, value, mask=None):if mask is not None:mask = mask.unsqueeze(0)batch_size = query.size(0)# 三个张量分别是三个输入,分别用三个线性层进行处理并重塑维度query, key, value = \[model(x).view(batch_size, -1, self.head, self.d_k).transpose(1, 2)for model, x in zip(self.linears, (query, key, value))]x, self.attn = attention(query, key, value, mask=mask,dropout=self.dropout)x = (x.transpose(1, 2).contiguous().view(batch_size, -1, self.head * self.d_k))# 拷贝的四个层还有一个就是这个对输入进行线性变换得到输出return self.linears[-1](x)# # 2.3.4前馈全连接层# 构建前馈全连接网络类
class PositionWiseFeedForward(nn.Module):def __init__(self, d_model, d_ff, dropout=0.1):super(PositionWiseFeedForward, self).__init__()self.w1 = nn.Linear(d_model, d_ff)self.w2 = nn.Linear(d_ff, d_model)self.dropout = nn.Dropout(p=dropout)def forward(self, x):return self.w2(self.dropout(F.dropout(F.relu(self.w1(x)))))# # 2.3.5规范化层class LayerNorm(nn.Module):def __init__(self, features, eps=1e-6):super(LayerNorm, self).__init__()self.a2 = nn.Parameter(torch.ones(features))self.b2 = nn.Parameter(torch.zeros(features))self.eps = epsdef forward(self, x):mean = x.mean(-1, keepdim=True)std = x.std(-1, keepdim=True)return self.a2 * (x - mean) / (std + self.eps) + self.b2# # 2.3.6子层连接结构# 构建子层连接结构
class SublayerConnection(nn.Module):def __init__(self, size, dropout=0.1):super(SublayerConnection, self).__init__()self.norm = LayerNorm(size)self.dropout = nn.Dropout(p=dropout)self.size = sizedef forward(self, x, sublayer):return x + self.dropout(sublayer(self.norm(x)))# # 2.3.7编码器层# 编码器层
class EncoderLayer(nn.Module):def __init__(self, size, self_attn, feed_forward, dropout):super(EncoderLayer, self).__init__()self.self_attn = self_attnself.feed_forward = feed_forwardself.size = sizeself.sublayer = clones(SublayerConnection(size, dropout), 2)def forward(self, x, mask):x = self.sublayer[0](x, lambda x: self.self_attn(x, x, x, mask))return self.sublayer[1](x, self.feed_forward)# # 2.3.8编码器# 构建编码器类
class Encoder(nn.Module):def __init__(self, layer, N):super(Encoder, self).__init__()self.layers = clones(layer, N)self.norm = LayerNorm(layer.size)def forward(self, x, mask):for layer in self.layers:x = layer(x, mask)return self.norm(x)# # 2.4解码器# # 2.4.1解码器层# 构建解码器层类
class DecoderLayer(nn.Module):def __init__(self, size, self_attn, src_attn, feed_forward, dropout):super(DecoderLayer, self).__init__()self.size = sizeself.self_attn = self_attnself.src_attn = src_attnself.feed_forward = feed_forwardself.dropout = dropoutself.sublayer = clones(SublayerConnection(size, dropout), 3)def forward(self, x, memory, source_mask, target_mask):m = memoryx = self.sublayer[0](x, lambda x: self.self_attn(x, x, x, target_mask))x = self.sublayer[1](x, lambda x: self.src_attn(x, m, m, source_mask))return self.sublayer[2](x, self.feed_forward)# # 2.4.2 解码器# 构建解码器类
class Decoder(nn.Module):def __init__(self, layer, N):super(Decoder, self).__init__()self.layers = clones(layer, N)self.norm = LayerNorm(layer.size)def forward(self, x, memory, source_mask, target_mask):for layer in self.layers:x = layer(x, memory, source_mask, target_mask)return self.norm(x)# # 2.5输出部分实现m = nn.Linear(20, 30)
input = torch.randn(128, 20)
output = m(input)
print(output.shape)# 构建Generator类
import torch.nn.functional as Fclass Generator(nn.Module):def __init__(self, d_model, vocal_size):super(Generator, self).__init__()self.project = nn.Linear(d_model, vocal_size)def forward(self, x):return F.log_softmax(self.project(x), dim=1)# # 2.6 Transformer模型构建# 实现编码解码结构
class EncoderDecoder(nn.Module):def __init__(self, encoder, decoder, source_embed, target_embed, generator):super(EncoderDecoder, self).__init__()self.encoder = encoderself.decoder = decoderself.src_embed = source_embedself.tgt_embed = target_embedself.generator = generatordef forward(self, source, target, source_mask, target_mask):return self.decode(self.encode(source, source_mask), source_mask,target, target_mask)def encode(self, source, source_mask):return self.encoder(self.src_embed(source), source_mask)def decode(self, memory, source_mask, target, target_mask):return self.decoder(self.tgt_embed(target), memory, source_mask,target_mask)# Transformer模型构建过程的代码分析
def make_model(source_vocab, target_vocab, N=6, d_model=512, d_ff=2048, head=8,dropout=0.1):c = copy.deepcopyattn = MultiHeadAttention(head, d_model)ff = PositionWiseFeedForward(d_model, d_ff, dropout)position = PositionalEncoding(d_model, dropout)model = EncoderDecoder(Encoder(EncoderLayer(d_model, c(attn), c(ff), dropout), N),Decoder(DecoderLayer(d_model, c(attn), c(attn), c(ff), dropout), N),nn.Sequential(Embeddings(d_model, source_vocab), c(position)),nn.Sequential(Embeddings(d_model, target_vocab), c(position)),Generator(d_model, target_vocab))for p in model.parameters():if p.dim() > 1:nn.init.xavier_uniform_(p)return model# # 2.7模型基本测试运行# 构建数据集生成器
def data_generator(V, batch_size, num_batch):for i in range(num_batch):data = torch.from_numpy(np.random.randint(1, V, size=(batch_size, 10)))data[:, 0] = 1# source = Variable(data, requires_grad=False).long()# target = Variable(data, requires_grad=False).long()source = Variable(data, requires_grad=False).long().cuda()target = Variable(data, requires_grad=False).long().cuda()yield Batch(source, target)V = 11
batch_size = 20
num_batch = 30# 获得Transformer模型及其优化器和损失函数
model = make_model(V, V, N=2)# 将模型移动到GPU上
model.cuda()model_optimizer = get_std_opt(model)criterion = LabelSmoothing(size=V, padding_idx=0, smoothing=0.0)
loss = SimpleLossCompute(model.generator, criterion, model_optimizer)# 运行模型进行训练和评估
def run(model, loss, epochs=10):for epoch in range(epochs):model.train()run_epoch(data_generator(V, 8, 20), model, loss)model.eval()run_epoch(data_generator(V, 8, 5), model, loss)start = time.time()run(model, loss)end = time.time()# 总时间
total_time = end - start
print(f"Total time: {total_time:.3f}s")# 使用模型进行贪婪解码
def run(model, loss, epochs=10):for epoch in range(epochs):model.train()run_epoch(data_generator(V, 8, 20), model, loss)model.eval()run_epoch(data_generator(V, 8, 5), model, loss)model.eval()source = torch.LongTensor([[1, 3, 2, 5, 4, 6, 7, 8, 9, 10]]).cuda()source_mask = torch.ones(1, 1, 10).cuda()result = greedy_decode(model, source, source_mask, max_len=10,start_symbol=1)print(result)start = time.time()run(model, loss)end = time.time()# 总时间
total_time = end - start
print(f"Total time: {total_time:.3f}s")
然后来自这个人的源代码讲解也非常好,和视频一样,我也修改了可以放在GPU运行,代码如下
【精选】Pytorch:Transformer(Encoder编码器-Decoder解码器、多头注意力机制、多头自注意力机制、掩码张量、前馈全连接层、规范化层、子层连接结构、pyitcast) part1_あずにゃん的博客-CSDN博客
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.autograd import Variable
import math
import matplotlib.pyplot as plt
import numpy as np
import copy# embedding = nn.Embedding(10, 3)
# input1 = torch.LongTensor([[1, 2, 4, 5], [4, 3, 2, 9]])
# print(embedding(input1))# embedding = nn.Embedding(10, 3, padding_idx=0)
# input1 = torch.LongTensor([[0, 2, 0, 5]])
# print(embedding(input1))# 构建Embedding类来实现文本嵌入层
class Embeddings(nn.Module):def __init__(self, d_model, vocab):# d_model: 词嵌入的维度# vocab: 词表的大小super(Embeddings, self).__init__()# 定义Embedding层self.lut = nn.Embedding(vocab, d_model)# 将参数传入类中self.d_model = d_modeldef forward(self, x):# x: 代表输入进模型的文本通过词汇映射后的数字张量return self.lut(x) * math.sqrt(self.d_model)d_model = 512
vocab = 1000# x = Variable(torch.LongTensor([[100, 2, 421, 508], [491, 998, 1, 221]]))
# emb = Embeddings(d_model, vocab)
# embr = emb(x)
# print("embr:", embr)
# print(embr.shape)# m = nn.Dropout(p=0.2)
# input1 = torch.randn(4, 5)
# output = m(input1)
# print(output)# x = torch.tensor([1, 2, 3, 4])
# y = torch.unsqueeze(x, 0)
# print(y)
# z = torch.unsqueeze(x, 1)
# print(z)# 构建位置编码器的类
class PositionalEncoding(nn.Module):def __init__(self, d_model, dropout, max_len=5000):# d_model: 代表词嵌入的维度# dropout: 代表Dropout层的置零比率# max_len: 代表每隔句子的最大长度super(PositionalEncoding, self).__init__()# 实例化Dropout层self.dropout = nn.Dropout(p=dropout)# 初始化一个位置编码矩阵, 大小是max_len * d_modelpe = torch.zeros(max_len, d_model)# 初始化一个绝对位置矩阵, max_len * 1position = torch.arange(0., max_len).unsqueeze(1)# 定义一个变化矩阵div_term, 跳跃式的初始化div_term = torch.exp(torch.arange(0., d_model, 2) * -(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)# 将位置编码矩阵注册成模型的buffer, 这个buffer不是模型中的参数, 不跟随优化器同步更新# 注册成buffer后我们就可以在模型保存后重新加载的时候, 将这个位置编码器和模型参数一同加载进来self.register_buffer('pe', pe)def forward(self, x):# x: 代表文本序列的词嵌入表示# 首先明确pe的编码太长了, 将第二个维度, 也就是max_len对应的那个维度缩小成x的句子长度同等的长度x = x + Variable(self.pe[:, :x.size(1)], requires_grad=False)return self.dropout(x)d_model = 512
dropout = 0.1
max_len = 60# x = embr
# pe = PositionalEncoding(d_model, dropout, max_len)
# pe_result = pe(x)
# print(pe_result)
# print(pe_result.shape)# 第一步设置一个画布
# plt.figure(figsize=(15, 5))# 实例化PositionalEncoding类对象, 词嵌入维度给20, 置零比率设置为0
# pe = PositionalEncoding(20, 0)# 向pe中传入一个全零初始化的x, 相当于展示pe
# y = pe(Variable(torch.zeros(1, 100, 20)))# plt.plot(np.arange(100), y[0, :, 4:8].data.numpy())# plt.legend(["dim %d"%p for p in [4, 5, 6, 7]])# print(np.triu([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]], k=-1))
# print(np.triu([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]], k=0))
# print(np.triu([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]], k=1))# 构建掩码张量的函数
def subsequent_mask(size):# size: 代表掩码张量后两个维度, 形成一个方阵attn_shape = (1, size, size)# 使用np.ones()先构建一个全1的张量, 然后利用np.triu()形成上三角矩阵subsequent_mask = np.triu(np.ones(attn_shape), k=1).astype('uint8')# 使得这个三角矩阵反转return torch.from_numpy(1 - subsequent_mask)size = 5# sm = subsequent_mask(size)
# print("sm:", sm)# plt.figure(figsize=(5, 5))
# plt.imshow(subsequent_mask(20)[0])# x = Variable(torch.randn(5, 5))
# print(x)# mask = Variable(torch.zeros(5, 5))
# print(mask)# y = x.masked_fill(mask == 0, -1e9)
# print(y)def attention(query, key, value, mask=None, dropout=None):# query, key, value: 代表注意力的三个输入张量# mask: 掩码张量# dropout: 传入的Dropout实例化对象# 首先将query的最后一个维度提取出来, 代表的是词嵌入的维度d_k = query.size(-1)# 按照注意力计算公式, 将query和key的转置进行矩阵乘法, 然后除以缩放稀疏scores = torch.matmul(query, key.transpose(-2, -1)) / math.sqrt(d_k)# 判断是否使用掩码张量if mask is not None:# 利用masked_fill方法, 将掩码张量和0进行位置的意义比较, 如果等于0, 替换成一个非常小的数值scores = scores.masked_fill(mask == 0, -1e9)# 对scores的最后一个维度上进行softmax操作p_attn = F.softmax(scores, dim=-1)# 判断是否使用dropoutif dropout is not None:p_attn = dropout(p_attn)# 最后一步完成p_attn和value张量的乘法, 并返回query注意力表示return torch.matmul(p_attn, value), p_attn# query = key = value = pe_result
# mask = Variable(torch.zeros(2, 4, 4))
# attn, p_attn = attention(query, key, value, mask=mask)
# print('attn:', attn)
# print(attn.shape)
# print('p_attn:', p_attn)
# print(p_attn.shape)# x = torch.randn(4, 4)
# print(x.size())
# y = x.view(16)
# print(y.size())
# z = x.view(-1, 8)
# print(z.size())# a = torch.randn(1, 2, 3, 4)
# print(a.size())
# print(a)# b = a.transpose(1, 2)
# print(b.size())
# print(b)# c = a.view(1, 3, 2, 4)
# print(c.size())
# print(c)# 实现克隆函数, 因为在多头注意力机制下, 要用到多个结构相同的线性层
# 需要使用clone函数将他们一同初始化到一个网络层列表对象中
def clones(module, N):# module: 代表要克隆的目标网络层# N: 将module克隆几个return nn.ModuleList([copy.deepcopy(module) for _ in range(N)])# 实现多头注意力机制的类
class MultiHeadedAttention(nn.Module):def __init__(self, head, embedding_dim, dropout=0.1):# head: 代表几个头的参数# embedding_dim: 代表词嵌入的维度# dropout: 进行Dropout操作时, 置零的比率super(MultiHeadedAttention, self).__init__()# 要确认一个事实: 多头的数量head需要整除词嵌入的维度embedding_dimassert embedding_dim % head == 0# 得到每个头获得的词向量的维度self.d_k = embedding_dim // headself.head = headself.embedding_dim = embedding_dim# 获得线性层, 要获得4个, 分别是Q,K,V以及最终的输出线性层self.linears = clones(nn.Linear(embedding_dim, embedding_dim), 4)# 初始化注意力张量self.attn = None# 初始化dropout对象self.dropout = nn.Dropout(p=dropout)def forward(self, query, key, value, mask=None):# query, key, value是注意力机制的三个输入张量, mask代表掩码张量# 首先判断是否使用掩码张量if mask is not None:# 使用unsqueeze将掩码张量进行维度扩充, 代表多头中的第n个头mask = mask.unsqueeze(0)# 得到batch_sizebatch_size = query.size(0)# 首先使用zip将网络层和输入数据连接在一起, 模型的输出利用view和transpose进行维度和形状的改变query, key, value = \[model(x).view(batch_size, -1, self.head, self.d_k).transpose(1, 2)for model, x in zip(self.linears, (query, key, value))]# 将每个头的输出传入到注意力层x, self.attn = attention(query, key, value, mask=mask,dropout=self.dropout)# 得到每个头的计算结果是4维张量, 需要进行形状的转换# 前面已经将1,2两个维度进行过转置, 在这里要重新转置回来# 注意: 经历了transpose()方法后, 必须要使用contiguous方法, 不然无法使用view()方法x = x.transpose(1, 2).contiguous().view(batch_size, -1,self.head * self.d_k)# 最后将x输入线性层列表中的最后一个线性层中进行处理, 得到最终的多头注意力结构输出return self.linears[-1](x)# 实例化若干参数
head = 8
embedding_dim = 512
dropout = 0.2# 若干输入参数的初始化
# query = key = value = pe_result# mask = Variable(torch.zeros(8, 4, 4))
# mha = MultiHeadedAttention(head, embedding_dim, dropout)
# mha_result = mha(query, key, value, mask)
# print(mha_result)
# print(mha_result.shape)# 构建前馈全连接网络类
class PositionwiseFeedForward(nn.Module):def __init__(self, d_model, d_ff, dropout=0.1):# d_model: 代表词嵌入的维度, 同时也是两个线性层的输入维度和输出维度# d_ff: 代表第一个线性层的输出维度, 和第二个线性层的输入维度# dropout: 经过Dropout层处理时, 随机置零的比率super(PositionwiseFeedForward, self).__init__()# 定义两层全连接的线性层self.w1 = nn.Linear(d_model, d_ff)self.w2 = nn.Linear(d_ff, d_model)self.dropout = nn.Dropout(p=dropout)def forward(self, x):# x: 代表来自上一层的输出# 首先将x送入第一个线性层网络, 然后经历relu函数的激活, 再经历dropout层的处理# 最后送入第二个线性层return self.w2(self.dropout(F.relu(self.w1(x))))d_model = 512
d_ff = 64
dropout = 0.2# x = mha_result
# ff = PositionwiseFeedForward(d_model, d_ff, dropout)
# ff_result = ff(x)
# print(ff_result)
# print(ff_result.shape)# 构建规范化层的类
class LayerNorm(nn.Module):def __init__(self, features, eps=1e-6):# features: 代表词嵌入的维度# eps: 一个足够小的正数, 用来在规范化计算公式的分母中, 防止除零操作super(LayerNorm, self).__init__()# 初始化两个参数张量a2, b2,用于对结果做规范化操作计算# 将其用nn.Parameter进行封装, 代表他们也是模型中的参数self.a2 = nn.Parameter(torch.ones(features))self.b2 = nn.Parameter(torch.zeros(features))self.eps = epsdef forward(self, x):# x: 代表上一层网络的输出# 首先对x进行最后一个维度上的求均值操作, 同时操持输出维度和输入维度一致mean = x.mean(-1, keepdim=True)# 接着对x进行字后一个维度上的求标准差的操作, 同时保持输出维度和输入维度一致std = x.std(-1, keepdim=True)# 按照规范化公式进行计算并返回return self.a2 * (x - mean) / (std + self.eps) + self.b2features = d_model = 512
eps = 1e-6# x = ff_result
# ln = LayerNorm(features, eps)
# ln_result = ln(x)
# print(ln_result)
# print(ln_result.shape)# 构建子层连接结构的类
class SublayerConnection(nn.Module):def __init__(self, size, dropout=0.1):# size: 代表词嵌入的维度# dropout: 进行Dropout操作的置零比率super(SublayerConnection, self).__init__()# 实例化一个规范化层的对象self.norm = LayerNorm(size)# 实例化一个dropout对象self.dropout = nn.Dropout(p=dropout)self.size = sizedef forward(self, x, sublayer):# x: 代表上一层传入的张量# sublayer: 该子层连接中子层函数# 首先将x进行规范化, 然后送入子层函数中处理, 处理结果进入dropout层, 最后进行残差连接return x + self.dropout(sublayer(self.norm(x)))size = d_model = 512
head = 8
dropout = 0.2# x = pe_result
# mask = Variable(torch.zeros(8, 4, 4))
# self_attn = MultiHeadedAttention(head, d_model)# sublayer = lambda x: self_attn(x, x, x, mask)# sc = SublayerConnection(size, dropout)
# sc_result = sc(x, sublayer)
# print(sc_result)
# print(sc_result.shape)# 构建编码器层的类
class EncoderLayer(nn.Module):def __init__(self, size, self_attn, feed_forward, dropout):# size: 代表词嵌入的维度# self_attn: 代表传入的多头自注意力子层的实例化对象# feed_forward: 代表前馈全连接层实例化对象# dropout: 进行dropout操作时的置零比率super(EncoderLayer, self).__init__()# 将两个实例化对象和参数传入类中self.self_attn = self_attnself.feed_forward = feed_forwardself.size = size# 编码器层中有2个子层连接结构, 使用clones函数进行操作self.sublayer = clones(SublayerConnection(size, dropout), 2)def forward(self, x, mask):# x: 代表上一层的传入张量# mask: 代表掩码张量# 首先让x经过第一个子层连接结构,内部包含多头自注意力机制子层# 再让张量经过第二个子层连接结构, 其中包含前馈全连接网络x = self.sublayer[0](x, lambda x: self.self_attn(x, x, x, mask))return self.sublayer[1](x, self.feed_forward)# size = d_model = 512
# head = 8
# d_ff = 64
# x = pe_result
# dropout = 0.2# self_attn = MultiHeadedAttention(head, d_model)
# ff = PositionwiseFeedForward(d_model, d_ff, dropout)
# mask = Variable(torch.zeros(8, 4, 4))# el = EncoderLayer(size, self_attn, ff, dropout)
# el_result = el(x, mask)
# print(el_result)
# print(el_result.shape)# 构建编码器类Encoder
class Encoder(nn.Module):def __init__(self, layer, N):# layer: 代表编码器层# N: 代表编码器中有几个layersuper(Encoder, self).__init__()# 首先使用clones函数克隆N个编码器层放置在self.layers中self.layers = clones(layer, N)# 初始化一个规范化层, 作用在编码器的最后面self.norm = LayerNorm(layer.size)def forward(self, x, mask):# x: 代表上一层输出的张量# mask: 代表掩码张量# 让x依次经历N个编码器层的处理, 最后再经过规范化层就可以输出了for layer in self.layers:x = layer(x, mask)return self.norm(x)# size = d_model = 512
# d_ff = 64
# head = 8
# c = copy.deepcopy
# attn = MultiHeadedAttention(head, d_model)
# ff = PositionwiseFeedForward(d_model, d_ff, dropout)
# dropout = 0.2
# layer = EncoderLayer(size, c(attn), c(ff), dropout)
# N = 8
# mask = Variable(torch.zeros(8, 4, 4))# en = Encoder(layer, N)
# en_result = en(x, mask)
# print(en_result)
# print(en_result.shape)# 构建解码器层类
class DecoderLayer(nn.Module):def __init__(self, size, self_attn, src_attn, feed_forward, dropout):# size: 代表词嵌入的维度# self_attn: 代表多头自注意力机制的对象# src_attn: 代表常规的注意力机制的对象# feed_forward: 代表前馈全连接层的对象# dropout: 代表Dropout的置零比率super(DecoderLayer, self).__init__()# 将参数传入类中self.size = sizeself.self_attn = self_attnself.src_attn = src_attnself.feed_forward = feed_forwardself.dropout = dropout# 按照解码器层的结构图, 使用clones函数克隆3个子层连接对象self.sublayer = clones(SublayerConnection(size, dropout), 3)def forward(self, x, memory, source_mask, target_mask):# x: 代表上一层输入的张量# memory: 代表编码器的语义存储张量# source_mask: 源数据的掩码张量# target_mask: 目标数据的掩码张量m = memory# 第一步让x经历第一个子层, 多头自注意力机制的子层# 采用target_mask, 为了将解码时未来的信息进行遮掩, 比如模型解码第二个字符, 只能看见第一个字符信息x = self.sublayer[0](x, lambda x: self.self_attn(x, x, x, target_mask))# 第二步让x经历第二个子层, 常规的注意力机制的子层, Q!=K=V# 采用source_mask, 为了遮掩掉对结果信息无用的数据x = self.sublayer[1](x, lambda x: self.src_attn(x, m, m, source_mask))# 第三步让x经历第三个子层, 前馈全连接层return self.sublayer[2](x, self.feed_forward)# size = d_model = 512
# head = 8
# d_ff = 64
# dropout = 0.2# self_attn = src_attn = MultiHeadedAttention(head, d_model, dropout)# ff = PositionwiseFeedForward(d_model, d_ff, dropout)# x = pe_result# memory = en_result# mask = Variable(torch.zeros(8, 4, 4))
# source_mask = target_mask = mask# dl = DecoderLayer(size, self_attn, src_attn, ff, dropout)
# dl_result = dl(x, memory, source_mask, target_mask)
# print(dl_result)
# print(dl_result.shape)# 构建解码器类
class Decoder(nn.Module):def __init__(self, layer, N):# layer: 代表解码器层的对象# N: 代表将layer进行几层的拷贝super(Decoder, self).__init__()# 利用clones函数克隆N个layerself.layers = clones(layer, N)# 实例化一个规范化层self.norm = LayerNorm(layer.size)def forward(self, x, memory, source_mask, target_mask):# x: 代表目标数据的嵌入表示,# memory: 代表编码器的输出张量# source_mask: 源数据的掩码张量# target_mask: 目标数据的掩码张量# 要将x依次经历所有的编码器层处理, 最后通过规范化层for layer in self.layers:x = layer(x, memory, source_mask, target_mask)return self.norm(x)# size = d_model = 512
# head = 8
# d_ff = 64
# dropout = 0.2
# c = copy.deepcopy
# attn = MultiHeadedAttention(head, d_model)
# ff = PositionwiseFeedForward(d_model, d_ff, dropout)
# layer = DecoderLayer(d_model, c(attn), c(attn), c(ff), dropout)# N = 8
# x = pe_result
# memory = en_result
# mask = Variable(torch.zeros(8, 4, 4))
# source_mask = target_mask = mask# de = Decoder(layer, N)
# de_result = de(x, memory, source_mask, target_mask)
# print(de_result)
# print(de_result.shape)# 构建Generator类
import torch.nn.functional as Fclass Generator(nn.Module):def __init__(self, d_model, vocab_size):# d_model: 代表词嵌入的维度# vocab_size: 代表词表的总大小super(Generator, self).__init__()# 定义一个线性层, 作用是完成网络输出维度的变换self.project = nn.Linear(d_model, vocab_size)def forward(self, x):# x: 代表上一层的输出张量# 首先将x送入线性层中, 让其经历softmax的处理return F.log_softmax(self.project(x), dim=-1)# d_model = 512
# vocab_size = 1000
# x = de_result# gen = Generator(d_model, vocab_size)
# gen_result = gen(x)
# print(gen_result)
# print(gen_result.shape)# 构建编码器-解码器结构类
class EncoderDecoder(nn.Module):def __init__(self, encoder, decoder, source_embed, target_embed, generator):# encoder: 代表编码器对象# decoder: 代表解码器对象# source_embed: 代表源数据的嵌入函数# target_embed: 代表目标数据的嵌入函数# generator: 代表输出部分类别生成器对象super(EncoderDecoder, self).__init__()self.encoder = encoderself.decoder = decoderself.src_embed = source_embedself.tgt_embed = target_embedself.generator = generatordef forward(self, source, target, source_mask, target_mask):# source: 代表源数据# target: 代表目标数据# source_mask: 代表源数据的掩码张量# target_mask: 代表目标数据的掩码张量return self.decode(self.encode(source, source_mask), source_mask,target, target_mask)def encode(self, source, source_mask):return self.encoder(self.src_embed(source), source_mask)def decode(self, memory, source_mask, target, target_mask):# memory: 代表经历编码器编码后的输出张量return self.decoder(self.tgt_embed(target), memory, source_mask,target_mask)# vocab_size = 1000
# d_model = 512
# encoder = en
# decoder = de
# source_embed = nn.Embedding(vocab_size, d_model)
# target_embed = nn.Embedding(vocab_size, d_model)
# generator = gen
#
# source = target = Variable(torch.LongTensor([[100, 2, 421, 508], [491, 998, 1, 221]]))
#
# source_mask = target_mask = Variable(torch.zeros(8, 4, 4))
#
# ed = EncoderDecoder(encoder, decoder, source_embed, target_embed, generator)
# ed_result = ed(source, target, source_mask, target_mask)
# print(ed_result)
# print(ed_result.shape)def make_model(source_vocab, target_vocab, N=6, d_model=512, d_ff=2048, head=8,dropout=0.1):# source_vocab: 代表源数据的词汇总数# target_vocab: 代表目标数据的词汇总数# N: 代表编码器和解码器堆叠的层数# d_model: 代表词嵌入的维度# d_ff: 代表前馈全连接层中变换矩阵的维度# head: 多头注意力机制中的头数# dropout: 指置零的比率c = copy.deepcopy# 实例化一个多头注意力的类attn = MultiHeadedAttention(head, d_model)# 实例化一个前馈全连接层的网络对象ff = PositionwiseFeedForward(d_model, d_ff, dropout)# 实例化一个位置编码器position = PositionalEncoding(d_model, dropout)# 实例化模型model,利用的是EncoderDecoder类# 编码器的结构里面有2个子层, attention层和前馈全连接层# 解码器的结构中有3个子层, 两个attention层和前馈全连接层model = EncoderDecoder(Encoder(EncoderLayer(d_model, c(attn), c(ff), dropout), N),Decoder(DecoderLayer(d_model, c(attn), c(attn), c(ff), dropout), N),nn.Sequential(Embeddings(d_model, source_vocab), c(position)),nn.Sequential(Embeddings(d_model, target_vocab), c(position)),Generator(d_model, target_vocab))# 初始化整个模型中的参数, 判断参数的维度大于1, 将矩阵初始化成一个服从均匀分布的矩阵for p in model.parameters():if p.dim() > 1:nn.init.xavier_uniform_(p)return modelsource_vocab = 11
target_vocab = 11
N = 6# if __name__ == '__main__':
# res = make_model(source_vocab, target_vocab, N)
# print(res)# ------------------------------------------------------from pyitcast.transformer_utils import Batch
from pyitcast.transformer_utils import get_std_opt
from pyitcast.transformer_utils import LabelSmoothing
from pyitcast.transformer_utils import SimpleLossComputefrom pyitcast.transformer_utils import run_epoch
from pyitcast.transformer_utils import greedy_decodedef data_generator(V, batch_size, num_batch):# V: 随机生成数据的最大值+1# batch_size: 每次输送给模型的样本数量, 经历这些样本训练后进行一次参数的更新# num_batch: 一共输送模型多少轮数据for i in range(num_batch):# 使用numpy中的random.randint()来随机生成[1, V)# 分布的形状(batch, 10)data = torch.from_numpy(np.random.randint(1, V, size=(batch_size, 10)))# 将数据的第一列全部设置为1, 作为起始标志data[:, 0] = 1# 因为是copy任务, 所以源数据和目标数据完全一致# 设置参数requires_grad=False, 样本的参数不需要参与梯度的计算source = Variable(data, requires_grad=False).long().cuda()target = Variable(data, requires_grad=False).long().cuda()yield Batch(source, target)V = 11
batch_size = 20
num_batch = 30# if __name__ == '__main__':
# res = data_generator(V, batch_size, num_batch)
# print(res)# 使用make_model()函数获得模型的实例化对象
model = make_model(V, V, N=2)
model.cuda()# 使用工具包get_std_opt获得模型的优化器
model_optimizer = get_std_opt(model)# 使用工具包LabelSmoothing获得标签平滑对象
criterion = LabelSmoothing(size=V, padding_idx=0, smoothing=0.0)# 使用工具包SimpleLossCompute获得利用标签平滑的结果得到的损失计算方法
loss = SimpleLossCompute(model.generator, criterion, model_optimizer)# crit = LabelSmoothing(size=5, padding_idx=0, smoothing=0.5)# predict = Variable(torch.FloatTensor([[0, 0.2, 0.7, 0.1, 0],
# [0, 0.2, 0.7, 0.1, 0],
# [0, 0.2, 0.7, 0.1, 0]]))# target = Variable(torch.LongTensor([2, 1, 0]))# crit(predict, target)# plt.imshow(crit.true_dist)# def run(model, loss, epochs=10):
# model: 代表将要训练的模型
# loss: 代表使用的损失计算方法
# epochs: 代表模型训练的轮次数
# for epoch in range(epochs):
# 首先进入训练模式, 所有的参数将会被更新
# model.train()
# 训练时, 传入的batch_size是20
# run_epoch(data_generator(V, 8, 20), model, loss)# 训练结束后, 进入评估模式, 所有的参数固定不变
# model.eval()
# 评估时, 传入的batch_size是5
# run_epoch(data_generator(V, 8, 5), model, loss)# if __name__ == '__main__':
# run(model, loss)def run(model, loss, epochs=10):for epoch in range(epochs):# 首先进入训练模式, 所有的参数将会被更新model.train()run_epoch(data_generator(V, 8, 20), model, loss)# 训练结束后, 进入评估模式, 所有的参数固定不变model.eval()run_epoch(data_generator(V, 8, 5), model, loss)# 跳出for循环后, 代表模型训练结束, 进入评估模式model.eval()# run_epoch(data_generator(V, 8, 5), model, loss)# 初始化一个输入张量source = torch.LongTensor([[1, 3, 2, 5, 4, 6, 7, 8, 9, 10]]).cuda()# 初始化一个输入张量的掩码张量, 全1代表没有任何的遮掩source_mask = torch.ones(1, 1, 10).cuda()# 设定解码的最大长度max_len等于10, 起始数字的标志默认等于1result = greedy_decode(model, source, source_mask, max_len=10,start_symbol=1)print(result)import timeif __name__ == '__main__':start = time.time()run(model, loss)end = time.time()# 总时间total_time = end - startprint(f"Total time: {total_time:.3f}s")
相关文章:
关于这个“这是B站目前讲的最好的【Transformer实战】教程!“视频的目前可以运行的源代码GPU版本
课程链接如下: 2.1认识Transformer架构-part1_哔哩哔哩_bilibili 因为网上可以找到源代码,但是呢,代码似乎有点小错误,我自己改正后,放到了GPU上运行, 代码如下: # 来自https://www.bilibil…...

STM32定时器输入捕获测量高电平时间
STM32定时器输入捕获测量高电平时间 输入捕获测量高电平时间CuebMX配置代码部分 本篇内容要求读者对STM32通用定时器有一点理解,如有不解,请看 夜深人静学32系列15——通用定时器 输入捕获 输入捕获是STM32通用定时器的一种功能,可以捕获特定…...

开源WIFI继电器之硬件电路
一、原理图 源文件 二、原理图说明 1、器件说明 U4:ESP8285模块 U6:触发器 U3:继电器 2、继电器状态检测说明 检测继电器线圈是否通电来判断继电器是否导通,当Q1不导通时,Q1集电极的电压为3.3V,经…...
远程执行ssh脚本
sshpass -p 123456 ssh root10.1.10.18 "/root/start.sh"sshpass: 这是一个工具,用于提供密码给 ssh 命令,以便无需手动输入密码就能通过 SSH 连接到远程服务器。 -p ‘123456’: 这是 sshpass 命令的选项,指定了连接时使用的密码…...

excel导入 Easy Excel
依旧是框架感觉有东西,但是确实是模拟不出来,各种零零散散的件太多了 controller层 ApiOperation(value "导入Excel", notes "导入Excel", httpMethod "POST", response ExcelResponseDTO.class)ApiImplicitParams({…...

html实现图片裁剪处理(附源码)
文章目录 1.设计来源1.1 主界面1.2 裁剪界面 2.效果和源码2.1 动态效果2.2 源代码 源码下载 作者:xcLeigh 文章地址:https://blog.csdn.net/weixin_43151418/article/details/134455169 html实现图片裁剪处理(附源码),支持图片放大缩小&#…...
前端语言报错
1. 语法错误(Syntax Errors) 这是由于代码不符合语法规则而引起的错误,通常在代码编译阶段发生。示例: javascriptCopy code if (x 10 { // 缺少了右括号 // 代码逻辑 } 2. 类型错误(Type Errors) 这…...
详细讲解什么是观察者模式
观察者模式(Observer Pattern)是一种行为设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,当主题对象状态发生变化时,所有依赖于它的观察者都会得到通知并自动更新。 该模…...

镭速,克服UDP传输缺点的百倍提速传输软件工具
在网络传输中,我们经常会面临这样的困难:文件太大,传输速度太慢,浪费时间和流量;文件太小,传输速度太快,容易出现丢包和乱序,损害数据的完整性和正确性。这些困难的根本在于传输层协…...

Semi-Supervised Multi-Modal Learning with Balanced Spectral Decomposition
Y是所有模态的表征矩阵, ∑ i 1 d h ( λ i ) \sum_{i1}^dh(\lambda_i) ∑i1dh(λi) is the proposed eigenvalue-based objective function,the final similarity matrix W for the multimodal data as a block matrix 辅助信息 作者未提供代码...
3296:【例50.2】 计算书费《信息学奥赛一本通编程启蒙(C++版)》
3296:【例50.2】 计算书费《信息学奥赛一本通编程启蒙(C版)》 【题目描述】 下面是一个图书的单价表: 1、计算概论 28.9 元/本 2、数据结构与算法 32.7 元/本 3、数字逻辑 45.6 元/本 4、C程序设计教程 78 元/本 5、人工智能…...

统一身份认证平台之SSO建设
前言 上篇说道Passwordless无密码技术,也提到了数字时代密码管理的难度,其实在日常的生活中,很多用户也会因为忘记某些网站的登录密码而烦恼。为了方便记忆,很多人都在不同的站点使用相同的用户名和密码,虽然也可以减少…...
【开题报告】基于SpringBoot的膳食营养健康网站的设计与实现
1.选题背景与意义 基于SpringBoot的膳食营养健康网站的设计与实现是一个具有重要意义的选题。背景和意义主要包括以下几点: (1)社会健康意识的提升:随着人们健康意识的提高,越来越多的人开始关注自己的饮食营养问题。…...
超五类网线和六类网线的相同点和区别
本文对超五类网线和六类网线的相同点和区别进行了简单介绍,帮助大家区分和建立相应的概念。 相同点: (1)都是网络跳线,用于连接网络设备。 (2)网线内部由8根不同颜色的线组成。 区别…...

Linux--初识和基本的指令(1)
目录 前言 0.什么是操作系统 0.1 搭建 Linux 环境 0.2搭建 Linux 环境小结 1.使用 XShell 远程登录 Linux 1.1关于 Linux 桌面 1.2下载安装 XShell 1.3查看 Linux 主机 ip 1.4XShell 下的复制粘贴 2.Linux下基本指令 2.1 pwd命令 2.2 ls命令 2.3 mkdir指令 2.4 cd…...

万宾科技智能井盖传感器,提升市政井盖健康
市政井盖就是城市里不可或缺的基础设施之一,关于它的监测工作可马虎不得。它承载着保护市民的交通安全以及城市正常运转的重要使命。虽然现在城市化的速度很快,但是传统的市政井盖管理方式变得有些力不从心了。井盖的覆盖范围很广,如果单单依…...

transformer学习资料
一、NLP 自然语言处理 NLP 是机器学习在语言学领域的研究,专注于理解与人类语言相关的一切。NLP 的目标不仅是要理解每个单独的单词含义,而且也要理解这些单词与之相关联的上下文之间的意思。 常见的NLP 任务列表: 对整句的分类࿱…...

一起学docker系列之四docker的常用命令--系统操作docker命令及镜像命令
目录 前言1 操作 Docker 的命令1.1 启动 Docker1.2 停止 Docker1.3 重启 Docker1.4 查看 Docker 状态1.5 查看 Docker 所有命令的信息1.6 查看某个命令的帮助信息 2 操作镜像的命令2.1 查看所有镜像2.2 搜索某个镜像2.3 下载某个镜像2.4 查看镜像所占空间2.5 删除镜像2.6 强制删…...

MySQL 的执行原理(三)
5.4. InnoDB 中的统计数据 我们前边唠叨查询成本的时候经常用到一些统计数据,比如通过 SHOW TABLE STATUS 可以看到关于表的统计数据,通过 SHOW INDEX 可以看到关于索引 的统计数据,那么这些统计数据是怎么来的呢?它们是以什么方…...
一道好题——分治
一道好题应该有一个简洁的题面。 有一个长度为 n,初始全为 0 的序列 a,另有一个长度为 n 的序列 b,你希望将 a 变成 b,你可以执行如下两种操作: 1 x:将 a 中所有值为 x 的数 11。 2 x:将 a 中下…...

【JavaEE】-- HTTP
1. HTTP是什么? HTTP(全称为"超文本传输协议")是一种应用非常广泛的应用层协议,HTTP是基于TCP协议的一种应用层协议。 应用层协议:是计算机网络协议栈中最高层的协议,它定义了运行在不同主机上…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序
一、开发准备 环境搭建: 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 项目创建: File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...

什么是库存周转?如何用进销存系统提高库存周转率?
你可能听说过这样一句话: “利润不是赚出来的,是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业,很多企业看着销售不错,账上却没钱、利润也不见了,一翻库存才发现: 一堆卖不动的旧货…...
什么是EULA和DPA
文章目录 EULA(End User License Agreement)DPA(Data Protection Agreement)一、定义与背景二、核心内容三、法律效力与责任四、实际应用与意义 EULA(End User License Agreement) 定义: EULA即…...
【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)
要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况,可以通过以下几种方式模拟或触发: 1. 增加CPU负载 运行大量计算密集型任务,例如: 使用多线程循环执行复杂计算(如数学运算、加密解密等)。运行图…...
Java线上CPU飙高问题排查全指南
一、引言 在Java应用的线上运行环境中,CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时,通常会导致应用响应缓慢,甚至服务不可用,严重影响用户体验和业务运行。因此,掌握一套科学有效的CPU飙高问题排查方法&…...
Xen Server服务器释放磁盘空间
disk.sh #!/bin/bashcd /run/sr-mount/e54f0646-ae11-0457-b64f-eba4673b824c # 全部虚拟机物理磁盘文件存储 a$(ls -l | awk {print $NF} | cut -d. -f1) # 使用中的虚拟机物理磁盘文件 b$(xe vm-disk-list --multiple | grep uuid | awk {print $NF})printf "%s\n"…...

基于 TAPD 进行项目管理
起因 自己写了个小工具,仓库用的Github。之前在用markdown进行需求管理,现在随着功能的增加,感觉有点难以管理了,所以用TAPD这个工具进行需求、Bug管理。 操作流程 注册 TAPD,需要提供一个企业名新建一个项目&#…...

ZYNQ学习记录FPGA(一)ZYNQ简介
一、知识准备 1.一些术语,缩写和概念: 1)ZYNQ全称:ZYNQ7000 All Pgrammable SoC 2)SoC:system on chips(片上系统),对比集成电路的SoB(system on board) 3)ARM:处理器…...

02.运算符
目录 什么是运算符 算术运算符 1.基本四则运算符 2.增量运算符 3.自增/自减运算符 关系运算符 逻辑运算符 &&:逻辑与 ||:逻辑或 !:逻辑非 短路求值 位运算符 按位与&: 按位或 | 按位取反~ …...