diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index ad7265f..e57b34c 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -21,9 +21,9 @@ jobs: uses: actions/setup-python@v4 with: python-version: '3.10' - - name: Install dependencies + - name: Install pre-commit run: | python -m pip install --upgrade pip - make install + pip install pre-commit - name: Run lint run: make lint diff --git a/README.md b/README.md index db2f492..637a0eb 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,19 @@ PaDiff 是基于 PaddlePaddle 与 PyTorch 的模型精度对齐工具。传入 Paddle 和 Torch 模型,对齐训练中间结果以及训练后的模型权重,并提示精度 diff 第一次出现的位置。 +本工具支持通过以下方法进行对比(任选其一): + +- 通过单行命令,自动完成 PaDiff 代码注入 --> 生成临时运行脚本 --> 自动运行 --> 监控运行过程并 dump 数据 --> 自动对比并返回结果。 +这种方式不需要手动修改代码,只需要妥善设置配置文件。但使用此方法时需要将 `paddlepaddle` 和 `torch` 包安装在**同一环境**中,可能会出现版本冲突 + +- 手动修改 Paddle 和 PyTorch 脚本,并在**分别**运行(不需要同时)这两个脚本,根据脚本 PaDiff 会分别监控运行过程并 dump 数据,最后手动调用 API 接口对比数据,得到对齐结果。两次运行可以在**两个环境**中,但每个环境中都需要同时存在 `paddlepaddle` 和 `torch` 包 + +- 参考[旧版本特性(v0.2版本)](#旧版本特性(v0.2版本)) 使用 auto_diff 接口进行对齐,这种方法需要将 Paddle 和 PyTorch 放在同一个文件中,同时将 `paddlepaddle` 和 `torch` 包安装在同一环境中,不仅两个模型的运行过程耦合,代码修改量也比较大,因此**不推荐**此用法 + ## 安装 -当前推荐通过源码安装,本工具需要安装 `paddlepaddle` 和 `torch`,但这两个包的版本可能存在冲突。建议通过如下命令安装后,根据 [paddlepaddle官网](https://www.paddlepaddle.org.cn/install/quick?docurl=/documentation/docs/zh/develop/install/pip/linux-pip.html) 和 [pytorch官网](https://pytorch.org/get-started/locally/) 自行安装 `paddlepaddle` 和 `torch` +当前推荐通过源码安装,本工具依赖 `paddlepaddle` 和 `torch`,但在同一个环境中安装这两个包时,可能存在依赖冲突。请通过如下命令安装本工具后,根据 [paddlepaddle官网](https://www.paddlepaddle.org.cn/install/quick?docurl=/documentation/docs/zh/develop/install/pip/linux-pip.html) 和 [pytorch官网](https://pytorch.org/get-started/locally/) 自行安装 `paddlepaddle` 和 `torch` ```sh python -m pip install -e . -i https://pypi.tuna.tsinghua.edu.cn/simple @@ -17,7 +26,7 @@ python -m pip install -e . -i https://pypi.tuna.tsinghua.edu.cn/simple ## 快速开始 -### 使用单行命令对齐(支持前反向对齐) +### 使用单行命令对齐 将命令写入配置文件后,通过如下命令运行 @@ -27,7 +36,40 @@ python -m padiff.cli --config padiff_config.yaml 完整文件示例请参考 [配置文件说明文档](docs/CLIConfig.md),同时,运行命令前,请运行 `python -m padiff.cli -h` 获取更详细的参数说明。 -### log 设置 +### 在不同环境中手动运行 + +![PaDiff 运行原理示意图](docs/imgs/flow.png) + +如上图所示,PaDiff 支持分别监控 Paddle 和 PyTorch 模型的运行流程,并自动捕获输出、参数梯度、权重等信息。手动修改脚本,仅需找到原代码中模型被调用的代码,使用 `with PaDiffGuard(...)` 包裹,然后分别运行两个项目中的模型训练/推理脚本即可。 + +`PaDiffGuard` 参数说明请参考 [PaDiffGuard API](docs/api/PaDiffGuard.md) + +在两个项目运行结束后,会在两个项目生成 "your_project/padiff_dump/model_name" 的目录,该目录下会保存一些用于对比的数据文件。 + +之后按照如下脚本,手动调用 `compare_dumps(...)` 进行比较。 + +```python +# compare_dumps.py +from padiff.utils import logger +from padiff import compare_dumps + +if __name__ == "__main__": + logger.reset_dir( "./padiff_log") + + cfg = { + "atol": 1e-6, + "rtol": 1e-4, + "compare_mode": "abs_mean", + "action_name": "loose_equal", + } + + pt_dump_path = "torch_proj/padiff_dump/model_torch" + pd_dump_path = "paddle_proj/padiff_dump/model_paddle" + compare_dumps(pt_dump_path, pd_dump_path, cfg) +``` + + +## log 设置 #### 开启 debug 模式 diff --git a/docs/CLIConfig.md b/docs/CLIConfig.md index 1e3ad8a..b96d4f0 100644 --- a/docs/CLIConfig.md +++ b/docs/CLIConfig.md @@ -25,10 +25,10 @@ | 参数 | 类型 | 必需 | 默认值 | 说明 | | ------------------- | ------------ | ---- | ------ | ---------------------------------------------------- | -| `align_depth` | int or "inf" | 否 | "inf" | 对齐的深度。"inf" 表示最细粒度 | +| `align_depth` | int or "inf" | 否 | "inf" | 对齐的深度。"inf" 表示检查所有层 | | `single_step_mode` | string | 否 | null | 单步对齐模式 ("forward", "backward", "both") | -| `load_init_weights` | bool | 否 | false | 是否自动对齐初始化权重,若已手动对齐,请设置为 false | -| `load_first_inputs` | bool | 否 | false | 是否自动第一次的输入,若已手动对齐,请设置为 false | +| `load_init_weights` | bool | 否 | false | 是否对齐对齐初始化权重,若已手动对齐,请设置为 false | +| `load_first_inputs` | bool | 否 | false | 是否自动第一轮输入,若已手动对齐,请设置为 false | | `max_calls` | int | 否 | 1 | 最大前反向调用次数 | | `black_list` | list | 否 | [] | 不参与对齐的层名列表,列表内元素为 str 类型 | | `keys_mapping` | dict | 否 | null | 模型参数名映射字典,键和值分别为 Paddle/PyTorch 参数名 | @@ -196,8 +196,13 @@ compare_dumps(pt_dump_path, pd_dump_path, cfg) - 控制模型输出结果的对比精度和模式 - atol: 绝对误差容忍度 (default: 1e-6) - rtol: 相对误差容忍度 (default: 1e-6) -- compare_mode: 对比模式,具体内容请看对应文档。可选值: mean, strict, abs_mean, 默认值: "mean" -- action_name: 对比行为,具体内容请看对应文档。可选值: equal, loose_equal, 默认值: "equal" +- compare_mode: 对比模式,可选值: mean, strict, abs_mean, 默认值: "mean" + - mean: 比较传入数据的均值 + - strict: 直接比较传入数据 + - abs_mean: 比较传入数据的绝对值的均值 +- action_name: 对比行为,可选值: equal, loose_equal, 默认值: "equal" + - equal: 进行严格的对比,比如会对输出的个数、shape 都进行检查 + - loose_equal: 较为宽松的对比,会尝试尽可能多的匹配数据,比如两个模型某层的输出数量分别为 1 和 3 ,会对第一个输出进行比较,同时在两个数据的形状不匹配时会尝试 transpose 或 reshape。仅在该层所有数据都无法匹配时报错。 ``` COMPARE: diff --git a/docs/api/PaDiffGuard.md b/docs/api/PaDiffGuard.md new file mode 100644 index 0000000..71f9152 --- /dev/null +++ b/docs/api/PaDiffGuard.md @@ -0,0 +1,43 @@ +# PaDiffGuard API 文档 + +`PaDiffGuard` 是一个上下文管理器(Context Manager),用于在深度学习模型的前向和反向传播过程中自动进行模型对齐检查。它通过拦截计算过程,比较不同框架(如 PyTorch 和 PaddlePaddle)或同一框架下不同实现的中间结果、权重和梯度,以确保它们的行为一致。 + +## 参数 + +| 参数 | 类型 | 必需 | 默认值 | 说明 | +| :--- | :--- | :--- | :--- | :--- | +| `model` | `paddle.nn.Layer` or `torch.nn.Module` | 是 | - | 需要进行对齐检查的主模型实例。 | +| `optimizer` | `paddle.optimizer` or `torch.optim` | 否 | `null` | 与 `model` 配对的优化器实例。

**当反向传播不能被 `PaDiffGuard` 包裹时**:如果传递此参数,`PaDiffGuard` 会包装其 `step()` 方法以确保梯度更新的一致性。

**当反向传播可以被 `PaDiffGuard` 包裹时**:梯度一旦更新就会被自动获取,此时不需要传递此参数。 | +| `name` | `string` | 否 | `"model"` | 为当前 `model` 指定一个名称,该名称将用于日志记录和结果文件的命名。 | +| `align_depth` | `int` or `"inf"` | 否 | `"inf"` | 对齐的深度。

- 可以是一个整数,表示只检查前 N 层。
- 也可以是 `"inf"`,表示检查所有层。 | +| `single_step_mode` | `string` | 否 | `null` | 单步对齐模式,可选值为 `"forward"`, `"backward"`, `"both"`。

在此模式下,`PaDiffGuard` 会从 `base_dump_path` 加载参考数据进行比对。 | +| `load_init_weights` | `bool` | 否 | `false` | 是否从 `base_dump_path` 加载初始化权重。

若已手动对齐初始化权重,请设置为 `false`。 | +| `load_first_inputs` | `bool` | 否 | `false` | 是否从 `base_dump_path` 加载第一轮输入。

若已手动对齐输入,请设置为 `false`。 | +| `base_dump_path` | `string` | 否 | `null` | 基准数据的根目录路径。

当 `load_init_weights`、`load_first_inputs` 或 `single_step_mode` 被启用时,此参数必须提供。 | +| `framework` | `string` | 否 | `null` | 指定模型所属的框架,可选值为 `"paddle"` 和 `"torch"`。

用于指导如何处理特定框架的数据,当 `load_first_inputs` 被启用时,此参数必须提供。 | +| `seed` | `int` | 否 | `42` | 设置随机种子,以确保对齐过程中的随机行为是可复现的。 | +| `max_calls` | `int` | 否 | `1` | 最大前/反向调用次数。

达到上限后,会强制退出整个程序。例如,前向对齐时应该设置为 `1`。 | +| `black_list` | `list[str]` | 否 | `[]` | 不参与对齐的层名列表,列表内元素为 `str` 类型。 | +| `keys_mapping` | `dict` or `Callable` | 否 | `null` | 模型参数名映射,可以为:

- **字典形式**:键和值分别为 Paddle/PyTorch 的参数名。
- **可调用函数**:如 `lambda name: name.replace('model_pt', 'model_pa')`。 | + +## 用法 + +```python +from padiff import PaDiffGuard + +with PaDiffGuard( + model=model, + optimizer=optimizer, + name="my_model", +): + out = model(input_data) +loss = loss_fn(out, target) +loss.backward() +optimizer.step() + +``` +``` python +trainer = SFTTrainer(...) +with PaDiffGuard(trainer.model, name='model_name'): + trainer.train() +``` diff --git a/docs/imgs/flow.png b/docs/imgs/flow.png new file mode 100644 index 0000000..c380005 Binary files /dev/null and b/docs/imgs/flow.png differ diff --git a/pyproject.toml b/pyproject.toml index 6c6fbdd..443729a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.black] line-length = 119 -target_version = ['py37', 'py38', 'py39', 'py310'] +target_version = ['py38', 'py39', 'py310', 'py311'] exclude = ['.flake8'] [tool.pytest.ini_options] diff --git a/requirements.txt b/requirements.txt index 0df50f7..a7bf5f5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,12 +1,12 @@ # paddlepaddle-gpu>=3.0 # torch>=2.6 +# torchvision pre-commit pytest parameterized pytest-cov regex pytest-xdist -torchvision graphviz numpy coverage