news 2026/5/9 11:08:52

CANN GE后端CN_CLIP优化示例

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
CANN GE后端CN_CLIP优化示例

CN_CLIP模型优化示例

【免费下载链接】triton-inference-server-ge-backendge-backend基于triton inference server框架实现对接NPU生态,快速实现传统CV\NLP等模型的服务化。项目地址: https://gitcode.com/cann/triton-inference-server-ge-backend

CLIP全名Contrastive Language-Image Pretraining,在2021年由OpenAI提出,其核心理念为图文对比学习预训练,是一种多模态学习模型,旨在将图像和文本进行关联,它可以快速实现图文特征相似度计算、跨模态检索、零样本图片分类等任务。Chinese-CLIP(CN_CLIP)是一次极其朴素的开源,没错就是CLIP的汉化,旨在推动中文社区多模态发展,原始的CLIP模型基于英文图文语料,不能用于中文的图文表征提取场景。Chinese-CLIP以英文CLIP视觉侧参数和中文Roberta参数,作为模型初始化值。

CN_CLIP模型的优化包括:小batch合并、动态图转静态图、多流并行+锁核、ENSEMBLE,未涉及自动融合和融合PASS。因部署采用容器化,请求bs固定为256,且每个容器对cpu有限制,所以未考虑小batch自动融合的动态图优化方案

模型导出

修改模型文件

CLIP模型目录如下:

clip ├── bert_tokenizer.py ├── configuration_bert.py ├── __init__.py ├── model_configs │ ├── RBT3-chinese.json │ ├── RN50.json │ ├── RoBERTa-wwm-ext-base-chinese.json │ ├── RoBERTa-wwm-ext-large-chinese.json │ ├── ViT-B-16.json │ ├── ViT-B-32.json │ ├── ViT-H-14.json │ ├── ViT-L-14-336.json │ └── ViT-L-14.json ├── modeling_bert.py ├── model.py ├── utils.py └── vocab.txt

由于最终目的是部署推理,需要"model.py"中的forward返回值进行修改,具体如下:

def forward(self, image, text, mask_ratio=0): assert image is not None or text is not None, "text and image cannot both be None!" if image is None: return self.encode_text(text) elif text is None: return self.encode_image(image) image_features = self.encode_image(image, mask_ratio) text_features = self.encode_text(text) image_features = image_features / image_features.norm(dim=-1, keepdim=True) text_features = text_features / text_features.norm(dim=-1, keepdim=True) # return image_features, text_features, self.logit_scale.exp() return image_features, text_features

注:由于self.logit_scale.exp()参数没有shape,而在triton模型仓的config.pbtxt中需要设置输出shape,会导致模型无法运行。

导出模型

首先创建模型,需要选择model_arch(如ViT-B-16)和模型权重(checkpoint)

# prepare the PyTorch implemented model and restore weights model = create_model(_MODEL_INFO[args.model_arch]['struct'], checkpoint).float().eval()

设置输入占位数据:

# prepare empty image and text as input placeholders for ONNX resolution = _MODEL_INFO[args.model_arch]['input_resolution'] preprocess = image_transform(resolution) image = preprocess(Image.new('RGB', (resolution, resolution))).unsqueeze(0) text = clip.tokenize([""], context_length=args.context_length)

注:resolution需根据图像分辨率进行设置,然后调用clip模型自带方法转换成相应shape的输入,此输入的内容不重要,起到占位作用即可。

使用torch.onnx.export导出模型:

torch.onnx.export(model, (image, text), fusion_fp32_onnx_path, input_names=['image','text'], output_names=['unnorm_image_features',"unnorm_text_features"], dynamic_axes={ 'image':{0:"batch_size",2:"height",3:"width"}, 'text':{0:"batch_size",1:"seq_len"}, 'unnorm_image_features':{0:"batch_size",1:"unnorm_feats"}, 'unnorm_text_features':{0:"batch_size",1:"unnorm_feats"} }, export_params=True, do_constant_folding=False, opset_version=14, verbose=True )

