飞桨x昇腾生态适配方案:04_模型精度对齐

简介: 本文详细介绍了模型在不同硬件(如GPU与NPU)间迁移时的精度对齐方法,包括前向和反向对齐的具体步骤。前向对齐通过模块化对比计算结果(如平均值、最大最小值等),确保误差在合理范围内;反向对齐则聚焦于梯度差异,利用二分法定位问题算子。同时,文章结合PPHGNet_small和MultiHead等具体模块代码,说明了如何打印输出并分析中间结果。此外,还探讨了私有格式、梯度异常及特殊shape等可能影响精度的因素,并提出相应解决策略。整体流程清晰,为跨硬件模型迁移提供了实用指导。

精度对齐说明

精度对齐旨在确保模型在训练一定轮次后,其损失(LOSS)或评分能够与原硬件训练的结果基本相符。

精度对齐标准

下图所示为在原硬件GPU上的训练精度:
01_GPU精度.png

迁移到NPU上后要求与GPU训练精度相差在千分之五左右,若精度误差过大则需要通过前向、反向对齐操作定位问题算子。
02_NPU精度.png

精度对齐思路

一旦精度出现偏差,首先确认环境变量配置(例如,NPU私有格式环境变量是否关闭export FLAGS_npu_storage_format=0)、输入数据是否一致,如以上两项一致,则极有可能是算子的问题。

常用的问题算子定位方法是分模块对齐,在单模块内用二分法逐步缩小排查范围,直至确定最小问题单元。

前向对齐

前向对齐时可先进行单步对齐,再做多步验证。通常而言,单步对齐要求误差在百分之五以内;而多步验证则要求平均相对误差或平均绝对误差小于百分之二。

前向计算结果可在模型侧代码中打印输出。对比时逐模块进行,若模块结果差异大,采用二分法逐步收缩排查模块内部,直至定位问题算子。

模型结构分析

YAML 配置文件中的 Architecture 部分定义了模型的整体结构。

Architecture:
  model_type: rec
  algorithm: SVTR_HGNet
  Transform: null
  Backbone:
    name: PPHGNet_small
  Head:
    name: MultiHead

关键信息说明:

  • "model_type: rec":指定模型的类型为识别(recognition)任务,在 OCR 场景中,通常分为检测(detection)和识别两个主要任务,这里明确该模型用于文字识别。
  • "algorithm: SVTR_HGNet":指定使用的算法为 SVTR_HGNet。SVTR(Scene Text Visual Recognition)是一种用于场景文本识别的模型架构。
  • "Backbone":模型的骨干网络模块,主要负责从输入数据中提取特征。
  • "name: PPHGNet_small":指定骨干网络使用 PPHGNet_small 模型。PPHGNet 是 PaddlePaddle 团队提出的一种轻量级骨干网络,small 表示使用其小型版本,通常具有较少的参数和较低的计算量。
  • "Head":模型的头部网络模块,用于根据骨干网络提取的特征进行具体的任务预测。
  • "name: MultiHead":表示使用多头部网络结构。

模块代码查找

根据模型结构中各个模块的名称查找对应模块的代码实现:

# 进入模型结构代码目录
cd  paddleOCR/ppocr/modeling

# 根据模块名称查找对应代码实现
# Backbones模块名称为PPHGNet_small,则在./backbones目录下搜索:
grep -rn "PPHGNet_small" ./backbones/

# Head模块名称为MultiHead,则在./heads目录下搜索:
grep -rn "MultiHead" ./heads/

模块结果打印

找到各个模块代码后,对各模块的计算结果进行打印输出,可以分别打印平均值,最大最小值,形状和数据类型。

本节以Head模块为例,MultiHead代码实现在rec_multi_head.py文件中,其中forward方法实现如下:

def forward(self, x, targets=None):
    if self.use_pool:
        x = self.pool(
            x.reshape([0, 3, -1, self.in_channels]).transpose([0, 3, 1, 2])
        )
    ctc_encoder = self.ctc_encoder(x)
    ctc_out = self.ctc_head(ctc_encoder, targets)
    head_out = dict()
    head_out["ctc"] = ctc_out
    head_out["ctc_neck"] = ctc_encoder
    # eval mode
    if not self.training:
        return ctc_out
    if self.gtc_head == "sar":
        sar_out = self.sar_head(x, targets[1:])
        head_out["sar"] = sar_out
    else:
        gtc_out = self.gtc_head(self.before_gtc(x), targets[1:])
        head_out["gtc"] = gtc_out
    return head_out

在"return head_out"语句之前,添加如下代码段:

print("head_out['ctc']")
print("平均值:", head_out["ctc"].mean())
print("最小值:", head_out["ctc"].min())
print("最大值:", head_out["ctc"].max())
print("形状:", head_out["ctc"].shape)
print("数据类型:", head_out["ctc"].dtype)

