
这几年“AI工程”这个词被反复提及各种“7天转行AI”“大模型实战速成”满天飞。但聊过不少准备入行或者刚转行的朋友之后我发现真正卡住人的往往不是“不会调包”而是对整条链路缺乏掌控力——模型在笔记本上跑得再顺一进生产环境就崩或者写神经网络头头是道但根本不清楚数据怎么管、推理怎么提速、效果怎么科学评估。这也是我想完整复盘“ai-engineering-from-scratch”的原因从一张白纸到能够独立把一个机器学习项目落地成可交付的稳定系统。这里没有短视频式的捷径只有我亲身踩坑后总结出的完整路径以及必备的技术细节、工具选型和判断原则。无论你是刚入门的学生、做后端想转AI的开发者还是已经在调模型但总觉得“差点意思”的工程师这套思路都值得从头过一遍。1. 先想清楚AI工程到底在解决什么问题1.1 为什么很多人会卡在半路不少人一开始学AI最大的错觉是把“训练模型”等同于“AI工程”。一个模型训练出来只是拿到了一个干巴巴的产物。当年我花了两周跑通一个图像分类模型在验证集上准确率看起来不错但放到真实环境里遇到了完全没见过的数据分布、请求并发、延迟要求、模型更新流程才发现自己连系统化的处理框架都没有。那一刻就意识到AI工程的核心矛盾不是“如何提高准确率”而是“如何在真实环境中稳定、可控、可迭代地生产和维护模型能力”。这个认知差别很关键。训练一个模型像在实验室里做一道菜只要按菜谱来成品大概率能看。但AI工程是开一家餐厅要考虑食材供应、菜品的一致性、高峰期出餐速度、顾客过敏反馈、菜单更新机制。任何一个环节掉链子菜做得再漂亮也开不下去。所以从头回顾AI工程我倾向于把它定义为“数据、模型、代码、基础设施与业务目标之间的一整套协作体系”它远远大于“搭个神经网络”这一件事。1.2 一张能力地图照着查缺补漏既然是一个体系最忌讳的就是零散地东学一点、西学一点。我把AI工程拆成几个层次每个层次有明确的最低目标第一层是数学与编程基础这是后续所有操作的载体第二层是机器学习与深度学习原理它决定了你能否正确解读模型行为第三层是数据工程涵盖采集、清洗、标注、版本管理和特征处理第四层是训练与评估包括实验管理、超参数搜索、指标设计第五层是把模型包装成服务并稳定运行涵盖接口设计、性能优化、监控告警和模型更新机制第六层是LLM出现后的新应用范式比如RAG和Agent编排但万变不离其宗底层还是“数据、模型、评估、迭代”这件事。我建议按这个地图查缺补漏不要从头到尾啃完一本大部头再动手。成年人学习的最优策略是“输出倒推输入”确定一个目标场景缺哪块学哪块但一定要把缺的知识系统地补进自己的框架里而不是学完就忘了。比如你想做一个电商评论情感分析系统那你至少会涉及爬取/导出数据、文本清洗、模型选型、微调、打包API、监控漂移。这一圈下来地图上的每个格子你都会路过一遍。这才叫“from scratch”不是“从零背公式”而是“从零完整经过一个项目”。2. 打地基时最容易忽略的三件事2.1 数学到底要学到什么程度一说到基础很多人第一反应是“我数学不好是不是学不了AI”。实际上大部分AI工程场景用到的数学没有想象中那么深但核心概念必须理解到位。我实际工作里最常用到的数学主要是三类线性代数中的矩阵乘法、维度变换和向量相似度概率论中的条件概率、分布、贝叶斯思想微积分中的梯度与导数的含义。比如Transformer架构里的注意力机制本质是对Q、K、V三组矩阵做乘法然后缩放朝哪个方向加信息全靠矩阵维度对齐。不理解矩阵怎么相乘、为什么维度要匹配写代码时根本看不明白报错信息更别说手工推导一个shape对不对。概率论更不用说了模型输出的本质是条件概率分布。你设置损失函数时其实是在假设误差服从某种分布。干脆举个容易理解的例子做推荐系统时CTR预估模型的输出值通常会被当作“用户点击的概率”然后按这个概率排序展示。如果不懂概率校准你的输出值可能整体偏高或偏低排序没问题但一旦要结合成本或收益做决策就会出错。微积分的梯度则对应着“参数往哪个方向调整损失下降最快”理解这个你才知道学习率为什么不能太大也不能太小为什么模型会发散。我的建议很实际你不用去证明复杂的定理但遇到一个公式至少要能用“人话”解释它是在干什么。随便拿一篇模型论文其中90%的数学表达式你要是能对着代码说出“这个地方在做特征缩放”“这个loss在惩罚预测过于自信的错误”基本功就够了。具体的数学细节遇到再去查但核心感觉不能缺。2.2 Python工具链先养成肌肉记忆学AI工程Python基本是默认的实践语言。不少人觉得会写import pandas as pd就算入门了实际上一碰到工程化就手忙脚乱。我记得自己早期吃过一个亏直接在全局Python环境里装依赖某次为了试一个新模型顺手升级了NumPy结果把另一个正在跑的数据处理脚本搞崩了整整排查了一下午。从那以后我养成了一个习惯每个项目一个独立的虚拟环境并用文件把依赖锁死。现在Python生态里环境管理工具很多最传统的是venv加requirements.txt更现代的有poetry、uv等。我不太想在这里吹捧某一个工具因为习惯最重要。但有两个原则建议从第一天就坚持第一环境必须与项目绑定换项目就换环境第二依赖库的版本必须锁定至少精确到PATCH级别。你训练完一个模型三个月后回来复现如果依赖版本全都漂移了一定会想抽自己。项目根目录放一个requirements.txt或者pyproject.lock把每次实际验证过的版本记录下来这比任何“复现指南”都有用。另外一个细节是版本管理。别以为只有代码才需要Git数据处理脚本、模型训练脚本、配置文件、实验结果记录都应该纳入版本管理。代码写得烂可以改没有版本管理才是灾难。很多“from scratch”项目最后做不下去不是模型不行是代码和实验记录乱到连作者本人都无法还原自己做了什么。2.3 用一次“手写训练循环”来打通原理理解训练过程最快的方式不是直接上高级框架而是亲手写一个小型训练循环。哪怕你之后每天都在用PyTorch Lightning或Keras我也强烈建议至少在初学阶段手工实现一次前向传播、损失计算、反向传播的调用和参数更新。不是为了造轮子而是为了清楚地看到每一行代码在做什么。import torch import torch.nn.functional as F model torch.nn.Linear(32, 8) # 一个最简单的网络 optimizer torch.optim.SGD(model.parameters(), lr0.01) dataset torch.randn(128, 32) labels torch.randint(0, 8, (128,)) for epoch in range(10): optimizer.zero_grad() logits model(dataset) loss F.cross_entropy(logits, labels) loss.backward() optimizer.step() print(fepoch {epoch}: loss {loss.item():.4f})这段代码看着简单每个环节都有不可省略的作用。zero_grad()是清空上一次迭代留下的梯度不清空的话梯度会累加参数更新方向就错了model(dataset)做前向传播得到每个类别的得分cross_entropy把得分转成概率分布同时计算预测分布和真实分布的差距backward()根据损失对每个参数求梯度step()按照梯度和学习率更新参数。跑通这个循环后你已经理解了所有深度学习训练的骨架后续看什么框架都是在这个骨架上加缓存、加分布式、加自动调参。3. 数据工程让模型吃到干净食物的关键环节3.1 一份真实数据集的处理流水线模型训练里有一句话“垃圾进垃圾出”。但什么是“垃圾”很多时候要亲自处理过脏数据才有体感。我从一个真实项目的处理流程说起假设你要做用户评论情感分类原始数据可能是一堆导出的订单备注每行文本带着各种表情符号、错别字、中英文混杂、脱敏信息、重复提交。完整的处理流水线应该分几步走。第一步采样和探查。不要一上来就写清洗代码而是先用pandas对数据进行描述性统计看看每列的非空率、唯一值个数、长度分布。我会把文本长度按分位数打出来确认有没有异常长的灌水文本有没有大量空值行。第二步清洗规范化。统一把文本转成小写、全角转半角、去掉URL和特殊符号、处理emoji表情具体操作取决于你要不要保留表情作为情感特征。第三步去重与标签修正。很多人会忽略去重但线上真实数据里同一用户重复提交很常见不去重会导致模型对某些重复样本记忆过深。标签修正更麻烦最好通过规则加人工抽检结合。第四步数据集划分。这一步务必放到特征处理之前划分原则是先切分再处理避免信息泄漏。第五步特征工程与保存。把清洗好的文本转成模型需要的数值格式并以规范的文件结构存储。数据版本管理是我特别想强调但容易忽略的环节。产品需求变了数据分布换了老板让你对比新旧模型效果这时候如果连“旧模型用哪份数据训练的都说不清”对比就没有意义。轻量方案是给每份数据集打上日期和schema版本号存成独立目录团队协作多了可以用DVC这类工具把数据文件纳入Git一样的版本管理。数据是模型的“食材”食材变质了厨艺再好也白搭。3.2 数据泄漏我连续踩了两次的雷数据泄漏是AI工程里最常见、也最隐蔽的问题我在这上面连续吃过两次大亏。第一次是在一个时序预测项目里我用随机切分把数据集分成训练集和验证集模型在离线验证集上表现非常好但一上真实环境立刻失灵。后来才发现时序数据里未来的信息混进了训练集模型等于“提前看了答案”当然准确。这个教训的核心是对时间序列数据切分数据的逻辑必须是“按时间切分”前80%的时间段用来训练后20%的时间段用来验证不能随机打乱。第二次是在特征处理时先对整个数据集的均值、标准差做了标准化再切分训练测试集。听上去挺合理实际上测试集的信息已经通过“全局均值”偷偷流进了训练过程导致离线评估虚高。正确的做法是先只在训练集上计算均值和标准差再用同一组参数去转换测试集。这类泄漏很微妙但它对我们的判断会产生严重误导。我把几种常见泄漏来源整理成一张速查表泄漏来源场景对策随机切分时序数据股票预测、销量预测、异常检测按时间切分或使用时间序列交叉验证全局统计特征标准化、归一化、缺失值填充只用训练集统计量转换时应用到验证/测试集重复样本跨集数据增强产生的近似重复样本同时出现在训练和验证集按样本源头去重做分组切分特征包含未来信息引入“当月总计”这类事后统计字段严格对照特征产生时间与预测时间点数据泄漏最可怕的地方在于它会让你的离线指标全线飘绿但到了线上完全失效而这类“技术债”往往要到项目上线才爆发。排查泄漏的时候不妨问自己一句这个特征在预测那一刻真的“已经知道”了吗如果答案不确定宁可先砍掉再做实验。4. 训练、评估与迭代不要凭感觉做实验4.1 一份可复现的实验管理方案开始学AI时我的实验记录就是随便记在文本文档里的几行字改了learning rate效果变好了。但“变好”具体是多少、用哪份数据、哪个commit、哪种预处理统统没记。后来想复现曾经验证过的一个好效果重试了好几次都回不到当时的结果。从那时起我强迫自己给每个实验建立一个规范记录至少包含实验目的、代码版本、数据版本、关键超参数、验证指标、结论备注。不用一开始就上个平台一个电子表格或Markdown文件就够了关键是有结构地记录。更工程化的做法是使用MLflow或者Weights Biases这类实验管理工具。把每个实验的配置、指标、模型产物自动记录下来随时可以横向对比不同参数的影响。以MLflow为例你只需要在训练脚本里加几行mlflow.log_param()和mlflow.log_metric()它就会自动为每个实验注册成一个带时间戳的运行记录。有了这套机制超参数搜索就不再是瞎猜你可以把学习率、批大小、层数都记录在案然后按指标排序找出最优组合。固定随机种子也是如此不固定的情况下一次训练结果前后差1%到2%都算正常这会导致你连“这个参数改动到底是有效还是噪声”都判断不了。4.2 评估指标怎么选才不会自欺欺人评估指标是AI工程里最容易被误解的一环。我见过很多新人对模型表现的第一反应就是“准确率多少”实际上准确率在类别不平衡的场景里可能是一个高度误导性的指标。设想一个罕见的设备故障检测场景负样本占99.9%。你只要做一个“永远返回正常”的模型准确率就有99.9%但业务上这个模型毫无价值。这时应该看召回率真实故障里抓到了多少、精确率报警里有多少是真的误报或者更业务化的F1、PR曲线下面积、每个类别的单独指标四象限。另一个很容易犯的错是把模型指标和业务指标混为一谈。模型指标比如“点击率预估的AUC”通过简单的排序调整就能显著提升AUC但业务希望看到的是“GMV提升”或“用户留存变化”这中间隔着价格、商品供给、推荐位策略等多个环节。我自己的做法是先把业务目标翻译成可优化的模型目标业务要“减少客服投诉量”拆下来可能是“更准确地识别出高投诉风险用户”再拆成“投诉样本的召回率优先但精度不能低于某个阈值”。评估指标必须在所有参数变化前先定好一次实验只改一个变量。不然你同时改了模型结构和数据配比效果变好或变差了你根本不知道功劳或锅该算在谁头上。5. 交付把模型变成一个可靠的服务5.1 从Jupyter到生产环境的五个动作模型训好只是开始真正拉开工程师差距的是交付环节。把Jupyter里的代码变成一个线上服务我每次都会检查五个动作。第一模型序列化。保存模型结构加权重我通常用joblib或torch.save同时保存一份特征处理流程保证预测时的输入格式和训练时完全一致。第二接口封装。把它包成一个带清晰输入输出定义的API比如用FastAPI声明请求体是什么样、响应体是什么样。第三并发与资源控制。默认情况下一个Python进程里的模型推理是串行的高并发时请求会排队严重时直接超时。所以我会在服务里设置合理的线程池或异步任务队列模型加载到内存后常驻而不是每个请求都重新加载一遍。第四超时与重试。任何线上服务都必须有超时时间不然一个慢请求会拖垮整个服务。客户端要配重试机制但要小心重试风暴——所有客户端同时重试同一个下游很容易把下游打挂。第五可观测性。至少要有请求量、延迟分位数、错误率、推理服务占用四类指标配合日志能回溯单个请求的输入输出。一段最简单的FastAPI预测服务骨架看起来是这样的from fastapi import FastAPI from pydantic import BaseModel app FastAPI() model load_model() # 在启动时加载一次 class Item(BaseModel): text: str app.post(/predict) def predict(item: Item): features preprocess(item.text) # 必须与训练时一致 score model.predict_proba([features])[0][1] return {score: score}这里最关键的一点是模型是无状态的你不能把某个请求的用户信息存在模型内部变量里。另一个点是启动时加载模型如果你在predict函数内部加载模型每次请求都等于重新读一遍磁盘和大文件延迟会高到没法用。5.2 推理性能优化先别急着上大招网上聊性能优化一上来就是量化、蒸馏、TensorRT动辄精度损失多少、加速多少倍。但我实际做项目的经验是90%的场景根本用不着这些炫酷技术朴素的工程优化就能满足需求。第一步永远是定位瓶颈是CPU计算瓶颈、内存带宽瓶颈、还是锁竞争和IO瓶颈我之前碰到过一个“模型慢”的报障一顿分析后发现90%的时间花在一个无关紧要的规则判断函数上和模型推理毫无关系优化那个函数后整体延迟直接降了一个数量级。一般的优化优先级我建议这样排第一优先是批处理把多个请求合并成一个batch喂给模型。GPU推理尤其吃这一套单请求延迟可能差不多但吞吐量能提升好几倍。第二是模型结构本身的缩放如果精度允许切换到更小的模型变体比如从大模型换到蒸馏后的小模型收益非常直接。第三是缓存对相同或高度相似的输入直接返回缓存结果。比如推荐系统里同一用户一天的推荐结果几乎不会变你完全可以用短时缓存扛住大部分流量。第四才是量化等高级手段。量化的实际收益要看硬件和框架部署前一定要做A/B测试确认精度损失在业务可接受范围内。性能优化最怕的不是没有方案而是不知道哪个方案解决当前问题白做一堆“看起来很高级”的无用功。6. LLM时代AI工程的新技能与不变的老规矩6.1 如何从零搭一个可用的RAG应用LLM爆发后“AI工程”这个词的外延又被撑大了一圈。以前我们讲机器学习工程现在还得讲Prompt编排、向量检索、Agent工具调用。我个人认为新手切入LLM应用开发最值得掌握的范式是RAGRetrieval-Augmented Generation因为它的链条清晰而且对底层原理的依赖相对温和。RAG的核心思路是不直接让大模型凭空回答而是先从你自己的知识库/文档里检索出相关片段再把片段和大模型一起组装成回答。这样既能减少幻觉也方便把模型能力约束在你自己的私有知识体系内。从零搭建一个RAG应用过程通常包括文档加载、文本切块、向量化Embedding、存入向量数据库、查询时做召回、组装Prompt、交给LLM生成答案。整个链路里文本切块是新手最不重视但又最容易影响效果的一步。块太大检索出来的是大段无关内容既浪费token又容易让模型抓不到重点块太小语义不完整召回结果断章取义。我自己在实际项目里常用的是500到800字符的块大小同时让相邻块之间有50到100字符的重叠这样即使关键信息刚好被切分到两个块的交界处重叠机制也能帮助它完整出现。向量数据库的选择上也别一上来就上重型服务。起步阶段用Chroma或FAISS这类轻量级方案就够了本地跑一个原型效果好再迁移到正式集群。Prompt组装看起来是在“写话”其实是个工程问题。系统提示词里要有明确的角色定位、回答边界和输出格式要求还应该把检索到的片段用清晰的分隔符包裹起来明确告诉模型“只依据以下材料回答不要编造”。一个又长又乱的Prompt不如把每个部分的结构和目的写清楚。RAG工程落地后还需要加一步关键机制当检索结果与问题完全不相关时宁可让模型说“不知道”也不要硬编。这一点需要在Prompt里强调并在测试集中覆盖这类边界case。6.2 评估与护栏是LLM工程的生死线LLM应用最容易让人掉以轻心的地方就是它对“效果”的评估很难自动化。传统模型看AUC、看F1但大模型生成的一段回答是否准确、是否完整、是否有害没有一个自动指标能完全替代人工判断。我自己的方法是分层建立评估体系第一层是检索评估只评估召回出来的文档片段和问题之间的相关性指标比如RecallK第二层是生成评估让标注人员或者一个更强的模型对回答打分从准确性、完整性、可读性等维度综合评判第三层是A/B测试对比新旧版本在真实流量中的用户反馈、采纳率、投诉率。护栏这个东西很多人会觉得是合规或风控的附加品但对技术人来说它本质上是系统设计的一部分。例如对用户的输入和模型的输出做关键词过滤、敏感信息脱敏、权限隔离如果应用内要调用外部工具工具调用必须限制在最小必要范围内对于涉及推荐、决策的场景必须记录决策日志方便事后审计和追溯。没有护栏的LLM应用就像不带刹车就上路速度越快风险越大。成本控制也不可忽视。LLM推理是按token计费的同样一个功能模型回答长度越长、检索片段越多成本越高。我会在实际开发中做一次token成本估算预估每日请求量乘以单次平均输入输出token数再乘单价看看一个月下来要走多少预算。如果成本超标优先从“减少不必要检索上下文”和“让小模型做粗排、强模型做精排”这类方向去压而不是一味砍功能。7. 常见问题与排查技巧实录7.1 环境部署三板斧环境问题是入门时最掉头发的难点而且报错信息经常极具误导性。最常见的三大类CUDA版本和PyTorch不匹配报CUDA error: no kernel imagePython版本冲突导致某些包编译失败还有系统里同时存在多个Python解释器终端敲python和python3用的是完全不同的环境。我的排查套路就三板斧先看版本再看显存最后看路径。先确定当前环境用的Python是哪个which python搞清楚路径然后检查GPU驱动版本和CUDA可用性nvidia-smi能看到驱动支持的CUDA版本PyTorch官网会写每个版本对应什么CUDA编译版本两边要匹配上。第二个常见问题是显存溢出CUDA out of memory很多人以为要换更大的卡其实可以先试着把batch size调小、梯度累积做起来、或者开启混合精度训练往往能解决一大半问题。第三个是我强烈推荐的终极方案所有本地环境只用来调试真正任务统一跑在Docker容器里。容器一旦构建成功在任何机器上表现一致等于把你从“环境地狱”里解放出来。这个习惯早养成早受益。7.2 训练里最常见的三个“症”训练模型时翻车的地方就那么几个我按频率排一下Loss不降、Loss发散、过拟合。Loss不降先别急着改模型结构。我用小规模样例过拟合一次确认代码逻辑没有bug、数据标签没有错位再检查学习率太高了会在最优点附近震荡太低了半天挪不动地方再看是不是数据分布极其不均衡导致模型一直输出多数类。Loss发散通常是大问题多数原因是学习率过大、梯度爆炸或者数据里有极端离群值。做了梯度裁剪还发散就要回头看看损失函数的数值稳定性。过拟合是训练集指标好、验证集指标差的经典现象常见对策包括增加数据、加正则项、做数据增强、早停、还有把网络变小。这里有一个很容易被忽略的点如果验证集分布和线上分布不一致你为了“减过拟合”调出来的模型可能只是拟合了验证集的口味到了线上一样挂。所以验证集本身的代表性与维护更新也是重要功课。7.3 线上服务异常定位三步走模型服务上了线异常永远不会只有一个原因。我的定位顺序通常先看监控数据再看日志最后复现。监控数据先回答“什么指标异常”延迟涨了是慢请求变多还是负载涨了错误率涨了是输入格式变了还是模型版本出了问题日志阶段要看单条请求的全链路从入口到模型推理再到下游调用每一步耗时和返回状态都记录下来。最后才是复现用同样的输入在测试环境跑一遍看是否能稳定再现问题能稳定再现就立刻缩小范围到某一步。还有一个容易被忽视的线上问题数据漂移。线上用户行为分布一直在缓慢变化你在离线训练时用的数据分布可能半年后就走样了。面对漂移最有效的手段是每隔一段时间做一次重训练并且用监控指标追踪输入特征分布和模型预测分布的变化趋势。报警设一个合理的阈值不要设得太灵敏导致告警疲劳。我在经历了几次线上事故后总结出一句话线上服务出问题不可怕可怕的是你完全没有观测到问题已经发生的能力所以可观测性是AI工程里最值得提前投入的部分。最后分享一个我坚持到现在的习惯从“from scratch”一路走过来如果只挑一个最值得保留的习惯我会毫不犹豫地选“写实验笔记”。每天花十分钟把当天尝试了什么方法、为什么尝试、结果如何、下一步打算记录在一个连续编号的文档里。这个笔记不需要多工整但它是你个人工程思维不断生长的证据。很多创新不是灵光一现而是你从某次失败记录里发现了被忽略的线索。AI工程是一个需要持续积累判断力的领域而判断力来自足够多的“做过—复盘—修正”循环。记得一开始给自己定一个能完成的基准项目哪怕很小把它从数据处理一路做到线上运行起来你收获的远比看十遍教程要多。