2.1 使用RNN模型构建人名分类器
- 学习目标:
- 了解有关人名分类问题和有关数据.
- 掌握使用RNN构建人名分类器实现过程.
- 关于人名分类问题:
- 以一个人名为输入, 使用模型帮助我们判断它最有可能是来自哪一个国家的人名, 这在某些国际化公司的业务中具有重要意义, 在用户注册过程中, 会根据用户填写的名字直接给他分配可能的国家或地区选项, 以及该国家或地区的国旗, 限制手机号码位数等等.
- 人名分类数据:
- 数据下载地址: https://download.pytorch.org/tutorial/data.zip
- 数据文件预览:
1 | - data/ |
- Chiness.txt预览:
1 | Ang |
- 整个案例的实现可分为以下五个步骤:
- 第一步: 导入必备的工具包.
- 第二步: 对data文件中的数据进行处理,满足训练要求.
- 第三步: 构建RNN模型(包括传统RNN, LSTM以及GRU).
- 第四步: 构建训练函数并进行训练.
- 第五步: 构建评估函数并进行预测.
- 第一步: 导入必备的工具包
- python版本使用3.6.x, pytorch版本使用1.3.1
1 | pip install torch==1.3.1 |
1 |
|
- 第二步: 对data文件中的数据进行处理,满足训练要求.
- 获取常用的字符数量:
1 |
|
- 输出效果:
1 | n_letter: 57 |
- 字符规范化之unicode转Ascii函数:
1 |
|
- 调用:
1 | s = "Ślusàrski" |
- 输出效果:
1 | Slusarski |
- 构建一个从持久化文件中读取内容到内存的函数:
1 | data_path = "./data/name/" |
- 调用:
1 | # filename是数据集中某个具体的文件, 我们这里选择Chinese.txt |
- 输出效果:
1 | lines: ['Ang', 'AuYong', 'Bai', 'Ban', 'Bao', 'Bei', 'Bian', 'Bui', 'Cai', 'Cao', 'Cen', 'Chai', 'Chaim', 'Chan', 'Chang', 'Chao', 'Che', 'Chen', 'Cheng', 'Cheung', 'Chew', 'Chieu', 'Chin', 'Chong', 'Chou', 'Chu', 'Cui', 'Dai', 'Deng', 'Ding', 'Dong', 'Dou', 'Duan', 'Eng', 'Fan', 'Fei', 'Feng', 'Foong', 'Fung', 'Gan', 'Gauk', 'Geng', 'Gim', 'Gok', 'Gong', 'Guan', 'Guang', 'Guo', 'Gwock', 'Han', 'Hang', 'Hao', 'Hew', 'Hiu', 'Hong', 'Hor', 'Hsiao', 'Hua', 'Huan', 'Huang', 'Hui', 'Huie', 'Huo', 'Jia', 'Jiang', 'Jin', 'Jing', 'Joe', 'Kang', 'Kau', 'Khoo', 'Khu', 'Kong', 'Koo', 'Kwan', 'Kwei', 'Kwong', 'Lai', 'Lam', 'Lang', 'Lau', 'Law', 'Lew', 'Lian', 'Liao', 'Lim', 'Lin', 'Ling', 'Liu', 'Loh', 'Long', 'Loong', 'Luo', 'Mah', 'Mai', 'Mak', 'Mao', 'Mar', 'Mei', 'Meng', 'Miao', 'Min', 'Ming', 'Moy', 'Mui', 'Nie', 'Niu', 'OuYang', 'OwYang', 'Pan', 'Pang', 'Pei', 'Peng', 'Ping', 'Qian', 'Qin', 'Qiu', 'Quan', 'Que', 'Ran', 'Rao', 'Rong', 'Ruan', 'Sam', 'Seah', 'See ', 'Seow', 'Seto', 'Sha', 'Shan', 'Shang', 'Shao', 'Shaw', 'She', 'Shen', 'Sheng', 'Shi', 'Shu', 'Shuai', 'Shui', 'Shum', 'Siew', 'Siu', 'Song', 'Sum', 'Sun', 'Sze ', 'Tan', 'Tang', 'Tao', 'Teng', 'Teoh', 'Thean', 'Thian', 'Thien', 'Tian', 'Tong', 'Tow', 'Tsang', 'Tse', 'Tsen', 'Tso', 'Tze', 'Wan', 'Wang', 'Wei', 'Wen', 'Weng', 'Won', 'Wong', 'Woo', 'Xiang', 'Xiao', 'Xie', 'Xing', 'Xue', 'Xun', 'Yan', 'Yang', 'Yao', 'Yap', 'Yau', 'Yee', 'Yep', 'Yim', 'Yin', 'Ying', 'Yong', 'You', 'Yuan', 'Zang', 'Zeng', 'Zha', 'Zhan', 'Zhang', 'Zhao', 'Zhen', 'Zheng', 'Zhong', 'Zhou', 'Zhu', 'Zhuo', 'Zong', 'Zou', 'Bing', 'Chi', 'Chu', 'Cong', 'Cuan', 'Dan', 'Fei', 'Feng', 'Gai', 'Gao', 'Gou', 'Guan', 'Gui', 'Guo', 'Hong', 'Hou', 'Huan', 'Jian', 'Jiao', 'Jin', 'Jiu', 'Juan', 'Jue', 'Kan', 'Kuai', 'Kuang', 'Kui', 'Lao', 'Liang', 'Lu', 'Luo', 'Man', 'Nao', 'Pian', 'Qiao', 'Qing', 'Qiu', 'Rang', 'Rui', 'She', 'Shi', 'Shuo', 'Sui', 'Tai', 'Wan', 'Wei', 'Xian', 'Xie', 'Xin', 'Xing', 'Xiong', 'Xuan', 'Yan', 'Yin', 'Ying', 'Yuan', 'Yue', 'Yun', 'Zha', 'Zhai', 'Zhang', 'Zhi', 'Zhuan', 'Zhui'] |
- 构建人名类别(所属的语言)列表与人名对应关系字典:
1 |
|
- 输出效果:
1 | n_categories: 18 |
- 将人名转化为对应onehot张量表示:
1 | # 将字符串(单词粒度)转化为张量表示,如:"ab" ---> |
- 调用:
1 | line = "Bai" |
- 输出效果:
1 | line_tensot: tensor([[[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., |
- 第三步: 构建RNN模型
- 构建传统的RNN模型:
1 | # 使用nn.RNN构建完成传统RNN使用类 |
- torch.unsqueeze演示:
1 | >>> x = torch.tensor([1, 2, 3, 4]) |
- 构建LSTM模型:
1 | # 使用nn.LSTM构建完成LSTM使用类 |
- 构建GRU模型:
1 | # 使用nn.GRU构建完成传统RNN使用类 |
- 实例化参数:
1 | # 因为是onehot编码, 输入张量最后一维的尺寸就是n_letters |
- 输入参数:
1 | # 假如我们以一个字母B作为RNN的首次输入, 它通过lineToTensor转为张量 |
- 调用:
1 | rnn = RNN(n_letters, n_hidden, n_categories) |
- 输出效果:
1 | rnn: tensor([[[-2.8822, -2.8615, -2.9488, -2.8898, -2.9205, -2.8113, -2.9328, |
- 第四步: 构建训练函数并进行训练
- 从输出结果中获得指定类别函数:
1 | def categoryFromOutput(output): |
- torch.topk演示:
1 | >>> x = torch.arange(1., 6.) |
- 输入参数:
1 | # 将上一步中gru的输出作为函数的输入 |
- 调用:
1 | category, category_i = categoryFromOutput(output) |
- 输出效果:
1 | category: Portuguese |
- 随机生成训练数据:
1 | def randomTrainingExample(): |
- 调用:
1 |
|
- 输出效果:
1 | category = French / line = Fontaine / category_tensor = tensor([5]) |
- 构建传统RNN训练函数:
1 | # 定义损失函数为nn.NLLLoss,因为RNN的最后一层是nn.LogSoftmax, 两者的内部计算逻辑正好能够吻合. |
- torch.add演示:
1 | >>> a = torch.randn(4) |
- 构建LSTM训练函数:
1 | # 与传统RNN相比多出细胞状态c |
- 构建GRU训练函数:
1 | # 与传统RNN完全相同, 只不过名字改成了GRU |
- 构建时间计算函数:
1 | def timeSince(since): |
- 输入参数:
1 | # 假定模型训练开始时间是10min之前 |
- 调用:
1 | period = timeSince(since) |
- 输出效果:
1 | 10m 0s |
- 构建训练过程的日志打印函数:
1 | # 设置训练迭代次数 |
- 开始训练传统RNN, LSTM, GRU模型并制作对比图:
1 | # 调用train函数, 分别进行RNN, LSTM, GRU模型的训练 |
- 传统RNN训练日志输出:
1 | 5000 5% (0m 16s) 3.2264 Carr / Chinese ✗ (English) |
- LSTM训练日志输出:
1 | 5000 5% (0m 25s) 2.8640 Fabian / Dutch ✗ (Polish) |
- GRU训练日志输出:
1 | 5000 5% (0m 22s) 2.8182 Bernard / Irish ✗ (Polish) |
- 损失对比曲线:
- 损失对比曲线分析:
- 模型训练的损失降低快慢代表模型收敛程度, 由图可知, 传统RNN的模型收敛情况最好, 然后是GRU, 最后是LSTM, 这是因为: 我们当前处理的文本数据是人名, 他们的长度有限, 且长距离字母间基本无特定关联, 因此无法发挥改进模型LSTM和GRU的长距离捕捉语义关联的优势. 所以在以后的模型选用时, 要通过对任务的分析以及实验对比, 选择最适合的模型.
- 训练耗时对比图:
- 训练耗时对比图分析:
- 模型训练的耗时长短代表模型的计算复杂度, 由图可知, 也正如我们之前的理论分析, 传统RNN复杂度最低, 耗时几乎只是后两者的一半, 然后是GRU, 最后是复杂度最高的LSTM.
- 结论:
- 模型选用一般应通过实验对比, 并非越复杂或越先进的模型表现越好, 而是需要结合自己的特定任务, 从对数据的分析和实验结果中获得最佳答案.
- 第五步: 构建评估函数并进行预测
- 构建传统RNN评估函数:
1 | def evaluateRNN(line_tensor): |
- 构建LSTM评估函数:
1 | def evaluateLSTM(line_tensor): |
- 构建GRU评估函数:
1 | def evaluateGRU(line_tensor): |
- 输入参数:
1 | line = "Bai" |
- 调用:
1 | rnn_output = evaluateRNN(line_tensor) |
- 输出效果:
1 | rnn_output: tensor([[-2.8923, -2.7665, -2.8640, -2.7907, -2.9919, -2.9482, -2.8809, -2.9526, |
- 构建预测函数:
1 | def predict(input_line, evaluate, n_predictions=3): |
- 调用:
1 | for evaluate_fn in [evaluateRNN, evaluateLSTM, evaluateGRU]: |
- 输出效果
1 | ------------------ |
小节总结:
- 学习了关于人名分类问题: 以一个人名为输入, 使用模型帮助我们判断它最有可能是来自哪一个国家的人名, 这在某些国际化公司的业务中具有重要意义, 在用户注册过程中, 会根据用户填写的名字直接给他分配可能的国家或地区选项, 以及该国家或地区的国旗, 限制手机号码位数等等.
- 人名分类器的实现可分为以下五个步骤:
- 第一步: 导入必备的工具包.
- 第二步: 对data文件中的数据进行处理,满足训练要求.
- 第三步: 构建RNN模型(包括传统RNN, LSTM以及GRU).
- 第四步: 构建训练函数并进行训练.
- 第五步: 构建评估函数并进行预测.
- 第一步: 导入必备的工具包
- python版本使用3.6.x, pytorch版本使用1.3.1
- 第二步: 对data文件中的数据进行处理,满足训练要求
- 定义数据集路径并获取常用的字符数量.
- 字符规范化之unicode转Ascii函数unicodeToAscii.
- 构建一个从持久化文件中读取内容到内存的函数readLines.
- 构建人名类别(所属的语言)列表与人名对应关系字典
- 将人名转化为对应onehot张量表示函数lineToTensor
- 第三步: 构建RNN模型
- 构建传统的RNN模型的类class RNN.
- 构建LSTM模型的类class LSTM.
- 构建GRU模型的类class GRU.
- 第四步: 构建训练函数并进行训练
- 从输出结果中获得指定类别函数categoryFromOutput.
- 随机生成训练数据函数randomTrainingExample.
- 构建传统RNN训练函数trainRNN.
- 构建LSTM训练函数trainLSTM.
- 构建GRU训练函数trainGRU.
- 构建时间计算函数timeSince.
- 构建训练过程的日志打印函数train.得到损失对比曲线和训练耗时对比图.
- 损失对比曲线分析:
- 模型训练的损失降低快慢代表模型收敛程度, 由图可知, 传统RNN的模型收敛情况最好, 然后是GRU, 最后是LSTM, 这是因为: 我们当前处理的文本数据是人名, 他们的长度有限, 且长距离字母间基本无特定关联, 因此无法发挥改进模型LSTM和GRU的长距离捕捉语义关联的优势. 所以在以后的模型选用时, 要通过对任务的分析以及实验对比, 选择最适合的模型.
- 训练耗时对比图分析:
- 模型训练的耗时长短代表模型的计算复杂度, 由图可知, 也正如我们之前的理论分析, 传统RNN复杂度最低, 耗时几乎只是后两者的一半, 然后是GRU, 最后是复杂度最高的LSTM.
- 结论:
- 模型选用一般应通过实验对比, 并非越复杂或越先进的模型表现越好, 而是需要结合自己的特定任务, 从对数据的分析和实验结果中获得最佳答案.
- 第五步: 构建评估函数并进行预测
- 构建传统RNN评估函数evaluateRNN.
- 构建LSTM评估函数evaluateLSTM.
- 构建GRU评估函数evaluateGRU.
- 构建预测函数predict.
2.2 使用seq2seq模型架构实现英译法任务
- 学习目标:
- 更深一步了解seq2seq模型架构和翻译数据集.
- 掌握使用基于GRU的seq2seq模型架构实现翻译的过程.
- 掌握Attention机制在解码器端的实现过程.
- seq2seq模型架构:
- seq2seq模型架构分析:
- 从图中可知, seq2seq模型架构, 包括两部分分别是encoder(编码器)和decoder(解码器), 编码器和解码器的内部实现都使用了GRU模型, 这里它要完成的是一个中文到英文的翻译: 欢迎 来 北京 –> welcome to BeiJing. 编码器首先处理中文输入”欢迎 来 北京”, 通过GRU模型获得每个时间步的输出张量,最后将它们拼接成一个中间语义张量c, 接着解码器将使用这个中间语义张量c以及每一个时间步的隐层张量, 逐个生成对应的翻译语言.
- 翻译数据集:
- 下载地址: https://download.pytorch.org/tutorial/data.zip
- 数据文件预览:
1 | - data/ |
1 | She feeds her dog a meat-free diet. Elle fait suivre à son chien un régime sans viande. |
- 基于GRU的seq2seq模型架构实现翻译的过程:
- 第一步: 导入必备的工具包.
- 第二步: 对持久化文件中数据进行处理, 以满足模型训练要求.
- 第三步: 构建基于GRU的编码器和解码器.
- 第四步: 构建模型训练函数, 并进行训练.
- 第五步: 构建模型评估函数, 并进行测试以及Attention效果分析.
- 第一步: 导入必备的工具包
- python版本使用3.6.x, pytorch版本使用1.3.1
1 | pip install torch==1.3.1 |
1 |
|
第二步: 对持久化文件中数据进行处理, 以满足模型训练要求
- 将指定语言中的词汇映射成数值:
1 | # 起始标志 |
- 实例化参数:
1 | name = "eng" |
- 输入参数:
1 | sentence = "hello I am Jay" |
- 调用:
1 | engl = Lang(name) |
- 输出效果:
1 | word2index: {'hello': 2, 'I': 3, 'am': 4, 'Jay': 5} |
- 字符规范化:
1 | # 将unicode转为Ascii, 我们可以认为是去掉一些语言中的重音标记:Ślusàrski |
- 输入参数:
1 | s = "Are you kidding me?" |
- 调用:
1 | nsr = normalizeString(s) |
- 输出效果:
1 | are you kidding me ? |
- 将持久化文件中的数据加载到内存, 并实例化类Lang
1 | data_path = '../Downloads/data/eng-fra.txt' |
- 输入参数:
1 | lang1 = "eng" |
- 调用:
1 | input_lang, output_lang, pairs = readLangs(lang1, lang2) |
- 输出效果:
1 | input_lang: <__main__.Lang object at 0x11ecf0828> |
- 过滤出符合我们要求的语言对:
1 | # 设置组成句子中单词或标点的最多个数 |
- 输入参数:
1 | # 输入参数pairs使用readLangs函数的输出结果pairs |
- 调用:
1 | fpairs = filterPairs(pairs) |
- 输出效果:
1 | 过滤后的pairs前五个: [['i m .', 'j ai ans .'], ['i m ok .', 'je vais bien .'], ['i m ok .', 'ca va .'], ['i m fat .', 'je suis gras .'], ['i m fat .', 'je suis gros .']] |
- 对以上数据准备函数进行整合, 并使用类Lang对语言对进行数值映射:
1 | def prepareData(lang1, lang2): |
- 调用:
1 | input_lang, output_lang, pairs = prepareData('eng', 'fra') |
- 输出效果:
1 | input_n_words: 2803 |
- 将语言对转化为模型输入需要的张量:
1 | def tensorFromSentence(lang, sentence): |
- 输入参数:
1 | # 取pairs的第一条 |
- 调用:
1 | pair_tensor = tensorsFromPair(pair) |
- 输出效果:
1 | (tensor([[2], |
- 第三步: 构建基于GRU的编码器和解码器
- 构建基于GRU的编码器
- 编码器结构图:
1 | class EncoderRNN(nn.Module): |
- 实例化参数:
1 | hidden_size = 25 |
- 输入参数:
1 | # pair_tensor[0]代表源语言即英文的句子,pair_tensor[0][0]代表句子中 |
- 调用:
1 | encoder = EncoderRNN(input_size, hidden_size) |
- 输出效果:
1 | tensor([[[ 1.9149e-01, -2.0070e-01, -8.3882e-02, -3.3037e-02, -1.3491e-01, |
- 构建基于GRU的解码器
- 解码器结构图:
1 | class DecoderRNN(nn.Module): |
- 实例化参数:
1 | hidden_size = 25 |
- 输入参数:
1 | # pair_tensor[1]代表目标语言即法文的句子,pair_tensor[1][0]代表句子中的第一个词 |
- 调用:
1 | decoder = DecoderR hidden_size, output_size) |
- 输出效果:
1 | tensor([[-2.3554, -2.3551, -2.4361, -2.2158, -2.2550, -2.6237, -2.2917, -2.2663, |
- 构建基于GRU和Attention的解码器
- 解码器结构图:
1 | class AttnDecoderRNN(nn.Module): |
- 实例化参数:
1 | hidden_size = 25 |
- 输入参数:
1 | input = pair_tensor[1][0] |
- 调用:
1 | decoder = AttnDecoderR hidden_size, output_size) |
- 输出效果:
1 | tensor([[-2.3556, -2.1418, -2.2012, -2.5109, -2.4025, -2.2182, -2.2123, -2.4608, |
- 第四步: 构建模型训练函数, 并进行训练
- 什么是teacher_forcing?
- 它是一种用于序列生成任务的训练技巧, 在seq2seq架构中, 根据循环神经网络理论,解码器每次应该使用上一步的结果作为输入的一部分, 但是训练过程中,一旦上一步的结果是错误的,就会导致这种错误被累积,无法达到训练效果, 因此,我们需要一种机制改变上一步出错的情况,因为训练时我们是已知正确的输出应该是什么,因此可以强制将上一步结果设置成正确的输出, 这种方式就叫做teacher_forcing.
- teacher_forcing的作用:
- 能够在训练的时候矫正模型的预测,避免在序列生成的过程中误差进一步放大.
- teacher_forcing能够极大的加快模型的收敛速度,令模型训练过程更快更平稳.
- 构建训练函数:
1 | # 设置teacher_forcing比率为0.5 |
- 构建时间计算函数:
1 | # 导入时间和数学工具包 |
- 输入参数:
1 | # 假定模型训练开始时间是10min之前 |
- 调用:
1 | period = timeSince(since) |
- 输出效果:
1 | 10m 0s |
- 调用训练函数并打印日志和制图:
1 | # 导入plt以便绘制损失曲线 |
- 输入参数:
1 | # 设置隐层大小为256 ,也是词嵌入维度 |
- 调用:
1 | # 调用trainIters进行模型训练,将编码器对象encoder1,码器对象attn_decoder1,迭代步数,日志打印间隔传入其中 |
- 输出效果:
1 | 3m 35s (5000 6%) 3.4159 |
- 损失下降曲线:
- 损失曲线分析:
- 一直下降的损失曲线, 说明模型正在收敛, 能够从数据中找到一些规律应用于数据.
第五步: 构建模型评估函数, 并进行测试以及Attention效果分析.
- 构建模型评估函数:
1 | def evaluate(encoder, decoder, sentence, max_length=MAX_LENGTH): |
- 随机选择指定数量的数据进行评估:
1 | def evaluateRandomly(encoder, decoder, n=6): |
- 调用:
1 | # 调用evaluateRandomly进行模型测试,将编码器对象encoder1,码器对象attn_decoder1传入其中 |
- 输出效果:
1 | > i m impressed with your french . |
- Attention张量制图:
1 | sentence = "we re both teachers ." |
- 输出效果:
1 | ['nous', 'sommes', 'toutes', 'deux', 'enseignantes', '.', '<EOS>'] |
- Attention可视化:
- 分析:
- Attention图像的纵坐标代表输入的源语言各个词汇对应的索引, 0-6分别对应[“we”, “re”, “both”, “teachers”, “.”, “”], 纵坐标代表生成的目标语言各个词汇对应的索引, 0-7代表[‘nous’, ‘sommes’, ‘toutes’, ‘deux’, ‘enseignantes’, ‘.’, ‘’], 图中浅色小方块(颜色越浅说明影响越大)代表词汇之间的影响关系, 比如源语言的第1个词汇对生成目标语言的第1个词汇影响最大, 源语言的第4,5个词对生成目标语言的第5个词会影响最大, 通过这样的可视化图像, 我们可以知道Attention的效果好坏, 与我们人为去判定到底还有多大的差距. 进而衡量我们训练模型的可用性.
小节总结:
- seq2seq模型架构分析:
- 从图中可知, seq2seq模型架构, 包括两部分分别是encoder(编码器)和decoder(解码器), 编码器和解码器的内部实现都使用了GRU模型, 这里它要完成的是一个中文到英文的翻译: 欢迎 来 北京 –> welcome to BeiJing. 编码器首先处理中文输入”欢迎 来 北京”, 通过GRU模型获得每个时间步的输出张量,最后将它们拼接成一个中间语义张量c, 接着解码器将使用这个中间语义张量c以及每一个时间步的隐层张量, 逐个生成对应的翻译语言.
- 基于GRU的seq2seq模型架构实现翻译的过程:
- 第一步: 导入必备的工具包.
- 第二步: 对持久化文件中数据进行处理, 以满足模型训练要求.
- 第三步: 构建基于GRU的编码器和解码器.
- 第四步: 构建模型训练函数, 并进行训练.
- 第五步: 构建模型评估函数, 并进行测试以及Attention效果分析.
- 第一步: 导入必备的工具包
- python版本使用3.6.x, pytorch版本使用1.3.1
- 第二步: 对持久化文件中数据进行处理, 以满足模型训练要求
- 将指定语言中的词汇映射成数值
- 字符规范化
- 将持久化文件中的数据加载到内存, 并实例化类Lang
- 过滤出符合我们要求的语言对
- 对以上数据准备函数进行整合, 并使用类Lang对语言对进行数值映射
- 将语言对转化为模型输入需要的张量
- 第三步: 构建基于GRU的编码器和解码器
- 构建基于GRU的编码器
- 构建基于GRU的解码器
- 构建基于GRU和Attention的解码器
- 第四步: 构建模型训练函数, 并进行训练
- 什么是teacher_forcing: 它是一种用于序列生成任务的训练技巧, 在seq2seq架构中, 根据循环神经网络理论,解码器每次应该使用上一步的结果作为输入的一部分, 但是训练过程中,一旦上一步的结果是错误的,就会导致这种错误被累积,无法达到训练效果, 因此,我们需要一种机制改变上一步出错的情况,因为训练时我们是已知正确的输出应该是什么,因此可以强制将上一步结果设置成正确的输出, 这种方式就叫做teacher_forcing.
- teacher_forcing的作用: 能够在训练的时候矫正模型的预测,避免在序列生成的过程中误差进一步放大. 另外, teacher_forcing能够极大的加快模型的收敛速度,令模型训练过程更快更平稳.
- 构建训练函数train
- 构建时间计算函数timeSince
- 调用训练函数并打印日志和制图
- 损失曲线分析: 一直下降的损失曲线, 说明模型正在收敛, 能够从数据中找到一些规律应用于数据
- 第五步: 构建模型评估函数, 并进行测试以及Attention效果分析
- 构建模型评估函数evaluate
- 随机选择指定数量的数据进行评估
- 进行了Attention可视化分析
- seq2seq模型架构分析: