Atlas 300V 24G实战:从零部署YOLO目标检测全指南
发布时间:2026/9/20 9:51:45来源:尧图网络
1. Atlas 300V 24G到底是什么为什么要拿它跑YOLO1.1 一张卡的本质它不是GPU但也不是“孬货”先回答很多人在搜的问题atlas 300v 24g 是运算加速卡吗是而且是一张专门为AI推理设计的运算加速卡。它核心芯片是昇腾310P系列板载24GB显存整体设计目标就是“高吞吐、低功耗、多路视频流并发推理”。我第一次拿到这张卡的时候第一反应也是拿它和手里的RTX 3080比。但实际用过之后得说清楚Atlas 300V 24G不是用来替代GPU训练卡的它的主战场是“训练完之后模型落地部署”那一侧。你如果打算在上面跑PyTorch训练那趁早打消这个念头但如果你要在一个机房服务器里同时处理十几路甚至几十路摄像头画面、实时跑YOLO目标检测那它的性价比和稳定性会给你不小的惊喜。它和GPU最大的差异在于计算架构。GPU核心数量多通用计算能力强适合训练这种“乱拳打死老师傅”的并行计算而昇腾的处理单元是AI Core专门为矩阵运算和卷积这种深度学习最常见操作做了硬化。YOLO模型在GPU上推理时很多算力其实花在了内存搬运和通用指令上而在Atlas上整个计算流程是为推理链路的固定图结构深度优化的同样的模型跑起来功耗往往只有GPU的一半甚至更低单路功耗大约在70W到90W之间。还有一个非常实际的点24GB显存。YOLOv5s这种轻量模型INT8量化之后模型大小也就十几MB到几十MB模型本身用不了几个G。24G显存的真正价值在于你可以同时加载多个模型或者用Batch批量方式同时处理几十路视频帧而不用担心显存溢出。对做视频结构化、智慧园区、明厨亮灶这类项目的团队来说这24G就是多路并发的底气。1.2 什么样的项目场景适合拿它部署YOLO我从实际项目经验出发列几个典型场景你看看自己属不属于这类需求视频监控与安防比如园区周界入侵检测、工地未戴安全帽识别、工厂人员违规行为检测。这类项目通常有十几个甚至几十个摄像头每路画面都要实时跑YOLO检测对单卡吞吐量要求很高Atlas 300V的24G显存和内置的Video Decoder硬件解码刚好能吃下这种压力。边缘服务器/盒子不需要在终端设备上跑模型但在靠近数据源头的机房做本地推理减少视频流转发的带宽压力。Atlas 300V单卡半高半长的设计能轻松塞进2U或者4U的服务器里。零售与传统行业货架商品识别、生产流水线瑕疵检测、农产品分拣。这类项目的共同特征是模型不大、类别不多、对单帧延迟不极端敏感几百毫秒内都能接受但对长时间稳定运行要求极高Atlas系列在稳定性上口碑不错这也让它成为这类场景里的常见选项。如果你手里已经有了这个卡或者正在评估采购这篇文章就是给你准备的。下面我会把从零部署YOLO到Atlas 300V 24G上跑的完整链路拆开讲清楚包含我踩过的坑和验证过的参数。2. 部署前的准备工作环境搭建决定后面70%的成败2.1 搞清楚硬件形态再下手Atlas 300V 24G市面上常见的有两个形态一个是标准的PCIe加速卡插在服务器或者工作站主板上这个适合自己已经有机器的团队另一个是Atlas 800推理服务器型号300V里预装的整机形态厂商会预装好驱动和固件省去很多麻烦。如果你是拿到一张裸卡要先确认几个硬件层面的东西服务器主板要有PCIe 3.0 x16或者x8的插槽最好是最靠近CPU的那条减少跨NUMA节点的拷贝开销。主板BIOS里要把Above 4G Decoding开启否则PCIe设备DMA访问超过4GB地址空间时会报错或者直接认不到卡。电源功率300V 24G单卡满载功耗大约在70W到90W之间根据负载和版本有差异这个功耗对如今动辄350W的GPU来说相当友好了450W以上的服务器电源基本没有压力。这些点我第一遍全部踩过。有一次我拿一张新卡插在测试机上系统死活认不到设备查了半天才发现是Above 4G Decoding没开BIOS一改立刻正常。2.2 驱动、固件和CANN工具链的正确安装顺序官方文档其实写得挺全但实际安装有一个忌讳顺序错了就会出现各种诡异问题。我的建议顺序是先升级固件npu-smi 能看到固件版本后再操作再装驱动driver最后装CANN toolkit为什么要按这个顺序因为固件是最底层的驱动需要适配固件版本而CANN运行时会去校验驱动版本。顺序反了CANN上层的模型转换工具和运行库可能会因为驱动过旧而报错最典型的就是ATC转换时报“RUNTIME_INVALID_DRIVER_VERSION”。驱动包直接去昇腾社区下载对应Ubuntu/CentOS的版本安装方式很简单chmod x Ascend-hdk-*.run ./Ascend-hdk-*.run --full装完之后用这个命令确认设备是否正常识别npu-smi info正常的话会看到设备编号、芯片型号、显存大小、温度等信息第一行会显示类似300V的信息。如果这里都看不到卡那别往下走了先回头查硬件插接和BIOS设置。CANN toolkit是昇腾的软件栈总称里面包含了模型转换工具、推理运行时、算子库等等。对跑YOLO来说我建议直接装CANN 6.3.x或者7.0版本对应配套的驱动和固件版本在下载页面都会给一个“配套版本”表格严格对照就行。./Ascend-cann-toolkit_7.0.0_linux-x86_64.run --full装完之后设置环境变量source /usr/local/Ascend/ascend-toolkit/set_env.sh2.3 三个新手最容易忽略的环境细节第一个是非root用户的权限和依赖库路径。很多团队用普通用户跑推理服务但CANN装在了root路径下。最省事的方式是在~/.bashrc里加环境变量并确保用户对/usr/local/Ascend有读和执行权限同时用户组加到HwHiAiUser安装过程中会自动创建这个组。第二个是Python版本兼容性。CANN的pyACL模块对Python版本有明确要求实测在Python 3.8到3.10下都比较稳过高或者过低都会出现导入_ascend_acl模块失败的问题。第三个是NPU显存和系统内存的动态分配。pyACL默认会申请一块设备显存池如果程序退出时没有显式释放下次运行会提示“out of memory”。所以强依赖context生命周期管理后面章节我会给一套稳妥的写法。3. 核心链路把YOLO模型“翻译”成Atlas能吃的OM格式3.1 模型从哪里来PyTorch导出ONNX的细节Atlas不直接跑PyTorch的pt权重它吃的是自家定义的OMOffline Model格式。所以整个部署链路就是PyTorch/YOLOv5/YOLOv8 → ONNX → OM。我以YOLOv5和YOLOv8为例说明这两个是实际项目里最常被问到的。YOLOv5导出ONNX时默认的export.py已经能导出动态或者固定shape的ONNX但有几个参数必须关注python export.py --weights yolov5s.pt --include onnx --opset 12 --dynamic--opset 12ATC对ONNX算子版本支持得比较全opset 12和13都没问题。--dynamic如果要支持不同分辨率输入必须导出动态ONNX如果固定尺寸建议导出固定shape性能会更好。导出后务必用onnxsim做一次图精简去掉冗余算子ATC转换时会少很多麻烦。YOLOv8导出命令类似yolo export modelyolov8s.pt formatonnx opset12 dynamicTrue注意一点YOLOv8导出的ONNX默认包含了最终的检测输出层1x84x8400这种结构的输出那在写后处理时就要对应去解析。而YOLOv5的ONNX导出会带一个检测头有些自定义版本会在导出时把检测头去掉只保留主干特征图输出接着ATC转换时手动拼接解码层。这两条路线我走下来最省事的是直接用官方导出保留输出层这样后处理逻辑在Host侧Python里处理调试起来也直观。3.2 ATC模型转换一行命令背后的“为什么”ATC工具是CANN里负责把ONNX/Caffe/TensorFlow模型转成OM的命令行工具。转换YOLOv5最简命令如下atc --modelyolov5s.onnx \ --outputyolov5s_bs1 \ --input-shapeimages:1,3,640,640 \ --framework5 \ --soc_versionAscend310P3参数解释--framework55代表ONNX。--soc_versionAscend310P3根据实际芯片型号指定300V 24G对应的就是310P系列。用npu-smi info可以查看芯片型号不确定是P几的时候去CANN安装目录下找auto_tune脚本帮忙检测或者直接试A310P3/A310P1/A310P2跟CANN版本有关。选不对会在加载模型时报“model compile failed”。--input-shapeONNX里输入名是images必须和导出时的名字一致否则报输入名不匹配。--output输出路径和名字出来的文件是yolov5s_bs1.om。如果要支持分辨率可变的输入可以改成atc --modelyolov5s.onnx \ --outputyolov5s_dynamic \ --input-shapeimages:1,3,-1,-1 \ --dynamic-image-size640,640;736,736;832,832 \ --framework5 \ --soc_versionAscend310P3--dynamic-image-size后面跟的是多个可切换的分辨率组合分号拆分。这样在运行推理时通过设置输入张量的shape可以在不重新转换模型的前提下适配不同分辨率的画面。代价是性能比固定shape略低因为NPU需要根据实际shape重新规划内存布局。3.3 图像预处理被“吸进”模型里性能直接翻倍这是Atlas部署YOLO最关键的性能优化手段之一AIPPAI Preprocessing配置。AIPP的意义在于标准YOLO推理流程里图像要先做resize、归一化除以255、减均值除方差这些操作如果在Host侧用Python或者OpenCV做每一帧都会占CPU时间而且大量数据在内存和显存之间来回拷贝。而Atlas的AIPP指令可以把resize、归一化、像素格式转换这些操作直接放进硬件流水线里在数据从H2D搬运后、进入AI Core计算之前自动完成。AIPP配置是一个独立的配置文件.cfg内容大致如下aipp_op { aipp_mode: static input_format: RGB888_U8 src_image_size_w: 640 src_image_size_h: 640 crop: false resize: true resize_w: 640 resize_h: 640 csc_switch: true rbuv_swap_switch: true min_chn_0: 0 min_chn_1: 0 min_chn_2: 0 var_reci_chn_0: 0.003921569 var_reci_chn_1: 0.003921569 var_reci_chn_2: 0.003921569 }参数说明input_format模型输入端的原始图像格式通常摄像头输出BGR这里就填BGR888_U8。resize_w/resize_h把原图直接缩放到模型输入尺寸。var_reci_chn_*归一化的倒数即1/255≈0.003921569。csc_switch与rbuv_swap_switch颜色空间转换和通道交换开关根据实际格式配置用错会出现“红蓝通道互换”这类问题。配置好之后ATC转换时加上一句atc --modelyolov5s.onnx \ --outputyolov5s_aipp \ --input-shapeimages:1,3,640,640 \ --framework5 \ --soc_versionAscend310P3 \ --insert_op_confaipp.cfg转换完之后你再看原来的ONNX里面那些归一化操作已经不再需要做了推理代码里只需要把原始图像数据交给模型就行。这一步如果调好了性能提升非常可观我实测从纯Python做预处理的版本改到AIPP版本后单路推理帧率翻倍都不止。不过AIPP有个小坑模型转换时如果用了静态AIPP那输入shape必须是固定尺寸因为AIPP硬件单元需要明确知道源图和输出图的分辨率。动态shape下面要小心使用。4. 基于pyACL的推理代码从初始化到画框的完整流程4.1 初始化、加载模型、申请内存的正确姿势CANN提供了两种运行方式一种是C接口性能最优另一种是Python的pyACL开发速度快得多。对于YOLO这种推理密度不极端的场景单路720P/1080P帧率25以下Python完全够用而且调试起来非常愉快。先给一个标准的骨架代码这个框架是我经过多次重构之后稳定下来的版本可以直接在项目里当模板用import acl import numpy as np import cv2 # 常量定义 ACL_MEM_MALLOC_HUGE_FIRST 0 ACL_MEMCPY_DEVICE_TO_DEVICE 3 ACL_MEMCPY_HOST_TO_DEVICE 1 ACL_MEMCPY_DEVICE_TO_HOST 2 class AtlasYOLO: def __init__(self, model_path, device_id0): self.device_id device_id self.model_path model_path self._init_resource() self._load_model() def _init_resource(self): ret acl.init() assert ret 0, fACL init failed: {ret} ret acl.rt.set_device(self.device_id) assert ret 0, fset device failed: {ret} self.context acl.rt.create_context(self.device_id) assert self.context ! 0, create context failed # 显式申请运行内存池避免多次申请释放导致碎片 self.mem_pool acl.rt.create_mem_pool(256 * 1024 * 1024) def _load_model(self): self.model_id acl.mdl.load_from_file(self.model_path) assert self.model_id 0, model load failed self.desc acl.mdl.create_desc() acl.mdl.get_desc(self.desc, self.model_id) # 解析模型输入输出信息 self.input_num acl.mdl.get_num_inputs(self.desc) self.output_num acl.mdl.get_num_outputs(self.desc) self.input_size [] self.output_size [] for i in range(self.input_num): size acl.mdl.get_input_size_by_index(self.desc, i) self.input_size.append(size) for i in range(self.output_num): size acl.mdl.get_output_size_by_index(self.desc, i) self.output_size.append(size) # 申请device侧输入输出内存 self.input_buf acl.rt.malloc(self.input_size[0], 32) self.output_buf [] for size in self.output_size: buf acl.rt.malloc(size, 32) self.output_buf.append(buf)初始化这块有几个细节值得反复强调acl.init()必须只在全局调用一次。如果在多个线程或者多次实例化里重复调用可能出现未知错误。项目里建议把这个方法做成单例或者放在模块加载时执行。acl.rt.set_device绑定NPU设备ID。机器上有多张300V时可以通过npu-smi info查到Device ID多卡推理就为每个线程设置不同的device_id。输入输出内存都从设备侧申请acl.rt.malloc并且对齐要求是32字节。对齐不够在某些CANN版本上会直接报“invalid data buffer”。4.2 推理主循环数据搬运、执行、取回结果的完整逻辑推理一帧的完整流程是读取图像Host→ 如果是AIPP模型则原图直接送入如果不是则先做归一化Host→ 拷贝到Device输入内存 → 执行模型 → 从Device输出内存拷回结果 → 后处理得到检测框。标准代码如下def infer(self, image_np): image_np: 形状为 (H, W, 3) 的BGR/ RGB uint8图像 # 1. 如果使用AIPP这里直接把图像连续内存传进去 img np.ascontiguousarray(image_np) img_shape img.shape # 2. 将输入数据拷贝到device内存 ret acl.rt.memcpy(self.input_buf, self.input_size[0], img.tobytes(), img.nbytes, ACL_MEMCPY_HOST_TO_DEVICE) assert ret 0, fmemcpy H2D failed: {ret} # 3. 执行推理 ret acl.mdl.execute(self.model_id, [self.input_buf], self.output_buf) assert ret 0, fmodel execute failed: {ret} # 4. 取回所有输出 outputs [] for i in range(self.output_num): out_data acl.rt.memcpy_d2h(self.output_size[i], self.output_buf[i]) outputs.append(np.frombuffer(out_data, dtypenp.float32)) return outputs如果是非AIPP模型预处理得自己在Python里做def preprocess(image_np, target_size(640, 640)): h, w image_np.shape[:2] # 保序resize scale min(target_size[0] / h, target_size[1] / w) new_w, new_h int(w * scale), int(h * scale) resized cv2.resize(image_np, (new_w, new_h)) # 填充到目标尺寸灰色填充实际填充值影响不大因为输入是归一化后的 canvas np.full((target_size[0], target_size[1], 3), 114, dtypenp.uint8) canvas[:new_h, :new_w, :] resized # CHW 归一化 chw np.transpose(canvas, (2, 0, 1)).astype(np.float32) / 255.0 return np.expand_dims(chw, axis0)很多人在这一步把记忆里的RGB通道顺序搞混。YOLOv5训练时用的是RGB还是BGR取决于你训练代码里是怎么读图的。一般来说OpenCV读进来是BGRYOLOv5官方仓库内部用的是RGB训练时做了转换。如果你直接用训练好的pt导出ONNX并接着做推理后处理画框颜色不对没关系但检测精度明显下降时十有八九就是通道顺序反了。4.3 后处理把模型的输出解析成检测框以YOLOv8的ONNX输出为例输出shape是[1, 84, 8400]含义是84 4框坐标 80COCO类别数8400是不同尺度特征图上的anchor点总数。解析代码def postprocess_yolov8(output, conf_thresh0.25, iou_thresh0.45): output: shape (1, 84, 8400), float32 preds output[0] # (84, 8400) preds preds.transpose(1, 0) # (8400, 84) boxes preds[:, :4] # cx, cy, w, h class_probs preds[:, 4:] # 转成 x1y1x2y2 x1 boxes[:, 0] - boxes[:, 2] / 2 y1 boxes[:, 1] - boxes[:, 3] / 2 x2 boxes[:, 0] boxes[:, 2] / 2 y2 boxes[:, 1] boxes[:, 3] / 2 scores class_probs.max(axis1) labels class_probs.argmax(axis1) keep scores conf_thresh # 过滤低置信度框 final_boxes np.stack([x1, y1, x2, y2], axis1)[keep] final_scores scores[keep] final_labels labels[keep] # NMS keep_idx nms(final_boxes, final_scores, iou_thresh) return final_boxes[keep_idx], final_scores[keep_idx], final_labels[keep_idx]NMS可以直接用OpenCV的cv2.dnn.NMSBoxes实测比手写NMS快而且稳def nms(boxes, scores, iou_thresh): indices cv2.dnn.NMSBoxes( boxes.tolist(), scores.tolist(), score_threshold0.0, nms_thresholdiou_thresh ) if len(indices) 0: return [] return indices.flatten().tolist()这里有一个容易踩的坑Atlas的推理输出可能是fp16也可能因为模型转换选项变成fp32。我建议在ATC转换时明确--output_typeFP32省得后处理里还要写判断分支。4.4 性能调优多路视频流并发才是300V的正确打开方式单帧单张跑完只算“能跑”但要发挥Atlas 300V 24G这张卡的实力必须做并发。我推荐两种方案方案一Python的多线程 每线程一个context。pyACL在执行acl.mdl.execute时会阻塞当前线程直到推理完成所以想同时跑多路视频就用ThreadPoolExecutor每路一个线程每个线程里自己创建context并加载同一个OM模型。from concurrent.futures import ThreadPoolExecutor def process_rtsp(rtsp_url, model_path): yolov8 AtlasYOLO(model_path) cap cv2.VideoCapture(rtsp_url) while True: ret, frame cap.read() if not ret: break outputs yolov8.infer(frame) boxes, scores, labels postprocess_yolov8(outputs[0]) # 画框、上报、存储逻辑... executor ThreadPoolExecutor(max_workers8) # 8路并发 for url in rtsp_urls: executor.submit(process_rtsp, url, yolov8s_aipp.om)方案二Batch推理。多路视频帧拼成一个batch一次执行模型。这个方案最省设备侧内存分配和调度开销但需要在线程间做帧同步和结果拆分复杂度高一些适合那种推理延迟敏感且希望最大化吞吐的项目。代码上就是把多帧数据拼接成[N, 3, 640, 640]的输入然后一次acl.mdl.execute拿到输出后按batch维度切分。从实际数据来看8路1080P视频同时推YOLOv8s8线程并发的方式单张300V的帧率大概能稳定在单路25FPS左右整体吞吐不比一张中端GPU差功耗还低不少。5. 常见问题与排查技巧实录5.1 一颗“玄学”错误快速定位表我整理了这段时间在Atlas 300V 24G上跑YOLO遇到的高频问题做成一个速查表方便你遇到问题时直接对照错误/现象可能原因解决方案acl.mdl.load_from_file返回0模型文件路径不对或者OM格式损坏检查路径重新用ATC转换确认--soc_version匹配实际芯片模型加载成功推理结果全是0输入数据没有正确写入设备内存或者通道顺序/归一化不对打印Host侧输入数据的前几个值对比预处理输出是否符合模型预期检查是否用了AIPP但没按AIPP要求给原始图像acl.rt.memcpy报ACL_ERROR_INVALID_PARAM输入数据内存不连续用np.ascontiguousarray确保图像数据连续推理速度很慢只有几FPS没开AIPP每次推理都在做低效预处理或者没有用多线程并发参考3.3配置AIPP参考4.4做多线程并发报错device 0 is busy或者申请不到显存上一轮程序没清理NPU资源泄漏检查代码里acl.rt.free和acl.mdl.unload是否配套调用必要时重启设备清除资源池转换模型时报E40026等算子不支持错误导出ONNX时带了某些ATC不支持的算子用onnxsim精简模型换用opset 12如果还不行需要手动替换或拆解那个不支持的算子层多线程时程序崩溃Python直接core dump多个线程共享了同一个ACL context确保每个线程独立创建context并在线程内调用acl.rt.set_context5.2 “显卡长啸”显存泄漏与内存踩踏的可怕后果有一段时间我的服务跑一个晚上就会挂掉第二天早上看日志发现进程还在但推理异常慢最后整机卡死。查下来原因是显存泄漏每次infer之后没有释放acl.rt.memcpy_d2h返回的buffer。这里明确一个概念acl.rt.memcpy_d2h返回的是一个Python对象底层是一块Host侧内存如果你只是用np.frombuffer包装它这个buffer的释放取决于Python的GC机制而Python不是每次都触发GC导致设备侧的输出内存一直被占用最终NPU显存耗尽。稳妥的写法是每次都主动释放out_data acl.rt.memcpy_d2h(self.output_size[i], self.output_buf[i]) outputs.append(np.frombuffer(out_data, dtypenp.float32).copy()) del out_data.copy()这一步看似多余其实是把数据从临时的Host内存里复制到独立管理的numpy数组避免临时buffer生命周期不受控制导致的泄漏。我加上这个之后服务连续跑了三周都没再出问题。另一个容易出的坑是内存踩踏输入图像的shape和模型固定输入尺寸不一致时img.tobytes()的长度大于或者小于self.input_size[0]acl.rt.memcpy不会好心地告诉你“大小不匹配”而是直接越界写。越界通常不会立刻报错但会随机污染别的内存区域导致推理结果偶尔出现莫名其妙的框。排查方法是每次拷贝之前检查长度assert img.nbytes self.input_size[0], \ finput size mismatch: {img.nbytes} vs {self.input_size[0]}5.3 AIPP后图像“变绿变紫”目标检测出来全乱套百分之八十的AIPP问题都出在通道顺序和颜色空间配置上。YOLOv5官方在训练时使用的是RGB而摄像头和OpenCV主要生成BGR如果模型本身是RGB训练的AIPP里就得把input_format设为RGB888_U8同时记得开rbuv_swap_switch做BGR到RGB的交换让送到AI Core的数据和训练时看到的数据一致。如果你在调试时发现检测框的位置基本都是乱的但置信度还好优先检查图像是不是发生了resize比例不对的问题。AIPP的src_image_size_w/h是源图分辨率resize_w/h是目标分辨率两者必须都是模型的实际输入尺寸。如果源图分辨率设置得和实际图像不一致AIPP硬件会按错误比例缩放画面被拉伸了框自然就对不上。5.4 多路并发时如何避免CPU成为瓶颈不要低估视频流的解码开销。Atlas 300V板载硬件视频解码能力如果用CPU软解做RTSP拉流8路1080P就能把一半CPU吃满推理没卡CPU先爆了。解决思路有两条使用昇腾的DVPPDigital Vision Pre-Processing模块做硬件解码和图像缩放把视频帧的H264/H265解码直接丢给NPU硬件单元。CANN提供了acldvppC接口和pyACL里的VPC能力代码复杂度会比OpenCV拉流高一些但CPU占用能降到很低。适合把Atlas当成一个独立的视频推理一体机来用。保守方案如果项目周期紧先用多核CPU软解但是把解码和推理放到不同的线程池控制解码线程数量在物理核数以内避免上下文切换开销。我实际测过一个场景用8线程做软解 8线程推理在24核心的服务器上CPU占用率大约60%在16核心的机器上接近90%而用DVPP做硬解后CPU占用率降到20%以内推理帧率还小幅度提升了。如果这是长期运行的线上服务非常值得把解码也迁移到NPU侧。6. 部署之后的一些个人心得项目从零开始到稳定运行我花了两周其中大概三天是环境搭建四天是卡在模型转换和精度对齐上剩下的时间在调并发和排查内存问题。走过这一趟之后我个人的体会是Atlas 300V 24G是一张很适合做YOLO推理落地的卡但不是那种“开箱即用、无脑跑飞”的硬件它需要你花时间去理解CANN这套工具链的思维方式。和GPU生态比起来昇腾的软件栈文档确实有进步空间很多细节藏在社区论坛的犄角旮旯里。但只要你把环境版本对好、把模型转换流程走通、把AIPP和并发用起来它的稳定性和性价比会让你觉得前期这些投入值得。最后再分享一个小技巧版本控制很重要。CANN、驱动、固件这三个东西的版本必须配套我强烈建议在项目开始就把这三个版本号写进README最好再附上对应版本的下载链接。隔几个月回头升级环境或者换机器时能省掉大量“为什么之前能跑现在不能跑”的排查时间。
网站建设高端定制企业官网