CANN动态shape推理:处理可变输入的高效方案
引言在实际的深度学习应用中模型的输入尺寸往往不是固定的。例如自然语言处理中的文本长度、目标检测中的图像分辨率、语音识别中的音频时长都可能变化。传统的静态shape推理需要为每种输入尺寸编译一个模型既浪费存储空间又增加了部署复杂度。华为CANN平台提供了强大的动态shape推理能力允许模型在运行时适应不同尺寸的输入同时保持高性能。本文将详细介绍CANN动态shape的使用方法和优化技巧。相关链接CANN组织ops-nn仓库一、动态shape的应用场景1.1 为什么需要动态shape在以下场景中动态shape是必不可少的自然语言处理不同句子的长度差异很大从几个词到几百个词不等。如果使用固定长度要么浪费计算资源padding到最大长度要么限制了模型的适用范围。计算机视觉目标检测和分割任务中输入图像的分辨率可能不同视频处理中帧数也是可变的。语音识别音频片段的时长各不相同使用动态shape可以避免不必要的padding。批处理优化在服务端部署时可以根据请求量动态调整batch size提高硬件利用率。1.2 CANN动态shape的优势相比其他推理框架CANN的动态shape实现具有以下优势零拷贝切换在不同shape之间切换时无需重新加载模型自动优化CANN会为常见的shape组合生成优化的执行路径内存高效动态分配内存避免为最大尺寸预留空间性能稳定通过shape缓存机制常用shape的性能接近静态编译二、CANN动态shape基础使用2.1 导出动态shape模型首先我们需要在模型导出时指定哪些维度是动态的。以BERT模型为例importtorchimporttorch.onnx# 加载BERT模型fromtransformersimportBertModel,BertTokenizer modelBertModel.from_pretrained(bert-base-uncased)model.eval()# 准备示例输入tokenizerBertTokenizer.from_pretrained(bert-base-uncased)textHello, CANN dynamic shape!inputstokenizer(text,return_tensorspt)# 导出为ONNX指定动态维度# batch_size和sequence_length都是动态的dynamic_axes{input_ids:{0:batch_size,1:sequence_length},attention_mask:{0:batch_size,1:sequence_length},last_hidden_state:{0:batch_size,1:sequence_length}}torch.onnx.export(model,(inputs[input_ids],inputs[attention_mask]),bert_dynamic.onnx,input_names[input_ids,attention_mask],output_names[last_hidden_state],dynamic_axesdynamic_axes,opset_version14)print(动态shape模型导出完成)2.2 使用ATC转换动态shape模型接下来使用CANN的ATC工具将ONNX模型转换为支持动态shape的OM格式# 转换为动态shape的OM模型atc --modelbert_dynamic.onnx\--framework5\--outputbert_dynamic\--input_shapeinput_ids:-1,-1;attention_mask:-1,-1\--dynamic_dims1,128;1,256;1,512;4,128;4,256;4,512\--soc_versionAscend910\--logerror# 参数说明# --input_shape: -1表示该维度是动态的# --dynamic_dims: 指定支持的shape组合格式为batch,seq_len# 这里支持batch1或4seq_len128/256/512的组合这个命令告诉CANN模型需要支持6种不同的输入shape组合。CANN会为这些组合生成优化的执行计划。2.3 动态shape推理代码使用CANN ACL接口进行动态shape推理importaclimportnumpyasnpclassCANNDynamicInference:CANN动态shape推理类def__init__(self,model_path,device_id0):# 初始化ACLself.device_iddevice_id retacl.init()assertret0,ACL初始化失败retacl.rt.set_device(self.device_id)assertret0,设置设备失败# 加载模型self.model_id,retacl.mdl.load_from_file(model_path)assertret0,模型加载失败self.model_descacl.mdl.create_desc()retacl.mdl.get_desc(self.model_desc,self.model_id)assertret0,获取模型描述失败print(f模型加载成功支持动态shape推理)definfer(self,input_ids,attention_mask):执行动态shape推理batch_size,seq_lengthinput_ids.shapeprint(f推理shape: batch{batch_size}, seq_len{seq_length})# 创建输入数据集input_datasetacl.mdl.create_dataset()# 添加input_idsinput_ids_bufferself._create_data_buffer(input_ids)acl.mdl.add_dataset_buffer(input_dataset,input_ids_buffer)# 添加attention_maskmask_bufferself._create_data_buffer(attention_mask)acl.mdl.add_dataset_buffer(input_dataset,mask_buffer)# 创建输出数据集output_datasetacl.mdl.create_dataset()# 执行推理 - CANN会自动适配当前的shaperetacl.mdl.execute(self.model_id,input_dataset,output_dataset)assertret0,推理执行失败# 获取输出output_bufferacl.mdl.get_dataset_buffer(output_dataset,0)output_dataself._get_buffer_data(output_buffer,(batch_size,seq_length,768))# 清理资源self._destroy_dataset(input_dataset)self._destroy_dataset(output_dataset)returnoutput_datadef_create_data_buffer(self,data):创建数据bufferdata_npdata.astype(np.int32)data_sizedata_np.nbytes# 在设备上分配内存device_ptr,retacl.rt.malloc(data_size,0)assertret0,设备内存分配失败# 拷贝数据到设备retacl.rt.memcpy(device_ptr,data_size,data_np.ctypes.data,data_size,0)assertret0,数据拷贝失败# 创建bufferbufferacl.create_data_buffer(device_ptr,data_size)returnbufferdef_get_buffer_data(self,buffer,shape):从buffer获取数据data_ptracl.get_data_buffer_addr(buffer)data_sizeacl.get_data_buffer_size(buffer)# 分配主机内存host_datanp.zeros(shape,dtypenp.float32)# 从设备拷贝到主机retacl.rt.memcpy(host_data.ctypes.data,data_size,data_ptr,data_size,1)assertret0,数据拷贝失败returnhost_datadef_destroy_dataset(self,dataset):销毁数据集numacl.mdl.get_dataset_num_buffers(dataset)foriinrange(num):bufferacl.mdl.get_dataset_buffer(dataset,i)data_ptracl.get_data_buffer_addr(buffer)acl.rt.free(data_ptr)acl.destroy_data_buffer(buffer)acl.mdl.destroy_dataset(dataset)# 使用示例inferenceCANNDynamicInference(bert_dynamic.om)# 测试不同的输入shapetest_cases[(1,128),# batch1, seq_len128(4,256),# batch4, seq_len256(1,512),# batch1, seq_len512]forbatch,seq_lenintest_cases:input_idsnp.random.randint(0,30000,(batch,seq_len))attention_masknp.ones((batch,seq_len))outputinference.infer(input_ids,attention_mask)print(f输出shape:{output.shape}\n)三、动态shape性能优化3.1 Shape档位设计在使用动态shape时合理设计shape档位gear是性能优化的关键。档位就是在模型转换时通过--dynamic_dims指定的shape组合。档位设计原则覆盖常用场景分析实际业务数据统计最常出现的输入尺寸优先为这些尺寸设置档位避免过多档位档位越多模型文件越大首次推理的编译时间也越长。建议控制在10个以内考虑内存对齐选择能被硬件向量长度整除的尺寸如64、128、256等预留余量为未来可能的需求预留一些档位例如对于BERT模型可以这样设计档位# 推荐的档位设计--dynamic_dims1,64;1,128;1,256;1,512;4,64;4,128;4,256;8,128# 覆盖场景# - 单样本推理batch1支持64/128/256/512长度# - 小批量batch4支持64/128/256长度# - 批处理batch8支持128长度3.2 Shape缓存机制CANN内部实现了shape缓存机制。当某个shape首次出现时CANN会进行即时编译JIT生成针对该shape的优化代码。后续再遇到相同shape时直接使用缓存的代码性能接近静态编译。importtimedefbenchmark_dynamic_shape(inference,shapes,warmup5,iterations50):测试动态shape性能results{}forbatch,seq_leninshapes:input_idsnp.random.randint(0,30000,(batch,seq_len))attention_masknp.ones((batch,seq_len))# 预热 - 触发shape缓存print(f预热 shape ({batch},{seq_len})...)for_inrange(warmup):_inference.infer(input_ids,attention_mask)# 性能测试starttime.time()for_inrange(iterations):_inference.infer(input_ids,attention_mask)elapsed(time.time()-start)/iterations results[(batch,seq_len)]elapsed*1000# 转换为msprint(fShape ({batch},{seq_len}):{elapsed*1000:.2f}ms)returnresults# 测试不同shape的性能shapes[(1,128),(1,256),(4,128),(4,256)]resultsbenchmark_dynamic_shape(inference,shapes)# 分析结果print(\n性能汇总:)forshape,latencyinresults.items():print(f{shape}:{latency:.2f}ms)从测试结果可以看到预热后的推理性能非常稳定说明shape缓存机制工作良好。3.3 内存优化策略动态shape推理的内存管理比静态shape更复杂。CANN提供了几种内存优化策略动态内存分配CANN会根据实际输入的shape动态分配内存避免为最大尺寸预留空间。内存池复用对于频繁出现的shapeCANN会将内存保留在池中减少分配和释放的开销。分段分配对于超大输入CANN支持分段处理避免一次性分配过多内存。# 配置CANN内存策略importacl# 设置内存复用acl.rt.set_op_wait_timeout(100)# 设置算子等待超时# 启用内存优化acl.rt.set_op_debug_level(0)# 关闭调试减少内存开销四、高级应用场景4.1 自适应批处理在服务端部署时可以根据请求量动态调整batch size提高吞吐量importqueueimportthreadingclassCANNDynamicBatcher:CANN动态批处理器def__init__(self,inference,max_batch8,timeout0.01):self.inferenceinference self.max_batchmax_batch self.timeouttimeout self.request_queuequeue.Queue()self.runningTrue# 启动批处理线程self.workerthreading.Thread(targetself._batch_worker)self.worker.start()defpredict(self,input_ids,attention_mask):提交推理请求result_queuequeue.Queue()self.request_queue.put((input_ids,attention_mask,result_queue))returnresult_queue.get()# 等待结果def_batch_worker(self):批处理工作线程whileself.running:batch_requests[]# 收集请求直到达到max_batch或超时try:# 等待第一个请求requestself.request_queue.get(timeoutself.timeout)batch_requests.append(request)# 尝试收集更多请求whilelen(batch_requests)self.max_batch:try:requestself.request_queue.get(timeout0.001)batch_requests.append(request)exceptqueue.Empty:breakexceptqueue.Empty:continue# 执行批量推理ifbatch_requests:self._process_batch(batch_requests)def_process_batch(self,requests):处理一批请求# 合并输入input_ids_list[req[0]forreqinrequests]mask_list[req[1]forreqinrequests]# Padding到相同长度max_lenmax(ids.shape[0]foridsininput_ids_list)batch_sizelen(requests)input_ids_batchnp.zeros((batch_size,max_len),dtypenp.int32)mask_batchnp.zeros((batch_size,max_len),dtypenp.int32)fori,(ids,mask)inenumerate(zip(input_ids_list,mask_list)):lengthids.shape[0]input_ids_batch[i,:length]ids mask_batch[i,:length]mask# 批量推理 - CANN自动适配batch sizeoutputself.inference.infer(input_ids_batch,mask_batch)# 分发结果fori,requestinenumerate(requests):result_queuerequest[2]result_queue.put(output[i])defstop(self):停止批处理器self.runningFalseself.worker.join()# 使用示例batcherCANNDynamicBatcher(inference,max_batch8)# 模拟多个并发请求importconcurrent.futuresdefsend_request(text_length):input_idsnp.random.randint(0,30000,text_length)attention_masknp.ones(text_length)resultbatcher.predict(input_ids,attention_mask)returnresult.shape# 并发发送请求withconcurrent.futures.ThreadPoolExecutor(max_workers10)asexecutor:futures[executor.submit(send_request,np.random.randint(64,256))for_inrange(20)]results[f.result()forfinfutures]print(f处理了{len(results)}个请求)batcher.stop()4.2 多模态动态shape在多模态模型中不同模态的输入shape可能都是动态的。CANN支持多输入的动态shape# 导出多模态模型图像文本dynamic_axes{image:{0:batch_size,2:height,3:width},# 图像动态batch和分辨率text:{0:batch_size,1:seq_length},# 文本动态batch和长度output:{0:batch_size}}torch.onnx.export(multimodal_model,(sample_image,sample_text),multimodal_dynamic.onnx,dynamic_axesdynamic_axes,opset_version14)# ATC转换时指定多个动态维度# atc --modelmultimodal_dynamic.onnx \# --input_shapeimage:-1,3,-1,-1;text:-1,-1 \# --dynamic_dims1,224,224,128;1,384,384,256;4,224,224,128 \# ...五、动态shape最佳实践5.1 何时使用动态shape适合使用动态shape的场景输入尺寸变化范围大且不可预测需要支持多种输入尺寸但不想维护多个模型服务端部署需要动态批处理不适合使用动态shape的场景输入尺寸固定或变化很小对首次推理延迟要求极高动态shape首次需要编译边缘设备资源受限动态shape需要更多内存5.2 性能调优建议合理设置档位根据实际数据分布设置档位避免过多或过少预热充分在正式服务前对所有档位进行预热触发shape缓存监控shape分布记录实际推理中各shape的出现频率优化档位设置批处理优化尽量使用批处理提高硬件利用率# 预热所有档位defwarmup_all_gears(inference,gear_shapes,iterations10):预热所有档位print(开始预热所有档位...)forbatch,seq_leningear_shapes:print(f 预热 ({batch},{seq_len})...)input_idsnp.random.randint(0,30000,(batch,seq_len))attention_masknp.ones((batch,seq_len))for_inrange(iterations):_inference.infer(input_ids,attention_mask)print(预热完成所有档位已缓存)# 在服务启动时预热gear_shapes[(1,128),(1,256),(4,128),(4,256)]warmup_all_gears(inference,gear_shapes)5.3 故障排查如果遇到动态shape相关问题可以按以下步骤排查检查档位设置确认实际输入shape是否在档位范围内查看日志CANN会输出详细的shape匹配日志验证模型导出确认ONNX模型的动态维度设置正确测试边界情况测试最小和最大shape是否正常工作总结CANN的动态shape功能为处理可变输入提供了高效的解决方案。通过本文的学习我们掌握了动态shape的应用场景和优势如何导出和转换动态shape模型动态shape推理的实现方法性能优化技巧包括档位设计和shape缓存高级应用场景如自适应批处理和多模态支持在实际应用中建议根据业务特点选择合适的档位配置并通过充分预热来获得最佳性能。动态shape虽然增加了一些复杂度但带来的灵活性和资源节省是非常值得的。

