1. 先把概念掰清楚:AI工程不是"写几个模型"
我见过太多人被"AI工程"这四个字吓住,或者反过来被它迷惑。有人觉得它等于调参炼丹,有人觉得它等于数学奥赛,还有人觉得只要会pip install transformers就算入了门。实际情况是:AI工程是一个从业务问题出发、以模型为核心、以稳定交付为终点的完整过程,模型训练只是这条流水线上的一环,甚至不是最耗时的一环。
几年前我接手过一个内部项目,团队里算法同事交出来的模型在测试集上准确率接近99%,但放到线上一周,业务方就炸了。不是模型不好,而是模型跑不起来:特征和训练时对不上、上游数据源半夜断流、模型版本没人记、推理延迟忽高忽低。那一周我们没写一行模型代码,全在补工程债。从那时候我彻底想明白一件事:AI工程的核心壁垒,是把"能跑的模型"变成"能一直跑的业务"。
标题叫"ai-engineering-from-scratch",我想写的不是又一份框架教程,而是一条从零开始、尽量少踩坑的完整路径。这篇文章主要面向两类人:一是刚转行想做AI应用开发的程序员,二是已经在跑模型但总觉得"上线就翻车"的算法工程师。它会围绕AI工程到底是干什么的、入门顺序怎么排、工具链怎么选、一个完整项目怎么落地、以及哪些环节最容易炸这五个问题展开,中间穿插我这几年真实踩过的坑和最终沉淀成习惯的做法。
2. 从零开始的学习顺序:这条路我替你们走过了
2.1 先破一个误区:入门要先学算法,还是先学工程
很多自学路线图上来就是线性代数、凸优化、手推反向传播,学了三个月还在原地。我并不是说这些不重要——它们重要,但它们是"天花板",不是"地板"。对于从零开始做AI工程的人来说,最先需要的是把一个机器学习项目跑通闭环的能力:拿到数据、预处理、训练一个简单模型、评估、把这个过程脚本化、能被别人复现。
我建议的起点是"最小闭环"而非"深挖原理"。用你最熟悉的语言(Python最好)完成一个最朴素的分类或回归任务,比如用逻辑回归预测鸢尾花、用线性回归拟合房价。这个过程会逼你接触sklearn、pandas、numpy这几个最基础的工具,也会让你第一次体会什么叫"数据质量决定模型上限"——哪怕是最简单的模型,只要数据喂得对,也能出不错的效果。等这个闭环跑通了,再回头补数学就不慌了,因为你已经知道每个公式大概在解决什么环节的问题。
2.2 真正值得按顺序死磕的四块内容
我把学习路径砍成四个阶段,每个阶段的产出都是"能跑的东西",而不是"看过的教程"。这个顺序我反复验证过,对零基础的人最友好:
阶段一:数据操作与Python基本功(2~3周)
- 重点掌握
pandas的数据清洗、聚合、透视,numpy的广播机制和向量化操作 - 练习从CSV、数据库、API三种常见数据源取数并做标准化处理
- 目标:拿到任何一份"脏数据"能在一个小时内整理成规范的表格
阶段二:经典机器学习流程(4~6周)
- 学
sklearn的Pipeline机制,理解fit/transform/predict三阶段设计 - 掌握交叉验证和评估指标的选择:分类看precision/recall/F1,回归看MSE/MAE
- 重点练特征工程:缺失值填充、类别编码、数值归一化、特征交叉
- 目标:完成一个带完整评估报告的二分类任务,并写清楚每个步骤的理由
阶段三:深度学习入门与框架熟练(3~4周)
- 学
PyTorch的基础张量操作、自动求导、nn.Module、DataLoader - 不急着追模型结构,先徒手实现一遍MLP在Mnist上的训练循环
- 理解训练/验证/测试集划分、过拟合的直观表现、早停和Dropout的作用
- 目标:能独立从零训练一个模型并保存加载权重,不依赖笔记本里的现成代码
阶段四:工程化意识(持续进行)
- 学会用
Git管理代码和模型文件,学会写README让别人能复现 - 了解Docker镜像构建、依赖锁定、环境隔离
- 懂一点Linux基本命令和shell脚本:
grep、crontab、systemd - 目标:你训练好的模型,换一台干净机器,两条命令能跑起来
这四个阶段不需要等完全学完再进项目。我见过最高效的做法是学完阶段二就立刻做第一个端到端小项目,哪怕只是做个房价预测的定时脚本,也比你刷十遍教程管用。
2.3 要不要系统学数学?我的个人建议
直接给结论:线性代数要会"用",概率统计要会"理解",微积分可以用到再补。你不需要手推Attention的梯度,但你必须知道矩阵乘法的形状变化、Softmax在干嘛、正则化项惩罚的是什么。最经济的方式是:学到哪个算法,就去查哪个算法背后的数学直觉。比如你学逻辑回归,就去搞懂为什么用sigmoid把线性输出压到0到1之间,为什么loss用交叉熵而不是均方误差。这样数学是跟着实际问题长出来的,不会有"学了一堆不知道怎么用"的虚无感。
3. 工具链选型:这几年我固定下来的组合
3.1 为什么我不推荐"一步到位"学全套平台
市面上的AI平台工具五花八门,有全托管MLOps平台,有可视化的拖拽式建模工具,还有大而全的数据科学平台。我的观点很直接:入门阶段这些东西最好别碰。它们能让你很快看到结果,但也会让你完全丧失对底层过程的理解。一旦平台配置和你的业务场景不匹配,你连问题出在哪都定位不了。
从零开始的人最需要的工具组合是"够用但不过度封装"的:模型层用PyTorch,数据处理用pandas+Polars,实验追踪用MLflow,工作流编排用Prefect或Airflow,部署用FastAPI+Docker。这套组合的好处是每一层都能拿掉替换、都能看到内部逻辑,出了问题你可以从最底层排查。
3.2 核心工具推荐清单(含理由)
| 工具 | 用途 | 为什么选它 |
|---|---|---|
| Python 3.10+ | 主语言 | AI生态最全,没有之一 |
| PyTorch | 深度学习框架 | 动态图调试方便,社区资源多,工业界覆盖面广 |
| pandas / Polars | 数据处理 | pandas生态成熟,Polars处理大数据更快,两者可以共存 |
| sklearn | 传统机器学习 | Pipeline和评估体系太完善了,很难被替代 |
| MLflow | 实验追踪与模型注册 | 几行代码就能记录参数、指标、模型产物,自托管免费 |
| FastAPI | 模型服务化 | 原生异步、自动生成API文档、Pydantic校验类型 |
| Docker | 环境一致性 | 解决"在我机器上是好的"这个经典问题 |
| Airflow / Prefect | 数据与训练流程调度 | 定时、依赖管理、失败重试,生产环境的拐杖 |
| Git + DVC | 代码与数据版本管理 | 代码用Git,数据用DVC,分别处理两类不同大小的资产 |
3.3 选型的一个关键直觉:越接近业务的地方,越要选可插拔的工具
这句话我踩了不少坑才领悟。比如做模型服务,早期我图省事直接把训练代码封装成接口,结果每次新增一个特征都要重新部署整个模型。后来改成FastAPI + 独立的特征工程模块,训练和推理共享同一份特征代码,但部署时各自独立,改动一个模块不影响另一个。这个习惯帮我省了无数周末。
同样,实验追踪不要等到项目大了再补。哪怕你只是自己在本地训练,也要从第一次跑就习惯性记录:数据集版本、代码commit号、关键超参数、评估指标。我见过太多团队最后对着几个模型的输出文件说不清哪个是哪个,就是在这一步省了事。习惯成自然之后,你根本不需要刻意记,随手就打上tag了。
4. 一个能跑通全流程的实战例子:评论情绪打分服务
4.1 任务定义:先想清楚"目标"再动键盘
整个AI工程里,最容易被跳过的就是任务定义。我用一个例子带你看完整流程。假设你要做一个"电商评论情绪打分服务":输入一条评论文字,输出-1到1之间的情感分,负数偏负面、正数偏正面,同时给一个置信度。
很多人的第一反应是"先找一个预训练模型跑起来看看"。我的建议是反过来的:先定义清楚评估标准、响应延迟和数据边界。比如:
- 业务目标:过滤出负面情绪集中的商品评价,准确率优先还是召回率优先?
- 性能目标:单条评论的接口响应时间在99分位不超过300ms
- 数据边界:目前只处理中文电商评论,评论长度最长为512字
这三点定了之后,模型选型、数据标注、服务架构都有了约束。没有约束的方案是设计不出来的。
4.2 数据获取与清洗:花的时间比你想象的多得多
我从一个公开数据集来源找了一批中文电商评论,大约两万条,已经标注好了正负情感。但实际用的时候仍然要做三件事:
第一,去重和过滤。评论里大量"好评"、"666"、"物流快"这种极短文本,重复度极高。保留它们会导致模型严重偏向这些高频表达,所以我按文本完全去重,并把长度小于4个字的过滤了一部分。
第二,标签平衡。原始的负面评论只有两成左右,直接训练会导致模型什么都预测成正面。我用欠采样(随机丢掉一些正面样本)把正负面比例调到大概3:2,既保留多样性,又不太偏。
第三,划分数据集。严格按评论的商品ID划分训练/验证/测试,而不是随机划分。这样能测试模型对"没见过的商品"的泛化能力,避免同一个商品的相似评论同时出现在训练和测试里,评估结果虚高。
4.3 模型选型与训练:用小模型先探路
这个任务我用的是一条很轻的路线:中文预训练向量 + 一个简单的逻辑回归作为基线,然后对比一个用bert-base-chinese微调的模型。为什么要跑两个?因为基线的意义是给你一个"最差不能低于它"的锚点。如果你的BERT模型只比逻辑回归高0.5个点,那部署成本和推理延迟根本不值得。
评论长度不一,我先把所有文本做了切词,然后对每条评论取词向量平均,喂给逻辑回归。这个训练过程不到两分钟就完了,测试集准确率大约0.82。然后我用transformers库加载BERT中文模型,加了AutoModelForSequenceClassification,输出层是2分类,训练5个epoch,batch size调成16,学习率设2e-5。显存不够用的话就开梯度累积,效果差别不大。微调一轮大概二十分钟,最终在测试集上拿到了0.89的准确率。这个差距合算,值得上BERT。
训练过程中我每一轮都用MLflow做了记录,包括验证集的loss和F1,最后选验证集表现最好的epoch而不是最后一个epoch。
4.4 服务化:把模型包成别人能调的HTTP接口
训练只是拿到一个产物,要让别人用这个模型,得做一层服务。我用FastAPI写了一个极简接口:
from fastapi import FastAPI from pydantic import BaseModel, Field import torch from model import load_model_and_tokenizer app = FastAPI() model, tokenizer = load_model_and_tokenizer("./artifacts/best_model.pt") class CommentInput(BaseModel): text: str = Field(..., max_length=512, description="评论内容") class ScoreOutput(BaseModel): sentiment: float confidence: float @app.post("/score", response_model=ScoreOutput) def score_comment(comment: CommentInput): encoded = tokenizer(comment.text, truncation=True, max_length=128, return_tensors="pt") with torch.no_grad(): logits = model(**encoded).logits prob = torch.softmax(logits, dim=-1)[0] # 把正类概率映射到 -1..1 sentiment = float(prob[1] * 2 - 1) confidence = float(torch.max(prob)) return ScoreOutput(sentiment=sentiment, confidence=confidence)这里有一个关键工程细节:推理时一定要包torch.no_grad()。否则PyTorch会继续构建计算图,内存越跑越大,延迟也会一路飙升。这也是新手最容易漏的地方。
4.5 把这套东西容器化并验证可复现
服务写完,下一步是容器化。Dockerfile核心内容很简单:
FROM python:3.10-slim RUN apt-get update && apt-get install -y --no-install-recommends \ gcc libgomp1 WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY src ./src COPY artifacts ./artifacts EXPOSE 8000 CMD ["uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8000"]关键在libgomp1这里。没有这个库的话,PyTorch在容器里加载时大概率报错cannot load libgomp.so.1。这种"镜像小坑"对没有容器经验的人来说能卡一整晚,我把这个放出来,希望帮大家省这个时间。
到这一步,整个项目构成了最小闭环:数据清洗脚本、训练脚本、实验日志、模型服务。你随便找台机器装好Docker,docker build+docker run,一个能用的AI服务就跑起来了。这就是AI工程从零到一的地基,后面的优化都是在这个框架上加砖加瓦。
5. 上线容易翻车的地方:我亲历过的完整排查链路
5.1 第一个坑:训练-推理特征不一致
这是AI工程里最经典也最隐蔽的问题,几乎每个团队都会踩。现象是:模型在离线评估时准确率0.89,线上调用时对同样的输入却给出完全不同的输出。我做过的排查过程是这样的:
第一步,复现问题。拿一条线上日志里的输入,直接用本地模型跑一遍。这一步发现本地结果正常,说明模型本身没有坏。
第二步,对比特征。我把线上请求和训练样本同时走一遍特征工程代码,打印每一步的中间输出。然后发现,线上环境里有几个特征字段是空的,因为上游接口换了字段名,而我们特征工程的代码还在用老字段名。这一下就定位了:不是模型坏了,是喂给模型的特征和训练时不一样。特征缺失、特征顺序不一致、特征编码方式改变,都属于这个坑。
解决方案:把特征工程抽成独立模块,训练和推理统一引用同一个函数。同时给特征加schema校验,上线前跑一遍"训练样本MAP到线上特征"的一致性测试。
5.2 第二个坑:数据漂移的温水煮青蛙
还有一次,模型上线两个月后准确率肉眼可见地下降,但没有任何报错。排查思路是:先看输入数据的分布。我统计了线上最近一周的评论长度、高频词、正负面比例,和训练集做对比,发现评论长度明显变长——因为业务增加了"追评"功能,多了很多长文本,而这些长文本的措辞风格和训练数据差异很大。
这个问题的难点在于它不会主动报警,只能靠监控。我后来固定下来的做法是每周跑一次线上数据分布快照,和基线分布做KL散度对比,超过阈值就触发告警。同时定期拿一批人工标注的样本做漂移评估,及时发现模型失效。
5.3 第三个坑:模型版本和数据版本对不上
你说"用最好的模型",但"最好的模型"是哪个?有一次发布时回滚模型,发现回滚后指标和当初记录的对不上。查了半天原因是:当时训练那个模型用的数据集和后来做评测的数据集已经不是同一个版本了——有人重跑了数据清洗脚本,但没有记录清洗规则的版本。
从此我养成了一个近乎偏执的习惯:每个模型产物袋子里必须有一份训练数据快照的哈希、一份依赖库版本列表、一份关键代码commit号。用MLflow统一管这些元信息,发布之前先核对三件套。这件事看起来繁琐,但在排查线上问题时能节省几个小时。
5.4 排查通用方法论:别靠猜,靠分层定位
这几个坑走下来,我总结了一套通用的排查流程,现在每次线上模型出问题都按这个顺序走:
- 先确认是模型问题还是数据问题:用固定测试集跑一次当前线上模型,对比历史指标
- 再确认是数据问题还是管道问题:看线上请求的特征日志,对比训练样本
- 然后确认是概念漂移还是偶发异常:看时间窗口分布,是突变的还是渐变的
- 最后看模型代码是不是和记录的一致:核对模型文件的哈希值
这套流程的核心是逐层缩小范围,而不是上来就重训模型。重训只应该在确认"数据和代码都对、但模型确实过时了"之后才做。
6. 越过入门线之后,怎么继续深入AI工程
6.1 从"能跑"到"能稳定跑":可观测性是第二阶段的核心
很多个人项目或小团队项目停在了"能跑"这一步,但如果要做成真正的AI工程,下一步必须补可观测性:请求量、延迟分位数、输入特征分布、预测结果分布、显存占用、模型加载时间、单条推理时长,这些指标全部要有仪表盘和告警。
我自己会用Prometheus+Grafana监控服务层指标,用前面说的数据漂移检测脚本监控数据层指标。一个务实的建议是:不要一开始就全量上监控,先针对你最怕的两三个故障场景(响应慢、结果劣化、上游断流)做最朴素的alert,等摸清规律了再逐步扩。
6.2 性能优化方向:从"能用"到"用得起"
BERT类模型上线初期,单条推理大概要50到80毫秒,吞吐量撑不住高峰期。这条路往下走的三个方向是:
- 模型轻量化:蒸馏或量化,把精度损失控制在一个点以内
- 推理加速:换
ONNX Runtime或TensorRT,显存和速度都能明显改善 - 缓存:对重复输入(比如热门商品的同一条评论)加一层结果缓存,性价比极高
我遇到过最值的优化就是加缓存,有些线上场景请求重复率高达30%,加一层Redis缓存峰值负载直接降下来一截。这类优化不需要高深的算法功底,但对业务现象的观察很关键。
6.3 别忘了AI工程还有"人的问题"
最后说一个容易被忽略但很重要的体会:AI工程落地难,很多时候不在技术,而在协作。算法工程师要理解业务方要什么,后端工程师要理解模型能做什么不能做什么,产品经理要理解"准确率和召回率的取舍"意味着什么。
具体到做法上,我有个习惯:每次模型迭代都主动拉着相关方过一遍评估报告,不看指标数字,直接看错误例子。让业务方看到模型在哪些案例上会看走眼,比抽象讨论F1值有意义得多。这一张表出来,大家对于"模型上线意味着什么"就有了一致的预期。
我也越来越认可一个判断:AI工程的核心能力不是会多少框架,而是把模型这个"不完美的东西"放进现实系统里,还能让它稳定运行、可维护、可回滚、可迭代。这件事没有终点,每上线一个新项目都会撞见新问题。但地基打好了,后续的路就顺了。