没想到今年一个项目把我彻底推进了云上训练之前我一直在本地机器上跑模型数据集、环境、GPU驱动全靠自己折腾。项目要求基于华为云 ModelArts 完成模型训练与部署把训练结果变成一个在线的推理服务我一开始还觉得“这不就是把代码放到云上跑嘛”真动手后才发现自己想简单了。ModelArts 并不是一个大号的训练脚本托管网站而是一整套从数据管理、算法选择、训练调度到部署发布的工作流平台。这篇笔记把整个流程重新过了一遍记录我自己踩过的坎、理清的思路和最后沉淀下来的执行细节适合第一次接触云上 AI 平台、想完整走通训练和部署闭环的人参考。顺便说一句别指望这是一份官方文档的目录整理。官方文档写的是“应该怎么做”我这里更想记录的是“实际做的时候什么事情会卡住”。比如数据路径填错了会怎么样、训练日志要看哪几行、模型包结构为什么必须在训练阶段就想好。这些细节不写在文档里但确实决定你能不能把项目做顺畅。1. ModelArts 不是“一个训练网站”它是一个云上实验体系1.1 我最初的理解为什么是错的我最早看到 ModelArts 控制台觉得有点像“页面租机器”你上传代码点一下训练等结果再到页面上去部署。这个理解不能说全错但它把核心逻辑简化过头了。实际的云上 AI 平台真正要解决的是实验的可复现、可追踪、可沉淀数据在对象存储里训练代码在代码包里算力资源按需伸缩中间产物和最终模型都在云端统一管理。你想跑一个项目不是只“跑一次模型”而是要在一个平台上反复调整、重跑、对比然后把最终那版模型稳定地发布出去。我在本地训练时的习惯是数据放在/data目录代码和权重放在一起跑完直接pickle.dump到本地。这套做法在 ModelArts 上完全不适用。云上训练作业执行完之后容器会被销毁所有没有传到对象存储的文件都会消失。你必须在代码里把模型权重写进指定的输出路径再由平台自动同步到 OBS 桶里。这个思维转变比学平台按钮还重要。1.2 快速熟悉平台上这几个不同入口第一次打开 ModelArts 控制台我一度看懵了界面上有自动学习、训练作业、Notebook、AI Gallery、模型管理、部署上线每一个都能点但不知道哪个才是我该用的。我最后把它们拆成四类心里就清楚了入口适合场景我的使用建议自动学习只有数据想快速验证能不能解决不想写训练代码适合给需求方出效果样张也适合先检验数据质量训练作业自己有代码要精确控制模型结构、训练流程、资源规格项目正式推进后最常用的入口Notebook交互式调试、数据分析、边改边跑我在数据清洗和调试小样本时用正式训练还是交回训练作业AI Gallery找预置模型、算法、数据集常见算法可以直接订阅减少重复造轮子刚开始我最容易犯的错是高频用 Notebook 跑完整训练。Notebook 适合探索但它本质是一台常驻的云主机你开着它就计费而且它没有训练作业那样完善的日志、指标和任务生命周期管理。正式的大批量数据训练还是应该打包成训练作业提交。1.3 把零散入口串联成一条主线我在笔记里给自己画了一条主线OBS 数据 → 数据标注/校验 → 训练作业 → 模型注册 → 在线部署 → 推理调用。这条线从头到尾都围绕同一个项目的产物流动每一步都会产生一个可以在控制台里管理的资产例如数据集版本、训练任务、模型版本、服务版本。理解了这条主线后很多页面上的重复概念就通顺了。比如“模型管理”并不仅仅指你训练好的权重文件它还包含了推理所需的代码和配置“在线服务”也不是单纯启动一台服务器它会把你的模型包部署成一个带 API 地址的推理服务并且可以配置实例数、规格、安全组。云上平台的本质是帮你把“训练”和“部署”变成两段可管理、可重复的过程。2. 训练前的地基OBS 数据组织与实验命名规则2.1 先建桶再谈模型OBS 桶在 ModelArts 里的角色类似本地工作站的硬盘。一般来说你要先规划一个桶再在桶里为不同用途建目录。我第一次是直接一股脑把所有照片传到桶根目录结果后面做数据标注、训练输出、模型注册时路径混乱到无法直视。后来重构成下面的结构才顺了obs://my-project-bucket/ ├── data/ │ ├── raw/ # 原始数据只读 │ ├── processed/ # 清洗、裁剪、归一化后的数据 │ └── datasets/ # ModelArts 数据集导入路径 ├── code/ # 训练脚本和配置文件 ├── results/ │ ├── train_output/ # 训练作业输出 │ └── logs/ # 日志归档 └── model/ └── release/ # 最终发布用的模型版本这样做的理由很简单训练作业的输出路径、模型注册的路径来源、后续版本回溯全部依赖清晰稳定的目录结构。你如果连目录都没有固定规范跑三五次之后就不知道某个output里是哪次实验的产物了。目录命名尽量用英文小写加下划线别带空格和中文否则在云端路径解析时容易给自己惹麻烦。2.2 数据整理、标注与校验的隐藏工作量在 ModelArts 中数据整理不只是“把文件传上去”。你可以在数据管理里创建数据集支持图片分类、物体检测、文本分类等标注类型。但我实际测试下来平台能帮你提升效率却不能替代你对数据的理解。数据进训练之前至少要确认三件事类别是否均衡、样本是否有噪声、训练集和验证集的分布是否一致。我在项目里做的是图片分类一开始类别 A 有 8000 张类别 B 只有 300 张模型训练出来准确率整体看着还行但看混淆矩阵就露馅了B 类几乎全错。后来重新整理了样本做了一些数据增强才把指标拉回来。这个步骤如果省了后面的调参基本是在帮垃圾数据打工。数据管理里的“数据校验”功能可以帮你检查图片是否损坏、格式是否合法但这些只是下限真正的数据质量把控还是得人工介入。2.3 实验清单从第一次训练前就建立这是我这篇笔记里最想保留的习惯之一。第一次跑训练前我建了一个实验追踪表把自己每次提交训练作业时设置的关键信息都记下来。表格很简单但作用非常大实验编号数据版本模型/框架batch_sizelearning_rate资源规格训练时长最优准确率模型输出路径001v1.0ResNet18321e-3GPU 单卡04:15:000.821obs://.../001/002v1.1ResNet18641e-3GPU 单卡03:50:000.836obs://.../002/一开始多写两行字后面少两个小时翻控制台记录的时间。尤其是学习率这类超参控制台日志里也可能找到但要翻半天远不如自己随手记下来清晰。另外实验记录里一定要写“这版改了什么、为什么改”为了下次回来时不用看代码 diff 猜思路。3. 训练作业的创建与脚本改造参数、路径、资源三件套3.1 算法与框架选型的取舍ModelArts 训练作业支持多种来源的算法我把它归纳为三条路内置/订阅算法、自定义代码镜像、自带训练脚本。内置或订阅的算法最大的优势是开箱即用很多经典模型的代码已经封装好你只需要配置数据集路径和超参数劣势是你不好改内部结构出了问题比较难深入排查。自带训练脚本更灵活但要求你对代码组织、依赖安装、路径处理都有一定把握。我这次因为模型结构要按需求微调选了自定义训练脚本方式。过程大概是把训练代码和依赖声明打包到代码目录在训练作业中指定启动文件和运行参数随后 ModelArts 会拉取指定规格的计算资源执行任务。如果你有特殊依赖比如自己编译的算子或者特殊的库建议用镜像方式把整个运行环境固化下来否则每次作业都要在线安装依赖既慢又不稳定。3.2 训练作业参数到底在填什么创建训练作业时表单里会有一堆字段第一次看容易头皮发麻。我整理了一下实际上就三组信息第一组是代码位置和启动方式代码目录选哪个 OBS 路径、启动文件是哪个 Python 脚本、运行参数怎么传。第二组是数据与产物位置训练数据集输入、训练输出路径。第三组是资源规格CPU 还是 GPU、单卡还是多卡、用哪个资源池。这三组里最容易填错的是运行参数。平台一般不会帮你自动把数据路径映射进代码你需要在运行参数里显式传--data_dir、--output_dir之类。后面脚本里再解析这些参数而不是在代码里写死绝对路径。我自己的习惯是每个路径都对成双保险既在控制台参数里填好又在脚本开头打一行日志确认当前拿到的路径避免脚本用了一个意外的不良根目录。还有一点要注意训练作业的任务名称和实验记录不要随缘命名。我建议格式带日期和实验编号例如train_20250314_001这样在控制台列表里扫一眼就能定位跟前面的实验表也能对上。3.3 脚本改造让训练任务能在云端被“调度”我在本地脚本最核心的训练代码放到云端时改掉了两块。第一块是路径全部改成参数化。不依赖外部命令时也不再用os.getcwd()指望当前目录一定是什么而是根据平台传入的参数拼路径。第二块是模型保存一定要把权重写到输出目录。示意的骨架是这样的import argparse import os def parse_args(): parser argparse.ArgumentParser() parser.add_argument(--data_dir, typestr, requiredTrue) parser.add_argument(--output_dir, typestr, requiredTrue) parser.add_argument(--batch_size, typeint, default32) parser.add_argument(--learning_rate, typefloat, default1e-3) return parser.parse_args() def main(): args parse_args() print(f[INFO] data_dir{args.data_dir}) print(f[INFO] output_dir{args.output_dir}) # 训练逻辑... # 得到 model 后必须保存到 args.output_dir 指向的目录 os.makedirs(args.output_dir, exist_okTrue) model_path os.path.join(args.output_dir, model.pt) # 这里执行实际的模型保存 print(f[INFO] model saved to {model_path}) if __name__ __main__: main()你别看这个代码简单它恰恰是云上训练能否跑通的命门。本地训练时很多人会顺手把模型权重存到/home/user/model.pt在云端如果这个路径不在输出目录下容器一旦销毁模型就没了。拿不到模型后面部署环节根本无从谈起。另一个容易踩的是打印日志不清理有些框架每隔几步就刷屏日志文件膨胀后会影响查看效率我在会长期运行的作业里会控制日志频率把每个 epoch 的指标单独打成一列便于后面截取分析。4. 训练过程监控与第一次排障日志、指标和资源可见性4.1 从控制台日志开始排查训练作业跑起来之后我第一次遇到的是启动报错。打开训练日志那一屏里面堆了几千行依赖导入信息滚到最底下才看到一行ModuleNotFoundError。这个场景太典型了所以我建议所有新手都把日志页面当成第一现场但不要从头看直接搜索Error、Traceback、Exception。一旦定位到报错十有八九是下面的原因之一依赖缺失训练作业的启动环境没安装你脚本需要的库路径不存在代码里用了一个 OBS 目录但那个目录在桶里根本不存在数据格式和代码预期不一致图片尺寸、通道数、标签格式等我在本地跑脚本时依赖都装好了云端是干净环境所以第一类问题最容易翻车。解决思路是在代码目录里把依赖声明写全并且在训练脚本最开始加一个print输出环境里几个关键包的版本便于后面核对。4.2 用指标曲线判断是否要提前停止训练过程中ModelArts 控制台一般会展示一些实时指标比如 loss、准确率。我第一次盯着准确率看发现它一直在一个区间小幅抖动还以为是模型出了问题后来才意识到是学习率设置偏高再加上没有做学习率衰减导致后期没法收敛。这个经验让我回去补做了两件事一是设置合理的轮数和早停机制比如验证集指标连续多个周期不涨就停止二是把每次验证集的指标也打到日志里不要只看训练集表现否则过拟合很难发现。如果你用的是内置算法平台通常会自带一些指标汇报能力如果是自定义训练脚本强烈建议在代码里接上平台支持的指标上报方式比如通过log_metrics或写入指定日志关键字。这样一来训练结束后你能在作业详情页看到完整的指标曲线而不是只能靠人工翻日志统计。我试过之后评估两版实验结果的速度快了不少。4.3 配额、计费与欠费中断不能忽略的云端边界这节内容是我在学习过程中最意外的部分。本地训练不会突然因为没有 GPU 配额而中断但云端会。我第一次提交作业时资源规格选得不合理加上资源池排队时间被低估白白耗了不少时间。用云平台一定要提前确认三件事当前账号的配额是否足够、目标资源池是否可用、计费模式是按需还是包周期。训练作业中断最心疼的是时间和进度。如果训练很久才发现资源调度有问题前面的时间全部浪费了。实际执行时我建议先提交一个小批量数据、小模型结构的作业把链路跑通再上全量数据和完整模型。这就像做饭先试吃一口不要等到整锅上桌才发现盐放多了。费用也一样先确定一个预算上限避免实验无限跑下去。5. 部署在线推理服务的完整链路5.1 模型必须先注册到模型管理训练作业跑完拿到了输出目录下的模型文件还需要在 ModelArts 的模型管理里“登记”一下才能在部署环节选择它。这一步我最初以为只是选一个 OBS 路径当模型来源实际上它更贴近“把模型转成可部署的推理包”。你要保证模型文件、推理代码、依赖配置都在合适的位置并且按平台要求的组织方式放到模型目录下。模型包结构不要等到部署前才想明白。我经历过一次重新整理推理包的痛苦因为训练阶段只保存了权重文件没想清楚推理时还需要一套加载权重、做前处理、执行推理、整理返回结果的脚本。最后反工了一次把代码补在模型目录里才正常部署起来。正确姿势是训练阶段就在代码目录里预留好推理文件和配置声明这样训练输出和部署输入才能无缝衔接。5.2 在线服务的创建与调用姿势模型注册好后进入“部署上线”选择在线服务配置资源规格和实例数等待平台完成部署。部署成功后你会拿到一个 API 调用地址。我在这一步也有个认知转变在线服务本质上是一个被你调用的 HTTP 接口不再是“你 ssh 上去执行 python 脚本”的机器所以后续关心的应该是请求格式、响应格式、鉴权方式而不是服务器上的目录和进程。调用在线服务的代码如下具体地址和认证方式以你控制台生成的信息为准。这里给一个最简单的 Python 调用示例帮助你理解整体套路import base64 import json import requests # 假设你已经有 # api_url https://modelarts-infer.example.com/v1/infers/xxxx # token 从认证流程获取的临时凭证 def encode_image(image_path): with open(image_path, rb) as fp: return base64.b64encode(fp.read()).decode() payload { image: encode_image(test.jpg), # 如果模型还有其他输入参数继续在这里补充 } headers { Content-Type: application/json, X-Auth-Token: token, } response requests.post(api_url, headersheaders, datajson.dumps(payload)) if response.status_code 200: result response.json() print(推理结果:, result) else: print(调用失败:, response.status_code, response.text)这里特别提醒认证 token 是有有效期的不要硬编码到长期运行的服务里。如果是有多个调用方的场景最好先了解平台支持的正式鉴权方案再按那个方式统一对接。我在试错阶段图方便直接用了控制台里临时生成的凭证结果第二天再跑脚本就报认证过期让我折腾了一阵子才反应过来。5.3 性能与稳定性调整的经验在线服务部署成功了不代表万事大吉。我测试时发现单个实例在高并发下延迟明显上升这时候需要看两样东西推理的平均延迟和实例的算力占用。一般来说优化顺序是先确认推理代码有没有不必要的重复计算再考虑更小的模型或量化手段最后再决定加实例还是升级规格。盲目加实例可能能扛住压力但费用会直线上升。还有一类问题是“冷启动”服务一段时间没有请求后第一个请求会明显变慢。云端的推理实例数量会根据流量配置扩缩容缩容后新起的实例要加载模型权重首请求自然慢一些。如果你的场景要求低延迟建议保持一个最小实例数常驻而不是等流量进来了才扩容。这些参数在服务配置里都可以调整重点是根据你的真实流量特征去定而不是拍脑袋选最大规格。6. 我把这套流程沉淀下来的检查清单学习笔记的收尾6.1 一个能直接套用的迭代检查单学完整个流程后我把容易出错的地方固化成一份检查单每次提交训练作业前都过一遍。这份清单在这里直接分享可以当成你的 List。数据是否已整理到指定 OBS 目录训练集、验证集有没有混淆数据标签是否已校验类别分布是否有严重不均衡代码目录和启动文件是否指向正确位置脚本是否通过参数接收数据路径和输出路径而不是硬编码超参数是否需要修改实验表是否已记录本次修改原因输出路径是否和上次实验分开避免覆盖历史模型资源规格是否符合数据量资源池是否可正常调度训练日志里的关键信息是否完整能否追溯每轮指标模型注册前推理脚本和前处理逻辑是否已经在代码里验证过部署时是否明确实例数、规格、API 鉴权方式每一条看着都很基础但实测下来大多数返工和排障都藏在这些基础项里。你以为自己肯定做对了偏偏漏了其中一个最后卡住的还是它。6.2 我最后悔没早点想明白的三件事第一件事模型部署不是训练结束后的“附加任务”而是从项目第一天就要想清楚的目标。如果训练阶段就规划好模型输出格式、推理脚本、依赖清单后面的部署会快非常多反过来等模型跑完才开始补推理代码等于把项目总时长拉长了三分之一。第二件事云上 AI 平台的计费、配额、排队这些运维因素在本地开发时完全无感但上云后就是实打实的成本和时间约束。把资源调度规则提前摸清楚比多调几次参更重要。我是吃了好几次“作业排队很久才发现选错规格”的亏才把这个意识补回来的。第三件事笔记不仅是写给未来自己看的它也是梳理思路的工具。每次我把一段复杂操作写成可复用的检查单下一次执行的速度和质量都会明显提高。这篇学习笔记最值钱的地方其实不是我跑通了一个项目而是把跑通的过程抽离成了一套可以复制到下一个项目的方法。以后如果再有人问我“基于华为云 ModelArts 完成模型训练与部署要注意什么”我大概率会回他一句话别急着点训练按钮先把数据路径、实验清单和推理包结构都想好。数据地基稳了训练作业按部就班部署自然水到渠成。这套流程跑顺之后你回去再看官方文档会发现很多原来觉得抽象的名称突然都有了具体的落点。