# 项目架构说明 这份文档说明当前项目的代码结构,以及一次任务从入口脚本到训练、导出、样例测试的执行链路。重点说明四件事: - 数据从哪里来,如何变成训练数据 - 模型在哪里构建,在哪里编译,如何转换成推理模型 - 训练、导出、样例测试如何共用一套路径、日志和检查点规则 - Gradio 展示如何复用导出的模型,而不参与训练抽象 ## 总体分层 项目主要分成五层。 ### `tasks` `tasks/*/runner.py` 是每个任务的命令入口。 它负责声明开发配置和生产配置,包括数据源、模型构建器、训练规则、生成规则和检查点加载规则。入口脚本本身不写训练循环,而是把配置好的 Pipeline 交给 `PipelineRunner`。 典型结构是: ```python pipeline = resolve_env( SupervisedModelPipeline(...), SupervisedModelPipeline(...) ) pipeline_runner = PipelineRunner(pipeline) ``` 文本生成任务也遵循同样形状,只是 Pipeline 类型换成 `TextGenerationPipeline`。 `tasks/*/gradio.py` 是展示入口。它通常加载已导出的模型或生成器,负责页面交互,不承担训练编排职责。 ### `data` `src/deep_learning/data` 负责把原始数据变成 Pipeline 能消费的数据源。 当前对外契约集中在 `src/deep_learning/data/spec/__init__.py`: - `SupervisedDataSource`:用于分类、分割、检测这类有监督任务,需要提供 `training_ds()` 和 `test_examples(model)` - `TextGenerationDataSource`:用于文本生成任务,需要提供原始文档、token 数据、分词资源和数据配置 - `TokenizerBundle`:文本推理时模型之外需要携带的分词器、解码函数、词表大小、结束标记等资源 这里的设计取向是让数据源自己说明训练数据怎么构建。比如 batch size、validation batches、sequence length 这类更贴近数据切分的配置,放在具体 DataSource 里,而不是塞进通用训练规则里。 ### `models` `src/deep_learning/models` 负责模型构建、训练模型编译、推理模型转换和模型加载。 当前对外契约集中在 `src/deep_learning/models/spec/__init__.py`: - `SupervisedModelBuilder`:负责构建有监督任务的训练模型、编译训练模型、转换或加载推理模型 - `TextGenerationModelBuilder`:负责构建文本生成训练产物、编译模型、转换或加载文本推理产物 - `ModelArtifact` 和 `TextGenerationModel`:用统一对象承载模型产物 模型层不负责找数据、不负责写训练循环,也不负责 Gradio 页面。它只回答“这个任务的模型长什么样,怎么训练,怎么用于推理”。 ### `pipeline` `src/deep_learning/pipeline` 是训练、导出和测试样例的统一编排层。 它负责: - 创建运行目录、日志目录、检查点目录和 TensorBoard 目录 - 开启通用运行环境,例如 mixed precision - 写出配置日志 - 加载或创建训练模型 - 编译模型并组织 callbacks - 执行 Keras 训练 - 按检查点导出完整模型 - 从导出模型或训练检查点执行样例测试 `PipelineRunner` 暴露三个动作: - `train` - `export` - `test` 因此任务入口只需要声明一个 Pipeline,再交给 `PipelineRunner`。调用方不需要知道训练、导出和测试内部要走哪些目录和检查点规则。 ### `ui` `src/deep_learning/ui` 和 `tasks/*/gradio.py` 负责展示层。 展示层的定位是“使用推理产物”,而不是“参与训练流程”。它可以复用数据集里的标签、分词器、样例展示能力,也可以复用 Pipeline 或模型构建器的加载能力,但不应该成为训练抽象的一部分。 ## 两条主流程 当前项目主要有两条流程:有监督任务和文本生成任务。 ### 有监督任务 有监督任务使用: - `SupervisedDataSource` - `SupervisedModelBuilder` - `SupervisedModelPipeline` 适用场景包括图片分类、语义分割,以及后续可以统一进来的其他监督学习任务。 训练链路是: 1. `tasks/*/runner.py` 声明 `SupervisedModelPipeline` 2. `PipelineRunner.train()` 调用 `pipeline.train()` 3. Pipeline 创建运行目录,写配置日志 4. Pipeline 从检查点恢复训练模型,或调用 `model_builder.build_training_artifact()` 创建新模型 5. Pipeline 调用 `model_builder.compile_training_model(model)` 编译模型 6. Pipeline 调用 `data_source.training_ds()` 得到训练集和验证集 7. Pipeline 追加通用 callbacks,包括检查点和日志 8. Pipeline 调用 `model.fit(...)` 导出链路是: 1. `PipelineRunner.export()` 调用 `pipeline.export_model()` 2. Pipeline 按导出规则加载训练检查点 3. Pipeline 调用 `model_builder.convert_to_inference_artifact(...)` 4. Pipeline 用 `SaveKerasModelStage` 保存完整 `.keras` 模型到 `saved/models/` 样例测试链路是: 1. `PipelineRunner.test()` 调用 `pipeline.test_examples(checkpoint=...)` 2. 如果 checkpoint 是 `test`,Pipeline 从 `saved/models/` 加载导出模型 3. 如果 checkpoint 是 `train`,Pipeline 从训练检查点加载权重并转换推理模型 4. Pipeline 调用 `data_source.test_examples(model)` 展示样例结果 ### 文本生成任务 文本生成任务使用: - `TextGenerationDataSource` - `TextGenerationModelBuilder` - `TextGenerationPipeline` 适用场景包括诗词 GPT、Wiki GPT,以及字符级或 token 级文本生成任务。 训练链路是: 1. `tasks/*/runner.py` 声明 `TextGenerationPipeline` 2. `PipelineRunner.train()` 调用 `pipeline.train()` 3. Pipeline 加载原始文档和 tokenizer 信息 4. Pipeline 从 `tokens_ds()` 切出验证数据和训练数据 5. Pipeline 从检查点恢复模型,或根据词表大小和序列长度创建新模型 6. Pipeline 调用 `model_builder.compile_training_model(model)` 编译模型 7. Pipeline 根据 `GenerationRule` 追加生成日志回调 8. Pipeline 调用通用 `KerasTrainStage` 执行训练 导出链路是: 1. Pipeline 准备文本数据和 tokenizer 资源 2. Pipeline 按导出规则加载训练检查点 3. Pipeline 调用 `model_builder.convert_to_inference_artifact(...)` 4. Pipeline 保存完整推理模型 样例测试链路是: 1. Pipeline 准备文本数据和 tokenizer 资源 2. 如果 checkpoint 是 `test`,从导出模型加载推理产物 3. 如果 checkpoint 是 `train`,从训练检查点加载训练产物并转换推理产物 4. Pipeline 构建 `TextGenerator` 5. Pipeline 使用 `GenerationRule.prompts_generator` 生成提示词,并打印生成结果 ## 配置与产物 当前配置主要分为几类。 - `TrainingRule`:训练轮数和每轮步数 - `GenerationRule`:文本生成提示词来源和采样策略 - `CheckpointLoadRules`:训练、导出、测试各自如何选择检查点 - `CheckpointConfig`:检查点目录、具体路径、轮次和文件后缀 运行时目录由 `build_runtime` 统一生成,主要包括: - `logs/`:配置日志和生成日志 - `checkpoints/`:训练过程中的权重检查点 - `saved/models/`:导出的完整模型 这样的划分让任务入口只声明“我想怎么跑”,Pipeline 统一处理“文件放哪里、检查点怎么找、导出怎么命名”。 ## 历史结构与当前结构 上一版结构更强调通用阶段编排: - 一个通用 `Pipeline` 串联 runtime、data source、preprocess、model、compile、train、inference、export 等阶段 - 每类任务通过 builder 装配 Pipeline,例如 image classification builder、segmentation builder、text builder、yolo builder - 任务目录里常见多个入口脚本,例如 `train.py`、`save_model.py`、`generate.py` - 一些数据切分相关配置也放在训练规则中统一传递 这种设计的优点是阶段边界清楚,也更容易表达“所有任务都经过同一套通用流程”。但它的阅读成本更高:读者通常需要在任务入口、任务 builder、通用 Pipeline、各个 Stage、模型构建器和数据集之间多次跳转,才能理解一次训练具体发生了什么。 当前结构的设计取向是降低抽象层级,减少装配跳转,提升可读性和可改造性: - 任务入口统一为 `tasks/*/runner.py` - 入口脚本直接声明 DataSource、ModelBuilder 和 Pipeline - 有监督任务用 `SupervisedModelPipeline` 直接表达主流程 - 文本任务保留必要的文本阶段对象,但主入口仍收敛到 `TextGenerationPipeline` - batch、validation、sequence length 等数据切分配置回到 DataSource - 模型构建、编译、训练产物和推理产物转换回到 ModelBuilder - `PipelineRunner` 统一暴露 `train`、`export`、`test` 这不是在否定上一版设计。更准确地说,这是一次项目定位变化:从“抽象完整、阶段统一”,转向“入口更清楚、跳转更少、读者更容易改”。对于当前项目规模,读者能快速看懂数据、模型、训练和展示之间的关系,比保留更通用的阶段装配能力更重要。 ## 当前边界 这个项目目前仍是小型 Keras 任务框架,不是完整生产级训练平台。 它适合: - 学习 Keras 任务如何从数据走到训练、导出和展示 - 快速复制一个已有任务并改成自己的数据和模型 - 在个人实验里保持统一目录、统一入口和统一检查点习惯