ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

AI工程从零搭建:数据训练评估部署监控全链路实践

AI工程从零搭建:数据训练评估部署监控全链路实践 搞AI的时间长了你会发现一个挺扎心的现象很多人能跑通一个模型但做不了一个AI项目。跑通模型是在本地notebook里调用一个现成的接口或者拿公开数据集训练个demo这跟把模型放进真实业务里持续跑、持续迭代、持续出效果是两码事。后者涉及的整套方法论和基础设施就是AI工程。而from scratch这个限定词是我特别想拿出来聊的。它不是让你从零手写梯度下降而是说当你决定认认真真搞AI工程时不能只依赖云平台一键部署或者现成的全托管服务而是要自己从底层把数据、训练、评估、发布、监控这条链路一层层搭起来。这个从零的价值在于你会知道每一块组件为什么存在出问题时能定位到哪一层而不是对着黑盒一脸懵。这篇文章适合三类人一是刚入门AI方向、想系统搞懂工程化而非只调参的同学二是团队里已经跑过模型、但交付总靠“人肉运维”的工程师三是技术管理者想评估自建AI工程体系到底要投入什么。内容里我会把我实际踩过的坑、反复调整过的方案、以及每个看似“过度设计”的决策背后的原因全部摊开说清楚。1. 从零开始做AI工程到底是在做什么1.1 先给AI工程画个边界AI工程不是深度学习理论的延伸也不是换个名字的软件工程。它是一套完整的生命周期管理方法覆盖从业务问题定义到模型下线回收的整个过程。我习惯把它拆成六层数据层、实验层、训练层、评估层、部署层、监控层。每一层之间都有明确的上下游关系也应该有清晰的接口。这里要强调一个容易犯的认知错误很多人觉得AI工程只是把模型用Docker包起来、挂个API接口任务就算完了。但实际上部署只是整个生命周期里最靠后的一步。真正决定项目成败的往往是数据层和实验层。数据层要解决的是“喂给模型的东西是否可信”实验层要解决的是“怎么高效地试错”。如果这两层没搭好后面你只会收获一个“偶尔好用、经常抽风”的黑盒子而且你还不知道它为什么抽风。所以“from scratch”在这里的含义不是一切都自己写而是确保每一层都能被自己掌控。比如你可以用开源组件做存储和调度但每一层的输入输出、质量指标、版本记录都必须由你显式定义。全托管服务看起来轻松但当你遇到数据分布漂移、模型版本回滚、并发扩容这类事情时没有底层控制权会非常被动。1.2 为什么我不建议直接上全家桶方案现在市场上的云平台都在推“AI全家桶”一键部署、自动扩缩容、内置监控面板听起来很美好。我自己也试过一两个这种方案最后都撤下来了。原因不是它们不好用而是它们把关键细节藏得太深。模型训练完的指标分布、请求日志、特征重要性的变化曲线这些东西在平台上确实能看到但你拿不到原始数据也没法接入你自己的告警体系。举个例子有一次我上线一个推荐模型第二天发现线上请求延迟正常、错误率也很低但点击率却悄悄掉了10%。平台监控面板显示一切健康我却找不到原因。后来自己搭了监控链路把特征输入日志、预测分数分布、标签回流数据全部对齐才发现是上游特征源的分布变了导致模型在某个分桶上的预测分数偏高进而影响了排序结果。这种问题全托管服务根本不会帮你定位因为它对你屏蔽了中间层。from-scratch的价值就在这种时候体现所有中间产物、所有日志、所有版本都是你自己的你可以顺着链路一层层盘直到找出问题根源。代价是初期投入大但长期来看这套体系带来的可控性远远值得那几周的搭建时间。2. 动手前的技术选型与架构取舍2.1 技术栈选择的三个原则我搭这套体系时始终守着三个原则第一组件必须可替换核心模块之间用标准接口隔开今天用这套存储明天换另一套不应该牵动模型代码第二配置必须代码化环境变量、数据路径、训练参数不能散落在各处要统一管理、有版本记录第三能用成熟开源的不重复造轮子但前提是你能读懂它的源码至少要知道核心流程在做什么。围绕这三个原则我的选择如下训练框架用PyTorch因为它的生态最完整从模型定义到部署导出都有成熟路径。实验追踪用MLflow它的接口简单支持自定义指标和产物记录部署上轻量但够用。数据集版本管理用DVC它能把数据快照和Git提交关联起来回滚时数据也跟着回退。容器化和编排用Docker加Kubernetes但这俩属于“必选项”而非“可选项”后面专门说。这些选型不是固定答案但你至少要有一个能追溯的、能扩展的、能控制底层细节的组合。不要被框架绑架也不要在某个环节手搓一个过于复杂的内部系统。能用开源组件的版本管理解决就不要自己写版本数据库否则后面维护成本会压得你喘不过气。2.2 为什么编排层我选了Kubernetes很多人一听Kubernetes就头痛觉得那是运维的事。但AI工程有一个绕不开的需求训练任务和推理服务的资源管理。批式训练任务要求独立拉起一组GPU实例跑完就释放在线服务则要求常驻、可扩缩容、能自动恢复。这两类场景放在同一套资源池里Kubernetes是当前最成熟的答案。具体到实操上我用Kubernetes的Namespace区分环境用ResourceQuota限制各团队资源上限用节点亲和性把GPU节点和CPU节点分开。训练任务用Kubernetes的原生Job或者Volcano调度在线服务用Deployment加HorizontalPodAutoscaler。这样不管是凌晨两点定时跑的数据预处理还是中午高峰时的推理请求激增都有一套自动化的资源调度机制在支撑。如果你以前没接触过K8s我给一个直接可用的心理模型它就是一个数据中心的操作系统。Docker是你的应用打包工具Kubernetes负责在一大堆机器上安排这些包往哪里跑、跑多少份、挂了怎么重启。AI项目涉及的机器学习系统本质上是分布式状态管理问题训练、评估、上线、回滚都要在大量机器上协调缺了这个编排层你会非常狼狈。2.3 存储和版本管理的关键选择AI工程里最难管的不是代码是数据。代码可以用Git轻松管理但数据集通常是几十GB甚至TB级而且迭代频繁特征方案一变可能就要生成新版本。这个问题我早期没处理好导致过“模型训练时用了旧数据实验报告里显示的效果和线上完全不匹配”的尴尬。后来我把数据资产分了两类静态数据和派生数据。静态数据是原始采集的只读追加每个批次写入后生成一个不可变的路径派生数据是经过清洗、标注、特征工程后生成的每次生成都会带着代码版本、输入数据版本、参数配置一起登记。用DVC做版本标记用对象存储存实体积元数据放进单独的服务里。这样任何一个训练任务启动时都能明确说出自己用了哪份数据、哪版代码、哪些参数实验的可复现性一下子就上来了。3. 从零搭建的落地路径与核心步骤3.1 环境与应用骨架的搭建我不会直接跳到模型代码而是先把一套标准的项目骨架搭出来。目录分成config、data、models、experiments、tests、deploy六个区块。下面是一个我常用的初始化脚本你可以照着跑一遍mkdir -p ai_eng_ws/{config,data/{raw,processed},models,src/{data,models,eval},experiments,tests,deploy} cd ai_eng_ws git init python -m venv venv source venv/bin/activate pip install torch torchvision mlflow dvc pandas scikit-learn这里尤其要强调虚拟环境加依赖锁定。训练环境和部署环境如果依赖版本不一致问题会特别隐蔽。比如某个库的小版本更新改了一个默认参数模型推理结果就悄悄变了。我建议在项目根目录维护requirements.in作为顶层依赖声明然后用pip-tools生成requirements.txt锁定全部传递依赖每次升级都要走代码评审。配置管理这块我使用Hydra做参数配置。你可能会觉得用Python的yaml库直接读取也行但Hydra的好处是支持配置组合与覆盖比如同一个模型在开发、测试、生产环境用不同的数据路径和日志级别只需要通过命令行或配置文件覆盖不用改代码。训练超参、模型结构参数、数据参数全部写在配置里实验时每次尝试一个组合就生成一个独立的输出目录这样对比时非常清晰。3.2 从数据到训练完整链路的一个最小示例任何AI工程体系到最后都要落到具体的训练流程上。我用一个图像分类任务来演示最小闭环读取原始图片列表、按比例切分训练验证测试集、写入版本控制、训练一个MobileNetV3模型、记录指标到MLflow、保存模型产物。# src/train.py import mlflow from torch.utils.data import DataLoader from torchvision import transforms, models from dataset import load_dataset from config import get_cfg cfg get_cfg() train_ds, val_ds, test_ds load_dataset(cfg.data.train_path) model models.mobilenet_v3_small(pretrainedTrue) optimizer torch.optim.Adam(model.parameters(), lrcfg.train.lr) criterion torch.nn.CrossEntropyLoss() with mlflow.start_run(): mlflow.log_params({lr: cfg.train.lr, epochs: cfg.train.epochs}) for epoch in range(cfg.train.epochs): train_one_epoch(model, train_ds, optimizer, criterion) val_acc evaluate(model, val_ds) mlflow.log_metric(val_acc, val_acc, stepepoch) mlflow.pytorch.log_model(model, model)这段代码看起来不难但它能跑通的前提是数据加载逻辑和配置管理已经就位。很多零基础项目卡在“单机notebook能跑一上集群就崩”的阶段原因就在于代码里到处是硬编码的路径、全局变量、魔术数字。把这些东西全部收敛到配置层是整个工程化的第一步。分布式训练这块我建议先别急着上大规模并行。先把单机多卡或者多机单卡的Horovod接入跑通再考虑弹性调度。多数业务场景的模型规模根本用不上千卡集群一个节点四张卡就够用了。精调通信效率和容错策略比盲目扩大规模实际得多。3.3 模型评估不能只看一个准确率我在做实际项目时发现团队最容易误判的就是模型效果。拿单一准确率指标评估就像拿平均温度评价天气看起来很均衡实际上极端情况全被抹平了。真实业务场景里长尾类别、高分位数延迟、失败样本分布往往才是决定用户体验的关键。我的评估流程分三层。第一层离线指标和切片分析除了总体准确率还要按类别、按数据来源、按时段分别计算指标第二层对比基线每个新模型都要跟当前线上模型在同一份测试集上做对比用统计检验来看差异是否显著第三层影子评估新模型不直接接流量而是在后台同步跑真实请求把它的预测结果和线上模型对比观察分布差异。这套流程比较重建议至少做到第一和第二层否则你永远不知道上线一个模型到底是在变好还是变坏。实践中我还特别重视评估数据的一致性。测试集一旦确定就要锁定不能每次实验都换一份。我踩过一个反复的坑某次新模型各方面指标都比之前好上线后线上表现却变差了后来发现是因为测试集的划分代码在某个提交里变了新旧模型的评估数据根本不在同一分布上。后来我把所有评估任务都绑定到数据版本的哈希值一旦数据集变更所有未完成的实验标记为invalid强制重新评估。3.4 推理服务化与模型优化训练完成不叫交付模型能以稳定的方式暴露出去才叫交付。我自己常用的路线是把PyTorch模型导出成TorchScript或者ONNX格式再用NVIDIA Triton做推理引擎外面套一层自己的API服务。这里的最佳实践是避免在API服务里直接加载pth权重文件因为那样每次启动都会引入框架初始化的额外开销服务冷启动时间会拉长到几十秒。推理服务里有两个细节很关键。第一个是预处理和后处理的分离特征标准化、图像缩放这些操作放在API服务里做确保输入到推理引擎的Tensor格式严格一致预测分数、类别映射等后处理也放在服务里不要留在模型文件内。第二个是并发控制GPU推理引擎的并发不是越大越好要结合具体模型和卡的显存来配置。Triton里可以通过Instance Group设置并发数我会先用压测找到最优值而不是按默认参数直接上线。一个具体的部署配置片段展示Kubernetes Deployment申请GPU资源的写法apiVersion: apps/v1 kind: Deployment metadata: name: classifier-api spec: replicas: 2 selector: matchLabels: app: classifier-api template: metadata: labels: app: classifier-api spec: containers: - name: triton-server image: nvcr.io/nvidia/tritonserver:23.12-py3 args: [tritonserver, --model-repository/models] resources: limits: nvidia.com/gpu: 1 volumeMounts: - name: model-storage mountPath: /models volumes: - name: model-storage persistentVolumeClaim: claimName: model-pvc这套配置里我特别加了资源限制的声明Kubernetes调度器必须在具备GPU的节点上创建Pod并且每个实例独占一张卡。通过增加replicas你可以横向扩展推理能力而模型的更新则通过切换镜像Tag或者模型目录里软链接的指向来完成不必动Pod。3.5 上线后的模型监控体系模型上线不是终点而是另一个起点。我在监控层主要盯三类指标业务指标、模型指标、基础设施指标。业务指标是点击率、转化率、时长这类直接反映业务结果的模型指标是预测分数均值、置信度分布、类别分布、特征覆盖率基础设施指标是延迟、错误率、GPU利用率。三类指标要所出两条链路。一条是请求日志实时流记录下每个请求的输入特征、预测结果、真实反馈通过消息队列写入数据仓库另一条是定期任务每小时计算模型指标分布和业务指标的相关性如果发现相关性突然减弱就触发告警。这两条链路缺一不可只有链路没有前一条等于不知道模型预测背后发生了什么只有前一条没有定期计算等于数据囤在仓库里从没被消费。我看到最有效的告警不是“准确率下降”这种笼统表述而是“用户群体的请求分布偏移”或者“某类特征的中位数超过训练时的3倍标准差”。想做到这种粒度需要把训练时积累下来的基准分布存下来然后在线进行比较。4. 实际工程中最容易踩的坑与排查诀窍4.1 我反复踩过的五个典型问题第一个问题训练和线上数据预处理逻辑不一致训练时做了数据增强和归一化线上推理时忘记做同样的操作导致效果大打折扣。解决思路是把预处理函数单独抽出来共享给训练和推理两端使用同时用单元测试锁定输出张量的shape和数值范围。第二个问题实验记录缺失。早期跑实验不记录完整参数事后想复现一张图都难更不用说排查线上问题了。解决思路是强制规定每一次训练任务必须绑定代码Commit、数据版本、配置Hash三者缺一不可否则不启动训练。第三个问题多机训练时的随机种子未固定导致相同配置下两次训练结果差异大到完全不可信。解决思路是固定所有可能影响随机性的库和框架的随机种子包括Python random、numpy、torch以及DataLoader的worker进程。第四个问题监控指标太多但从不看。我在某项目堆了五十多个监控面板最后真正触发过告警的只有四个。解决思路是砍指标每个业务线只保留最关键的五到八个核心指标其余作为调试参考不参与告警。第五个问题模型回滚难。线上模型出问题时想退回上一个版本却没有保留上一版的系统镜像或模型产物。解决思路是设置模型仓库的回收策略必须保留最近N个版本的模型并且在发布流程里写明回滚操作手册。4.2 排查问题的顺序和技巧模型上线后如果业务指标出了问题我会按照一套固定顺序排查。第一步查基础设施看延迟、错误率、GPU利用率是否有异常不行就翻容器日志第二步查数据链路看上游特征是否缺失、流量是否正常、经过特征工程的每个字段分布是否和预期一致第三步查模型行为把线上真实请求重新喂给模型统计预测分数分布对比训练时的分布第四步回头查标记和评估看线上收到的标签回流是否异常评估数据是否被人为污染过。这个顺序看起来笨但效率最高因为每一步都可以直接利用前面搭的监控体系来缩小范围。最忌讳的是直接怀疑模型本身不行然后花一周重排特征、换模型结构最后发现是上线时少挂了一个配置文件。这种事情我见过太多次包括我自己也干过。4.3 几个可以直接拿走的避坑清单上线前必须做容量压测压测数据用真实请求的回放不能用随机数据糊弄。任何外部依赖的变动数据集、模型权重、配置都要有相应的记录最好和代码提交关联。GPU资源管理要设置配额和优先级防止一个任务吃光所有卡导致其他任务饿死。推理服务启动时要加载模型到内存中预热避免上线头几秒出现超时。监控告警要留“静默窗口”凌晨三点的抖动如果没有持续超过一定时长不应该骚扰值班人员。这些清单本质上属于工程纪律技术上不难难的是坚持执行。我的体会是每一次线上翻车事故都能对应到某一项纪律没有执行到位。5. 最后分享一点我的真实体会从零搭建一套AI工程体系最开始会非常痛苦因为你会发现自己面对的是无数个细节的交织环境的依赖冲突、数据版本的混乱、调度平台的权限设计、监控告警的表达方式每一个都可能卡住你两天。但恰恰是这种痛苦能帮你建立对AI项目最扎实的掌控感。现在我带团队接新项目第一周永远是搭环境和定接口听起来进度缓慢但后面整个开发周期会顺畅非常多。很多朋友问我要不要直接上大型分布式平台我的建议是先用小规模、自建的方式把完整链路跑通哪怕只是在一台八卡服务器上。你亲手搭一遍才能真正搞清楚训练、评估、部署、监控各自扮演什么样的角色会遇到什么样的问题。等到业务规模确实大到需要更复杂的平台时你已经能带着清晰的问题清单去做选型而不是被广告宣传带着走。最后再分享一个我很受益的小习惯每做完一个AI项目写一篇“复盘笔记”记录哪些决策是对的、哪些是错的、如果重来会怎么做。这个习惯坚持两年后你会发现自己对AI工程的理解会产生质的飞跃。很多看起来复杂的高层概念最后都只汇成一句话用工程化手段确保每个环节可追踪、可控制、可回归。
返回列表