相关新闻

如何创建一个PR

如何创建一个PR

第一阶段:本地准备 (在终端操作) 这几步是为了确保你的代码在本地是干净、准确地打包好的。 1. 确认身份 git branch 作用:查看当前所在的分支。 检查点:必须看到 * crj_develop(你的名字分支)是绿色的。 为什么&…

2026/7/5 19:51:46 阅读更多 →
区块链宕机致爆仓提现延迟成常态,Matrixdock交易平台能扛住重压吗?

区块链宕机致爆仓提现延迟成常态,Matrixdock交易平台能扛住重压吗?

区块链宕机致爆仓提现延迟成常态,Matrixdock交易平台能扛住重压吗?近年来,区块链交易所宕机事件频发,从币安多次因系统过载导致交易中断,到TON区块链因迷因币热潮引发网络瘫痪,投资者对交易平台的稳定性越发…

2026/7/5 2:23:52 阅读更多 →
用星流AI做库洛米卡牌APP,每一张都颜值爆表!

用星流AI做库洛米卡牌APP,每一张都颜值爆表!

家人们,库洛米脑袋要嗨疯了,我不仅把它做成了手势交互游戏,又甜又酷。而且我做了全套库洛米APP,可以感受暗黑甜心沉浸式体验哦。这是我用星流agent做的库洛米卡牌星流,就是国内版的lovart没有网络要求,小白…