print("head_out['ctc_neck']")
print("平均值:", head_out["ctc_neck"].mean())
print("最小值:", head_out["ctc_neck"].min())
print("最大值:", head_out["ctc_neck"].max())
print("形状:", head_out["ctc_neck"].shape)
print("数据类型:", head_out["ctc_neck"].dtype)

print("head_out['gtc']")
print("平均值:", head_out["gtc"].mean())
print("最小值:", head_out["gtc"].min())
print("最大值:", head_out["gtc"].max())
print("形状:", head_out["gtc"].shape)
print("数据类型:", head_out["gtc"].dtype)

前向输出对比

添加打印代码后,执行以下命令行,分别开启 NPU 与 CPU(本文仅以 CPU 为例,实际需与 GPU 输出对比)上的训练。

  • NPU:
python -m paddle.distributed.launch --devices 0,1,2,3,4,5,6,7 tools/train.py -c PP-OCRv4_server_rec_ic15_data.yaml -o Global.use_gpu=False Global.use_npu=True > ocr_npu.log  2>&1
  • CPU:
python  tools/train.py -c PP-OCRv4_server_rec_ic15_data.yaml -o Global.use_gpu=False -o  Global.use_cpu=True > ocr_cpu.log  2>&1

对比结果如下:
03_前向对比结果.png

合格标准:平均值的绝对误差或者相对误差在百分之一以内。

反向对齐

固定输入和权重,采用相同配置,在 GPU 和 NPU 上对比训练一步的 loss 和梯度。在前向对齐的情况下,若梯度不一致,在 loss.backward() 后用 tensor.grad 获取梯度值,再通过二分法逐步缩小范围查找差异,定位无法与 GPU 梯度对齐的 API 或操作。

反向代码查找

在模型训练脚本中,找到训练入口定位到反向代码,步骤如下:

  • 在tools/train.py中搜索关键词program.train,进入program.py文件;
  • 在训练循环中找到和scaled_avg_loss.backward()或avg_loss.backward(),如下图所示:

04_反向代码查找.png

梯度值打印

在avg_loss.backward()方法之后,使用tensor.grad获取梯度值,随后打印出梯度值的最大最小和平均值,代码如下:

05_梯度值打印.png

反向结果对比

添加打印代码后,执行以下命令行,分别开启 NPU 与 CPU(本文仅以 CPU 为例,实际需与 GPU 输出对比)上的训练。
NPU:

python -m paddle.distributed.launch --devices 0,1,2,3,4,5,6,7 tools/train.py -c PP-OCRv4_server_rec_ic15_data.yaml -o Global.use_gpu=False Global.use_npu=True > ocr_npu.log  2>&1

CPU:

python  tools/train.py -c PP-OCRv4_server_rec_ic15_data.yaml -o Global.use_gpu=False -o  Global.use_cpu=True > ocr_cpu.log  2>&1

对比结果如下:
06_反向结果对比.png

合格标准:平均值的绝对误差或者相对误差在百分之一以内。

其它可能精度问题

私有格式问题

私有格式常出现问题,目前默认关闭,确认是否关闭:

export FLAGS_npu_storage_format=0

梯度消失/爆炸

确认是否存在梯度消失/梯度爆炸问题,尝试加上梯度裁剪等策略。

特殊shape

有些偶现问题常见与出现在特殊的shape,如0维Tensor,需要特别注意。

多模型场景

在同时对多个模型进行调优时,常常会出现大部分模型能够实现精度对齐,而个别模型却出现异常的情况。此时,可以通过对不同算子进行筛查,找出可疑算子,最终通过将可疑算子列入黑名单的方式进行排查,以解决精度问题。

