使用 MXNet Gluon 与 GluonNLP 训练 GNMT 模型:IWSLT 2015 英越神经机器翻译实战

发布时间:2026/9/21 2:08:15
使用 MXNet Gluon 与 GluonNLP 训练 GNMT 模型:IWSLT 2015 英越神经机器翻译实战 使用 MXNet Gluon 与 GluonNLP 训练 GNMT 模型IWSLT 2015 英越神经机器翻译实战【免费下载链接】mxnetLightweight, Portable, Flexible Distributed/Mobile Deep Learning with Dynamic, Mutation-aware Dataflow Dep Scheduler; for Python, R, Julia, Scala, Go, Javascript and more项目地址: https://gitcode.com/gh_mirrors/mxne/mxnet本指南基于当前仓库中的官方教程文档 gnmt.rst完整演示如何使用 MXNet Gluon 与 GluonNLP 工具包在 IWSLT 2015 英语-越南语数据集上从零训练 Google Neural Machine TranslationGNMT模型。读完本文你将掌握机器翻译任务的标准四步流水线数据加载与预处理、Sampler/DataLoader 构建、编码器-解码器模型组装、训练与 BLEU 评估并理解其中每个环节在 MXNet 源码中的底层实现。教程背景与整体流程GNMTGoogle Neural Machine Translation是经典的基于注意力机制的序列到序列seq2seq翻译模型。本教程选用 IWSLT 2015 English-Vietnamese 数据集构建过程分为四个步骤加载并处理数据集裁剪序列长度、分词、映射词表索引、添加 BOS/EOS 标记并缓存处理结果以便复用创建 Sampler 与 DataLoader通过分桶bucketing采样器将长度相近的句子分到同一批配合 Pad/Stack 组装 mini-batch构建模型调用get_gnmt_encoder_decoder得到 GNMT 编码器与解码器再封装为NMTModel并构建用于推理的 Beam Search 翻译器编写训练循环使用 ADAM 优化器、全局梯度裁剪、每轮验证 BLEU 分数并在验证集 BLEU 提升时保存最优参数。与之配套的 Transformer 教程见 transformer.rst二者共享同一套nmt模块数据集处理、模型定义、评测工具可对比阅读。环境准备与超参数设定首先导入依赖库并设置随机种子与计算设备。教程默认使用 GPUmx.gpu(0)同时需要安装gluonnlp以及 GluonNLP 仓库 scripts 目录下的nmt模块import warnings warnings.filterwarnings(ignore) import argparse import time import random import os import logging import numpy as np import mxnet as mx from mxnet import gluon import gluonnlp as nlp import nmt随后设定本次实验的全部超参数分为数据集、模型、训练、测试四组np.random.seed(100) random.seed(100) mx.random.seed(10000) device mx.gpu(0) # parameters for dataset dataset IWSLT2015 src_lang, tgt_lang en, vi src_max_len, tgt_max_len 50, 50 # parameters for model num_hidden 512 num_layers 2 num_bi_layers 1 dropout 0.2 # parameters for training batch_size, test_batch_size 128, 32 num_buckets 5 epochs 1 clip 5 lr 0.001 lr_update_factor 0.5 log_interval 10 save_dir gnmt_en_vi_u512 #parameters for testing beam_size 10 lp_alpha 1.0 lp_k 5 nmt.utils.logging_config(save_dir)关键参数含义如下参数取值作用src_max_len/tgt_max_len50 / 50源/目标序列最大长度超过部分被裁剪num_hidden512隐藏层维度同时作为词嵌入维度embed_sizenum_layers/num_bi_layers2 / 1解码器层数 / 编码器双向层数dropout0.2各层 dropout 概率batch_size/test_batch_size128 / 32训练/评测批大小num_buckets5分桶采样器的桶数量clip5全局梯度范数裁剪阈值lr/lr_update_factor0.001 / 0.5初始学习率及衰减因子beam_size/lp_alpha/lp_k10 / 1.0 / 5Beam Search 束宽、长度惩罚 alpha、K 值nmt.utils.logging_config(save_dir)会将训练日志写入save_dir目录后续所有验证/测试输出文件也存放于此。加载与预处理数据集数据处理的目标是把原始字符串句子转换为词表索引序列。教程将处理步骤概括为1) 裁剪源/目标序列长度2) 把字符串切分为 token 列表3) 将 token 映射为词表中的整数索引4) 给源句末尾追加 EOS给目标句首尾分别追加 BOS 与 EOS。数据集缓存cache_dataset 与 load_cached_datasetcache_dataset把处理后的数据以.npz格式缓存到nmt._constants.CACHE_PATH目录load_cached_dataset则按前缀读取缓存命中时直接返回避免重复处理大数据集def cache_dataset(dataset, prefix): Cache the processed npy dataset the dataset into a npz Parameters ---------- dataset : gluon.data.SimpleDataset file_path : str if not os.path.exists(nmt._constants.CACHE_PATH): os.makedirs(nmt._constants.CACHE_PATH) src_data np.array([ele[0] for ele in dataset]) tgt_data np.array([ele[1] for ele in dataset]) np.savez(os.path.join(nmt._constants.CACHE_PATH, prefix .npz), src_datasrc_data, tgt_datatgt_data) def load_cached_dataset(prefix): cached_file_path os.path.join(nmt._constants.CACHE_PATH, prefix .npz) if os.path.exists(cached_file_path): print(Load cached data from {}.format(cached_file_path)) dat np.load(cached_file_path) return gluon.data.ArrayDataset(np.array(dat[src_data]), np.array(dat[tgt_data])) else: return None转换器TrainValDataTransformTrainValDataTransform是核心的样本变换器通过Vocab完成 token 到索引的映射并实现 BOS/EOS 的插入逻辑。src_max_len/tgt_max_len若为 0 或负数则不做长度裁剪class TrainValDataTransform(object): Transform the machine translation dataset. Clip source and the target sentences to the maximum length. For the source sentence, append the EOS. For the target sentence, append BOS and EOS. Parameters ---------- src_vocab : Vocab tgt_vocab : Vocab src_max_len : int tgt_max_len : int def __init__(self, src_vocab, tgt_vocab, src_max_len, tgt_max_len): self._src_vocab src_vocab self._tgt_vocab tgt_vocab self._src_max_len src_max_len self._tgt_max_len tgt_max_len def __call__(self, src, tgt): if self._src_max_len 0: src_sentence self._src_vocab[src.split()[:self._src_max_len]] else: src_sentence self._src_vocab[src.split()] if self._tgt_max_len 0: tgt_sentence self._tgt_vocab[tgt.split()[:self._tgt_max_len]] else: tgt_sentence self._tgt_vocab[tgt.split()] src_sentence.append(self._src_vocab[self._src_vocab.eos_token]) tgt_sentence.insert(0, self._tgt_vocab[self._tgt_vocab.bos_token]) tgt_sentence.append(self._tgt_vocab[self._tgt_vocab.eos_token]) src_npy np.array(src_sentence, dtypenp.int32) tgt_npy np.array(tgt_sentence, dtypenp.int32) return src_npy, tgt_npy注意目标句的处理方式在句首插入 BOS、句尾追加 EOS而源句只追加 EOS。这一不对称设计直接服务于训练目标——用tgt_seq[:, :-1]以 BOS 起始的移位目标预测tgt_seq[:, 1:]以 EOS 结尾的真实目标详见下文训练循环。流程编排process_dataset 与 load_translation_dataprocess_dataset用dataset.transform(..., lazyFalse)立即执行全部变换并统计耗时load_translation_data负责加载训练/验证/测试三个子集、取出词表、按前缀尝试读缓存、并在未命中时处理并缓存def process_dataset(dataset, src_vocab, tgt_vocab, src_max_len-1, tgt_max_len-1): start time.time() dataset_processed dataset.transform(TrainValDataTransform(src_vocab, tgt_vocab, src_max_len, tgt_max_len), lazyFalse) end time.time() print(Processing time spent: {}.format(end - start)) return dataset_processed def load_translation_data(dataset, src_langen, tgt_langvi): Load translation dataset Parameters ---------- dataset : str src_lang : str, default en tgt_lang : str, default vi Returns ------- data_train_processed : Dataset The preprocessed training sentence pairs data_val_processed : Dataset The preprocessed validation sentence pairs data_test_processed : Dataset The preprocessed test sentence pairs val_tgt_sentences : list The target sentences in the validation set test_tgt_sentences : list The target sentences in the test set src_vocab : Vocab Vocabulary of the source language tgt_vocab : Vocab Vocabulary of the target language common_prefix IWSLT2015_{}_{}_{}_{}.format(src_lang, tgt_lang, src_max_len, tgt_max_len) data_train nlp.data.IWSLT2015(train, src_langsrc_lang, tgt_langtgt_lang) data_val nlp.data.IWSLT2015(val, src_langsrc_lang, tgt_langtgt_lang) data_test nlp.data.IWSLT2015(test, src_langsrc_lang, tgt_langtgt_lang) src_vocab, tgt_vocab data_train.src_vocab, data_train.tgt_vocab data_train_processed load_cached_dataset(common_prefix _train) if not data_train_processed: data_train_processed process_dataset(data_train, src_vocab, tgt_vocab, src_max_len, tgt_max_len) cache_dataset(data_train_processed, common_prefix _train) data_val_processed load_cached_dataset(common_prefix _val) if not data_val_processed: data_val_processed process_dataset(data_val, src_vocab, tgt_vocab) cache_dataset(data_val_processed, common_prefix _val) data_test_processed load_cached_dataset(common_prefix _test) if not data_test_processed: data_test_processed process_dataset(data_test, src_vocab, tgt_vocab) cache_dataset(data_test_processed, common_prefix _test) fetch_tgt_sentence lambda src, tgt: tgt.split() val_tgt_sentences list(data_val.transform(fetch_tgt_sentence)) test_tgt_sentences list(data_test.transform(fetch_tgt_sentence)) return data_train_processed, data_val_processed, data_test_processed, \ val_tgt_sentences, test_tgt_sentences, src_vocab, tgt_vocab def get_data_lengths(dataset): return list(dataset.transform(lambda srg, tgt: (len(srg), len(tgt))))缓存前缀由数据集名_源语言_目标语言_源最大长度_目标最大长度拼接而成这意味着修改src_max_len/tgt_max_len会自动生成不同的缓存文件避免命中过期的处理结果。组装最终数据集并导出金标准译文处理完成后为训练/验证/测试数据分别附加长度信息与实例编号训练集直接变换为(src, tgt, len(src), len(tgt))四元组验证集与测试集额外附带原始下标i用于评测时把翻译结果按实例 id 归位见evaluate函数。同时把验证集与测试集的参考译文ground truth写入save_dir下的val_gt.txt、test_gt.txtdata_train, data_val, data_test, val_tgt_sentences, test_tgt_sentences, src_vocab, tgt_vocab\ load_translation_data(datasetdataset, src_langsrc_lang, tgt_langtgt_lang) data_train_lengths get_data_lengths(data_train) data_val_lengths get_data_lengths(data_val) data_test_lengths get_data_lengths(data_test) with open(os.path.join(save_dir, val_gt.txt), w, encodingutf-8) as of: for ele in val_tgt_sentences: of.write( .join(ele) \n) with open(os.path.join(save_dir, test_gt.txt), w, encodingutf-8) as of: for ele in test_tgt_sentences: of.write( .join(ele) \n) data_train data_train.transform(lambda src, tgt: (src, tgt, len(src), len(tgt)), lazyFalse) data_val gluon.data.SimpleDataset([(ele[0], ele[1], len(ele[0]), len(ele[1]), i) for i, ele in enumerate(data_val)]) data_test gluon.data.SimpleDataset([(ele[0], ele[1], len(ele[0]), len(ele[1]), i) for i, ele in enumerate(data_test)])创建 Sampler 与 DataLoader拿到处理后的data_train、data_val、data_test后下一步是构造 batchify 函数与分桶采样器最终生成可迭代的 DataLoader。batchify 函数Pad、Stack 与 Tuple机器翻译中同批句子长度不一需要先 pad 到相同长度再堆叠。训练与测试的 batchify 函数都先用Pad()分别处理源序列与目标序列再用Stack(dtypefloat32)堆叠长度数组测试集额外多一个Stack()用于携带实例编号train_batchify_fn nlp.data.batchify.Tuple(nlp.data.batchify.Pad(), nlp.data.batchify.Pad(), nlp.data.batchify.Stack(dtypefloat32), nlp.data.batchify.Stack(dtypefloat32)) test_batchify_fn nlp.data.batchify.Tuple(nlp.data.batchify.Pad(), nlp.data.batchify.Pad(), nlp.data.batchify.Stack(dtypefloat32), nlp.data.batchify.Stack(dtypefloat32), nlp.data.batchify.Stack())这些 API 在 MXNet 仓库 python/mxnet/gluon/data/batchify.py 中有对应实现。从源码看Pad的构造参数包括val填充值默认 0、dtype输出类型默认沿用输入、round_to将填充后维度向上取整为指定值的倍数、use_shared_mem其 docstring 示例显示batchify.Pad()([[1,2,3,4],[4,5,6],[8,2]])会得到3x4的补齐矩阵不足部分用 0 填充——这正是批内句子长度不齐时的处理方式。Pad会将各样本沿第一个维度补齐到批内最大长度后堆叠因此一个 batch 的序列张量形状为(batch_size, max_len)。分桶采样器ExpWidthBucket 与 FixedBucketSampler直接按原始顺序分批会导致同一批内句子长度差异悬殊、填充浪费严重。分桶采样器把长度相近的句子分到同一桶再按桶组织批次从而显著降低 padding 比例、提升训练吞吐bucket_scheme nlp.data.ExpWidthBucket(bucket_len_step1.2) train_batch_sampler nlp.data.FixedBucketSampler(lengthsdata_train_lengths, batch_sizebatch_size, num_bucketsnum_buckets, shuffleTrue, bucket_schemebucket_scheme) logging.info(Train Batch Sampler:\n{}.format(train_batch_sampler.stats())) val_batch_sampler nlp.data.FixedBucketSampler(lengthsdata_val_lengths, batch_sizetest_batch_size, num_bucketsnum_buckets, shuffleFalse) logging.info(Valid Batch Sampler:\n{}.format(val_batch_sampler.stats())) test_batch_sampler nlp.data.FixedBucketSampler(lengthsdata_test_lengths, batch_sizetest_batch_size, num_bucketsnum_buckets, shuffleFalse) logging.info(Test Batch Sampler:\n{}.format(test_batch_sampler.stats()))ExpWidthBucket(bucket_len_step1.2)采用指数宽度划分桶边界即相邻桶的宽度按 1.2 倍递增短句桶更密、长句桶更宽这是经验性的分桶方案教程原文注明 the bucketing scheme is empirically determinedFixedBucketSampler接收每个样本的长度列表lengths、批大小、桶数量num_buckets训练时开启shuffleTrue打乱批次顺序验证/测试时shuffleFalse保持稳定顺序sampler.stats()会打印每个桶的样本数、批数等统计信息便于观察长度分布。Transformer 教程中对该采样器还有更多可选参数的用法如use_average_lengthTrue、ratio0.0、num_shards1可在需要按平均长度估算批大小时参考。构造 DataLoader给定采样器后用gluon.data.DataLoader包装数据集并指定 batchify 函数与多进程 worker 数量train_data_loader gluon.data.DataLoader(data_train, batch_samplertrain_batch_sampler, batchify_fntrain_batchify_fn, num_workers4) val_data_loader gluon.data.DataLoader(data_val, batch_samplerval_batch_sampler, batchify_fntest_batchify_fn, num_workers4) test_data_loader gluon.data.DataLoader(data_test, batch_samplertest_batch_sampler, batchify_fntest_batchify_fn, num_workers4)DataLoader的实现位于 python/mxnet/gluon/data/dataloader.py。从源码可见当指定batch_sampler时不能同时指定batch_size/shuffle/sampler/last_batchbatchify_fn用于将样本合并为一个 batch的自定义回调默认是gluon.data.batchify.Stack()num_workers控制多进程数据预处理的工作进程数prefetch默认取num_workers * 2允许 worker 预取若干批次以平滑启动性能。本教程将num_workers设为 4即在 4 个子进程中并行完成 pad/stack 等预处理。构建 GNMT 模型编码器-解码器与 NMTModelGNMT 编码器和解码器通过nmt.gnmt.get_gnmt_encoder_decoder一行获取再喂给NMTModel组装成完整模型。model.hybridize()让计算切换到符号后端symbolic backend从而获得图优化与静态内存分配带来的加速encoder, decoder nmt.gnmt.get_gnmt_encoder_decoder(hidden_sizenum_hidden, dropoutdropout, num_layersnum_layers, num_bi_layersnum_bi_layers) model nmt.translation.NMTModel(src_vocabsrc_vocab, tgt_vocabtgt_vocab, encoderencoder, decoderdecoder, embed_sizenum_hidden, prefixgnmt_) model.initialize(initmx.init.Uniform(0.1), devicedevice) static_alloc True model.hybridize(static_allocstatic_alloc) logging.info(model) # Due to the paddings, we need to mask out the losses corresponding to padding tokens. loss_function nmt.loss.SoftmaxCEMaskedLoss() loss_function.hybridize(static_allocstatic_alloc)要点说明hidden_size512同时作为词嵌入维度embed_size传入num_bi_layers1表示编码器底部有一层双向 RNN其余层单向参数用mx.init.Uniform(0.1)在[-0.1, 0.1]区间均匀初始化static_allocTrue指示 hybridize 采用静态内存分配由于批内有 padding token不能直接对所有位置计算交叉熵因此使用nmt.loss.SoftmaxCEMaskedLoss()屏蔽 padding 位置对应的损失。Beam Search 翻译器推理阶段使用束搜索Beam Search生成译文。BeamSearchTranslator接收模型、束宽、打分器与最大输出长度。BeamSearchScorer(alpha, K)实现长度惩罚alpha1.0为长度归一化系数K5为长度惩罚的分母阈值translator nmt.translation.BeamSearchTranslator(modelmodel, beam_sizebeam_size, scorernlp.model.BeamSearchScorer(alphalp_alpha, Klp_k), max_lengthtgt_max_len 100) logging.info(Use beam_size{}, alpha{}, K{}.format(beam_size, lp_alpha, lp_k))max_length设为tgt_max_len 100 150允许生成的译文略长于训练时的最大目标长度。评估函数 evaluate 与 write_sentencesevaluate遍历验证/测试 DataLoader同时完成两件事一是用掩码交叉熵计算平均损失二是用 Beam Search 翻译器生成译文并通过inst_ids把翻译结果按原始顺序归位因为分桶会打乱样本顺序。译文由tgt_vocab.idx_to_token反查 token并去除首尾的 BOS/EOSdef evaluate(data_loader): Evaluate given the data loader Parameters ---------- data_loader : gluon.data.DataLoader Returns ------- avg_loss : float Average loss real_translation_out : list of list of str The translation output translation_out [] all_inst_ids [] avg_loss_denom 0 avg_loss 0.0 for _, (src_seq, tgt_seq, src_valid_length, tgt_valid_length, inst_ids) \ in enumerate(data_loader): src_seq src_seq.to_device(device) tgt_seq tgt_seq.to_device(device) src_valid_length src_valid_length.to_device(device) tgt_valid_length tgt_valid_length.to_device(device) # Calculating Loss out, _ model(src_seq, tgt_seq[:, :-1], src_valid_length, tgt_valid_length - 1) loss loss_function(out, tgt_seq[:, 1:], tgt_valid_length - 1).mean().asscalar() all_inst_ids.extend(inst_ids.asnumpy().astype(np.int32).tolist()) avg_loss loss * (tgt_seq.shape[1] - 1) avg_loss_denom (tgt_seq.shape[1] - 1) # Translate samples, _, sample_valid_length \ translator.translate(src_seqsrc_seq, src_valid_lengthsrc_valid_length) max_score_sample samples[:, 0, :].asnumpy() sample_valid_length sample_valid_length[:, 0].asnumpy() for i in range(max_score_sample.shape[0]): translation_out.append( [tgt_vocab.idx_to_token[ele] for ele in max_score_sample[i][1:(sample_valid_length[i] - 1)]]) avg_loss avg_loss / avg_loss_denom real_translation_out [None for _ in range(len(all_inst_ids))] for ind, sentence in zip(all_inst_ids, translation_out): real_translation_out[ind] sentence return avg_loss, real_translation_out def write_sentences(sentences, file_path): with open(file_path, w, encodingutf-8) as of: for sent in sentences: of.write( .join(sent) \n)这里的to_device(device)将 mini-batch 复制到 GPUsamples[:, 0, :]取每个样本得分最高的那条 beam 作为最终译文[1:(sample_valid_length[i] - 1)]去掉 BOS 与 EOS 标记。编写训练循环创建 Trainer训练采用 ADAM 优化器学习率为 0.001trainer gluon.Trainer(model.collect_params(), adam, {learning_rate: lr})逐 epoch 训练训练循环的核心逻辑每轮从train_data_loader取批to_device拷贝到 GPU在mx.autograd.record()块内做前向、计算掩码损失并loss.backward()反向传播随后收集各参数梯度、用gluon.utils.clip_global_norm做全局梯度裁剪阈值clip5并trainer.step(1)更新参数。mx.autograd.record()的作用是告诉 Gluon 后端对块内运算记录并计算梯度。每个 epoch 结束后在验证集与测试集上调用evaluate用nmt.bleu.compute_bleu计算 BLEU 分数并将验证集 BLEU 最高的参数保存为valid_best.paramsbest_valid_bleu 0.0 for epoch_id in range(epochs): log_avg_loss 0 log_avg_gnorm 0 log_wc 0 log_start_time time.time() for batch_id, (src_seq, tgt_seq, src_valid_length, tgt_valid_length)\ in enumerate(train_data_loader): # logging.info(src_seq.context) Context suddenly becomes GPU. src_seq src_seq.to_device(device) tgt_seq tgt_seq.to_device(device) src_valid_length src_valid_length.to_device(device) tgt_valid_length tgt_valid_length.to_device(device) with mx.autograd.record(): out, _ model(src_seq, tgt_seq[:, :-1], src_valid_length, tgt_valid_length - 1) loss loss_function(out, tgt_seq[:, 1:], tgt_valid_length - 1).mean() loss loss * (tgt_seq.shape[1] - 1) / (tgt_valid_length - 1).mean() loss.backward() grads [p.grad(device) for p in model.collect_params().values()] gnorm gluon.utils.clip_global_norm(grads, clip) trainer.step(1) src_wc src_valid_length.sum().asscalar() tgt_wc (tgt_valid_length - 1).sum().asscalar() step_loss loss.asscalar() log_avg_loss step_loss log_avg_gnorm gnorm log_wc src_wc tgt_wc if (batch_id 1) % log_interval 0: wps log_wc / (time.time() - log_start_time) logging.info([Epoch {} Batch {}/{}] loss{:.4f}, ppl{:.4f}, gnorm{:.4f}, throughput{:.2f}K wps, wc{:.2f}K .format(epoch_id, batch_id 1, len(train_data_loader), log_avg_loss / log_interval, np.exp(log_avg_loss / log_interval), log_avg_gnorm / log_interval, wps / 1000, log_wc / 1000)) log_start_time time.time() log_avg_loss 0 log_avg_gnorm 0 log_wc 0 valid_loss, valid_translation_out evaluate(val_data_loader) valid_bleu_score, _, _, _, _ nmt.bleu.compute_bleu([val_tgt_sentences], valid_translation_out) logging.info([Epoch {}] valid Loss{:.4f}, valid ppl{:.4f}, valid bleu{:.2f} .format(epoch_id, valid_loss, np.exp(valid_loss), valid_bleu_score * 100)) test_loss, test_translation_out evaluate(test_data_loader) test_bleu_score, _, _, _, _ nmt.bleu.compute_bleu([test_tgt_sentences], test_translation_out) logging.info([Epoch {}] test Loss{:.4f}, test ppl{:.4f}, test bleu{:.2f} .format(epoch_id, test_loss, np.exp(test_loss), test_bleu_score * 100)) write_sentences(valid_translation_out, os.path.join(save_dir, epoch{:d}_valid_out.txt).format(epoch_id)) write_sentences(test_translation_out, os.path.join(save_dir, epoch{:d}_test_out.txt).format(epoch_id)) if valid_bleu_score best_valid_bleu: best_valid_bleu valid_bleu_score save_path os.path.join(save_dir, valid_best.params) logging.info(Save best parameters to {}.format(save_path)) model.save_parameters(save_path) if epoch_id 1 (epochs * 2) // 3: new_lr trainer.learning_rate * lr_update_factor logging.info(Learning rate change to {}.format(new_lr)) trainer.set_learning_rate(new_lr)值得注意的几个工程细节损失加权loss loss * (tgt_seq.shape[1] - 1) / (tgt_valid_length - 1).mean()按目标有效长度对批内平均损失做了归一化使长短句对梯度的贡献更均衡吞吐统计以 wpswords per second词/秒为指标记录训练吞吐wc统计的是源与目标的有效词数之和困惑度日志中的pplperplexity由np.exp(loss)计算得到学习率衰减训练进行到epochs * 2 // 3轮之后每个 epoch 结束都把学习率乘以lr_update_factor0.5通过trainer.set_learning_rate生效即指数衰减策略模型保存仅当验证 BLEU 刷新最优值时才覆盖保存valid_best.params并同步导出每个 epoch 的验证/测试译文文件epoch{id}_valid_out.txt、epoch{id}_test_out.txt。总结本教程完整展示了在 MXNet Gluon 与 GluonNLP 框架下训练 GNMT 英越翻译模型的全部环节数据管线TrainValDataTransform完成裁剪、分词、词表映射与 BOS/EOS 注入cache_dataset/load_cached_dataset实现处理结果的磁盘缓存批处理优化Pad/Stackbatchify 与FixedBucketSampler分桶采样配合把长度相近的句子分到同一批有效减少 padding 浪费这些 API 的底层实现可在 batchify.py 与 dataloader.py 中进一步查看模型组装get_gnmt_encoder_decoderNMTModelhybridize组合出可符号化执行的高效模型BeamSearchTranslator与BeamSearchScorer提供带长度惩罚的束搜索推理训练与评测ADAM 优化器、全局梯度裁剪、masked softmax 交叉熵、逐 epoch 的 BLEU 评测、最优参数保存与学习率指数衰减构成了一个完整可复现的训练闭环。教程中的nmt模块nmt.gnmt、nmt.translation、nmt.loss、nmt.bleu等来自 GluonNLP 项目的 scripts 目录完整训练脚本train_gnmt.py及复现命令以 GluonNLP 官方发布内容为准。将epochs从示例的 1 增大、配合足够的训练轮数与学习率调度即可在 IWSLT 2015 英越任务上获得可用的翻译模型该流水线同样适用于其他语言对与 seq2seq 任务。【免费下载链接】mxnetLightweight, Portable, Flexible Distributed/Mobile Deep Learning with Dynamic, Mutation-aware Dataflow Dep Scheduler; for Python, R, Julia, Scala, Go, Javascript and more项目地址: https://gitcode.com/gh_mirrors/mxne/mxnet创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考