2026/7/5 9:39:54 阅读更多 →

最新新闻

你的前端代码打包后究竟经历了什么?

你的前端代码打包后究竟经历了什么?

打包命令执行的一瞬间,构建工具并不会立刻编译代码,第一步永远是读取并整合所有配置规则。构建工具配置读取: 以 Vite 为例,工具会自动查找项目根目录 vite.config.js,读取入口文件、输出目录、打包策略、公共路径等核…

2026/7/6 3:50:11 阅读更多 →
[实例] SPI接口的ADC芯片全通道纯硬件驱动——基于HAL库和TLA2518芯片

[实例] SPI接口的ADC芯片全通道纯硬件驱动——基于HAL库和TLA2518芯片

本次需要通过TI的TL2518芯片进行ADC采样。该芯片为SPI接口,具有八个通道,可以全部配置成AIN进行采样,本次需要探究如何该如何配置才能将芯片的采样率达到最大。1.TLA2158首先要陈列一下该芯片的一些特性,为节省篇幅,此…

2026/7/6 3:48:11 阅读更多 →
【全文系列目录】风控PM记

【全文系列目录】风控PM记

风控PM记 一:风险认知与识别(入门篇) ① 入门第一课:认识风险,了解风控 ② 入门第二课:业务催生风险,常见的业务风险有哪些? ③ 《电商风控入门:我们到底在“防”什…

