ARTICLE DETAIL

资讯详情

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

从本地训练到云端部署:华为云ModelArts实战全流程记录

从本地训练到云端部署:华为云ModelArts实战全流程记录 本地显存不够、训练环境反复崩溃、部署服务还要写一堆接口的痛估计每个搞过深度学习的人都深有体会。我一度也是在自己的机器上跑实验数据量小的时候还能忍一旦换成ResNet这种预训练模型做微调、或者想开多个并发任务硬件瓶颈就扑面而来。后来我把整套流程迁移到华为云ModelArts上从数据准备、训练作业到在线部署完整跑了一遍才真正理解云上这套工作流的设计思路。这篇文章严格说不是官方教程而是我基于实际操作的记录记录我如何基于华为云ModelArts完成一次模型训练与部署。笔记会按我亲身体验的顺序展开为什么从本地迁到云上、资源怎么规划、数据怎么传、训练作业怎么配、模型怎么顺利发布成在线服务以及我踩过的那些坑。内容偏向实操路径代码和命令都是我实际用过的不搞玄学。适合第一次接触ModelArts、想快速跑通训练部署闭环的人也适合本地训练已经玩得很熟、但还不太理解云上流程的同学。1. 为什么是ModelArts从本地训练到云上训练的迁移动机1.1 本地训练遇到的三堵墙先说说我在迁移之前到底遇到什么问题否则你不会理解我在ModelArts上做的每一个选择。第一堵墙是硬件。我的主力是一张消费级显卡显存12GB跑一些小型分类网络没问题但一旦想加载ImageNet预训练权重做全量微调或者把输入分辨率提到512瞬间就爆显存。更难受的是训练和实验并行做的时候机器内存也不够经常训练到一半进程被系统杀掉。第二堵墙是环境。做过深度学习的人都知道Conda环境有多容易崩溃今天装一个OpenCV明天升级一个CUDA版本后天依赖冲突一折腾就是半天。我试过用Docker来固定环境但本地磁盘空间又报警了。第三堵墙是部署。训练出来的模型要给别人用本地机器没有公网IP要么做内网穿透要么写一堆服务脚本把Flask或FastAPI包一层。好不容易跑起来还要面对并发性能、日志监控、进程守护这些工程问题。整个链路下来训练时间可能只有一半另一半全在跟环境、部署、运维搏斗。1.2 ModelArts与OBS的分工逻辑ModelArts给我的第一印象不是一个AI平台而是一套训练与部署的作业流水线。它做的事情可以粗略分成两块训练侧和推理侧。训练侧负责提供算力、调度训练作业、收集日志推理侧负责把模型发布成HTTP服务供外部调用。而几乎所有数据、权重、日志都放在OBS这个对象存储里。这里有个很重要的思路数据和计算是解耦的。训练作业跑完就销毁数据却一直留在OBS里模型产物也会回传到OBS指定路径。这个设计的好处是你想换一个更大的GPU集群重跑数据不用重新上传你想对比多个训练版本产物也都在对象存储里躺着随时可以拉出来看。用生活里的场景类比OBS是一个大型仓库ModelArts是加工车间。仓库负责存原料和成品车间负责把原料加工成成品。车间里机器用完可以清空甚至换一批机器仓库里的东西不会丢。理解了这两个角色的分工后面的操作就顺理成章了。2. 训练前的准备账号、资源池与开发环境2.1 OBS桶规划区域一致性和目录结构很多人第一次用ModelArts时上来就想创建训练作业结果卡在数据导入环节因为压根没把OBS桶建好或者桶的区域跟ModelArts不一致。要注意区域一致性这个细节。ModelArts在华北、华东等区域都有资源你在哪个区域使用ModelArts你的OBS桶最好也建在同一个区域。跨区域的数据访问不是不能做但网络延迟和费用都会变高训练作业读取数据的速度也会受影响。我第一次用的时候就是桶在华南、服务在华东结果上传数据和读取数据都慢得离谱后来把桶删掉重建才恢复正常。目录结构建议一开始就规划好。我的习惯是这样的bucket-name/ ├── datasets/ │ ├── train/ │ ├── val/ │ └── test/ ├── code/ └── output/ └── experiment-001/datasets放原始数据code放训练脚本output下按实验批次划分子目录每个子目录保存对应的模型文件、日志和指标。这样做的直接好处是当你同时跑多个实验时不会在OBS里分不清哪个模型是哪个任务产出的。ModelArts的很多操作也需要指定OBS路径作为输入或输出一个整洁的路径规划能避免后面频繁修改配置。2.2 Notebook实例规格与镜像选择ModelArts的Notebook是一个JupyterLab环境可以在里面写代码、做交互式实验。创建Notebook时你会遇到几个选择实例规格、存储空间和镜像。规格的选择我的经验是调试阶段别一上来就申请最贵的GPU。先用一个小规格的实例把代码逻辑、数据处理、模型结构验证跑通确认没问题之后再跑到训练作业里用大规格资源正式训练。这不仅省钱更重要的是调试期的代码问题不需要大算力大算力应该留给出真正训练时。下面是我常用的选型参考场景建议规格理由数据处理、代码调试、可视化分析CPU实例便宜够用不需要GPU小批量训练、尝试不同超参单张入门级GPU显存占用可控能快速验证正式训练、多卡并行多卡高性能GPU缩短训练时间跑大量epoch镜像方面ModelArts通常会提供PyTorch、TensorFlow、MindSpore等常见深度学习框架的预置镜像。新手建议直接用预置镜像不要自己捣鼓自定义镜像因为预置镜像里已经把CUDA、cuDNN、常用依赖装好了省掉很多麻烦。等你需要一些冷门依赖或者特殊算子时再考虑自定义镜像也不迟。2.3 通过obsutil批量上传数据数据如果不大在OBS控制台手动上传也能接受。但一旦数据集到了几个GB甚至几十个GB网页上传基本就不可用了动不动断掉还没法断点续传。这时候要用obsutil这个命令行工具。obsutil是华为云OBS的命令行客户端支持批量上传、下载、增量同步和断点续传。配置很简单先下载对应操作系统的工具然后用obsutil config进行初始化。上传数据我一般这样执行# 将本地的datasets文件夹同步到OBS桶 obsutil cp ./datasets obs://bucket-name/datasets -r -f-r表示递归同步整个目录-f表示强制覆盖同名文件。如果中间断网了重新执行一次同样的命令它会自动跳过已上传的文件只补传缺失的部分。这个机制对大数据集非常友好。我实测下来的经验是上传大量小文件时obsutil的并发效率比网页上传高非常多。几千张图片如果分成几千个文件网页上传几乎会卡到怀疑人生obsutil可以设置-parallel参数调整并发数把传输速度拉满。不过也要注意OBS访问有频率限制并发太高可能触发限流我习惯把并发数设在合理范围具体数值根据网络情况调整即可。3. 数据集组织与质量检查容易被忽视的起点3.1 数据目录与标签文件在做图像分类这个任务时我用的数据组织方式是最常见的文件夹式标注结构datasets/train/ cat/ 001.jpg 002.jpg dog/ 001.jpg 002.jpg datasets/val/ cat/ 001.jpg dog/ 001.jpg这种结构的好处是PyTorch的ImageFolder可以直接读取不用手动写路径与标签的映射关系。如果你的任务是目标检测数据需要的是标注文件比如COCO格式的json或者YOLO格式的txt那目录结构就完全不一样。无论哪一种核心原则是一样的保持数据的目录结构、标签文件与训练脚本的预期完全一致。我后来还养成了一个习惯每次数据清洗完都把数据清单生成一份元信息文件存在OBS桶里和数据集放在一起。清单里记录这张图片的路径、尺寸、标签、来源批次。这样即使隔了几个月回头再看某个实验也能快速确认当时训练用的到底是哪批数据而不至于看着一堆精度数字不知道是在什么数据上跑出来的。3.2 用Python脚本做数据完整性校验在本地训练时数据有问题可以直接打开文件夹翻翻找找但在云上数据都在OBS里如果训练到一半才发现数据有问题那浪费的就不只是时间还有算力费用。所以我在上传数据之后、启动训练之前会先写一个脚本做数据完整性校验。import os from PIL import Image root datasets/train count 0 broken [] for cls in os.listdir(root): cls_path os.path.join(root, cls) if not os.path.isdir(cls_path): continue for fname in os.listdir(cls_path): fpath os.path.join(cls_path, fname) count 1 try: img Image.open(fpath) img.load() except Exception as e: broken.append(fpath) print(total:, count) print(broken:, len(broken)) for b in broken: print(b)这个脚本做两件事统计图片数量是否符合预期逐个打开图片检查是否损坏。如果图片损坏在训练过程中通常表现为图像解码报错或者更隐蔽地某些框架缓存到内存里的数据是空的直接导致训练中断或者出现奇怪的精度问题。另外归一化检查也很重要。很多模型训练前都需要对输入做标准化如果你的图片里有大量全黑、全白或者值域异常的样本输出特征分布就会很怪。我一般会在校验时随机抽样几十张图统计像素的均值、方差确保数据没有明显的异常分布。3.3 分布式训练前的shard/split设计如果你只是用单卡训练数据组织简单点问题不大。但如果你打算用多卡分布式训练或者用ModelArts的训练作业同时跑多个节点数据的读取方式就要重新考虑了。大量小文件直接从OBS读取时每张图片都要经历一次网络请求IO开销非常惊人GPU基本会饿死等数据。解决思路有两个方向。一是准备训练前把数据打包或者转成内存映射格式比如把图像打包成TFRecord或WebDataset格式或者直接生成一个record文件让训练时顺序读大文件而不是随机读小文件。二是在训练脚本里做数据预取和数据加载器的参数调优增加num_workers、开prefetch_factor尽量用流水线方式掩盖IO延迟。这一节写在学习笔记里是因为很多人在单卡上跑通之后第一次上分布式训练就遇到吞吐量上不去的坑根因往往不是模型问题而是数据供给出了问题。提前把数据格式设计好后面会省很多事。4. 训练任务配置从Notebook实验到训练作业4.1 Notebook内训练与训练作业的差别我一开始的做法是在Notebook里跑训练脚本因为交互式环境方便调试各种打印输出直接看。但跑了几轮之后发现Notebook里训练有几个问题长时间训练很容易因为浏览器连接断开而中断资源不释放费用居高不下日志太多时滚动查看不方便。后面我改用ModelArts的训练作业功能。训练作业本质上是一个脱离交互式环境的独立任务你指定算法或镜像、数据路径、输出路径和资源规格平台就会调度一台机器去跑整个过程是异步的训练日志会统一收集任务结束后资源自动释放。训练作业和Notebook调试的定位不同。Notebook适合探索、画图、快速验证想法训练作业适合固化下来的正式实验。我的流程是先在Notebook里用少量数据跑通代码然后把脚本整理干净去训练作业里用全量数据训练。这样做既得了调试的方便又得了正式训练的稳定。4.2 用ResNet预训练模型快速起步我的任务是一个细粒度图像分类样本量不大从头训练一个深度网络很难收敛所以我用了ResNet预训练模型作为起点。热门关键词里提到的resnet预训练模型正是这个思路。所谓预训练就是模型已经在ImageNet这种超大数据集上做过一遍通用特征学习我们拿来做微调只需要把最后的全连接层换成自己的分类头。代码大致是这样import torchvision.models as models import torch.nn as nn model models.resnet18(weightsmodels.ResNet18_Weights.IMAGENET1K_V1) num_classes 10 model.fc nn.Linear(model.fc.in_features, num_classes)用预训练模型的好处是收敛快得多一般几十个epoch就能达到不错的效果对数据量的要求也低很多不需要几十万张图从零学起。代价是特征提取器部分是从ImageNet学来的如果你的图片域跟ImageNet差异非常大比如医学影像或者卫星图可能一开始的迁移效果并不理想需要解冻更多层做全量微调。训练脚本里还有几个关键点优化器一般用带动量的SGD或AdamW学习率建议从小一点开始因为预训练模型的初始特征已经很好花大力气去动它的参数反而容易破坏已有特征。我个人习惯用torch.optim.lr_scheduler做学习率衰减每隔若干epoch把学习率降一档。4.3 日志与监控怎么判断训练是否正常训练作业跑起来之后不能干等着。我一般会在代码里记录两类信息一类是每一轮或者每几十步的损失值和准确率另一类是当前学习率和已用时间这些信息能帮你判断训练有没有正常收敛。日志建议这样打印import logging logging.info(fepoch: {epoch}, step: {step}, loss: {loss:.4f}, acc: {acc:.4f}, lr: {lr:.6f})训练作业的日志会统一展示在控制台页面里。如果训练中途异常退出日志里通常会有traceback要习惯第一时间翻日志而不是盲目重跑。除了日志还可以用TensorBoard的思路把训练指标写到文件里传到OBS跑完用Notebook拉下来画曲线。ModelArts的训练作业通常也能配置一些监控指标。不过我的习惯是先在本地小规模验证整个训练流程能跑通再提交到云上正式训练这样能大幅避免训练两小时后才发现代码有bug的悲剧。5. 模型部署为在线服务推理代码才是关键5.1 模型文件与推理脚本的打包结构好不容易把模型训好接下来就是部署。很多人在这一步卡住是因为训练时只需要model.load_state_dict就能恢复模型但部署到在线服务时平台需要一个完整的推理服务包里面不仅有模型权重还要有推理脚本。ModelArts的在线服务本质上是一个常驻的HTTP服务每个请求进来都会经过推理脚本的处理。推理脚本必须实现一个_preprocess预处理和_postprocess后处理的流程前者把用户传来的图片或文本变成模型能接受的张量后者把模型输出的张量变成用户能看懂的结果。我用的推理脚本结构大致如下def _preprocess(self, data): # 把请求体中的二进制图片数据解码成numpy数组 # 缩放、归一化、转成tensor需要和训练时保持一致 return input_tensor def _postprocess(self, data): # 从模型输出里取每个类别的概率 # 返回最高概率的类别标签和置信度 return {label: label_name, score: float(score)}这里最容易被忽视的坑是预处理逻辑必须和训练时的数据增强完全一致。训练时如果做了归一化推理时也要做一模一样的归一化否则模型效果会打折扣。很多模型部署后精度下降排查半天结果就是推理代码里的均值方差填错了。5.2 在线服务配置与请求测试部署在线服务时需要选择模型版本、服务名称、计算资源规格和实例数。计算资源规格的选择直接影响推理速度和费用如果你的业务是低延迟要求的需要选择更强劲的规格如果是后台异步处理任务可以适当降低规格来省钱。部署完成后平台会提供一个访问地址和API密钥调用方可以发送HTTP请求来获取推理结果。我一般用Python的requests库做联调测试import requests import base64 import json url your_inference_url headers {Content-Type: application/json, X-Auth-Token: your_token} with open(test.jpg, rb) as f: img_base64 base64.b64encode(f.read()).decode() resp requests.post(url, headersheaders, json{image_base64: img_base64}) print(resp.json())有一点要提醒在线服务启动后通常需要一段时间加载模型、初始化推理框架最早发来的几个请求可能响应很慢。这在自动扩缩容的机制下尤其明显新启动的实例第一次请求要等模型从存储加载到内存或显存里时间会比较长。后面会单独聊这个问题。5.3 自定义镜像适合什么场景ModelArts的在线服务通常支持两种部署方式一种是用平台预置的推理引擎把你训练好的模型文件和推理脚本放进去就能跑另一种是自定义镜像把整个环境和推理代码都自己做进Docker镜像里。预置推理引擎适合绝大多数常规场景尤其是PyTorch、TensorFlow等主流框架训练的模型。但如果你的项目有特殊依赖、需要特定版本的库、或者推理逻辑很复杂自定义镜像是更灵活的选择。自定义镜像需要自己处理的事情比较多装基础依赖、拉取推理框架、写推理服务入口、暴露端口。好处是环境完全可控不会遇到平台环境里少了一个包这种问题。我的建议是初学者先走预置引擎路线等确实有需求再去研究自定义镜像否则容易陷入环境工程的泥潭。6. 实测中的翻车与排查链路6.1 训练loss变成NaN一条完整的排查链训练过程中loss突然变成NaN这个问题在社区里被问烂了但真到自己遇到时排查起来还是容易慌。我记录一下我的完整排查链路希望能给你一个参考。第一步看数据。输入图片有没有包含无效值如果图片里存在全黑像素、损坏文件或者数据标注有误网络可能在反向传播时算出非数值梯度。排查方式是单独写一个数据检查脚本把每张图片的像素值范围打印出来看看是不是出现了NaN或者无穷大。另外还要检查归一化时用的统计量有没有问题如果图片的均值方差没有正确计算也可能导致数值异常。第二步看学习率。这是我遇到的最常见原因。学习率设置过大尤其是使用预训练模型时初始loss本身就比较小一个大学习率可能导致loss直接炸掉。排查方式很简单把学习率降到原来的十分之一再试一下。如果降学习率后没有出现NaN那基本可以确定是学习率的问题。第三步看损失函数。如果你的损失函数涉及log运算比如交叉熵损失预测概率接近0时会引入无穷大从而让loss变成NaN。解决方法是给log加上一个很小的epsilon比如log(x 1e-8)或者在代码里做数值截断。第四步看混合精度。开了AMP自动混合精度后某些算子在FP16下容易出现数值溢出特别是loss scaling处理不当的时候。排查方法是先关闭AMP确认问题消失再重新开启并调整loss scaling策略。排查NaN一定不要一上来就怀疑模型结构先怀疑数据和超参这是最常见也最容易确认的原因。我做了个简单对照表可能原因快速验证方法解决方向数据含NaN像素或损坏样本打印数据统计值清洗数据、重新归一化学习率过高学习率降10倍调整优化器参数损失函数数值不稳定加epsilon或截断增强数值稳定性AMP精度溢出关闭AMP测试调整loss scaling6.2 冷启动慢、首个请求超时在线服务部署好之后我第一次发起请求等了很久都没有返回差点以为服务出了问题。后来发现这只是冷启动。云上的推理服务在实例刚启动时需要把模型文件从对象存储拉到本地再加载到显存或内存里还要初始化推理框架的算子库整个过程可能需要几十秒甚至更久。针对这个问题有几种处理手段。一是在代码里做一个预热逻辑比如在服务初始化时就主动跑一次推理把常用图层的算子都预加载一遍这样第一个用户请求就不会命中冷启动。二是把实例数设成多个配合负载均衡让每个实例分摊请求压力。三是如果业务允许可以把最小的实例数量设置为1服务常驻不因空闲被回收。另外如果你发现即使服务热了单次推理耗时依然很高就要考虑模型优化了。轻量化的手段包括剪枝、量化和知识蒸馏尤其是量化把FP32的权重转为INT8或FP16推理速度通常有明显提升。网上讨论的热词里有rk3588部署yolov8这类边缘端部署思路其实类似都是为了在有限算力下把推理跑起来。6.3 磁盘与资源配额问题训练作业跑着跑着突然失败控制台提示磁盘空间不足。这个问题我第一次遇到时很意外因为我的数据集并不算大。后来才明白训练作业分配的临时磁盘空间是有上限的代码一旦把数据集拷到本地临时目录同时保存大量checkpoint和日志很容易把临时空间占满。解决办法是尽量流式地从OBS读取数据不要让训练脚本把所有数据都拉到本地checkpoint和日志定期清理只保留最近的几个版本输出产物及时上传到OBS指定路径。训练过程中产生的临时文件在脚本里也应该主动删除。资源配额是另一个容易被忽略的问题。新申请的账号通常默认资源配额不高创建GPU训练作业时会提示资源不足或者配额已达上限。这个不是故障而是需要去配额中心申请提升一般审核很快。我第一次遇到时还以为是平台故障折腾了半天才发现是配额问题希望你不要重蹈我的覆辙。整个流程走下来我的感受是ModelArts真正解决的问题是把训练和部署这条链路的工程复杂度降下来了让数据存储、算力调度、日志收集、服务暴露这些基础设施工作变得标准化。但有一点没变——你对模型本身的理解、对数据的敏感度、对排查问题的思路这些仍然是决定实验能否成功的关键。平台只是工具用得顺不顺手最终还是看使用者对业务和算法的认知深度。最后分享两个小建议第一每次做完实验记得把Notebook停掉、把不再需要的资源释放云上资源按量计费长时间挂着不跑任务的实例账户余额流得飞快。第二训练作业的日志和模型产物养成定期归档的习惯后续做对比实验时会发现一个整洁的OBS目录结构比任何实验记录文档都直观。
返回列表