ARTICLE DETAIL

资讯详情

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

从零搭建AI工程:技术选型、数据管线与部署监控实战指南

从零搭建AI工程:技术选型、数据管线与部署监控实战指南 说实话我第一次见到“ai-engineering-from-scratch”这个标题时脑子里浮现的是很多朋友问过我的同一个问题“我想自己搭一套AI系统但网上教程要么太浅只讲调用API要么太深直接甩论文公式到底有没有一条能完整走通的路”我的回答是有但你先得把“AI工程”这四个字拆开看清楚。它不等于训练一个模型也不等于会调库。它是一整套从数据到服务的工业化流程。这篇文章不讲花活我不带你啃Transformer源码也不带你复现GPT。我会用我自己做过的项目经验把从零搭建一个AI工程需要的技术选型、数据管线、训练评估、部署监控、踩坑修复完完整整捋一遍。适合谁看想系统入门AI工程化的后端工程师、算法岗新人、以及想在公司内部从零搭一套可维护AI系统的技术负责人。看完你至少知道每一步该用什么、为什么这么选、出了事怎么查。1. 我为什么坚持从零搭AI工程而不是直接用现成平台1.1 AI工程不等于训练模型很多人对AI工程有个误解觉得“我本地能跑通一个模型工程就算搞定了”。这是完全错误的。本地notebook里能跑通只是拿到了一个模型文件。真正的AI工程是你这个模型文件得能被人稳定调用、能被监控、能被持续迭代、换台机器还能复现结果。我之前帮一家公司做过一个客服工单分类系统的项目数据量不大两万多条文本模型用中等规模的预训练模型微调一下准确率很快就上去了。但上线才是噩梦的开始数据源每天都会进来新格式的工单标签体系时不时要调整训练环境换一台机器就装不齐依赖模型更新一次要停服半小时。这些问题没一个跟模型网络结构有关全是工程问题。所以“AI engineering from scratch”里的“engineering”核心是流程化、自动化、可观测。你把数据、训练、评估、部署、监控这五个环节串成一条流水线每个环节都有版本、有记录、有回滚能力这才叫AI工程。1.2 什么时候值得自建什么时候该用现成平台先说结论不是所有场景都需要从零搭一套。我判断的标准是三个问题第一个问题你的数据能不能出公司网络很多业务数据涉及用户隐私出域即不合规。这种情况下云上训练平台、云端推理API都是空中楼阁你必须在自己可控的服务器上把整条链路搭起来。第二个问题你的推理成本有没有量级约束如果每天百万次调用量走第三方API单次几分钱一年下来不是小数目。自己部署开源模型综合算下来很多时候是省钱的。第三个问题你的业务是否需要频繁定制现成平台的算法是黑盒调参空间有限。业务上要频繁改标签体系、要跑自定义评估逻辑、要深度调试badcase黑盒会让你干着急。如果上面三个问题有两个答案是“是”那你就值得从零搭一套自己的AI工程链路。但提醒一句自建意味着你要同时扛住技术架构和数据安全的责任不是领导说“你自己研究一下”就能糊弄过去的。1.3 自建AI工程能带来什么好处自建最大的收益不是省钱是可控性和迭代速度。自己掌握全链路之后我发现有几件事是直接用平台办不到的第一训练数据和线上数据的一致性。平台方案很容易出现“线下用了A数据分布线上走的是B数据管道”最后线上效果崩了都找不到原因。自建后我会强迫自己在同一套数据管理规范下工作。第二模型的独立部署能力。模型文件、预处理逻辑、特征代码打包成一个镜像扔到哪台机器都能跑不再依赖平台环境。第三对指标的归因能力。模型线上指标降了是数据变了、还是环境变了、还是用户行为变了自建后每一步都有日志能一层层查下去。我在前面铺这么多是想强调一件事从零搭AI工程最先搭的应该是工程思维而不是代码。思维没转过来工具再新你也只是在一个大黑盒外面蹭一蹭。2. 从零开始的技术栈选型一套能跑通的最小组合2.1 编程语言与深度学习框架编程语言这块目前做AI工程Python是事实上的标准你不用太纠结。虽然我偶尔也会用Rust或Go去写高性能推理服务但那是纯工程侧的优化手段。从零开始、团队协作、快速迭代Python的生态优势没法绕开。PyTorch或者说它的生态目前也是深度学习实现和部署的最优选没有悬念。TensorFlow还有存量项目在用但如果你是从零开始我建议直接站在PyTorch这边理由有三个第一个PyTorch的动态计算图对调试友好断点打进去能看到每一层tensor的shape和值这在排查模型问题时能省很多时间。第二个PyTorch的生态从训练到部署是贯通的你用torch写好模型可以torchscript脚本化可以转成ONNX标准格式可以上TensorRT指令集优化。第三个社区活跃度在这两年已经把老前辈甩开了一条街HuggingFace生态、各种新模型的实现都是PyTorch优先。2.2 周边工程组件的职责划分语言和框架定了接下来要选一套“最小组合”。我不是那种让团队一口气上几十个组件的激进派组件越多维护成本越高。AI工程从零起步有一个组合我用了很久走通全流程绰绰有余还是直接列个表吧清晰一点。环节推荐选型职责边界环境管理conda requirements.txt锁定Python版本和依赖版本保证可复现数据管理本地存储 数据集版本号原始数据、清洗数据分目录不覆盖旧版本训练框架PyTorch PyTorch Lightning可选封装训练循环减少样板代码实验记录MLflow记录参数、指标、模型产物服务封装FastAPI提供HTTP推理接口自带OpenAPI文档容器化Docker docker-compose统一训练/推理环境任务调度cron 或 APScheduler前期够用定期触发训练、评估任务监控Prometheus Grafana采集接口延迟、调用量、推理结果分布这套组合里我最想重点解释的是MLflow。很多人觉得实验记录就是个“日志功能”随便找个Excel表记一下不也一样。真不一样。MLflow把每次训练的hyperparameters、metrics、模型权重、还有额外产物比如词汇表、预处理配置都按run为单位绑定在一起。你回头看的时候不是看一行“准确率0.92”而是能一键拉起来那个0.92的模型跑个推理或者复跑它当时的训练参数。这是Excel做不到的。2.3 依赖管理与环境隔离这个环节的坑我踩得最深。早期我做项目直接在自己电脑的全局Python环境里pip install装了十几个包。后来换电脑、换服务器环境一塌糊涂pandas版本冲突、torch版本和CUDA版本对不上一跑就崩光恢复环境就花了整整两天。现在我的做法是每个项目一个独立的conda环境Python版本和依赖版本全部用requirements.txt锁死。打个命令就是一套干净环境conda create -n ai-eng python3.10 -y conda activate ai-eng pip install -r requirements.txtrequirements.txt里我做的第一件事不是全列出来是先锁主版本。比如给出一句说明把核心依赖加精确版本号传递依赖让pip自己解析。通用于工程的稳定版方案是把torch这类底层依赖严格锁版本其余库允许次要版本浮动torch2.1.2 transformers4.36.2 pandas2.0,2.2 fastapi0.109.0 uvicorn0.27.0 mlflow2.9.2版本漂移是AI项目最隐蔽的杀手。你三月份训练模型用的是torch 2.0七月份更新了torch 2.2重新加载同一份checkpoint做推理结果有细微差异。绝大多数场景差异可忽略但一旦业务刚好在边界值上线上判断就变了。所以工程上模型训练环境一旦稳定就不要频繁升级底层依赖。真需要升级就当成一次全新的重建项目重新跑一遍完整验证流程。3. 数据管线的搭建AI工程里最容易被低估的环节3.1 数据采集与清洗的工程化在AI工程里数据管线和模型训练一样重要但它的好处很难被量化。模型上线要100%的努力但数据线的价值会体现在长期稳定性上。数据不干净模型再强也白搭。我给团队立过一个铁规矩原始数据一律不动清洗逻辑必须可重跑。操作上就是三个目录raw原始数据只读、processed清洗后数据可重建、feature特征加工后数据可重建。清洗脚本必须输入输出明确跑多少次结果都一样。举一个我在工单分类项目里遇到的例子。工单文本是从企业内部多个系统汇总来的有的带HTML标签有的带乱码字符有的工单就是转发链里的一串碎片。我把清洗规则写成一条管道去HTML标签、统一大小写、全角半角归一化、去掉URL和邮箱、去掉重复标点。每条规则单独一个函数单元测试保证不会把“Python 3.10准时发布”这种正常文本里的大小写搞坏。清洗管道的好处是后期发现某个清洗步骤太激进改一个函数重新跑一遍管道就行不用从头手动清洗一批新数据。3.2 标注流程与数据质检标注是AI工程里最容易被低估工作量的一环。很多人以为标注就是找几个人点点鼠标实际上标注质量直接决定模型天花板。我记得当时客服工单分类项目的标签体系是6个一级类目、21个二级类目一开始标注一致性不到70%模型怎么调都上不去。后来我跟团队建立了两轮审核机制第一轮两个标注员独立标注同一批样本第二轮分歧样本由小组长仲裁。每周随机抽5%的标注结果做交叉复核算标注员一致率。标注一致性低于85%这批标注数据不进训练集。这个质检机制看起来增加了工作量但对比一下后面模型返工调badcase的时间比标注质检的时间多得多。3.3 数据切分与防泄漏数据切分这个环节如果做得粗糙后面线上指标和线下指标就会出现很大的落差。我在这个坑上栽过输得非常彻底后面专门写一节完整复盘。这里我只想强调一个核心原则切分数据时你要想清楚模型上线之后“看到的真实世界”长什么样然后尽量让测试集模拟那个世界。文本分类项目最常见的问题就是按行随机切分导致同一个用户的多条相似工单同时出现在训练集和测试集。模型等于提前偷看了答案线下指标虚高线上真实场景根本没有这种“同源数据”帮忙。我现在的做法是先按业务主体分组再在组维度上做切分。比如用户维度的数据就保证同一个用户所有记录只在切分的某一侧时间序列数据就按时间窗口前80%时间段做训练、后20%做测试绝不乱序。3.4 数据版本管理数据要不要做版本管理我的经验是训练集、验证集、测试集一旦确认下来就是基线数据版本。项目期两个月起步的情况下数据变化很频繁业务方今天加一批标签明天删一批历史数据后天告诉你字段格式变了。如果没有版本管理回头复现模型效果你根本不知道这次训练用的到底是哪批数据。轻量级方案用DVCData Version Control但是前期有一个更简单、零依赖的做法每次构建数据集把所有样本的hash清单存一份。训练完模型记录里同时写上数据集的hash。下次任何人质疑“这个结果是不是这个数据跑出来的”拿hash一比就能确认。我倾向于前期先用hash目录快照数据复杂到多个来源协作时再上DVC。4. 模型训练与评估让训练过程可以被复现4.1 从基线模型起步别一上来就上大模型很多人从零搭建AI工程容易犯一个毛病跳过基线、直接冲最新模型。大模型确实强但你要先搞清楚一个问题——你的业务和它适配不匹配。而且你连一个能稳定跑通的baseline都没有直接上复杂模型出了问题你根本没法归因。我的做法是三步走。第一步先跑一个简单的启发式方法。比如文本分类可以直接用TF-IDF逻辑回归。这一步你不用它上线它的价值是给后续模型定一条“及格线”。如果深度学习模型连TF-IDF都打不过那问题多半不在模型在你前面数据管线。第二步上一个中等规模的预训练模型像较小的预训练中文模型或者英文模型跑通完整的finetune流程把训练、评估、部署链路全部验证一遍。第三步模型迭代优化再考虑更大参数量的模型。这个节奏看起来慢实际上是最快的。因为你每一步都有可比较的对象每次改动影响的是“变好还是变坏”而不是“不知道为啥跑到哪儿了”。4.2 训练超参数的设置逻辑超参数这块最核心的几个是学习率、batch size、训练轮数epochs。很多人直接照搬论文或者默认值然后抱怨模型不收敛。我给个基础建议文本分类微调学习率在1e-5到5e-5之间调batch size 16到32epochs 3到5加early stopping。但更关键的是你得理解这些参数的交互逻辑。学习率太高loss会震荡有时还爆掉学习率太低收敛慢还容易卡在局部最优。batch size大训练稳定但占用显存多需要相应调大学习率。epochs太多模型记住训练集的细节泛化能力反而下降。早期我调试的时候习惯先把训练曲线画出来。如果训练集loss下降正常、验证集loss居高不下那大概率是过拟合调整方向是增加数据增强、加正则或者降低模型容量。如果训练集loss都不降那才是调学习率、调模型结构的问题。方向错了你再调多久都是白费。从工程角度每次训练的运行信息我都记进MLflow包括seed。很多人会忽略随机种子但复现实验时随机种子不锁结果差0.5个百分点完全无法定位是模型改了还是随机波动。虽然繁琐但这是我的底线动作。4.3 评估指标体系评估指标的选择不是一个数学偏好问题是业务导向问题。准确率这个指标有很多场景并不适用比如类别严重不平衡时你把所有样本都预测成多数类准确率也能到90%。如果你的业务里漏掉少数类样本的代价远高于误报的代价——比如风控场景、疾病筛查场景——你必须在评估阶段模拟这种代价不对称。我常用的做法是每轮训练后除了看准确率还要银行取款机一样地看完整的分类报告。分类报告包含精确率precision、召回率recall、F1值。精确率和召回率是一对跷跷板你提高精确率预测为正的里面真正正的多召回率往往会下降该抓的正样本没抓全反之亦然。你要根据业务去选F1权重的形态业务更怕漏判那就让测试集里少数类的recall权重更高业务更怕误报那就让precision优先。我习惯在评估阶段再额外存一份badcase明细就是被模型分错的样本列表。每一条badcase都带上原文、真实标签、预测标签、模型置信度。这四个信息对跨团队沟通太重要了。业务部门提一句“我感觉这模型经常出错”你手里有他业务场景的具体例子拿数据说话比对着综合准确率解释半天有效得多。4.4 实验管理与模型注册实验管理的核心目的是一个灵魂拷问三个月后你还能不能毫无痛苦地知道“当前线上这个模型当初是怎么训出来的”MLflow的run记录我要求的详细程度是这样的训练脚本的hash、数据集的hash、关键超参数、模型产物地址、所有的评估指标、以及当时的备注比如“修复了标签噪声问题”“增加了XX类样本”。有了这些任何人接手项目都能在一个小时内复现当时的实验而不是看半年前一封含混的邮件。模型注册假设你有严格的上线流程候选模型先注册到staging预发布区跑完离线回测、小流量灰度再晋升到production生产区。这个过程最终沉淀为一份“上线审批单”记录了模型版本、数据版本、评估结果、回放验证人。这套流程全走下来一个新模型从训练到上线我基本确定风险可控。5. 部署上线与推理优化从本地notebook到真实服务5.1 服务化封装模型在notebook里跑得再欢也只是个半成品。线上要提供稳定的推理能力就得把模型包成一个HTTP服务。我现在的主力方案是FastAPI因为它写起来简单、自带接口文档、异步能力成熟团队协作容易上手。接下来是代码示例一个非常基础但能直接跑的推理服务from fastapi import FastAPI from pydantic import BaseModel app FastAPI() class InferRequest(BaseModel): text: str class InferResponse(BaseModel): label: str confidence: float MODEL None TOKENIZER None def load_model(): # 全局只加载一次避免每次请求重复加载 global MODEL, TOKENIZER MODEL, TOKENIZER load_pretrained_model() load_model() app.post(/predict, response_modelInferResponse) def predict(req: InferRequest): inputs TOKENIZER(req.text, truncationTrue, max_length128, return_tensorspt) outputs MODEL(**inputs) # softmax 省略直接取当条最大概率与对应标签 label, confidence decode_output(outputs) return InferResponse(labellabel, confidenceconfidence)这里有两个工程细节值得说一下。第一个模型和tokenizer在进程启动时加载一次放进全局变量。如果每次请求都重新加载服务根本撑不住压力。第二个解码部分把训练时的id2label映射保留下来写进一个配置文件而不是硬编码在代码里。模型一更新id2label有可能变配置化改起来就是改一个json文件。推理服务上线前我还会压一遍最基础的性能指标单并发下的P99延迟、请求成功率、GPU显存占用。这些数字给后期容量规划做底数。5.2 模型转换与推理优化PyTorch模型直接用torch.save保存的那份checkpoint只能满足“能跑”的需求。真要上生产有一个明显更好的方案先把模型转成TorchScript或者ONNX格式再做推理加速。ONNX的好处是格式标准可选的运行时很多。CPU服务可以用ONNX Runtime英伟达GPU服务可以用TensorRT以及triton的优化流水线。部分任务在大模型上一张GPU卡能支撑的最高并发一般取决于两个瓶颈单次推理延迟和显存占用。转换后模型一般会有一定的提速收益有的场景还比较可观。转换步骤用代码示意一下import torch # 假设 model 是训练好的 PyTorch 模型 model.eval() dummy_input torch.randn(1, 128, dtypetorch.long) # 导出到 ONNXenable 动态轴是为了让 batch 和 seq_len 可变 torch.onnx.export( model, dummy_input, model.onnx, opset_version17, input_names[input_ids], output_names[logits], dynamic_axes{input_ids: {0: batch, 1: seq}, logits: {0: batch, 1: seq}} )还有一招在模型训练阶段就考虑部署。如果你知道线上是CPU推理那可以把训练时的max_seq_length从512缩短到128。文本分类场景里绝大多数业务文本的有效信息集中在前128个token以内。序列长度缩短后推理时间会明显下降。这是“在训练阶段做部署设计”的一个例子。5.3 监控与持续观察服务上线只是开始监控才是真正要长期耗神的环节。我的监控体系分三层。第一层是基础设施层CPU、内存、GPU利用率、磁盘IO用Prometheus采集指标Grafana做可视化面板。第二层是服务层每个推理接口的QPS、P50/P95/P99延迟、错误码分布。第三层是业务层最容易被忽略的一层预测标签的分布。AI模型上线后如果模型预测的类别比例和训练时的类别比例发生大的漂移往往说明线上数据分布已经变了模型可能正在失效。这个监控项值得严肃对待。举一个真实例子。客服工单分类项目上线三周后我注意到“退款投诉”这个类别被预测的比例从15%悄悄涨到了24%而人工标注统计的实际比例才16%。模型并没有在准确率上有明显变化但业务分布漂移了。后来一查是产品线调整导致退款类工单措辞变多。这种情况下如果只看总准确率你根本发现不了模型已经开始在某些细分场景失准。设置业务层的分布监控之后我才真正觉得这个系统是“活的”、可管理的。6. 一个端到端的小项目从零搭一个工单分类系统6.1 项目目标与数据准备我想起客服工单分类项目是怎么一步步跑通的。这里不是重复前面零碎步骤而是完整串一遍让你对“从零到一”有个体感。目标把用户提交的工单文本自动归类到6个业务大类退款、物流、账号、技术支持、投诉、咨询。数据集是脱敏后的历史工单共2.4万条文本平均长度85个字。数据准备阶段四条路径并行清洗管道去掉工单模板里的固定抬头、去掉URL和内部代号、统一大小写。去重逻辑同一用户对同一事件重复提交的工单只保留最新一条避免训练集中同源数据过多。切分策略按用户ID分组保证同一用户的所有工单只在训练集或验证集某一侧。语义验证随机抽200条由标注员确认label正确。这个阶段我最想强调的是数据准备绝对不是一次性动作。它要随着模型调优反复迭代。第一次清洗出的数据和第五次清洗出的数据往往质量差很多而这差距直接把模型线下准确率从84%推到90%以上。6.2 训练与评估模型选了一个中等规模的中文预训练模型finetune。这里不写大段训练代码了说几个关键参数和结果tokenizer的max_length设为128。学习率2e-5权重衰减0.01必要的正则防止过拟合。batch size 32训练5个epoch用early stopping守卫验证集loss。类别不平衡处理少数类在loss里加权但没有重采样因为重度补样会造成信息重复。评估结果我保存得很规范。跑完一轮后在MLflow里记录的是一套组合指标整体准确率、加权F1、每个类别单独的precision/recall/F1以及一份badcase明细表的路径。当时基线TF-IDF逻辑回归的准确率是78%finetune模型到了91%这13个百分点就是模型带来的真实增益。更让我放心的是badcase明细显示错误分布是分散的没有集中在某个极端小众类别上。6.3 部署与调用评估阶段用的是PyTorch原生模型部署前我做了两件事转ONNX格式服务层用FastAPI包起来模型镜像和代码镜像用Docker打包docker-compose管理启动。启动服务后可以用curl快速验证一下curl -X POST http://localhost:8000/predict \ -H Content-Type: application/json \ -d {text: 我买的商品一直不发货想申请退款客服电话打不通}返回结果像这样{ label: 退款, confidence: 0.9845 }我观察到整个推理过程在CPU上P99延迟约25ms吞吐量完全满足客服系统每天几万条的调用量。这个项目从零开始到跑完第一轮线上灰度前后大概用了三周贵在了数据清洗和badcase迭代模型训练本身反而不是最耗时的。7. 踩坑记录训练不收敛、数据泄漏、版本漂移的经验笔记7.1 排查一模型loss不降反升这个坑我踩得记忆犹新。有一次跑文本分类loss曲线像心跳一样上下跳完全看不到下降趋势。我的排查顺序是这样的第一步检查数据。随机打印20条训练样本确认text和label没有配对错误。再检查标签分布如果某个类别只有个位数样本模型基本学不到规律loss也会波动。当时看了下数据有3个类别样本量超过3000但“账号申诉”类只有23条这在6分类任务里基本等于不存在。第二步检查学习率。学习率太大是最常见的loss震荡原因。我试着把学习率从2e-5调到1e-5loss曲线立刻平缓了很多。实际上转模型优化视角如果你的损失在一个量级上反复振荡先降一半学习率再观察这比动架构要快得多。第三步查模型最后的输出和loss的计算方式。检查标签编号是否从0开始连续。有一次从别的项目移植代码标签字典的id从1开始结果模型的输出维度是6而标签取值是1到6直接错位。这类问题只有静态排查才能发现。7.2 排查二线下指标高分线上表现崩塌这是我职业生涯里最刻骨铭心的一个教训。有一次离线评估准确率做到93%结果上线两周后被业务方告状“跟瞎猜差不多”。我当时第一反应是模型被环境影响了但查了一圈发现推理代码没问题。后来花了两天做归因最后定位到根因数据泄漏。泄漏出在哪我早期按行随机切分训练集和测试集没有按用户分组。同一用户的工单内容高度相似很多样本的文本几乎就是同一段话改了改时间。随机切分后测试集里大量样本的“同源兄弟”就在训练集里。模型相当于预习了测试题的答案线下指标当然虚高。等到线上来了全新用户、全新措辞的工单模型立刻现出原形。这个教训让我定了一条死规矩切分之前先做业务主体分组然后组级别切分。从那以后线下线上指标差距从15个百分点压缩到了3个百分点以内剩下的差距属于真实场景冗杂波动的正常范围。7.3 排查三换个环境就复现不出结果有一次一个同事去其他组做技术分享把代码拷过去一跑效果差了五个百分点。他回来找我怀疑模型文件拷错了。我过去一看环境依赖不匹配出现了。他的代码和模型权重没问题但环境里装的是第二天的依赖版本一些细微实现差异带来了结果漂移。这个问题的根治方案是环境一致性。光有requirements.txt不够还有两件事要做用Docker把整个运行环境做成镜像。镜像一旦构建好每次训练和推理都用同一个镜像完全隔离宿主机器上的乱七八糟环境。用seed锁随机性。训练脚本里设置随机种子并且把seed记录到MLflow。PyTorch、NumPy、Python标准库的random三处都要设。做完这两件事后换环境复现结果的问题基本消失了。如果你在团队里被“本地能跑、服务器上跑不出来”折磨过认准这两个方案。这套流程走完你手头就有一个能持续迭代、能复现、能被监控的AI工程系统。我始终觉得AI工程从某种意义上说是一场管理学游戏管住数据、管住实验、管住环境、管住版本比调出哪怕一个惊艳的模型更值得投入。因为这些“枯燥的流程”就是项目能活过前六个月的底气。你去踩坑、去记录、去固化规范最后回头看收获最大的是这套自己认真打磨过的流水线本身。
返回列表