使用序列到序列模型完成数字加法

    日期: 2021.01

    摘要: 本示例教程介绍如何使用飞桨完成一个数字加法任务,我们将会使用飞桨提供的LSTM的API,组建一个序列到序列模型,并在随机生成的数据集上完成数字加法任务的模型训练与预测。

    本教程基于Paddle 2.0 编写,如果您的环境不是本版本,请先参考官网 Paddle 2.0 。

    二、构建数据集

    • 随机生成数据,并使用生成的数据构造数据集

    • 通过继承 paddle.io.Dataset 来完成数据集的构造

    1. generating datas..
    2. making the dataset...
    3. finish
    • 本次介绍的模型是一个简单的基于 LSTMSeq2Seq 模型

    • 一共有如下四个主要的网络层:

      1. 嵌入层(Embedding):将输入的文本序列转为嵌入向量

      2. 编码层(LSTM):将嵌入向量进行编码

      3. 解码层(LSTM):将编码向量进行解码

    • 损失函数为交叉熵损失函数

    四、模型训练与评估

    • 使用 Adam 作为优化器进行模型训练

    • 以模型准确率作为评价指标

    • 使用 VisualDL 对训练数据进行可视化

    • 训练过程中会同时进行模型评估和最佳模型的保存

    1. # 初始化log写入器
    2. log_writer = LogWriter(logdir="./log")
    3. # 模型参数设置
    4. embedding_size = 128
    5. hidden_size=128
    6. num_layers=1
    7. # 训练参数设置
    8. epoch_num = 50
    9. learning_rate = 0.001
    10. log_iter = 2000
    11. eval_iter = 500
    12. # 定义一些所需变量
    13. global_step = 0
    14. log_step = 0
    15. max_acc = 0
    16. # 实例化模型
    17. model = Addition_Model(
    18. char_len=len(label_dict),
    19. embedding_size=embedding_size,
    20. num_layers=num_layers,
    21. DIGITS=DIGITS)
    22. # 将模型设置为训练模式
    23. model.train()
    24. opt = paddle.optimizer.Adam(
    25. learning_rate=learning_rate,
    26. parameters=model.parameters()
    27. )
    28. # 启动训练,循环epoch_num个轮次
    29. for epoch in range(epoch_num):
    30. # 遍历数据集读取数据
    31. for batch_id, data in enumerate(train_reader()):
    32. # 读取数据
    33. inputs, labels = data
    34. # 模型前向计算
    35. loss, acc = model(inputs, labels=labels)
    36. # 打印训练数据
    37. if global_step%log_iter==0:
    38. print('train epoch:%d step: %d loss:%f acc:%f' % (epoch, global_step, loss.numpy(), acc.numpy()))
    39. log_writer.add_scalar(tag="train/loss", step=log_step, value=loss.numpy())
    40. log_writer.add_scalar(tag="train/acc", step=log_step, value=acc.numpy())
    41. log_step+=1
    42. # 模型验证
    43. if global_step%eval_iter==0:
    44. model.eval()
    45. losses = []
    46. accs = []
    47. for data in dev_reader():
    48. loss, acc = model(inputs, labels=labels)
    49. losses.append(loss.numpy())
    50. accs.append(acc.numpy())
    51. avg_loss = np.concatenate(losses).mean()
    52. avg_acc = np.concatenate(accs).mean()
    53. print('eval epoch:%d step: %d loss:%f acc:%f' % (epoch, global_step, avg_loss, avg_acc))
    54. log_writer.add_scalar(tag="dev/loss", step=log_step, value=avg_loss)
    55. log_writer.add_scalar(tag="dev/acc", step=log_step, value=avg_acc)
    56. # 保存最佳模型
    57. if avg_acc>max_acc:
    58. max_acc = avg_acc
    59. paddle.save(model.state_dict(), 'best_model')
    60. model.train()
    61. # 反向传播
    62. loss.backward()
    63. # 使用优化器进行参数优化
    64. opt.step()
    65. # 清除梯度
    66. opt.clear_grad()
    67. # 全局步数加一
    68. global_step += 1
    69. # 保存最终模型
    70. paddle.save(model.state_dict(),'final_model')
    • 使用保存的最佳模型进行测试
    1. # 反转字符表
    2. label_dict_adv = {v: k for k, v in label_dict.items()}
    3. # 输入计算题目
    4. input_text = '12+40'
    5. # 编码输入为ID
    6. inputs = encoder(input_text, MAXLEN, label_dict)
    7. # 转换输入为向量形式
    8. inputs = np.array(inputs).reshape(-1, MAXLEN)
    9. inputs = paddle.to_tensor(inputs)
    10. # 加载模型
    11. params_dict= paddle.load('best_model')
    12. model.set_dict(params_dict)
    13. # 设置为评估模式
    14. model.eval()
    15. # 模型推理
    16. out = model(inputs)
    17. # 结果转换
    18. result = ''.join([label_dict_adv[_] for _ in np.argmax(out.numpy(), -1).reshape(-1)])
    19. # 打印结果
    20. print('the model answer: %s=%s' % (input_text, result))
    21. print('the true answer: %s=%s' % (input_text, eval(input_text)))

    六、总结

    • 同时,也可以尝试在其他的类似的任务中用飞桨来完成实际的实践