ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

ModelArts模型训练与部署全流程实操指南(零基础拆解)

ModelArts模型训练与部署全流程实操指南(零基础拆解) 第一次打开ModelArts控制台的时候说实话我有点懵。训练作业、开发环境、模型管理、部署上线每一个词都认识但每一个入口应该在哪一步用、和旁边那个长得差不多的菜单有什么区别我完全说不出来。特别是在准备认证考试的那些天教程刷了一堆Demo跑了几个一旦脱离别人的操作步骤让我自己从头规划一次训练和部署脑子还是空的。这篇文章就是我后来把整套流程彻底捋顺之后的复盘总结。不绕弯子从零基础视角出发把ModelArts平台上模型训练与部署这件事拆成三层来讲第一层是它到底是什么、解决什么问题第二层是训练前要准备什么、训练作业怎么配第三层是从训练产物到在线服务这一路有哪些考点和坑。准备面试、备考认证或者纯粹想搞懂这套云端AI流程怎么运转的人都能从里面拿到可以直接用的理解框架。1. 零基础看ModelArts先搞清楚它在整个AI流程里干了什么1.1 传统机器学习从0到1的七步痛点在接触云端AI平台之前我一直觉得训练一个模型是一件听起来高大上、做起来全是脏活的事情。因为从数据到模型中间要经历一条很长的链路第一步是准备机器。你得有一台带GPU的服务器或者本地电脑没有的话训练一个稍微像样点的图像模型就能等到天荒地老。第二步是装环境驱动、CUDA、PyTorch或者TensorFlow版本稍微不对训练一开始就崩。第三步是管理数据集几万张图片要从某个地方读进来放在本地磁盘还是挂载存储路径配置错了直接找不到文件。第四步是跑训练脚本这一步通常几个小时起步期间你得盯着loss曲线防止loss变成NaN或者模型不收敛。第五步是保存模型把训练好的权重文件存到一个固定位置。第六步是部署把模型文件变成一个能处理HTTP请求的服务。第七步是上线后的监控和版本迭代。这七步每一步本身不算特别难但它们合在一起就非常折磨人。尤其对于我这种原本主要写业务代码、偶尔才碰AI的人而言环境问题消耗的时间比模型本身还多。所以我理解ModelArts的方式很简单它就是想把这七步里能标准化、能搬上云的部分全部变成平台能力让你把注意力从伺候环境转移到设计模型和改进数据上。1.2 ModelArts把哪几步搬到了云上用ModelArts的视角重新看上面那七步你会发现它能覆盖中间的大多数环节。数据存储和管理交给对象存储服务来做也就是常说的OBS桶相当于一个无限扩容的云端文件柜。环境配置交给开发环境和训练作业的预置镜像来解决你不需要自己装CUDA平台已经给你准备好了带GPU的Notebook和训练容器。训练执行交给训练作业模块你只需要提交脚本和配置平台负责在GPU集群上调度并拉起来一个容器跑任务。模型保存和版本管理有专门的模型管理模块统一纳管。部署上线有在线服务功能把模型文件发布成一个可以被外部调用的API。所以整个ModelArts的训练与部署主线其实就是写代码写在Notebook或者本地数据放在OBS训练任务在训练作业里跑产物在模型管理里登记最后部署成在线服务。这条主线理解清楚之后控制台里那些密密麻麻的入口就不再是威胁了它们只是这条主线上某个环节的专属工具。1.3 一张表看懂核心组件与考点对应关系平台模块对应传统流程主要作用零基础容易混淆的点开发环境写代码、调试提供带GPU的Notebook可在线写代码、做实验不是训练任务的最终跑批环境适合调试小数据数据管理数据集整理管理OBS中的数据集版本、标注信息它不存数据数据在OBS里这里只是入口训练作业训练模型提交训练脚本在GPU集群上跑训练任务需要自行提供训练脚本和指定输入输出路径模型管理保存模型把训练产物登记为模型版本配置推理脚本不是简单的权重文件仓库还包含推理代码部署上线提供服务把模型发布成在线服务供应用调用可以选择不同规格的实例涉及计费这张表我建议零基础的读者反复看几遍因为很多考试题出的就是某某功能该用哪个模块处理这一类送分题。但送分的前提是你能把模块的职责边界说清楚。2. 训练开工前的准备工作对象存储、数据集与运行规格2.1 对象存储为什么会成为训练的后勤仓库ModelArts里的训练任务跑在集群节点上这些节点本身是临时的任务一结束容器就被回收了。所以你的数据不能只放在训练机器的本地磁盘里下次任务重新调度到另一台机器上数据就没了。这就是为什么需要对象存储也就是OBS桶来做中转数据先传到桶里训练任务启动时通过配置好的数据路径从桶里读取。打个比方对象存储像是一个公共仓库训练任务的每一台机器都只是临时来仓库取料的工人取完料做完活仓库还在工人走了也不影响下次继续取料。这个理念理解之后你再看训练作业配置里的数据来源和输出路径两个选项就非常清晰数据来源指向仓库里你放原始数据的位置输出路径指向仓库里你希望模型文件被写回的位置。实际使用中有个很容易踩的坑就是权限配置。你创建了OBS桶、上传了数据但训练任务启动时却报AccessDenied之类的错误。原因通常是训练作业运行的角色没有读取该桶的权限。建模的时候需要确保训练任务使用的委托或者凭证对桶至少有只读权限对输出目录有读写权限。别小看这一步我见过不少人在桶权限上耗掉大半天最后只是加了一条授权策略。2.2 数据集格式与上传这是零基础最容易忽略的一环训练脚本不关心你的数据集是从哪个平台上传的它只关心数据长什么样。所以在往OBS里传数据之前你得想清楚脚本打算怎么读。常见的做法有两种。第一种是简单的目录分类法适合图像分类任务在OBS桶里建一个根目录下面按类别建子文件夹每个子文件夹放对应类别的图片。第二种是标注文件法适合目标检测或者更复杂的任务图片统一放在一个目录标注结果放在一个JSON、XML或者文本文件里脚本解析标注文件得到每个目标的框和类别。我强烈建议零基础的读者先做一个小型的、只有几十张图片的数据集来打通流程。原因很现实用全量数据集去测试训练配置一旦报错日志刷得又快又多你根本分不清是数据问题、路径问题还是脚本问题。小数据集跑一遍确认从OBS读取、进模型、出模型、写回OBS的完整链路都通再换大数据集跑正式任务心里会踏实很多。上传数据本身没什么技术含量控制台拖拽、用对象存储的客户端工具批量上传都可以。我的习惯是在桶里面把目录结构规划成分层清晰的样式比如dataset/classification/train/class_a这样的层级而不是把所有图片一股脑丢在桶根目录下。层级清楚后面写训练脚本的时候环境变量里填路径就不用猜了。2.3 训练规格的选择思路与计费避坑零基础面对训练规格第一反应往往是选贵的贵的跑得快。这句话本身没错但容易造成两个问题一是配额不够用二是账单涨得飞快。规格列表里通常能看到CPU规格和GPU规格GPU下面还区分不同的卡型。如果是第一次跑Demo或者实验性任务我建议先用CPU规格或者最基础的GPU规格验证代码。模型本身不大、数据量又少的时候CPU也能在几分钟内跑完没必要一上来就申请最强的卡。等确认脚本没有逻辑错误、训练能稳定收敛再按实际数据量和模型复杂度升级规格。计费这块是很多初学者最肉疼的地方。训练作业结束之后如果你忘了检查有没有遗留其他资源比如调试时的Notebook实例、部署后没用的在线服务它们都会持续计费。我后来养成了一个习惯所有实验类任务都提前在控制台确认实例状态训练完立刻看训练作业列表的状态验证模型只要部署过一次就及时把不需要的在线服务删除。这里有个小技巧Notebook实例即使关闭了页面只要没有执行停止操作底层资源依然会计费所以停止操作一定要主动去控制台点不要以为关了浏览器就万事大吉。3. 训练作业全流程拆解脚本怎么写、参数怎么配、结果怎么收3.1 一个标准训练脚本的基本骨架ModelArts上的训练作业虽然跑在平台环境里但它本质执行的还是你提供的脚本。对零基础来说最友好的一点是脚本不需要用到什么神秘API它就是一段普通的Python代码按照约定从环境变量里读取输入输出路径把训练循环写好最后把模型保存到指定目录即可。下面是一个PyTorch图像分类脚本的最小骨架我习惯把路径全部通过环境变量传入这样换环境就不用改代码import os import torch from torch import nn from torch.utils.data import DataLoader def main(): # 从环境变量读取路径 data_dir os.environ.get(DATA_PATH, ./data) output_dir os.environ.get(TRAIN_PATH, ./output) train_dataset load_my_dataset(data_dir) # 自定义数据加载 loader DataLoader(train_dataset, batch_size32, shuffleTrue) model build_simple_model() optimizer torch.optim.Adam(model.parameters(), lr1e-3) criterion nn.CrossEntropyLoss() model.train() for epoch in range(10): for batch_x, batch_y in loader: optimizer.zero_grad() logits model(batch_x) loss criterion(logits, batch_y) loss.backward() optimizer.step() print(fepoch {epoch} finished, loss: {loss.item():.4f}) os.makedirs(output_dir, exist_okTrue) torch.save(model.state_dict(), os.path.join(output_dir, model.pth)) if __name__ __main__: main()这段脚本没有平台特定的内容。真正和平台打交道的地方是在创建训练作业时指定怎么把OBS路径翻译成容器里的环境变量。例如平台会允许配置两个关键位置的映射数据输入路径把OBS桶的某个目录映射到容器里的DATA_PATH训练输出路径把OBS桶的某个目录映射到容器里的TRAIN_PATH。脚本通过在启动时读取os.environ就能拿到这些路径做到完全不用写死。理解了这个映射机制很多东西就豁然开朗了为什么训练作业里要填OBS路径因为平台要替你完成从仓库取料这件事。为什么要分输入输出两个映射因为输入只有读权限就够了输出需要写权限职责分清楚更安全。3.2 创建训练作业时的几项关键配置在控制台新建训练作业时页面上会有一堆选项。零基础容易一头扎进代码和数据里忽略了几个和代码同等重要的配置项。比较关键的几项我整理如下算法来源通常选择自定义然后指定镜像或训练脚本。如果是第一次跑优先用平台预置的基础镜像Python和框架版本都已经配对好不会出现本地环境正常、平台环境缺包的情况。数据来源就是上文说的输入映射。填OBS路径时要注意路径的写法有些同学把桶名后面多跟了一层不存在的目录训练一启动就提示找不到文件。训练输出模型保存的OBS路径。这里要保证路径所在的桶和训练作业使用的是同一个区域的资源跨区域访问速度慢且容易出权限问题。运行规格按第二节说的思路选验证实验用小规格正式训练再升级。日志很多初学者会忽略保存日志这个开关。建议打开并把日志保存到指定的OBS目录因为训练作业运行中的控制台日志一旦任务结束页面可能不再保留详细输出存在OBS里的日志才能长期留下来供排查使用。创建任务之后页面一般会显示一个任务列表状态从初始化变成运行中最终变成成功或者失败。这时候最忌讳的事情就是一直刷新页面干等着。我一般会打开日志边等边看训练进度如果几十秒钟都没有任何输出通常说明路径有问题。3.3 任务跑起来之后怎么确认它真的在学东西训练作业跑起来不等于模型一定正确。我见过不少情况是任务状态显示成功但打开模型一看参数全是随机的因为训练循环里的数据读取部分写错了模型压根没利用训练数据。要确认训练真的有效最可靠的办法是看训练日志里的loss如果loss随着epoch推进持续下降说明梯度在正常流动训练是有效的如果loss一直不动或者上下乱跳你就要检查学习率、数据加载顺序、标签对齐这些基础问题。还有一个容易被忽视的点是脚本里的print输出什么时候能看到。平台日志通常是持续刷新的但输出有缓冲建议在关键位置调用print之后加上flushTrue否则你会看到日志长时间没有更新误以为任务卡住了。任务成功结束后去训练输出目录看一眼确认模型文件确实生成了。有些人只在日志里看到save model success就放心了但如果你没打开写入OBS这个映射的具体权限脚本可能在报错之后被平台判定为失败也可能明明写了但没写进预期的目录。宁可多花一分钟去OBS界面核一眼也别等到部署阶段才发现产物不存在。4. 从训练产物到在线服务模型部署与推理调用的完整流程4.1 模型从训练输出到可服务需要补什么训练作业结束了模型文件躺在OBS里但这并不等于可以对外提供API。因为在线服务需要的至少三样东西模型权重文件、加载模型并处理请求的推理脚本、描述服务和模型元信息的配置文件。很多初学者会把模型管理和在线服务混在一起理解。模型管理更像是登记把模型文件、推理脚本、运行环境整理成一个版本化的对象部署上线则是把这个登记好的对象真正分配到计算资源上运行。想清楚这个区分之后考试中常见的先创建模型再部署服务其实不是流程冗余而是它们本来就应该分开。推理脚本是整个部署环节的灵魂。训练脚本关心的是怎么把loss降下去推理脚本关心的是给定一个输入怎么输出结果。以图像分类为例推理脚本通常需要实现两类方法一类是模型加载逻辑负责把权重文件读进来、构造完整的模型结构另一类是请求处理逻辑把收到的HTTP请求体里的图片解码成张量做和训练时相同的数据预处理然后经过模型前向计算得到类别再把结果序列化成JSON。这里有一个非常典型的错误推理脚本里的预处理方式和训练脚本不一致。比如训练时图片统一缩放到224×224推理时却忘了缩放模型可能不会报错但准确率会变得很差。所以如果你自己编写推理逻辑一定要回头跟训练脚本里的数据预处理保持一致。4.2 创建在线服务时的参数细节在ModelArts上把一个模型发布成在线服务核心配置无非是以下几项选择模型及版本、指定计算规格、设置实例数、确认访问方式。计算规格的选择和训练作业有一些类似但又有点不同的逻辑。推理对实时性有要求如果业务请求量不大选择低一点的规格就够如果后续有大流量压进来再调整实例数做扩容。平台一般支持配置多个实例请求会自动分发到各个实例上实现一定程度的负载均衡。访问方式方面通常有公网访问和内部访问两种类型。如果只是自己测试公网访问最方便生成一个HTTP调用地址加上Token鉴权就能发起请求。但要注意暴露公网后你就得想清楚鉴权怎么管理Token相当于钥匙别把它硬编码在前端代码里。创建在线服务后平台一般会提供一个健康检查机制。我实测下来部署的启动过程通常需要几分钟因为要拉镜像、加载模型、启动推理进程。在此期间服务状态可能会显示创建中或者部署中此时不要急着发请求等状态变为运行中再测否则会得到超时错误。4.3 推理请求怎么发、结果怎么解析部署完成后测试服务最直接的办法是使用控制台自带的测试功能。把一张测试图片传上去平台会调用推理脚本并展示返回结果。但实际项目中更多是通过API来调用这时候你需要构造带鉴权信息的HTTP请求。流程大致是先获取Token然后把它放在请求头里body里带上待推理的数据提交到服务地址。下面是一个简单的调用示例import requests import base64 # 读取本地图片 with open(test.jpg, rb) as f: image_data base64.b64encode(f.read()).decode() # 构造请求体字段名需与推理脚本中定义的一致 payload { image_base64: image_data } headers { Content-Type: application/json, X-Auth-Token: your_token_here } resp requests.post(https://your_service_endpoint, jsonpayload, headersheaders) print(resp.status_code) print(resp.json())这一段属于拿来就能用的实战模板。真实的字段名和请求地址以服务详情页展示为准但思路就是这样图片做base64编码传过去推理脚本解码后处理最终以JSON返回类别、置信度这类信息。你如果发请求之后返回了404或者500不用慌有一半的概率不是代码问题而是服务还没起来或者请求地址末尾漏了路径。5. 针对考点的复盘高频题型、易错点与答题思路5.1 概念辨析类题目概念题在考试里占的比例不低而且往往是最容易拿分也最容易丢分的部分。它考的是你对平台能力的准确理解而不是你背了多少个功能按钮的名称。一类典型题目是把几个容易混淆的模块放在一起让你判断某个操作属于哪个模块。比如将训练得到的模型文件发布为一个API服务应使用哪个功能答案是部署上线。再比如标注好的数据集保存到哪里答案是对象存储服务而不是数据管理模块数据管理更像是数据集的组织与展示入口。还有一类是考平台定位的。AI开发平台本身属于PaaS范畴它向下帮你封装了底层的GPU资源调度、网络、环境配置向上提供模型训练、部署等能力。理解这个定位之后遇到资源池由谁负责用户需要关心的层次是哪一层这类题目时你就知道标准答案的倾向平台管底层用户管数据和模型。我的建议是不要只看官方功能的介绍文档而是把核心流程亲手走一遍。你会发现模型训练和模型部署在页面入口上的区别对象存储和开发环境在数据管理上的边界这些在实操中早已根深蒂固考概念题时几乎不需要额外背诵。5.2 流程操作类题目流程题很少考点击哪个按钮而是考在多步操作中哪一步应该先做、哪一步应该后做。最常见的坑就是把数据上传和数据集创建的顺序搞反或者把模型创建和服务发布的顺序弄混。正确的思路是数据先到OBS训练作业才能读取训练成功之后训练产物在OBS输出目录中模型管理创建模型版本时选中该输出目录并配套推理脚本模型创建成功后再部署在线服务服务运行中才能调用推理。这个链路在任何试题里都应该保持稳定。哪怕题目换个场景比如使用预置算法或者订阅算法前面两步可能会被简化但数据准备→模型创建→服务部署这个骨架不会变。另一种流程题会包装成排错题给你一段操作记录让你指出哪一步出错了。这类题只要你在心里有一套正确的顺序清单逐个对照基本都能看出来。我复习的时候给自己编了个口诀数据先入桶训练再起步产物登记好最后才发布。口诀很土但管用。5.3 故障排查类题目故障题是零基础最头疼的因为它考的不仅是知识点还有排查思路。不过考试能出的故障其实很有限无非是下面几类找不到文件、权限不足、资源不足、模型加载失败、服务调用出错。遇到训练作业立即失败的题目优先想数据路径和权限问题因为这两个问题往往在任务刚启动时就暴露。遇到服务无法访问的题目优先想服务状态和鉴权配置因为你不会在刚部署完成就去查模型推理逻辑有没有写错。排查顺序在考试中也是一种得分点有些题目会要求你按最先检查什么来排序牢记从外到内、从配置到代码的顺序先看路径和权限再看资源和规格最后才怀疑代码本身。这种思路同样适用于面试中的场景题。当被问到模型部署后返回504怎么办你如果能从服务规格、实例状态、网络超时配置几个方向逐一分析就已经展示出比单纯背API好得多的工程素养。6. 我在实操中反复踩过的四个坑以及完整的排查链路6.1 数据路径写错训练一开启就直接退出第一次提交训练作业时我满心期待地等了十分钟任务状态还停留在初始化然后突然变成失败。打开日志一看错误信息是找不到某个目录。原因就是我在数据来源里填的OBS路径多写了一个层级桶里实际上没有那个目录。这并不是一个愚蠢的错误恰恰是零基础最常犯的错误。排查链路其实很短确认OBS里实际路径的层级对照作业配置里填的路径一个字一个字比对。这里我建议不要相信肉眼直接复制粘贴因为OBS路径常常包含很长的字符串差一个字符就会前功尽弃。从那以后我养成了一个习惯提交训练作业前先在OBS的网页界面上打开一遍数据目录复制地址栏里真实的路径填进去。6.2 配额不足与资源配置冲突训练任务创建成功后等了一会儿还在排队页面提示资源不足。这种问题在高配置规格上尤其常见因为平台的资源池是有限的不是所有规格随时都有空余。遇到这种情况先别急着加钱换更大的规格而是看看当前规格的选择是不是超出了账号的配额限制。如果只是临时测试换成次一档的规格往往立刻就能调度。还有一个隐蔽的点是你并发地创建了好几个训练作业或者Notebook实例每个都在申请GPU资源累计起来就超过了配额。排查时把正在运行的资源全部列出来停掉不需要的再重新提交任务基本都能解决。6.3 模型加载阶段的常见报错部署在线服务时我遇到过服务一直无法进入运行中的状态。查看日志后发现问题出在模型文件和推理脚本要求的模型结构不匹配。训练时我保存的是整个模型的状态字典推理脚本里却构造了一个不同层数的模型结构加载权重时自然报错。这种问题在训练脚本和推理脚本不是同一个人写的场景中特别常见。排查时首先要做的不是反复改网络结构而是确认权重文件里的键名和推理脚本里构造模型的键名是否一一对应。你可以加载文件打印一下键名和推理脚本模型的state_dict键名对比很快就能定位是哪里差了一层。记住一个原则训练时的模型定义代码和推理时的模型定义代码必须完全一致除非你导出成统一的中间格式再做转换。6.4 忘记释放资源导致持续计费这不算报错但比报错更让人难受。我的某次实验结束后部署了一个在线服务用来演示演示完毕直接关了浏览器。过了几天一查账单多出来的费用都来自那个还在运行的在线服务实例。资源没有关闭浏览器就自动释放这回事所有实例只要没有被显式停止或删除都会持续存在并计费。我的排查习惯是从计费后台的账单反查资源找到高额项目然后一个个去对应的控制台页面确认实例状态关掉之后再回来看账单是否停止增长。这条链路虽然没什么技术含量但非常有效。现在我在每次实验结束前都会做一次资源大扫除把Notebook实例、训练作业的存量、在线服务全部过一遍该停的停、该删的删。最后分享一个我后来保留的操作习惯每次正式提交训练作业之前一定先拿一小批数据跑一个最小化任务确认路径、脚本、输出三个环节全通再放开手脚跑大任务。这套习惯帮我省下的时间和成本远超我当初为搞懂平台而花掉的功夫。零基础学ModelArts怕的不是不懂概念而是没亲手走通一遍流程就去钻研高深的优化技巧。把这条训练与部署的主线走通后面的路自然会顺很多。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进