
我花了大概两周时间把华为云ModelArts的完整链路跑了一遍从上传数据集、跑训练作业到把模型注册上线最后部署成一个能接收图片请求的在线推理服务。这篇学习笔记把中间踩过的坑和关键路径都记了下来目标是让后来的人少走弯路也当给自己留一份复现手册。在真正动手之前我一直有个疑问训练代码不是本地也能跑吗为什么还要上云把整个流程走完我的答案是——如果模型一次训练要几个小时中途还伴随着环境依赖、数据管理和部署上线云端平台带来的收益就会非常明显。ModelArts刚好是那种把训练、管理、部署揉成一站式平台的产品很适合个人学习和中小团队做实验验证。如果你也是刚接触建议先别急着写代码把下面的准备步骤跟一遍后面会省掉很多“找不到文件”的烦恼。1. 先把思路理清楚为什么选择云端训练ModelArts能省掉哪些事1.1 本地训练的真实困境我自己在本地跑过一段时间的模型训练最开始用的是普通笔记本电脑。数据集几个G就足够让磁盘报警训练一个不算大的分类网络CPU要跑几个小时GPU根本谈不上。后来好不容易找到一张旧的独立显卡又折腾驱动、CUDA版本、深度学习框架版本好不容易把环境装好发现显卡显存只有4G稍微大一点的batch size直接OOM。这种体验在个人开发者里太常见了。做模型训练的人很大一部分时间不是花在模型设计上而是花在环境配置、硬件升级这些和算法无关的事情上。云端平台解决的正是这部分问题。ModelArts本身不取代你的算法设计能力它提供一个开箱即用的计算环境你提交训练代码平台负责拉镜像、分配资源、跑任务、收集日志。对个人来说最大的变化是“训练”这件事从“折腾设备和环境”变成了“提交任务和看结果”。1.2 ModelArts在整条链路里的位置ModelArts并不是只有一个训练功能。它覆盖了从数据准备到模型上线的四个环节数据层面可以创建数据集、做标注、直接管理OBS对象存储里的训练数据开发层面提供代码开发环境也支持使用预置算法直接训练训练层面可以提交训练作业支持PyTorch、TensorFlow、MindSpore等主流框架部署层面模型注册之后可以发布成在线服务、批量任务或者边缘推理。我这次重点跑的是其中一条路线自定义训练脚本 → 训练作业 → 模型注册 → 在线部署。这四个环节也是绝大多数深度学习应用落地的核心路径。1.3 使用云平台前需要接受的三件事第一数据不能假设在本地磁盘旁。训练代码在云端执行数据源一般放在对象存储OBS里代码需要先把它拉取到本地的临时目录或者边读边流式加载。这和平时“直接指向本地路径”的思路不一样。第二任务不是交互式的。训练作业提交后基本是黑盒执行你通过日志观察进度。如果脚本里写死了本地交互输入训练一定会卡住或者失败。第三计费与资源池绑定。不用的时候要把服务删除、作业停止否则会一直产生费用。这不是说平台贵而是说你要换一种使用习惯按需开、用完关。接受这三点之后整个ModelArts的操作逻辑就顺了。2. 环境准备清单账号、OBS桶、委托权限一个都不能少2.1 账号开通与区域选择使用ModelArts的第一步是开通服务。整个过程不复杂但有几点值得注意账户要完成实名认证否则部分资源完全拿不到初次使用会要求选择区域数据存储在哪个区域的OBS训练作业当前也建议用同一区域跨区域访问会带来额外延迟和流量费用如果只是学习选择距离自己较近且资源充足的区域即可后面可以在设置里调整。区域这个问题我第一次没当回事选了离自己最近的区域结果训练作业依赖的某个公共镜像在那个区域没有同步只能换区重来。后来我学聪明了先查一下想用的算法框架或者镜像在哪个区域可用再决定最终落点。2.2 创建OBS桶与目录规划OBS桶可以理解成一个云端的大硬盘对象存储不需要关心文件系统的挂载和分区上传之后就有一个全局唯一的访问路径。我创建桶时的要点桶名全局唯一建议用项目名加后缀比如xxx-modelarts-demo存储类别选“标准存储”就够训练场景用不到低频存储桶策略保持“私有读写”默认设置训练作业通过委托方式访问不要图省事设成公开读。目录结构建议提前规划好。我用的结构比较简单my-modelarts-demo/ ├── data/ │ ├── train/ │ └── valid/ ├── output/ └── code/data放原始训练数据output放训练输出code放训练脚本压缩包。这样训练作业的输入路径、输出路径、代码路径一目了然。2.3 数据上传的三种方法上传数据很容易被忽略但真正做起来坑也不少。我试过三种方式第一种控制台直接上传。适合小文件几十个文件以内没问题。超过几百个文件的目录控制台上传体验就非常差了。第二种obsutil命令行工具。这是我最常用的方式支持批量上传、断点续传、并发控制。命令大致长这样./obsutil cp ./data/ s3://my-modelarts-demo/data/ -r -f-r表示递归-f表示强制覆盖。大文件传一半断了重新执行一遍obsutil会跳过已经传完的部分这对多文件数据集来说非常实用。第三种通过SDK写脚本上传。适合定期更新数据的场景比如每周自动把新的训练数据同步上去。个人学习阶段用不太到但值得知道有这条路。2.4 委托权限最容易忽略的隐藏卡点我把OBS桶和数据都准备好之后第一次提交训练作业就报错了错误信息大概是“访问OBS被拒绝”。查了半天问题出在账号没有给ModelArts授权访问OBS。这是很典型的新手问题。ModelArts执行训练作业时需要临时以某个身份访问你的OBS桶这个身份就是委托。你需要在管理控制台的权限配置里创建一个委托并授权给ModelArts。创建委托时选“自动获取”即可平台会自动关联OBS相关权限。这个配置只需要做一次后面所有训练作业都会使用这个身份去读写数据。提示如果换了新账号、新区域或者换了项目务必重新检查委托是否已配置。很多报错的第一排查项不是代码而是权限。3. 训练任务实操从零写一个可跑通的PyTorch脚本3.1 从自动学习开始还是直接写自定义脚本ModelArts提供了好几条训练路线不同熟练度的人选择不一样。自动学习是最低门槛的入口不需要写代码只需要上传若干张图片平台会自动完成训练和部署。它的本质是迁移学习加自动调参对小规模分类任务很有效。我最初用一组不足两百张的图片试过准确率已经能到9成以上非常适合验证业务想法。但自动学习适合快速验证不适合深度定制模型结构、损失函数和评估指标。如果后续要调网络结构、加数据增强、设计复杂的训练逻辑还得走自定义训练脚本这条路。这次学习笔记里我重点记录自定义训练脚本的过程因为这条路线的可控性最强理解之后也能迁移到其他云平台。3.2 训练脚本与云端路径的交互方式在ModelArts上写训练脚本最核心的一点是理解输入输出参数。训练作业提交时可以给脚本传入一系列参数。最常用的两个是--data_url训练数据的OBS路径--train_url训练输出的OBS路径。训练作业启动后代码所在的目录是预置工作目录数据并不在这个目录里它还在OBS上。如果脚本直接找相对路径文件必挂无疑。正确做法是先把OBS上的数据拷贝到本地缓存目录训练完成后再把模型文件拷贝回OBS输出路径。因为对象存储上的IO性能远低于本地盘拷贝到本地再训练速度会明显更快。这段代码是我使用的下载和训练骨架的一部分import argparse import os import torch import torchvision import torch.nn as nn import moxing as mox def parse_args(): parser argparse.ArgumentParser() parser.add_argument(--data_url, typestr, requiredTrue) parser.add_argument(--train_url, typestr, requiredTrue) parser.add_argument(--epochs, typeint, default5) parser.add_argument(--batch_size, typeint, default32) return parser.parse_args() def main(): args parse_args() local_data_path /cache/data os.makedirs(local_data_path, exist_okTrue) # OBS数据拷贝到本地缓存 mox.file.copy_parallel(args.data_url, local_data_path) dataset torchvision.datasets.ImageFolder( rootlocal_data_path, transformtorchvision.transforms.Compose([ torchvision.transforms.Resize((224, 224)), torchvision.transforms.ToTensor() ]) ) loader torch.utils.data.DataLoader( dataset, batch_sizeargs.batch_size, shuffleTrue ) model torchvision.models.resnet18(pretrainedTrue) model.fc nn.Linear(model.fc.in_features, 2) criterion nn.CrossEntropyLoss() optimizer torch.optim.SGD(model.parameters(), lr0.001, momentum0.9) # 训练循环略去 local_output_path /cache/output os.makedirs(local_output_path, exist_okTrue) torch.save(model.state_dict(), os.path.join(local_output_path, model.pth)) # 模型拷贝回OBS mox.file.copy_parallel(local_output_path, args.train_url) if __name__ __main__: main()这段代码里moxing是平台内置的OBS访问库在ModelArts的训练环境里可以直接使用。它的copy_parallel方法可以把整个目录递归拷贝非常方便。3.3 本地使用OBS路径时的三个常见坑第一路径协议别写错。有些文档用s3://有些用obs://模型服务配置界面里常显示s3://桶名/路径。本质上它们指向同一个对象存储但脚本内路径必须和运行时环境支持的方式匹配。我的经验是在训练作业中尽量用s3://桶名/路径这种格式因为在ModelArts内部它兼容性更好。第二路径末尾的斜杠也会影响行为。copy_parallel把s3://bucket/data当作目录拷贝和s3://bucket/data/在某些版本里可能表现不同。要么都带要么都不带保持统一。第三目录不要写进相对路径。很多代码写习惯了默认把输出模型放在当前目录。训练作业结束前平台会保留当前环境的临时文件但不会帮你把任意路径的文件同步到OBS。只要模型没有上传到train_url指定路径后面注册模型时就找不到任何内容。3.4 创建训练作业时的配置项解读脚本准备好之后在ModelArts控制台创建训练作业。关键配置项有这些算法来源选择“自定义”方式上传刚才准备好的脚本和对应的框架环境。也可以选择预置算法镜像省去配置依赖的麻烦代码目录填写代码在OBS上的目录启动文件填写要执行的Python脚本文件名数据来源选择“OBS”填入训练数据路径训练输出选择“OBS”填入输出路径资源池测试阶段可以用CPU正式训练用GPU。经验是先用一小批数据在CPU上把代码跑通再换GPU跑全量避免一个明显的代码错误烧掉大把时间超参数可以直接在控制台填入--epochs、--batch_size也可以写进脚本默认值。创建时还有一个“重启策略”建议选“从不”。训练失败时不会自动重跑方便你自己看到原始报错。调通之后再改成“总是”可以节省人工盯任务的时间。训练作业一旦创建就会进入排队、启动、运行的状态此时可以在详情页看到实时日志。如果日志迟迟没有输出先看看是不是排队等待资源。如果显示“启动失败”多半是镜像拉取或者脚本路径配置错误日志信息会把根本原因点出来。4. 训练过程监控与模型注册日志、曲线和版本管理4.1 实时日志的正确看法训练作业启动后最关心的就是日志。ModelArts控制台提供日志预览和下载两种方式。预览适合看少量输出下载则适合拿日志做二次分析。日志的输出位置来自两个方向一是训练脚本本身的标准输出也就是print语句的内容二是框架、系统在运行时产生的输出。脚本里print的信息都会在日志里出现所以建议在关键节点多打印每个epoch结束后的loss和accuracy每完成一定步数后的当前进度模型保存时打印保存路径。我踩过的坑是脚本跑了一小时日志一直没有更新我以为是卡住了。后来发现是打印内容太多日志页面刷新滞后刷新了几次之后内容一次性全部出现了。如果训练卡住更可靠的判断指标是CPU/GPU利用率资源池监控面板里利用率长时间为0才是真正出问题。4.2 训练指标曲线的生成逻辑ModelArts在训练作业详情页会展示算法指标曲线比如损失曲线、准确率曲线。但这些曲线不是平台自发生成的它需要训练脚本在输出日志时使用特定的格式平台才能解析。不同版本的控制台识别方式不完全一样。如果你的脚本只是普通print(loss0.123),页面通常不会生成曲线。这时候不要以为平台坏了更直接的办法是自己在脚本里把指标写入一个日志文件训练结束后从输出目录拿回来再画图。我自己更常做的做法是每个epoch结束后把epoch、loss、acc打印成一行训练结束之后用脚本把打印出来的数据抄下来本地快速画图。虽然麻烦一点但完全不依赖平台解析。4.3 训练输出与模型文件命名训练结束后输出目录里就是所有拷贝回OBS的文件。这里有一个特别需要注意的点模型文件的命名最好固定、且容易识别。ModelArts在模型注册时经常需要你指定模型文件名。如果脚本保存的是best_model.pth注册时把文件名填对就行。但如果一个训练作业里同时保存了多个文件比如checkpoint_1.pth、checkpoint_2.pth注册时就要特别小心别选错版本。我习惯把最终用于部署的模型固定命名为model.pth中间检查点单独放在一个子目录。这样可以减少后续操作中的歧义。4.4 模型注册与版本管理模型注册简单理解就是把训练产物以及配套的推理配置打包成一个“可用的模型”。它是训练环节和部署环节之间的桥梁。在ModelArts控制台进入“模型管理”创建模型时可以选择“从训练作业导入”选中刚才的训练作业平台会自动读取输出目录里的模型文件也可以选择“从模板导入”按固定目录结构把模型文件和推理代码传到一个OBS路径。比较关键的是“模型版本”这个概念。同一个模型名称下可以管理多个版本每次重新训练生成新版本。比如model.pth基于V1数据训练后来又基于扩充后的V2数据训练两个版本都会保留。部署时可以显式指定版本号。这样做的价值在于上线稳定版本后后续新版本可以在测试环境中验证验证通过再切换流量不必每次改动都重构整个部署。5. 部署在线推理服务配置、推理代码与接口测试5.1 三种部署形态怎么选ModelArts部署过程支持三种方式在线服务是最常用的形态。模型部署后会提供一个固定API地址客户端随时发起请求适合实时推理场景比如在线识图、实时风控。批量服务则是一次性处理一批数据。数据准备好之后提交任务平台跑完再把结果存到OBS适合离线批量计算比如周末把某个月存量图片全部跑一遍。边缘服务则是把模型下发到靠近数据源的设备端适合需要低延迟、弱网、离线场景。我这次用的是在线服务也是学习阶段最直观的方式。5.2 在线服务的关键配置创建在线服务时核心是这几项选择模型及版本通常直接选刚才注册好的模型即可资源规格如果是用CPU推理选择合适的内存规格就行如果模型比较大、推理时延要求高可考虑GPU规格。个人试验阶段先用CPU规格把流程跑顺再根据实际耗时决定要不要升级。实例数先从1开始。ModelArts支持自动扩缩容但自动扩缩容的对数策略对个人学习阶段来说不是必需品。手动控制不但省钱还能让你更清楚资源利用情况。环境变量有些模型需要环境变量控制推理行为比如编码格式、超时时间。按需配置不需要就不填。5.3 推理脚本里要做的事部署在线服务时ModelArts会加载模型并执行推理脚本。推理脚本不能只是“加载一下模型”它需要完成三个步骤预处理把网络请求里的原始数据转换成模型可接受的张量格式。最常见的图片请求客户端传过来的可能是base64字符串你要先解码成图像再缩放、归一化。推理调用模型forward或者predict方法得到原始输出。后处理把Tensor输出转换成Python原生类型。这里是我踩得最惨的一个坑我直接返回了一个Tensor对象部署后调用服务报错报错信息无法序列化。处理方式很简单用.item()或者int()把Tensor转成数字再包装成dict。推理脚本的长相大致是这样import base64 import torch from PIL import Image from torchvision import transforms from model_service.model_service import ModelService class MyService(ModelService): def __init__(self, model_name, model_path): super().__init__(model_name, model_path) self.model torch.load(model_path, map_locationcpu) self.model.eval() self.transform transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), ]) def preprocess(self, data): image_base64 data.get(image_base64) if image_base64 is None: raise ValueError(缺少image_base64字段) image_bytes base64.b64decode(image_base64) image Image.open(image_bytes).convert(RGB) image self.transform(image).unsqueeze(0) return image def inference(self, data): with torch.no_grad(): outputs self.model(data) return torch.softmax(outputs, dim1) def postprocess(self, data): probabilities, indices torch.max(data, dim1) label_idx int(indices[0].item()) confidence float(probabilities[0].item()) return { label_idx: label_idx, confidence: confidence }这里特别要说明一点具体推理类的继承方式不同版本会略有差别但整体思路是通用的。如果遇到平台的模板和示例代码优先读它自带的模板确认类名和方法名。5.4 接口联调与业务封装服务部署完成后控制台会分配一个API地址同时提供“调试”功能。最方便的做法是先在调试页面上传一张图片看返回是否正常。如果调试正常再去外部调用接口。调用方式我建议用curl或者Postman先确认接口本身没问题再写业务代码对接。一个简单的curl示例如下curl -X POST https://your-api-endpoint/predict \ -H Content-Type: application/json \ -d {image_base64: 这里放图片的base64编码}有些服务会配置安全认证比如Bearer Token、AK/SK签名。我在学习阶段先把安全配置关闭用最简单的接口调试通。正式部署时再开启鉴权。返回结果应该是一段JSON类似{ label_idx: 1, confidence: 0.91 }拿到这个结果后再做业务集成比如调用方根据label_idx展示不同的信息或者进一步分析置信度是否达到业务阈值。6. 排障现场高频报错与实际问题处理技巧6.1 我遇到的高频报错汇总这一部分直接整理成表格方便对应查找。报错关键词实际原因排查方法NoSuchKeyOBS路径下的文件不存在先确认上传目录结构尤其注意大小写和路径前缀AccessDenied委托权限未配置或授权不足检查委托是否已关联ModelArts桶策略是否放行CUDA out of memoryGPU显存不足降低batch size或换更大显存的资源规格FileNotFoundError代码里引用了本地不存在路径确认已经执行数据拷贝到/cache不要假设数据就在当前目录model.pth not found训练输出文件没拷贝到OBS确认训练结束前调用了copy_parallel或类似上传逻辑JSON序列化失败推理返回值包含Tensor、ndarray在postprocess阶段全部转成Python原生类型服务一直显示异常推理进程崩溃或端口未启动查看服务日志确认模型加载成功与否确认推理脚本没有语法错误其中“NoSuchKey”是我发生的最高频的错误。原因几乎都是数据上传目录和训练作业里填的路径不完全一致比如多了个前导子目录。最好办法是用obsutil在本地执行一次ls把桶里的实际路径列出来和训练作业配置里填的路径对一遍。6.2 资源与成本控制经验谈大多数人刚开始用云平台最容易忽视成本。我自己的几条经验是训练作业一定要先小数据集调试。用几十张图片把整个流程跑通确认模型能正常训练和保存再换全量数据。一个完整的训练任务持续几小时和几分钟费用完全不同。在线服务不调试时记得删除。在线服务是持续计费的就算没有请求也会占用资源。个人学习阶段我在调试完成后直接删除服务需要演示时再重新创建。这个操作很快不必一直保留。合理使用“自动停止”功能。训练作业和Notebook都支持配置自动停止时间。一旦训练意外卡住至少不会白白扣费。不需要的时候把作业或服务实例停掉。6.3 把学习笔记转化为可复用的模版整个流程跑通之后我做的最后一件事是把脚本、命令、目录结构整理成一个模板存到了自己的代码仓库里。下一次遇到新任务只需要复制模板改一下数据集和模型定义就能快速跑通。这个习惯对我来说价值很大。因为云端任务流程非常标准化模板化之后换任务只是在填充几个变量不需要从零思考数据怎么上传、路径怎么填、模型怎么注册。如果你也是刚入门我建议你也做一件类似的事把这份笔记里的目录结构、训练脚本骨架、推理脚本骨架存下来作为自己的“ModelArts起手式”。6.4 一些最后的排障心得云端训练的报错和本地最大的不同在于你无法直接看到机器上的完整状态。所以排查时不要靠猜一定要先打开日志把第一处异常看明白。很多问题都是前面的一个小错误引发后面连锁报错比如路径前缀写错导致数据没加载然后dataloader报空数据集最后一脸茫然。如果遇到实在查不出的问题有一个很笨但有效的方法写一个最小测试脚本先不训练只打印“读取数据成功”和“当前环境温度、目录结构”提交一遍训练作业。如果最小脚本能跑通说明环境没问题再把完整脚本放上去对比差异。这个方法看起来浪费时间实际能省很多时间。因为环境、路径、权限的问题最小脚本一分钟就能暴露而不完整的模型训练脚本可能要跑十分钟才报错。我个人在实际使用中还有一个体会云平台的功能很多但自己真正需要的只是其中一条主链路。刚开始不要被控制台里五花八门的菜单带偏方向专注从数据到部署的一条路走通把这里面的原理搞清楚其他功能可以边用边学。做完一次完整的模型训练与部署后再回头读ModelArts的文档你会发现自己能看懂的东西一下多了很多。那些之前觉得陌生的概念比如委托、资源池、模型注册全都对应到了自己做过的某一步操作。这个“从操作反推概念”的过程是学习云计算平台最省力的方式。