ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

从零搭建AI工程能力:环境、数据、训练、推理与监控全链路实践

从零搭建AI工程能力:环境、数据、训练、推理与监控全链路实践 1. 从零搭建AI工程能力为什么“会用模型”和“会做工程”是两回事很多人第一次接触AI项目时都会经历一个相似的阶段在笔记本里跑通一个模型准确率看着还不错于是觉得“AI也就这么回事”。可一旦要把这个模型放到真实业务里问题就全冒出来了——推理延迟高得离谱、显存动不动就爆、并发一上来服务直接挂、模型更新一次要停机半小时、线上效果和离线评估对不上。这些问题的根源往往不是模型本身不够好而是AI工程能力没有跟上。ai-engineering-from-scratch这个标题说的就是从零开始构建一整套AI工程能力而不是只停留在“调包跑模型”的层面。它涵盖的范围其实很广数据管线的搭建、训练与推理环境的隔离、模型服务的部署、性能压测、监控告警、版本管理、成本控制等等。换句话说它关心的是一个AI系统从实验到生产、从单机到集群、从一次性脚本到可持续迭代的完整生命周期。这篇文章适合几类人看一是刚入行、只会写训练脚本但没碰过部署的算法同学二是做后端或运维、被拉来支持AI项目的工程师三是想系统梳理AI工程知识体系的技术负责人。我会尽量用从业者的视角把每个环节“为什么这么做”“不这么做会怎样”“实际怎么落地”讲清楚而不是罗列一堆工具名字。文中涉及的具体参数和配置都是基于常见生产实践给出的参考值你可以根据自己的硬件和业务规模调整。需要先说明一点AI工程没有银弹也没有一套放之四海皆准的架构。小团队用一台带GPU的服务器就能撑起早期业务大团队才需要上Kubernetes和分布式推理。所以我在讲每个模块时都会区分“最小可用方案”和“规模化方案”你可以按需取用。2. 环境与依赖管理别让“在我机器上能跑”成为团队噩梦2.1 为什么AI项目的环境问题比普通后端更棘手普通后端项目的依赖相对稳定一个requirements.txt或go.mod基本能锁定。但AI项目不一样CUDA版本、cuDNN版本、PyTorch/TensorFlow版本、Python版本、显卡驱动版本这五者之间存在严格的兼容矩阵。我见过太多团队因为某台机器驱动版本差了一个小版本导致整个训练任务跑不起来排查半天才发现是环境问题。更麻烦的是训练环境和推理环境的需求往往不同。训练需要完整的框架和大量科学计算库推理只需要运行时和少量依赖。如果两者混用同一个环境推理镜像会臃肿到几个GB启动慢、攻击面大、传输成本高。所以从项目一开始就要有意识地把环境分层管理。2.2 用容器把环境“焊死”我的建议是从第一天起就用容器。哪怕你现在只有一台机器也值得花半天时间把Docker环境搭好。原因很简单——容器把操作系统层以下的差异屏蔽掉了你只需要关心镜像内部的依赖。一个典型的训练镜像Dockerfile大致长这样FROM nvidia/cuda:12.1.1-cudnn8-runtime-ubuntu22.04 ENV DEBIAN_FRONTENDnoninteractive ENV PYTHONUNBUFFERED1 RUN apt-get update apt-get install -y \ python3.10 python3-pip git curl \ rm -rf /var/lib/apt/lists/* WORKDIR /workspace COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . CMD [python, train.py]这里有几个细节值得说。第一基础镜像选runtime而不是devel除非你需要编译自定义算子否则devel镜像多出来的几个GB纯属浪费。第二PYTHONUNBUFFERED1这个环境变量很关键它保证Python的输出实时刷到stdout否则你在容器日志里看到的可能是延迟几分钟的缓冲内容排查问题时非常痛苦。第三pip install放在COPY . .之前这样只要依赖没变改代码时就能命中Docker层缓存构建速度快很多。推理镜像则要精简得多通常基于python:3.10-slim只装推理框架和必要的运行时库。如果你用ONNX Runtime或TensorRT镜像可以压到几百MB。2.3 依赖锁定的实操细节requirements.txt里写torch2.0这种松散的约束在团队协作里是灾难。今天装出来是2.0.1明天可能是2.1.0行为可能就不一样了。正确做法是用pip-compile来自pip-tools生成锁定文件pip install pip-tools pip-compile requirements.in --output-file requirements.txtrequirements.in里写你直接依赖的包requirements.txt里会生成包含所有传递依赖和精确版本号的锁定文件。这样任何人、任何时间构建出来的环境都是一致的。提示GPU相关的包如torch在pip-compile时要注意指定正确的index-url否则可能装到CPU版本。可以在requirements.in里用--extra-index-url指定或者干脆在Dockerfile里单独安装torch。2.4 我踩过的一个坑驱动版本与CUDA的“隐形墙”早期我做项目时遇到过训练任务在A机器上正常、在B机器上报CUDA error: no kernel image is available for execution on the device。查了半天代码没问题最后发现是B机器的显卡驱动版本太老不支持镜像里的CUDA 12.1。驱动版本决定了它能支持的最高CUDA版本这个信息在nvidia-smi输出的右上角能看到。所以团队里最好维护一张“机器-驱动-CUDA”对照表新机器加入集群前先核对。这个习惯能省下大量莫名其妙的排查时间。3. 数据管线AI工程里最容易被低估的“脏活累活”3.1 数据管线的三个核心诉求模型效果的上限由数据决定但数据管线往往是AI项目里最不被重视的部分。一个合格的数据管线要满足三点可复现、可扩展、可监控。可复现指的是给定同样的原始数据和同样的处理代码任何时候跑出来的训练集都应该完全一致。这听起来是废话但实际中因为随机种子没固定、文件遍历顺序不稳定、多进程写入竞争等问题很多团队的数据集每次生成都不一样导致实验无法对比。可扩展指的是当数据量从10GB涨到1TB时管线不需要推倒重来。这通常意味着要用流式处理而不是全量加载到内存用分布式文件系统而不是本地磁盘。可监控指的是你能知道每天新增了多少数据、有多少条被过滤掉、各类别的分布有没有漂移。没有监控的数据管线等于在盲飞。3.2 从原始数据到训练样本的典型流程一个完整的数据管线通常包含这几个阶段采集、清洗、标注、切分、特征化、打包。每个阶段都有坑。采集阶段要注意数据的合法合规来源以及去重。我见过一个项目因为训练集里混入了大量重复样本导致模型在验证集上表现虚高上线后一塌糊涂。去重可以用哈希如SimHash做近似去重成本低效果好。清洗阶段主要是过滤低质量样本比如过短、乱码、敏感内容。这里建议把过滤规则写成可配置的而不是硬编码在代码里方便后续调整。切分阶段要特别注意按时间切分还是随机切分。如果你的业务有时间属性比如推荐、风控随机切分会导致数据泄漏——用未来的数据预测过去离线指标好看但线上没用。正确做法是按时间划分训练集和验证集。特征化和打包阶段建议把处理后的数据存成列式格式如Parquet或专门的训练格式如TFRecord、WebDataset。列式格式压缩率高、读取快特别适合后续做特征分析。3.3 用DVC或类似工具管理数据版本代码有Git管理数据也需要版本管理。DVCData Version Control是一个常用方案它把大文件的元信息存在Git里实际数据存在对象存储或共享目录。这样你可以像git checkout一样切换数据版本。dvc init dvc add data/train.parquet git add data/train.parquet.dvc data/.gitignore git commit -m add training data v1当数据更新时dvc add会生成新的哈希你就能清楚地知道某次实验用的是哪个版本的数据。这个习惯在多人协作和长期迭代中价值巨大。3.4 数据管线的监控指标我一般会在管线里埋这几类指标每日新增样本数、过滤率、各标签分布、特征缺失率、处理耗时。这些指标推到Prometheus或简单的日志系统里配合告警。比如过滤率突然从5%涨到50%很可能是上游数据源出了问题早点发现能避免用脏数据训练出一版废模型。注意数据管线的监控不要只看“有没有报错”更要看“数据分布有没有异常”。静默的数据漂移比显式的报错更危险。4. 训练工程让实验可复现、可对比、可追溯4.1 实验管理的核心是“配置与代码分离”很多人的训练脚本里超参数是硬编码的改一个学习率要动代码。这在实验阶段是灾难因为你无法快速对比不同配置。正确做法是把所有超参数抽到一个配置文件里YAML或JSON代码只读配置。# configs/exp_001.yaml model: name: resnet50 num_classes: 10 train: batch_size: 64 lr: 0.001 epochs: 50 seed: 42 data: train_path: data/train.parquet val_path: data/val.parquet然后每次实验用一个独立的配置文件和独立的输出目录。输出目录里保存配置副本、日志、checkpoint、评估结果。这样半年后你回头看某次实验能完整还原当时的所有条件。4.2 随机种子与确定性深度学习的随机性来源很多权重初始化、数据打乱、Dropout、数据增强。要完全确定很难但至少要固定Python、NumPy、框架的种子import random import numpy as np import torch def set_seed(seed): 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 Falsecudnn.deterministic True会牺牲一点性能换取确定性实验阶段建议开启生产训练可以关掉。另外要注意多卡训练时数据并行的梯度聚合顺序可能影响结果完全确定性在多卡下较难保证但单卡实验应该做到可复现。4.3 Checkpoint策略不只是保存模型Checkpoint不只是保存模型权重还应该保存优化器状态、学习率调度器状态、当前epoch、随机数状态。这样中断后能精确恢复而不是从头再来。def save_checkpoint(state, path): torch.save(state, path) checkpoint { epoch: epoch, model_state: model.state_dict(), optimizer_state: optimizer.state_dict(), scheduler_state: scheduler.state_dict(), best_metric: best_metric, rng_state: torch.get_rng_state(), } save_checkpoint(checkpoint, fckpt/epoch_{epoch}.pt)保存频率上我一般每N个epoch存一次常规checkpoint同时单独维护一个“最佳指标”checkpoint。磁盘紧张的话可以只保留最近3个和最佳1个。4.4 训练日志与可视化日志要结构化方便后续分析。我习惯用CSV或JSONL记录每个step/epoch的loss、指标、学习率、耗时。可视化可以用TensorBoard或Weights Biases但底层数据一定要自己留一份避免工具停服后数据丢失。一个容易被忽略的点是记录每个epoch的耗时和显存占用。这能帮你判断瓶颈在哪也为后续推理部署的资源配置提供参考。4.5 分布式训练的入门建议如果你的模型单卡放得下先别急着上分布式。分布式训练引入的复杂度和调试成本很高收益在中小模型上不明显。只有当单卡显存不够、或者训练时间无法接受时才考虑数据并行DDP或模型并行。上DDP时最常见的坑是DataLoader的num_workers和batch_size设置。每个进程都要独立加载数据batch_size是单卡的总batch是batch_size * world_size。学习率通常也要按比例放大但具体放大多少需要实验。5. 推理服务从“能跑”到“扛得住”的跨越5.1 推理服务的核心指标训练关心的是收敛和精度推理关心的是延迟、吞吐、资源占用、稳定性。这四个指标往往互相制约。降低延迟可能牺牲吞吐提高吞吐可能增加延迟。所以第一步是明确业务需求是要求单次请求快如实时交互还是要求单位时间处理量大如离线批处理实时场景通常关注P99延迟比如要求99%的请求在100ms内返回。批处理场景关注吞吐比如每秒处理多少张图片。两者的优化方向完全不同。5.2 模型导出与格式选择训练框架PyTorch/TensorFlow直接做推理性能通常不是最优的。常见做法是导出成中间格式格式适用场景优点缺点ONNX跨框架、跨硬件通用性好工具链成熟部分算子支持不全TensorRTNVIDIA GPU性能极致绑定NVIDIA转换有门槛TorchScriptPyTorch生态转换简单性能提升有限OpenVINOIntel CPU/GPUCPU推理优化好绑定Intel我的经验是先用ONNX跑通如果性能不达标再考虑TensorRT。ONNX的转换相对简单torch.onnx.export基本能覆盖大部分模型。转换后一定要做数值对齐验证确保ONNX输出和原模型输出误差在可接受范围内通常1e-4以内。5.3 服务框架选型推理服务框架的选择要看团队技术栈和规模。简单的可以用FastAPI自己包一层灵活但需要自己处理批处理、并发、限流。成熟的方案有Triton Inference Server、TorchServe、TensorFlow Serving等。Triton是我比较推荐的它支持多框架、多模型、动态批处理、模型版本管理而且性能好。缺点是配置稍复杂学习曲线陡一点。对于小团队FastAPI ONNX Runtime也能撑起早期业务等量上来了再迁移。5.4 动态批处理吞吐提升的关键动态批处理Dynamic Batching是推理服务里提升吞吐最有效的手段之一。它的思路是不立即处理每个请求而是等一小段时间如10ms把这段时间内到达的请求合并成一个batch一起推理。GPU擅长并行计算batch越大单位算力的吞吐越高。代价是引入了额外延迟等待时间。所以批处理窗口要根据业务延迟要求调整。实时性要求高的场景窗口设小一点如5ms离线场景可以设大一点如100ms。Triton里配置动态批处理大致是这样dynamic_batching { preferred_batch_size: [8, 16, 32] max_queue_delay_microseconds: 10000 }preferred_batch_size告诉Triton优先凑成这些batch大小max_queue_delay_microseconds是最大等待时间。5.5 压测上线前的必修课服务写完了不代表能上线必须压测。压测要回答几个问题QPS到多少时延迟开始飙升显存占用随并发怎么变化长时间运行会不会内存泄漏压测工具可以用Locust、wrk或JMeter。我习惯用Locust因为可以用Python写测试逻辑方便构造真实的请求数据。from locust import HttpUser, task, between class InferenceUser(HttpUser): wait_time between(0.01, 0.05) task def predict(self): self.client.post(/predict, json{input: [0.1] * 768})压测时要从低并发逐步往上加观察延迟和错误率的变化曲线。找到“延迟开始非线性增长”的拐点那个点就是当前配置的容量上限。生产环境要留30%以上的余量。注意压测数据要尽量接近真实分布。用全零或随机数据压出来的结果和真实请求可能差很多因为不同输入的计算量可能不同比如变长序列。6. 监控、告警与持续迭代上线只是开始6.1 推理服务要监控什么服务上线后监控是眼睛。我一般分三层监控基础设施层CPU、内存、GPU利用率、显存、网络IO。这些用node_exporter和DCGM exporter采集Prometheus存储Grafana展示。服务层QPS、延迟分布P50/P95/P99、错误率、批处理大小分布。这些需要在服务代码里埋点用Prometheus客户端库暴露。业务层预测结果的分布、置信度分布、输入数据分布。这一层最容易被忽略但最重要。因为模型效果下降往往先体现在业务指标上而不是服务指标上。6.2 数据漂移与模型退化模型上线后输入数据的分布可能随时间变化这叫数据漂移。比如推荐系统里用户兴趣变了风控系统里欺诈手法变了。漂移会导致模型效果逐渐下降但服务本身一切正常不会报错。检测漂移的常用方法是统计输入特征的分布和训练时的分布做对比。可以用PSIPopulation Stability Index或KL散度。当漂移超过阈值时触发告警提示需要重新训练。import numpy as np from scipy.stats import entropy def kl_divergence(p, q): p np.asarray(p, dtypefloat) 1e-10 q np.asarray(q, dtypefloat) 1e-10 p / p.sum() q / q.sum() return entropy(p, q)实际中更简单的方法是监控关键特征的均值和方差变化超过一定比例就告警。这个方法粗糙但有效。6.3 模型版本管理与灰度发布模型更新不能一刀切全量替换要支持灰度。常见做法是同时加载多个版本的模型按流量比例或用户分组路由。Triton支持多版本共存可以通过请求里的版本号指定。灰度发布的流程一般是新模型先接1%流量观察一段时间几小时到几天指标正常再逐步放大到10%、50%、100%。任何阶段指标异常都能快速回滚。回滚要能做到秒级所以旧版本的模型文件不能删服务要支持热切换。6.4 持续迭代的闭环一个健康的AI系统应该形成闭环线上数据回流 → 数据清洗标注 → 重新训练 → 评估 → 灰度发布 → 监控。这个闭环转得越快模型迭代越高效。闭环里最容易卡住的是数据回流和标注。建议从项目早期就设计好数据回流机制比如把线上请求和预测结果在合规前提下落盘定期抽样标注。不要等到模型效果不行了才想起来要数据。7. 一些关于成本与团队协作的实在话7.1 GPU成本控制GPU是AI项目里最贵的资源。控制成本有几个方向一是提高利用率通过动态批处理、多模型共享GPU、混部训练和推理来摊薄二是选对卡型不是所有任务都需要A100/H100推理用T4或L4往往性价比更高三是用好竞价实例或弹性调度训练任务对中断的容忍度比推理高。我见过不少团队GPU利用率长期低于30%这是巨大的浪费。建议定期统计GPU利用率低于50%就要考虑优化调度或合并任务。7.2 团队分工与协作AI工程不是一个人能全包的。小团队里算法工程师往往要兼做工程这时候要优先把“可复现”和“可监控”做好别追求一步到位的完美架构。大团队里算法、数据、平台、运维要明确边界接口用契约固定下来避免互相等待。一个实用的建议是把模型训练和推理服务的接口定义清楚输入输出格式、性能要求两边可以并行开发。算法同学专注模型效果工程同学专注服务性能通过接口对接。7.3 文档与知识沉淀AI项目迭代快人员流动也快。没有文档新人上手要花几周。建议至少维护三类文档环境搭建文档、数据管线说明、模型服务接口文档。文档不用写得多漂亮但要保证“照着做能跑起来”。我在实际项目里体会最深的一点是AI工程的难点从来不是某个单点技术而是把数据、训练、推理、监控这些环节串成一条稳定运转的流水线。每个环节单独看都不复杂但组合起来任何一个环节的疏漏都会在线上放大。所以从零搭建AI工程能力最重要的心态是“把每个环节都当成生产系统来对待”而不是“先跑通再说”。跑通只是起点稳定、可观测、可迭代才是目标。
返回列表