1. 项目概述与学习起点
1.1 为什么“从零开始”往往是最难的操作
如果你搜过“ai-engineering”,大概率会看到两类内容:一类是铺天盖地的课程广告,从“七天入门AI”到“三个月拿下大厂Offer”;另一类是各种知识星球、付费社群里的大神贴,动辄甩出几十个链接,让你自己看论文、刷LeetCode、啃源码。
我在这个行业里待了十一年,做过算法岗也做过平台架构,带过应届生也带过社招转行的新人。说实话,最让我担心的人才缺口,不是那些“算法题能刷到周赛前排”的人,而是能在真实业务环境里,把一个AI系统从零开始搭起来、跑起来、并且稳定运行下去的人。
如果你是我,你会怎么教一个新人?这个问题我认真想过。后来我总结出来的答案是:不要从“深度学习”开始,不要从“Transformer”开始,甚至不要从“调库调包”开始。真正有效的ai-engineering起点,是先想明白一个朴素但致命的问题:在真实世界里,一条AI落地路径的前后左右,到底站着哪些角色、哪些技术、哪些流程、哪些坑。
这也是我写这个项目的初衷。“ai-engineering-from-scratch”不是一个炫耀术语的标题,而是我这一年多来沉淀下来的一套完整学习路径。它面向的不是天才,而是那些愿意一步一步把地基打牢、愿意在调试和排错中真正理解系统全局的普通人。不管你是刚入门的技术新人,还是已经被业务催着上模型的老后端,我想这篇文章都可能帮你在“AI工程到底是什么”这件事上,建立一条更清晰的轴距。
1.2 这个项目解决什么问题
如果你身边有做过AI项目的朋友,你可以问问他:项目里最耗时间的是哪个环节?答案大概率不是“写模型代码”,而是“去搞清楚为什么代码在训练时和上线后行为不一致”,或者是“数据管道又在半夜挂掉了”。
这条经验背后的原因很有意思。传统软件工程面对的是确定的输入输出,你可以用单元测试把逻辑钉死;但AI系统面对的是统计意义上的“不确定”,数据分布一偏移,模型精度就会无声下跌,而系统本身并不会报错。这就是AI工程和普通后端的本质区别:你要维护的不是一段代码,而是一整套“数据 + 模型 + 运行环境”的耦合体。
从零开始的AI工程,核心目标就是让你具备驾驭这套耦合体的能力。具体拆开来看,需要覆盖四块核心内容:
- 数据工程的底座能力:知道拿到的原始数据如何清洗、如何标注、如何做版本管理,明白训练数据和上线后的数据为什么不能有太大差异
- 模型训练与评估的工程化:不再用Notebook里那套“跑通了就行”的野路子,而是搭出可复现、可追踪、可回滚的训练流程
- 部署与交付链路:搞懂在线推理、离线批处理、A/B实验,以及模型上线后到底怎么监控它的健康状态
- 持续迭代与维护的手段:模型会老化、数据会漂移,没有反馈闭环的AI系统撑不过三个月
我要特别说明的是,这套体系不只属于大厂。一个人数不到三十的小团队、一个做垂直行业SaaS的公司,同样能用这套方法论把自己的AI能力建立起来。这也是我在“ai-engineering-from-scratch”这个项目里反复强调的一个立场:小团队更需要工程化,因为小团队没有人力去反复救火。
2. 核心技术与工具选型解析
2.1 AI工程的基本技术栈分层
很多刚入行的朋友喜欢一上来就看榜单,哪个模型分高就玩哪个,哪个框架热就学哪个。这种学习方式不能说完全错,但效率确实很低。因为它忽略了工程落地里最关键的“分层结构”思维。
我习惯把AI工程的技术栈切成四层:
第一层是基础组件层,包括Python、SQL、Linux操作、Docker基本功。这一层相当于盖楼用的砖和水泥,不牢固的话,后面每一层都会抖。有个细节可能很多人没意识到:熟练的SQL能力在AI项目里比“会写PyTorch”更重要,因为绝大多数AI项目的时间其实花在数据取用和预处理上。
第二层是数据与特征层,包括数据管道(ETL)、数据质量检测、特征工程、数据版本管理。这一层在“学习路线图”里通常被跳过去,但我在项目实践中发现,它对最终模型效果的影响能占到六成以上。数据里藏着脏数据、重复样本、标签噪声,每一个都会让模型表现“看起来还行,实际线上崩”。
第三层是模型训练与调优层,包括模型架构选择、超参数调优、训练追踪、实验管理。注意我这里把“训练追踪”和“实验管理”放得和模型算法本身同等重要,因为在团队协作的场景里,连“哪个实验对应哪份数据和哪份参数”都说不清楚的项目,复盘时就是一笔糊涂账。
第四层是生产交付层,包括模型部署、在线推理服务、性能监控、模型回退、CI/CD流水线。这一层是“AI工程”区别于“AI研究”的分水岭。很多学算法出身的人卡在这一层,不是因为难,而是因为压根没有这个概念框架。
理解了这个分层,你再回来看市面上各种课程和框架,就不会晕头转向了。选工具之前先定位自己的需求落在哪一层,再去比较同一层里的具体方案,思路会清晰很多。
2.2 工具选择的三个关键视角
工具选型是个常聊常新的话题。今天前端框架都换了好几代了,AI领域的工具迭代更是快。但万变不离其宗,不论工具怎么出新的,你在选型时只要盯住三个视角就不会跑偏。
第一个视角是数据流的可追溯性。你的数据从原始文件变成训练样本,中间经历了哪些SQL运算、哪些清洗脚本、哪些特征编码?如果这些逻辑散落在各种临时脚本里,没有统一的编排和记录,后期排查问题会让你生不如死。所以我建议工具选型的第一优先级先看“谁在管理数据血缘”,而不是“谁训练模型更快”。
第二个视角是实验的可复现性。你周一跑出来的那个85%准确率的模型,到了周五还能原样复现吗?版本、种子、数据切片、依赖库版本,只要漏了任何一个,复现就会失败。这个维度上,MLflow这类实验追踪工具几乎成了标配,后面我会专门讲。
第三个视角是运行环境的可移植性。本地能跑通不代表服务器能跑通,CPU跑得通的模型不一定能在GPU上复现相同逻辑。Docker和Kubernetes这类容器化方案,就是把环境差异的锅从根上扔掉。哪怕是个人项目,我也建议至少用Docker把环境锁死,避免“我机器上明明能跑啊”这种经典对话反复发生。
工具选型本质上不是技术洁癖,而是风险控制。这三个视角对应了工程里最常翻车的三个风险点:数据不可追溯、实验不可复现、环境不可迁移。把它们控制住了,你的AI系统就好比在安全屋里干活。
2.3 推荐的技术栈组合(个人亲测)
我知道光说原则还不够,直接给一套我用了很久、在多个项目里验证过的组合会更实在。这套组合覆盖上面说的四层,而且尽量选了社区活跃、资料多、上手成本低的方案。
| 层级 | 推荐工具 | 选型理由 |
|---|---|---|
| 容器与运行环境 | Docker + Docker Compose | 环境一致性、本地/云端迁移无痛 |
| 数据管道 | Airflow(轻量用Prefect) | 可视化调度、重试机制、任务依赖直观 |
| 数据版本管理 | DVC | 对Git友好、学习曲线平缓、支持大文件存储 |
| 实验追踪 | MLflow | 追踪实验参数/指标/产物,自带模型注册中心 |
| 特征工程(可选) | Feast | 统一线上/线下特征口径,避免训练/推理不一致 |
| 模型训练 | PyTorch / Scikit-learn | PyTorch灵活,适合研究到生产;Sklearn适合快速基线 |
| 部署与服务 | FastAPI + ONNX Runtime / Triton | FastAPI轻量,ONNX加速跨平台推理 |
| 监控与告警 | Prometheus + Grafana | 云原生可观测性的事实标准,配合自定义指标 |
这套组合的实用之处在于:既有“重”组件(Airflow、MLflow),也有“轻”方案(FastAPI、DVC),能根据团队体量和项目阶段灵活裁剪。对个人学习和极小型项目,你完全可以砍掉Airflow,用Prefect甚至纯Cron加Shell脚本先跑起来。工具是辅助,思路才是主菜。
3. 从零到一:可复制的落地实操流程
3.1 第一步:搭好开发环境和项目骨架
好,接下来进入硬核实操环节。我以“从零开始搭建一个可落地的AI工程项目”为例,演示完整流程。这个项目我选择的是一个经典场景:商品评论情感分类。原因很简单:任务本身不复杂,数据获取容易(公开数据集一大堆),但又能完整覆盖数据处理、模型训练、推理部署、监控反馈的所有环节,非常适合拿来讲透AI工程链条。
项目目录我建议这样组织:
. ├── configs/ # 配置文件,含模型参数、路径、训练超参 ├── data/ │ ├── raw/ # 原始数据(不可变) │ ├── processed/ # 清洗后数据 │ └── versions/ # DVC数据版本目录 ├── src/ │ ├── data_processing/ # 清洗、转换、特征工程 │ ├── training/ # 模型训练、评估、实验记录 │ ├── deployment/ # 推理服务、API接口 │ └── monitoring/ # 指标采集、告警逻辑 ├── models/ # 模型产物存储 ├── tests/ # 单元测试与数据校验测试 ├── docker-compose.yml ├── .env.example └── requirements.txt这个目录结构不算复杂,但它严格区分了“数据”“代码”“模型”“配置”四个核心对象,这是AI工程与普通Web工程最容易忽视的差异。你写Web接口时,配置和数据通常没那么要紧;但AI模型产出的本质是“参数 + 结构 + 代码环境”的绑定体,缺一个都跑不起来。
创建虚拟环境和基础依赖:
python -m venv .venv source .venv/bin/activate pip install mlflow dvc pandas numpy scikit-learn fastapi uvicorn pydantic这里我先把核心依赖装上。DVC用于数据版本控制,MLflow做实验追踪,FastAPI提供推理服务接口。这些工具在后续流程中各有明确用途,装完不是摆设。
3.2 第二步:数据获取与版本化
我先准备一份公开的IMDb影评数据集(或者任何你自己标注过的分好类的文本数据),按照data/raw/目录放好。这一步看似简单,但有个重要的工程决策:从原始数据进入项目的那一刻,就要做版本管理。
运行DVC初始化:
dvc init dvc add data/raw/imdb_reviews.csv git add data/raw/imdb_reviews.csv.dvc .dvc/config git commit -m "add raw imdb reviews dataset"这里有个容易被新手忽略的细节:dvc add不会把真实数据文件提交到Git,它只是生成一个指针文件。数据本身可能存到本地磁盘、S3或者MinIO等远程存储里,Git仓库记录的是“这份数据文件的元信息和哈希指纹”。这种设计的好处是,Git仓库永远保持轻量,且数据内容严格不可变、可溯源——哪天你想回头复现“上周一用90%训练数据跑出的模型”,一条dvc checkout命令就能把当时那份数据完整拉回来。
数据清洗阶段,我用Pandas做基础处理,包括去重、去空值、标签统一、文本长度过滤。这一步除了写代码,我还建议增加一条“数据校验”流程,比如断言“不存在空标签”“样本数不少于X”“正负样本比例不超出阈值”。这也是我从线上事故里学到的教训:数据管道静默地跑,常常会把脏数据无声带进训练集,等你发现时模型已经学歪了。
# src/data_processing/validate.py import pandas as pd def validate_data(df: pd.DataFrame) -> bool: assert len(df) > 10000, "样本量太少,不具备训练意义" assert df["label"].isin([0, 1]).all(), "标签取值异常" assert df["text"].map(len).min() > 10, "存在过短无效文本" return True df = pd.read_csv("data/processed/train.csv") if not validate_data(df): raise ValueError("数据校验未通过,阻止进入训练")这段校验代码虽然简单,但能挡掉不少低级错误。我见过太多团队在拼命调模型参数,结果发现训练数据里有大段空字符串、标签错位的问题——浪费几天时间,还不自知。
3.3 第三步:训练代码结构设计与实验追踪
接下来是最核心的模型训练工程化环节。为了演示,我选择用逻辑回归加TF-IDF特征作为基线模型,之后可以无缝切换到BERT这类深度学习模型。用逻辑回归做开局不是因为技术上老旧,而是因为工程链路可以完整跑通,且速度极快,方便验证“管道是否畅通”。先把烂流程跑通,再谈好模型,这是我一贯的原则。
训练脚本的入口设计:
# src/training/train.py import mlflow import pandas as pd from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.linear_model import LogisticRegression from sklearn.pipeline import Pipeline from sklearn.metrics import accuracy_score, f1_score mlflow.set_tracking_uri("http://localhost:5000") mlflow.set_experiment("sentiment_analysis") with mlflow.start_run(run_name="lr_tfidf_baseline"): train_df = pd.read_csv("data/processed/train.csv") X_train = train_df["text"] y_train = train_df["label"] vec = TfidfVectorizer(max_features=5000, ngram_range=(1, 2)) clf = LogisticRegression(C=1.0, max_iter=1000) pipeline = Pipeline([("vec", vec), ("clf", clf)]) pipeline.fit(X_train, y_train) val_df = pd.read_csv("data/processed/val.csv") X_val = val_df["text"] y_val = val_df["label"] y_pred = pipeline.predict(X_val) acc = accuracy_score(y_val, y_pred) f1 = f1_score(y_val, y_pred) mlflow.log_params({"max_features": 5000, "ngram_range": "1,2", "C": 1.0}) mlflow.log_metrics({"accuracy": acc, "f1_score": f1}) mlflow.sklearn.log_model(pipeline, artifact_path="model", registered_model_name="SentimentLR")你会发现这个训练脚本里面几乎没有任何“特殊”的算法操作,无非是常规的数据读取、模型拟合和指标计算。但正因为多了MLflow的追踪封装,每一次运行都会留下:参数是什么、指标是什么、模型产物在哪个位置、用了哪份数据。当实验次数积累到几十上百次之后,这个记录就是你唯一的对比依据。没有追踪的训练,都是没有记忆的漂流瓶。
超参数优化阶段,我建议用Sklearn的GridSearchCV结合MLflow的嵌套记录,把每次组合对应的结果都存下来,而不是只记最优结果。原因在于,最优值可能受数据影响而偶然偏高,你周围的环境一变(数据增加、业务口径调整),未必还是最优。把所有实验过程记录下来,相当于给自己留了一条可回溯的决策路径,下次调整时有据可依。
3.4 第四步:模型部署与推理服务
模型训练完成只是第一步。接下来的部署环节,才是我认为最能区分“会AI”和“会AI工程”的分水岭。
我用FastAPI实现了一个轻量级在线推理服务。它接收一段文本,返回情感分类标签和置信度。注意在服务内部,文本特征化和模型预测必须走同一套预处理逻辑,这个一致性是部署环节最大的隐形陷阱。为了确保线上和训练时的特征处理口径一致,最稳妥的办法是把特征器放进同一个Pipeline里整体保存,不要分开保存再在服务端重建。
# src/deployment/app.py from fastapi import FastAPI from pydantic import BaseModel import mlflow import numpy as np app = FastAPI() model = mlflow.sklearn.load_model("models:/SentimentLR/latest") class TextInput(BaseModel): text: str class Prediction(BaseModel): label: int confidence: float @app.post("/predict", response_model=Prediction) def predict(input: TextInput): prob = model.predict_proba([input.text])[0] label = int(np.argmax(prob)) confidence = float(np.max(prob)) return Prediction(label=label, confidence=confidence)写完代码后,用Docker把服务封装:
FROM python:3.10-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY src/ ./src/ CMD ["uvicorn", "src.deployment.app:app", "--host", "0.0.0.0", "--port", "8000"]然后用Docker Compose把服务、MLflow追踪服务、PostgreSQL(存MLflow元数据)一起编排起来:
version: "3.8" services: mlflow: image: ghcr.io/mlflow/mlflow:v2.3.0 ports: - "5000:5000" command: mlflow server --backend-store-uri postgresql://mlflow:mlflow@db/mlflow --default-artifact-root ./mlruns db: image: postgres:14 environment: POSTGRES_USER: mlflow POSTGRES_PASSWORD: mlflow POSTGRES_DB: mlflow api: build: . ports: - "8000:8000" depends_on: - mlflow容器化部署的核心价值在于:把模型的运行环境固化成镜像,换机器、加副本、弹性伸缩时都不会因为环境差异而行为失常。这一步做得好,后续的监控、A/B测试、蓝绿发布都有了可靠的底座。
3.5 第五步:监控、告警与持续迭代闭环
模型上线后,还有一件很多人没有意识到的事:模型上线的那一天,才是工作真正开始的那一天。传统软件只要逻辑不变就不会“变旧”,但模型的性能会随时间推移而衰减,因为数据分布会漂移。这就是AI工程特有的“模型老化”问题。
我建议至少监控三类指标:
- 业务指标:比如准确率、F1、用户满意度,这是最终要看的
- 输入分布指标:比如文本长度均值、词表覆盖、类别概率分布,这类指标能提前暴露“数据漂移”
- 基础设施指标:如推理延迟、P95响应时间、CPU/内存占用、错误率
# src/monitoring/metrics.py from prometheus_client import Counter, Histogram, Gauge PREDICTION_COUNTER = Counter("predictions_total", "Total prediction requests") PREDICTION_LATENCY = Histogram("prediction_latency_seconds", "Prediction latency") INPUT_TEXT_LENGTH = Gauge("input_text_length", "Length of input text") # 修改app代码,在predict函数中记录指标Prometheus配上Grafana,就是一个非常成熟的可观测性组合。更进一步的,你还可以接入“模型性能看板”,把数据漂移检测指标(如PSI,群体稳定性指数)画成趋势线,当PSI超过告警阈值时自动触发重新训练提醒。这样你就不用来回问“模型现在还行不行了”,系统会自动告诉你“训练分布开始偏移了,该考虑补充数据了”。
4. 常见问题与避坑指南
4.1 新手最常见的五个工程问题
我总结这些年在带人过程中最常见的问题,做个速查表。很多问题看起来截然不同,根子上都是同一个毛病:没有把AI项目当系统工程来做。
| 问题现象 | 根因 | 解决方案 |
|---|---|---|
| 模型在验证集精度很高,上线后效果断崖下跌 | 训练/推理数据分布不一致;特征处理口径分裂 | 统一Pipeline保存;上线前做数据分布对比 |
| “换个随机种子结果就不一样” | 训练过程未锁定随机性,数据shuffle顺序不一致 | 固定随机种子;对数据顺序做确定性控制 |
| 服务器上复现不了本地结果 | 依赖库版本不一致;硬件差异导致浮点数行为不同 | 使用Docker锁定环境;尽量统一GPU型号 |
| 实验跑了一堆,复盘时不知道哪个对应哪个 | 缺乏实验追踪,参数和结果散落各处 | 立刻接入MLflow或同类工具,形成规范习惯 |
| 数据管道半夜挂了,第二天才发现 | 缺少自动化告警和数据质量监控 | 用Airflow/Prefect做任务编排,配置失败重试与告警 |
这里有两条建议可以说价值千金。第一条是:从第一个实验开始,就不要跑“裸代码”,哪怕只是手动把参数和指标存到一个文本文件也好,先养成记录习惯。第二条是:不要在Notebook里完成“最后一步的训练”,Notebook适合分析和探索,不适合作为训练产物的正式源头,因为它的执行顺序不可控,很容易产生“上一个细胞跑过才有变量、新环境跑不了”这种问题。
4.2 团队协作中的AI工程规范
如果你不是孤军奋战,而是有三五个人一起做项目,那AI工程规范会直接决定协作效率。
我强烈建议用Git分支管理实验代码,DVC管理数据和模型版本,二者组合正好形成一个完整闭环。常规做法是:
- 每个实验或者每个功能开一个分支;
- 分支上改代码或调整参数,用Git提交代码快照;
- 用DVC记录本次实验对应的数据快照和模型产物快照;
- 合并代码前,必须确保训练脚本能在干净环境里跑通,并且产出的模型能加载进推理服务。
这套组合拳打通后,就等于给团队了一套完整的“时空穿梭机”:任何一个实验,都可以精确回溯到当时的代码、当时的参数、当时的数据和当时的模型。我带过的团队里,凡是坚持这么做的,基本没有因为“搞不清上次怎么跑出这个数”而争吵过。
另外一个细节是环境依赖管理。不要用pip freeze > requirements.txt这种偷懒方式,它会把我们这台机器的所有包全部导出,连无关的包都会进去。建议用pip-tools或者poetry这类工具,生成精确但有层次的依赖文件,至少区分requirements/base.txt和requirements/dev.txt,避免测试环境装出一堆用不上的包。这个习惯在长期维护的项目里,能省下无数个“为什么我环境装不上”的下午。
4.3 我的复盘与一些小建议
踩了这么多年的坑,我越来越觉得,做AI工程最需要的不是聪明,而是耐心和系统性。
有一件事我反复和团队强调:先把“脏活累活”(数据管道、实验追踪、监控、部署链路)建设好,再谈模型技巧。很多新手一进来就想搞最潮的模型架构,这是方向性的错误。在工程链路不健全的项目里,哪怕你把模型精度从80%调到82%,模型上线后的表现也只会是玄学——恶化的可能比变好还大。反之,链路健全之后,哪怕你的模型只是最简单的逻辑回归,它可以通过稳定迭代一点一点优化,最终也能达到可观的效果。
我还特别喜欢用“盖房子”来做类比。模型结构是装修风格,工程体系是钢筋水泥。你会因为装修风格好看就忽略承重墙吗?在AI项目里,很多人恰恰就是这么干的。他们全身心扑在“用哪个SOTA模型”,却忽视了数据漂移检测、实验可追溯、A/B测试这些真正的承重墙。等到业务波动、数据漂移、模型崩溃的时候,再回头补工程,代价至少翻三倍。
从我个人的实操体会来说,坚持把工程基础做扎实,带来的收益是越滚越大的。最开始你会觉得麻烦,为什么要记录实验参数?为什么要做数据校验?为什么要写Dockerfile?但当你半年后需要重构一个旧模型、回答老板“为什么线上越来越不准”、或者把一个同事用过的东西交接给另一个同事时,你会发现,所有当初嫌麻烦的步骤,其实都是在为未来的自己省钱。这条路我已经反复验证过很多次,也建议你们在下一个AI项目里,真的试试。