前言cann-samples 仓是昇腾官方的示例代码库涵盖了从基础到进阶的各种场景。这篇文章从零开始用 cann-samples 把 ResNet50 的推理走通。cann-samples 仓的结构仓库目录gitclone https://atomgit.com/cann/cann-samplescdcann-samples tree-L2# cann-samples/# ├── basic/ # 基础示例HelloWorld、算子调用# ├── cv/ # 计算机视觉图像分类、目标检测# ├── nlp/ # 自然语言处理文本分类、序列模型# ├── audio/ # 音频处理语音识别、音频分类# └── utils/ # 通用工具数据加载、性能测试快速定位示例每个示例都有完整的 README.md告诉你需要什么输入、如何运行、预期输出是什么。基础示例HelloWorld 算子调用第一个昇腾程序# 01_hello_world.pyimportcannimportnumpyasnp# 1. 初始化 ACLAscend Computing Languagecann.init()# 2. 创建一个 tensordevice_tensorcann.Tensor(shape(3,3),dtypecann.DTYPE.FLOAT32,devicecann.DEVICE.NPU)# 3. 填充数据datanp.random.randn(3,3).astype(np.float32)device_tensor.from_numpy(data)# 4. 调用算子矩阵加法resultcann.ops.add(device_tensor,device_tensor)# 5. 把结果拿回 CPUoutputresult.to_numpy()print(f输入:\n{data})print(f输出:\n{output})print(f结果正确:{np.allclose(data*2,output)})# 运行python 01_hello_world.py理解 ACL 初始化ACL 是昇腾的统一 API 层初始化是所有操作的前置条件# 02_init_acl.pyimportcann# 方式1默认初始化cann.init()# 方式2指定配置configcann.Config({device_id:0,# 使用第 0 个 NPUrank_id:0,# 分布式 rank单卡设为 0log_level:3,# 日志级别0ERROR, 1WARNING, 2INFO, 3DEBUGacl_config:acl.json# 配置文件路径})cann.init(config)# 3. 确认初始化成功print(fACL 版本:{cann.__version__})print(f设备数量:{cann.get_device_count()})# 输出# ACL 版本: 5.1.RC3# 设备数量: 8图像分类ResNet50 端到端推理完整推理流程# 03_resnet50_inference.pyimportcannimportnumpyasnpfromPILimportImage# 1. 初始化cann.init()# 2. 加载模型OM 格式modelcann.model.load(resnet50.om)print(f模型加载成功输入shape:{model.get_input_shape()})# 输出模型加载成功输入shape: [1, 3, 224, 224]# 3. 图片预处理defpreprocess(image_path):ImageNet 标准预处理imgImage.open(image_path).convert(RGB)# 缩放到 256w,himg.size scale256/min(w,h)new_w,new_hint(w*scale),int(h*scale)imgimg.resize((new_w,new_h))# 中心裁剪到 224left(new_w-224)//2top(new_h-224)//2imgimg.crop((left,top,left224,top224))# 转 numpy 数组 (H, W, C) - (C, H, W)img_arraynp.array(img).astype(np.float32)/255.0# ImageNet 标准化meannp.array([0.485,0.456,0.406])stdnp.array([0.229,0.224,0.225])img_array(img_array-mean)/std# (H, W, C) - (C, H, W) - (N, C, H, W)img_arrayimg_array.transpose(2,0,1)img_arrayimg_array[np.newaxis,:,:,:]returnimg_array.astype(np.float32)# 4. 执行推理imagepreprocess(test_image.jpg)input_tensorcann.Tensor.from_numpy(image)output_tensormodel.execute(input_tensor)# 5. 后处理outputoutput_tensor.to_numpy()pred_classint(np.argmax(output))print(f预测类别:{pred_class})# 6. 清理资源model.release()cann.shutdown()批量推理# 04_batch_inference.pyimportcannimportglob# 初始化cann.init()modelcann.model.load(resnet50.om)# 批量处理图片image_pathsglob.glob(test_images/*.jpg)batch_size8results[]foriinrange(0,len(image_paths),batch_size):batch_pathsimage_paths[i:ibatch_size]batch_images[]forpathinbatch_paths:imgpreprocess(path)batch_images.append(img)# 拼接 batchbatch_tensorcann.Tensor.from_numpy(np.concatenate(batch_images,axis0))# 推理outputsmodel.execute(batch_tensor)outputs_npoutputs.to_numpy()# 记录结果forj,outinenumerate(outputs_np):predint(np.argmax(out))results.append((batch_paths[j],pred))print(f{batch_paths[j]}- 类别{pred})print(f批量推理完成:{len(results)}张图片)# 清理model.release()cann.shutdown()性能调优batch size 和并行数配置调整 batch size# 05_batch_size_tuning.pyimportcannimporttimeimportnumpyasnp cann.init()modelcann.model.load(resnet50.om)# 不同的 batch size 测试batch_sizes[1,2,4,8,16,32,64]forbsinbatch_sizes:# 准备输入dummynp.random.randn(bs,3,224,224).astype(np.float32)input_tensorcann.Tensor.from_numpy(dummy)# Warmupfor_inrange(10):_model.execute(input_tensor)# 正式测试n_iters100starttime.time()for_inrange(n_iters):_model.execute(input_tensor)elapsedtime.time()-start avg_latency(elapsed/n_iters)*1000# msthroughputbs/(elapsed/n_iters)# FPSprint(fBatch{bs:3d}Latency{avg_latency:6.2f}ms Throughput{throughput:7.1f}FPS)# 输出示例# Batch 1 Latency 1.23ms Throughput 813.0 FPS# Batch 8 Latency 4.56ms Throughput 1754.4 FPS# Batch 16 Latency 8.12ms Throughput 1970.4 FPS# Batch 32 Latency 14.23ms Throughput 2248.2 FPS# Batch 64 Latency 26.45ms Throughput 2420.8 FPSmodel.release()cann.shutdown()batch size 选型建议场景推荐 batch size原因延迟敏感在线推理1~4快速响应吞吐敏感批处理16~32吞吐最优超大模型显存受限1~2显存限制延迟不敏感离线处理64吞吐最高NPU 并行数配置# 06_parallel_config.pyimportcann# 配置 NPU 并行执行configcann.Config()# 方案1单线程延迟敏感场景config.set(acl_thread,1)config.set(acl_device,0)# 方案2多线程吞吐敏感场景config.set(acl_thread,4)# 4 个线程并发执行config.set(acl_device,0)# 方案3多卡并行大规模推理fordevice_idinrange(8):modelcann.model.load(resnet50.om,device_iddevice_id)# 每个 device 独立加载一个模型实例# 验证配置print(f当前线程数:{cann.get_config(acl_thread)})print(f当前设备ID:{cann.get_config(acl_device)})性能分析工具# 07_profiling.pyimportcann# 开启 profilingprofilercann.Profiler()profiler.start()# 执行推理modelcann.model.load(resnet50.om)for_inrange(100):input_tensorcann.Tensor.from_numpy(np.random.randn(1,3,224,224).astype(np.float32))_model.execute(input_tensor)# 停止 profiling 并导出报告profiler.stop()profiler.export(resnet50_profile.json)# 查看报告print(profiler.summary())# 输出示例# Operator Calls Avg(ms) Total(ms)# Conv2d 100 0.523 52.3# BatchNorm2d 100 0.089 8.9# ReLU 100 0.042 4.2# MatMul 50 0.312 15.6# Softmax 100 0.067 6.7踩坑记录Atlas 服务器上的环境变量问题常见错误汇总错误原因解决方式ACL 初始化失败驱动版本不匹配升级/降级驱动到匹配版本模型加载失败OM 文件路径问题使用绝对路径推理结果全零输入数据未正确传输到 NPU检查 Tensor.from_numpy()batch size 受限显存不足减小 batch 或优化模型多线程卡死ACL 线程数配置错误设为 1 或偶数环境变量配置# 07_env_config.sh#!/bin/bash# 必须设置的环境变量exportASCEND_DEVICE_ID0exportASCEND_VISIBLE_DEVICES0,1,2,3,4,5,6,7# CANN 安装路径exportASCEND_TOOLKIT_HOME/usr/local/Ascend/ascend-toolkitexportLD_LIBRARY_PATH$ASCEND_TOOLKIT_HOME/runtime/lib64:$LD_LIBRARY_PATHexportPATH$ASCEND_TOOLKIT_HOME/bin:$PATH# ACL 配置可选exportASCEND_GLOBAL_LOG_LEVEL3exportASCEND_GLOBAL_DUMP_PATH./dump# 运行python 03_resnet50_inference.py# 查看 NPU 状态npu-smi info资源泄漏排查# 08_resource_leak_check.pyimportcann# 每次运行后检查资源defcheck_resources():print(f已分配 Tensor 数:{cann.Tensor.get_allocated_count()})print(f已加载模型数:{cann.model.get_loaded_count()})print(f显存使用:{cann.mem.get_allocated()/1024**3:.2f}GB)print(f显存峰值:{cann.mem.get_peak()/1024**3:.2f}GB)# 在循环中使用确保资源释放forepochinrange(1000):modelcann.model.load(resnet50.om)_model.execute(input_tensor)# 释放模型资源model.release()# 每 100 次检查一次ifepoch%1000:check_resources()# 如果发现资源持续增长说明有泄漏进阶示例目标检测模型在图像分类基础上目标检测需要更复杂的预处理和后处理# 09_object_detection.pyimportcannimportnumpyasnpfromPILimportImage cann.init()modelcann.model.load(yolov5s.om)defpreprocess_detection(image_path):YOLOv5 预处理640x640 方形输入imgImage.open(image_path).convert(RGB)w,himg.size# 计算缩放比例scale640/max(w,h)new_w,new_hint(w*scale),int(h*scale)img_resizedimg.resize((new_w,new_h),Image.BILINEAR)# 填充到 640x640灰色填充img_paddedImage.new(RGB,(640,640),(114,114,114))img_padded.paste(img_resized,(0,0))# 归一化img_arraynp.array(img_padded).astype(np.float32)/255.0img_arrayimg_array.transpose(2,0,1)returnimg_array.astype(np.float32),scaledefpostprocess_detect(outputs,scale,orig_size,conf_thresh0.5):NMS 后处理# 解析输出[batch, 5num_classes, num_boxes]predictionsoutputs[0]# shape: (1, 85, 8400)boxes[]forpredinpredictions.transpose(2,1):# (8400, 85)obj_confpred[4]class_scorespred[5:]*obj_conf class_idnp.argmax(class_scores)class_scoreclass_scores[class_id]ifclass_scoreconf_thresh:x,y,w,hpred[0:4]boxes.append([x,y,w,h,class_id,class_score])# NMSboxesnp.array(boxes)iflen(boxes)0:return[]# 按置信度排序boxesboxes[boxes[:,5].argsort()[::-1]]# NMSkeep[]whilelen(boxes)0:keep.append(boxes[0])iflen(boxes)1:break# 计算 IoUious[compute_iou(boxes[0],b)forbinboxes[1:]]boxesboxes[1:][np.array(ious)0.45]returnkeep# 完整流程image,scalepreprocess_detection(test.jpg)input_tensorcann.Tensor.from_numpy(image[np.newaxis])outputsmodel.execute(input_tensor)orig_w,orig_hImage.open(test.jpg).size detectionspostprocess_detect(outputs,scale,(orig_w,orig_h))print(f检测到{len(detections)}个目标)fordetindetections:x,y,w,h,class_id,scoredetprint(f 类别{int(class_id)}: 置信度{score:.2f}, 位置 ({x:.0f},{y:.0f},{w:.0f},{h:.0f}))总结cann-samples 是学习 CANN 最快的路径每个示例都跑一遍上手速度翻倍。阶段内容建议时间HelloWorld理解 ACL 初始化和基本 tensor 操作30 分钟ResNet50走通从图片到预测的完整流程1 小时性能调优batch size、并行数、profiling2 小时踩坑记录环境变量、资源泄漏、错误排查持续进阶路线图基础 (1-2天) - HelloWorld ResNet50 推理 - 熟悉 ACL API 和调试方法 进阶 (3-5天) - 目标检测、语义分割等 CV 任务 - 多卡并行推理 - Profiling 性能分析 生产 (1周) - 模型量化FP16/INT8 - 流水线优化 - 自定义算子开发仓库地址https://atomgit.com/cann/cann-samples
昇腾 CANN cann-samples 仓:从 HelloWorld 到 ResNet50 推理
前言cann-samples 仓是昇腾官方的示例代码库涵盖了从基础到进阶的各种场景。这篇文章从零开始用 cann-samples 把 ResNet50 的推理走通。cann-samples 仓的结构仓库目录gitclone https://atomgit.com/cann/cann-samplescdcann-samples tree-L2# cann-samples/# ├── basic/ # 基础示例HelloWorld、算子调用# ├── cv/ # 计算机视觉图像分类、目标检测# ├── nlp/ # 自然语言处理文本分类、序列模型# ├── audio/ # 音频处理语音识别、音频分类# └── utils/ # 通用工具数据加载、性能测试快速定位示例每个示例都有完整的 README.md告诉你需要什么输入、如何运行、预期输出是什么。基础示例HelloWorld 算子调用第一个昇腾程序# 01_hello_world.pyimportcannimportnumpyasnp# 1. 初始化 ACLAscend Computing Languagecann.init()# 2. 创建一个 tensordevice_tensorcann.Tensor(shape(3,3),dtypecann.DTYPE.FLOAT32,devicecann.DEVICE.NPU)# 3. 填充数据datanp.random.randn(3,3).astype(np.float32)device_tensor.from_numpy(data)# 4. 调用算子矩阵加法resultcann.ops.add(device_tensor,device_tensor)# 5. 把结果拿回 CPUoutputresult.to_numpy()print(f输入:\n{data})print(f输出:\n{output})print(f结果正确:{np.allclose(data*2,output)})# 运行python 01_hello_world.py理解 ACL 初始化ACL 是昇腾的统一 API 层初始化是所有操作的前置条件# 02_init_acl.pyimportcann# 方式1默认初始化cann.init()# 方式2指定配置configcann.Config({device_id:0,# 使用第 0 个 NPUrank_id:0,# 分布式 rank单卡设为 0log_level:3,# 日志级别0ERROR, 1WARNING, 2INFO, 3DEBUGacl_config:acl.json# 配置文件路径})cann.init(config)# 3. 确认初始化成功print(fACL 版本:{cann.__version__})print(f设备数量:{cann.get_device_count()})# 输出# ACL 版本: 5.1.RC3# 设备数量: 8图像分类ResNet50 端到端推理完整推理流程# 03_resnet50_inference.pyimportcannimportnumpyasnpfromPILimportImage# 1. 初始化cann.init()# 2. 加载模型OM 格式modelcann.model.load(resnet50.om)print(f模型加载成功输入shape:{model.get_input_shape()})# 输出模型加载成功输入shape: [1, 3, 224, 224]# 3. 图片预处理defpreprocess(image_path):ImageNet 标准预处理imgImage.open(image_path).convert(RGB)# 缩放到 256w,himg.size scale256/min(w,h)new_w,new_hint(w*scale),int(h*scale)imgimg.resize((new_w,new_h))# 中心裁剪到 224left(new_w-224)//2top(new_h-224)//2imgimg.crop((left,top,left224,top224))# 转 numpy 数组 (H, W, C) - (C, H, W)img_arraynp.array(img).astype(np.float32)/255.0# ImageNet 标准化meannp.array([0.485,0.456,0.406])stdnp.array([0.229,0.224,0.225])img_array(img_array-mean)/std# (H, W, C) - (C, H, W) - (N, C, H, W)img_arrayimg_array.transpose(2,0,1)img_arrayimg_array[np.newaxis,:,:,:]returnimg_array.astype(np.float32)# 4. 执行推理imagepreprocess(test_image.jpg)input_tensorcann.Tensor.from_numpy(image)output_tensormodel.execute(input_tensor)# 5. 后处理outputoutput_tensor.to_numpy()pred_classint(np.argmax(output))print(f预测类别:{pred_class})# 6. 清理资源model.release()cann.shutdown()批量推理# 04_batch_inference.pyimportcannimportglob# 初始化cann.init()modelcann.model.load(resnet50.om)# 批量处理图片image_pathsglob.glob(test_images/*.jpg)batch_size8results[]foriinrange(0,len(image_paths),batch_size):batch_pathsimage_paths[i:ibatch_size]batch_images[]forpathinbatch_paths:imgpreprocess(path)batch_images.append(img)# 拼接 batchbatch_tensorcann.Tensor.from_numpy(np.concatenate(batch_images,axis0))# 推理outputsmodel.execute(batch_tensor)outputs_npoutputs.to_numpy()# 记录结果forj,outinenumerate(outputs_np):predint(np.argmax(out))results.append((batch_paths[j],pred))print(f{batch_paths[j]}- 类别{pred})print(f批量推理完成:{len(results)}张图片)# 清理model.release()cann.shutdown()性能调优batch size 和并行数配置调整 batch size# 05_batch_size_tuning.pyimportcannimporttimeimportnumpyasnp cann.init()modelcann.model.load(resnet50.om)# 不同的 batch size 测试batch_sizes[1,2,4,8,16,32,64]forbsinbatch_sizes:# 准备输入dummynp.random.randn(bs,3,224,224).astype(np.float32)input_tensorcann.Tensor.from_numpy(dummy)# Warmupfor_inrange(10):_model.execute(input_tensor)# 正式测试n_iters100starttime.time()for_inrange(n_iters):_model.execute(input_tensor)elapsedtime.time()-start avg_latency(elapsed/n_iters)*1000# msthroughputbs/(elapsed/n_iters)# FPSprint(fBatch{bs:3d}Latency{avg_latency:6.2f}ms Throughput{throughput:7.1f}FPS)# 输出示例# Batch 1 Latency 1.23ms Throughput 813.0 FPS# Batch 8 Latency 4.56ms Throughput 1754.4 FPS# Batch 16 Latency 8.12ms Throughput 1970.4 FPS# Batch 32 Latency 14.23ms Throughput 2248.2 FPS# Batch 64 Latency 26.45ms Throughput 2420.8 FPSmodel.release()cann.shutdown()batch size 选型建议场景推荐 batch size原因延迟敏感在线推理1~4快速响应吞吐敏感批处理16~32吞吐最优超大模型显存受限1~2显存限制延迟不敏感离线处理64吞吐最高NPU 并行数配置# 06_parallel_config.pyimportcann# 配置 NPU 并行执行configcann.Config()# 方案1单线程延迟敏感场景config.set(acl_thread,1)config.set(acl_device,0)# 方案2多线程吞吐敏感场景config.set(acl_thread,4)# 4 个线程并发执行config.set(acl_device,0)# 方案3多卡并行大规模推理fordevice_idinrange(8):modelcann.model.load(resnet50.om,device_iddevice_id)# 每个 device 独立加载一个模型实例# 验证配置print(f当前线程数:{cann.get_config(acl_thread)})print(f当前设备ID:{cann.get_config(acl_device)})性能分析工具# 07_profiling.pyimportcann# 开启 profilingprofilercann.Profiler()profiler.start()# 执行推理modelcann.model.load(resnet50.om)for_inrange(100):input_tensorcann.Tensor.from_numpy(np.random.randn(1,3,224,224).astype(np.float32))_model.execute(input_tensor)# 停止 profiling 并导出报告profiler.stop()profiler.export(resnet50_profile.json)# 查看报告print(profiler.summary())# 输出示例# Operator Calls Avg(ms) Total(ms)# Conv2d 100 0.523 52.3# BatchNorm2d 100 0.089 8.9# ReLU 100 0.042 4.2# MatMul 50 0.312 15.6# Softmax 100 0.067 6.7踩坑记录Atlas 服务器上的环境变量问题常见错误汇总错误原因解决方式ACL 初始化失败驱动版本不匹配升级/降级驱动到匹配版本模型加载失败OM 文件路径问题使用绝对路径推理结果全零输入数据未正确传输到 NPU检查 Tensor.from_numpy()batch size 受限显存不足减小 batch 或优化模型多线程卡死ACL 线程数配置错误设为 1 或偶数环境变量配置# 07_env_config.sh#!/bin/bash# 必须设置的环境变量exportASCEND_DEVICE_ID0exportASCEND_VISIBLE_DEVICES0,1,2,3,4,5,6,7# CANN 安装路径exportASCEND_TOOLKIT_HOME/usr/local/Ascend/ascend-toolkitexportLD_LIBRARY_PATH$ASCEND_TOOLKIT_HOME/runtime/lib64:$LD_LIBRARY_PATHexportPATH$ASCEND_TOOLKIT_HOME/bin:$PATH# ACL 配置可选exportASCEND_GLOBAL_LOG_LEVEL3exportASCEND_GLOBAL_DUMP_PATH./dump# 运行python 03_resnet50_inference.py# 查看 NPU 状态npu-smi info资源泄漏排查# 08_resource_leak_check.pyimportcann# 每次运行后检查资源defcheck_resources():print(f已分配 Tensor 数:{cann.Tensor.get_allocated_count()})print(f已加载模型数:{cann.model.get_loaded_count()})print(f显存使用:{cann.mem.get_allocated()/1024**3:.2f}GB)print(f显存峰值:{cann.mem.get_peak()/1024**3:.2f}GB)# 在循环中使用确保资源释放forepochinrange(1000):modelcann.model.load(resnet50.om)_model.execute(input_tensor)# 释放模型资源model.release()# 每 100 次检查一次ifepoch%1000:check_resources()# 如果发现资源持续增长说明有泄漏进阶示例目标检测模型在图像分类基础上目标检测需要更复杂的预处理和后处理# 09_object_detection.pyimportcannimportnumpyasnpfromPILimportImage cann.init()modelcann.model.load(yolov5s.om)defpreprocess_detection(image_path):YOLOv5 预处理640x640 方形输入imgImage.open(image_path).convert(RGB)w,himg.size# 计算缩放比例scale640/max(w,h)new_w,new_hint(w*scale),int(h*scale)img_resizedimg.resize((new_w,new_h),Image.BILINEAR)# 填充到 640x640灰色填充img_paddedImage.new(RGB,(640,640),(114,114,114))img_padded.paste(img_resized,(0,0))# 归一化img_arraynp.array(img_padded).astype(np.float32)/255.0img_arrayimg_array.transpose(2,0,1)returnimg_array.astype(np.float32),scaledefpostprocess_detect(outputs,scale,orig_size,conf_thresh0.5):NMS 后处理# 解析输出[batch, 5num_classes, num_boxes]predictionsoutputs[0]# shape: (1, 85, 8400)boxes[]forpredinpredictions.transpose(2,1):# (8400, 85)obj_confpred[4]class_scorespred[5:]*obj_conf class_idnp.argmax(class_scores)class_scoreclass_scores[class_id]ifclass_scoreconf_thresh:x,y,w,hpred[0:4]boxes.append([x,y,w,h,class_id,class_score])# NMSboxesnp.array(boxes)iflen(boxes)0:return[]# 按置信度排序boxesboxes[boxes[:,5].argsort()[::-1]]# NMSkeep[]whilelen(boxes)0:keep.append(boxes[0])iflen(boxes)1:break# 计算 IoUious[compute_iou(boxes[0],b)forbinboxes[1:]]boxesboxes[1:][np.array(ious)0.45]returnkeep# 完整流程image,scalepreprocess_detection(test.jpg)input_tensorcann.Tensor.from_numpy(image[np.newaxis])outputsmodel.execute(input_tensor)orig_w,orig_hImage.open(test.jpg).size detectionspostprocess_detect(outputs,scale,(orig_w,orig_h))print(f检测到{len(detections)}个目标)fordetindetections:x,y,w,h,class_id,scoredetprint(f 类别{int(class_id)}: 置信度{score:.2f}, 位置 ({x:.0f},{y:.0f},{w:.0f},{h:.0f}))总结cann-samples 是学习 CANN 最快的路径每个示例都跑一遍上手速度翻倍。阶段内容建议时间HelloWorld理解 ACL 初始化和基本 tensor 操作30 分钟ResNet50走通从图片到预测的完整流程1 小时性能调优batch size、并行数、profiling2 小时踩坑记录环境变量、资源泄漏、错误排查持续进阶路线图基础 (1-2天) - HelloWorld ResNet50 推理 - 熟悉 ACL API 和调试方法 进阶 (3-5天) - 目标检测、语义分割等 CV 任务 - 多卡并行推理 - Profiling 性能分析 生产 (1周) - 模型量化FP16/INT8 - 流水线优化 - 自定义算子开发仓库地址https://atomgit.com/cann/cann-samples