其中,(image.text)构成模型输入,可以根据需求设置为(image,None),即只导出image分支;input_names 和 output_names 用于设置模型输入输出名称;导出静态图时应不设置dynamic_axes参数。

动态图转静态图

首先根据模型导出章节的步骤导出ONNX模型,然后据此模型生成GE图,并采集一次Profiling数据(采集方法见Profiling文档),查看整个推理过程是否全部变为静态,模型中是否有dynamic情况,如果有,就需要具体分析是否可以消除。

动态图执行Profiling如下:可以看到,在动态图下,有好多空泡,在1bs情况下,明显的hostbound问题。

动态图执行Profiling如下:可以看到一次推理中,前面一部分变成了图下沉模式,而后面出现了部分图又没有下沉成功,这种情况,我们需要打开op_summary_xxx.csv 文件,查看是啥原因导致了图下沉截断。可以看到在执行Mod操作时,图从前面的static模式变成了dynamic模式,而且算子本身是一个AI_CPU类算子,初步定位为Mod导致问题,我们可以打开onnx 文件查看Mod算子作用,用Netron打开相应的onnx文件。找到这个Mod。可以看到Mod之前是一个Add计算,A是一个固定值3 ,3-1=2, 2Mod3 = 2 ,每一层均是一个固定值,那这个Mod过程其实可以把Mod这个节点给删掉,让Add的结果2,直接接入到Mod的下面两个节点。

  • 删除节点的代码可以通过Deepseek等AI生成,通过修改后,再次跑图,用profiling采集,所有node均变为static。

两者吞吐对比(1 instance):

  1. 动态图:
  2. 静态图:

可以看出,在GE静态图场景下,单Instance 都有2.6倍吞吐差异。

模型优化

可参考 性能调优方法论 章节

多流并行+锁核

由于代码框架已集成该功能,直接添加启动命令参数启动即可开启,每个Stream限制12个Cube,10个Vector:

--backend-config=npu_ge,ge.aicoreNum="12|10"
  • 具体含义请参考性能调优方法论 性能优化结果如下:
  1. 多流并行+优化后静态
  2. 多流并行+优化后静态图+锁核

可以看出,在限制每条流的CV核数后,8流并行情况下整体吞吐又有35%的提升。

ENSEMBLE

为方便分析,删去CLIP模型的text分支,固定batch = 1,则其image输入shape为 [1,3,224,224]。

构建预处理 preprocess Python Model

添加模型文件和相应的config.pbtxt:

preprocess |-- 1 | `-- model.py |-- config.pbtxt `-- preprocess-py310.tar.gz

config.pbtxt文件内容如下:

name: "preprocess" backend: "python" input [ { name: "image_binary" data_type: TYPE_STRING dims: [1] } ] output [ { name: "preprocessed_image" data_type: TYPE_FP32 dims: [1, 3, 224, 224] } ] instance_group [ { count: 8 } ] parameters: { key: "EXECUTION_ENV_PATH", value: {string_value: "$$TRITON_MODEL_DIRECTORY/preprocess-py310.tar.gz"} }

其中,instance_group 参数用于配置preprocess模型的实例数,实例数越多可以应对更高的并发;参数 EXECUTION_ENV_PATH 用于指定Python Model执行依赖Python虚拟环境的路径,宏“$$TRITON_MODEL_DIRECTORY”表示模型仓路径,对应于模型仓中预先放置的“preprocess-py310.tar.gz”包。

注:Python Model虚拟环境打包方法见附录conda-pack。

模型代码为:

