尽管我也写了不少年的代码,但是总感觉力有未逮。每次开展一个新的大项目,总要思考很久代码的组织问题。今天干脆总结一下个人领悟的一些原则,看看是否能指导之后的实践。当然要真正系统的学习这些原则,应该参考软件工程方面的书籍。但是由于我并非科班出身,也没有太多时间自学,所以将就如此吧。
一个深度学习项目逻辑中大概有以下组成部分:
- 模型
- 数据
- 训练
- 配置
- 实验
- 推理 其中模型、数据、训练不消多说,配置就是决定架构的模型的超参数,以及决定训练过程的优化器超参数、调度器超参数等等,还有就是管理实验的超参数如日志打印,指标打印等等。
编写这个项目的代码大致有几个原则:
内容和逻辑分开:其中内容是静态的、具体的,包括模型权重、数据集、配置文件,逻辑是管理内容的,例如模型架构代码、数据集处理代码、配置文件管理代码,训练代码。下面是一个案例:
/config /data /model /training /data /model /src /data /model /training能用现成的库就不要重复造轮子。常用库:
- 模型架构和数据集处理
pytorch - 配置文件管理
hydra - 训练代码
lightning
- 模型架构和数据集处理
用 monorepo 维护多个模型实现时,如果一份代码的改动的影响范围有限,那么就把它放在子目录,否则放在根目录。例如下面两个案例:
/config应该放在对应的子目录。因为/config总是从属于某个具体的模型、数据集,因而它的特异性强而复用性弱,因此应该使用软件工程中的就近原则、开闭原则、内聚原则。- 像 conformer 这种既非 pytorch 原生实现中的模块,又非独立使用的模型应该放在一个公共的/core/architecture 目录,因为它足够通用。但是需要注意的是,对 conformer 的改动有两种,如果这种改动自由度高,那么需要使用组合+依赖注入,将这种改动暴露在 conformer 类的接口;如果自由度低,说明特异性高,则应该使用继承,并且这种继承变体应该存放在某个特定模型的子目录中。
两种配置管理方案:
- yaml 方案:纯字符串格式,用 hydra 在运行时实例化,但没有拼写检查。适合调参。
- dataclass 方案:python 格式,在开发阶段即可校验类型、使用自动补全。Transformers 即采用这种做法。适合开发。当然 hydra 也可以用 dataclass 管理配置。 我们的想法是,避免配置层级过多,只对较大的模块或完整模型记录配置,保持配置文件夹的清爽。用 yaml 方案管理 api 稳定的配置,用 dataclass 管理正在开发的 api。