找回密码
 会员注册
查看: 18|回复: 0

Python用pytorch从头写Transformer源码,一行一解释;机器翻译实例代码;Transformer源码解读与实战

[复制链接]

2万

主题

0

回帖

7万

积分

超级版主

积分
71769
发表于 2024-9-9 10:08:36 | 显示全部楼层 |阅读模式
1.Transformer简介        Transformer模型被提出的主要动机是解决序列处理任务的长距离依赖问题。传统的RNN和LSTM虽然能够处理序列任务,但因为它们在处理序列时需要一步步前进,因此其他信息无法立即对其产生影响,当序列变长时,长距离依赖的信息很可能会被丢失。为了解决这个问题,Transformer模型被设计出来,内核思想是利用自注意力机制,这样模型可以直接对输入序列的任意两个位置建立直接连接,并且可以并行处理序列信息。        Transformer模型的主要组成部分为Encoder(编码器)和Decoder(解码器),每个部分都由若干个相同的层堆叠而成。这些层的主要操作就是“自注意力机制(Self-Attention)”和“前馈全连接网络”。        Encoder负责把输入语句映射成一个连续向量,这个向量包函了输入语句的所有信息。具体来说,编码器的每一层都包含两个子层:Self-Attention层和FeedForward层。每个子层后面都配有一个残差连接和归一化层。        Decoder的结构与Encoder类似,但额外增加了一个使用Encoder的输出来进行“编码器-解码器注意力”操作的子层。解码器的每一层都包含三个子层:“自注意力机制”,“编码器-解码器注意力”和一个“前馈全连接网络”。Decoder在生成输出序列的过程中,自注意力层仅仅允许关注到先前位置的信息,防止关注到未来位置的信息。 2.Transformer代码实战2.1定义缩放点积注意力类importnumpyasnp#导入numpy库importtorch#导入torch库importtorch.nnasnn#导入torch.nn库d_k=64#K(=Q)维度d_v=64#V维度#定义缩放点积注意力类classScaledDotProductAttention(nn.Module):def__init__(self):super(ScaledDotProductAttention,self).__init__()defforward(self,Q,K,V,attn_mask):#-------------------------维度信息--------------------------------#QKV[batch_size,n_heads,len_q/k/v,dim_q=k/v](dim_q=dim_k)#attn_mask[batch_size,n_heads,len_q,len_k]#----------------------------------------------------------------#计算注意力分数(原始权重)[batch_size,n_heads,len_q,len_k]scores=torch.matmul(Q,K.transpose(-1,-2))/np.sqrt(d_k)#-------------------------维度信息--------------------------------#scores[batch_size,n_heads,len_q,len_k]#-----------------------------------------------------------------#使用注意力掩码,将attn_mask中值为1的位置的权重替换为极小值#-------------------------维度信息--------------------------------#attn_mask[batch_size,n_heads,len_q,len_k],形状和scores相同#-----------------------------------------------------------------scores.masked_fill_(attn_mask,-1e9)#对注意力分数进行softmax归一化weights=nn.Softmax(dim=-1)(scores)#-------------------------维度信息--------------------------------#weights[batch_size,n_heads,len_q,len_k],形状和scores相同#-----------------------------------------------------------------#计算上下文向量(也就是注意力的输出),是上下文信息的紧凑表示context=torch.matmul(weights,V)#-------------------------维度信息--------------------------------#context[batch_size,n_heads,len_q,dim_v]#-----------------------------------------------------------------returncontext,weights#返回上下文向量和注意力分数2.2定义多头自注意力类#定义多头自注意力类d_embedding=512#Embedding的维度n_heads=8#Multi-HeadAttention中头的个数batch_size=3#每一批的数据大小classMultiHeadAttention(nn.Module):def__init__(self):super(MultiHeadAttention,self).__init__()self.W_Q=nn.Linear(d_embedding,d_k*n_heads)#Q的线性变换层self.W_K=nn.Linear(d_embedding,d_k*n_heads)#K的线性变换层self.W_V=nn.Linear(d_embedding,d_v*n_heads)#V的线性变换层self.linear=nn.Linear(n_heads*d_v,d_embedding)self.layer_norm=nn.LayerNorm(d_embedding)defforward(self,Q,K,V,attn_mask):#-------------------------维度信息--------------------------------#QKV[batch_size,len_q/k/v,embedding_dim]#-----------------------------------------------------------------residual,batch_size=Q,Q.size(0)#保留残差连接#将输入进行线性变换和重塑,以便后续处理q_s=self.W_Q(Q).view(batch_size,-1,n_heads,d_k).transpose(1,2)k_s=self.W_K(K).view(batch_size,-1,n_heads,d_k).transpose(1,2)v_s=self.W_V(V).view(batch_size,-1,n_heads,d_v).transpose(1,2)#-------------------------维度信息--------------------------------#q_sk_sv_s:[batch_size,n_heads,len_q/k/v,d_q=k/v]#-----------------------------------------------------------------#将注意力掩码复制到多头attn_mask:[batch_size,n_heads,len_q,len_k]attn_mask=attn_mask.unsqueeze(1).repeat(1,n_heads,1,1)#-------------------------维度信息--------------------------------#attn_mask[batch_size,n_heads,len_q,len_k]#-----------------------------------------------------------------#使用缩放点积注意力计算上下文和注意力权重context,weights=ScaledDotProductAttention()(q_s,k_s,v_s,attn_mask)#-------------------------维度信息--------------------------------#context[batch_size,n_heads,len_q,dim_v]#weights[batch_size,n_heads,len_q,len_k]#-----------------------------------------------------------------#通过调整维度将多个头的上下文向量连接在一起context=context.transpose(1,2).contiguous().view(batch_size,-1,n_heads*d_v)#-------------------------维度信息--------------------------------#context[batch_size,len_q,n_heads*dim_v]#-----------------------------------------------------------------#用一个线性层把连接后的多头自注意力结果转换,原始地嵌入维度output=self.linear(context)#-------------------------维度信息--------------------------------#output[batch_size,len_q,embedding_dim]#-----------------------------------------------------------------#与输入(Q)进行残差链接,并进行层归一化后输出output=self.layer_norm(output+residual)#-------------------------维度信息--------------------------------#output[batch_size,len_q,embedding_dim]#-----------------------------------------------------------------returnoutput,weights#返回层归一化的输出和注意力权重2.3定义逐位置前馈网络类#定义逐位置前馈网络类classPoswiseFeedForwardNet(nn.Module):def__init__(self,d_ff=2048):super(PoswiseFeedForwardNet,self).__init__()#定义一维卷积层1,用于将输入映射到更高维度self.conv1=nn.Conv1d(in_channels=d_embedding,out_channels=d_ff,kernel_size=1)#定义一维卷积层2,用于将输入映射回原始维度self.conv2=nn.Conv1d(in_channels=d_ff,out_channels=d_embedding,kernel_size=1)#定义层归一化self.layer_norm=nn.LayerNorm(d_embedding)defforward(self,inputs):#-------------------------维度信息--------------------------------#inputs[batch_size,len_q,embedding_dim]#----------------------------------------------------------------residual=inputs#保留残差连接#在卷积层1后使用ReLU激活函数output=nn.ReLU()(self.conv1(inputs.transpose(1,2)))#-------------------------维度信息--------------------------------#output[batch_size,d_ff,len_q]#----------------------------------------------------------------#使用卷积层2进行降维output=self.conv2(output).transpose(1,2)#-------------------------维度信息--------------------------------#output[batch_size,len_q,embedding_dim]#----------------------------------------------------------------#与输入进行残差链接,并进行层归一化output=self.layer_norm(output+residual)#-------------------------维度信息--------------------------------#output[batch_size,len_q,embedding_dim]#----------------------------------------------------------------returnoutput#返回加入残差连接后层归一化的结果2.4生成正弦位置编码表的函数,用于在Transformer中引入位置信息 #生成正弦位置编码表的函数,用于在Transformer中引入位置信息defget_sin_enc_table(n_position,embedding_dim):#-------------------------维度信息--------------------------------#n_position:输入序列的最大长度#embedding_dim:词嵌入向量的维度#-----------------------------------------------------------------#根据位置和维度信息,初始化正弦位置编码表sinusoid_table=np.zeros((n_position,embedding_dim))#遍历所有位置和维度,计算角度值forpos_iinrange(n_position):forhid_jinrange(embedding_dim):angle=pos_i/np.power(10000,2*(hid_j//2)/embedding_dim)sinusoid_table[pos_i,hid_j]=angle#计算正弦和余弦值sinusoid_table[:,0::2]=np.sin(sinusoid_table[:,0::2])#dim2i偶数维sinusoid_table[:,1::2]=np.cos(sinusoid_table[:,1::2])#dim2i+1奇数维#-------------------------维度信息--------------------------------#sinusoid_table的维度是[n_position,embedding_dim]#----------------------------------------------------------------returntorch.FloatTensor(sinusoid_table)#返回正弦位置编码表'运行运行2.5定义填充注意力掩码函数#定义填充注意力掩码函数defget_attn_pad_mask(seq_q,seq_k):#-------------------------维度信息--------------------------------#seq_q的维度是[batch_size,len_q]#seq_k的维度是[batch_size,len_k]#-----------------------------------------------------------------batch_size,len_q=seq_q.size()batch_size,len_k=seq_k.size()#生成布尔类型张量pad_attn_mask=seq_k.data.eq(0).unsqueeze(1)# token的编码值为0#-------------------------维度信息--------------------------------#pad_attn_mask的维度是[batch_size,1,len_k]#-----------------------------------------------------------------#变形为与注意力分数相同形状的张量pad_attn_mask=pad_attn_mask.expand(batch_size,len_q,len_k)#-------------------------维度信息--------------------------------#pad_attn_mask的维度是[batch_size,len_q,len_k]#-----------------------------------------------------------------returnpad_attn_mask'运行运行2.6定义编码器层类#定义编码器层类classEncoderLayer(nn.Module):def__init__(self):super(EncoderLayer,self).__init__()self.enc_self_attn=MultiHeadAttention()#多头自注意力层self.pos_ffn=PoswiseFeedForwardNet()#位置前馈神经网络层defforward(self,enc_inputs,enc_self_attn_mask):#-------------------------维度信息--------------------------------#enc_inputs的维度是[batch_size,seq_len,embedding_dim]#enc_self_attn_mask的维度是[batch_size,seq_len,seq_len]#-----------------------------------------------------------------#将相同的Q,K,V输入多头自注意力层,返回的attn_weights增加了头数enc_outputs,attn_weights=self.enc_self_attn(enc_inputs,enc_inputs,enc_inputs,enc_self_attn_mask)#-------------------------维度信息--------------------------------#enc_outputs的维度是[batch_size,seq_len,embedding_dim]#attn_weights的维度是[batch_size,n_heads,seq_len,seq_len]#将多头自注意力outputs输入位置前馈神经网络层enc_outputs=self.pos_ffn(enc_outputs)#维度与enc_inputs相同#-------------------------维度信息--------------------------------#enc_outputs的维度是[batch_size,seq_len,embedding_dim]#-----------------------------------------------------------------returnenc_outputs,attn_weights#返回编码器输出和每层编码器注意力权重2.7定义编码器类#定义编码器类n_layers=6#设置Encoder的层数classEncoder(nn.Module):def__init__(self,corpus):super(Encoder,self).__init__()self.src_emb=nn.Embedding(len(corpus.src_vocab),d_embedding)#词嵌入层self.pos_emb=nn.Embedding.from_pretrained(\get_sin_enc_table(corpus.src_len+1,d_embedding),freeze=True)#位置嵌入层self.layers=nn.ModuleList(EncoderLayer()for_inrange(n_layers))#编码器层数defforward(self,enc_inputs):#-------------------------维度信息--------------------------------#enc_inputs的维度是[batch_size,source_len]#-----------------------------------------------------------------#创建一个从1到source_len的位置索引序列pos_indices=torch.arange(1,enc_inputs.size(1)+1).unsqueeze(0).to(enc_inputs)#-------------------------维度信息--------------------------------#pos_indices的维度是[1,source_len]#-----------------------------------------------------------------#对输入进行词嵌入和位置嵌入相加[batch_size,source_len,embedding_dim]enc_outputs=self.src_emb(enc_inputs)+self.pos_emb(pos_indices)#-------------------------维度信息--------------------------------#enc_outputs的维度是[batch_size,seq_len,embedding_dim]#-----------------------------------------------------------------#生成自注意力掩码enc_self_attn_mask=get_attn_pad_mask(enc_inputs,enc_inputs)#-------------------------维度信息--------------------------------#enc_self_attn_mask的维度是[batch_size,len_q,len_k]#-----------------------------------------------------------------enc_self_attn_weights=[]#初始化enc_self_attn_weights#通过编码器层[batch_size,seq_len,embedding_dim]forlayerinself.layers:enc_outputs,enc_self_attn_weight=layer(enc_outputs,enc_self_attn_mask)enc_self_attn_weights.append(enc_self_attn_weight)#-------------------------维度信息--------------------------------#enc_outputs的维度是[batch_size,seq_len,embedding_dim]维度与enc_inputs相同#enc_self_attn_weights是一个列表,每个元素的维度是[batch_size,n_heads,seq_len,seq_len]#-----------------------------------------------------------------returnenc_outputs,enc_self_attn_weights#返回编码器输出和编码器注意力权重2.8生成后续注意力掩码的函数,用于在多头自注意力计算中忽略未来信息#生成后续注意力掩码的函数,用于在多头自注意力计算中忽略未来信息defget_attn_subsequent_mask(seq):#-------------------------维度信息--------------------------------#seq的维度是[batch_size,seq_len(Q)=seq_len(K)]#-----------------------------------------------------------------#获取输入序列的形状attn_shape=[seq.size(0),seq.size(1),seq.size(1)]#-------------------------维度信息--------------------------------#attn_shape是一个一维张量[batch_size,seq_len(Q),seq_len(K)]#-----------------------------------------------------------------#使用numpy创建一个上三角矩阵(triu=triangleupper)subsequent_mask=np.triu(np.ones(attn_shape),k=1)#-------------------------维度信息--------------------------------#subsequent_mask的维度是[batch_size,seq_len(Q),seq_len(K)]#-----------------------------------------------------------------#将numpy数组转换为PyTorch张量,并将数据类型设置为byte(布尔值)subsequent_mask=torch.from_numpy(subsequent_mask).byte()#-------------------------维度信息--------------------------------#返回的subsequent_mask的维度是[batch_size,seq_len(Q),seq_len(K)]#-----------------------------------------------------------------returnsubsequent_mask#返回后续位置的注意力掩码'运行运行2.9定义解码器层类#定义解码器层类classDecoderLayer(nn.Module):def__init__(self):super(DecoderLayer,self).__init__()self.dec_self_attn=MultiHeadAttention()#多头自注意力层self.dec_enc_attn=MultiHeadAttention()#多头自注意力层,连接编码器和解码器self.pos_ffn=PoswiseFeedForwardNet()#位置前馈神经网络层defforward(self,dec_inputs,enc_outputs,dec_self_attn_mask,dec_enc_attn_mask):#-------------------------维度信息--------------------------------#dec_inputs的维度是[batch_size,target_len,embedding_dim]#enc_outputs的维度是[batch_size,source_len,embedding_dim]#dec_self_attn_mask的维度是[batch_size,target_len,target_len]#dec_enc_attn_mask的维度是[batch_size,target_len,source_len]#-----------------------------------------------------------------#将相同的Q,K,V输入多头自注意力层dec_outputs,dec_self_attn=self.dec_self_attn(dec_inputs,dec_inputs,dec_inputs,dec_self_attn_mask)#-------------------------维度信息--------------------------------#dec_outputs的维度是[batch_size,target_len,embedding_dim]#dec_self_attn的维度是[batch_size,n_heads,target_len,target_len]#-----------------------------------------------------------------#将解码器输出和编码器输出输入多头自注意力层dec_outputs,dec_enc_attn=self.dec_enc_attn(dec_outputs,enc_outputs,enc_outputs,dec_enc_attn_mask)#-------------------------维度信息--------------------------------#dec_outputs的维度是[batch_size,target_len,embedding_dim]#dec_enc_attn的维度是[batch_size,n_heads,target_len,source_len]#-----------------------------------------------------------------#输入位置前馈神经网络层dec_outputs=self.pos_ffn(dec_outputs)#-------------------------维度信息--------------------------------#dec_outputs的维度是[batch_size,target_len,embedding_dim]#dec_self_attn的维度是[batch_size,n_heads,target_len,target_len]#dec_enc_attn的维度是[batch_size,n_heads,target_len,source_len]#-----------------------------------------------------------------#返回解码器层输出,每层的自注意力和解-编码器注意力权重returndec_outputs,dec_self_attn,dec_enc_attn2.10定义解码器类#定义解码器类n_layers=6#设置Decoder的层数classDecoder(nn.Module):def__init__(self,corpus):super(Decoder,self).__init__()self.tgt_emb=nn.Embedding(len(corpus.tgt_vocab),d_embedding)#词嵌入层self.pos_emb=nn.Embedding.from_pretrained(\get_sin_enc_table(corpus.tgt_len+1,d_embedding),freeze=True)#位置嵌入层self.layers=nn.ModuleList([DecoderLayer()for_inrange(n_layers)])#叠加多层defforward(self,dec_inputs,enc_inputs,enc_outputs):#-------------------------维度信息--------------------------------#dec_inputs的维度是[batch_size,target_len]#enc_inputs的维度是[batch_size,source_len]#enc_outputs的维度是[batch_size,source_len,embedding_dim]#-----------------------------------------------------------------#创建一个从1到source_len的位置索引序列pos_indices=torch.arange(1,dec_inputs.size(1)+1).unsqueeze(0).to(dec_inputs)#-------------------------维度信息--------------------------------#pos_indices的维度是[1,target_len]#-----------------------------------------------------------------#对输入进行词嵌入和位置嵌入相加dec_outputs=self.tgt_emb(dec_inputs)+self.pos_emb(pos_indices)#-------------------------维度信息--------------------------------#dec_outputs的维度是[batch_size,target_len,embedding_dim]#-----------------------------------------------------------------#生成解码器自注意力掩码和解码器-编码器注意力掩码dec_self_attn_pad_mask=get_attn_pad_mask(dec_inputs,dec_inputs)#填充位掩码dec_self_attn_subsequent_mask=get_attn_subsequent_mask(dec_inputs)#后续位掩码dec_self_attn_mask=torch.gt((dec_self_attn_pad_mask\+dec_self_attn_subsequent_mask),0)dec_enc_attn_mask=get_attn_pad_mask(dec_inputs,enc_inputs)#解码器-编码器掩码#-------------------------维度信息--------------------------------#dec_self_attn_pad_mask的维度是[batch_size,target_len,target_len]#dec_self_attn_subsequent_mask的维度是[batch_size,target_len,target_len]#dec_self_attn_mask的维度是[batch_size,target_len,target_len]#dec_enc_attn_mask的维度是[batch_size,target_len,source_len]#-----------------------------------------------------------------dec_self_attns,dec_enc_attns=[],[]#初始化dec_self_attns,dec_enc_attns#通过解码器层[batch_size,seq_len,embedding_dim]forlayerinself.layers:dec_outputs,dec_self_attn,dec_enc_attn=layer(dec_outputs,enc_outputs,dec_self_attn_mask,dec_enc_attn_mask)dec_self_attns.append(dec_self_attn)dec_enc_attns.append(dec_enc_attn)#-------------------------维度信息--------------------------------#dec_outputs的维度是[batch_size,target_len,embedding_dim]#dec_self_attns是一个列表,每个元素的维度是[batch_size,n_heads,target_len,target_len]#dec_enc_attns是一个列表,每个元素的维度是[batch_size,n_heads,target_len,source_len]#-----------------------------------------------------------------#返回解码器输出,解码器自注意力和解码器-编码器注意力权重returndec_outputs,dec_self_attns,dec_enc_attns2.11定义Transformer模型#定义Transformer模型classTransformer(nn.Module):def__init__(self,corpus):super(Transformer,self).__init__()self.encoder=Encoder(corpus)#初始化编码器实例self.decoder=Decoder(corpus)#初始化解码器实例#定义线性投影层,将解码器输出转换为目标词汇表大小的概率分布self.projection=nn.Linear(d_embedding,len(corpus.tgt_vocab),bias=False)defforward(self,enc_inputs,dec_inputs):#-------------------------维度信息--------------------------------#enc_inputs的维度是[batch_size,source_seq_len]#dec_inputs的维度是[batch_size,target_seq_len]#-----------------------------------------------------------------#将输入传递给编码器,并获取编码器输出和自注意力权重enc_outputs,enc_self_attns=self.encoder(enc_inputs)#-------------------------维度信息--------------------------------#enc_outputs的维度是[batch_size,source_len,embedding_dim]#enc_self_attns是一个列表,每个元素的维度是[batch_size,n_heads,src_seq_len,src_seq_len]#-----------------------------------------------------------------#将编码器输出、解码器输入和编码器输入传递给解码器#获取解码器输出、解码器自注意力权重和编码器-解码器注意力权重dec_outputs,dec_self_attns,dec_enc_attns=self.decoder(dec_inputs,enc_inputs,enc_outputs)#-------------------------维度信息--------------------------------#dec_outputs的维度是[batch_size,target_len,embedding_dim]#dec_self_attns是一个列表,每个元素的维度是[batch_size,n_heads,tgt_seq_len,src_seq_len]#dec_enc_attns是一个列表,每个元素的维度是[batch_size,n_heads,tgt_seq_len,src_seq_len]#-----------------------------------------------------------------#将解码器输出传递给投影层,生成目标词汇表大小的概率分布dec_logits=self.projection(dec_outputs)#-------------------------维度信息--------------------------------#dec_logits的维度是[batch_size,tgt_seq_len,tgt_vocab_size]#-----------------------------------------------------------------#返回逻辑值(原始预测结果),编码器自注意力权重,解码器自注意力权重,解-编码器注意力权重returndec_logits,enc_self_attns,dec_self_attns,dec_enc_attns2.12实战用例,机器翻译sentences=[['咖哥喜欢小冰','KaGelikesXiaoBing'],['我爱学习人工智能','IlovestudyingAI'],['深度学习改变世界','DLchangedtheworld'],['自然语言处理很强大','NLPispowerful'],['神经网络非常复杂','Neural-networksarecomplex']]'运行运行2.13定义用例的类fromcollectionsimportCounter#导入Counter类#定义TranslationCorpus类classTranslationCorpus:def__init__(self,sentences):self.sentences=sentences#计算源语言和目标语言的最大句子长度,并分别加1和2以容纳填充符和特殊符号self.src_len=max(len(sentence[0].split())forsentenceinsentences)+1self.tgt_len=max(len(sentence[1].split())forsentenceinsentences)+2#创建源语言和目标语言的词汇表self.src_vocab,self.tgt_vocab=self.create_vocabularies()#创建索引到单词的映射self.src_idx2word={v:kfork,vinself.src_vocab.items()}self.tgt_idx2word={v:kfork,vinself.tgt_vocab.items()}#定义创建词汇表的函数defcreate_vocabularies(self):#统计源语言和目标语言的单词频率src_counter=Counter(wordforsentenceinself.sentencesforwordinsentence[0].split())tgt_counter=Counter(wordforsentenceinself.sentencesforwordinsentence[1].split())#创建源语言和目标语言的词汇表,并为每个单词分配一个唯一的索引src_vocab={' ':0,**{word:i+1fori,wordinenumerate(src_counter)}}tgt_vocab={' ':0,'':1,'':2,**{word:i+3fori,wordinenumerate(tgt_counter)}}returnsrc_vocab,tgt_vocab#定义创建批次数据的函数defmake_batch(self,batch_size,test_batch=False):input_batch,output_batch,target_batch=[],[],[]#随机选择句子索引sentence_indices=torch.randperm(len(self.sentences))[:batch_size]forindexinsentence_indices:src_sentence,tgt_sentence=self.sentences[index]#将源语言和目标语言的句子转换为索引序列src_seq=[self.src_vocab[word]forwordinsrc_sentence.split()]tgt_seq=[self.tgt_vocab['']]+[self.tgt_vocab[word]\forwordintgt_sentence.split()]+[self.tgt_vocab['']]#对源语言和目标语言的序列进行填充src_seq+=[self.src_vocab[' ']]*(self.src_len-len(src_seq))tgt_seq+=[self.tgt_vocab[' ']]*(self.tgt_len-len(tgt_seq))#将处理好的序列添加到批次中input_batch.append(src_seq)output_batch.append([self.tgt_vocab['']]+([self.tgt_vocab[' ']]*\(self.tgt_len-2))iftest_batchelsetgt_seq[:-1])target_batch.append(tgt_seq[1:])#将批次转换为LongTensor类型input_batch=torch.LongTensor(input_batch)output_batch=torch.LongTensor(output_batch)target_batch=torch.LongTensor(target_batch)returninput_batch,output_batch,target_batch#创建语料库类实例corpus=TranslationCorpus(sentences)2.14开始训练importtorch#导入torchimporttorch.optimasoptim#导入优化器model=Transformer(corpus)#创建模型实例criterion=nn.CrossEntropyLoss()#损失函数optimizer=optim.Adam(model.parameters(),lr=0.0001)#优化器epochs=5#训练轮次forepochinrange(epochs):#训练100轮optimizer.zero_grad()#梯度清零enc_inputs,dec_inputs,target_batch=corpus.make_batch(batch_size)#创建训练数据outputs,_,_,_=model(enc_inputs,dec_inputs)#获取模型输出loss=criterion(outputs.view(-1,len(corpus.tgt_vocab)),target_batch.view(-1))#计算损失if(epoch+1)%1==0:#打印损失print(f"Epoch:{epoch+1:04d}cost={loss:.6f}")loss.backward()#反向传播optimizer.step()#更新参数2.15模型测试#创建一个大小为1的批次,目标语言序列dec_inputs在测试阶段,仅包含句子开始符号enc_inputs,dec_inputs,target_batch=corpus.make_batch(batch_size=1,test_batch=True)print("编码器输入:",enc_inputs)#打印编码器输入print("解码器输入:",dec_inputs)#打印解码器输入print("目标数据:",target_batch)#打印目标数据predict,enc_self_attns,dec_self_attns,dec_enc_attns=model(enc_inputs,dec_inputs)#用模型进行翻译predict=predict.view(-1,len(corpus.tgt_vocab))#将预测结果维度重塑predict=predict.data.max(1,keepdim=True)[1]#找到每个位置概率最大的词汇的索引#解码预测的输出,将所预测的目标句子中的索引转换为单词translated_sentence=[corpus.tgt_idx2word[idx.item()]foridxinpredict.squeeze()]#将输入的源语言句子中的索引转换为单词input_sentence=''.join([corpus.src_idx2word[idx.item()]foridxinenc_inputs[0]])print(input_sentence,'->',translated_sentence)#打印原始句子和翻译后的句子3.总结        Transformer的核心思想是完全基于注意力机制来建模序列间的依赖关系,避免了传统循环神经网络(RNN)的顺序计算限制,能够并行处理序列中的所有位置。这种机制使得Transformer在训练和推理时的效率都比传统的循环神经网络更高。        Transformer由两个重要的组件组成:编码器(Encoder)和解码器(Decoder)。编码器负责将输入序列转换为更丰富的特征表示,而解码器则基于编码器的输出生成目标序列。        每个编码器和解码器都由多个相同的层堆叠而成。每个层包含两个子层,即多头自注意力机制和前馈神经网络。自注意力机制能够学习到序列中各个位置的重要程度,而前馈神经网络则负责对特征进行非线性转换。        在自注意力机制中,Transformer使用了多头注意力机制来处理多个不同位置的表示。每个头都会学习到不同的上下文关系,然后将它们组合起来得到最终的表示。这种并行计算的方式使得模型能够更好地捕捉到序列中的长距离依赖关系。        除了自注意力机制和前馈神经网络,Transformer还引入了一些关键的技术来增强模型性能,如层归一化、残差连接和位置编码等。位置编码用于将序列中的单词位置信息嵌入到模型中,以保持序列中不同位置的相对距离。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 会员注册

本版积分规则

QQ|手机版|心飞设计-版权所有:微度网络信息技术服务中心 ( 鲁ICP备17032091号-12 )|网站地图

GMT+8, 2025-1-10 13:50 , Processed in 0.456717 second(s), 26 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表