上个月我手里的一个图像分类项目跑到了瓶颈期,本机那块显卡的显存被参数和数据反复撑爆,一次完整训练要断断续续跑上两天,中间还不敢关机。后来我把整个流程迁到了华为云ModelArts上,从数据处理、模型训练到最终部署成在线服务,完整走了一遍,踩了不少坑,也摸清了套路。这篇笔记就是把这段实际操作用到的流程、参数、报错和解决办法记录下来,给准备用ModelArts做训练和部署的同学一个参考。
先说我最终做到的效果:一个大概两万多张图片的分类数据集,在ModelArts上开了一张GPU规格的按需实例,训练从本地的一两天压缩到了不到三个小时,日志和指标都可以在页面上直接看,训练结束后生成的模型一键部署成了在线服务,支持API调用。整个过程并不复杂,但细节比想象中多,尤其是数据准备、脚本适配和资源选型这几块,一点不注意就会反复卡壳。
1. 为什么从本地训练迁到ModelArts
1.1 本地训练的痛点:显卡、环境、协作
做深度学习的人对本地训练又爱又恨。爱的是环境自由,想装什么装什么;恨的是显卡永远不够用、显存永远在爆的边缘。我的情况比较典型,一台算力一般的笔记本,跑一个中等规模的分类模型,batch size稍微调大一点就会OOM,只能把图像尺寸压缩到很小,结果就是模型精度上不去。
除了硬件瓶颈,环境问题也很折磨人。不同项目需要不同版本的框架,Python环境里conda环境建了一堆,有时候隔一个星期回来,发现之前能跑的代码因为某个依赖升级突然报错。如果团队协作,每个人本地环境还不一样,模型复现就成了玄学。
还有一个被很多人忽略的问题是训练过程中的算力浪费。本地训练时,显卡在跑,但人不能一直在旁边守着,跑完一个实验马上要手动启动下一个,中间的时间全是空的。在云上就不一样,训练作业做完一个,脚本里可以直接串起下一个,或者用平台的通知功能在结束后推消息,基本可以把实验节奏拉满。
1.2 ModelArts解决的三个核心问题
我选ModelArts而不是自己搭服务器,核心是看中了三个能力。
第一是GPU资源的弹性获取。训练的时候开卡,训练完了释放,按小时计费,不用像自己买卡那样心疼闲置成本。尤其是做多个对比实验的时候,可以同时开两三个不同规格的实例跑不同配置,跑完直接释放,成本可控。
第二是环境一致性。ModelArts训练作业会拉取一个指定的镜像,镜像里预装了常用的框架和工具,训练脚本在本地调试的时候用一套依赖,提交到云端跑的时候也是同一套,基本杜绝了“在我电脑上是好的”这种问题。
第三是平台把训练和部署串起来了。ModelArts里训练生成的模型可以自动注册到模型列表,然后直接部署成在线服务,包括后端的资源监控、版本管理、灰度发布这些都是平台提供的。对于我这种一个人兼顾算法和工程的人来说,省掉了很多前后端对接的杂活。
不过也要说实话,ModelArts并不是零成本上手,它的操作界面和信息密度比较高,初次接触会有点懵。这也是我写这篇笔记的动机,把那些光看文档不一定能一次性理解的东西,用实际踩坑的方式讲清楚。
2. 准备工作:账号、OBS桶与数据集
2.1 创建OBS桶和文件夹规范
ModelArts里的所有数据、训练脚本、模型文件,本质上都是存在OBS(对象存储服务)里的。训练作业运行的时候,平台会把OBS里的脚本和数据下载到训练容器中,所以第一步就是把项目文件整理到OBS桶里。
我第一次用的时候直接在桶里随便建了几个文件夹,结果训练作业找不着模型输出路径,日志报了一堆错。后来我按下面这套目录结构来整理,就没再乱过:
my-project-bucket/ ├── data/ │ ├── train/ │ └── val/ ├── code/ │ ├── train.py │ ├── model.py │ └── requirements.txt └── output/OBS桶本身有区域概念,建议和ModelArts使用同一个区域,这样训练作业读写数据走内网,速度快而且不额外收流量费。桶的权限设置上,我直接用的华为云统一身份认证,在ModelArts的训练作业里指定同一个账号的OBS路径即可,不需要单独去配复杂的策略。如果你是要把数据集分享给团队其他人,可以再细分权限,自己一个人跑的话不用折腾。
2.2 数据集上传与自动标注
数据集上传这一步,最直观的方式是用OBS控制台传,但如果你本地的数据集比较大,比如几十G甚至上百G,页面上传容易断。我试过用OBS提供的命令行工具来传,支持断点续传和并行上传,速度明显稳定得多。如果机器上装了Python,也可以用OBS的SDK写个几行的上传脚本,对大目录递归上传更方便。
ModelArts里自带数据管理功能,可以创建数据集,支持图片分类、物体检测等场景。我这次用的是图片分类,直接把图片按类别放到不同的文件夹下,然后在数据集页面选择对应的OBS路径,平台会自动扫描并生成标注信息。这里有个小技巧:如果你已经有自己的标注格式,比如文本标签文件,可以通过数据集的导入功能来关联,不一定非要在页面上手动标注。手动标注适合几百张的小数据集,几千张以上真的会标到怀疑人生。
数据集的版本管理也是容易被忽略的功能。我在调了几轮数据清洗策略之后,发现最好每次清洗后都生成一个新版本,训练时指定这个版本。这样回头对比实验效果时,能明确知道用的是哪一份数据,不会出现“这次涨点是因为数据还是因为模型结构”说不清楚的尴尬。
2.3 镜像与环境的选型思路
ModelArts训练作业需要指定一个运行环境,也就是镜像。平台预置了主流的深度学习镜像,比如不同版本的PyTorch、TensorFlow、MindSpore,也支持自定义镜像。我的建议是,如果你的代码不依赖特殊的系统库,直接用预置镜像就好,省心省力。
选择镜像的时候,一定要和本地开发环境的框架版本对齐。我就吃过这个亏:本地用的是PyTorch 2.0的新接口,平台上默认的旧版本镜像不认,代码里一个简单的torch.compile直接报错。后来重新选了对应新版本的镜像,问题才消失。你在选镜像时,不光要看框架主版本,还要看CUDA版本,如果用到特定算子的自定义实现,CUDA版本不匹配也会很痛。
如果你的项目有额外的pip依赖,可以在训练脚本里通过pip install临时安装,也可以在requirements.txt里声明,让ModelArts启动训练前自动安装。我比较推荐后者,因为依赖清晰可复现。不过要注意,每次训练作业启动时都会重新安装依赖,如果依赖很多,会拖长启动时间,所以能塞进预置镜像的常用依赖,就尽量别在业务脚本里再装一遍。
3. 训练作业实操全流程
3.1 编写训练脚本时最容易踩的三个坑
ModelArts训练作业的脚本入口和本地跑训练有些不一样,最核心的一点是:数据路径、输出路径这些不要写死,而是通过环境变量或命令行参数传进来。
平台会在训练容器里设置一些固定的环境变量,比如MODELARTS_DATA_DIR指向数据集目录,MODELARTS_OUTPUT_DIR指向输出目录。我第一次没注意,还是用本地绝对路径,结果训练作业启动后数据怎么都读不到。后来改成从环境变量里取路径,问题就解决了。
第二个坑是训练容器里的系统环境比较干净,不像本地装了各种可视化工具。你在代码里用了matplotlib.pyplot来显示图片,只会在日志里堆一堆无用输出,甚至可能因为没有图形库报错。训练脚本里真正需要的是配置日志记录、保存checkpoint、打印关键指标这些能力,别把可视化逻辑放进去。
第三个坑是数据预处理不能太拖沓。本地训练时,CPU读图、解码、resize虽然慢一点,但能忍。云上训练如果数据读取变成瓶颈,GPU会大量时间空转,按小时计费就很亏。我用的是PyTorch的DataLoader,必须把num_workers调高,并且配合prefetch_factor,尽量让数据管线不拖后腿。ModelArts的存储性能其实不错,但如果你每次都从OBS拉全量数据,仍然会有IO等待,建议小数据集先下到本地缓存目录,大数据集用OBS的并行文件系统挂载。
3.2 配置训练作业的关键参数
新建训练作业的时候,有几个配置项非常关键。
第一是资源规格。ModelArts提供多种规格,包括CPU和GPU。GPU的显存大小直接决定你能用多大的batch size。我这次用的是带有16G显存的规格,两万张图、批量大小64,刚好合适。如果你的模型更大,可以直接选更高规格的卡,但计算费用也会同步增长,属于需要平衡的项。
第二是训练时长和自动停止。平台支持设置最大训练时长,到了时间会自动终止作业并释放资源。这个功能我强烈建议开启,尤其是刚开始调试的时候。我有一次忘记关闭一个死循环式的数据加载线程,训练作业跑了好几个小时才发现,费用白白浪费。设置了最长训练时间之后,最多被系统掐断,不会一直扣钱。
第三是预训练模型和Checkpoint的加载。如果你的模型需要加载在别处训练的权重,可以把权重文件放到OBS,在训练脚本里指定路径加载。这里需要注意的是,ModelArts训练容器和OBS的文件系统不是天然打通的,需要先通过平台提供的moxing库将OBS文件复制到本地路径,再执行加载。这个库刚开始用会有点绕,但看一遍示例就明白了,本质就是mox.file.copy_parallel这类接口。
训练作业创建完成后,平台会先启动容器、下载代码和数据,然后才执行训练命令。这个准备阶段也可能受镜像大小和数据集规模影响,有时候要等几分钟。如果训练数据特别大,建议提前把数据做压缩或者只传训练所需的子集,能明显加快启动速度。
3.3 日志查看与调优经验
训练作业运行起来之后,我习惯在ModelArts的日志页面观察训练输出。日志是实时刷新的,可以看到每个epoch的loss、accuracy和耗时。我的一个感触是,云上训练时,日志打得越精简越好,因为日志太多不仅看着累,还会占用存储。最好每N个iteration打印一次关键指标。
我这次训练中遇到的一个典型问题是loss下降很慢,排查之后发现是学习率没有跟着batch size调整。因为云上的显卡比本地好,我把batch size从8调到了64,但学习率还是原来的值,等于学习率相对变小了。后来把初始学习率从0.001调到0.003左右,loss的收敛速度才正常。这说明迁移到云端之后,很多超参需要重新审视,不能直接沿用本地参数。
另外,ModelArts训练作业支持在运行中查看资源使用率,比如GPU util、显存占用这些。如果发现GPU util始终很低,说明瓶颈可能不在算力,而是在数据加载或者CPU预处理。这个时候优先去优化数据管线,而不是盲目加钱升卡。
4. 模型部署:从端点到在线服务
4.1 模型转换与版本管理
训练完成之后,ModelArts会把指定的输出目录里的模型文件自动注册到模型列表中。如果你的模型不是平台预置的那几个框架格式,可能还需要做一次转换。我这次用的是PyTorch训练的模型,导出成推理需要的格式后上传到OBS,然后在模型管理页面创建模型时会要求选择AI引擎和模型路径。
这里有一个值得注意的点:训练时的模型文件和推理时需要的模型文件可能不同。比如训练时你保存的是包含优化器状态的完整checkpoint,文件可能好几个G,但推理只需要网络权重,可能就几百MB。在ModelArts创建模型时,最好单独准备一个干净的推理模型文件,把权重提取出来再做部署,这样实例启动更快,版本也更清爽。
模型版本管理方面,我在同一个模型下发布了多个版本,部署时可以直接指定版本号,也方便做回退。如果线上的模型效果变差,一键切回上一个版本,比临时改代码重新部署要快得多。
4.2 在线服务的配置细节
ModelArts部署在线服务时,需要选择模型、服务名称、资源规格和实例数。第一次部署的时候我以为直接默认就好,结果选了CPU规格,推理速度惨不忍睹。后来换成GPU规格,单次预测的耗时从几百毫秒降到了几十毫秒,体验完全不一样。如果你的服务是给内部验证用,或者并发量不高,CPU也可以先顶着;但如果要接外部流量,还是直接上GPU更稳。
配置服务实例数的时候,如果选择最小实例数为0,可以实现按需伸缩,没有请求时不跑实例,有请求时快速拉起。这个模式成本很低,但冷启动会有延迟,不适合对响应时间要求高的场景。如果预算充足,生产环境建议至少保留一个常驻实例。
在线服务创建成功后,平台会分配一个推理地址,通过这个地址发送API请求就可以拿到预测结果。请求体的格式需要和你的推理代码对应。ModelArts支持自定义推理脚本,比如在模型目录里放一个customize_service.py,可以重写预处理和后处理逻辑。我把图像解码、归一化这些操作都写进了自定义脚本,这样外部调用方只需要传图片的base64编码,不需要关心模型内部的输入细节,接口友好很多。
4.3 模型监控与资源变配
部署上线之后,可以在ModelArts的监控页面查看服务调用量、平均时延和错误率。我每次调整模型版本之后,都会先手动发几条请求验证预期输出,再观察监控页面上的时延曲线有没有异常。
如果服务在运行过程中遇到流量突增,比如临时做一个活动,可以在页面上直接调整实例数或者切换更大规格。这个操作的代价是服务会有一个滚动更新的过程,期间可能出现短暂的不可用,所以尽量在低峰期操作。如果流量模型比较稳定,也可以配置自动扩缩容策略,让平台根据指标自动调整实例数,而不是一直开着高规格实例烧钱。
5. 常见问题与排查心得
5.1 训练作业卡死或重启
训练作业运行一段时间后卡死,是云上训练最让人头疼的问题之一。我遇到过两类情况。一类是数据加载线程出了问题,某个worker读数据时抛了异常,但主进程没有正常退出,导致整个作业一直挂着。排查方法是把DataLoader的num_workers临时设为0,看是否还会卡死,如果0的时候不卡,说明问题出在子进程的异常处理上。
另一类是内存溢出的问题。训练容器内存是有限制的,如果数据集被一次性加载到内存,很容易把内存打爆。我在做数据预处理时习惯把所有图片读成numpy数组放在内存里,两万张图加起来好几个G,云上容器直接吃不消。后来改成在__getitem__里实时读取和预处理,内存占用一下就降下来了。
遇到作业非预期中断时,ModelArts的日志里通常会有Error级别信息,但在日志中搜索“Error”可能会翻到很多无关的警告。我更推荐直接把日志下载下来,检索最后一批关键信息,看看到底是框架报错、系统杀进程,还是网络超时。经验是一步步缩小范围,而不是靠猜。
5.2 模型部署后延迟偏高
模型部署完成后,如果发现推理延迟不理想,先确认服务本身用的是不是GPU规格。之前我遇到过一次,页面配置看起来选了GPU,但请求延迟还是高,后来发现是模型转换后默认导出的格式在GPU上支持度不好,某些算子走了CPU回退。解决办法是导出模型时用更通用的格式,或者直接用平台适配好的推理引擎重新转换。
还有一个容易被忽略的点,就是网络传输耗时。在线服务的地址可能是公网的,如果你的业务程序也在云上,可以考虑使用同一区域内的VPC访问方式,走内网调用,延迟会比公网低一个档次。这个在ModelArts的在线服务详情页里可以找到对应的访问配置。
5.3 费用控制建议
使用按需付费的云服务,费用控制是一个无论如何都绕不开的话题。我这次训练加部署,实际产生的费用还算可控,主要有几个经验。
训练前尽量在本地把代码和参数调试到基本稳定,再提交到ModelArts跑正式实验。我不建议把云上训练当成调试机,每一次启动都在花钱。调试阶段可以用小数据集、小规格实例,确认逻辑没问题后再切换到全量数据和更大规格。
部署服务时,如果只是偶尔调用,最小实例数设为0能省不少钱。但如果每天都在用,实例数为0导致的冷启动延迟很容易让人抓狂,那就老老实实开一个常驻实例,费用也就一杯奶茶钱级别。
空闲时记得释放资源。训练作业结束之后,平台会自动释放计算资源,但如果你用了Notebook这类常驻资源,做完实验一定要手动停止。我一开始就有一台Notebook忘记关,挂了一整个周末,白白多了几天的费用。
6. 个人复盘与建议
6.1 什么场景更适合上云训练
经过这次完整实践,我的看法是,如果你的项目满足下面几个条件,ModelArts这类云平台会很适合你:模型训练耗时较长,本机算力不足;需要频繁调整数据和参数做对比实验;训练完成后还要部署成服务供外部调用。
反过来,如果你只是临时跑一个几分钟就能完成的小模型,或者代码还处于早期探索阶段,频繁改动幅度很大,那么在本地先把东西跑通会更划算。云上的优势是弹性和规模,不是取代本地调试。
6.2 后续可以进一步尝试的方向
我这次只是走了最基础的训练到部署链路,ModelArts上还有很多能力没有接触,比如自动学习、Workflow编排、模型压缩和边缘部署。下次如果模型要往移动端迁移,我计划试一下平台的模型压缩工具,把模型大小再压缩一波,看看精度损失情况。数据的版本管理和实验追踪我这次也只是简单用了一下,后面打算把一个项目的多次实验系统地记录下来,快速对比不同超参组合的表现。
说实话,这次在ModelArts上踩坑最多的并不是模型本身,而是工程细节:数据怎么传、路径怎么配、环境怎么选、日志怎么看。这类问题在文档里往往只有一句话,但实际做下来每一个都能卡好一阵子。希望这篇笔记能帮你把这些坑提前绕过去。如果你也打算在云上跑训练,建议第一次先拿一个小型数据集完整走一遍流程,再上全量数据。流程顺了之后,云端训练带来的效率提升真的非常明显。