对于一般的深度学习实验,有几个部分需要处理。
- 按照模型的需要对数据进行预处理,并且按批次迭代数据集;
- 定义模型以及优化器等组件;
- 写出训练过程(一般包括 forward/backward 计算,参数更新,log 记录,可视化,定期评估等步骤);
- 配置并运行实验。
对于数据处理,parakeet.data
采用了 paddlepaddle 常用的 Dataset -> DataLoader
的流程。数据处理流程的概览如下:
Dataset --(transform)--> Dataset --+
sampler --+
batch_fn --+-> DataLoader
其中 transform 代表的是对样例的预处理。可以使用 parakeet.data
中的 TransformDataset 来从一个 Dataset 构建另一个 Dataset.
得到想要的 Dataset 之后,提供 sampler 和 batch function, 即可据此构建 DataLoader. DataLoader 产生的结果可以直接用作模型的输入。
详细的使用方式参见 data_cn.
为了对模型的可复用行和功能做较好的平衡,我们把模型按照其特征分为几种。
对于较为常用,可以作为其他更大的模型的部分的模块,我们尽可能将其实现得足够简单和通用,因为它们会被复用。对于含有可训练参数的模块,一般实现为 paddle.nn.Layer
的子类,但它们不是直接面向一个任务,因此不会带上处理未加工的输入和输出的功能。对于不含有可训练参数的模块,可以直接实现为一个函数,其输入输出都是 paddle.Tensor
或其集合。
针对一个特定任务的开箱模型,一般实现为 paddle.nn.Layer
的子类,是一个任务的核心计算单元。为了方便地处理输入和输出,一般还可以为它添加处理未加工的输入输出的功能。比如对于 NLP 任务来说,尽管神经网络接受的输出是文本的 id, 但是为了使模型能够处理未加工的输入,文本预处理的功能,以及文本转 id 的字典,也都应该视作模型的一部分。
当一个模型足够复杂,对其进行模块化切分是更好的选择,尽管拆分出来的小模块的功能也不一定非常通用,可能只是用于某个模型,但是当作么做有利于代码的清晰简洁时,仍然推荐这么做。
在 parakeet 的目录结构中,复用性较高的模块被放在 parakeet.modules, 但是针对特定任务的模型则放在 parakeet.models.
当开发新的模型的时候,开发这需要考虑拆分模块的可行性,以及模块的通用程度,把它们分置于合适的目录。
我们使用 yacs 和 argparse 分别处理配置文件解析和命令行参数解析。关于配置的推荐方式,参考 实验配置.
训练流程一般就是多次训练一个循环体。典型的循环体包含如下的过程:
- 迭代数据集;
- 处理批次数据;
- 神经网络的 forward/backward 计算;
- 参数更新;
- 符合一定条件时,在验证数据集上评估模型;
- 写日志,可视化,以及在某些情况下保存必要的中间结果;
- 保存模型和优化器的状态。
数据处理
包含了数据集以及 batch_function 的定义, 模型和优化器包含了模型的 forward/backward 计算的定义。而在模型和数据都准备好了,我们需要把这些组织起来,完成实验代码。
训练流程的组装,可以参考 实验流程.
实验代码一般以如下的方式组织:
├── README.md (实验的帮助信息)
├── config.py (默认配置)
├── preprocess.py (数据预处理脚本)
├── data.py (Dataset, batch_function 等的定义)
├── synthesis.py (用于生成的代码)
├── train.py (用于训练的代码)
└── utils.py (其他必要的辅助函数)
在这个软件源中包含了几个例子,可以在 Parakeet/examples 中查看。这些实验被作为样例提供给用户,可以直接运行。同时也欢迎用户添加新的模型和实验并为 Parakeet
贡献代码。