1. 从零搭建AI工程能力:为什么我劝你别一上来就调包
这两年AI应用开发的门槛肉眼可见地降低了,随便拉个框架、调个API就能跑出一个能对话的Demo。但我带过不少新人,也面试过不少号称“做过AI项目”的候选人,发现一个很普遍的问题:模型能跑起来,但一问到数据怎么清洗、特征怎么组织、推理延迟为什么高、线上效果为什么和离线对不上,基本就卡壳了。这就是典型的“会调包,但不懂工程”。
ai-engineering-from-scratch这个方向,说白了就是要把AI从“实验室玩具”变成“能扛住真实流量和真实业务”的系统。它涵盖的东西很杂:数据管道、特征工程、模型训练与评估、推理服务、监控告警、成本控制,每一块都有坑。适合谁来参考?我觉得有三类人:一是刚转行做AI的开发者,想补齐工程侧的短板;二是后端或数据工程师,想搞清楚AI系统和自己熟悉的服务架构到底差在哪;三是技术负责人,需要一套可落地的从零搭建路径,而不是堆一堆论文里的名词。
我自己走过完整的从零到一,也踩过不少坑。下面就把这套东西拆开讲,尽量说人话,把“为什么这么做”讲透,而不是只丢一堆配置。
2. 整体架构设计与技术选型思路
2.1 先想清楚:你的AI系统到底解决什么问题
很多人一上来就问“用什么模型”,这是本末倒置。我习惯先画一张最简单的图:输入是什么、输出是什么、中间需要哪些步骤、每一步的失败会带来什么后果。比如做一个商品标题生成工具,输入是商品属性,输出是标题文本,中间涉及数据清洗、模板约束、模型推理、后处理过滤。如果后处理没做好,模型可能生成违规词,这就是业务风险。
从零搭建AI工程,第一步不是选框架,而是定义清楚服务等级目标。我一般会问三个问题:延迟要求是多少?准确率底线在哪里?每天调用量大概多少?这三个数字直接决定后面所有选型。延迟要求200毫秒以内,那大模型基本别想,得用小模型或者蒸馏;每天调用量上百万,那推理成本必须压到极低,量化、缓存、批处理都得安排上。
注意:不要拿离线测试集上的准确率去承诺线上效果。线上数据分布会漂移,用户输入会千奇百怪,离线95%的模型上线后可能只有80%。留足缓冲。
2.2 技术栈选型的取舍逻辑
选型这件事,我的原则是:能用简单方案解决的,绝不引入复杂组件。很多团队一上来就上Kubernetes、上特征存储、上向量数据库,结果维护成本比业务收益还高。从零搭建的阶段,我建议按这个优先级来:
- 数据存储:先用关系型数据库加对象存储,别急着上数据湖。数据量到TB级别再考虑。
- 训练框架:PyTorch足够,生态好、调试方便。TensorFlow也不是不行,但新项目我倾向PyTorch。
- 推理服务:小规模直接用FastAPI包一层,规模大了再考虑Triton或专门的推理服务器。
- 监控:Prometheus加Grafana是标配,日志用ELK或Loki,别自己造轮子。
为什么这么选?因为从零搭建的核心矛盾是快速验证和迭代,而不是追求架构的“先进性”。我见过太多项目死在过度设计上,三个月搭架子,业务需求早变了。
2.3 分层架构:把变化的部分隔离出来
AI系统和传统后端最大的区别在于,模型和数据是持续变化的。所以架构上一定要做分层,我通常分成四层:
- 数据层:负责原始数据接入、清洗、存储。这一层要保证可追溯,每条训练数据都能查到来源。
- 特征层:把原始数据转成模型能吃的格式。这一层要保证训练和推理的一致性,否则就是经典的“训练服务偏差”。
- 模型层:训练、评估、版本管理。模型文件要带元数据,包括训练数据版本、超参数、评估指标。
- 服务层:推理接口、限流、降级、监控。这一层要能扛住异常输入和流量突增。
分层的意义在于,当模型效果下降时,你能快速定位是数据问题、特征问题还是模型问题,而不是一锅粥。
3. 数据管道与特征工程的核心细节
3.1 数据清洗:脏数据比没数据更可怕
我做过一个文本分类项目,离线评估F1到了0.92,上线后惨不忍睹。排查了一周才发现,训练数据里有大量重复样本,而且标注质量参差不齐。模型把重复样本的噪声当成了信号。所以数据清洗这一步,我现在的标准流程是:
- 去重:精确去重加近似去重。近似去重用MinHash或SimHash,别用编辑距离,太慢。
- 异常检测:统计每条样本的长度、字符分布、标签分布,偏离均值三个标准差的先人工看一眼。
- 标注一致性检查:同一批数据多人标注,算Kappa系数,低于0.6的批次直接打回。
这里有个经验:清洗规则要写成可复现的脚本,不要手动改数据。手动改的数据没法追溯,下次重新训练就乱了。
3.2 特征工程:训练和推理必须用同一套代码
这是新手最容易翻车的地方。训练时用Pandas做特征,推理时用另一套逻辑,结果特征分布对不上,线上效果直接崩。我的做法是:特征计算逻辑只写一次,训练和推理共用。
具体来说,把特征计算封装成独立的函数或类,输入是原始数据,输出是特征向量。训练时批量调用,推理时单条调用。如果性能不够,再考虑用C++或Rust重写热点部分,但逻辑必须一致。
对于文本特征,常见的坑是分词器和词表。训练时用jieba分词,推理时也得用同一个版本,词表也要冻结。我一般会把分词器和词表一起打包进模型文件,避免版本错乱。
3.3 数据版本管理:别再用文件名区分了
train_data_v2_final_真的最终版.csv这种命名,我见过太多次。数据版本管理不是矫情,是刚需。我的方案很简单:用DVC或者自己写个脚本,给每次数据变更生成一个哈希值,记录在数据库里。训练时指定数据版本哈希,模型文件里也存这个哈希。这样任何时候都能复现。
提示:数据版本要和代码版本、模型版本关联起来。我习惯用一张表记录三元组:数据哈希、代码提交号、模型文件路径。排查问题时直接查表。
4. 模型训练、评估与推理服务的实操要点
4.1 训练流程:从单机脚本到可复现实验
刚开始别搞分布式,单机加一块GPU足够跑通大部分中小模型。训练脚本我要求必须包含这几个部分:
- 固定随机种子:Python、NumPy、PyTorch的种子都要设,否则每次结果不一样,没法对比。
- 配置文件驱动:超参数、数据路径、模型结构都写在YAML里,别硬编码。
- 检查点保存:每个epoch存一次,同时保存优化器状态,方便断点续训。
- 日志记录:损失、准确率、学习率、梯度范数都要记,用TensorBoard或WandB看曲线。
我踩过的一个坑是:训练时用了数据增强,但评估时忘了关,导致评估指标虚高。所以评估函数要单独写,明确区分训练模式和评估模式。
4.2 评估指标:别只看准确率
准确率在类别不平衡时毫无意义。我一般会同时看精确率、召回率、F1,以及业务指标。比如推荐系统,AUC高不代表点击率高,还得看线上AB测试。对于生成式任务,BLEU和ROUGE只能参考,最终还得人工评估或用户反馈。
评估集的选择也很关键。我习惯留三个集合:训练集、验证集、测试集。验证集用来调参,测试集只在最后用一次。测试集要尽量贴近线上分布,如果线上数据有季节性,测试集也要覆盖。
4.3 推理服务:延迟和吞吐的平衡
推理服务上线前,必须做压力测试。我用Locust或wrk模拟并发,观察P99延迟和吞吐量。几个关键优化点:
- 批处理:把多个请求攒成一批推理,能显著提高GPU利用率。但批处理会增加延迟,需要根据业务容忍度调批次大小和等待时间。
- 量化:FP16或INT8量化能大幅降低显存和延迟,但可能损失精度。量化后必须重新评估。
- 缓存:对于重复输入,直接返回缓存结果。缓存键要包含模型版本,否则模型更新后缓存会脏。
我实测下来,一个7B参数的模型,FP16推理在A10上单条延迟约50毫秒,INT8能降到30毫秒左右,但精度掉了一个点。具体选哪个,看业务能不能接受。
4.4 模型版本管理与灰度发布
模型更新不能一把梭全量替换。我的做法是:新模型先跑影子模式,把线上流量复制一份给它,对比输出差异。差异在可接受范围内,再切5%流量做AB测试。AB测试看业务指标,不只看模型指标。稳定后再逐步放大流量。
模型文件要带版本号,服务层根据版本号路由。回滚要能在分钟级完成,所以旧模型文件不能删,至少保留三个版本。
5. 监控、告警与常见问题排查实录
5.1 监控体系:模型指标和系统指标都要看
很多人只监控CPU、内存、GPU利用率,忽略了模型层面的监控。我要求至少监控这几项:
- 输入分布:输入长度、词频、类别分布的统计量,和训练集对比,发现漂移及时告警。
- 输出分布:输出长度、置信度分布、拒绝率。如果置信度突然普遍降低,说明模型可能遇到了没见过的数据。
- 业务指标:点击率、转化率、人工审核通过率。这些是最终裁判。
系统指标用Prometheus采集,模型指标我习惯写个定时任务,每小时统计一次,推到同一个监控系统。
5.2 常见问题速查表
| 问题现象 | 可能原因 | 排查方向 | 解决方法 |
|---|---|---|---|
| 线上效果远低于离线 | 训练服务偏差 | 对比训练和推理的特征分布 | 统一特征计算逻辑 |
| 推理延迟突然升高 | 流量突增或模型退化 | 看QPS和P99延迟曲线 | 限流、扩容、回滚模型 |
| 输出结果重复 | 解码策略问题 | 检查beam search参数 | 调整重复惩罚或换采样策略 |
| 显存溢出 | 批次太大或内存泄漏 | 看显存占用曲线 | 减小批次、及时释放中间变量 |
| 模型效果逐渐下降 | 数据漂移 | 监控输入分布 | 重新训练或在线学习 |
5.3 独家避坑技巧
第一个坑:别在推理服务里做特征工程的重计算。我见过一个服务,每次请求都重新加载词表,延迟直接爆炸。词表和模型一样,启动时加载一次,常驻内存。
第二个坑:日志别打太多。推理服务打详细日志会拖慢速度,尤其是高并发时。我一般只打采样日志,比如1%的请求打全量日志,其余只打摘要。
第三个坑:异常输入要兜底。用户可能传空字符串、超长文本、特殊字符。服务层必须做输入校验和截断,否则模型可能崩溃或输出乱码。
第四个坑:GPU内存碎片。长时间运行后,GPU内存可能碎片化,导致明明有空间却分配失败。定期重启服务或使用内存池能缓解。
6. 成本控制与持续迭代的实战经验
6.1 推理成本:每一分钱都要算清楚
AI系统的成本大头在推理。我算过一笔账:一个7B模型,FP16精度,单次推理约0.5秒,用A10 GPU,按需实例每小时约1美元。如果每天100万次调用,光GPU成本就上万。所以成本优化是必须的。
几个有效手段:一是用更小的模型,蒸馏或剪枝后的小模型往往能保留90%的效果,但成本降一半;二是批处理,把GPU利用率从30%提到70%,成本直接减半;三是缓存,重复请求直接返回,我见过一个场景缓存命中率40%,省了四成成本。
6.2 持续迭代:建立反馈闭环
模型上线不是终点。我要求每个AI系统都必须有反馈收集机制。用户点击、纠错、人工审核结果,都要回流到训练数据里。但注意,反馈数据有偏,不能直接用,需要做加权或采样。
迭代节奏上,我一般两周一个小版本,一个月一个大版本。小版本调参和修bug,大版本换模型或加特征。每次迭代都要有明确的假设和评估指标,不能为了迭代而迭代。
6.3 团队协作:工程和算法的边界
从零搭建AI工程,往往算法和工程是同一个人。但规模大了之后,必须分工。我的经验是:算法同学负责模型结构和训练策略,工程同学负责数据管道、推理服务和监控。两边通过接口和契约协作,比如特征接口的输入输出格式要冻结,模型文件的元数据格式要统一。
提示:接口变更必须通知对方,并且保留旧接口至少一个版本周期。我吃过亏,算法同学改了特征格式没通知,工程侧直接崩了。
7. 我个人在实际操作中的几点体会
从零搭建AI工程这件事,技术选型只占三成,七成是工程纪律和迭代节奏。我最大的体会是:先跑通最小闭环,再优化单点。很多人卡在数据清洗上,洗了三个月还没开始训练,其实先用脏数据跑个基线,再逐步清洗,效率高得多。
另外,别迷信大模型。很多业务场景,一个精心调参的小模型加好的特征工程,效果不比大模型差,成本却低一个数量级。我做过一个意图分类任务,BERT-base微调后F1 0.89,后来用TextCNN加词向量,F1 0.87,但推理速度快了二十倍,线上直接选后者。
最后分享一个小技巧:每次模型更新,都保留一份旧模型的预测结果。这样新模型出问题时,可以快速对比,定位是模型问题还是数据问题。这个习惯帮我省了很多排查时间。