
作为一个在算法和工程之间来回折腾了快八年的老家伙我见过太多模型跑通就以为万事大吉的团队。Notebook里F1分数再漂亮一上线上就变成事故现场数据延迟、特征穿越、模型膨胀、监控缺失哪一件都能让你在凌晨三点被on-call电话吵醒。所以当我看到有人把ai-engineering-from-scratch当项目标题时第一反应是欣慰——终于有人愿意从地基开始盖房子了。这个标题翻译过来就是从零开始的AI工程它不是一个具体的算法模型也不是某个现成的框架而是一条完整的修炼路径从数据怎么进、特征怎么算、模型怎么训、服务怎么上到线上怎么监控、怎么迭代把所有环节用工程化的手段串起来。这篇文章就是想跟你聊聊我理解的ai-engineering-from-scratch到底该怎么落地包含哪些核心模块有哪些必须提前避开的坑以及一个普通工程师要怎么系统地把这套东西搭起来。这篇内容适合三类人刚入行的算法工程师想补工程短板的人被算法落地难困扰的团队技术负责人以及想转行进入AI应用开发领域的后端工程师。我会尽量用大白话讲清楚原理并给出可以直接照着做的实操方案哪怕你手头只有一个GPU和一台云服务器也能一步步把系统跑起来。1. 先想清楚终点AI工程的完整链路长什么样很多人一提AI工程就想到训练模型这是最大的误区。模型的训练只是整条链路里很小的一个环节就像装修房子时刷墙只是最后一步水电改造、墙体结构、材料采购才是真正决定质量的东西。一个完整的AI工程链路应该有下面这几个阶段。1.1 数据获取与质量保障数据是整个AI系统的地基。这一阶段要回答的问题包括数据从哪里来业务数据库、日志、第三方接口、用户标注等数据怎么采集、怎么同步如何保证数据质量完整性、一致性、时效性数据是否需要脱敏和合规审查以我个人的经验数据清洗和校验往往占据整个项目40%以上的时间这不是浪费而是必要的代价。数据问题的处理速度通常决定了一个AI项目的生死因为脏数据会让后面的所有环节全都白做。1.2 特征工程与存储设计传统机器学习里有一句话叫特征决定了模型的上限深度学习时代这句话依然成立只是特征从手动设计变成了模型自己学。但工程上仍然需要解决特征怎么算实时还是离线、特征怎么存特征仓库、特征怎么对齐训练时和推理时用同一套逻辑的问题。特征穿越用到了未来数据是离线指标好看、线上效果崩盘的头号原因必须在工程架构上彻底杜绝。1.3 模型训练与实验管理训练并不只是跑一次fit就完事。你需要管理训练数据版本、代码版本、超参数组合、模型产物需要一个实验记录系统追踪每一次训练的效果差异。我见过太多团队这次效果好了但没人记得怎么复现。没有实验管理的AI项目最后都会变成一次性项目不可持续。1.4 评测体系评测是AI工程最容易糊弄、也最不该糊弄的环节。除了常规的离线指标准确率、召回率、AUC等还要设计坏case分析流程、长尾场景覆盖测试、鲁棒性测试输入扰动、异常输入、以及必要的线上A/B测试。没有评测体系你根本不知道模型上线后是在变好还是在变坏。1.5 推理服务与成本控制模型最终要被人调用所以你得把它封装成服务考虑延迟P99、吞吐量QPS、并发处理能力、资源占用。一个大模型的推理服务一天可能烧掉几百上千元的GPU费用如果没有合理的缓存、批处理、模型压缩等优化手段成本分分钟失控。1.6 线上监控与反馈闭环上线不是终点是起点。你需要监控线上推理请求的指标延迟、错误率、流量分布、监控输入数据的分布漂移数据漂移还需要建立明确的反馈闭环线上的坏case如何回流、如何变成下一轮的训练数据。AI系统区别于普通软件的地方就在于它有一个数据-模型-服务-反馈-再训练的闭环把这个闭环跑通了才叫AI工程化。2. 技术栈选型不要把时间浪费在反复换框架上技术选型是ai-engineering-from-scratch的第一步也是最容易踩坑的一步。很多新手喜欢追新框架今天用这个明天用那个结果工具链换来换去业务一点没推进。我的建议是用主流的、社区活跃的、你团队已经有经验的技术不要为了炫技选择冷门方案。2.1 编程语言与项目脚手架Python为主工程规范兜底Python是AI领域没有争议的主流语言生态最全社区最活跃。但Python的工程性也最差所以必须有严格的工程规范来兜底。项目从0开始我建议用下面的结构来组织├── configs/ # 配置文件YAML/JSON ├── data/ # 数据存放通常通过gitignore忽略 ├── docs/ # 项目文档 ├── src/ │ ├── data/ # 数据下载、预处理、清洗 │ ├── features/ # 特征工程 │ ├── models/ # 模型定义与训练逻辑 │ ├── evaluation/ # 评测代码 │ └── serving/ # 线上服务封装 ├── tests/ # 单元测试与集成测试 ├── scripts/ # 运维脚本与批处理任务 ├── pyproject.toml # 依赖管理 └── README.md这个结构的好处是数据、特征、模型、服务、测试彼此隔离任何人加入项目都能很快定位到对应模块。Python依赖管理我强烈建议用uv或poetry而不是裸pip install加一个requirements.txt因为后者的依赖冲突会让你在半年后怀疑人生。2.2 数据存储选型离线、实时、特征分开对待数据的存储要根据使用场景分开选型这是工程化的重要标志数据用途推荐方案说明原始数据湖对象存储S3/OSS/MinIO Hive/Iceberg 表格式低成本存储海量原始数据支持time travel离线特征/训练集列式存储Parquet 数据仓库Doris/ClickHouse批量读取性能好适合离线训练实时特征Redis 特征计算服务毫秒级读取支持线上实时推理实验记录SQLite/MySQL MLflow轻量级实验管理日志落库这里要特别提醒一点不要在同一个库里又存原始数据又存模型产物。训练数据、测试数据、线上日志这些东西的生命周期和访问模式完全不同混在一起只会让运维变成噩梦。2.3 训练框架PyTorch为主别两种框架混用现在做AI工程PyTorch基本是默认选项。TensorFlow当然也能做但现在社区里新模型、新论文几乎都用PyTorch实现你抄作业都更方便。如果你的团队已经有人熟练使用某个框架那就直接用那个框架迁移的成本远比想象中高。训练基础设施方面推荐用PyTorch Lightning或者Hugging Face Trainer来管理训练循环它们把日志、checkpoint、分布式训练这些重复工作都封装好了能省下大量时间。别自己造训练循环的轮子除非你想深入研究底层机制。2.4 部署方案从FastAPI到推理服务框架模型部署的最简方案是FastAPI ONNX Runtime或者TorchServe。如果只做内部调用、QPS不高FastAPI完全够用。如果要做高并发、低延迟的在线推理建议直接上推理服务框架比如NVIDIA Triton Inference Server它能自动做动态批处理、多模型管理、GPU资源调度。Rust生态的mature框架也可以考虑但对大多数团队来说不是必要选项。2.5 可观测性日志、指标、链路追踪三位一体AI服务也是服务所以可观测性不能缺席日志收集用ELK或Loki指标监控用Prometheus Grafana链路追踪用Jaeger或OpenTelemetry。很多AI工程师忽略这一块觉得模型能出结果就行直到线上出了问题才发现没有任何线索可查。在我的经验里可观测性的投入应该在项目里占比不低于10%这钱花得值。3. 核心环节实操从数据集到可交付模型的全过程技术栈选好了接下来就是实打实的干活环节。这一章我按数据→特征→训练→评估的顺序把每一个关键环节的实操做法和配置参数写清楚你可以直接对照着落地。3.1 数据管线清洗、校验、版本管理第一步是搭数据管线。最简单的做法是写一套可重复执行的ETL脚本用dlt、dbt这类工具管理。清洗的步骤要固化成代码不要用临时处理一次的Notebook。数据质量校验是几乎所有人都会忽略的环节。一定要在管线里加入自动校验规则比如字段完整性关键列不能有超过1%的空值类型正确性数字列不能被存成字符串值域范围年龄必须在0~120之间价格不能是负数时间顺序训练集的事件时间必须早于线上预测时间每条规则失败时管线要能自动告警并阻止训练任务启动。用Great Expectations或Pandera做校验都很方便。数据版本管理方面可以用DVC或Delta Lake的time travel能力确保训练结果可回溯。3.2 特征工程实践离线计算与线上一致的秘密特征工程的工程化核心只有一个词一致性。训练时怎么算特征推理时就必须一模一样。为此强烈推荐把特征计算逻辑封装成独立的模块训练代码和推理服务都调用同一个函数或类绝不允许各写一套。举个例子假设你要计算一个用户过去7天平均下单金额的特征# feature_definitions/user_features.py from datetime import datetime, timedelta class UserOrderStatFeature: def __init__(self, window_days: int 7): self.window_days window_days def compute(self, events: list[dict], current_time: datetime) - float: 计算用户在current_time之前window_days天内的平均下单金额 window_start current_time - timedelta(daysself.window_days) filtered [ e for e in events if e[event_time] window_start and e[event_time] current_time ] if not filtered: return 0.0 return sum(e[amount] for e in filtered) / len(filtered)只要训练脚本和推理服务都调这个compute方法就能保证特征一致性。注意里面有个容易被忽略的细节时间窗口应该是以预测时刻为终点往前推而不是以自然日为单位。很多feature leak就是这么产生的。特征存储方面我建议为每个实体用户、商品等建立一个特征表用时间戳管理版本线上读取走Redis缓存离线训练直接查表。不要用每次训练临时跑一遍特征任务的原始方式后面线上对不上你会痛苦得想辞职。3.3 模型训练配置管理、日志与超参数训练实验的可复现性依赖三个东西代码版本、数据版本、配置版本。代码用Git管理数据用DVC或表格式的time travel管理配置必须存到文件里而不是散落在代码里。我建议项目的配置目录长这样# configs/experiment_001.yaml model: name: bert_encoder hidden_size: 768 num_layers: 12 data: train_path: s3://bucket/train/v003.parquet eval_path: s3://bucket/eval/v003.parquet batch_size: 32 num_workers: 8 train: optimizer: adamw learning_rate: 2e-5 epochs: 5 gradient_clipping: 1.0 fp16: true save_every: 500 # steps evaluation: metrics: [accuracy, f1, auc]每次开启新实验复制一份配置改个名训练日志里记录配置文件的路径和内容的哈希值这样过了一个月你翻回来还能知道这个模型是怎么来的。训练过程中的日志用loguru或者Python内置logging建议至少记录每个step的loss、每个epoch的验证指标、GPU显存占用、学习率变化。超参数调优方面不要用网格搜索硬扫太费时间和算力。用Optuna做贝叶斯搜索先粗后细先把关键的超参数学习率、batch size、层数找到合适的范围再在这个范围里精细搜索。每个试验的记录要及时入库。3.4 评估体系别只看准确率多维度评测才算数模型评估是决定上线质量的关键但也是大家最爱偷懒的地方。只算一个总准确率的评估体系等于没有评估因为在绝大多数业务场景中类别不平衡、长尾分布、case多样性都是比平均表现更重要的东西。一份规范的评估报告至少要包含这些内容分层的指标按用户群体、按商品类目、按流量渠道分别统计效果混淆矩阵或按阈值切换的Precision-Recall曲线坏case分析抽样看预测错的样本归纳错误类型鲁棒性测试对输入做微小扰动、加噪声、缺字段看输出是否稳定与线上基线或上一个模型的对比测试如果做的是在线推荐、搜索排序这类业务还要评估模型对多样性的影响、对新鲜内容的友好程度。这些指标虽然在离线阶段不完全准确但至少能提前暴露不少问题。在评估代码里我强烈建议把每次评估结果输出成一个报告文件HTML或Markdown附带样本展示这样团队里非算法人员也能参与评审有助于建立互信。说白了算法不是做给自己看的是给业务和老板看的你要让他们看懂你的算法到底好在哪。4. 部署上线与成本优化让模型真正跑起来训练做完模型还只是在实验室里跑通了。真正把它变成能对外提供服务的系统需要把部署和成本两件事都安排明白。4.1 服务化封装把模型包成一套标准接口模型上线的第一步是封装。推荐用FastAPI把模型包装成一个HTTP服务接口设计遵循下面这样的模板# serving/app.py from fastapi import FastAPI, HTTPException from pydantic import BaseModel import onnxruntime as ort app FastAPI() class PredictRequest(BaseModel): user_id: str item_id: str context: dict {} class PredictResponse(BaseModel): score: float model_version: str latency_ms: float sess ort.InferenceSession(models/model_v3.onnx) app.post(/predict, response_modelPredictResponse) async def predict(req: PredictRequest): # 1. 实时特征拼接 # 2. 特征预处理与训练一致 # 3. ONNX推理 # 4. 返回结果 ... return PredictResponse(scorescore, model_versionv3, latency_mslatency)注意这里用了ONNX格式它比直接用PyTorch做推理有几点好处推理速度更快有图优化、内存占用更低、部署时不需要安装庞大的PyTorch环境。如果你觉得转ONNX麻烦用PyTorch的torch.compile压缩后部署也可以接受但长期来看ONNX或者TensorRT是更专业的路线。4.2 容器化与资源估算一台机器能扛多少流量部署载体几乎无脑选Docker。Docker镜像要把项目依赖、模型权重、启动脚本全部打包。模型文件超过1GB时注意镜像构建时不要用ADD直接塞模型而是用运行时挂载或模型仓库拉取的方式否则每次发布都要传几个GB的文件非常痛苦。资源估算是部署之前必须做的功课。用一个简单的公式估算假设单机QPS上限为q业务高峰期预测QPS为Q那么最少需要的实例数约为Q / (q * 0.7)0.7是安全系数给突发流量留缓冲。而单机QPS需要用压测来确定不要靠猜。用locust或wrk做压测解出P99延迟和最大QPS再倒推资源需求。下面是一个简化的资源估算表假设单个模型请求的平均处理时间5ms单卡A100服务类型单实例QPS目标QPS需要实例数备注纯CPU推理小模型2008006CPU部署成本低GPU推理中等模型805008需考虑GPU利用率大模型7B以上1010012需要beam search等优化表格只是示例实际数值因模型和硬件差异很大一定要以自己的压测为准。压测的时候还要关注显存和内存的峰值占用防止流量过大时OOM内存溢出。4.3 性能优化P99延迟从300ms降到50ms性能优化是工程能力的分水岭。有几个常见的方向按性价比排序第一批处理。推理服务可以攒一小批请求一起计算GPU利用率会大幅度提升。Triton推理服务自动支持这个功能用FastAPI的话可以用asyncio.gather手动攒批。第二模型量化。把FP16换成INT8显存占用减半推理速度提升2~4倍代价是精度可能下降0.5%~2%。如果业务对精度没有那么敏感量化几乎是无脑的选择。推荐用TensorRT或ONNX Runtime自带的量化工具。第三缓存。对于同样的输入比如同样的user_id和item_id短时间内的预测结果如果一致可以用Redis缓存TTL设30秒到几分钟命中率高的场景能省一半以上的算力。第四降低请求体大小。有些接口会传大量文本或几十个特征字段序列化和反序列化本身就耗时能精简的字段尽量精简能用整数ID的不要用字符串。我给过一个模型的P99延迟做了完整的优化从380ms降到52ms代价是精度损失0.6%但业务方完全接受因为这个模型的使用场景对速度比精度更敏感。这就是典型的工程取舍不要追求最优指标要追求够用且最快。4.4 成本治理看懂账单做算力规划AI项目的成本大头几乎都是GPU算力。成本治理的关键是能离线算的不要实时算能用CPU的不要用GPU能用小模型的不用大模型。先说离线与实时分离。推荐、搜索类业务的粗排阶段可以用小模型或者双塔模型离线算出候选集线上只做精排。这样实时计算的压力会小一个数量级成本大幅下降。我现在做的项目里线上实时计算的比例只占全链路的10%剩下90%都是离线批量计算加缓存。再说自适应弹性。如果是跑在Kubernetes集群里可以开基于负载的HPA水平自动扩缩容根据QPS和延迟动态调整实例数。高峰期自动扩容低谷期自动缩容成本能降30%以上。别忘了给训练任务设置最大并发数防止某次调参实验偷偷吃光整个集群的资源。5. 线上监控与持续迭代AI系统能不能长期健康运行AI系统上线之后真正的挑战才开始。模型不是静态代码它的输入分布会随时间变化业务场景会变用户行为会变如果不做监控和迭代模型效果会不知不觉地衰减直到某一天业务方跑来质问你们模型是不是坏了。5.1 监控指标设计技术指标与业务指标分开看AI服务的监控要分两个层面。技术指标和普通后端服务一样请求量、错误率、P50/P95/P99延迟、GPU利用率、内存占用。业务指标则要结合模型效果预测分数分布、正样本占比、TopK命中率、A/B测试的核心业务指标点击率、转化率等。一个容易被忽视的细节是预测分数分布本身就是一个很有效的前置告警信号。正常情况下一个训练良好的模型的预测分数分布应该是稳定的。如果某天线上请求的平均分数从0.7突然变成0.5那很可能不是因为模型坏了而是输入数据发生了变化比如用户群体变化、特征源挂了这时候即使准确率还没变化你也应该知道出事了。我习惯在Grafana里加一个预测分数均值的时序图配合3-sigma告警规则这个习惯救过我很多次。5.2 数据漂移检测监控数据变了没而不是模型坏了没数据漂移是AI系统独有的问题。最简单实用的方法是定期对比线上实际输入特征分布与训练时特征分布的差异用PSIPopulation Stability Index或KL散度量化。PSI计算方式并不复杂一个简单的实现可以是import numpy as np def calculate_psi(expected, actual, bins10): # 把两个分布都切成bins个桶 expected_counts, edges np.histogram(expected, binsbins) actual_counts, _ np.histogram(actual, binsedges) expected_rate expected_counts / expected_counts.sum() actual_rate actual_counts / actual_counts.sum() # 防止分母为0 actual_rate np.clip(actual_rate, 1e-6, None) psi np.sum((actual_rate - expected_rate) * np.log(actual_rate / expected_rate)) return psiPSI超过0.2时就要开始警惕超过0.25基本可以判定分布发生了显著变化。这里计算的不是单条请求而是周期性的批次统计。你可以每天跑一个定时任务把当天的特征分布和训练集特征分布做对比输出到报表系统。5.3 模型版本管理与灰度发布上线前的安全网模型上线最大的风险是新版比旧版差却没被发现。解决方案是灰度发布加完善的对比机制。我的做法是旧模型保持在线新模型先以5%流量试运行对分到新模型的流量做实时埋点记录预测分数和业务结果每天跑一个对比报告新旧模型的线上效果指标对比、分数分布对比、延迟对比如果连续3天新模型核心指标不低于旧模型再逐步放量到30%、50%、100%如果任何一天新模型出现异常立即切回旧模型这个流程依赖一个很重要的前提新模型和旧模型必须同时在线且能够被独立观测。所以服务启动时要带上版本号预测结果的log要记录版本信息。版本号在日志、监控、响应体里都要带这样排查问题时才知道是哪个模型在处理请求。5.4 反馈闭环让线上的坏case变成下一轮的训练数据AI系统持续变好的核心是反馈闭环。具体来说要把线上预测错误或者用户反馈不满意的case回流到标注系统由人工评估标注然后并入下一轮的训练集。这一步听着简单但很多团队卡在没有反馈通路上。落地时至少要打通三条通路日志通路线上推理日志定期导入数仓标注通路从日志中抽样的坏case推送到标注平台标注结果入库训练通路标注数据合并到增量训练集触发新一轮训练我在实际项目里遇到过最遗憾的情况是一个推荐系统上线后用户点击数据每天都在产生但没有任何人把这部分数据用来更新模型。团队以为模型上线就万事大吉结果三个月后点击率比刚上线时跌了20%。反馈闭环不是可选项而是AI系统的保养系统没有它模型就是一次性的消耗品。6. 常见问题与排查实录那些年我踩过的坑做AI工程这么久踩过的坑可以装一卡车了。这一节挑几个常见的系统性故障写成速查表希望能帮你省掉几个加班的夜晚。症状可能原因排查方法解决建议离线指标好线上效果差特征穿越 / 特征不一致核对特征计算时间窗口对照训练与推理代码统一特征逻辑写成单一模块供两端调用线上延迟突然升高数据源变慢 / GPU利用率饱和查看链路追踪压测对比加缓存、批处理扩容实例预测分数分布漂移输入特征源变更 / 用户群体变化对比训练与线上特征分布PSI重训模型标注新数据加入训练集P99延迟很高但平均低受长尾大请求影响查看延迟分布直方图对单条最大输入长度做限制加超时控制灰度模型崩溃且无法回滚没有同时保留旧版本检查部署脚本采用蓝绿部署或金丝雀发布保留上一版本训练一半GPU OOMbatch_size过大 / 显存碎片缩小batch_size检查显存占用用梯度累积开魔改内存分配策略这中间最值得展开说的是特征穿越。有一次团队训练了一个CTR预估模型离线AUC高达0.82比线上旧模型高了不少大家都很开心。结果上线压测时发现效果一塌糊涂。后来排查才发现训练集里一个近7天是否点击过该商品的特征清洗脚本里误用了整个时间段的未来数据。这样的问题离线几乎无法发现只能靠严格的时间特征校验来终结。所以在做特征工程时务必确认特征计算只能依赖当前预测时刻之前的信息这是一条铁律。另一个高频问题就是线上请求和训练数据的特征拼接。训练时特征都是从离线表里查的而线上推理时有些特征根本拿不到比如用户当天是否浏览过该商品线上还没产生这个行为。这种情况必须在特征设计阶段就预判把实时拿不到的特征改成取用户最近一段时间的历史行为。这个坑几乎每个人都会踩只能靠对业务的理解来规避。7. 写在最后给正在搭这套系统的人几句掏心窝的话ai-engineering-from-scratch说起来是一个项目标题但真正做起来它是一整套关于如何让算法稳定产生业务价值的方法论。我个人这几年最大的体会是AI工程化的难点从来不在于算法有多前沿而在于每个环节是否都足够朴素、足够扎实。数据干净吗特征一致吗监控及时吗回滚方便吗这些问题没有一个是高难度算法但每一个都能让一个AI项目夭折。最后再分享一个实用的小技巧无论项目多小从第一天开始就要写项目工程文档记录架构图、数据流、依赖项、上线checklist。遇到问题更新排查记录。这个文档平时看起来不起眼但当团队从1个人变成5个人、从日活一万变到日活百万时它会成为整个系统能否持续演进的基石。如果你正准备从零开始搭AI工程别追求一步到位先把最小闭环跑通一份干净的数据、一个能复现的训练脚本、一个简单有监控的推理服务。在此基础上迭代比一开始就想做一套完美平台要靠谱得多。祝你的模型上线顺利也祝你再也不用凌晨三点被报警电话吵醒。