尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

基于ModelArts的模型训练与部署全流程实战指南

基于ModelArts的模型训练与部署全流程实战指南 模型训练这件事最劝退新手的往往不是算法本身而是环境。CUDA 版本对不上、驱动和框架打架、本地显卡显存不够、训练到一半 OOM这些琐碎问题能把一个想认真做项目的人耗到放弃。ModelArts 这类云端 AI 开发平台出现的意义就是把这堆脏活从你手里拿走——你只管准备数据和代码算力和环境交给平台。这篇内容我围绕基于 ModelArts 完成模型训练与部署这条主线把从数据上传、代码适配、训练任务配置到模型部署成可调用服务的完整链路拆开讲一遍中间穿插我自己踩过的坑和参数取舍逻辑。不管你是刚接触深度学习的学生还是想把本地训练搬到云上的开发者都能照着走通一遍。1. 先想清楚为什么把训练搬到 ModelArts 上1.1 本地训练的三个硬伤先说结论不是所有项目都值得上云但下面这三种情况本地训练基本是自找麻烦。第一是算力瓶颈。你本地一张消费级显卡显存 8G 或 12G跑个 ResNet 分类还行一旦上到目标检测、分割、或者大一点的预训练模型微调batch size 只能压到 1 或 2训练速度慢到怀疑人生。更难受的是显存不够直接报 OOM你连调参的机会都没有。第二是环境地狱。深度学习框架、CUDA、cuDNN、显卡驱动四者版本必须严格匹配。我见过太多人卡在torch.cuda.is_available()返回 False 这一步折腾一整天。而且不同项目依赖的框架版本还不一样本地装两套环境互相污染是常态。第三是任务中断风险。本地机器跑一个几十小时的训练中间断电、系统更新、误触关机全白跑。没有断点续训机制的话心态直接崩。ModelArts 针对的正是这三点按需申请的 GPU/NPU 算力、预置好的框架镜像、以及训练作业的断点续训和日志留存。你付出的成本是按使用时长计费对于阶段性项目来说比买一张高端卡划算得多。1.2 ModelArts 的能力边界别神化也别低估得客观说ModelArts 不是万能的。它本质是一个托管式的 AI 开发平台核心能力分三块数据管理数据集、标注、开发与训练Notebook、训练作业、自动学习、部署与推理在线服务、批量服务、边缘部署。它擅长的是标准化的训练流程你有数据、有代码、有明确的输入输出平台帮你把算力调度、环境、日志、模型版本管理都包了。它不擅长的是高度定制化的底层算子开发、需要特殊硬件拓扑的分布式训练调优这类场景——这些还是得自己在裸金属或自建集群上搞。所以判断标准很简单你的任务是用现成框架训练一个模型并部署成服务ModelArts 非常合适你的任务是改框架源码、写自定义 CUDA 算子那平台会束缚你。1.3 一个典型项目的全流程地图在动手前先把整条链路在脑子里过一遍避免做到一半发现方向错了。一个完整的 ModelArts 项目大致是这样准备数据上传到 OBS对象存储服务或直接在平台创建数据集在 Notebook 里做数据探索、调试代码小规模跑通把调试好的代码整理成训练脚本配置训练作业提交训练作业选算力规格、镜像、超参监控训练过程训练产出模型文件注册到模型管理把模型部署成在线服务对外提供推理接口用测试数据验证服务做性能压测这条链路里第 2 步和第 3 步之间的转换是最容易出问题的因为 Notebook 里是交互式执行训练作业是脚本式执行路径、依赖、参数传递方式都不一样。后面我会专门讲这块。2. 数据准备与 OBS 存储的那些门道2.1 为什么数据要先上 OBSModelArts 的训练作业运行在独立的计算节点上这些节点读不到你本地的硬盘也读不到 Notebook 的临时目录。所有训练数据必须放在一个训练节点能访问的持久化存储里而 OBS 就是这个角色。你可以把 OBS 理解成一个云盘但它是对象存储用桶bucket和对象object来组织不是传统的文件夹树。路径写法是obs://桶名/目录/文件。训练作业配置里你把 OBS 路径填进去平台会在任务启动时把数据挂载或下载到容器内的指定目录。这里有个关键选择是让平台自动下载数据还是代码里直接读 OBS自动下载配置里指定数据来源 OBS 路径和容器内目标路径平台启动时同步。适合数据量不大几十 G 以内、需要频繁随机读取的场景。代码直读训练脚本里用 MoXing 或 obsutil 直接读 OBS。适合数据量巨大、不想全量下载的场景但读取延迟比本地盘高。我的经验是数据量在 50G 以下直接让平台下载到容器本地盘训练速度最稳。超过这个量级再考虑流式读取否则光是下载就要等很久而且每次任务重启都要重下。2.2 数据格式与目录结构规范训练脚本对目录结构是有预期的乱放会导致找不到文件。以图像分类为例推荐的结构是dataset/ ├── train/ │ ├── class_a/ │ │ ├── 001.jpg │ │ └── 002.jpg │ └── class_b/ │ ├── 001.jpg │ └── 002.jpg └── val/ ├── class_a/ └── class_b/这种按类别分文件夹的结构配合torchvision.datasets.ImageFolder或tf.keras.utils.image_dataset_from_directory可以直接用不用自己写标签映射。如果你用的是自定义 Dataset那结构随意但要在代码里把路径拼对。注意OBS 上的路径是大小写敏感的本地 Windows 开发时文件名大小写混乱上传后可能对不上。上传前统一小写是个好习惯。2.3 上传数据的几种方式和速度对比上传数据到 OBS常见有这几种方式方式适用场景速度备注控制台网页上传小文件、少量慢大文件容易断obsutil 命令行批量、大文件快支持断点续传、并发SDK 编程上传集成到流程中灵活但需写代码Notebook 内挂载调试阶段中临时用方便批量数据我强烈建议用obsutil它支持多线程并发上传和断点续传。一个几十 G 的数据集网页上传可能传半天还断obsutil 配置好并发数后快很多。基本命令长这样# 配置访问凭证 obsutil config -i你的AK -k你的SK -eendpoint地址 # 上传整个目录-r 递归-f 强制覆盖 obsutil cp -r -f ./local_dataset obs://your-bucket/dataset/-r是递归目录-f是强制覆盖已存在文件。并发数可以通过-p参数调整默认值一般够用网络好的话可以调大。2.4 数据版本管理别让数据集悄悄变了一个容易被忽视的点数据集是会变的。你今天用 v1 数据训练明天补了一批样本变成 v2如果没做版本管理回头想复现 v1 的结果就难了。ModelArts 的数据集功能支持版本管理每次修改生成一个新版本训练时指定版本号。如果你直接用 OBS 路径那就自己在路径里带版本比如obs://bucket/dataset/v1/、obs://bucket/dataset/v2/。这个习惯在团队协作里尤其重要否则别人复现你的实验时根本不知道你用的哪版数据。3. 从 Notebook 调试到训练作业代码适配的坑3.1 Notebook 里跑通不等于训练作业能跑这是新手最容易栽的地方。Notebook 是交互式的你一个 cell 一个 cell 执行变量在内存里传递路径是相对当前工作目录。训练作业是一次性脚本执行从头到尾跑一遍没有交互路径是绝对路径。常见的翻车点Notebook 里data_path ./data能跑训练作业里就找不到因为工作目录变了Notebook 里手动pip install的包训练作业镜像里没有Notebook 里用!wget下载的预训练权重训练作业里没这个文件Notebook 里靠 cell 顺序定义的全局变量脚本里执行顺序不对就报错解决办法是在 Notebook 里就把代码写成能从头到尾一次性执行的形式用一个 main 函数包起来路径全部用参数传入不要依赖交互式状态。这样调试通了直接复制成 .py 文件就能当训练脚本用。3.2 训练脚本的参数化改造训练作业提交时超参是通过命令行参数或环境变量传进去的。所以你的脚本必须支持参数解析。用 argparse 是标准做法import argparse def parse_args(): parser argparse.ArgumentParser() parser.add_argument(--data_url, typestr, default./data, help训练数据路径) parser.add_argument(--train_url, typestr, default./output, help模型输出路径) parser.add_argument(--epochs, typeint, default10) parser.add_argument(--batch_size, typeint, default32) parser.add_argument(--lr, typefloat, default0.001) return parser.parse_args() if __name__ __main__: args parse_args() # 训练逻辑这里data_url和train_url是 ModelArts 训练作业的约定参数名平台会自动把配置里的数据路径和输出路径注入进去。你只要在脚本里接收就行不用自己拼 OBS 路径。提示train_url指向的目录平台会在训练结束后自动同步回 OBS。所以模型 checkpoint 往这个目录写训练完就能在 OBS 上拿到。3.3 依赖管理镜像里没有的包怎么办ModelArts 预置了主流框架的镜像PyTorch、TensorFlow、MindSpore 都有常见依赖基本齐全。但如果你用了冷门库镜像里没有就得自己装。两种方案方案一训练启动时 pip 安装。在脚本开头或启动命令里加pip install xxx。简单但每次任务启动都要装一遍浪费时间而且如果没网就失败。方案二自定义镜像。基于官方镜像把依赖装好推到镜像仓库训练时指定自己的镜像。一劳永逸适合固定技术栈的团队。我的建议调试阶段用方案一快速验证稳定后转方案二。另外注意pip 安装要指定版本否则不同时间装出来的版本可能不一样导致结果不可复现。3.4 断点续训让长任务不怕中断训练几十上百个 epoch中途因为抢占或超时中断是常事。如果没有断点续训只能从头再来。实现思路很简单每个 epoch 结束保存 checkpoint任务启动时先检查有没有 checkpoint有就加载继续。import os import torch start_epoch 0 ckpt_path os.path.join(args.train_url, latest.pth) if os.path.exists(ckpt_path): ckpt torch.load(ckpt_path) model.load_state_dict(ckpt[model]) optimizer.load_state_dict(ckpt[optimizer]) start_epoch ckpt[epoch] 1 print(f从 epoch {start_epoch} 继续训练) for epoch in range(start_epoch, args.epochs): train_one_epoch() torch.save({ model: model.state_dict(), optimizer: optimizer.state_dict(), epoch: epoch }, ckpt_path)关键点是 checkpoint 要存到train_url目录这样中断后重新提交任务平台会把上次的输出同步回来脚本就能读到。这个机制配合平台的自动重启功能长任务基本不用人盯着。4. 训练作业配置算力、镜像、超参怎么选4.1 算力规格的选择逻辑ModelArts 提供多种算力规格从单卡到多卡从 GPU 到 NPU。怎么选看三个因素模型大小、数据量、时间预算。小模型ResNet、MobileNet 级别 中等数据单卡 V100 或 A100 够用大模型ViT、大语言模型微调需要多卡甚至多机数据量特别大瓶颈可能在数据读取加卡收益递减一个实用的判断方法先在单卡上跑一个 epoch看耗时和显存占用。如果显存占用不到一半说明可以加大 batch size 或换更大模型如果显存快满了考虑梯度累积或换大显存卡如果单 epoch 耗时太长考虑多卡并行。多卡训练要注意不是所有代码都支持多卡。PyTorch 的DataParallel单机多卡和DistributedDataParallel多机多卡需要代码适配。ModelArts 的多卡任务会设置环境变量你的代码要读取这些变量来初始化分布式环境。4.2 镜像选择与框架版本匹配镜像选择的核心原则镜像里的框架版本必须和你代码兼容。比如你的代码用了 PyTorch 2.0 的新 API就不能选 1.8 的镜像。反过来老代码用了被废弃的 API选新镜像会报错。ModelArts 的镜像命名一般带框架和版本信息比如pytorch_2.0.0-cuda_11.7。选的时候注意 CUDA 版本要和你的代码里编译的算子匹配如果你用了自定义算子。注意不同镜像的 Python 版本可能不同这会影响你 pip 装的包。选镜像前先确认 Python 版本避免装出来的包不兼容。4.3 超参配置与调优策略超参在训练作业配置里以键值对形式传入对应脚本里的 argparse 参数。常见的超参超参典型范围调优建议learning_rate1e-5 ~ 1e-2先用 1e-3 试不收敛再降batch_size16 ~ 256受显存限制尽量大epochs10 ~ 200看验证集指标早停weight_decay1e-5 ~ 1e-2防过拟合从 1e-4 起调参不要盲目网格搜索先固定其他参数单独调学习率找到量级后再微调。学习率是最敏感的量级错了其他都白搭。如果平台支持超参搜索Hyperparameter Tuning可以用它自动跑多组配置。但要注意每组配置都是一次完整训练算力成本是叠加的别一上来就搜几百组。4.4 训练过程监控与日志排查训练作业提交后最关心的就是跑起来没有loss 降没降。ModelArts 提供训练日志查看可以实时看标准输出。排查训练问题的顺序看任务状态是运行中、失败还是已完成。失败的话看错误信息。看启动日志环境初始化、依赖安装、数据下载有没有报错。看训练日志loss 是否正常下降有没有 NaN速度是否正常。看资源监控GPU 利用率、显存占用。利用率低说明数据加载是瓶颈。loss 变 NaN 是高频问题原因通常是学习率太大、数据里有异常值、或者数值不稳定比如除零。解决办法降学习率、检查数据、加梯度裁剪。这个在热词里也有人问确实是训练中最常见的坑之一。5. 模型部署从训练产物到在线服务5.1 训练产物的整理与模型注册训练完成后train_url目录里会有模型文件。部署前要先整理成推理需要的格式模型权重 推理代码 配置文件。ModelArts 的模型管理支持从 OBS 路径或训练作业导入模型。导入时要指定模型文件路径OBS 上的位置推理代码一个customize_service.py定义预处理、推理、后处理运行环境框架和版本输入输出格式推理代码是部署的核心它决定了服务怎么接收请求、怎么处理数据、怎么返回结果。一个典型的customize_service.py结构from model_service.pytorch_model_service import PTServingBaseService import torch import numpy as np class MyService(PTServingBaseService): def __init__(self, model_name, model_path): super().__init__(model_name, model_path) self.model torch.load(model_path, map_locationcpu) self.model.eval() def _preprocess(self, data): # 把请求数据转成模型输入 img data[input] # 归一化、resize 等 return {input: tensor} def _inference(self, data): with torch.no_grad(): output self.model(data[input]) return output def _postprocess(self, data): # 把模型输出转成响应格式 return {result: data.tolist()}三个方法分别对应预处理、推理、后处理这是推理服务的标准三段式。预处理负责把原始请求转成张量推理跑模型后处理把输出转成人类可读或业务需要的格式。5.2 在线服务配置规格、并发、自动扩缩容部署在线服务时要配置几个关键项计算规格推理用的算力一般比训练小。CPU 推理适合小模型GPU 推理适合大模型或高并发。实例数量初始几个实例决定并发能力。自动扩缩容根据负载自动增减实例。流量波动大的场景必开。请求超时单次推理的最大耗时超时返回错误。自动扩缩容的触发条件是 CPU/GPU 利用率或 QPS。配置时要设最小和最大实例数避免流量突增时扩不出来也避免流量低谷时实例空跑浪费钱。提示推理服务的冷启动时间要重视。模型大的话实例启动加载模型可能要几十秒这段时间请求会失败。可以配置预热或保持最小实例数不为零。5.3 服务测试与性能压测服务部署好后先做功能测试发几条真实数据看返回结果对不对。ModelArts 控制台一般有在线测试功能也可以自己写脚本调 API。功能没问题后做性能压测关注三个指标延迟Latency单次请求耗时P99 延迟比平均延迟更重要吞吐Throughput每秒能处理多少请求错误率失败请求占比压测工具可以用 Apache Bench、wrk 或自己写脚本。压测时逐步加压找到服务的拐点——超过某个并发后延迟飙升或错误率上升那个点就是当前配置的容量上限。5.4 部署后的常见问题与排查部署环节的坑也不少列几个高频的问题可能原因排查方向服务启动失败推理代码报错、依赖缺失看服务日志请求返回 500预处理/推理异常看错误堆栈延迟很高模型太大、实例规格低换 GPU 或优化模型结果不对预处理和后处理不匹配对比训练时的处理逻辑冷启动慢模型加载耗时减小模型或预热结果不对是最隐蔽的问题因为服务能跑只是输出错了。根因通常是推理时的预处理和训练时不一致——比如训练时归一化用了 ImageNet 的均值方差推理时忘了结果就偏了。所以推理代码里的预处理逻辑一定要和训练时严格对齐。6. 几个实战中反复踩的坑6.1 路径问题OBS 路径和容器路径的映射训练作业配置里你填的是 OBS 路径但脚本里读到的是容器内的本地路径。这个映射关系是平台做的你不需要关心 OBS 怎么挂载只要在脚本里用data_url和train_url这两个参数接收就行。但有个坑如果你在脚本里硬编码了 OBS 路径去读数据会失败因为容器里没有 OBS 客户端也没有凭证。所有数据访问都要通过平台注入的本地路径。6.2 显存溢出OOM的几种解法OOM 是训练和推理都常见的问题。解法按优先级减小 batch size最直接但可能影响收敛梯度累积小 batch 多次前向后向再更新等效大 batch混合精度训练用 fp16 减少显存占用速度也快梯度检查点用时间换空间适合超大模型模型并行把模型拆到多卡复杂度高一般先试 1 和 3混合精度AMP几乎是无脑开显存省一半速度还快精度损失很小。6.3 训练速度慢的定位方法训练慢先定位瓶颈在哪GPU 利用率低50%瓶颈在数据加载。加num_workers、用更快的存储、预取数据。GPU 利用率高但速度还是慢模型本身计算量大考虑换卡或优化模型结构。启动阶段慢数据下载或依赖安装耗时考虑预置镜像或减少数据量。用nvidia-smi看 GPU 利用率用time或 profiler 看各阶段耗时。定位准了再优化别瞎调。6.4 模型复现性随机种子和版本锁定同一个脚本跑两次结果不一样是随机性导致的。要复现得固定所有随机源import random import numpy as np import torch def set_seed(seed42): random.seed(seed) np.random.seed(seed) torch.manual_seed(seed) torch.cuda.manual_seed_all(seed) torch.backends.cudnn.deterministic True torch.backends.cudnn.benchmark False注意cudnn.deterministic True会牺牲一点速度换确定性。另外数据加载的 shuffle、dropout 这些也受种子影响都要覆盖到。除了种子框架版本、依赖版本、数据版本也要锁定否则环境变了结果也会变。这就是前面强调版本管理的原因。7. 关于成本控制的一点个人经验最后聊个实际的话题钱。云端训练是按量计费的不注意的话账单会很吓人。几个控制成本的习惯第一调试用低规格正式训练再上高规格。Notebook 调试阶段用 CPU 或小 GPU 就够代码跑通了再提交大算力训练作业。第二训练作业设超时和自动停止。万一代码有 bug 导致死循环没有超时机制会一直烧钱。配置里设个合理的最大运行时间。第三及时停止不用的服务。在线服务部署后如果不用了记得停掉否则实例一直计费。测试完就停需要时再启。第四checkpoint 存到 OBS 而不是容器本地。容器销毁后本地数据就没了OBS 是持久的。而且断点续训也依赖 checkpoint 持久化。我自己刚开始用的时候就因为忘了停一个测试服务白白跑了两天。后来养成了习惯每次用完先检查有没有遗留的运行中任务和服务。这个习惯比任何优化技巧都省钱。
返回列表