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

资讯详情

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

AI工程从零到生产可用:环境、数据、训练、部署与监控全链路实践

AI工程从零到生产可用:环境、数据、训练、部署与监控全链路实践 看到 ai-engineering-from-scratch 这个标题很多人第一反应是“从零开始学机器学习手写一个神经网络”。老实说我一开始也是这么想的但真把一个AI项目从零做到生产可用之后我发现“from scratch”的重点压根不在算法本身而在于从零到一的那条工程链路。这篇文章是我对自己过去大半年AI工程实践的完整复盘包括环境、数据、训练、部署、监控这几层的真实操作和避坑经验。适合准备转AI方向的工程师、刚启动AI项目的团队以及那些已经能跑通模型、却总在落地上翻车的朋友。1. “模型能跑”和“系统能用”重新定义from scratch1.1 为什么很多项目卡在Notebook这一步我见过太多团队模型在Notebook里效果惊艳一上生产就事故频发。有一个很典型的项目算法同事花三周微调模型AUC做到0.9以上大家都很兴奋。结果接进业务系统发现SQL取数少了一个筛选条件、特征字段类型不一致、上线后模型预测分布和离线测试差了一大截项目硬生生拖了一个月。原因很简单大家把自己定位成了“会写训练代码的人”而不是“对整个系统负责的人”。AI工程如果只有算法那只能算实验加上数据、接口、监控、迭代才能叫工程。所以这里我要先重新定义一下“from scratch”。它指的不是从线性回归手写开始而是从“你如何定义问题、如何衡量成功、如何把结果送到用户手里”这一整套链路开始。算法反而是这条链路里最容易被替换的部分。1.2 一条完整的AI工程链路包括哪些环节我习惯把它理解成一条流水线问题定义你要解决的真实业务问题是什么用户痛在哪里指标设定怎么量化“做得更好”用什么线下指标代理线上效果数据获取数据从哪些系统来权限、口径、更新频率分别是什么数据验证数据是否符合预期格式脏数据和缺失怎么处理特征工程特征怎么加工、存储、在线离线一致性怎么保证模型训练选基线、实验追踪、资源调度、结果回放评估验证在接近线下的数据分布上做评估而不是看一眼Loss部署上线服务化、性能压测、灰度发布、回滚预案监控反馈延迟、错误率、数据分布漂移、业务结果反馈你会发现模型训练只占全流程的十分之一左右但它却是大多数入门者唯一关注的部分。这也是为什么很多项目“从零开始”却“死在路上”——不是算法不行而是前面的地基和后面的运维没人管。1.3 动手之前先定义“完成”这里我强烈建议在写第一行代码之前就把“完成”写清楚。什么叫这个项目做完我通常会用一份一页纸的需求说明来约束目标用户和决策场景线上线下评估指标以及目标值数据源和更新频率接口协议输入输出格式、延迟要求、吞吐要求失败预案模型挂了是返回兜底结果还是直接降级上线后的监控项和告警阈值别觉得这些是文档工作它们才是工程的骨架。我见过很多团队模型训练完才发现接口协议没定、特征存储方案没选、离线在线特征对不齐导致返工。这些问题越早确认后期越省力。2. 第一层地基环境、依赖与可复现性怎么一次到位2.1 GPU驱动、CUDA和框架版本的排列组合从零开始的第一步就是环境。你可能觉得很简单实际上这是第一个大坑。我在一个全新服务器上跑PyTorch花了整整半天才把CUDA版本和驱动版本对齐。核心原因每个深度学习框架都有对应的CUDA版本要求而CUDA版本又依赖NVIDIA驱动的最低版本。网上很多教程直接写pip install torch结果跑到30%的时候突然告诉你libcuda.so不存在。我自己现在用的一套稳定组合是这样的可以给你参考Ubuntu 22.04服务器驱动 550.x对应支持CUDA 12.4CUDA Toolkit 12.4主要在容器内Python 3.11PyTorch 2.3 cu124注意宿主机不一定需要装完整的CUDA Toolkit很多时候只需要驱动支持。真正编译和运行都在容器里完成靠nvidia-docker把GPU映射进容器。这样做的好处是训练环境和生产环境可以完全一致不用在宿主机上折腾各种版本。2.2 用锁定文件替代一长串requirements.txt很多人习惯在requirements.txt里写numpy1.21 pandas1.3 scikit-learn1.0这种写法在AI项目里很危险。因为四个月之后重新构建环境依赖会变成新版本你根本不知道哪个包升级导致模型分变了。如果遇到需要复现事故现场的场景你会非常被动。更靠谱的做法是用锁定文件。我目前是uv的忠实用户或者用poetry也可以核心思路一样pyproject.toml里写宽松的版本范围uv.lock里锁定精确版本和传递依赖。任何依赖升级都通过重新生成锁文件来完成并且要经过CI检查。简单理解锁文件让“从零搭建环境”这件事变成确定性的而不是掷骰子。这对个人项目可能无所谓但对需要长期维护的AI工程来说几乎等于保险。2.3 Docker镜像把运行时固化依赖锁定之后还要保证操作系统层面的运行环境一致。我的做法是把基础镜像固定到一个不带latest的版本。一个比较典型的DockerfileFROM nvidia/cuda:12.4.1-runtime-ubuntu22.04 RUN apt-get update apt-get install -y --no-install-recommends \ python3.11 python3.11-venv \ rm -rf /var/lib/apt/lists/* WORKDIR /app COPY pyproject.toml uv.lock ./ RUN pip install uv uv sync --frozen COPY . . CMD [uv, run, python, serve.py]这里每一步都有讲究用nvidia/cuda而不是python镜像避免GPU环境不匹配用--frozen安装锁文件保证和开发一致先复制依赖文件再复制代码这样依赖层可以复用Docker构建缓存改代码不用重新装包。2.4 环境的可复现性会一路影响你到线上我有一次经历了特别深刻的教训训练环境是CUDA 11.8生产机器上镜像里的CUDA是11.2很多算子没走最优实现推理速度比训练环境慢了三倍。排查了一下午才发现是CUDA版本不一致。从那以后我每次新项目只做两件事第一在项目目录里写README.md记录CPU/GPU/Python版本第二所有跑模型的流程全部容器化。这个习惯带来的省心程度我觉得值回票价。3. 第二层地基数据管线里最常见的四类事故3.1 数据来源不透明很多AI工程的第一份数据来自业务部门导出的Excel或数据库查询结果。问题是你要做的是长期线上系统不是一次精度的学术实验。我踩过的坑某同事离职后他写的取数SQL里有个特殊条件没人知道导致我们训练集和未来的线上特征分布差一截模型效果一直不稳定。后来我规定项目内必须有一个表格登记每个数据集的来源、取数时间、过滤条件、负责人和更新周期。你可以用简单的data_sources.md或者直接做进数据库元数据表里。核心目标是六个月后哪怕原负责人离职别人也能搞清楚这份数据是什么。3.2 数据验证是保命符数据验证在我眼里不是可选项而是必须项。尤其当多个部门的数据汇入时字段类型、缺失率、取值范围随时会变。我推荐用pandera或Great Expectations做Schema校验。下面是一个最简例子import pandera as pa schema pa.DataFrameSchema( columns{ user_id: pa.Column(str, uniqueTrue), age: pa.Column(int, pa.Check.in_range(0, 120)), click_count: pa.Column(int, pa.Check.ge(0)), logined_at: pa.Column(pa.DateTime), } ) validated_df schema.validate(df)把这样的校验放在数据接入任务的第一步一旦数据异常就阻断管道而不是让脏数据流到模型训练里。很多人觉得这样会拖慢流程但一个异常值导致模型训练中断所浪费的时间远超校验本身的开销。3.3 数据集本身也要做版本管理代码有Git版本模型有权重文件版本但数据集呢很多团队的数据集就放在共享目录里文件名类似train_20241001_v2_final.parquet然后过几天又出现一个v2_final_2。这几乎注定会出问题。我的做法是用dvc或lakeFS这种工具把数据集和代码版本绑定。DVC的核心用法很简单dvc add data/train.parquet git add data/train.parquet.dvc数据集本身放在远程存储Git只记录版本指针。这样回滚代码时可以同时回滚到对应的数据版本整个实验才是可复现的。对于小项目哪怕只是给训练数据打一个带时间戳的标签也比文件名叫final强得多。3.4 数据泄漏最隐蔽的坑数据泄漏不是一个“踩到”的问题而是“踩到你还没发现”的问题。典型的场景用全量数据的统计值做归一化然后才划分训练测试集。这会让你在测试集上的指标虚高但线上效果一落千丈。因为归一化用的min/max已经“见过”测试集的信息了。我能够给到的最实用的建议是在切分数据之前就关闭一切涉及全量统计的特征工程操作所有标准化参数只在训练集上fit再应用到验证集和测试集。还有一个容易被忽视的点时间序列数据不能用随机切分必须按时间顺序切分否则预测未来就变成了“偷看未来”。这一点请在项目初始化时就写进团队的开发规范里。4. 训练实验的工程化别再用Notebook碰运气4.1 第一个训练的模型必须小到能跑通我在新项目里的习惯是先不碰大模型不碰复杂网络先跑一个不超过100行代码的基线。假设做用户点击率预测先上一个带一阶特征的Logistic Regression做图像分类先把图片缩小到64x64用三四个卷积层堆个小网络。目标很简单打通“数据读取-训练循环-评估输出-日志记录”这条管线让整个流程在半小时内跑完。这个基线的价值有两点第一它验证了数据管线和训练代码本身没有低级错误第二后期所有复杂模型都要和这个基线对比如果没有显著提升说明问题可能在特征或者数据上而不是网络深度不够。4.2 用实验追踪工具记录每一次尝试深度学习实验里最糟糕的状态是“我记得上次跑了一个AUC 0.92的实验但参数是什么来着” 这种场景只要连续调十组参数就会遇到。我现在用MLflow来管理没有特殊原因就是部署简单、UI直观可以和代码仓库解耦。核心概念是每次运行记录三样东西Parameters学习率、batch size、网络层数、随机种子Metricsloss、AUC、F1每个epoch的变化Artifacts模型权重、特征重要性、预测结果样例比如一个训练脚本里with mlflow.start_run(): mlflow.log_param(lr, lr) mlflow.log_param(batch_size, batch_size) mlflow.log_metric(val_auc, val_auc) mlflow.log_artifact(model.pt)同样一个实验跑一百次也不怕随时可以回到某一次运行查看完整记录。对于任何团队来说这都不应该是一个可选优化而是一门必修课。4.3 评估指标要贴近线上业务我看到很多人在分类任务里默认看准确率但准确率在类别不平衡的数据集上几乎没有任何参考意义。举一个极端例子欺诈交易占比0.1%模型把所有交易都预测为正常准确率也能有99.9%但它根本毫无价值。通常我会根据业务场景选一组指标组合场景更合适的指标欺诈检测召回率、PrecisionTopK商品推荐离线AUC、线上CTR/CVR提升搜索排序NDCG、MRR回归预测MAE、RMSE、业务自定义分位误差更重要的是离线指标只是线上效果的代理。我在一个推荐项目里离线AUC提升0.02线上点击率不升反降。后来发现离线测试集的分布和线上分布差太多样本里的用户活跃度整体高出一截。所以后来我养成了习惯做离线评估时尽量从线上采样一段时间的真实流量做回放模拟未来环境。4.4 随机种子和数据集切分的纪律为了让实验结果可比较需要把随机种子固定下来。否则同一组超参数跑两次指标波动可能比超参数本身带来的差异还大你就没法调参了。实际操作中我在训练入口固定Python、NumPy、PyTorch和DataLoader的随机种子。但也要知道在多卡并行和部分算子下绝对的确定性很难做到。所以更诚实的做法是同一个配置至少跑三次看均值和方差。如果方差大于不同配置之间的差距那说明噪声淹没了信号先不要急着下结论。5. 上线不是终点监控、灰度与模型救火预案5.1 服务化方案选型先别急着上重型推理框架模型训练完之后你需要把它暴露成一个HTTP接口。市面上的方案很多我见过不少人一上来就上Triton Inference Server结果部署半天还没跑通。我的建议是分层考虑单机小模型、业务初期直接用FastAPI包一个接口加上一个进程缓存十几行代码搞定。有一定并发要求、模型较多考虑TorchServe或BentoML模型仓库和对外API开箱即用。高并发、低延迟、需要动态批处理再上Triton它确实能最大化GPU利用率但运维复杂度也上一个量级。大多数项目刚开始根本用不到GPU持续满载所以没必要过度设计。能用一个简单的FastAPI服务跑起来配合负载均衡已经解决了80%的问题。5.2 性能和成本之间找平衡模型部署后我一般会压测三个指标P95延迟、QPS、GPU显存占用。有个很典型的经验如果模型推理不是瓶颈瓶颈往往在特征拼接和数据库查询。很多模型服务慢不是算力不够而是每来一个请求都去查一次数据库导致延迟飙升。优化思路通常是把高频特征放在Redis或进程内缓存能批量推理就批量推理不要逐个请求穿模型对无回退必要的模型适当降低输入精度比如float16替代float32还有一点不得不提推理服务要设置超时和熔断。模型一旦变慢或崩溃不能把整个业务拖死。宁可返回兜底结果也不要无限等待。5.3 监控什么不止延迟和错误率我曾经天真地以为监控只包含延迟、QPS和错误率。后来一次事故教育了我模型接口一切正常但业务方反馈推荐结果越来越离谱。查了半天才发现线上特征的数值分布发生了漂移模型碰到没见过的分布输出开始退化。所以AI工程的监控至少分三层系统层延迟、QPS、错误率、GPU使用率特征层每个特征的最大值、均值、缺失率、取值频率和训练集做对比预测层预测分数分布、各类别占比、拒识率特征层监控可以用类似Evidently AI的开源库定期计算数据漂移指标。当PSI或KS超过阈值时就触发告警。这个告警往往比系统级告警更早暴露问题。5.4 灰度发布与回滚预案模型发版要像代码发版一样谨慎。我的流程通常是先在线上开启流量影子模式把真实请求复制一份到新模型但不影响业务。影子模式跑几天对比新旧模型预测分布和线下指标。然后按5%、20%、50%逐步切流量。每一步都看线上业务指标是否改善不改善就回滚。回滚在模型场景里有一点特殊旧模型的权重文件必须保留而且特征管道也要和旧模型配套。如果新模型依赖新的特征版本回滚时特征管道也得一起回滚。所以版本管理要把“模型权重特征配置”绑定成一个可发布的包而不是只存一个model.pt。6. 一次真实交付复盘三个坑、三张清单、一条收敛路线6.1 我在这个从零项目里踩过的三个坑第一个坑是环境问题。项目开始的第一周我们花了两天时间统一CUDA版本和容器镜像。如果一开始就把这个事按规范做完其实一天都用不了问题在于大家各自用自己的本地环境最后联调时发现同一个模型在不同机器上效果不一致。后来的教训是从第一天起所有训练统一在一个固定Docker镜像里跑不允许本地裸环境训练。第二个坑是数据时间窗口错位。我们把用户特征和标签按不同的时间窗口聚合结果训练时没发现问题上线后模型效果很诡异。花了很久才意识到有的特征统计的是当天数据而标签是未来三天的转化率两者基准时间不一致。一旦梳理清时间口径模型效果立刻回升。第三个坑是上线后没有反馈回路。模型预测完但业务团队没有把用户是否采纳结果存下来。少了这一步所有后续优化都是在盲人摸象。后来我们强制要求每次模型产出都记录一个唯一ID用户行为回流后和预测ID做关联形成真正的效果评估闭环。6.2 如果重新开始我会照这几张清单走如果让我再启动一个From Scratch的AI项目我会按这个顺序走一、环境清单统一Docker基础镜像固定标签Python版本、CUDA版本、框架版本写入README锁文件生成并通过CI校验二、数据清单每个数据源登记来源、更新周期、负责人Schema校验和异常处理写进管道入口数据集切分规则提前定好时间序列按时间随机问题固定seed三、交付清单接口协议先行输入输出字段、延迟要求、错误码离线评估报告包含指标定义和基线对比灰度方案和回滚方案在协作区先过一遍这套清单看起来很笨但真的能救命。我自己在那些省略清单的项目里无一例外都付出了额外时间。6.3 不同基础的人起点完全不同如果你是算法工程师想补工程能力直接从“数据版本管理部署监控”入手这两层是你最容易忽视但最容易出问题的。如果你是后端工程师想转AI工程先别急着啃论文把特征存储、模型服务化、数据漂移这些概念弄明白再回头去理解训练代码就顺了。如果你是刚入门的学生我不建议第一周就搭深度学习环境先拿现成框架跑通一个小项目再逐步把工程环节补上。每个人眼里“from scratch”的起点不同但只要把整条链路拆开来找到自己最不熟悉的一段去补习方向就不会跑偏。最后再分享一个小技巧我在每次新项目里都会把第一个模型部署到线上作为一个“Hello World”。哪怕这个模型很弱只要接口通了、监控有了、特征管道完整了后面所有迭代都是在这个稳定骨架上进行。这种做法让我绕开了无数“最后集成”才爆发的问题。后来我把整个过程重新整理成了一条可复用的实践路线名称就叫 ai-engineering-from-scratch。如果你现在也正准备从零起步可以拿这份清单当镜子对着照一遍自己的环境、数据和流程大概率能保住你几个周末的时间。
返回列表