import numpy as np from PIL import Image import io import triton_python_backend_utils as pb_utils class TritonPythonModel: def initialize(self, args): """初始化模型(加载预处理参数)""" # CLIP 预训练模型的默认预处理参数(ViT-B-16 为例) self.input_size = (224, 224) # 图像尺寸 self.mean = np.array([0.48145466, 0.4578275, 0.40821073], dtype=np.float32) # RGB 均值 self.std = np.array([0.26862954, 0.26130258, 0.27577711], dtype=np.float32) # RGB 标准差 def execute(self, requests): """处理推理请求:PNG 解码 → 预处理 → 输出 CLIP 输入""" responses = [] for request in requests: # 获取客户端输入的 PNG 字节流 png_input = pb_utils.get_input_tensor_by_name(request, "image_binary") png_bytes_list = png_input.as_numpy() # 形状: [batch_size],元素为 bytes # 批量处理每张图像 clip_inputs = [] for png_bytes in png_bytes_list: # 解码 PNG(处理可能的 Alpha 通道) image = Image.open(io.BytesIO(png_bytes)).convert("RGB") # 转为 RGB,丢弃 Alpha # 调整尺寸到 CLIP 要求的大小 image = image.resize(self.input_size, Image.BILINEAR) # 转为 numpy 数组(HWC 格式) image_np = np.array(image, dtype=np.float32) # 转为 CHW 格式(CLIP 输入为 [C, H, W]) image_np = image_np.transpose(2, 0, 1) # (H, W, 3) → (3, H, W) # 归一化(减去均值,除以标准差) image_np = (image_np / 255.0 - self.mean[:, None, None]) / self.std[:, None, None] clip_inputs.append(image_np) # 堆叠为批处理张量 clip_inputs_np = np.stack(clip_inputs, axis=0) # 形状: [batch_size, 3, 224, 224] # 构造输出张量 output_tensor = pb_utils.Tensor("preprocessed_image", clip_inputs_np) inference_response = pb_utils.InferenceResponse(output_tensors=[output_tensor]) responses.append(inference_response) return responses def finalize(self): """清理资源(可选)""" pass

配置ENSEMBLE

为使能ENSEMBLE能力需要添加一个ENSEMBLE模型仓,其结构如下

