From e88672825a7a1e7a25f5b6db7cc3993075bdcb45 Mon Sep 17 00:00:00 2001 From: Chengmo Date: Thu, 11 Jun 2020 11:21:41 +0800 Subject: [PATCH] update doc (#67) Co-authored-by: tangwei12 --- README.md | 10 +- doc/design.md | 78 +++++----- doc/{development.md => model_develop.md} | 0 doc/predict.md | 2 +- doc/serving.md | 2 +- doc/train.md | 93 +++++++++--- doc/trainer_develop.md | 181 +++++++++++++++++++++++ 7 files changed, 305 insertions(+), 61 deletions(-) rename doc/{development.md => model_develop.md} (100%) create mode 100644 doc/trainer_develop.md diff --git a/README.md b/README.md index b326d4c5..53e09184 100644 --- a/README.md +++ b/README.md @@ -38,15 +38,15 @@ | 匹配 | [DSSM](models/match/dssm/model.py) | ✓ | x | ✓ | | 匹配 | [MultiView-Simnet](models/match/multiview-simnet/model.py) | ✓ | x | ✓ | | 召回 | [TDM](models/treebased/tdm/model.py) | ✓ | x | ✓ | - | 召回 | [fasttext](models/recall/fasttext/model.py) | ✓ | x | x | + | 召回 | [fasttext](models/recall/fasttext/model.py) | ✓ | x | x | | 召回 | [Word2Vec](models/recall/word2vec/model.py) | ✓ | x | ✓ | | 召回 | [SSR](models/recall/ssr/model.py) | ✓ | ✓ | ✓ | | 召回 | [Gru4Rec](models/recall/gru4rec/model.py) | ✓ | ✓ | ✓ | | 召回 | [Youtube_dnn](models/recall/youtube_dnn/model.py) | ✓ | ✓ | ✓ | | 召回 | [NCF](models/recall/ncf/model.py) | ✓ | ✓ | ✓ | - | 排序 | [Logistic Regression](models/rank/logistic_regression/model.py) | ✓ | x | ✓ | + | 排序 | [Logistic Regression](models/rank/logistic_regression/model.py) | ✓ | x | ✓ | | 排序 | [Dnn](models/rank/dnn/model.py) | ✓ | x | ✓ | - | 排序 | [FM](models/rank/fm/model.py) | ✓ | x | ✓ | + | 排序 | [FM](models/rank/fm/model.py) | ✓ | x | ✓ | | 排序 | [FFM](models/rank/ffm/model.py) | ✓ | x | ✓ | | 排序 | [Pnn](models/rank/pnn/model.py) | ✓ | x | ✓ | | 排序 | [DCN](models/rank/dcn/model.py) | ✓ | x | ✓ | @@ -128,8 +128,8 @@ python -m paddlerec.run -m paddlerec.models.rank.dnn ### 进阶教程 * [自定义Reader](doc/custom_reader.md) -* [自定义模型](doc/development.md) -* [自定义流程](doc/development.md) +* [自定义模型](doc/model_develop.md) +* [自定义流程](doc/trainer_develop.md) * [yaml配置说明](doc/yaml.md) * [PaddleRec设计文档](doc/design.md) diff --git a/doc/design.md b/doc/design.md index f88401aa..c09889dc 100644 --- a/doc/design.md +++ b/doc/design.md @@ -4,17 +4,13 @@ ## PaddleRec 整体设计概览 PaddleRec将推荐模型的训练与预测流程,整体抽象为了五个大模块: -* [Engine 流程执行引擎](#engine) -* [Trainer 流程具体定义](#trainer) -* [Model 模型组网定义](#model) -* [Reader 数据读取定义](#reader) -* [Metric 精度指标打印](#metric) - -层级结构,以及一键启动训练时的调用关系如下图所示: - -

- -

+- [PaddleRec 设计](#paddlerec-设计) + - [PaddleRec 整体设计概览](#paddlerec-整体设计概览) + - [Engine](#engine) + - [Trainer](#trainer) + - [Model](#model) + - [Reader](#reader) + - [Metric](#metric) core的文件结构如下,后续分别对各个模块进行介绍。 ``` @@ -50,7 +46,7 @@ Engine是整体训练的执行引擎,与组网逻辑及数据无关,只与 运行设备是指: - CPU - GPU -- AI芯片 +- 其他AI芯片 在用户调用`python -m paddlerec.run`时,首先会根据`yaml`文件中的配置信息选择合适的执行引擎, 以下代码位于[run.py](../run.py): ```python @@ -62,15 +58,36 @@ engine.run() 我们以`single engine`为例,概览engine的行为: ```python -def single_engine(args): - trainer = get_trainer_prefix(args) + "SingleTrainer" +def single_train_engine(args): + _envs = envs.load_yaml(args.model) + run_extras = get_all_inters_from_yaml(args.model, ["train.", "runner."]) + trainer_class = run_extras.get( + "runner." + _envs["mode"] + ".trainer_class", None) + + if trainer_class: + trainer = trainer_class + else: + trainer = "GeneralTrainer" + + executor_mode = "train" + fleet_mode = run_extras.get("runner." + _envs["mode"] + ".fleet_mode", + "ps") + device = run_extras.get("runner." + _envs["mode"] + ".device", "cpu") + selected_gpus = run_extras.get( + "runner." + _envs["mode"] + ".selected_gpus", "0") + selected_gpus_num = len(selected_gpus.split(",")) + if device.upper() == "GPU": + assert selected_gpus_num == 1, "Single Mode Only Support One GPU, Set Local Cluster Mode to use Multi-GPUS" + single_envs = {} + single_envs["selsected_gpus"] = selected_gpus + single_envs["FLAGS_selected_gpus"] = selected_gpus single_envs["train.trainer.trainer"] = trainer + single_envs["fleet_mode"] = fleet_mode + single_envs["train.trainer.executor_mode"] = executor_mode single_envs["train.trainer.threads"] = "2" - single_envs["train.trainer.engine"] = "single" - single_envs["train.trainer.device"] = args.device single_envs["train.trainer.platform"] = envs.get_platform() - print("use {} engine to run model: {}".format(trainer, args.model)) + single_envs["train.trainer.engine"] = "single" set_runtime_envs(single_envs, args.model) trainer = TrainerFactory.create(args.model) @@ -91,34 +108,29 @@ Engine的自定义实现,可以参考[local_cluster.py](../core/engine/local_c `Trainer`是训练与预测流程的具体实现,会run模型中定义的各个流程,与model、reader、metric紧密相关。PaddleRec以有限状态机的逻辑定义了训练中的各个阶段,不同的Trainer子类会分别实现阶段中的特殊需求。有限状态机的流程在`def processor_register()`中注册。 -我们以SingleTrainer为例,概览Trainer行为: +我们以GeneralTrainer为例,概览Trainer行为: ```python class SingleTrainer(TranspileTrainer): def processor_register(self): + print("processor_register begin") self.regist_context_processor('uninit', self.instance) - self.regist_context_processor('init_pass', self.init) + self.regist_context_processor('network_pass', self.network) self.regist_context_processor('startup_pass', self.startup) - if envs.get_platform() == "LINUX" and envs.get_global_env("dataset_class", None, "train.reader") != "DataLoader": - self.regist_context_processor('train_pass', self.dataset_train) - else: - self.regist_context_processor('train_pass', self.dataloader_train) - - self.regist_context_processor('infer_pass', self.infer) + self.regist_context_processor('train_pass', self.runner) self.regist_context_processor('terminal_pass', self.terminal) ``` SingleTrainer首先注册了完成任务所需的步骤,各步骤首先按照注册顺序加入`Trainer`基类中名为`status_processor`的字典,运行的先后顺序,可以在每个执行步骤中改变`context['status']`的值,指定下一步运行哪个步骤。 -SingleTrainer指定了以下6个步骤: -1. uninit:默认排在首位,通过环境变量决定model的对象 -1. init_pass:调用model_的接口,生成模型的组网,初始化fetch及metric的变量 -2. startup_pass:初始化模型组网中的各个参数,run(fluid.default_startup_program) -3. train_pass:会根据环境分别调用`dataset`与`dataloader`进行训练的流程。 -4. infer_pass:在训练结束后,会对训练保存的模型在测试集上验证效果 -5. terminal_pass:打印全局变量及预测结果等自定义的信息。 +SingleTrainer指定了以下5个步骤: +1. uninit:默认排在首位,通过环境变量启动paddle分布式的实例,执行在模型训练前的所有操作。 +2. network_pass:根据模型组网生成训练的program +3. startup_pass:初始化模型组网中的各个参数,以及加载模型 +4. train_pass:会根据环境分别调用`dataset`与`dataloader`进行训练的流程。 +5. terminal_pass:停止worker,以及执行模型训练后的所有操作 -Trainer的自定义实现,可以参照[single_trainer.py](../core/trainers/single_trainer.py) +Trainer的自定义实现,可以参照[general_trainer.py](../core/trainers/general_trainer.py) ## Model diff --git a/doc/development.md b/doc/model_develop.md similarity index 100% rename from doc/development.md rename to doc/model_develop.md diff --git a/doc/predict.md b/doc/predict.md index 91291867..4957713d 100644 --- a/doc/predict.md +++ b/doc/predict.md @@ -9,7 +9,7 @@ mode: runner_infer # 执行名为 runner1 的运行器 runner: - name: runner_infer # 定义 runner 名为 runner1 - class: single_infer # 执行单机预测 class = single_infer + class: infer # 执行单机预测 class = infer device: cpu # 执行在 cpu 上 init_model_path: "init_model" # 指定初始化模型的地址 print_interval: 10 # 预测信息的打印间隔,以batch为单位 diff --git a/doc/serving.md b/doc/serving.md index da33ce84..5a74f7e9 100644 --- a/doc/serving.md +++ b/doc/serving.md @@ -9,7 +9,7 @@ mode: runner_train # 执行名为 runner_train 的运行器 runner: - name: runner_train # 定义 runner 名为 runner_train - class: single_train # 执行单机训练 class = single_train + class: train # 执行单机训练 class = train device: cpu # 执行在 cpu 上 epochs: 10 # 训练轮数 diff --git a/doc/train.md b/doc/train.md index 9df3806b..3987bdc5 100644 --- a/doc/train.md +++ b/doc/train.md @@ -1,5 +1,7 @@ # PaddleRec 启动训练 + + ## 启动方法 ### 1. 启动内置模型的默认配置训练 @@ -27,29 +29,34 @@ python -m paddlerec.run -m paddlerec.models.recall.word2vec - **没有改动模型组网** -假如你将paddlerec代码库克隆在了`/home/PaddleRec`,并修改了`/home/PaddleRec/models/rank/dnn/config.yaml`,则如下启动训练 + 假如你将paddlerec代码库克隆在了`/home/PaddleRec`,并修改了`/home/PaddleRec/models/rank/dnn/config.yaml`,则如下启动训练 -```shell -python -m paddlerec.run -m /home/PaddleRec/models/rank/dnn/config.yaml -``` + ```shell + python -m paddlerec.run -m /home/PaddleRec/models/rank/dnn/config.yaml + ``` -paddlerec 运行的是在paddlerec库安装目录下的组网文件(model.py) + paddlerec 运行的是在paddlerec库安装目录下的组网文件(model.py),但个性化配置`config.yaml`是用的是指定路径下的yaml文件。 - **改动了模型组网** -假如你将paddlerec代码库克隆在了`/home/PaddleRec`,并修改了`/home/PaddleRec/models/rank/dnn/model.py`, 以及`/home/PaddleRec/models/rank/dnn/config.yaml`,则首先需要更改`yaml`中的`workspace`的设置: + 假如你将paddlerec代码库克隆在了`/home/PaddleRec`,并修改了`/home/PaddleRec/models/rank/dnn/model.py`, 以及`/home/PaddleRec/models/rank/dnn/config.yaml`,则首先需要更改`yaml`中的`workspace`的设置: -```yaml -workspace: /home/PaddleRec/models/rank/dnn/ -``` + ```yaml + workspace: /home/PaddleRec/models/rank/dnn/ + ``` -再执行: + 再执行: -```shell -python -m paddlerec.run -m /home/PaddleRec/models/rank/dnn/config.yaml -``` + ```shell + python -m paddlerec.run -m /home/PaddleRec/models/rank/dnn/config.yaml + ``` + + paddlerec 运行的是绝对路径下的组网文件(model.py)以及个性化配置文件(config.yaml) -paddlerec 运行的是绝对路径下的组网文件(model.py) + + + +## yaml训练配置 ### yaml中训练相关的概念 @@ -58,19 +65,18 @@ paddlerec 运行的是绝对路径下的组网文件(model.py) - **`runner`** : runner是训练的引擎,亦可称之为运行器,在runner中定义执行设备(cpu、gpu),执行的模式(训练、预测、单机、多机等),以及运行的超参,例如训练轮数,模型保存地址等。 - **`phase`** : phase是训练中的阶段的概念,是引擎具体执行的内容,该内容是指:具体运行哪个模型文件,使用哪个reader。 -PaddleRec每次运行时,只会执行一个运行器,通过`mode`指定`runner`的名字。但每个运行器可以执行多个`phase`,所以PaddleRec支持一键启动多阶段的训练。 +PaddleRec每次运行时,会执行一个运行器,通过`mode`指定`runner`的名字。每个运行器可以执行多个`phase`,所以PaddleRec支持一键启动多阶段的训练。 +### 单机CPU训练 -### 单机训练启动配置 - -下面我们开始定义一个单机训练的`runner`: +下面我们开始定义一个单机CPU训练的`runner`: ```yaml -mode: runner_train # 执行名为 runner_train 的运行器 +mode: single_cpu_train # 执行名为 single_cpu_train 的运行器 runner: -- name: runner_train # 定义 runner 名为 runner_train - class: single_train # 执行单机训练 class = single_train +- name: single_cpu_train # 定义 runner 名为 single_cpu_train + class: train # 执行单机训练,亦可为 single_train device: cpu # 执行在 cpu 上 epochs: 10 # 训练轮数 @@ -101,3 +107,48 @@ dataset: dense_slots: "dense_var:13" # dense参数的维度定义 ``` + +### 单机单卡GPU训练 + +具体执行内容与reader与前述相同,下面介绍需要改动的地方 + +```yaml +mode: single_gpu_train # 执行名为 single_gpu_train 的运行器 + +runner: +- name: single_gpu_train # 定义 runner 名为 single_gpu_train + class: train # 执行单机训练,亦可为 single_train + device: gpu # 执行在 gpu 上 + selected_gpus: "0" # 默认选择在id=0的卡上执行训练 + epochs: 10 # 训练轮数 +``` + +### 单机多卡GPU训练 + +具体执行内容与reader与前述相同,下面介绍需要改动的地方 + +```yaml +mode: single_multi_gpu_train # 执行名为 single_multi_gpu_train 的运行器 + +runner: +- name: single_multi_gpu_train # 定义 runner 名为 single_multi_gpu_train + class: train # 执行单机训练,亦可为 single_train + device: gpu # 执行在 gpu 上 + selected_gpus: "0,1,2,3" # 选择多卡执行训练 + epochs: 10 # 训练轮数 +``` + +### 本地模拟参数服务器训练 +具体执行内容与reader与前述相同,下面介绍需要改动的地方 + +```yaml +mode: local_cluster_cpu_train # 执行名为 local_cluster_cpu_train 的运行器 + +runner: +- name: local_cluster_cpu_train # 定义 runner 名为 runner_train + class: local_cluster # 执行本地模拟分布式——参数服务器训练 + device: cpu # 执行在 cpu 上(paddle后续版本会支持PS-GPU) + worker_num: 1 # (可选)worker进程数量,默认1 + server_num: 1 # (可选)server进程数量,默认1 + epochs: 10 # 训练轮数 +``` diff --git a/doc/trainer_develop.md b/doc/trainer_develop.md new file mode 100644 index 00000000..be72e004 --- /dev/null +++ b/doc/trainer_develop.md @@ -0,0 +1,181 @@ +# 如何添加自定义流程 + +模型训练的流程也可以像`model`及`reader`一样,由用户自定义,并在`config.yaml`中指定路径,由PaddleRec调用。 + +PaddleRec可自定义的流程有如下5个: +1. **instance**: 执行训练前的所有操作 +2. **network**:执行组网的前向/反向,训练策略的添加 +3. **startup**:执行模型的初始化,加载 +4. **runnner**: 执行模型的训练 +5. **terminal**: 执行训练后的所有操作 + +## instance + +instance由GeneralTrainer首先调用,执行模型组网前的所有操作。用户可以在这里进行下载数据,import不同的包,配置环境变量等操作。instance的官方实现位于[instance.py](../core/trainers/framework/instance.py),instance基类定义如下: + +```python +class InstanceBase(object): + def __init__(self, context): + pass + + def instance(self, context): + pass +``` + +您需要继承`InstanceBase`并命名为`Instance`,完成`instance`的实现,通过上下文信息字典`context`拿到模型所需信息,及保存相关配置。 + +## network + +network将在instanc后调用,执行模型的组网。network的官方实现位于[network.py](../core/trainers/framework/network.py),network基类定义如下: + +```python +class NetworkBase(object): + def __init__(self, context): + pass + + def build_network(self, context): + pass +``` + +可参照其他模式的实现方式,自定其中的部分步骤。您需要您需要继承`NetworkBase`并命名为`Network`,完成`build_network`的实现,通过上下文信息字典`context`拿到模型所需信息,并在context中保存模型的program与scope信息,例如: + +```python +context["model"][model_dict["name"]][ + "main_program"] = train_program +context["model"][model_dict["name"]][ + "startup_program"] = startup_program +context["model"][model_dict["name"]]["scope"] = scope +context["model"][model_dict["name"]]["model"] = model +context["model"][model_dict["name"]][ + "default_main_program"] = train_program.clone() +``` + +## startup + +startup执行网络参数的初始化,抑或模型的热启动,主要功能是执行`exe.run(fluid.default_startup_program())`。 startup的官方实现在[startup](../core/trainers/framework/startup.py) + +```python +class StartupBase(object): + def __init__(self, context): + pass + + def startup(self, context): + pass + + def load(self, context, is_fleet=False, main_program=None): + dirname = envs.get_global_env( + "runner." + context["runner_name"] + ".init_model_path", None) + if dirname is None or dirname == "": + return + print("going to load ", dirname) + if is_fleet: + context["fleet"].load_persistables(context["exe"], dirname) + else: + fluid.io.load_persistables( + context["exe"], dirname, main_program=main_program) +``` + +自定义startup流程,您需要您需要继承`StartupBase`并命名为`Startup`,实现该类型中startup成员函数。 + +## runner + +runner是运行的主要流程,主要功能是reader的运行,网络的运行,指标的打印以及模型的保存。以参数服务器Runner为示例,如下: + +```python +class PSRunner(RunnerBase): + def __init__(self, context): + print("Running PSRunner.") + pass + + def run(self, context): + # 通过超参拿到迭代次数 + epochs = int( + envs.get_global_env("runner." + context["runner_name"] + + ".epochs")) + # 取第一个phase的模型与reader + model_dict = context["env"]["phase"][0] + for epoch in range(epochs): + begin_time = time.time() + # 调用run进行训练 + self._run(context, model_dict) + end_time = time.time() + seconds = end_time - begin_time + print("epoch {} done, use time: {}".format(epoch, seconds)) + with fluid.scope_guard(context["model"][model_dict["name"]][ + "scope"]): + train_prog = context["model"][model_dict["name"]][ + "main_program"] + startup_prog = context["model"][model_dict["name"]][ + "startup_program"] + with fluid.program_guard(train_prog, startup_prog): + # 保存模型 + self.save(epoch, context, True) + context["status"] = "terminal_pass" +``` + +自定义runner需要参照官方实现[runner.py](../core/trainers/framework/startup.py),继承基类`RunnerBase`,命名为`Runner`,并实现`run`成员函数。 + +## terminal + +terminal主要进行分布式训练结束后的`stop worker`,以及其他需要在模型训练完成后进行的工作,比如数据整理,模型上传等等。 + +```python +class TerminalBase(object): + def __init__(self, context): + pass + + def terminal(self, context): + print("PaddleRec Finish") +``` + +自定义terminal需要继承`TerminalBase`命名为`Terminal`,并实现成员函数`terminal`。 + +## 自定义流程参与训练 + +假如我们自定义了某个流程,将其与model/reader一样,放在workspace下,并同时更改yaml配置中的runner相关选项,PaddleRec会自动用指定的流程替换原始的类别。 + +```yaml +runner: + - name: train_runner + class: single_train + epochs: 2 + device: cpu + instance_class_path: "{workspace}/your_instance.py" + network_class_path: "{workspace}/your_network.py" + startup_class_path: "{workspace}/your_startup.py" + runner_class_path: "{workspace}/your_runner.py" + terminal_class_path: "{workspace}/your_terminal.py" + print_interval: 1 +``` + +## 示例 + +官方模型中的TDM是一个很好的示例,该模型自定义了`startup`的实现,可以参考[tdm_startup.py](../models/treebased/tdm/tdm_startup.py) + +```python +class Startup(StartupBase): + def startup(self, context): + logger.info("Run TDM Trainer Startup Pass") + if context["engine"] == EngineMode.SINGLE: + self._single_startup(context) + else: + self._cluster_startup(context) + + context['status'] = 'train_pass' + + def _single_startup(self, context): + # single process + + def _cluster_startup(self, context): + # cluster process +``` + +于此同时,在yaml中更改了默认的startup执行类: +```yaml +runner: +- name: runner1 + class: single_train + startup_class_path: "{workspace}/tdm_startup.py" + epochs: 10 + device: cpu +``` -- GitLab