ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Atlas 300V 24G推理加速卡部署YOLO的实战总结

Atlas 300V 24G推理加速卡部署YOLO的实战总结 做AI推理部署的朋友最近应该没少被“atlas”这个词刷屏。尤其是“atlas部署yolo”和“atlas 300v 24g 是运算加速卡吗”这两个问题几乎隔三差五就在技术群里冒出来。我一开始也以为是什么新框架后来才反应过来大家问的是华为昇腾的Atlas系列推理卡。恰好我手头正好有一台Atlas 300V 24G做过几个YOLO模型的适配和部署踩了不少坑也沉淀了一些心得。这篇就把这事从头捋一遍给准备入手的你做个参考。先说结论省得你搜半天Atlas 300V 24G确实是运算加速卡但它是AI推理加速卡NPU不是用来渲染画面的显卡也不像GPU那样什么计算任务都能撒手跑。它的定位非常精准——面向数据中心和边缘场景专门高效执行神经网络推理任务。正因为它天生为推理而生当你想把YOLO这类目标检测模型从实验环境搬到生产环境、从GPU迁移到国产化平台时它就成了一个很现实的选型。接下来我把硬件本质、部署流程、调优经验和踩坑记录都摊开讲。1. Atlas 300V 24G 到底是什么——先回答“是不是运算加速卡”这个高频问题1.1 名字背后的硬件定位它不是 GPU却干着 GPU 的活很多第一次接触Atlas的人第一反应都是拿它跟GPU比。这很正常因为从使用方式上看它也是插在PCIe插槽上、需要装驱动、有独立显存、能跑深度学习模型的一个加速设备。但它和GPU有本质区别GPU是图形处理器设计之初是为了大规模并行浮点计算后来被借来跑深度学习属于“既当爹又当妈”而Atlas 300V用的是昇腾310P芯片是专用集成电路ASIC思路设计的AI处理器整个芯片架构就是围绕神经网络算子来优化的指令集、缓存、带宽分配全部为了推理服务。所以你问“它是不是运算加速卡”答案是肯定的——它是一张如假包换的AI运算加速卡。但如果你问“它能不能当GPU用”那我建议你先分清楚业务。跑训练、跑CUDA生态的代码别指望它生态不兼容跑训练好的模型做推理那它的性价比和能效比很能打。我实测下来单张Atlas 300V Pro 24G在YOLOv5s模型上FP16精度的推理吞吐跟同档次的中端专业推理卡相比完全不落下风而且功耗低一大截。再说个容易混淆的点Atlas 300V是一整个产品线下面有300V、300V Pro等不同型号24G指的是显存容量不是型号。很多人问“300V 24G是不是运算加速卡”其实是在问昇腾310P这颗芯片到底行不行。我可以负责任地说310P的推理能力在昇腾产品线里属于中坚力量虽然不能跟最新的Atlas 800系列旗舰卡比绝对算力但在72W功耗的约束下能给出这样的吞吐已经很有竞争力了。它特别适合那种“机房空间有限、供电有限、但需要并发处理多路视频流”的场景。1.2 24G 显存和算力规格到底应该怎么看既然聊到这里我把Atlas 300V Pro 24G的关键规格摊开讲一下帮你看懂厂家宣传页没写明白的话。这张卡的核心规格大致如下昇腾310P芯片INT8整数算力在百TOPS级别FP16浮点算力大概在几十TFLOPS级别毕竟是推理卡INT8才是它的主场。显存24GB接口是PCIe Gen4 x16整卡最大功耗72W左右不需要外接供电插上就能用。对比一下主流的中端GPU推理卡你会发现它的功耗只有对方的一半到三分之一但INT8推理吞吐可以做到同一量级这就是ASIC架构的威力——牺牲了通用性换来了极致的能效比。24G显存是个什么概念拿YOLO模型举例YOLOv5s的权重文件才十几MB运行时的激活值和中间张量占用的显存也不大单路推理用不了多少显存YOLOv7、YOLOv8中大型模型或者你需要开大batch并发推理时显存需求会显著上涨。24G意味着你可以比较从容地跑较大的模型或者用一个较大的batch size把NPU喂饱。我个人实测Atlas 300V 24G在batch size 8的配置下跑YOLOv8x显存占用大概在10G左右余量还很充足。但是必须提醒一个关键点华为的昇腾工具链CANN和主流训练框架的生态不完全互通。你手头的PyTorch权重不能直接丢给它跑必须先做模型转换转成昇腾的专属离线模型格式OM。这一步是很多人“劝退”的地方也是我后面要重点讲的部分。另一个容易忽略的是Atlas 300V虽然有24G显存但它的显存带宽比同级别GPU要低所以对访存密集型的算子比如某些大尺寸feature map的操作会比较敏感这也是调优时要关注的方向。提示买卡之前先查清楚自己的业务是固定shape还是动态shape。昇腾NPU对固定shape的优化最狠动态shape要启动动态分档功能性能会有损耗后面我会讲怎么处理。2. 为什么 Atlas 部署 YOLO 成了刚需——推理场景的真实瓶颈2.1 YOLO 在 CPU/GPU 上部署的成本与性能矛盾YOLO系列在目标检测领域有多流行不用我多说从工业质检到安防监控到自动驾驶到处都是它的影子。但部署这事儿远没有训练那么潇洒。训练阶段你可以在实验室里用几张高端GPU慢慢跑到了生产环境要考虑的事情就变了成本、功耗、体积、稳定性、合规性。先看CPU推理。YOLOv5s模型在普通服务器CPU上跑单帧大概需要几百毫秒到一秒以上帧率上不去并发一高CPU直接打满。而且CPU通用计算跑神经网络纯属“高射炮打蚊子”硬件利用率低耗电却不低。再看GPU推理性能确实好但中高端GPU价格高、功耗大一张卡两三百瓦很常见还需要配套的散热和电源改造。如果你的项目是几十上百路视频流同时做检测这个电费和硬件成本会非常可观。这就引出了Atlas这类专用推理卡的定位单卡72W功耗INT8算力上百TOPS价格比同算力GPU低而且能满足国产化、自主可控这类硬性要求。性能上我实测YOLOv5s在Atlas 300V上的单卡吞吐能做到几十路1080p视频流实时检测CPU完全做不到这个能效比。说白了在“特定模型、固定流程、高并发推理”这个场景下专用NPU就是比通用GPU划算。2.2 Atlas 适合的典型部署场景具体到部署场景我梳理了一下Atlas 300V 24G在这些地方特别受欢迎第一个是视频监控与分析。比如园区安防、智慧工地、工厂质检摄像机拉了十几路甚至几十路视频流每一路都要做目标检测。这种场景对单帧延迟不敏感几百毫秒能接受但对吞吐要求极高要求支持多路并发这正是Atlas的强项。24G显存可以塞下较大的模型同时处理多路视频的预处理和推理任务。第二个是边缘机房和私有化交付。很多项目要求模型必须部署在客户的内网环境对设备体积、功耗、噪音都有明确限制。一张PCIe卡的形态非常友好插入标准服务器就能用也不需要额外的供电改造。客户如果对国产化有要求昇腾平台的选项在项目招标阶段就很有分量。第三个是模型迭代速度较慢、但推理量很大的业务。比如车牌识别、OCR检测、表计读数这类相对固定的模型一年可能才更新几次。这种业务最适合用离线模型OM部署——先把PyTorch模型转成OM然后部署到NPU上稳定跑很久。因为这些模型结构变化不大转换一次的成本可以被长期摊薄。2.3 迁移之前先想清楚你的模型适不适合 NPU不是所有模型都能顺滑地跑到Atlas上。我在迁移一个YOLO变体模型时遇到过算子不支持的问题最后排查发现是模型里用了自定义算子昇腾的ATC工具不认识。所以在你决定用Atlas之前先花点时间梳理一下模型结构用了哪些算子和操作算子种类越少、越标准迁移越顺利如果用了大量复杂的自定义算子或者模型结构频繁变动就要慎重了。除了算子兼容性还有精度问题。昇腾的混合精度机制和GPU不完全一样转成FP16或者INT8后个别层的精度表现可能有差异。我建议你迁移前准备一套完整的测试集跑完转换后做精度对比不要直接上线。另外模型的输入shape是否固定也很关键。YOLO系列模型的输入通常固定为640x640或者416x416这正好是昇腾NPU最喜欢的那种固定shape输入不需要额外的动态shape处理转换后性能表现很理想。3. 基于 Atlas 300V 24G 部署 YOLO 的完整流程3.1 环境准备驱动、CANN 和版本对照拿到Atlas 300V 24G之后第一步不是赶紧跑模型而是把环境配好。昇腾的软件栈分几个层次底层是驱动和固件HDK负责管理NPU硬件资源往上是CANNCompute Architecture for Neural Networks相当于“昇腾版的CUDA工具包”提供算子库、运行时和模型转换工具再往上才是你真正接触到的推理框架和Python接口。装驱动和CANN有个最容易被忽视的坑版本要严格匹配。华为对版本兼容性管理得非常严格驱动版本不匹配可能导致CANN工具包无法加载或者运行时报错。我的建议是直接去昇腾社区的“版本配套表”里查一下当前最新的一套组合然后用这个组合去装。比如你的服务器操作系统是Ubuntu 20.04那驱动和固件就下载对应版本的Ascend HDKCANN下载对应版本的Ascend Toolkit版本号对齐。安装流程大体如下先装驱动和固件包含NPU的内核驱动、用户态驱动和带外管理组件装完后用npu-smi命令能看到卡的信息证明驱动层已经识别到了NPU然后装CANN Toolkit装完后配好环境变量主要就是CANN的安装路径最后可以用python导入acl库验证工具链是否正常工作。这里有个小技巧npu-smi info命令一定要先跑通这是后面所有排障的基础。如果这里都看不到卡那后面的CANN配置全都白搭。注意CANN安装时会检查系统依赖包括Python版本、gcc版本等。建议用Ubuntu 20.04或22.04这类主流的LTS系统折腾的麻烦会少很多。CentOS 7.6也有支持版本但老内核容易踩坑没有特殊要求就不要选。3.2 模型转换从 PyTorch 权重到 OM 离线模型环境装好之后最核心的一步来了把PyTorch的YOLO权重转成昇腾的OM离线模型。为什么不能直接跑PyTorch因为NPU的指令集和编译器跟GPU完全不同PyTorch原生代码无法直接在NPU上执行。所以华为提供了一条工具链先把PyTorch模型导出为ONNX再用ATC工具把ONNX转换为OM格式。这个过程非常像在“编译”一个模型所以OM经常被称为离线模型。转换前先要把YOLO模型导出为ONNX。这一步要格外小心因为YOLO的官方仓库导出的ONNX往往包含一些自定义算子或后处理逻辑直接转OM会失败。我的经验是只导出模型的backbone和head部分不要导出NMS后处理后处理放到CPU上做。这一步能避免90%的转换报错。然后就是ATC转换命令。命令的核心参数包括--model指定输入ONNX文件--output指定输出OM文件--framework设为5表示ONNX--input_shape指定输入的shape比如“images:1,3,640,640”--soc_version指定芯片型号比如Ascend310P。如果是动态batch可以用--dynamic_batch_size后面跟用逗号分隔的候选batch值。一个经常让人犯迷糊的地方是图像预处理参数。PyTorch训练时对图像的预处理比如归一化到0-1、BGR转RGB、letterbox等比缩放填充不会包含在模型结构里你需要把这些操作显式地“告诉”ATC工具配置一个AIPP文件AI Preprocessing。如果AIPP配置和训练时不一致部署后模型的推理精度会明显下降甚至检测框跑飞。我把AIPP文件的常见配置贴一下{ aipp_op: { input_format: RGB888_U8, src_image_size_h: 640, src_image_size_w: 640, mean: [0, 0, 0], min: [0, 0, 0], var: [0.003921568627451, 0.003921568627451, 0.003921568627451], crop: true, load_start_pos_h: 0, load_start_pos_w: 0 } }这里min表示最小值var是1/255这样输入图像送入NPU后会自动完成归一化。但是有两点特别提醒YOLOv5官方仓库训练时用的是RGB格式且归一化到0-1而OpenCV读图默认是BGR格式所以你在AIPP里写RGB888_U8的同时还得在自己代码里把BGR转成RGB不然检测效果会很差。更稳妥的做法是把预处理全部放在自己代码里AIPP只做最基础的格式转换避免两层之间理解不一致。转换成功后会生成一个.om文件用npu-smi可以看到NPU负载用CANN自带的msame工具可以做一次离线推理验证确认输出shape和你的预期一致。到这一步模型工作就算完成了之后你的推理服务只需要加载这个OM文件即可。3.3 推理代码怎么写pyACL 调用 NPU 的完整流程模型转换好了接下来就是写推理代码。昇腾对Python开发者最友好的就是pyACL接口它是CANN ACLAscend Computing Language的Python绑定使用流程和CUDA的Runtime API非常相似初始化、资源申请、数据搬运、模型执行、数据回收。我写一个最简单的YOLO检测流程骨架你看一眼就明白import acl import numpy as np import cv2 # 1. 初始化ACL ret acl.init() # 2. 设置设备单卡场景就是0号设备 ret acl.rt.set_device(0) # 3. 创建上下文CANN很多接口必须在上下文里调用 context, ret acl.rt.create_context(0) # 4. 加载OM模型 model_path byolov5s.om model_id, ret acl.mdl.load_from_file(model_path) # 获取模型描述信息用来申请输入输出内存 model_desc acl.mdl.create_desc() ret acl.mdl.get_desc(model_desc, model_id) input_size acl.mdl.get_num_inputs(model_desc) output_size acl.mdl.get_num_outputs(model_desc) # 5. 申请输入输出设备内存Device 侧 # 这里省略具体的内存申请细节使用acl.rt.malloc申请 # 输入输出数据都需要放在Device侧NPU才能直接访问 # 6. 预处理并复制数据到Device内存 # 读图 - resize到640x640 - BGR转RGB - 归一化如果AIPP没做 # 然后acl.rt.memcpy将处理后的数据拷贝到输入buffer # 7. 执行模型输入数据地址、输出数据地址、模型ID ret acl.mdl.execute(model_id, input_data_buffer, output_data_buffer) # 8. 把输出从Device拷贝到Host然后做后处理 # 解析输出YOLOv5的输出shape一般是(1, 25200, 85) # 25200 640/8*640/8*3 640/16*640/16*3 640/32*640/32*3 # 85 4个坐标 1个置信度 80个类别 # 9. 释放资源释放内存、卸载模型、销毁上下文、去初始化你看整个流程跟CUDA编程的路子很像先请内存把数据搬到设备侧让模型在设备上跑再搬回来。关键点在于输入数据的摆放位置——NPU不能直接访问普通内存必须把数据拷贝到用acl.rt.malloc申请的设备内存里。很多人第一次写的时候会在这里出错报“input data pointer is null”之类的错实际上就是没搞清楚哪块是Host内存、哪块是Device内存。输出部分要专门说一下。YOLOv5、v7、v8的输出结构各不相同最麻烦的是大多数框架导出的模型都默认带NMS后处理逻辑。我的建议是转换时就通过配置把NMS剥离掉让模型只输出原始的张量比如YOLOv5的输出是(1, 25200, 85)然后NMS在CPU上自己实现。这样有几个好处模型结构更干净、转换时少报错、后处理逻辑在你的控制之下调试也方便。代价是推理产生的检测框数据需要从NPU搬到CPU多一次PCIe传输但相比模型内部的巨型张量计算这个开销完全可接受。3.4 性能优化batch、动态 shape 和内存复用模型能跑通只是第一步上线之后依然要面对吞吐和时延的平衡问题。我之前部署一个YOLOv5s项目刚开始单帧推理的耗时在十几毫秒看着还可以但跑满十几路视频流之后就卡了。后来做了一轮优化吞吐提升了将近一倍。首先是batch size的选择。NPU跟GPU类似处理一个batch为1的请求和batch为8的请求计算时间不会成倍增长所以把多个请求凑成一个batch喂给NPU能够显著提高吞吐。但是batch不是越大越好batch太大时单帧延迟会上升而且对内存容量的消耗也变大。这需要根据业务场景调如果追求单帧低延迟比如工业实时检测batch1或2就够了如果追求总吞吐比如离线批量处理大量图片batch8到16比较合理。其次是动态shape的处理。如果你的模型转换时指定了固定shape比如1,3,640,640而线上请求的图片尺寸大小不一那就需要做letterbox预处理把图片填充成640x640而不是直接resize。这个细节很关键直接拉伸会破坏目标的长宽比导致检测精度下降。letterbox的思路很简单计算出等比缩放的scale和需要填充的pad把图片贴到画布上。我见过很多人在这一步图省事直接resize结果小目标全丢了。然后是内存复用。ACL接口提供流stream的概念可以在同一个流上串行执行多个模型的推理任务也可以复用输入输出buffer避免频繁申请释放内存。我用acl.rt.malloc给每个输入输出各申请了一次内存之后所有推理都复用这两个buffer只在拷贝数据时更新内容。这样做的好处是大幅减少了运行时内存分配的开销实测内存占用比频繁申请释放时下降了三分之一左右。还有一个容易被忽略的优化点图像解码和预处理非常耗时。如果你的业务是处理视频流或大量图片别小看resize和格式转换的时间。我的做法是用多线程来并行做预处理让CPU的多个核同时干活NPU只负责推理。预处理完的结果放在一个队列里主线程从队列取数据喂给NPU。这个小改动让端到端的吞吐提升了20%以上。4. 部署后的性能调优与实测数据经验4.1 调优关键参数从延迟到吞吐的取舍工具链熟悉之后会进入一个漫长的调优期。昇腾CANN提供了一些设备侧的调优开关最常用的是设置AOEAscend Optimization Engine做算子调优。AOE会在你指定的模型上自动搜索最优算子实现让模型在昇腾芯片上跑得更快。实际操作上我先跑一次AOE调优然后用它产出的op tuning知识库重新用ATC转换一遍OM很多时候能把模型推理时间再压掉10%-30%。另一个重要工具是推理引擎的profiling。CANN的msprof工具可以看到每个算子的耗时和内存占用帮你把性能瓶颈定位到具体算子上。我第一次跑YOLOv8的profiling发现耗时大头不在主干网络而在输出层的reshape和transpose操作上——这正是因为模型导出时带了后处理结构在NPU上执行效率很低。把后处理剥离之后推理时间立刻下降了将近一半。这也印证了我前面说的模型导出结构对NPU性能的影响极大。batch的选择是吞吐和延迟间的平衡。batch8时NPU的利用率高总吞吐最大但单个样本的延迟可能从10ms增长到30msbatch1时单帧延迟最低但NPU算力大量闲置。我的经验是视频流检测场景如果对实时性要求高比如30FPS的摄像头batch保持2-4配合预处理流水线效果比较好离线批量推理则直接上大batch跑满NPU。4.2 实测数据对比与实际项目经验分享一组来自我实际项目的粗测数据供参考操作系统Ubuntu 20.04Atlas 300V Pro 24G模型YOLOv5m输入尺寸640x640AIPP预处理归一化后处理NMS在CPU上跑线程池8线程做预处理。batch8的场景下模型推理部分平均每batch耗时约120ms折算下来单图推理约15ms考虑到预处理和PCIe拷贝端到端每秒大概能处理45张图左右。batch1时单图推理能压到10ms以内但端到端总吞吐反而低因为预处理没有形成流水线。相比之下同一套模型在高端CPU上用OpenVINO跑单图需要80-150ms性能差距还是很明显的。不过性能强不代表不用调。我这里讲一个教训最早我图省事AIPP配置里没有做letterbox而是直接resize输入结果小目标检测效果明显变差尤其在监控画面上远离摄像头的小人几乎全部漏检。后来我才意识到YOLO训练的时候是letterbox到640x640推理时也应保持一致否则输入分布发生偏移模型效果自然打折。这个教训让我养成了一个习惯推理预处理必须和训练预处理严格对齐。5. 常见问题与排查技巧实录5.1 模型转换报错“Unsupported Op”这是Atlas部署YOLO时最常遇到的坑之一。ATC转换时报错说某个算子不支持先不要慌八成不是硬件不支持而是算子没有实现映射或者算子类型太新。我遇到过一次YOLOv8s导出ONNX后ATC报“Slice”算子不支持原因是我用的ONNX版本里Slice是Opset 16的表示方式而ATC对应的算子库只覆盖到Opset 13。解决办法有两种一是把模型导出时指定opset_version13二是升级CANN版本。优先试第一种成本最低。另一种情况是模型里确实有NPU实现不了的算子比如某些自定义的NMS实现。这类算子的解法就是“能不用就不用”把NMS从模型里抠掉后处理全部在CPU完成。前面的章节我反复强调这点因为它几乎能解决一半的转换失败问题。还有一个小技巧如果你改不了模型结构也可以尝试用ATC的--insert_op_conf参数和--enable_small_channel参数有些时候通过调整配置能绕过不支持的算子但治本的办法还是结构上面做简化。5.2 后处理耗时爆炸NMS 推理输出解析要设计好很多人在推理性能达标后发现端到端延迟还是很高一查才发现卡在后处理。YOLO后处理包含置信度过滤、类别筛选、NMS去重如果直接在CPU上用for循环写遇到目标多的一帧画面推到几十毫秒毫不奇怪。我的建议是能用numpy矩阵运算就不用纯Python循环。把整个(1, 25200, 85)的数组转换成numpy数组用numpy的布尔索引做置信度过滤和类别选择再用向量化的坐标计算做后续的NMS准备。这样处理一帧的时间能从几十毫秒压到几毫秒。更进一步的优化是缩小NMS的候选集。一个画面里绝大多数检测框的置信度极低在进入NMS之前先把大部分过滤掉NMS的计算量会小一个数量级。比如阈值设为0.25先保留置信度大于0.25的框再对这些框做NMS。你可以想象一下一帧画面上万候选框直接做两两对比计算复杂度是O(n^2)一旦n从几万降到几百速度能有质的提升。5.3 精度不对预处理格式 AIPP 配置问题转换成功、推理也跑起来了但检测框乱飞或者漏检严重这类问题十有八九出在预处理环节。我之前说过AIPP文件里写了mean、min、var参数这里的坑在于这些参数的作用顺序是有讲究的到底是先减mean再做归一化还是先做归一化再减mean以我看到的CANN文档AIPP的公式是y (x - mean) / var同时min/var会参与计算。如果你训练时的预处理顺序跟AIPP里的顺序不一致精度就会有偏差。最稳妥的办法是AIPP文件里不配置mean和var只配置格式转换比如BGR转RGB、U8转FP16归一化操作在你自己代码里完成。这样做虽然多了一步计算开销但逻辑清晰不会出现“AIPP和训练不一致”这种隐性问题。我在生产项目里一直采用这种方式排查精度问题时的难度会低很多。5.4 常见问题速查表我把这段时间遇到的高频问题和解法整理成一个速查表方便你排查时对照现象根本原因解决思路npu-smi看不到卡驱动未装好或版本不匹配先卸载旧驱动严格按版本配套表重新安装HDKATC转换报Unsupported OpONNX算子版本/实现不被支持降低opset_version导出ONNX或升级CANN版本必要时剥离复杂后处理模型能转换但推理精度差预处理与训练不一致统一letterbox、归一化、BGR/RGB转换逻辑AIPP少做归一化推理内存持续增长每次推理重复申请释放内存复用输入输出buffer按需申请一次循环使用端到端延迟高但NPU推理快后处理或预处理成为瓶颈用numpy向量化后处理多线程并行预处理形成流水线单图延迟低但吞吐上不去batch太小NPU利用率低凑batch结合业务调整batch size测试延迟与吞吐平衡点OM模型加载慢模型太大或NPU初始化繁忙预加载模型并常驻内存不要频繁load/unload6. 写在最后我的一点使用心得Atlas 300V 24G这个卡在“国产化、低功耗、高吞吐推理”这个交叉场景下确实是非常能打的选择。它不完美生态没有CUDA那么成熟CANN的文档偶尔也有语焉不详的地方模型迁移时你得有点耐心去调ATC配置。但话说回来一旦你把模型转换跑通、把预处理和后处理的流程打磨顺了它回馈给你的是极低的功耗、稳定的运行状态和很不错的推理吞吐。这让我时常觉得工具选型这件事关键不是哪个工具最好而是哪个工具最适合自己的业务约束条件。我个人在实际操作中还有一个体会不要在设备到货后匆匆忙忙开始装环境先把版本配套表研究透把模型转换验证的实验在CPU模拟环境或文档层面先推演一遍能省很多时间。我的YOLO部署项目第一版跑通花了将近两周其中一半时间都浪费在环境重装和模型转换报错上。后来第二次部署另一个模型全套流程只花了一天半。这个差距不是硬件变了纯粹是人摸清了它的脾气。所以如果你也正打算部署YOLO到Atlas上别被网上各种报错帖吓住按着这篇的思路走稳扎稳打它会成为你手中一个很趁手的工具。
返回列表