之前组里有个兄弟问我“哥我想在昇腾上跑个推理不想用 PyTorch有没有更简单的方式”我说有用 acl。好问题。今天一次说清楚。acl 是啥acl Ascend Computing Language昇腾应用开发接口。昇腾的应用层 API让你可以直接调用昇腾的各种能力。一句话说清楚acl 是昇腾的应用开发接口提供推理、预处理、模型管理等高层 API不用关心底层细节。你说气人不气人原来要写 500 行代码用 acl 只要 50 行。为什么要用 acl三个字简单直接。不用 aclPyTorch 风格importtorchimporttorch.npu# 加载模型modeltorch.load(model.pth).npu()model.eval()# 预处理input_datapreprocess(raw_data).npu()# 推理withtorch.no_grad():outputmodel(input_data)# 后处理resultpostprocess(output)用 acl高层 APIimportacl# 初始化acl.init()# 加载模型modelacl.Model.load(model.om)# 推理一行搞定outputmodel.infer(input_data)# 后处理resultpostprocess(output)你说气人不气人用 acl 代码少了 10 倍。核心概念就三个1. 初始化和清理每个 acl 程序都要初始化和清理importacl# 初始化程序开头retacl.init()ifret!0:print(fInit failed:{ret})# 设置设备device_id0retacl.set_device(device_id)ifret!0:print(fSet device failed:{ret})# ... 业务逻辑 ...# 清理程序结尾acl.destroy()acl.finalize()2. 内存管理acl 有自己的内存管理接口importacl# 分配内存input_size224*224*3*4# bytesinput_bufferacl.util.malloc(input_size,acl.AclMemcpyKind.ACL_MEM_MALLOC_HUGE_FIRST)# 拷贝数据到设备dataload_image()# numpy arrayacl.util.copy_data_to_device(data,input_buffer)# 使用 buffer 进行推理# ...# 释放内存acl.util.free(input_buffer)3. 模型加载和推理acl 的核心功能importacl# 加载模型.om 文件由 ATC 转换而来model_pathmodel.ommodelacl.Model.load(model_path)# 准备输入input_dataload_preprocessed_data()# numpy arrayinputs[acl.Tensor.from_numpy(input_data)]# 推理outputsmodel.infer(inputs)# 处理输出outputoutputs[0].to_numpy()print(fResult:{output})为什么要用 acl三个理由1. 不用写 PyTorch有些人不想用 PyTorch或者用的是其他语言Python/C/Java直接用 acl 更简单importacl# C 风格也支持 C# acl.init();# acl.set_device(0);# auto model acl::Model::Load(model.om);# auto outputs model.Infer(input);2. 代码量少同样一个推理任务acl 代码量最少方式代码行数说明PyTorch100 行要写模型加载、预处理、推理、后处理ACL C API80 行要手动管理内存、TensorACL Python50 行高层封装更简单3. 性能不差acl 底层直接调用算子库性能和 PyTorch 差不多# PyTorchmodel.eval()withtorch.no_grad():outputmodel(input.npu())# ACLmodelacl.Model.load(model.om)outputmodel.infer(input)# 性能几乎一样都在 5ms 左右你说气人不气人代码少了 10 倍性能差不多。怎么用代码示例示例 1图像分类推理importaclimportnumpyasnpfromPILimportImage# 1. 初始化acl.init()# 2. 加载模型modelacl.Model.load(resnet50.om)# 3. 加载并预处理图片defpreprocess_image(image_path):imgImage.open(image_path).resize((224,224))imgnp.array(img).astype(np.float32)# 归一化img(img/255.0-0.5)/0.5# HWC - CHWimgimg.transpose(2,0,1)returnimg.flatten()# 4. 推理defpredict(image_path):# 预处理input_datapreprocess_image(image_path)# 推理inputs[acl.Tensor.from_numpy(input_data)]outputsmodel.infer(inputs)# 后处理outputoutputs[0].to_numpy()class_idnp.argmax(output)confidenceoutput[class_id]returnclass_id,confidence# 5. 测试class_id,confpredict(test.jpg)print(fClass:{class_id}, Confidence:{conf:.4f})# 6. 清理acl.destroy()acl.finalize()示例 2批量推理importaclimportnumpyasnpfromglobimportglob# 初始化acl.init()modelacl.Model.load(resnet50.om)# 批量推理defbatch_infer(image_paths,batch_size8):results[]foriinrange(0,len(image_paths),batch_size):batch_pathsimage_paths[i:ibatch_size]# 批量预处理batch_data[]forpathinbatch_paths:imgpreprocess_image(path)batch_data.append(img)batch_datanp.stack(batch_data)# 批量推理inputs[acl.Tensor.from_numpy(batch_data)]outputsmodel.infer(inputs)# 处理结果outputoutputs[0].to_numpy()forjinrange(len(batch_paths)):class_idnp.argmax(output[j])results.append((batch_paths[j],class_id))returnresults# 推理目录下所有图片image_pathsglob(images/*.jpg)resultsbatch_infer(image_paths,batch_size8)forpath,class_idinresults:print(f{path}: Class{class_id})示例 3C 风格进阶importaclimportnumpyasnp# 更底层的 API类似 C 风格# 1. 初始化acl.init()device_id0acl.set_device(device_id)# 2. 加载模型描述model_descacl.model.load_model_desc(resnet50.om)num_inputsacl.model.get_num_inputs(model_desc)num_outputsacl.model.get_num_outputs(model_desc)print(fInputs:{num_inputs}, Outputs:{num_outputs})# 3. 准备输入输出内存input_sizeacl.model.get_input_size(model_desc,0)input_bufferacl.util.malloc(input_size)output_sizeacl.model.get_output_size(model_desc,0)output_bufferacl.util.malloc(output_size)# 4. 加载模型并执行modelacl.model.load(resnet50.om)# 准备输入数据dataload_preprocessed_data()acl.util.copy_data_to_device(data,input_buffer)# 执行推理datasetsacl.DataSet()datasets.add_input(input_buffer,input_size)datasets.add_output(output_buffer,output_size)model.execute(datasets)# 获取结果resultacl.util.copy_data_from_device(output_buffer,output_size)resultnp.frombuffer(result,dtypenp.float32)# 5. 清理acl.util.free(input_buffer)acl.util.free(output_buffer)acl.model.unload(model)acl.destroy_device(device_id)acl.finalize()性能数据在昇腾 910 上推理 ResNet-50实现推理延迟吞吐量备注PyTorch5.2ms192 img/s通用实现ACL (高层)5.0ms200 img/s高层封装ACL (底层)4.8ms208 img/s底层优化你说气人不气人acl 和 PyTorch 性能差不多但代码量少很多。跟其他仓库的关系acl 在 CANN 架构里属于第 1 层昇腾计算语言层是应用开发的主入口。依赖关系acl应用层← 你的代码 ↓ 算子层ops-xxx ↓ catlass / ATB加速库 ↓ 算子基础opbase解释一下acl应用层 API推理、预处理、模型管理ops-xxx具体算子卷积、矩阵乘…catlass / ATB加速库底层优化opbase基础组件简单说acl是给做应用的人用的帮你快速跑推理不用关心底层。acl 的核心能力1. 模型加载和执行# 加载 .om 模型modelacl.Model.load(model.om)# 推理outputsmodel.infer(inputs)# 卸载模型model.unload()2. 内存管理# 分配设备内存bufacl.util.malloc(size,acl.AclMemcpyKind.ACL_MEM_MALLOC_HUGE_FIRST)# 拷贝数据acl.util.copy_data_to_device(cpu_data,device_buffer)acl.util.copy_data_from_device(device_buffer,cpu_data)# 释放内存acl.util.free(buf)3. 图片处理importacl.imageasacl_image# 读取图片imgacl_image.read(test.jpg)# 缩放resizedacl_image.resize(img,(224,224))# 格式转换rgbacl_image.convert_to_rgb(img)normalizedacl_image.normalize(rgb,(0.5,0.5,0.5),(0.5,0.5,0.5))# 释放acl_image.destroy(img)4. 数据类型转换# numpy - Tensortensoracl.Tensor.from_numpy(numpy_array)numpy_arraytensor.to_numpy()# numpy - Bufferacl.util.copy_data_to_device(numpy_array,buffer)acl.util.copy_data_from_device(buffer,numpy_array)适用场景什么情况下用 acl简单推理不需要 PyTorch 的复杂功能C 应用不想用 Python嵌入式资源受限需要轻量级 API快速原型快速验证算法什么情况下不用训练训练用 PyTorch不用 acl复杂模型PyTorch 更方便科研需要灵活控制总结acl 就是昇腾的应用开发接口模型加载一行代码加载 .om 模型推理执行一行代码跑推理图片处理缩放、格式转换、归一化内存管理设备内存分配和拷贝