2026/7/6 3:48:11 阅读更多 →
基于Databricks的企业级AI Agent生产实践:从架构设计到部署运维

基于Databricks的企业级AI Agent生产实践:从架构设计到部署运维

🚀 30款热门AI模型一站整合,DeepSeek/GLM/Qwen 随心用,限时 5 折。 👉 点击领海量免费额度 如果你正在考虑将AI Agent引入企业生产环境,可能会面临这样的困境:在本地开发环境中跑得飞快的Agent原型&…

2026/7/6 3:42:09 阅读更多 →
飞书卡片表格渲染踩坑记:从 Markdown 到原生 table 组件的迁移实战

飞书卡片表格渲染踩坑记:从 Markdown 到原生 table 组件的迁移实战

背景 团队每日通过飞书推送项目晨报和日报,内容从项目管理平台实时拉取,包含任务统计、进度列表、风险项等多维数据,天然需要表格来承载。 最初的实现方案是飞书消息推送 纯文本,格式简陋,阅读体验差。于是决定升级为…

2026/7/6 3:40:09 阅读更多 →
构建AI毒舌投资人:用Prompt工程验证副业想法的可行性

构建AI毒舌投资人:用Prompt工程验证副业想法的可行性

🚀 30款热门AI模型一站整合,DeepSeek/GLM/Qwen 随心用,限时 5 折。 👉 点击领海量免费额度 最近在折腾各种 AI 工具时,我发现一个挺有意思的现象:很多人拿到一个强大的 AI 模型,比如 DeepSee…

