资讯动态

Keras-Transformer中英翻译源码实战:从训练到推理避坑指南

发布时间:2026/10/8 11:26:14 来源:尧图企业网站定制
简介这份资源是一套基于Python实现的中英文机器翻译项目采用Keras-Transformer模型架构面向计算机、人工智能、通信等专业的学生与从业者可作为毕业设计、课程大作业或期末课设的参考方案也适合想入门神经机器翻译的小白学习与进阶。压缩包共17个文件约7.42MB包含3个py源码、2个ipynb交互式笔记本、1个h5模型权重、6个pkl序列化数据、1个md说明文档及txt语料等覆盖数据预处理、词表构建、模型训练与翻译推理的完整链路。项目已通过调试测试可直接运行答辩评审分达98分代码结构清晰、注释完整便于读者理解Transformer在翻译任务中的编码器-解码器实现细节。目前已有104人学习下载适合在此基础上修改调整扩展不同语向或功能。1. 一份能直接跑的 Keras-Transformer 中英翻译源码到底值不值得拆如果你正在做毕业设计选题是“基于 Python 的机器翻译”大概率会遇到一个尴尬局面Transformer 的论文看懂了注意力公式也能默写但真要从零搭一个能跑通、能收敛、能翻译出人话的模型中间隔着一堆说不清的细节。这份基于 Keras 的 Transformer 中英翻译源码解决的就是这个断层——它把编码器、解码器、多头注意力、位置编码、训练循环、推理解码全部串成一条可执行的链路配文档说明拿到手就能跑。适合谁适合需要一份完整基线做二次开发的学生也适合想用 Keras 而非 PyTorch 复现 Transformer 的工程师。我拆过不少“毕业设计级”的翻译项目多数死在数据预处理和推理阶段这份的完整度算中上值得花时间过一遍。2. 先看清骨架Keras-Transformer 的模块划分与数据流2.1 编码器-解码器结构在 Keras 里怎么落地Transformer 的核心是“堆叠 残差 层归一化”但 Keras 的实现方式和 PyTorch 有明显差异。这份源码用的是 Keras 函数式 API 加自定义层的方式而不是tf.keras.Sequential硬拼。编码器由 N 个EncoderLayer组成每层内部依次是多头自注意力、残差连接、层归一化、前馈网络、再残差再归一化。解码器多了一个交叉注意力子层用来对齐源语言和目标语言的表示。我一般会先确认三件事位置编码是加在嵌入之后还是拼在输入里、注意力掩码是加性掩码还是布尔掩码、层归一化放在残差前还是残差后。这份源码用的是“Post-LN”结构也就是先残差再归一化和原始论文一致。位置编码用的是正弦余弦函数不是可学习嵌入这意味着推理时对序列长度的外推能力稍好但训练时对位置信息的拟合依赖数据量。# 编码器层核心结构基于源码整理 class EncoderLayer(tf.keras.layers.Layer): def __init__(self, d_model, num_heads, dff, rate0.1): super(EncoderLayer, self).__init__() self.mha MultiHeadAttention(d_model, num_heads) # 多头自注意力 self.ffn point_wise_feed_forward_network(d_model, dff) # 前馈网络 self.layernorm1 tf.keras.layers.LayerNormalization(epsilon1e-6) self.layernorm2 tf.keras.layers.LayerNormalization(epsilon1e-6) self.dropout1 tf.keras.layers.Dropout(rate) self.dropout2 tf.keras.layers.Dropout(rate) def call(self, x, training, mask): attn_output, _ self.mha(x, x, x, mask) # 自注意力QKV attn_output self.dropout1(attn_output, trainingtraining) out1 self.layernorm1(x attn_output) # 残差 归一化 ffn_output self.ffn(out1) ffn_output self.dropout2(ffn_output, trainingtraining) out2 self.layernorm2(out1 ffn_output) # 第二次残差 归一化 return out2这段代码里最容易被忽略的是mask的传递。编码器需要的是填充掩码用来屏蔽 padding 位置解码器需要的是前瞻掩码加填充掩码的组合。源码里把掩码生成单独抽成了create_padding_mask和create_look_ahead_mask两个函数调用时再合并。参数d_model是嵌入维度num_heads是头数dff是前馈网络中间层维度。常见配置是d_model512, num_heads8, dff2048但毕业设计场景下如果显存不够可以降到d_model256, num_heads4, dff1024翻译质量会降但能跑起来。2.2 数据预处理中英文分词与词表构建的取舍中英文翻译的数据预处理比单语任务麻烦因为中文需要分词英文需要子词切分。这份源码用的是jieba做中文分词英文用简单的空格加标点切分然后统一转小写。词表构建用的是tf.keras.preprocessing.text.Tokenizer分别对源语言和目标语言各建一个词表保留频率最高的若干词其余归入unk。这里有个关键参数MAX_LENGTH。源码里默认设的是 40意思是超过 40 个 token 的句子会被截断。中英翻译的句子长度分布差异很大中文 20 个字翻成英文可能变成 15 个词也可能变成 30 个词。我建议先统计一下训练集里源句和目标句的长度分位数把MAX_LENGTH设成覆盖 95% 样本的值而不是拍脑袋定 40。# 数据预处理与词表构建基于源码整理 import jieba from tensorflow.keras.preprocessing.text import Tokenizer from tensorflow.keras.preprocessing.sequence import pad_sequences def preprocess_cn(text): return .join(jieba.cut(text.strip())) # 中文分词后用空格连接 def preprocess_en(text): text text.lower().strip() # 简单标点分离实际项目可换用 subword 方案 for p in ,.!?;:: text text.replace(p, f {p} ) return .join(text.split()) # 构建词表 tokenizer_src Tokenizer(num_words8000, filters, oov_tokenunk) tokenizer_tgt Tokenizer(num_words8000, filters, oov_tokenunk) tokenizer_src.fit_on_texts(src_sentences) tokenizer_tgt.fit_on_texts(tgt_sentences) # 转序列并填充 src_seq tokenizer_src.texts_to_sequences(src_sentences) tgt_seq tokenizer_tgt.texts_to_sequences(tgt_sentences) src_padded pad_sequences(src_seq, maxlenMAX_LENGTH, paddingpost) tgt_padded pad_sequences(tgt_seq, maxlenMAX_LENGTH, paddingpost)num_words8000是个经验值中英翻译任务如果训练集只有几万句对8000 词表够用如果数据量到几十万可以提到 16000 或 32000。filters是为了保留标点因为标点对翻译质量有影响默认的 Tokenizer 会把标点过滤掉这点源码里处理得对。paddingpost表示在句子后面补零配合后面的掩码逻辑补零位置会被屏蔽掉。2.3 训练循环与损失函数掩码损失怎么算Keras 的model.fit在这种带掩码的序列到序列任务里不够灵活源码用的是自定义训练循环加tf.GradientTape。损失函数是稀疏分类交叉熵但关键点在于计算损失时要屏蔽掉 padding 位置的贡献否则模型会学会预测 padding浪费容量。# 自定义训练步骤基于源码整理 loss_object tf.keras.losses.SparseCategoricalCrossentropy( from_logitsTrue, reductionnone) def loss_function(real, pred): mask tf.math.logical_not(tf.math.equal(real, 0)) # 屏蔽 padding loss_ loss_object(real, pred) mask tf.cast(mask, dtypeloss_.dtype) loss_ * mask return tf.reduce_sum(loss_) / tf.reduce_sum(mask) tf.function def train_step(inp, tar): tar_inp tar[:, :-1] # 解码器输入去掉最后一个 token tar_real tar[:, 1:] # 解码器目标去掉第一个 tokenstart with tf.GradientTape() as tape: predictions transformer(inp, tar_inp, trainingTrue) loss loss_function(tar_real, predictions) gradients tape.gradient(loss, transformer.trainable_variables) optimizer.apply_gradients(zip(gradients, transformer.trainable_variables)) return losstar_inp和tar_real的错位是 Transformer 训练的经典操作输入是start I love you目标是I love you end。from_logitsTrue表示模型输出的是未经过 softmax 的 logits损失函数内部会做 softmax数值更稳定。reductionnone是为了先算每个位置的损失再用掩码加权最后除以有效 token 数。如果这里直接用默认的reductionsumpadding 位置的损失会混进去模型收敛会变慢甚至发散。3. 动手跑通从环境配置到第一条翻译结果3.1 环境依赖与版本匹配的坑这份源码基于 TensorFlow 2.x 和 Keras但 Keras 从 2.6 版本开始已经独立于 TensorFlow 之外而 TensorFlow 2.15 之后又内置了 Keras 3.x。版本不匹配是第一个翻车点。源码文档里如果写的是tensorflow2.10那就老老实实装 2.10不要用最新的 2.16因为tf.keras的 API 在 2.16 里变化很大自定义层的call方法签名可能不兼容。# 建议的虚拟环境配置 python -m venv mt_env source mt_env/bin/activate # Windows 用 mt_env\Scripts\activate pip install tensorflow2.10.0 pip install jieba numpy pandas matplotlib如果用的是 GPU 版本还要确认 CUDA 和 cuDNN 版本对应。TensorFlow 2.10 对应 CUDA 11.2 和 cuDNN 8.1。装错版本的表现是import tensorflow不报错但tf.config.list_physical_devices(GPU)返回空列表训练时跑在 CPU 上一个 epoch 要等半小时。我一般会先跑一行验证import tensorflow as tf print(tf.__version__) print(tf.config.list_physical_devices(GPU))如果 GPU 列表为空但机器有显卡先检查驱动版本再检查 CUDA 路径是否加进了LD_LIBRARY_PATH。Windows 下常见问题是 cuDNN 的 dll 文件没放到C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.2\bin下。3.2 训练参数怎么设batch size、学习率与 warmupTransformer 对学习率敏感原始论文用的是 warmup 策略前若干步学习率线性上升之后按步数的平方根倒数下降。这份源码里实现了CustomSchedule类核心公式是d_model^-0.5 * min(step^-0.5, step * warmup_steps^-1.5)。class CustomSchedule(tf.keras.optimizers.schedules.LearningRateSchedule): def __init__(self, d_model, warmup_steps4000): super(CustomSchedule, self).__init__() self.d_model tf.cast(d_model, tf.float32) self.warmup_steps warmup_steps def __call__(self, step): arg1 tf.math.rsqrt(step) arg2 step * (self.warmup_steps ** -1.5) return tf.math.rsqrt(self.d_model) * tf.math.minimum(arg1, arg2)warmup_steps4000是论文默认值但毕业设计的数据集通常只有几万句对4000 步可能已经跑完好几个 epoch 了。我一般会按总训练步数 / 10来估 warmup比如总步数 20000warmup 设 2000。d_model要和模型配置一致如果降到 256学习率峰值会自动调整。batch size 方面源码默认可能是 64但如果显存不够降到 32 或 16同时把学习率按比例调小否则梯度噪声太大损失曲线会震荡。训练时重点看两个指标训练损失和验证损失。如果训练损失下降但验证损失上升说明过拟合可以加 dropout 或减少模型层数。如果两个都不降先检查学习率是不是太大或者掩码是不是写反了——掩码写反的典型表现是模型输出全是unk或重复同一个词。3.3 推理阶段贪心解码与 beam search 的取舍训练完模型推理阶段才是真正见真章的地方。源码里默认用的是贪心解码每一步取概率最大的 token拼到已生成序列后面直到遇到end或达到最大长度。贪心解码快但容易陷入局部最优翻译结果可能生硬。def evaluate(sentence, transformer, tokenizer_src, tokenizer_tgt, max_length40): sentence preprocess_cn(sentence) inputs tokenizer_src.texts_to_sequences([sentence])[0] inputs pad_sequences([inputs], maxlenmax_length, paddingpost) inputs tf.convert_to_tensor(inputs) start_token tokenizer_tgt.word_index[start] end_token tokenizer_tgt.word_index[end] decoder_input tf.expand_dims([start_token], 0) result [] for i in range(max_length): predictions transformer(inputs, decoder_input, trainingFalse) predicted_id tf.argmax(predictions[:, -1, :], axis-1).numpy()[0] if predicted_id end_token: break result.append(predicted_id) decoder_input tf.concat([decoder_input, [[predicted_id]]], axis-1) return tokenizer_tgt.sequences_to_texts([result])[0]这段代码里predictions[:, -1, :]取的是最后一个时间步的输出因为解码器是自回归的每次只预测下一个 token。decoder_input初始只有start每生成一个 token 就拼上去。如果翻译结果出现重复词比如“我我我我”通常是训练不充分或学习率太大。如果结果全是unk检查词表构建时oov_token有没有设对以及推理时的分词方式和训练时是否一致。beam search 的改进思路是每步保留概率最高的 k 个候选序列最后选整体概率最高的。源码里可能没实现但可以自己加。k 一般取 3 到 5再大收益递减且速度明显变慢。毕业设计答辩时如果能把贪心和 beam search 的结果做个对比是个不错的加分项。4. 避坑与排查这份源码最容易翻车的五个地方4.1 损失不下降输出全是重复词现象训练几个 epoch 后损失卡在某个值不动推理时模型反复输出同一个词比如“的的的的”。原因最常见的是学习率太大或 warmup 没生效。Transformer 对初始学习率极其敏感如果直接用Adam(learning_rate0.001)而不加 warmup前几百步梯度更新过猛模型会陷入局部最优。另一个可能是掩码写反了把有效位置屏蔽了模型只能看到 padding。解决先确认CustomSchedule被正确传入优化器打印前 100 步的学习率看是否从接近零开始上升。然后检查create_padding_mask里tf.math.equal(seq, 0)的逻辑确保 0 对应的是 padding 位置。如果还不行把学习率峰值降一半再试。4.2 显存溢出OOM在 batch size 很小时也发生现象batch size 已经降到 8还是报ResourceExhaustedError。原因Transformer 的显存占用和序列长度的平方成正比。如果MAX_LENGTH设成 100即使 batch size 是 8注意力矩阵也是 8×100×100再加上多头和层数显存很容易爆。另一个可能是dff设得太大前馈网络的中间层维度是d_model的 4 倍如果d_model512dff2048参数量不小。解决先把MAX_LENGTH降到实际需要的值统计训练集长度分布取 95 分位数。然后降d_model和dff比如d_model256, dff1024。如果还不行用tf.keras.mixed_precision开混合精度训练显存能省三分之一左右。4.3 推理时start和end找不到现象tokenizer_tgt.word_index[start]报KeyError。原因Tokenizer默认的filters会过滤掉和这些符号导致start和end在构建词表时被拆成了start和end或者直接被丢弃。源码里如果没设filters这个问题必现。解决构建 Tokenizer 时显式传filters并且在fit_on_texts之前手动把start和end加到句子的首尾。如果已经建好词表检查word_index里有没有这两个 key没有就重新建。4.4 训练损失正常但翻译结果语序混乱现象损失降到 1.0 以下但翻译出来的英文语序不对比如“我吃饭”翻成“eat I rice”。原因位置编码没有正确加到嵌入上或者解码器的前瞻掩码没生效导致模型在预测第 t 个词时看到了第 t1 个词。前瞻掩码的作用是屏蔽未来位置如果掩码矩阵写成了全零或全一自回归就失效了。解决检查create_look_ahead_mask返回的是不是上三角矩阵对角线及以上为 1其余为 0。然后确认在解码器自注意力里传入的掩码是combined_mask即前瞻掩码和填充掩码的逐元素最大值。打印掩码矩阵的前几行看一眼比猜快得多。4.5 保存的模型加载后推理结果不一致现象训练完保存模型重新加载后翻译结果和保存前不一样。原因Keras 保存自定义层时如果get_config方法没实现或实现不完整加载时自定义层的参数会丢失或初始化成随机值。这份源码里的MultiHeadAttention和EncoderLayer都是自定义层必须实现get_config和from_config。解决在每个自定义层里补上get_config返回初始化时的参数比如d_model、num_heads、dff、rate。保存时用model.save_weights加model.load_weights的方式比model.save整个模型更可靠。加载后先跑一条训练集里的句子对比保存前的输出一致再继续。5. 进阶技巧用 BLEU 验证翻译质量并做错误分析跑通训练和推理只是第一步毕业设计答辩时老师大概率会问“你怎么证明翻译质量”。BLEU 是机器翻译的经典自动评价指标虽然不完美但比肉眼扫几条结果有说服力。这份源码可能没带 BLEU 计算但用nltk几行就能加上。from nltk.translate.bleu_score import corpus_bleu, SmoothingFunction def compute_bleu(transformer, tokenizer_src, tokenizer_tgt, test_pairs): references [] hypotheses [] smoothie SmoothingFunction().method1 for src, tgt in test_pairs: pred evaluate(src, transformer, tokenizer_src, tokenizer_tgt) references.append([tgt.split()]) # 参考译文分词 hypotheses.append(pred.split()) # 模型输出分词 return corpus_bleu(references, hypotheses, smoothing_functionsmoothie)corpus_bleu的输入是“参考译文的列表的列表”和“模型输出的列表”注意嵌套层级。SmoothingFunction用来处理 n-gram 匹配为零的情况否则短句的 BLEU 会直接归零。测试集建议从训练集里切出 500 到 1000 句对不要用训练集本身算 BLEU那样分数虚高。算完 BLEU 后做错误分析比盯着分数更有用。我一般会把 BLEU 最低的 20 条样本拉出来按错误类型归类漏译、多译、词序错误、专有名词翻译错误、重复输出。漏译通常是MAX_LENGTH太短或模型欠拟合词序错误多半是位置编码或掩码问题专有名词错误说明词表覆盖不够可以考虑加 subword 或增大词表。把错误分析写进毕业设计论文的“实验结果与分析”章节比只贴一个 BLEU 分数扎实得多。从那以后我每次拿到一个翻译项目都会先跑通一条样本的完整链路——从分词、词表、模型前向、损失计算到推理输出——确认每一步的输入输出形状和数值范围都对再开始大规模训练。这个习惯帮我省下了至少几十个小时的无效等待。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