clip_ensemble/ |-- 1 `-- config.pbtxt

config文件内容为:

name: "clip_ensemble" platform: "ensemble" max_batch_size: 0 input [ { name: "ensemble_image" data_type: TYPE_STRING dims: [1] } ] output [ { name: "ensemble_feats" data_type: TYPE_FP32 dims: [1, 512] } ] ensemble_scheduling { step [ # 步骤 1:预处理 { model_name: "preprocess" model_version: 1 input_map { key: "image_binary"; value: "ensemble_image" } output_map { key: "preprocessed_image"; value: "interm_image" } }, # 步骤 2:CN-CLIP 推理 { model_name: "cn_clip" model_version: 1 input_map { key: "image"; value: "interm_image" } output_map { key: "unnorm_image_features"; value: "ensemble_feats" } } }

与预处理类似,我还可以在模型输出接后处理模型,将模型输出转为识别类别字符串,降低服务端传回数据量。

CN_CLIP模型输入原始图片数据为RGB彩色图片,字节数为147KB,转为Numpy数组,字节数增加到588KB,是原来的四倍。分别测试CN_CLIP和使用的ENSEMBLE后的吞吐率: | Model | thoughput (infer/sec) | |:------:|:-----:| | CN_CLIP | 836.831 | | ENSEMBLE | 847.627 |

可以看到,大约有10infer/sec的性能提升。 这是由于数据量变化不是特别大。在帮客户优化Yolo11模型时,发现在本地测试吞吐还不错,大概能到110qps,然而上到环境之后,发现无法达到理论值,仅有30qps左右,经过问题定位,发现是前处理过程中把图片通原始的jpg图片变成了[1,3,3600,3600]的Tensor,导致传输过程成为bound,原始图片一般只有500kB,而前置经过放大拉伸转为tensor后变成74MB左右。如果能把前置过程搬到服务器,通过Triton自带的Ensemble能力,通过流水线方式覆盖前处理过程,就可以在服务器上消除因前处理导致的网络传输bound。经过改造,我们将yolo11的前、后处理均通过ensemble串接为一个新的服务,这个服务只需要传原始图片,即可直接得到最终Text,改造后上线的吞吐由30qps提升至90qps,提升了200%。

附录

MindStudio Insight 下载

下载链接:https://www.hiascend.com/developer/download/community/result?module=sto%2Bcann

选择合适版本下载即可。详细使用方法见:https://www.hiascend.com/document/detail/zh/mindstudio/830/GUI_baseddevelopmenttool/msascendinsightug/Insight_userguide_0002.html

Netron 下载

下载链接:https://github.com/lutzroeder/netron
初始界面如下:

选择相应模型文件打开即可查看。

onnxsim 安装

安装命令:

pip install onnxsim

onnxsim优化命令:

onnxsim {旧模型} {新模型}

conda-pack 安装

conda-pack 要打包的python版本需要与ge backend 自带的版本保持一致,当前ge backend中使用的python版本为3.10版本,建议用户制作运行环境时使用此版本python,若必须更换,请参考triton inference server官方文档制作相应的stub文件。
下载conda-pack:

conda install conda-pack

打包过程:

我们可以从最基本的conda 虚拟环境开始,仅打包模型运行依赖的最小集,可以最大程度减少包的体积。

conda create --name clip_env python=3.10

下载所有依赖包之前,先:

export PYTHONNOUSERSITE=True

安装完所有包后,打包环境:

conda pack -n clip_env -o package_name.tar.gz

更详细的说明见conda-pack文档.

【免费下载链接】triton-inference-server-ge-backendge-backend基于triton inference server框架实现对接NPU生态,快速实现传统CV\NLP等模型的服务化。项目地址: https://gitcode.com/cann/triton-inference-server-ge-backend

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/9 11:08:30

增量备份为什么还是这么慢?KingbaseES块级永久增量备份给出答案!

🔥承渊政道:个人主页 ❄️个人专栏: 《C语言基础语法知识》 《数据结构与算法》 《C知识内容》 《Linux系统知识》 《算法刷题指南》 《测评文章活动推广》 《大模型语言路线学习》 ✨逆境不吐心中苦,顺境不忘来时路!✨ 🎬 博主简介: 增量备…

作者头像 李华
网站建设 2026/5/9 11:06:31

AI入门学习之工地人员图像识别 基于深度学习的YOLOv11施工人员安全装备识别系统 工地安全图像识别

YOLOv11 施工人员安全装备识别系统 1. 数据集预处理与增强本项目的数据集构建过程经过精心设计,主要包含以下关键步骤:原始数据采集: 通过多渠道网络爬取施工现场图像,初步收集30张包含各类施工人员的现场作业图片图片涵盖不同光照…

作者头像 李华
网站建设 2026/5/9 10:48:51

3步解锁音乐自由:网易云NCM格式转换全攻略

3步解锁音乐自由:网易云NCM格式转换全攻略 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 你是否曾遇到过这样的困扰:在网易云音乐下载的歌曲只能在官方App播放,无法导入车载音响、MP3播放器或其他…

作者头像 李华
网站建设 2026/5/9 10:47:30

终极SMAPI指南:如何用模组API彻底改变你的星露谷物语体验

终极SMAPI指南:如何用模组API彻底改变你的星露谷物语体验 【免费下载链接】SMAPI The modding API for Stardew Valley. 项目地址: https://gitcode.com/gh_mirrors/smap/SMAPI SMAPI(Stardew Valley Modding API)是星露谷物语的模组加…

作者头像 李华
网站建设 2026/5/9 10:42:07

icode 星芒算法挑战赛20260508

星芒算法挑战赛比较有意思。不刚要比代码能否得到三星,还要看谁的代码更短,步数更少。 最近做了几道题,有一些心得记录一下。 转向规律 尽量让Dev的转动方向保持一个方向。大不了转向右边,原来是向前,就改为向后。 Dev…

作者头像 李华