2026/7/6 3:40:09 阅读更多 →

日新闻

H2 与 MySQL 单元测试兼容性:5 个关键 SQL 语句差异与规避方案

H2 与 MySQL 单元测试兼容性:5 个关键 SQL 语句差异与规避方案

H2与MySQL单元测试兼容性:5个关键SQL语句差异与规避方案1. 单元测试中的数据库兼容性挑战在Java开发领域,单元测试是保证代码质量的重要环节。当应用涉及数据库操作时,测试环境的搭建往往成为开发者的痛点。H2数据库因其轻量级、内存模式和快…

2026/7/6 0:01:17 阅读更多 →
Windows任务栏终极清理指南:用RBTray一键隐藏窗口到系统托盘

Windows任务栏终极清理指南:用RBTray一键隐藏窗口到系统托盘

Windows任务栏终极清理指南:用RBTray一键隐藏窗口到系统托盘 【免费下载链接】rbtray A fork of RBTray from http://sourceforge.net/p/rbtray/code/. 项目地址: https://gitcode.com/gh_mirrors/rb/rbtray 你是否厌倦了Windows任务栏上密密麻麻的图标&…

2026/7/6 0:01:17 阅读更多 →
Visual C++ 运行时库一键安装终极指南:告别DLL缺失烦恼

Visual C++ 运行时库一键安装终极指南:告别DLL缺失烦恼

Visual C 运行时库一键安装终极指南:告别DLL缺失烦恼 【免费下载链接】vcredist AIO Repack for latest Microsoft Visual C Redistributable Runtimes 项目地址: https://gitcode.com/gh_mirrors/vc/vcredist 你是否曾经遇到过这样的情况:下载了…

2026/7/6 0:05:19 阅读更多 →

周新闻

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱,支持下载视频、番剧等等各类资源 项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliTools …

2026/7/5 0:03:34 阅读更多 →
威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型的陌生现状在忙碌疲惫的一天里,参与了关于混合后量子密码学的讨论,应付端点攻击找茬的人,还参与留言板讨论后,发现“威胁模型”对多数人仍是陌生概念,且多被当作时髦用语。有趣的相关画作有一幅由 Embyr 创作的…

2026/7/5 0:03:34 阅读更多 →
渗透测试入门指南:从零基础到实战环境搭建

渗透测试入门指南:从零基础到实战环境搭建

1. 从“看热闹”到“入门”:我理解的渗透测试到底是什么?每次看到新闻里说某个大公司的数据被“黑”了,或者某个网站被攻击导致服务瘫痪,你是不是和我一样,心里会冒出两个念头:一是“这黑客真厉害”&#x…

2026/7/5 0:07:38 阅读更多 →

月新闻