关于这个“这是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 中下…...
Asp.Net Core基于StackExchange Redis 缓存
NuGet安装 StackExchange.Redis Microsoft.Extensions.Options 0. appsettings.json初始化配置 {"Logging": {"LogLevel": {"Default": "Information","Microsoft.AspNetCore": "Warning"}},"AllowedHos…...
atc abc409E
原题链接:E - Pair Annihilation 题目背景: n 个点 n - 1 条边的有权无向图,每个点都有一个值,两个连通的点的值可以互相抵消,既将u 的 -1 传给 v 时可以抵消掉 v 的 1 并花费边权值;求最小花费。 考察算…...

记录一个用了很久的git提交到github和gitee比较方便的方法
在当前git init后,在隐藏的git文件夹中找到config文件 [user]name thels [remote "github"]url your github repository urlfetch refs/heads/*:refs/remotes/origin/* [remote "gitee"]url your gitee repository urlfetch refs/heads/*:…...
旅行商问题(TSP)的 C++ 动态规划解法教学攻略
一、问题描述 旅行商问题(TSP)是一个经典的组合优化问题。给定一个无向图,图中的顶点表示城市,边表示两个城市之间的路径,边的权重表示路径的距离。一个售货员需要从驻地出发,经过所有城市后回到驻地&…...

金融系统渗透测试
金融系统渗透测试是保障金融机构网络安全的核心环节,它的核心目标是通过模拟攻击手段主动发现系统漏洞,防范数据泄露、资金盗取等重大风险。 一、金融系统渗透测试的核心框架 合规性驱动 需严格遵循《网络安全法》《数据安全法》及金融行业监管要求&am…...
在C++中,头文件(.h或.hpp)的标准写法
目录 1.头文件保护(Include Guards)2.包含必要的标准库头文件3.前向声明(Forward Declarations)4.命名空间5.注释示例1:基础头文件示例2:包含模板和内联函数的头文件示例3:C11风格的枚举类头文件…...

【前端】es6相关,柯里化
0. 严格模式 严格模式的概念从ES6引进。通过严格模式,可以在函数内部选择进行较为严格的全局或局部的错误条件检测。 MDN中严格模式的描述 严格模式通过抛出错误来消除了一些原有静默错误严格模式修复了一些导致 JavaScript引擎难以执行优化的缺陷:有时…...
daz3d + PBRSkin (MDL)+ SSS
好的,我们来解释一下 Daz3D 中的 PBRSkin (MDL) Shader。 简单来说,PBRSkin (MDL) 是 Daz Studio 中一种基于物理渲染(PBR)技术、专门用于创建高度逼真人物皮肤效果的着色器(Shader)。 它利用 NVIDIA 的材…...
go中的接口返回设计思想
go中的接口返回设计思想 前言 在学习AI编码过程中,产生了类似以下结构的代码 : type MQClient interface {PublishMessage(queue string, message interface{}) error...... } ... type RabbitMQClient struct {conn *amqp.Connectionchannel *amqp.C…...
分布式微服务系统架构第144集:FastAPI全栈开发教育系统
加群联系作者vx:xiaoda0423 仓库地址:https://webvueblog.github.io/JavaPlusDoc/ https://1024bat.cn/ https://github.com/webVueBlog/fastapi_plus https://webvueblog.github.io/JavaPlusDoc/ 使用docker搭建常用开发环境 docker安装mysql docker ru…...