Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

【Hackathon 6th No.58】support model convert from fp32 to fp16 #1268

Merged
merged 22 commits into from
Jul 8, 2024

Conversation

xiaoyewww
Copy link
Contributor

Details:
support model convert from fp32 to fp16

@xiaoyewww
Copy link
Contributor Author

@Zheng-Bicheng 大佬,具体做的时候有以下几个问题:

  1. 添加对半精度 Paddle 模型的支持,这个具体行为是指将paddle fp16模型转化成onnx fp16的模型吗?还是将paddle fp32模型转化成onnx fp16模型就好,我看这个原生是支持的,只是过程中会将数值截断成fp16,是具体对这个地方修改吗?
  2. 对算子的FP16支持具体是怎么做呢?在每个op里面做一下数值的fp32到fp16的转换?

@Zheng-Bicheng
Copy link
Collaborator

@xiaoyewww

添加对半精度 Paddle 模型的支持,这个具体行为是指将paddle fp16模型转化成onnx fp16的模型吗?还是将paddle fp32模型转化成onnx fp16模型就好,我看这个原生是支持的,只是过程中会将数值截断成fp16,是具体对这个地方修改吗?

Paddle2ONNX 目前支持 FP16 模型的方式类似把 FP32 格式的 Paddle 模型转换为 FP32 的ONNX模型,然后再把 FP32 的 ONNX 转为 FP16 的 ONNX 模型。这次的任务是直接转换 FP16 格式的 Paddle 模型。

对算子的FP16支持具体是怎么做呢?在每个op里面做一下数值的fp32到fp16的转换?

基本上可以按照以下步骤进行:

  • 1 升级基本转换机制:paddle2onnx/parser/parser.cc 需要对 FP16 格式的 Paddle 模型做适配,否则会直接报错退出
  • 2 升级算子:paddle2onnx/mapper/nn/batch_norm.cc 中的算子,需要新增 GetMinOpset 函数,在函数中需要判断当前是否是FP16算子,是的话需要升级 opset,并添加针对性的实现

@Zheng-Bicheng
Copy link
Collaborator

FP16的Resnet模型可以通过类似以下方式来实现

import paddle
from paddlenlp.transformers import UIEX # 从模型代码中导入模型
model = UIEX.from_pretrained("uie-x-base") # 实例化模型
model.to(dtype="float16") # 加载预训练模型参数
model.eval() # 将模型设置为评估状态

input_spec = [
paddle.static.InputSpec(shape=[None, None], dtype="int64", name="input_ids"),
paddle.static.InputSpec(shape=[None, None], dtype="int64", name="token_type_ids"),
paddle.static.InputSpec(shape=[None, None], dtype="int64", name="position_ids"),
paddle.static.InputSpec(shape=[None, None], dtype="int64", name="attention_mask"),
paddle.static.InputSpec(shape=[None, None, 4], dtype="int64", name="bbox"),
paddle.static.InputSpec(shape=[None, 3, 224, 224], dtype="float16", name="image"),
] # # 定义输入数据

print("Exporting ONNX model to %s" % "./uiex_fp16.onnx")
paddle.onnx.export(model, "./uiex_fp16", input_spec=input_spec) # ONNX模型导出
print("ONNX model exported.")

@xiaoyewww
Copy link
Contributor Author

FP16的Resnet模型可以通过类似以下方式来实现

import paddle
from paddlenlp.transformers import UIEX # 从模型代码中导入模型
model = UIEX.from_pretrained("uie-x-base") # 实例化模型
model.to(dtype="float16") # 加载预训练模型参数
model.eval() # 将模型设置为评估状态

input_spec = [
paddle.static.InputSpec(shape=[None, None], dtype="int64", name="input_ids"),
paddle.static.InputSpec(shape=[None, None], dtype="int64", name="token_type_ids"),
paddle.static.InputSpec(shape=[None, None], dtype="int64", name="position_ids"),
paddle.static.InputSpec(shape=[None, None], dtype="int64", name="attention_mask"),
paddle.static.InputSpec(shape=[None, None, 4], dtype="int64", name="bbox"),
paddle.static.InputSpec(shape=[None, 3, 224, 224], dtype="float16", name="image"),
] # # 定义输入数据