目录
相关文章
|
前端开发 JavaScript API
阿里云智能媒体服务IMS(Intelligent Media Services)的视频剪辑Web SDK
【1月更文挑战第15天】【1月更文挑战第72篇】阿里云智能媒体服务IMS(Intelligent Media Services)的视频剪辑Web SDK
411 6
|
7月前
|
测试技术 API 异构计算
飞桨x昇腾生态适配方案:05_算子适配流程
本内容主要介绍Paddle针对非CPU和Nvidia GPU硬件(如NPU)的适配流程与方法。适配代码存于PaddleCustomDevice仓库,路径为`PaddleCustomDevice/backends/npu`,包含kernels(算子适配)和tests(单元测试)两个核心目录。适配流程分为算子注册、适配函数入参与主体实现三步,重点对齐Paddle与CANN算子参数。
250 0
|
5月前
|
自然语言处理 前端开发 JavaScript
用 通义灵码 一键生成“水波纹按钮”,连 CSS 动画都不用自己写了!
通义灵码是一款智能编程辅助工具,它可以根据自然语言指令自动生成高质量的代码。例如,只需输入“生成一个按钮,点击时带水波纹动画,模拟 Material Ripple 效果”,它就能生成具备完整交互逻辑、CSS 动画和良好性能的按钮组件。不仅如此,它还支持拓展功能,如长按触发提示、添加图标等,并能自动优化样式适配不同场景。通过通义灵码,开发者可以大幅提升效率,专注于创意实现,而不必纠结于繁琐的代码细节。
|
7月前
|
Python
飞桨x昇腾生态适配方案:15_loop算子缺失(下):for循环替换loop
本章节基于上一内容,将ONNX模型拆分为loop算子部分与非loop算子部分,分别转换为OM模型。通过使用for循环替换loop算子的计算逻辑,构造子图执行流程,并编写OM模型推理脚本进行验证。同时,编写ONNX模型推理脚本对比两者推理结果,确保一致性。实验结果表明,拆分后的OM模型与原始ONNX模型推理结果一致,证明方案可行。
290 27
|
3月前
|
边缘计算 缓存 人工智能
EdgeShard:通过协作边缘计算实现高效的大语言模型推理——论文解读
EdgeShard是一种基于协作边缘计算的大语言模型(LLM)推理框架,旨在解决LLM在云端部署面临的延迟高、带宽压力大和隐私泄露等问题。通过将LLM分片部署在多个边缘设备上,结合云边协同与设备间协作,EdgeShard实现了高效的模型推理。其核心创新包括:联合设备选择与模型划分优化、支持流水线并行与微批处理、提出EdgeShard-No-Bubbles策略以减少设备空闲时间,从而显著提升推理吞吐量并降低延迟。实验表明,EdgeShard在异构边缘设备上可实现高达50%的延迟降低和2倍的吞吐量提升,支持全精度模型推理而无精度损失,为资源受限的边缘环境提供了高效的LLM部署方案。
777 2
|
7月前
|
数据采集 Web App开发 数据挖掘
飞桨x昇腾生态适配方案:07_性能数据分析
本文介绍了性能调优的全流程,包括分析、定位与优化。通过 profiling 工具采集算子级性能数据,定位计算与调度通信瓶颈。针对计算时间过长问题,可通过升级算子或提交工单解决;调度优化则关注重复编译,关闭在线编译或使用 aclnn 算子可提升效率。数据采集使用 paddlepaddle 的 profiler 工具,结合 msprof 解析生成的性能数据,重点分析 op_statistic_*.csv 和 op_summary_*.csv 文件,通过关键字段(如 Ratio、Total Time、Task Duration 和 Task Wait Time)量化性能瓶颈并实施优化策略。
295 10
|
6月前
|
存储 分布式计算 NoSQL
特征存储避坑指南:对比 Feast/Hopsworks 在金融风控场景的落地实践
金融风控场景对特征存储系统有严苛要求,包括低延迟、强一致性、多源数据处理及合规性。本文对比Feast与Hopsworks两大平台的实战经验,解析其在特征服务优化、版本控制、性能调优等方面的优势与陷阱,并提出混合架构方案兼顾实时性与计算效率。通过实践验证,可显著提升系统性能并降低成本。
466 4
|
7月前
|
API Python
飞桨x昇腾生态适配方案:13_API离线推理
ais_bench 提供了基于昇腾硬件的 Python API,用于离线模型(.om模型)推理。支持静态与动态API场景,如单个或多个OM模型推理。通过 `InferSession` 类加载模型并执行推理,可灵活处理输入输出形状转换。示例代码涵盖图片读取、形状调整、多模型串联推理及资源释放等操作,满足多样化推理需求。
492 26
|
7月前
|
存储 算法
飞桨x昇腾生态适配方案:09_Paddle转ONNX
本节主要介绍如何将 PP-OCRv4 模型转化为 ONNX 模型,包括环境准备、模型下载、训练模型转 inference 模型及最终转为 ONNX 格式的过程。首先需安装 Paddle2ONNX 和 ONNXRuntime,接着下载并解压训练模型。通过 `export_model.py` 脚本将训练模型转化为 inference 模型,生成包含结构和参数的文件。最后使用 Paddle2ONNX 工具完成到 ONNX 格式的转换,并可选地使用 onnxslim 进行模型优化。各步骤均提供详细命令与参数说明,便于实际操作与部署。
325 9
|
7月前
|
存储 文字识别 文件存储
飞桨x昇腾生态适配方案:03_模型训练迁移
本案例以PaddleOCRv4模型为例,详细介绍了将模型迁移到NPU的完整流程。迁移过程中需确保模型功能在新硬件上无误,重点关注偶发性错误及长时间运行时可能出现的问题,并通过日志辅助定位问题。文档涵盖环境搭建、数据集准备、模型配置、训练启动及常见问题排查等内容。例如,通过设置环境变量排查缺失算子,处理Paddle版本兼容性问题,以及解决进程残留等。适合希望将OCR模型部署到NPU的开发者参考。
303 0