看到“ai-engineering-from-scratch”这个标题,我第一反应是:这又是一个典型的GitHub仓库名,但这个名字确实值得好好展开说说。过去一年多,我陆陆续续帮不少朋友、同事梳理过“从零开始做AI工程”这件事,自己也亲手把一个又一个模型从Notebook里捞出来,塞进线上服务,踩了数不清的坑。今天想把这些经验和盘托出,聊聊一个完全零基础的人,或者一个被各种概念绕晕的人,到底应该怎么理解、怎么起步、怎么一步步把“AI工程”这四个字落到自己手里。
这篇文章不会带你去看某个框架的文档,也不会把Transformer的结构从头推导一遍。它的核心目标只有一个:帮你建立一条从零到一、从一到能上线的完整路线,把“AI工程”当成一门手艺,而不是一堆术语的堆砌。无论你是想转行做AI方向的研发,还是已经在做后端、数据、算法,想补上工程这一课,下面这些内容都是值得花时间反复看的。
1. AI工程不是调模型:先搞清楚这条路的边界
很多人一听到“AI工程”,第一个想到的就是调模型、调参数、发论文。这其实是一个挺大的误解。AI工程和算法研究是两码事,边界也不在“模型怎么设计”,而在“模型怎么落地、怎么稳定地服务于业务”。说得直白一点:算法工程师的目标是找到一个更准的方案,AI工程师的目标是让这个方案在真实环境里每天24小时不出岔子。
举个例子。算法同学给你一个文本分类模型,离线测试AUC很高,他交给你就完事了。接下来你要面对的是什么?数据长什么样子、接口怎么设计、并发打上来会不会崩、遇到脏数据会不会报错、线上效果跌了怎么发现、模型怎么更新、回滚怎么处理。这些才是AI工程真正要解决的。
我还见过不少后端工程师误以为AI工程就是把模型加载进来,写个REST API就行。真上手就会发现问题远不止这些:推理卡的显存管理、批处理策略、特征计算延迟、模型文件版本和代码版本的一致性、AB实验的流量切分……每一项都是一整块工程知识。
所以我的建议是,第一步不是急着学哪个框架,而是先把AI工程在整条链路中的位置看清楚。它大致覆盖这些环节:
- 数据采集与清洗:原始数据怎么变成可训练、可校验的数据
- 特征与样本处理:训练样本的构造、标签的定义、类别的均衡
- 模型训练与评估:跑通Baseline,迭代优化,并且用可量化的指标证明它有效
- 模型服务化:把模型封装成接口,处理请求、并发、容错
- 监控与运维:上线之后的效果追踪、数据漂移检测、版本管理、模型更新
这五块合在一起,才是一个AI工程师每天真正在做的事情。这也是我把“AI工程”比作一条流水线的原因:模型只是流水在线中间的一个环节,流水线的进出口、质检、维修,才是工程的重头戏。
想清楚了边界,接下来就该聊能力模型。如果把自己当成一个要在这条流水线上干活的人,我通常会建议把知识拆成下面五个层面,逐一补齐:
第一层:工具与语言。不是把所有语言都学一遍,而是把Python、Shell、SQL这三样用熟。Python负责模型与数据处理,Shell负责服务器操作和自动化,SQL负责取数。这三样是地基中的地基。
第二层:数据工程基础。学会用Pandas或Polars这类工具做数据清洗,理解特征工程的常见方法,知道怎么验证数据质量。算法可以调包,数据这关过不去,后面全都是空中楼阁。
第三层:模型与算法基础。不需要从零实现Transformer,但得知道常见的模型能干什么、怎么用。传统机器学习的线性模型、树模型、集成模型,深度学习的CNN、RNN、Transformer结构,再到今天大模型时代的Prompt工程和RAG,这些都属于“会用、会选、会调”的范畴。
第四层:软件工程与部署。Git、依赖管理、单元测试、接口框架、容器化、CI/CD。这层是AI工程和纯算法工作分道扬镳的地方,也是绝大多数自学的人最薄弱的环节。
第五层:系统设计与运维。模型服务的架构设计、性能优化、监控告警、成本控制。到这一层,才真正称得上一个合格的AI工程师而不是一个“会训练模型的脚本小子”。
把这五层摆在眼前,你会发现“from scratch”这件事的本质其实是分层清扫,而不是一个知识点一个知识点地狂啃。
2. 从零起步的第一块地基:语言、环境与工具链选型
聊完能力模型,接下来落地到具体选型。有些朋友一上来就问“我应该学PyTorch还是学TensorFlow”,这个问题在零基础阶段毫无意义。真正该先解决的问题是:你的开发环境能不能撑起后面一整条流水线的工作。
Python是绕不开的,这个是共识。但比Python本身更容易被忽略的是两样东西:Shell和SQL。Shell解决的是“我能不能在一台陌生的Linux服务器上存活”,SQL解决的是“我能不能从公司的数仓里拿到训练数据”。我见过太多人Python写得很溜,一上服务器就卡在环境变量、文件权限、查日志这些基础命令上,效率低到令人崩溃。
开发环境方面,我自己的偏好很明确:不用全局Python环境,一律用虚拟环境隔离项目依赖。以前我用conda,最近这一年我在新项目里几乎都切到了uv。uv确实快得离谱,一条命令就能创建虚拟环境并装好依赖,对新手来说也比conda那套清晰得多。举个最简单的操作流程:
# 安装uv curl -LsSf https://astral.sh/uv/install.sh | sh # 初始化一个新项目环境 uv init my_ai_project cd my_ai_project # 添加核心依赖 uv add pandas scikit-learn fastapi uvicorn # 激活虚拟环境 source .venv/bin/activate这样一套下来,你的项目就是完全隔离的,不会出现“今天装了个包把昨天能跑的程序搞坏了”这种烂事。依赖地狱这件事,每个做过Python后端的人都懂,在AI项目里出现得更加频繁,因为数据科学相关的库依赖链又长又脆。
然后是IDE的问题。我推荐直接用VS Code,配合Remote-SSH插件和Jupyter Notebook插件。如果你需要交互式地探索数据、画图、看分布,就在Notebook里做;一旦要写正式的模块和接口,立刻切到.py脚本。关键是别让Notebook变成你唯一的工作环境。Notebook适合做实验记录,但它不适合做工程,因为版本管理差、运行顺序容易乱、测试更是无从谈起。
Git的使用也要从一开始就养成习惯。AI项目和普通软件项目有个很大的不同:除了代码,你往往还要管理配置、数据描述、模型文件。我的建议是,代码和配置文件必须进Git,模型文件和原始数据用Git LFS或者专门的对象存储(比如S3)来管理。你至少要养成一个习惯:每次改动有commit,每次commit有明确的message。等将来你需要回退到某个历史版本、复现某个实验结果的时候,你会感谢当初的自己。
还有一个容易忽略但极其重要的基础设施:虚拟环境之外,还要学会用Docker。Docker解决的是“我这套环境到别的地方还能不能跑”的问题。AI模型涉及一堆系统依赖、CUDA版本、Python包,少了任何一个都会原地爆炸。我通常在项目早期就写一份Dockerfile,哪怕是个很简陋的版本,也要让整套东西能在一台干净的机器上自动跑起来。这个习惯会让后面的部署环节省掉极大的痛苦。
3. 打通第一条端到端链路:一个最小可落地的分类项目
地基打得差不多了,就该真正动手做项目。这里我强烈建议:不要一上来就做大模型应用,也不要一上来就挑战图像生成这类重算力的任务。从零开始做AI工程,最适合的第一个项目是一个带有明确业务意义的文本分类任务。
举一个具体的例子:你可以做一个“工单自动分类”系统。输入是一句用户反馈,比如“我的账号登录不了了”“请问退款什么时候到账”,输出是类别标签,比如“账号问题”“退款问题”“产品咨询”。这个任务有几个非常适合新手的特性:
- 数据容易获取,你自己就可以编规则生成一批有标签的样本
- 效果可以量化,分类的准确率、精确率、召回率都是直观的数字
- 不需要烧显卡,CPU就能跑
- 敏感信息少,不像金融、医疗数据那样有复杂的合规问题
选定任务之后,你就要逼自己走完一整条链路:数据清洗、模型训练、接口封装、部署上线。这个过程里学到的东西,比看一百篇各类文章的干货加起来都有用。
3.1 数据环节:清洗比建模更花时间
很多初学者把精力都放在“选哪个模型”上,这其实顺序反了。真实的AI项目里,数据清洗和特征处理占掉的时间往往是最多的。以工单分类为例,你拿到的原始数据大概率是这样的:
- 大量重复的文本,同一个用户提交了多次
- 错别字和口语化表达满天飞,比如“登录不上”“登陆不了”“一直卡在登录页”
- 类别严重不平衡,90%都是咨询类,只有5%是投诉类
- 空值和异常值,有的工单根本没有正文
我当时用Pandas做清洗,大概长这样:
import pandas as pd import re df = pd.read_csv("tickets_raw.csv") # 去掉空文本 df = df.dropna(subset=["text"]) df = df[df["text"].str.strip() != ""] # 去掉重复工单 df = df.drop_duplicates(subset=["text"], keep="first") # 简单的文本归一化 def normalize(text): text = text.lower() text = re.sub(r"\s+", " ", text) return text.strip() df["clean_text"] = df["text"].map(normalize) # 查看类别分布 print(df["category"].value_counts())别看这段代码简单,它背后藏着一个关键原则:模型的上限由数据决定,你能做的只是逼近这个上限。如果你的数据里充满了重复、缺失、标签错误,再好的模型也救不回来。我在实际项目里遇到过一个情况,线上效果比离线测试差了15个百分点,最后排查了半天,发现罪魁祸首是训练数据的标签在人工标注时搞错了一批。
数据清洗完,还有一件很重要的事:数据版本管理。我通常会把清洗后的训练集、验证集、测试集单独存好,并且记录它的生成时间、包含多少条样本、标签分布长什么样。这样当你在迭代模型时,才能确信效果变好是因为模型变强了,而不是因为偷偷塞进去了更多数据。
3.2 模型环节:从Baseline起步,而不是一上来就追最新SOTA
模型部分,我的建议是先做一个非常简单的Baseline。以文本分类为例,最简单靠谱的方案就是TF-IDF加一个逻辑回归。这套组合在大量文本分类场景里都能达到不错的水平,而且训练只要几秒钟。
from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.linear_model import LogisticRegression from sklearn.pipeline import Pipeline model = Pipeline([ ("tfidf", TfidfVectorizer(max_features=20000, ngram_range=(1, 2))), ("clf", LogisticRegression(max_iter=1000)) ]) model.fit(X_train, y_train)跑通这个Baseline之后,你手里就有了一个“及格线”。后面不管你是换BERT还是用大模型API,都要拿这个Baseline来对比。如果复杂方案连5个点的提升都换不来,那它在工程上的麻烦(更大的存储、更慢的推理、更高的成本)就不值得付。
有了Baseline,这个项目的下一步是写一个完整点的模型训练脚本,把以下东西固化下来:数据加载、训练集和测试集的切分(按类别分层采样)、模型训练、指标计算、结果输出。这和Notebook里的随意探索不同,它是可以复现的、规范的。
我当时用Sklearn一套做完之后,又对数据做了一次类别重采样,把少的类别稍微上采样了一些,并且用网格搜索调过几个关键参数。整体流程并不复杂,但每一步都带着明确的判断:为什么要做、做了之后影响什么、什么指标在变好、什么指标在变差。
3.3 封装与部署:让模型真正跑起来
模型训练完毕,这个模型还只是躺在磁盘上的一个文件。接下来就是AI工程的关键环节:把它变成服务。
最简单的方案是FastAPI。选择FastAPI的原因无他:自带数据校验、自动生成API文档、异步支持好,而且写起来足够简洁。一个最小可用的模型服务大概长这样:
from fastapi import FastAPI from pydantic import BaseModel import joblib app = FastAPI() model = joblib.load("model.pkl") vectorizer = joblib.load("vectorizer.pkl") class Item(BaseModel): text: str @app.post("/predict") def predict(item: Item): features = vectorizer.transform([item.text]) proba = model.predict_proba(features)[0] label = model.classes_[proba.argmax()] confidence = float(proba.max()) return {"label": label, "confidence": confidence}然后通过uvicorn app:app --host 0.0.0.0 --port 8000就能让模型跑起来。我建议你在这一步多花点时间做两件事:压测和错误处理。
压测可以用最简单的写法和工具(比如直接用Python开多线程发请求),也可以用Apache Bench或Locust。目标是知道这个服务在多少并发下开始变慢、会不会崩。我当时用Python的ThreadPool简单发了一波请求,就发现了问题:每次请求都去加载一次模型文件。后来改成启动时一次性加载,Thread-safe地复用,接口的响应时间直接从几百毫秒降到了几毫秒。
错误处理也是新手很容易忽略的。用户传过来的文本可能不是字符串、可能是空的、可能超长。这些都要在接口层做好校验和兜底,不然后端一崩,整个链路都跟着遭殃。Pydantic在这里帮了很大的忙,但也要记得定义好请求的约束,比如最大长度、是否允许空字符串。
4. 工程化进阶:从“能跑”到“能用”的分水岭
模型能跑通接口,很多人的学习就到此为止了。但这恰恰是AI工程真正开始的地方。如果只用一句话来概括工程化的目标,那就是:让模型在无人值守的情况下,持续、稳定、可预测地运行。要做到这一点,必须补上三个环节:评估体系、监控体系、版本与发布体系。
4.1 评估体系:准确率之外还要看什么
在分类任务里,只看准确率是远远不够的。假设你的工单里95%是“咨询类”,那一个把所有工单都预测成“咨询类”的模型,准确率就已经是95%了。可它一点用都没有,因为它永远发现不了那5%的投诉。所以一定要看精确率、召回率和F1,稍微进阶一点还要看混淆矩阵。
- 精确率:预测为“投诉”的样本里,真的有多少是投诉
- 召回率:真实的“投诉”样本里,我们找回了多少
- F1:两者的调和平均,平衡两个指标
具体到不同的业务,精确率和召回率的重要性也不同。比如在工单系统里,把投诉漏判了(召回率低)可能意味着客户不满情绪被积压;把普通咨询错判为投诉(精确率低)可能只是转给了不合适的处理组,代价小一些。所以你需要根据业务场景决定这两个指标的权重取舍。
我还会做一件事:在测试集上逐个检查错误的样本。把模型的预测结果和真实标签不一致的样本全部打出来,一条条看。这个过程看似笨拙,实际上能看到很多指标看不出来的问题,比如某些类别在特征上高度重叠、某些标签本身标注就是错的、某些文本含有模型从没见过的专业名词。
4.2 监控与数据漂移:模型上线之后才是开始
模型部署上线,只是万里长征走完第一步。真实环境中,数据的分布是会变的。去年用户投诉里最多的是“账号问题”,今年可能变成了“支付问题”。模型训练时的数据和线上实时数据的差异,专业上叫数据漂移,这是AI系统特有的风险,普通软件工程里几乎不会遇到。
我做的第一版工单分类上线后,大约过了一个多月就出现了明显的效果下滑。当时排查的过程至今记忆犹新:接口没报错、服务器CPU正常、内存没涨,但用户反馈“分类越来越不准”。后来把线上收集的新数据拿下来一统计,发现新数据里“支付相关”的工单占比翻了三倍,而训练集里这个类别的样本本来就少,模型在它上面积累的规律根本不足以应对突然涌入的大量新变体。
从那以后,我在每一个项目中都会提前埋好监控点。最基础的做法是:记录所有请求的输入文本和预测结果,按小时/按天做统计,对比在线预测分布与训练分布之间的差异。对于文本分类,一个轻量方案是对预测类别的分布做跟踪,同时对新文本做简单的聚类或词频分析,及时发现“没见过的表达”是否在变多。
如果做的是数值型特征,那更可以用PSI(Population Stability Index)这类指标来量化分布漂移的程度。虽然第一次做这个会觉得麻烦,但等到线上效果出问题时,你会庆幸手上有这些数据可查。
4.3 CI/CD与模型版本管理
普通后端同学理解的CI/CD,到了AI项目里有了一层新的复杂性:除了代码会变,模型也会变。代码和模型版本不一致是很多线上事故的根源,比如服务端加载了新模型,但特征处理逻辑还是旧的;或者回滚了代码,却没跟着回滚模型。
我个人的习惯是:把模型和代码打成一个整体,一起构建、一起发布。最省事的方式是使用MLflow,它可以把模型产物的版本、训练参数、指标都记录在案。如果你不想引入太重的框架,至少要在项目里维护一个明文清单,记录每个模型文件的文件名、训练时间、训练数据路径、评估指标。
发布策略上,千万不要“一把梭”直接全量。哪怕是个简单的分类模型,也建议采用灰度发布:先让5%的流量走新模型,和旧模型的结果做对比,确认没有明显异常后再逐步放量。因为离线指标再好看,也无法预测真实流量的所有情况,灰度就是给意外留一张底牌。
5. 学习路线规划与避坑心法
走到这一步,已经覆盖了从语言、数据、模型到部署、监控、发布的完整链路。接下来给你一条可执行的学习路线,以及我自己踩坑之后的几点心得。
5.1 分阶段学习路线
如果每周能投入10到15个小时,这套路线大概需要六到八个月,能让你从零基础走到独立上线一个AI服务的水平。
第一阶段:工具与语言基础(第1-6周)。主攻Python语法、Linux常用命令、SQL取数。目标是能用Python处理文件和数据,能在服务器上部署环境、看日志,能从数据库里取到自己想要的数据。
第二阶段:数据处理与可视化(第7-10周)。主攻Pandas和Matplotlib/Seaborn。这个阶段要做的练习是拿一个真实数据集,自己完成清洗、统计、可视化,写一份简单的数据分析报告。
第三阶段:机器学习基础与Baseline项目(第11-18周)。这一阶段是核心。建议啃完Scikit-learn的官方教程,重点理解分类、回归、聚类的基本流程,然后完完整整地做一遍前面那个文本分类项目。
第四阶段:深度学习与大模型应用(第19-26周)。学习PyTorch的基本张量操作和训练循环,理解神经网络的基础结构,然后过渡到LLM应用开发,了解Prompt工程、RAG的基本套路。这个阶段不用钻研论文,够用就好。
第五阶段:工程化与MLOps(第27周以后)。把之前做好的分类服务逐步加上监控、灰度、模型版本管理,引入Docker,部署到一台云服务器上跑起来。学到这里,你就不只是会做实验的人了。
5.2 我踩过的坑,希望你能跳过
第一个坑是只学模型,不学工程。我见过不少朋友Transformer倒背如流,结果让他把模型用FastAPI包一层服务,憋了三天没写出来。AI工程的核心竞争力恰恰在模型之外,越到后面越明显。
第二个坑是拿现成的竞赛数据集当练手,忽略了真实脏数据的处理。Kaggle的数据集都洗得干干净净,在那里练不出处理数据的能力。真实项目里的数据,标签是错的、字段是缺的、口径是乱的。如果你身边没有真实场景,我建议你自己去“造”脏数据:从社交媒体上爬一些非结构化的文本,然后自己给它们打标签,你会发现这个过程的体验和竞赛完全不同。
第三个坑是不理解业务指标。技术指标最终要服务于业务,离线AUC再高,如果不解决用户的真实问题,一切等于零。我做工单分类时学到的教训是:每一次模型迭代,都必须回到“它能不能让客服处理工单更快、用户满意度更高”这个根本问题上思考。一旦技术和业务脱节,你做得越努力,跑得越偏。
如果让我重新从零学一次AI工程,我一定不会先抱着各种数学公式啃,也不会急着追最新的模型架构。我会先搭环境、跑通一个最小服务,先理解数据怎么流动、模型怎么部署、接口怎么响应,再去深挖模型的内部细节。工程这件事,本质上是在不确定性里建立生存能力,你掌握的工具越多、跑的链路越完整,遇到问题时的底气就越足。希望这篇内容能帮你把路线图看清楚,少走点弯路,尽早写出你的第一个“from scratch”。