print("Exporting ONNX model to %s" % "./uiex_fp16.onnx")
paddle.onnx.export(model, "./uiex_fp16", input_spec=input_spec) # ONNX模型导出
print("ONNX model exported.")

这里我看了一下PaddleClas相关的代码,Resnet看上去好像没有from_pretrained这种调用方式,所以我直接通过下载模型及其权重来转换了。

@xiaoyewww
Copy link
Contributor Author

@xiaoyewww

添加对半精度 Paddle 模型的支持,这个具体行为是指将paddle fp16模型转化成onnx fp16的模型吗?还是将paddle fp32模型转化成onnx fp16模型就好,我看这个原生是支持的,只是过程中会将数值截断成fp16,是具体对这个地方修改吗?

Paddle2ONNX 目前支持 FP16 模型的方式类似把 FP32 格式的 Paddle 模型转换为 FP32 的ONNX模型,然后再把 FP32 的 ONNX 转为 FP16 的 ONNX 模型。这次的任务是直接转换 FP16 格式的 Paddle 模型。

对算子的FP16支持具体是怎么做呢?在每个op里面做一下数值的fp32到fp16的转换?

基本上可以按照以下步骤进行:

  • 1 升级基本转换机制:paddle2onnx/parser/parser.cc 需要对 FP16 格式的 Paddle 模型做适配,否则会直接报错退出
  • 2 升级算子:paddle2onnx/mapper/nn/batch_norm.cc 中的算子,需要新增 GetMinOpset 函数,在函数中需要判断当前是否是FP16算子,是的话需要升级 opset,并添加针对性的实现

针对第二点,请问一下具体是什么呢?比如batch_norm算子是对相关float的参数做处理?

另外用paddle原生直接对resnet的模型做半精度推理,是不支持的吗,报了下面的错:

Traceback (most recent call last):
  File "/wuzp/Paddle2ONNX/tests/test_resnet_fp16.py", line 57, in <module>
    paddle_output = model(paddle_input)
  File "/root/miniconda3/envs/paddle_onnx/lib/python3.9/site-packages/paddle/nn/layer/layers.py", line 1429, in __call__
    return self.forward(*inputs, **kwargs)
  File "/root/miniconda3/envs/paddle_onnx/lib/python3.9/site-packages/paddle/jit/translated_layer.py", line 1477, in __i_m_p_l__
    return _run_dygraph(self, input, program_holder)
  File "/root/miniconda3/envs/paddle_onnx/lib/python3.9/site-packages/paddle/jit/translated_layer.py", line 1004, in _run_dygraph
    _legacy_C_ops.run_program(
ValueError: (InvalidArgument) Scale input should be of float type
  [Hint: Expected bn_param_type == framework::TransToProtoVarType( ctx.Input<phi::DenseTensor>("Scale")->dtype()), but received bn_param_type:5 != framework::TransToProtoVarType( ctx.Input<phi::DenseTensor>("Scale")->dtype()):4.] (at ../paddle/fluid/operators/batch_norm_op.cc:198)
  [operator < batch_norm > error]  [operator < run_program > error]

@Zheng-Bicheng
Copy link
Collaborator

这里我看了一下PaddleClas相关的代码,Resnet看上去好像没有from_pretrained这种调用方式,所以我直接通过下载模型及其权重来转换了。

没问题的

@Zheng-Bicheng
Copy link
Collaborator

另外用paddle原生直接对resnet的模型做半精度推理,是不支持的吗

要用GPU推理我记得,这个最好是去Paddle的Issues提一下

@xiaoyewww
Copy link
Contributor Author

xiaoyewww commented Jun 5, 2024

另外用paddle原生直接对resnet的模型做半精度推理,是不支持的吗

要用GPU推理我记得,这个最好是去Paddle的Issues提一下

我这里已经用的是gpu进行推理,我去issue上问一下

PaddlePaddle/Paddle#64935

@Zheng-Bicheng
Copy link
Collaborator

比如batch_norm算子是对相关float的参数做处理?

比如 layer_norm.cc#L35 这里会强转FP16为FP32,需要针对性添加支持

@xiaoyewww
Copy link
Contributor Author

比如batch_norm算子是对相关float的参数做处理?

比如 layer_norm.cc#L35 这里会强转FP16为FP32,需要针对性添加支持

resnet中没有layernorm,所以我对matmul修改了一下,麻烦看一下这样修改是否正确,没问题的话我剩下相关的layer都这样处理。

另外像batchnorm这些层该怎么升级op处理呢?这里说实话有点看不懂,这里有相关的dtype constraints,第一行看上去float,float16这些都支持,后面又说限制float,这里具体怎么处理呢?

@Zheng-Bicheng
Copy link
Collaborator

比如batch_norm算子是对相关float的参数做处理?

比如 layer_norm.cc#L35 这里会强转FP16为FP32,需要针对性添加支持

resnet中没有layernorm,所以我对matmul修改了一下,麻烦看一下这样修改是否正确,没问题的话我剩下相关的layer都这样处理。

另外像batchnorm这些层该怎么升级op处理呢?这里说实话有点看不懂,这里有相关的dtype constraints,第一行看上去float,float16这些都支持,后面又说限制float,这里具体怎么处理呢?

我只是举了个例子,可以按实际情况来处理,例如如果实际没有这个算子就不需要升级

@Zheng-Bicheng
Copy link
Collaborator

@xiaoyewww 这个有些问题,以你这里适配的matmul为例子:

  • 1 先打开 ONNX Matmul OP 查看一下算子的相关信息
  • 2 可以看到Mutmul在 opset为9的情况下是不支持fp16的,因此需要添加matmul对opset13的支持

@xiaoyewww
Copy link
Contributor Author

@Zheng-Bicheng 大佬,我修改layer_norm这个算子的时候遇到点问题,onnx上不是最低只支持opset17吗,怎么p2o上有opset7的支持呢?
https://onnx.ai/onnx/operators/onnx__LayerNormalization.html#layernormalization-17

@xiaoyewww
Copy link
Contributor Author

另外,这个误差范围多少合适呢,我目前比较的是fp32下的推理结果:

Mismatched elements: 35 / 1000 (3.5%)
Max absolute difference: 0.00010923
Max relative difference: 0.01125892
 x: array([[3.214033e-05, 2.723171e-04, 3.689834e-04, 3.431249e-04,
        5.028006e-04, 3.669124e-04, 9.550410e-04, 1.896740e-04,
        8.601158e-05, 1.495139e-04, 5.724585e-04, 1.789319e-03,...
 y: array([[3.2008e-05, 2.7180e-04, 3.6883e-04, 3.4189e-04, 5.0068e-04,
        3.6669e-04, 9.5177e-04, 1.8966e-04, 8.5950e-05, 1.4973e-04,
        5.7554e-04, 1.8015e-03, 7.2336e-04, 1.2159e-03, 5.3692e-04,...

@xiaoyewww
Copy link
Contributor Author

@xiaoyewww 这个有些问题,以你这里适配的matmul为例子:

  • 1 先打开 ONNX Matmul OP 查看一下算子的相关信息
  • 2 可以看到Mutmul在 opset为9的情况下是不支持fp16的,因此需要添加matmul对opset13的支持

抱歉才看到,这里我看不是支持的吗,opset13支持的是BF16?

@Zheng-Bicheng
Copy link
Collaborator

@xiaoyewww 这个有些问题,以你这里适配的matmul为例子:

  • 1 先打开 ONNX Matmul OP 查看一下算子的相关信息
  • 2 可以看到Mutmul在 opset为9的情况下是不支持fp16的,因此需要添加matmul对opset13的支持

抱歉才看到,这里我看不是支持的吗,opset13支持的是BF16?

这里看错了,确实是支持FP16的,没有问题

@Zheng-Bicheng
Copy link
Collaborator

@Zheng-Bicheng 大佬,我修改layer_norm这个算子的时候遇到点问题,onnx上不是最低只支持opset17吗,怎么p2o上有opset7的支持呢? https://onnx.ai/onnx/operators/onnx__LayerNormalization.html#layernormalization-17

Opset7的支持是把这个算子拆分成很多个其他算子来做的

Copy link
Collaborator

@Zheng-Bicheng Zheng-Bicheng left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

以下CI出现了问题,麻烦再看看

2024-06-12T16:03:46.7357707Z ============ failed cases =============
2024-06-12T16:03:46.7358341Z ./test_argmax.py
2024-06-12T16:03:46.7358634Z ./test_argmin.py
2024-06-12T16:03:46.7358929Z ./test_argsort.py
2024-06-12T16:03:46.7359418Z ./test_auto_scan_argminmax.py
2024-06-12T16:03:46.7359794Z ./test_auto_scan_argsort.py
2024-06-12T16:03:46.7360129Z ./test_auto_scan_assign.py
2024-06-12T16:03:46.7360453Z ./test_auto_scan_avgpool.py
2024-06-12T16:03:46.7360806Z ./test_auto_scan_cast.py
2024-06-12T16:03:46.7361131Z ./test_auto_scan_eye.py
2024-06-12T16:03:46.7361456Z ./test_auto_scan_fill_constant.py
2024-06-12T16:03:46.7361822Z ./test_auto_scan_fill_like.py
2024-06-12T16:03:46.7362384Z ./test_auto_scan_gaussian_random.py
2024-06-12T16:03:46.7362770Z ./test_auto_scan_linspace.py
2024-06-12T16:03:46.7363501Z ./test_auto_scan_logical_ops.py
2024-06-12T16:03:46.7364071Z ./test_auto_scan_lookup_table_v2.py
2024-06-12T16:03:46.7364638Z ./test_auto_scan_matmul.py
2024-06-12T16:03:46.7365140Z ./test_auto_scan_one_hot_v2.py
2024-06-12T16:03:46.7365527Z ./test_auto_scan_pool_adaptive_avg_ops.py
2024-06-12T16:03:46.7365935Z ./test_auto_scan_pool_avg_ops.py
2024-06-12T16:03:46.7366295Z ./test_auto_scan_pool_max_ops.py
2024-06-12T16:03:46.7366772Z ./test_auto_scan_range.py
2024-06-12T16:03:46.7367271Z ./test_auto_scan_reduce_all_or_any.py
2024-06-12T16:03:46.7367879Z ./test_auto_scan_shape.py
2024-06-12T16:03:46.7368270Z ./test_auto_scan_size.py
2024-06-12T16:03:46.7368589Z ./test_auto_scan_where_index.py
2024-06-12T16:03:46.7368927Z ./test_cast.py
2024-06-12T16:03:46.7369185Z ./test_equal.py
2024-06-12T16:03:46.7369448Z ./test_greater_equal.py
2024-06-12T16:03:46.7369753Z ./test_greater_than.py
2024-06-12T16:03:46.7370051Z ./test_less_equal.py
2024-06-12T16:03:46.7370330Z ./test_less_than.py
2024-06-12T16:03:46.7370617Z ./test_nn_Embedding.py
2024-06-12T16:03:46.7370932Z ./test_nn_Functional_LogSoftmax.py
2024-06-12T16:03:46.7371283Z ./test_nonzero.py
2024-06-12T16:03:46.7371546Z ./test_numel.py
2024-06-12T16:03:46.7371811Z ./test_resnet_fp16.py
2024-06-12T16:03:46.7372087Z ./test_shape.py
2024-06-12T16:03:46.7372344Z total bugs: 37
2024-06-12T16:03:46.7385269Z ##[error]Process completed with exit code 37.

paddle2onnx/converter.cc Outdated Show resolved Hide resolved
@xiaoyewww
Copy link
Contributor Author

xiaoyewww commented Jun 17, 2024

@Zheng-Bicheng
修改好了,麻烦再review一下,目前有这么几个问题麻烦确认一下:

  1. 首先这个地方paddle2onnx/converter.cc这样修改有没有问题,我的想法是对inputs有fp16和export_fp16_model=true的情况,都对outputs中dtype==fp32或者fp64的情况进行强转fp16
  2. numpy 这两天更新到了2.0.0,我这边目前onnxruntime==1.18.0不支持2.0.0,所以这里做了一下限制 numpy <= 2.0.0,麻烦看一下有没有必要
  3. 最近新的commit中删掉了一点内容,导致paddle.onnx.export中没有了dygraph2onnx api接口,麻烦看一下这里是否要重新加回来,否则的话要对应修改paddle

Copy link
Collaborator

@Zheng-Bicheng Zheng-Bicheng left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

以下CI仍然存在问题

============ failed cases =============
./test_auto_scan_assign.py
./test_auto_scan_matmul.py
./test_resnet_fp16.py

paddle2onnx/__init__.py Outdated Show resolved Hide resolved
paddle2onnx/converter.cc Outdated Show resolved Hide resolved
@Zheng-Bicheng
Copy link
Collaborator

numpy 这两天更新到了2.0.0,我这边目前onnxruntime==1.18.0不支持2.0.0,所以这里做了一下限制 numpy <= 2.0.0,麻烦看一下有没有必要

关于onnx的问题,您能帮忙在 README 中也做对应的修改吗?

@xiaoyewww
Copy link
Contributor Author

numpy 这两天更新到了2.0.0,我这边目前onnxruntime==1.18.0不支持2.0.0,所以这里做了一下限制 numpy <= 2.0.0,麻烦看一下有没有必要

关于onnx的问题,您能帮忙在 README 中也做对应的修改吗?

已修改

@PaddlePaddle PaddlePaddle deleted a comment from xiaoyewww Jun 19, 2024
Copy link
Collaborator

@Zheng-Bicheng Zheng-Bicheng left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里再看下

paddle2onnx/mapper/mapper.h Outdated Show resolved Hide resolved
Copy link
Collaborator

@Zheng-Bicheng Zheng-Bicheng left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

大佬,数据类型列表这块是不是改成储存支持的数据类型好一点?我看你这漏了不少数据类型啊。

@@ -34,6 +34,7 @@ class MatmulMapper : public Mapper {

private:
std::string GetTrans(std::vector<TensorInfo>& input_info);
const std::unordered_set<int32_t> kNeedCastTypes{P2ODataType::INT8, P2ODataType::FP64};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里int16也是不支持的

@@ -33,6 +33,7 @@ class MatmulV2Mapper : public Mapper {

private:
std::string GetTrans(std::vector<TensorInfo>& input_info);
const std::unordered_set<int32_t> kNeedCastTypes{P2ODataType::INT8, P2ODataType::FP64};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里int16也是不支持的

@xiaoyewww
Copy link
Contributor Author

xiaoyewww commented Jun 24, 2024

大佬,数据类型列表这块是不是改成储存支持的数据类型好一点?我看你这漏了不少数据类型啊。

郑师傅,我这里这样理解的:

  1. 首先存不支持的数据比较直观点,可以明显看到哪些不支持的,可以方便后续添加支持
  2. 第二不支持的类别数量我理解在大多数算子上肯定比较少,方便管理
  3. 最重要的是我觉得需要补全测例吧,如果测例类型存在,就可以明确哪些数据类型明确不支持的,因为存在onnx不支持和p2o的情况,onnx不支持我们需要看文档,但是我们p2o不支持只能测试才能发现了,所以我感觉直接写个不支持的列表先默认不在列表内的支持,能让转换模型中的cast应少尽少

比如对pool这个算子,因为没有int8的测例,但又因为这是onnx支持的,所以我第一反应是这个算子是不需要cast的,所以没放在列表里

@xiaoyewww
Copy link
Contributor Author

@Zheng-Bicheng 已按要求修改,麻烦再review一下

Copy link
Collaborator

@Zheng-Bicheng Zheng-Bicheng left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@Zheng-Bicheng Zheng-Bicheng merged commit 0f85055 into PaddlePaddle:develop Jul 8, 2024
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants