说实话,我第一次看到“ai-engineering”这个词的时候,以为就是在讲怎么训练模型、调参跑分。等我真的把一个模型从Jupyter Notebook里挪到线上服务,才发现“训练出一个模型”只是整个系统里最小的一环。真正让模型长期稳定运行、可复现、可迭代的那套方法和工作流,才是AI工程的本质。这篇内容就围绕如何从零开始建立自己的AI工程能力展开,我会用自己走过的路线和踩过坑的细节,把环境搭建、数据管道、训练评估、服务部署、监控迭代这条主线完整讲一遍。适合正在入门AI、或者已经会调模型但没真正上过线的开发者参考。
1. 先搞懂AI工程的本质:从“训练模型”到“稳定产出服务”
1.1 模型开发只是冰山一角
很多人把AI工程等同于写模型,这是一个特别容易踩的误区。模型本身更像菜谱,工程做的却是开饭店的活儿——你不仅要能做出一道菜,还要保证每天高峰时段能稳定出菜,不同厨师做出来的味道基本一致,食材供应断了知道怎么应对,客人吃出问题了能快速定位是哪一环节出了事。
我见过太多团队死在“模型精度已经够好了,却迟迟上不了线”这个阶段。Notebook里跑通的代码,到线上要考虑的问题一个接一个:训练数据这次和上次有什么区别?超参数的实验记录去哪了?模型文件怎么版本管理?线上推理延迟能不能压到100毫秒以内?显存不够怎么办?模型悄悄变差了谁先发现?
这些问题的集合,才是AI工程真正要解决的。一个完整的AI工程链路大致长这样:
- 数据采集与清洗
- 数据版本管理与校验
- 特征工程与实验追踪
- 模型训练与评估
- 模型注册与版本治理
- 服务化部署与容器化
- 推理延迟与资源调优
- 监控告警与数据漂移检测
- 模型迭代与灰度回滚
这里面任何一环出问题,前面所有努力都可能白费。所以我在带项目的时候,一直跟人强调一句话:能把一个80分的模型稳定跑一年,远比在Notebook里调出一个90分的模型有价值。
1.2 前置能力地图:哪些技能必须补齐
从零开始做AI工程,需要的能力比“写代码 + 调参数”要多一圈。我把自己实际用到的技能整理成了一张能力地图,分三层来看比较清楚:
| 层次 | 核心技能 | 实际用途 |
|---|---|---|
| 基础层 | Python、Linux、SQL、Git | 写脚本、查数据、环境操作、代码协作 |
| 算法层 | PyTorch/TensorFlow、HuggingFace、数据处理库 | 模型训练、微调、数据预处理 |
| 工程层 | Docker、FastAPI、MLflow、监控体系、CI/CD | 服务化、实验管理、部署运维、持续迭代 |
不建议一上来就啃Kubernetes或大规模分布式训练。K8s是给“服务已经稳定跑起来、需要自动扩缩容”的阶段准备的,对一个还没上线过的项目来说,它只会增加学习成本和排障难度。
1.3 从零开始的推荐学习顺序
我当年走过的顺序回头复盘是合理的,你也可以照着走:
- 第一步:用PyTorch或HuggingFace跑通一个单机的完整闭环,从加载数据到训练再到评估。
- 第二步:把代码拆成结构清晰的脚本,每个环节独立可跑,加日志。
- 第三步:引入conda锁环境,用MLflow记录实验,用Git管理代码。
- 第四步:把模型用Docker容器化,用FastAPI暴露一个HTTP接口。
- 第五步:给服务加上监控、日志和数据漂移检测。
整套走下来,你建立的不是某个单独技能,而是“从数据到服务”的整体心智。这是AI工程最核心的东西。
2. 技术底座搭建:环境、实验追踪、数据与部署工具选型
2.1 环境管理:用conda把实验环境彻底锁住
AI工程里最让人崩溃的往往是环境问题。你半年前训练好的模型,今天重新跑推理,结果因为某个依赖库升级了版本,输出结果对不上。这种情况跟量子纠缠似的,很难排查,因为代码没变、模型文件没变,但行为变了。
我的做法是:无论做什么项目,第一步都用conda创建独立环境,并明确锁定Python版本和关键依赖版本。
conda create -n ai-eng python=3.10 conda activate ai-eng pip install torch==2.1.0 transformers==4.35.0 scikit-learn==1.3.0 pip freeze > requirements-lock.txtconda和pip各有各的优势。conda对二进制依赖的解析更好,装PyTorch这类带CUDA的库时省心很多;pip freeze能输出完整依赖清单,适合精确复现。我的习惯是两者结合:conda负责环境隔离,pip负责项目依赖记录,固定版本号必须写进requirements文件,这是第一位的。
2.2 实验追踪:用MLflow记录每一次尝试
调模型参数最有意思也最坑人。可能你某天随手调了一下learning rate,模型效果好得惊人,但三天后你想复现,死活想不起来当时改了什么。没有实验记录的AI工程,本质上是靠运气做项目,毫无工程尊严。
我强烈推荐把MLflow作为实验追踪的基础设施。它开源、可以自托管,不像有些平台数据存在云端,很多公司根本不敢把内部实验数据往外传。
实际用起来很简单,训练脚本里加几行:
import mlflow mlflow.set_experiment("text-classification") with mlflow.start_run(): # 记录参数 mlflow.log_param("learning_rate", 2e-5) mlflow.log_param("batch_size", 16) # 记录指标 mlflow.log_metric("val_acc", 0.91) mlflow.log_metric("val_f1", 0.87) # 记录模型文件 mlflow.log_artifact("outputs/best_model.pt")之后在浏览器打开MLflow的UI,就能看到所有历史实验的参数、指标、产物对比。哪个实验跑得好、用的什么配置、产物在哪,一目了然。我后来做项目,凡是超过三次调参,必上MLflow,已经成了肌肉记忆。
2.3 管道编排与数据版本:从Makefile到DVC
初学者最常犯的错,是把所有处理步骤全塞进一个巨大脚本里:加载数据、清洗、训练、评估、画图,一口气跑完。一旦中间某个环节报错,前面所有结果白算,而且你根本不知道这次用的是哪份数据、哪个中间产物。
我的做法是用Makefile把管道拆成独立阶段,每个阶段有明确输入和输出:
data: python steps/01_fetch_data.py train: data python steps/02_train.py evaluate: train python steps/03_evaluate.py serve: uvicorn app.main:app --host 0.0.0.0 --port 8000这样每次只跑需要的环节,而且通过Makefile的依赖关系,可以确保训练前数据一定是最新的。等你有更多定时任务、依赖调度需求时,再上Prefect或Luigi也不迟。Airflow我也用过,功能强大但架构较重,对个人项目或者小团队早期来说有点杀鸡用牛刀。
数据版本的痛点跟代码不同。代码有Git管着,数据一多一更新就很难说清。DVC就是来解决这个问题的,它把数据的版本信息记录在Git仓库里,但实际数据文件存放在本地、云存储或NAS。
dvc init dvc add data/raw git add data/raw.dvc之后每次数据更新,DVC都会记录一个校验值,配合Git提交,就能回到任何历史版本的数据。没有这套机制,你在实验记录里写“用8月版本数据”都是空谈,因为根本说不清8月版本是哪一份。
2.4 部署方案:Docker + FastAPI + ONNX 的黄金组合
模型上线我目前最顺手的方案,是Docker + FastAPI + ONNX Runtime。先说Docker,它解决了“在我机器上是好的”这个经典困境。一个标准的Dockerfile长这样:
FROM python:3.10-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY app/ ./app/ COPY models/ ./models/ EXPOSE 8000 CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]有几个细节需要注意:基础镜像要用确定版本,不要用latest,否则过一段时间镜像就可能不一样;编译依赖用完就清理,避免镜像越来越大;容器内尽量用非root用户运行,安全性和规范度都会好很多。
再说ONNX。PyTorch模型直接部署起来性能往往不理想,转成ONNX格式后用ONNX Runtime推理,加速效果非常明显,尤其在CPU部署场景下,实测大多数情况下能快一到三倍。
import torch model.eval() dummy_input = torch.randn(1, 128) torch.onnx.export( model, dummy_input, "model.onnx", input_names=["input_ids"], output_names=["logits"], dynamic_axes={"input_ids": {0: "batch_size"}}, opset_version=14 )FastAPI负责对外提供接口。它自带数据校验、自动生成接口文档,写起来直观,性能也不错。三件套组合起来,一个轻量、高效、可复现的模型服务就搭好了。
3. 核心环节落地:一个文本分类任务的完整工程实现
光讲工具和概念有点虚,我拿一个文本多分类任务做例子,带你走一遍从数据到上线需要的完整动作。
3.1 数据管道:一份能复现的数据集才算开始
数据是AI工程的七寸。数据一旦乱套,后面所有环节全部失守。我做数据管道时,目录结构会这样组织:
project/ ├── data/ │ ├── raw/ # 原始数据,只读 │ ├── processed/ # 清洗后的数据 │ └── splits/ # 训练/验证/测试划分 ├── steps/ │ ├── 01_fetch_data.py │ ├── 02_clean.py │ └── 03_split_data.py ├── outputs/ │ ├── checkpoints/ │ └── eval/ └── app/ └── main.pyraw目录里的文件我从来手动改,只由脚本写入。清洗过程要可重复执行,比如对文本做去重、去HTML标签、统一大小写等,每一步都输出日志。
划分数据集这件事,坑最多。如果是普通分类任务,很多人直接随机切分,但碰到有时间序列特征的数据就要小心了,比如电商评论按时间顺序变化明显,随机切分会让训练集和验证集“泄露”未来信息,评估结果虚高。我处理这类数据时,会按时间切片,确保训练集一定在时间上早于验证集。
每个数据集生成后,我还会做两个动作:计算一个校验哈希值,以及打印标签分布。标签分布不均是最隐蔽的问题,很多模型首轮训练就崩,或者准确率看似还行但小类全废,往往根源在数据划分上。
3.2 训练脚本里的工程化细节
训练环节看起来像写论文代码,实际写工程代码要朴素得多。我的经验是死磕几个点,完全可以提高复现概率:
- 固定所有随机种子,包括Python、NumPy、PyTorch的。
- 设置早停,防止验证集过拟合后还在硬跑。
- 保存checkpoint时,只保留验证集指标最好的两到三个,避免磁盘爆炸。
- 模型、优化器、学习率调度器的状态全都要存下来,缺一个断点续跑都会有问题。
- 训练日志里记录环境信息,包括Python版本、依赖版本、GPU型号、Conda环境名称。
训练脚本的骨架大致是这样:
import json import os import random import numpy as np import torch from sklearn.metrics import f1_score def set_seed(seed=42): random.seed(seed) np.random.seed(seed) torch.manual_seed(seed) if torch.cuda.is_available(): torch.cuda.manual_seed_all(seed) set_seed(42) config = { "learning_rate": 2e-5, "batch_size": 16, "epochs": 10 } with open("config.json", "w") as f: json.dump(config, f, indent=2)把config单独存成文件,是很多人忽略但非常关键的动作。模型压出来后,要是没有当时的完整参数,半年后你想调整它,只能靠猜。
3.3 模型评估与上线前的“隐蔽工作清单”
评估阶段,单看一个accuracy远远不够。文本分类我一般看三样东西:分类报告(precision/recall/F1的分类别结果)、混淆矩阵、错分样本抽样。只有这三样都对上了,模型才叫“评估通过”。
很多模型整体准确率85%,但中间某个业务关键类别只有30%,这样的模型直接上线是要出事故的。所以我在评估脚本里一定会输出一个按类别排序的错误率榜单,用最大力气的精力去盯最差的那几类。
真正上线前,还有一份隐蔽工作清单,每一项都会让人措手不及:
- 推理延迟:线上接口的P95延迟能否满足业务要求?
- 吞吐量:单实例能扛多少QPS?并发上来后显存和CPU会不会爆?
- 批处理:能否把多个请求拼成一个batch推理,提升GPU利用率?
- 预热:服务启动后第一次推理往往很慢,需要准备预热请求。
- 灰度与回滚:新模型出问题,能否快速切回旧版本?
这些问题不提前想好,上线前一晚必然还在熬夜。
4. 常见问题与排查技巧实录
AI工程有一半时间花在排查问题上。我平时遇到的问题至少可以归成四类,这里给出我的速查表和对应解法。
4.1 环境与依赖的高频问题排查
“昨天还能跑,今天跑不了”这类问题,90%出在环境漂移上。我总结了一份最常遇到的清单:
| 问题 | 排查思路 | 解决方案 |
|---|---|---|
| 本地能跑、Docker里报错ModuleNotFoundError | 检查下requirements.txt是否包含完整依赖 | 用pip freeze生成锁定版本文件 |
| 换了机器后精度对不上 | CUDA/PyTorch版本不一致导致的数值差异 | 锁定环境版本,尽量用Docker复现 |
| 不同Python版本行为不同 | 某些库在不同版本下结果不同 | 统一用conda环境,固定Python版本 |
环境问题的根本解法只有一个:把所有运行环境显式写下来,并用镜像固化。一旦确认环境一致,很多诡异问题会自动消失。
4.2 数据与训练过程典型故障速查表
训练过程中常见故障,我整理成了一张使用率很高的速查表:
| 故障现象 | 可能原因 | 解决动作 |
|---|---|---|
| Loss一直是NaN | 学习率过大、数据里有脏值、梯度爆炸 | 调小学习率、检查数据、做梯度裁剪 |
| 验证指标忽高忽低 | 数据划分不稳定 | 固定随机种子、检查标签分布 |
| 训练集loss极低但验证集差 | 过拟合 | 加正则化、早停、增强数据 |
| 训练特别慢 | 数据加载成瓶颈 | 用DataLoader的num_workers、预取数据 |
| 首次推理特别慢 | 没有预热 | 启动时设置好预加载模型、做预热请求 |
这些坑我基本全踩过。最冤的一次是训练特别慢,排查了半天发现数据存在机械硬盘上,读取速度成了瓶颈,换成SSD后训练时间直接省了一半。很多所谓“训练算法问题”,底层其实是IO问题。
4.3 上线之后的监控、告警与模型漂移
模型上线只代表考验刚刚开始。线上环境的数据分布和训练时不可能完全一致,时间久了模型效果一定会下降,这叫模型漂移。最典型的是商品推荐模型,用户消费习惯一变,旧模型很快就会跟不上。
我的基本监控组合有三层:
- 系统层:CPU、内存、GPU、磁盘,出问题先从这里查。
- 服务层:接口延迟、成功率、QPS,用Prometheus + Grafana这类工具就能搞定。
- 业务层:预测类别分布、平均置信度、输入文本的向量分布。
业务层最容易被忽略。模型输出的类别分布发生明显偏移时,不一定意味着代码坏了,更可能是线上数据分布变了。我还习惯给预测结果留一份日志,定期抽样做人工检查,这样能在用户投诉前发现问题。
告警不是越多越好,告警多了等于没告警。我只设置三个核心告警:接口成功率低于阈值、P95延迟超过阈值、预测类别分布与历史基线偏差过大。每个告警都要配明确处理预案,不然告警只会让人麻木。
4.4 新手最容易犯的五个工程错误
带过一些人之后发现,有一些错误是普遍会犯的,我总结为五个:
- 不做实验记录,调参靠记忆。我们前面说过,这是让项目变玄学的头号原因。
- 不写自动化测试。模型推理函数的输入输出格式、数据校验逻辑,这些都应该有测试,不然改坏别人根本不知道。
- 一份脚本跑到底。所有步骤混在大脚本里,出了问题很难定位。
- 上线前不准备回滚方案。新模型效果不好不是意外,而是常态,没有回滚方案等于拿生产环境做实验。
- 不关注数据分布变化。模型上线后就不管了,直到业务方反馈效果变差才手忙脚乱排查。
这五个错误,我自己前两三个都犯过。后来养成一个习惯:每次改完代码,先跑一遍最小的测试用例;每次上线模型前,写清回滚步骤。成本很低,收益极高。
5. 从零到落地:8周完成一个最小闭环项目的行动路线
讲了这么多,很多人容易越听越觉得无从下手。当初我也有这个阶段,后来发现AI工程这东西要想真正建立起来,必须靠一个完整的项目把它跑通。项目不必多大,但要完整。
5.1 项目选题与验收标准
建议选一个公开数据集上的文本多分类或图像多分类任务,例如新闻分类、情感分类这类。选这类题目的原因有三个:数据容易获取、领域知识门槛低、便于和别人的基线对比。
你的目标不是刷到多高的精度,而是交付一个能对外提供服务的完整系统。我给出的验收标准很具体:
- 训练过程可以通过一条命令完整复现。
- 每次实验的参数、指标、模型产物都能在MLflow里查到。
- 模型通过Docker部署到本地或云服务器,提供一个HTTP接口。
- 接口返回结果符合预期,并且有基础监控日志。
- 线上数据和训练数据分布出现偏差时,系统能产生告警。
达到这五条,你对AI工程就有了一个完整的、属于自己的骨架。
5.2 分阶段执行计划与关键产出
我把整个流程拆成八周,每两周一个阶段:
| 周期 | 核心工作 | 关键产出 |
|---|---|---|
| 第1-2周 | 环境搭建、数据下载与清洗、数据划分 | 稳定的conda环境、可复现的数据管道脚本 |
| 第3-4周 | 训练基线模型、接入MLflow | 复现指标、完整实验记录 |
| 第5周 | 模型导出与容器化 | ONNX模型文件、Docker镜像 |
| 第6周 | 用FastAPI封装推理接口 | 可调用的HTTP API、自动化测试 |
| 第7周 | 部署上线与监控接入 | 线上服务、日志与告警 |
| 第8周 | 模拟一次数据漂移,验证告警与回滚 | 完整的故障演练记录 |
这个节奏比较适合每天能抽出一两个小时的人。要是全职投入,可以压缩到四周左右。
5.3 给同样从零开始的人几句实在话
我从零到真正上手AI工程,耗时比想象中长很多,但中途最大的收获并非某个工具用得多熟练,而是建立了一套“把模型当作系统的一部分”的思维方式。模型再强,也只是系统里的一个组件,前后左右的数据流、监控、运维、迭代机制才是决定它价值的东西。
如果你正准备走这条路,我的建议特别简单:先把一个项目完整走完,再谈优化。做一个“丑但完整”的系统,比做一个“好看但残缺”的模型有价值得多。过程中你会发现,真正让你变强的不是某个框架的API,而是你亲手解决的那些工程问题——它们才是你以后面对复杂生产环境时的底气和直觉。