输出层
现在我们来假想一个英译汉的机器翻译任务。先把 Transformer 模型看作一个整体:假设待翻
译的英文数据是 “Are you OK?”,经过词嵌入和位置编码之后,是一个4*512的张量。通过前面博
客的讲解,我们知道经过解码器之后,输出仍然是一个4*512的张量。我们以推理模型来简单介
绍,输出层的设计。下面我们来看一下啊,transformer中是如何通过线性层和Softmax层实现预测
的?
基于此前已生成的结果,持续推理后续的输出内容。模型逐步生成输出序列的过程中,自回归的
推理模型如图所示。
假设我们的中文词汇表包含 10 个词(对应 10 个索引),整个预测流程如下:
(1)解码器在当前循环输出 1 个 512 维的特征(对应 “下一个要预测的词” 的抽象特征);
(2)这个 512 维特征传入线性层,被映射成 1×10 的张量 —— 张量里的 10 个数值,分别是当前
位置与中文词汇表中 10 个词的 “匹配分数”(也叫 logit);
(3)再将这 1×10 的分数张量传入 Softmax 函数,把分数转换成概率分布(10 个概率的和为 1);
(4)从这 10 个概率中找出数值最大的那个,对应的索引就是当前循环的预测索引;
(5)最后根据这个索引,到中文词汇表里查找对应的汉字,这个字就是当前循环的最终输出。
之后,会把这个已生成的字拼接到解码器的输入中,进入下一次循环,预测下一个词,直到生成结
束符(<end>)为止。
import torch import torch.nn as nn import torch.nn.functional as F # -------------------------- 1. 实现 Transformer 最后两层(线性层 + Softmax)-------------------------- class TransformerFinalLayers(nn.Module): def __init__(self, d_model: int, vocab_size: int): """ Args: d_model: 解码器输出的特征维度(固定512) vocab_size: 中文词汇表大小(这里设为10) """ super().__init__() # 线性层:512维特征 → 10维词汇表分数 self.linear = nn.Linear(d_model, vocab_size) # 参数初始化(保证预测稳定) nn.init.xavier_normal_(self.linear.weight, gain=0.02) nn.init.zeros_(self.linear.bias) def forward(self, decoder_feature: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor, int, str]: """ 前向传播(单个循环,仅输出1个词) Args: decoder_feature: 解码器当前循环的特征 (1, 512) → 1个样本,512维特征 Returns: scores: 线性层输出的分数 (1, 10) probs: Softmax后的概率 (1, 10) pred_idx: 最大概率对应的词索引(整数) pred_word: 最终预测的汉字(字符串) """ # 1. 线性层:高维特征 → 词汇表分数 scores = self.linear(decoder_feature) # (1, 10) # 2. Softmax:分数 → 概率(按最后一维归一化,和为1) probs = F.softmax(scores, dim=-1) # (1, 10) # 3. 取最大概率的索引 pred_idx = torch.argmax(probs, dim=-1).item() # 从张量转成整数索引 return scores, probs, pred_idx # -------------------------- 2. 具体例子验证(自回归单循环)-------------------------- if __name__ == "__main__": # -------------- 步骤1:设置关键参数和词汇表 -------------- d_model = 512 # 解码器特征维度 vocab_size = 10 # 中文词汇表大小 # 中文词汇表:索引→汉字映射(简化版) chinese_vocab = { 0: "<PAD>", # 填充词 1: "你", # 目标预测词(对应英文Are) 2: "好", # 后续循环预测词 3: "吗", # 后续循环预测词 4: "?", # 后续循环预测词 5: "我", # 干扰项 6: "他", # 干扰项 7: "是", # 干扰项 8: "在", # 干扰项 9: "吃" # 干扰项 } # -------------- 步骤2:模拟解码器输入特征(单个循环)-------------- # 自回归某一次循环:解码器输出 (1, 512) 特征(融合了历史生成词+原文信息) torch.manual_seed(42) # 固定种子,结果可复现 decoder_feature = torch.randn(1, d_model) # (1, 512) → 1个样本,512维特征 print("="*60) print("【自回归单循环 - 输入信息】") print(f"解码器当前循环输出特征形状:{decoder_feature.shape} → (1个样本, 512维特征)") print("词汇表:", chinese_vocab) print("="*60) # -------------- 步骤3:初始化最后两层并执行预测 -------------- final_layers = TransformerFinalLayers(d_model=d_model, vocab_size=vocab_size) scores, probs, pred_idx = final_layers(decoder_feature) pred_word = chinese_vocab[pred_idx] # 索引→汉字 # -------------- 步骤4:打印结果(验证流程)-------------- print("\n【预测流程验证】") # 1. 线性层输出(分数) print(f"1. 线性层输出(10个词的匹配分数):") scores_rounded = (torch.round(scores * 10000) / 10000).squeeze().tolist() # 保留4位小数 for idx, score in enumerate(scores_rounded): print(f" 索引{idx}(词:{chinese_vocab[idx]})→ 分数:{score}") # 2. Softmax输出(概率) print(f"\n2. Softmax输出(10个词的概率,和为1):") probs_rounded = (torch.round(probs * 10000) / 10000).squeeze().tolist() # 保留4位小数 total_prob = sum(probs_rounded) # 验证概率和为1 for idx, prob in enumerate(probs_rounded): print(f" 索引{idx}(词:{chinese_vocab[idx]})→ 概率:{prob}") print(f" 概率总和:{round(total_prob, 4)}(理论应为1)") # 3. 最终预测结果 print(f"\n3. 预测结果:") print(f" 最大概率对应的索引:{pred_idx}") print(f" 索引{pred_idx}对应的汉字:{pred_word}") print(f"\n结论:当前循环仅输出1个词 → {pred_word}") print("="*60)
集成化的实现
下面这个是Transformer的整体结构图,我们再来看一下:整体结构还是相当复杂的吧,看着感觉
就很复杂。
但是如果我们可以把编码器和解码器分装起来,整体结构是不是就很简单了,我们把分装起来之后
的模块叫做Transformer 模块。
这样我们就可以把整个Transformer 模型简化这几个模块:
(1)词嵌入层
(2)位置编码
(3)Transformer 模块
(4)全连接层
下面我将会详细介绍每一层的作用:
词嵌入层
首先我们来看一下词嵌入层:
把 “只能看懂数字的模型” 能理解的词索引(离散整数),转换成包含语义信息的高维向量(连续
数值)
import torch import torch.nn as nn # 1. 定义词嵌入层:词汇表大小=3(3个词),每个词转成2维向量(极简维度) embedding = nn.Embedding(num_embeddings=3, embedding_dim=2) # 输入3个词的索引(0、1、2) word_indices = torch.tensor([0, 1, 2], dtype=torch.long) vecs = embedding(word_indices) print(vecs)
一个简单的例子
然后让我们开始写一个简答的例子模拟每一层的输出和输出:
假设我只有一个样本,这个样本是一句话,包括3个词,所以我输入大小应该是(1*3)的
经过词嵌入层,输出(1*3*2)的张量
经过位置编码,输出(1*3*2)的张量
经过Transformer层,输出(1*3*2)的张量
经过全连接层,输出(1*3*3)的张量
import torch import torch.nn as nn # 定义完整的简化版 Transformer(含指定结构) class SimpleTransformer(nn.Module): def __init__(self, vocab_size=3, model_dim=2, max_seq_len=1000): super().__init__() # 1. 词嵌入层 self.embedding = nn.Embedding(num_embeddings=vocab_size, embedding_dim=model_dim) # 2. 位置编码(指定参数化方式) self.positional_encoding = nn.Parameter(torch.zeros(1, max_seq_len, model_dim)) # 3. Transformer 模块(batch_first=True) self.transformer = nn.Transformer( d_model=model_dim, nhead=1, # model_dim=2 仅支持1个注意力头(2÷1=2,整除) num_encoder_layers=1, num_decoder_layers=1, # 显式指定解码器层数(默认和编码器一致) batch_first=True ) # 4. 全连接层 self.fc = nn.Linear(model_dim, vocab_size) def forward(self, x): # 输入 x: (batch_size, seq_len) = (1, 3) batch_size, seq_len = x.shape # -------------------------- 层1:词嵌入层 -------------------------- embed_out = self.embedding(x) # (1, 3, 2) # -------------------------- 层2:位置编码层(叠加) -------------------------- # 只取前 seq_len 个位置的编码(避免超出序列长度) pos_enc = self.positional_encoding[:, :seq_len, :] # (1, 3, 2) pos_embed_out = embed_out + pos_enc # (1, 3, 2) # -------------------------- 层3:Transformer 模块 -------------------------- # 生成解码器掩码(屏蔽未来词) tgt_mask = self.transformer.generate_square_subsequent_mask(seq_len).to(x.device) # Transformer 前向传播(编码器+解码器) transformer_out = self.transformer( src=pos_embed_out, # 编码器输入:带位置的嵌入向量 tgt=pos_embed_out, # 解码器输入:带位置的嵌入向量(自回归场景) tgt_mask=tgt_mask # 解码器掩码 ) # (1, 3, 2) # -------------------------- 层4:全连接层 -------------------------- fc_out = self.fc(transformer_out) # (1, 3, 3) # 返回每一层的输出(用于打印) return { "输入": x, "词嵌入层输出": embed_out, "位置编码叠加后输出": pos_embed_out, "Transformer模块输出": transformer_out, "全连接层输出": fc_out } # -------------------------- 运行示例:打印每一层完整输出 -------------------------- if __name__ == "__main__": # 输入:1个样本,3个词的索引(shape: (1, 3)) input_tensor = torch.tensor([[0, 1, 2]], dtype=torch.long) # 初始化模型 model = SimpleTransformer(vocab_size=3, model_dim=2, max_seq_len=1000) # 前向传播,获取所有层输出 all_outputs = model(input_tensor) # 打印每一层的输出(保留4位小数,修复round报错) print("="*80) for layer_name, output in all_outputs.items(): print(f"\n【{layer_name}】") print(f"形状:{output.shape}") print("具体数值:") # 保留4位小数(兼容所有PyTorch版本) output_rounded = (torch.round(output * 10000) / 10000) print(output_rounded) print("\n" + "="*80)
================================================================================
【输入】
形状:torch.Size([1, 3])
具体数值:
tensor([[0., 1., 2.]])
【词嵌入层输出】
形状:torch.Size([1, 3, 2])
具体数值:
tensor([[[-0.9721, -1.0100],
[ 1.6261, -1.0117],
[-0.3243, 1.1878]]], grad_fn=<DivBackward0>)
【位置编码叠加后输出】
形状:torch.Size([1, 3, 2])
具体数值:
tensor([[[-0.9721, -1.0100],
[ 1.6261, -1.0117],
[-0.3243, 1.1878]]], grad_fn=<DivBackward0>)
【Transformer模块输出】
形状:torch.Size([1, 3, 2])
具体数值:
tensor([[[ 1., -1.],
[ 1., -1.],
[-1., 1.]]], grad_fn=<DivBackward0>)
【全连接层输出】
形状:torch.Size([1, 3, 3])
具体数值:
tensor([[[ 0.9120, 0.3824, -0.5490],
[ 0.9120, 0.3824, -0.5490],
[ 0.0754, 0.6252, -0.2340]]], grad_fn=<DivBackward0>)
================================================================================
完整代码
import torch import torch.nn as nn import torch.optim as optim class TransformerModel(nn.Module): def __init__(self, input_dim, model_dim, num_heads, num_layers, output_dim): super(TransformerModel, self).__init__() self.embedding = nn.Embedding(input_dim, model_dim) self.positional_encoding = nn.Parameter(torch.zeros(1, 1000, model_dim)) # 最大序列长度1000 # 关键修改:开启 batch_first=True,消除警告(输入输出维度变为 (batch_size, seq_len, model_dim)) self.transformer = nn.Transformer( d_model=model_dim, nhead=num_heads, num_encoder_layers=num_layers, batch_first=True # 适配 (batch_size, seq_len, dim) 格式 ) self.fc = nn.Linear(model_dim, output_dim) def forward(self, src, tgt): # 修正后输入维度:(batch_size, seq_len) → 因为 batch_first=True batch_size, src_seq_length = src.size(0), src.size(1) batch_size, tgt_seq_length = tgt.size(0), tgt.size(1) # 词嵌入 + 位置编码 src = self.embedding(src) + self.positional_encoding[:, :src_seq_length, :] # (batch, src_len, model_dim) tgt = self.embedding(tgt) + self.positional_encoding[:, :tgt_seq_length, :] # (batch, tgt_len, model_dim) # 生成解码器的掩码(避免看未来词,必须加!否则训练无效) tgt_mask = self.transformer.generate_square_subsequent_mask(tgt_seq_length).to(src.device) # 前向传播(传入 tgt_mask 屏蔽未来信息) transformer_output = self.transformer( src=src, tgt=tgt, tgt_mask=tgt_mask # 关键:自回归训练必须加掩码 ) output = self.fc(transformer_output) # (batch, tgt_len, output_dim) return output # 超参数 input_dim = 10000 # 词汇表大小 model_dim = 512 # 模型维度 num_heads = 8 # 多头注意力头数(需能被 model_dim 整除:512÷8=64,符合要求) num_layers = 6 # 编码器/解码器层数 output_dim = 10000 # 输出维度(与词汇表大小一致) # 初始化模型、损失函数和优化器 model = TransformerModel(input_dim, model_dim, num_heads, num_layers, output_dim) criterion = nn.CrossEntropyLoss() # 适合分类任务(词预测) optimizer = optim.Adam(model.parameters(), lr=0.001) # 修正输入数据维度:(batch_size, seq_len) → 因为 batch_first=True(原代码是 (seq_len, batch_size),会报错) batch_size = 32 src_seq_len = 10 # 源序列长度(比如英文句子长度) tgt_seq_len = 20 # 目标序列长度(比如中文翻译长度) src = torch.randint(0, input_dim, (batch_size, src_seq_len)) # (32, 10):32个样本,每个10个词索引 tgt = torch.randint(0, input_dim, (batch_size, tgt_seq_len)) # (32, 20):32个样本,每个20个词索引 # 训练步骤 model.train() # 开启训练模式 optimizer.zero_grad() # 清空梯度 output = model(src, tgt) # 前向传播:(32, 20, 10000) # 计算损失:CrossEntropyLoss 要求输入 (batch*seq_len, output_dim),标签 (batch*seq_len) loss = criterion(output.view(-1, output_dim), tgt.view(-1)) # 反向传播 + 参数更新 loss.backward() optimizer.step() print(f"初始训练损失:{loss.item():.4f}") # 输出约 9.3 左右,正常!
详细的输出:
================================================================================
【Transformer 每层输出详情】
================================================================================
1. 原始输入-src:
形状:torch.Size([32, 10])
维度解读:(batch_size, src_seq_len) = (32, 10)
数据类型:torch.int64
1. 原始输入-tgt:
形状:torch.Size([32, 20])
维度解读:(batch_size, tgt_seq_len) = (32, 20)
数据类型:torch.int64
2. 词嵌入层-src输出:
形状:torch.Size([32, 10, 512])
维度解读:(batch_size, src_seq_len, model_dim) = (32, 10, 512)
含义:每个英文词索引→512维语义向量
2. 词嵌入层-tgt输出:
形状:torch.Size([32, 20, 512])
维度解读:(batch_size, tgt_seq_len, model_dim) = (32, 20, 512)
含义:每个中文词索引→512维语义向量
3. 位置编码叠加后-src输出:
形状:torch.Size([32, 10, 512])
维度解读:(32, 10, 512)
含义:语义向量 + 位置信息(保留语义+添加词序)
3. 位置编码叠加后-tgt输出:
形状:torch.Size([32, 20, 512])
维度解读:(32, 20, 512)
含义:语义向量 + 位置信息(保留语义+添加词序)
4. 解码器掩码:
形状:torch.Size([20, 20])
维度解读:(tgt_seq_len, tgt_seq_len) = (20, 20)
含义:下三角掩码,屏蔽未来词(避免解码器作弊)
5. Transformer模块输出:
形状:torch.Size([32, 20, 512])
维度解读:(batch_size, tgt_seq_len, model_dim) = (32, 20, 512)
含义:融合英文全局特征+中文历史特征的512维抽象特征
6. 全连接层输出:
形状:torch.Size([32, 20, 10000])
维度解读:(batch_size, tgt_seq_len, output_dim) = (32, 20, 10000)
含义:每个中文位置→10000个词的匹配分数(logits)
7. 损失计算-输入调整后:
输入形状:torch.Size([640, 10000])
标签形状:torch.Size([640])
维度解读:输入(32×20, 10000)=(640,10000),标签(640)
含义:适配CrossEntropyLoss的输入格式
================================================================================
初始训练损失:9.4007
说明:损失约9.3左右为正常(词汇表10000时,随机猜测损失≈ln(10000)≈9.21)
================================================================================
从0开始手搓Transformer
多头注意力机制
首先假设我的数据大小是32*512的,32代表我的样本数,512是词向量的维度(d_model ),这里我
们使用8个自注意力(n_head)。每个注意力头的维度:512÷8=64(n_d),然后开始计算Q,K,
V。
分割多头后-Q/K/V
Q与K的点积,再除以√n_d(缩放,避免分数过大)
应用掩码后-注意力分数
屏蔽未来词或填充词(分数设为-10000,softmax后权重≈0),之后注意力分数的大小仍然是(8*3*3)
计算注意力权重
softmax归一化(每行和为1,代表关注程度),转换成概率分布,输出的大小是(8*3*3)
加权求和
注意力权重 × V向量(获取关注后的特征)
合并多头
线形层
最后再通过一个线性层,完成最后的输出,最后的输出的大小是,3*512
完整代码
import torch from torch import nn x=torch.rand(128,32,512) d_model=512 n_head=8 import math class MultiHeadAttention(nn.Module): def __init__(self, d_model, n_head): super(MultiHeadAttention, self).__init__() self.n_head = n_head self.d_model = d_model self.w_q = nn.Linear(d_model, d_model) self.w_k = nn.Linear(d_model, d_model) self.w_v = nn.Linear(d_model, d_model) self.w_combine = nn.Linear(d_model, d_model) self.softmax = nn.Softmax(dim=-1) def forward(self, q, k, v, mask=None): batch, time, dimension = q.shape n_d = self.d_model // self.n_head q, k, v = self.w_q(q), self.w_k(k), self.w_v(v) q = q.view(batch, time, self.n_head, n_d).permute(0, 2, 1, 3) k = k.view(batch, time, self.n_head, n_d).permute(0, 2, 1, 3) v = v.view(batch, time, self.n_head, n_d).permute(0, 2, 1, 3) score = q @ k.transpose(2, 3) / math.sqrt(n_d) if mask is not None: score = score.masked_fill(mask == 0, -10000) score = self.softmax(score) @ v score = score.permute(0, 2, 1, 3).contiguous().view(batch, time, dimension) out = self.w_combine(score) return out # 定义模型的维度和头数 d_model = 512 n_head = 8 # 创建多头注意力实例 attention = MultiHeadAttention(d_model, n_head) out=attention(x,x,x) print(out.shape)
位置前馈网络(Position-wise Feed-Forward Network)
由两个全连接层和一个 ReLU 激活函数组成,用于进一步处理注意力机制的输出。
class PositionWiseFeedForward(nn.Module): def __init__(self, d_model, d_ff): super(PositionWiseFeedForward, self).__init__() self.fc1 = nn.Linear(d_model, d_ff) # 第一层全连接 self.fc2 = nn.Linear(d_ff, d_model) # 第二层全连接 self.relu = nn.ReLU() # 激活函数 def forward(self, x): # 前馈网络的计算 return self.fc2(self.relu(self.fc1(x)))
输入形状:torch.Size([3, 512]) 输出形状:torch.Size([3, 512]),这个网络的特征是为了提取特
征,不改变输入的大小。
位置编码
位置编码用于注入输入序列中每个 token 的位置信息。
使用不同频率的正弦和余弦函数来生成位置编码。也是不改变输入的形状。
class PositionalEncoding(nn.Module): def __init__(self, d_model, max_seq_length): super(PositionalEncoding, self).__init__() pe = torch.zeros(max_seq_length, d_model) # 初始化位置编码矩阵 position = torch.arange(0, max_seq_length, dtype=torch.float).unsqueeze(1) div_term = torch.exp(torch.arange(0, d_model, 2).float() * -(math.log(10000.0) / d_model)) pe[:, 0::2] = torch.sin(position * div_term) # 偶数位置使用正弦函数 pe[:, 1::2] = torch.cos(position * div_term) # 奇数位置使用余弦函数 self.register_buffer('pe', pe.unsqueeze(0)) # 注册为缓冲区 def forward(self, x): # 将位置编码添加到输入中 return x + self.pe[:, :x.size(1)]
构建编码器块(Encoder Layer)
包含一个自注意力机制和一个前馈网络,每个子层后接残差连接和层归一化。
class EncoderLayer(nn.Module): def __init__(self, d_model, num_heads, d_ff, dropout): super(EncoderLayer, self).__init__() self.self_attn = MultiHeadAttention(d_model, num_heads) # 自注意力机制 self.feed_forward = PositionWiseFeedForward(d_model, d_ff) # 前馈网络 self.norm1 = nn.LayerNorm(d_model) # 层归一化 self.norm2 = nn.LayerNorm(d_model) self.dropout = nn.Dropout(dropout) # Dropout def forward(self, x, mask): # 自注意力机制 attn_output = self.self_attn(x, x, x, mask) x = self.norm1(x + self.dropout(attn_output)) # 残差连接和层归一化 # 前馈网络 ff_output = self.feed_forward(x) x = self.norm2(x + self.dropout(ff_output)) # 残差连接和层归一化 return x
构建解码器模块
包含一个自注意力机制、一个交叉注意力机制和一个前馈网络,每个子层后接残差连接和层归一化。
class DecoderLayer(nn.Module): def __init__(self, d_model, num_heads, d_ff, dropout): super(DecoderLayer, self).__init__() self.self_attn = MultiHeadAttention(d_model, num_heads) # 自注意力机制 self.cross_attn = MultiHeadAttention(d_model, num_heads) # 交叉注意力机制 self.feed_forward = PositionWiseFeedForward(d_model, d_ff) # 前馈网络 self.norm1 = nn.LayerNorm(d_model) # 层归一化 self.norm2 = nn.LayerNorm(d_model) self.norm3 = nn.LayerNorm(d_model) self.dropout = nn.Dropout(dropout) # Dropout def forward(self, x, enc_output, src_mask, tgt_mask): # 自注意力机制 attn_output = self.self_attn(x, x, x, tgt_mask) x = self.norm1(x + self.dropout(attn_output)) # 残差连接和层归一化 # 交叉注意力机制 attn_output = self.cross_attn(x, enc_output, enc_output, src_mask) x = self.norm2(x + self.dropout(attn_output)) # 残差连接和层归一化 # 前馈网络 ff_output = self.feed_forward(x) x = self.norm3(x + self.dropout(ff_output)) # 残差连接和层归一化 return x
构建完整的 Transformer 模型
使用随机数据训练模型,计算损失并更新参数。
# 超参数 src_vocab_size = 5000 # 源词汇表大小 tgt_vocab_size = 5000 # 目标词汇表大小 d_model = 512 # 模型维度 num_heads = 8 # 注意力头数量 num_layers = 6 # 编码器和解码器层数 d_ff = 2048 # 前馈网络内层维度 max_seq_length = 100 # 最大序列长度 dropout = 0.1 # Dropout 概率 # 初始化模型 transformer = Transformer(src_vocab_size, tgt_vocab_size, d_model, num_heads, num_layers, d_ff, max_seq_length, dropout) # 生成随机数据 src_data = torch.randint(1, src_vocab_size, (64, max_seq_length)) # 源序列 tgt_data = torch.randint(1, tgt_vocab_size, (64, max_seq_length)) # 目标序列 # 定义损失函数和优化器 criterion = nn.CrossEntropyLoss(ignore_index=0) # 忽略填充部分的损失 optimizer = optim.Adam(transformer.parameters(), lr=0.0001, betas=(0.9, 0.98), eps=1e-9) # 训练循环 transformer.train() for epoch in range(100): optimizer.zero_grad() # 清空梯度,防止累积 # 输入目标序列时去掉最后一个词(用于预测下一个词) output = transformer(src_data, tgt_data[:, :-1]) # 计算损失时,目标序列从第二个词开始(即预测下一个词) # output形状: (batch_size, seq_length-1, tgt_vocab_size) # 目标形状: (batch_size, seq_length-1) loss = criterion( output.contiguous().view(-1, tgt_vocab_size), tgt_data[:, 1:].contiguous().view(-1) ) loss.backward() # 反向传播 optimizer.step() # 更新参数 print(f"Epoch: {epoch+1}, Loss: {loss.item()}")
模型评估
评估过程:在验证数据上计算损失,评估模型性能。
transformer.eval() # 生成验证数据 val_src_data = torch.randint(1, src_vocab_size, (64, max_seq_length)) val_tgt_data = torch.randint(1, tgt_vocab_size, (64, max_seq_length)) # 假设输入为一批英文和对应的中文翻译(已转换为索引) # 示例数据: # src_data: [[3, 14, 25, ..., 0, 0], ...] # 英文句子(0为填充符) # tgt_data: [[5, 20, 36, ..., 0, 0], ...] # 中文翻译(0为填充符) # 注意:实际应用中需对文本进行分词、编码、填充等预处理 with torch.no_grad(): val_output = transformer(val_src_data, val_tgt_data[:, :-1]) val_loss = criterion(val_output.contiguous().view(-1, tgt_vocab_size), val_tgt_data[:, 1:].contiguous().view(-1)) print(f"Validation Loss: {val_loss.item()}")
参考文献: