搞了好几个月的ai-engineering-from-scratch项目现在回头看最庆幸的是当初没有直接套一套现成的MLOps模板而是从最底层一步步把AI工程链路搭了起来。这个项目说白了就是完完整整地经历一遍“AI能力从0到生产环境”的全过程从原始数据到特征工程从模型训练到模型服务化从上线监控到持续迭代。今天把我踩过的坑、验证过的方案、还有最终沉淀下来的工程结构全部整理出来不管你是刚开始接触AI工程的初学者还是已经在做算法但没做过工程的工程师这篇文章应该都能帮你节省至少两个月的摸索时间。先说这个项目到底解决什么问题。很多人学AI都是从notebook开始的但到了真要把它变成一个能稳定运行、能监控、能迭代的系统时会发现处处都是坑训练好的模型怎么给业务方调用数据变了模型效果下降怎么发现新的实验怎么管理GPU资源怎么分配这就是“AI工程”和“AI算法”最大的区别。这个项目就是用一套完整的实践路径逼着你去面对这些问题、解决这些问题。1. 为什么我从零开始搭AI工程而不是直接抄一套现成的1.1 “from scratch”不是重复造轮子而是理解轮子我相信很多人看到“from scratch”这个词的第一反应是是不是在自讨苦吃明明有现成的MLflow、Kubeflow、Airflow这些平台为什么还要自己从零搭一遍我的看法不一样。用现成工具和从零搭建本质上是两种学习深度。用现成工具就像是租房子住起来舒服但水管坏了你不会修从零搭建就像是自己盖房子虽然费时费力但每一根承重墙、每一条线路你都心里有数出了问题能快速定位。举个例子。我在项目里用了MLflow做实验追踪这是一个非常成熟的工具安装完就能用。但是如果只是“安装、使用”你永远不会理解实验管理到底在解决什么问题。直到有一天我发现同事跑了几百个实验之后根本不知道哪个模型效果最好超参数配置全靠笔记本记录才真正理解实验追踪的核心价值是“可复现性”。这个理解只有当你从零开始手动记录实验、忍无可忍之后才引入MLflow时才能真正获得。所以这个项目虽然叫“from scratch”但并不是说什么都要自己写代码实现而是说要把从原始数据到模型服务的整条链路自己亲手搭一遍。链路中该用现成工具的地方就用现成工具但是你要理解每个工具解决什么问题、为什么放在这个环节、换成别的行不行。1.2 自建技能栈的三个核心原则在这个项目里我给自己定了三条原则也是在后续踩坑过程中反复验证过的。第一不求大而全但求全链路打通。很多教程上来就教你怎么用Kubernetes部署模型但如果你连一个最基础的FastAPI服务都没写过直接上K8s只会让你一头雾水。我的做法是先用最简单的方式把整条链路跑通数据用Pandas处理模型用Scikit-learn训练部署用Flask写个API。等全链路通了之后再组件化地替换成更强力的工具。第二每个环节都要留下可以直接运行的代码或配置。AI工程项目最怕的就是“看懂了但跑不起来”。所以我每做完一个环节都会把这个环节的所有配置、代码、依赖打包保存。这既是工程的规范也是对自己学习成果的固化。第三监控和评估从第一天就设计进去。很多项目都是模型上线了才发现没有日志、没有效果监控、没有数据回看机制。我在这个项目里的做法是从写第一行代码开始就记录每个环节的指标哪怕是简单的print输出也要保证信息可追溯。1.3 哪些现成工具值得保留哪些必须自己实现说到工具选型我得强调一个观点工具本身没有高下之分只有和你的情境合不合适。以我的项目为例以下工具是我验证过值得保留的Python环境管理conda或者venv二选一就好关键是必须用。不要嫌麻烦环境冲突的问题后期会折磨到你怀疑人生。实验追踪MLflow。它的UI虽然不算好看但胜在功能全面实验比较、模型注册、模型部署都有成熟的方案。数据版本管理DVCData Version Control。这个工具能很好地解决“数据变来变去模型结果对不上”的问题。容器化部署Docker。这是整个部署环节的基础无论如何都要掌握。而需要自己动手的部分其实集中在业务逻辑层特征处理代码、训练脚本、推理服务、评估逻辑、监控脚本。这些是项目真正的核心资产不能靠现成工具替代。还有一个重要认知不要陷入“工具收集癖”的陷阱。我看到很多人囤了一堆工具教程实际根本用不上。AI工程的真正能力是能够在正确的时间用正确的工具解决正确的问题而不是会用所有工具。2. AI工程需要掌握的技术栈一张“知识地图”帮你梳理2.1 核心底座Python工程化 Linux Git缺一不可很多准备入门AI工程的朋友上来就学深度学习这其实是本末倒置。AI工程的最底层基础是三个看上去不太炫、但决定你走多远的东西。Python工程化指的不仅仅是能用Pandas处理数据更重要的是代码组织能力写模块化的函数和类、管理依赖、用好虚拟环境、封装公共组件。我见过太多的项目是一堆notebook拼起来的一旦需求变化改代码跟拆炸弹一样。在ai-engineering-from-scratch项目里从第一天起我就把代码按src/tests/configs/这样的标准目录结构组织好这个习惯后来帮了大忙。Linux基础同样重要。在本地Windows上写模型和到Linux服务器上跑模型完全是两回事。你需要掌握基本的文件操作、进程管理、查看GPU状态nvidia-smi、设置环境变量、看日志。这些技能看着基础但到了排查线上问题的时候就是决定性的胜负手。Git的作用就更加不言而喻了。模型迭代、代码回滚、多人协作每一步都离不开它。尤其是训练脚本改了一行参数效果变化巨大必须有版本管理才能追踪。我在项目里用的是比较标准的Git Flow模式分支从main拉出每个实验一个分支验证通过后再合并。2.2 机器学习建模框架选择与大模型时代的调整这两年大模型很火但我要给新手一个明确的建议AI工程的基础还是经典的机器学习理论和深度学习框架的掌握。ChatGPT之类的大模型能力强但它是一个“产品”不是你应该从零去“训练”的对象。你真正要掌握的是特征工程、模型结构、损失函数、优化器、评估指标这些核心概念然后才是用框架把这些概念落实成代码。框架选型上我的建议是如果是练手项目优先选Scikit-learn它简单稳定能让你的重心放在算法而非环境配置上。如果做深度学习选PyTorch而不是TensorFlow。不是我偏心而是PyTorch对调试更友好动态图的特性让人能一步步追踪数据流转。而且现在社区、预训练模型生态都是PyTorch占优势。如果是为了特定部署场景如移动端可以考虑TensorFlow Lite和ONNX Runtime但它们更适合作为推理阶段的事情来处理。建一个合格工程需要你有能力去定义完整的实验流程包括数据划分train/validation/test、固定随机种子保证可复现、用EarlyStopping防止过拟合、保存最佳模型权重。这些东西在框架文档里都有但串起来的方式就是工程经验。2.3 部署与运维从notebook到生产系统的跨越很多算法工程师在建模上很强但到部署环节就开始头疼。我见过太多模型在notebook里预测准得飞起一上线直接被QPS打垮。原因就是你缺少了一个“服务化”的思维转变。AI工程里讲“部署”本质上是回答几个问题模型怎么对外提供接口——我用的方案是FastAPI写REST API配合Pydantic做请求参数校验。FastAPI比Flask更现代有自动接口文档性能也好。模型运行在什么环境里——用Docker打包成一个镜像里面装好Python环境和所有依赖。这样不管是开发机还是服务器跑起来的逻辑完全一致。有没有考虑过推理速度——本地随便predict一次可能要几百毫秒你再想想线上接口并发的时候这个速度能不能接受。通常要做模型量化、批处理batch inference、甚至上GPU推理。模型参数怎么加载能不能高效地服务多个模型——这就涉及到推理服务的架构设计简单场景可以直接加载到内存里。在ai-engineering-from-scratch项目里最终部署的形态是一个Docker容器外面套一层Nginx做负载均衡模型推理服务用FastAPI实现支持单条预测和批量预测两种接口模式。2.4 数据工程AI工程里常常被低估的前置环节数据工程说白了是70%的时间花在处理数据上。而这件事的重要性几乎人人都承认却几乎人人都做不好。数据工程的完整链路包括采集、清洗、结构化、存储、标注、版本管理。我这边的做法是采集阶段根据业务需求写爬虫或对接API把原始数据落到本地或云存储。清洗阶段处理缺失值、离群点、重复样本、类别不平衡。所有步骤必须写成脚本不能靠手工操作Excel。结构化阶段把清洗好的数据统一成特征矩阵每个特征要有明确的定义。用一个feature_config.json文件管理特征清单防止后面特征漂移了都不知道。存储与版本管理数据文件用DVC追踪版本跟代码仓库关联保证任何历史模型都能调到当时训练用的数据。如果这些环节全都认真做好了后面建模和部署都是水到渠成的事。反过来很多项目后面出问题根源往往都追溯到这里训练集和线上数据分布不一致、特征命名混乱、用了未来数据导致数据泄漏。3. 从零实现一个端到端AI工程项目的完整记录接下来我挑一个完整的例子把整条链路实际怎么走说一遍。假设我们要做一个“电商商品评论情感分类器”输入是评论文本输出是正面/负面/中性三分类。这个问题不复杂但刚好能覆盖AI工程的全部核心环节。3.1 目标定义先定指标再定模型工程的第一步不是选模型而是定义清楚“什么叫做好了”。在这个项目里我定下了几个明确的指标准确率不低于85%且三个类别的F1分数不低于0.80。这个要求能防止模型只讨好大类样本直接忽略小类。P95推理延迟不超过150毫秒单条文本CPU环境。这是给线上服务质量留的余量。数据回流时间不超过24小时。也就是说今天新增的用户反馈明天就能变成新数据进入训练集。我把这些指标写在项目README的第一页这是整个项目的第一份设计文档。后续每个选择小到清洗规则大到模型选型都是在为这些指标服务。3.2 数据管线搭建采集、清洗、标注、版本管理数据方面我构造了一个模拟业务场景的流程从公开的评论数据源拉取原始数据然后经过清洗和标注作为初始训练集。因为我做的是全流程复现所以要用脚本实现全自动化的数据清洗def clean_review(text: str) - str: # 1. 去除HTML标签和多余空白 text re.sub(r.*?, , text) text re.sub(r\s, , text.strip()) # 2. 去除URL、数字串、提及针对文本噪声 text re.sub(rhttp\S|www\.\S, , text) text re.sub(r\d, , text) # 3. 去除无意义的标点符号保留中英文和基础标点 text re.sub(r[^\w\u4e00-\u9fa5。、], , text) return text这个函数看着简单但在实战里很多细节会让它不断膨胀比如表情符号处理、繁体转换、专有名词保护等。我建议一开始把清洗步骤拆成多个小函数每一步都单独测试后续才好迭代。数据版本管理用的DVC。每次数据有任何变化我用dvc add data/raw把新的数据版本记录下来然后跟代码一起提交到Git。这样模型训练完成之后随时可以追溯“这个模型是用哪一版数据训练出来的”。对于标注环节因为没有人工标注预算我用了启发式规则加少量人工修正来生成伪标签。这里必须强调真实项目中标注质量直接决定模型上限。如果你有预算一定要做标注规范培训、标注一致性评估比如Kappa系数这个钱不能省。3.3 模型训练超参数选择与实验管理MLflow模型选型上我用了经典的TF-IDF 朴素贝叶斯做baseline然后换成BERT做深度学习方案。这个对比不是为了炫技而是要让你理解两个关键事实简单模型往往可以快速验证流程深度学习模型在此基础上才能体现出增量。训练脚本用PyTorch实现核心配置放在一个config.yaml里model: name: bert-base-chinese num_labels: 3 training: batch_size: 16 learning_rate: 2e-5 epochs: 3 seed: 42 max_length: 128这里有一个特别容易踩的坑batch_size和learning_rate是绑在一起的。你把batch_size从16改成32那学习率通常也要按比例调整否则收敛效果会变差。我一开始不知道这个在一个项目里把batch_size调大之后忘了改学习率模型性能直接掉了好几个点排查了很久。每次训练跑完之后我用MLflow记录所有超参数和指标import mlflow with mlflow.start_run(): mlflow.log_params(config[training]) mlflow.log_metrics({accuracy: acc, f1_macro: f1}) mlflow.pytorch.log_model(model, artifact_pathmodel)MLflow的自动记录功能也特别方便mlflow.autolog()就能把框架自动检测到的参数和指标都记录下来。但有一个坑它记录的指标可能不够详细比如没有每个类别的F1分数所以关键的自定义指标最好还是手动记录。训练之后一定要做模型评估报告不仅仅看总体准确率还要看混淆矩阵、每个类别的精确率和召回率、以及从错误样例中发现改进方向。比如我发现模型把“物流太慢了”预测为负面这是对的但把“包装完好物流一般”预测为负面那就是一个错误——因为“一般”这个词在中文语境下有时是中性偏轻微负面模型很难区分。这提醒我后续应该增加更多这种带转折结构的中性样本。3.4 模型部署FastAPI Docker GPU推理服务模型训练好之后下一步是把它变成一个可以被业务系统调用的服务。我用FastAPI搭建了一个简单的推理服务from fastapi import FastAPI from pydantic import BaseModel app FastAPI() class ReviewRequest(BaseModel): text: str class PredictionResponse(BaseModel): label: str probabilities: dict app.post(/predict, response_modelPredictionResponse) def predict(req: ReviewRequest): proba model.predict_proba([req.text])[0] label labels[int(proba.argmax())] return PredictionResponse( labellabel, probabilities{labels[i]: float(p) for i, p in enumerate(proba)} )这里有几个容易被忽视的细节服务启动的时候要花几十秒加载模型。为了不让每次首次请求都等很久我选择在模块加载时就提前load_model()而不是在请求处理时才加载。请求的输入必须做校验。Pydantic帮你解决了类型问题但业务上还要考虑文本超长、空文本这些情况防止模型被脏数据打崩。部署使用DockerDockerfile如下FROM python:3.9-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . CMD [uvicorn, app.main:app, --host, 0.0.0.0, --port, 8000]这里我用的是CPU镜像因为一开始不需要GPU推理简化部署复杂度。等积累到更高并发需求时再切GPU服务和更复杂的推理优化。基础架构搭建阶段不要太早优化。3.5 上线后的监控与迭代日志、指标、漂移检测很多项目死在“上线即终点”。模型上线之后效果一定会随时间变化这是AI工程里必须接受的事实。所以我在部署服务的同时把监控体系也一起搭起来。监控分三层系统层CPU、内存、请求量、延迟。用Prometheus Grafana就能解决。服务层每个接口的调用量、单次推理耗时、异常率。这个在应用代码里打日志就能记录。模型层真实线上数据分布变化、预测置信度变化、回流标签与模型预测的一致性。模型层里最有价值也最难做的是数据漂移检测。一个简单有效的方法是记录训练集的特征分布比如TF-IDF向量的均值然后在线上实时对比实时数据的分布差异。差异超过阈值就告警。另外一个实操技巧是每次模型服务收到的预测请求和预测结果都要落库并定期抽取一部分给人工打标。这些数据攒够一批就是下一轮模型迭代的训练集。这就是“数据飞轮”。没有这个机制你的模型就是一瓶不改配方的药迟早失效。4. AI工程里的常见陷阱与排查速查表这部分我整理了我在这个项目中遇到的30多个坑精选出最值得说的几类做成一张速查表。对标你自己的项目一条条对照排查。4.1 环境与依赖问题症状解决方案Python环境冲突安装A包导致B包不能用立刻用conda创建独立环境每开新项目就建新环境不要偷懒CUDA版本不匹配PyTorch检测不到GPUnvidia-smi看到的CUDA版本是驱动支持的版本torch.cuda.is_available()才是实际能用用PyTorch官方推荐的CUDA版本安装依赖没有锁定版本过一阵子重跑安装了一堆新版本结果行为完全变了用requirements.txt或Pipfile严格锁定版本最好用poetry管理4.2 数据与特征问题症状解决方案数据泄漏训练集指标高得离谱线上效果差严格按时间序列划分数据确保特征中没有使用未来信息特征分布漂移线上预测结果集中偏向某一类定期比较特征均值/方差设置漂移告警数据版本不清晰模型和当时的训练数据对不上引入DVC数据版本跟Git提交强关联4.3 训练与优化问题症状解决方案过拟合训练集指标高验证集指标低加正则化、数据增强、早停或降低模型复杂度学习率跟batch size不匹配模型loss震荡不降记住lr与batch size大致呈线性缩放关系随机种子不一致每次训练结果差很多在数据划分、数据加载、模型初始化三处都设置固定seed4.4 部署与性能问题症状解决方案首次请求特别慢服务起来后第一次调用要等很久在启动阶段就加载模型不要懒加载GPU显存不够OOM、CUDA out of memory减小batch size、用梯度累积、换更小模型或做模型量化CPU推理太慢并发上来后延迟直线上升上batch inference或用ONNX Runtime加速推理4.5 工程协作与迭代问题症状解决方案模型复现困难同事跑同一个脚本结果不同代码、数据、超参数、环境四方面全部锁定没有模型版本管理线上模型和实验模型搞混了MLflow Model Registry注册每个生产模型版本缺少回滚机制模型上线后发现效果差无法快速回退部署时保留前一版模型用负载均衡做灰度发布个人实操中的一点体会说了这么多最后我想讲一个我在整个过程里感触最深的事情。ai-engineering-from-scratch这个项目最难的部分从来不是某个模型算法的数学原理而是面对不确定性的决策能力数据到底要清洗到哪一步算够模型是花时间调参还是赶紧部署上线再看效果线上数据漂移了是先补数据还是先降阈值这些问题的答案没有对错只有基于数据的判断。而工程化能力本质上是把你做判断的依据沉淀成可验证、可回溯、可迭代的流程。最后分享一个小技巧每周固定留一小时看一遍线上服务的日志和指标。不要等报警了才去看。你会从中发现很多有意思的信号——比如某个用户群体反复调用、某个特征值分布已经悄悄改变、某个接口的响应时间在缓慢爬升。这些细节才是你下一步迭代方向的来源。AI工程的功夫一半在搭建时一半在维护时。
阅读完成 · 觉得有帮助?