
第一次看到ai-engineering-from-scratch这个项目标题时我下意识觉得它不过是又一套“从入门到进阶”的资料合集。这类名字在网络里实在太常见了。但真正跟着这条路径把一个模型应用从数据清洗、特征构造、模型训练、评估选型、接口部署一直走到线上监控之后我才意识到标题里最值钱的部分不是“AI”而是那个“from scratch”。它逼着你不依赖某个现成平台、不依赖别人封装好的 Notebook而是亲手把每一个环节读懂、跑通、修理好。这篇博文就把这条路径拆开来讲讲讲 AI 工程到底是什么、需要哪些底层能力、怎样用一次完整项目实现闭环以及我从零走完一遍后踩过的那些坑。它更适合刚入行的算法工程师、写过 Python 但始终没有交付过模型应用的开发者也对想了解 AI 应用如何落地的产品和业务朋友有参考价值。1. 动手之前先把 AI 工程的边界想清楚在真正做 AI 工程之前我先做了好几年后端业务系统开发后来才转到算法方向。踩了很多年的坑后我渐渐对岗位分工有了一个比较清晰的判断算法研究员的主要任务是提出新方法、发表新结论做法上允许大量实验性和不确定性而 AI 工程师的核心任务是利用成熟方法解决现实世界的问题把模型变成长期稳定运转的业务能力。这个定义非常重要因为它决定了一条“从零开始”的学习路线到底该怎么走——不是比谁记住的深度网络结构更多而是能从业务问题出发挑选合适的模型和流程并把系统交付出去。我之前团队里有个现象很典型几个刚毕业的同学简历上写着会 PyTorch、会 Transformer但拿到一个实际的分类需求时第一反应是“我能不能上 BERT”而不是先问问“这个问题的数据量有多少、错误代价有多大、线上延迟要求是多少”。这不是他们不聪明而是没有人把工程边界给他们讲透。AI 工程的“工程”二字本质上是在约束条件下做取舍数据不够就先用线性模型延迟敏感就用轻量模型业务需要可解释性就不要盲目堆深度网络。一旦你接受了这种约束意识很多东西会突然变得简单。1.1 AI工程师不是“弱化版算法研究员”很多人会把 AI 工程师看成“调参侠”或者“弱化版的研究员”这是个大误会。研究者的产出是一篇论文或者一个新算法验证的是“理论上行得通”工程师的产出是一个在线上稳定运行、业务指标可量化的系统验证的是“实际上靠得住”。两者的评价体系完全不同。研究里一个模型 AUC 提升 0.01 可能是一篇论文的重要发现工程里一个模型训练时间缩短 20%、线上内存占用下降 30%可能比 AUC 提升 0.01 更让团队受益。从零开始转向 AI 工程的人最容易犯的心态错误就是总想“发明点什么”。我在工作里见过不少同学把大量时间花在尝试最新论文、复现 SOTA 模型上结果项目拖了很久却没有一个可用的服务上线。对工程师来说最重要的不是模型有多新而是它能不能在当前数据、资源、延迟限制下解决好问题。你选择逻辑回归不是因为它高级而是因为它简单、靠谱、可解释而且在一堆高基数稀疏特征上效果并不差。等到逻辑回归效果不够时再往上升级到 GBDT 类模型最后才轮到神经网络。这个从简到繁的路线才是工程里最常见的做法。1.2 全链路思维决定你能走多远AI 工程的全链路并不仅仅是“训练一个模型”。模型训练只是链条的中间环节它前面的数据获取、数据校验、特征工程它后面的模型封装、服务部署、线上监控每一个环节都至少和训练本身同等重要。刚入行时我也只盯着模型效果后来发现一个残酷的事实很多项目根本没机会走到“调模型”这一步因为数据质量问题已经把时间耗光了。我的类比是算法是菜谱AI 工程师是一个要负责把菜端上桌的人。菜谱只告诉你“盐少许、大火收汁”但你要自己去买菜、洗菜、切菜、掌握火候中间锅烧糊了要会补救客人吃完还得观察有没有不良反应。只会读菜谱不会做饭的人在真实厨房里是活不过半个工作日的。放到工程里这就意味着你至少要把几件事串起来能够用脚本获取并理解数据能写干净的预处理和特征逻辑能训练并客观评估模型能通过接口把模型暴露给别人调用能在上线后持续观察效果有没有衰减。这五件事是从零到一真正的最小闭环。2. 从零开始需要补的核心技能栈建议这样排优先级很多人在“从零开始学 AI”的阶段容易进入一个误区就是把所有数学课、所有编程课、所有框架课同时铺开最后既没学完数学也没写好代码更没有完整做完一个项目。我踩过同样的坑。后来我给自己定了一条原则不追求系统完整只追求“能独立交付一个项目”所需的最低必要知识然后在做项目的过程中逐步补全。这个“最低必要知识”并不是很玄的东西。它大概包含四层数学直觉、编码能力、数据处理能力、模型工程化能力。你在网上会看到很多学习路线动辄十门课起步但真正上手时你会发现多数项目对知识的要求远没有想象中那么高。你不是去推导神经网络收敛性而是把已经封装好的算法用对、用稳、用明白。2.1 数学能力的正确打开方式够用且要有直觉很多人对 AI 的恐惧来自数学。我得说句公道话如果目标是做 AI 工程而不是做算法研究你并不需要把数学推到研究生水平。但有三块数学知识是真绕不开的而且它们都有明确的工程落地场景不是纸上谈兵。第一块是线性代数。你不用会手推 SVD但你要理解“一个样本是一行特征一批样本就是一个矩阵”矩阵乘法就是批量计算特征和权重的点积。这个直觉在调试数据形状时会救你无数次。第二块是概率统计。你要知道“模型输出的概率表示什么”、交叉熵损失在衡量什么、AUC 作为一个指标为什么和阈值无关。这些概念到处都是但在实际项目里能准确说出来的人不多。第三块是微积分和优化直觉。你不用会证明梯度下降收敛但你要理解学习率太大是在悬崖上乱跳太小是在平地上挪动这个直觉能让你把训练过程调得相对顺滑。我当时的方法很简单每一项数学知识都和一个具体操作挂钩。比如学矩阵乘法时我就去看 NumPy 里(n_samples, n_features)和(n_features, n_targets)这两个维度怎么对齐学概率时我就去看predict_proba的输出和roc_auc_score的输入。知识只有用起来才真正长在你的脑子里。2.2 编程与工程工具Python之外还得熟悉这几样编程能力是 AI 工程的基本盘。你最少要熟练 Python 的基本语法、数据类型、文件读写、异常处理以及 Pandas 和 NumPy 这两个最重要的库。很多人以为会写个for循环就算会编程了真到处理数据时几百万行数据一跑效率差距立刻就出来了。我的建议是先练“向量化思维”能不用for就不用for能用apply就用apply能用内置方法绝不用手写循环。这种习惯会直接转化为数据处理速度上的优势。比 Python 本身更常被忽略的是工程工具。Git 你得会至少会clone、commit、branch、merge这一套基本操作否则你永远不敢大改代码最后变成在 Notebook 里复制粘贴、命名final_v2_reallyfinal.ipynb。Linux 命令你也得会因为绝大多数线上服务器都是 Linux你不会grep日志、不会看显存占用、不会后台执行任务工作效率会特别低。Docker 从入门到能用只需要一两天时间但它解决的是环境漂移的问题——同一个模型在 A 机器上能跑、在 B 机器上报错这种事情我见过太多次了。还有个容易被忽略的东西是虚拟环境和依赖管理。我强烈建议每次项目新建一个独立的虚拟环境并且把依赖版本用配置文件固定下来。别小看这个习惯它能让你的项目在半年后依然能被重新跑起来。我接手过不少“别人的代码”最痛苦的不是模型太难而是没人知道当年用的是哪个版本的库、哪个版本的 Python跑起来全是兼容性炸弹。2.3 数据和特征能力模型效果的分水岭在 AI 工程这个链条里数据和特征能力几乎决定了项目的上限。拥有同样一个模型不同人做出来的效果天差地别往往不是调参的差异而是对数据的理解深度不同。刚拿到一份数据时我的习惯动作是先回答这么几个问题表格长什么样子每一列是什么类型缺失值有多少目标变量的分布是否平衡哪些特征是数值型、哪些是类别型这些问题不回答完我不会碰任何模型代码。特征工程的核心不是“创造一堆神秘变量”而是把业务理解转化成模型能看懂的信号。比如做客户流失预测时tenure在网时长和contract_type合同类型往往比一堆复杂交互特征更有效因为它们直接反映了用户和产品的关系阶段。特征不是越多越好越多越容易引入噪声和过拟合。我自己的经验是先做少量有业务含义的核心特征把模型凑齐跑通再根据验证集表现有目的地增加特征。一上来就生成几百个特征、然后扔给模型自动筛选的做法只适用于比赛不适合需要长期维护的系统。数据处理还有一个容易被忽视的点数据划分。你得保证训练集和验证集之间没有信息重叠。比如同一个用户出现在两个集合里那验证集的评估结果就会虚高。后面我会单独讲这个问题但它确实是新手最容易踩的大坑。3. 跑通一个完整项目客户流失预测实记对“从零开始”的人来说最快的路径不是再读一本书而是亲手把一个完整项目做出来。我下面用一个常见的客户流失预测场景做例子把从原始数据到线上接口的完整过程走一遍。你不需要有背景知识只要跟着看就能知道整个 AI 工程长什么样。我用的数据是典型的电信客户流失数据集假设字段有tenure在网月数、contract_type合同类型月付/年付/两年、monthly_charges月费、total_charges累计费用、payment_method支付方式、support_tickets近半年客服工单数以及目标变量churn是否流失1 表示流失。这样的数据在真实业务里非常常见也很适合对照你的实际工作理解。3.1 拿到数据先别急着训练先回答基础问题我第一次拿到这种数据时也犯过直接开始建模的错误。后来养成一个习惯先加载数据再花十分钟做“体检”。这个体检主要是看数据的整体面貌代码非常朴素但带来的信息量巨大。import pandas as pd df pd.read_csv(churn.csv) print(df.head()) print( * 50) print(df.info()) print( * 50) print(df.isna().mean()) # 检查缺失比例 print( * 50) print(df[churn].value_counts(normalizeTrue)) # 目标分布这几行代码足够回答前面提到的基础问题。比如如果total_charges存在缺失可能是因为它和tenure有关——新用户累计费用还没产生但存了空值如果churn的分布是 73% 对 27%说明类别不平衡后面处理评估方式时得特别小心。不要小看这些“体检”我见过太多人跳过这一步直接建模最后发现模型预测结果全部偏向多数类准确率看着高实际毫无可用性。3.2 特征工程与划分别让自己无意中“偷看答案”做完探索之后进入特征工程。我的原则是先简单后复杂。这里的“简单”包括数值特征放进去“类别型也逐步理智编码”填充缺失值。但要注意这些处理都需要在整个训练流程的框架内完成不能先处理再划分否则容易引入数据泄漏。稳妥的做法是把预处理和模型串成一个 Pipeline并用分层抽样划分训练集和验证集from sklearn.model_selection import train_test_split X df.drop(columns[churn]) y df[churn] X_train, X_val, X_test, y_train, y_val, y_test train_test_split( X, y, test_size0.2, stratifyy, random_state42 )这里的stratifyy是为了保证训练集和验证集中正负样本比例一致。如果不做这一步可能随机切分后训练集里流失用户特别少模型压根学不到流失模式。接着把预处理写进ColumnTransformerfrom sklearn.compose import ColumnTransformer from sklearn.preprocessing import OneHotEncoder, StandardScaler numeric_cols [tenure, monthly_charges, total_charges, support_tickets] categorical_cols [contract_type, payment_method] preprocessor ColumnTransformer( transformers[ (num, StandardScaler(), numeric_cols), (cat, OneHotEncoder(handle_unknownignore), categorical_cols), ] )handle_unknownignore很重要它保证线上新出现的类别值不会让代码崩溃。这种细节在离线实验里看不出来一上线就会变成凌晨两点的报警电话。数据划分和预处理本身没有多高深但顺序错了、边界模糊了评估出的指标就不真实。3.3 训练与评估准确率是最容易欺骗你的指标我先用一个逻辑回归作为基准因为它的训练速度快、结果稳定而且可以作为后续复杂模型是否需要提升的参照。在类别不平衡的情况下还可以给少数类加权重from sklearn.pipeline import Pipeline from sklearn.linear_model import LogisticRegression model_lr Pipeline( steps[ (prep, preprocessor), (clf, LogisticRegression(max_iter1000, class_weightbalanced, random_state42)), ] ) model_lr.fit(X_train, y_train)之后再用 LightGBM 这类梯度提升树模型来对比。你会很惊讶地发现在某些表格数据上GBDT 类模型往往比深度神经网络更高效import lightgbm as lgb model_lgb Pipeline( steps[ (prep, preprocessor), (clf, lgb.LGBMClassifier(random_state42, n_estimators300, learning_rate0.05)), ] ) model_lgb.fit(X_train, y_train)我强调评估指标的选择。很多新手喜欢用准确率但它在类别不平衡的场景下几乎是骗人的如果 73% 的用户不流失一个“永远预测不流失”的模型也有 73% 的准确率。对流失预测我更推荐ROC_AUC和Precision-Recall Curvefrom sklearn.metrics import roc_auc_score, precision_recall_curve y_prob model_lgb.predict_proba(X_val)[:, 1] auc roc_auc_score(y_val, y_prob) print(fValidation AUC: {auc:.4f})AUC 高说明模型能较好地把流失用户排在非流失用户前面。但光有 AUC 还不够你还要根据业务成本选阈值。比如挽留一个用户要花 50 元而流失一个用户损失 500 元那你可以把阈值调低宁可多圈一批人去做挽留也不要漏掉真正会走的人。阈值不是默认 0.5而是要跟着业务成本走。这一点在学校里很少会教但在工程里非常关键。3.4 把模型封装成服务从 Notebook 到真正可用模型训练完只完成了一半工程。另一半是把模型变成一个别人能调用的服务。我常用的方案是 FastAPI它轻量、自带接口文档、部署也方便。这里最关键的一步是保存整个 Pipeline而不是只保存模型参数。这样你在线上收到原始特征后会先自动执行清洗、编码、缩放再走模型预测特征处理逻辑只维护一份。import joblib # 保存完整的 pipeline joblib.dump(model_lgb, churn_model.joblib)线上接口示例from fastapi import FastAPI from pydantic import BaseModel import joblib app FastAPI() model joblib.load(churn_model.joblib) class ChurnItem(BaseModel): tenure: float contract_type: str monthly_charges: float total_charges: float payment_method: str support_tickets: float app.post(/predict) def predict(item: ChurnItem): data [[ item.tenure, item.contract_type, item.monthly_charges, item.total_charges, item.payment_method, item.support_tickets, ]] prob model.predict_proba(data)[0][1] return {churn_prob: round(float(prob), 4)}这个例子做了不少简化但核心思想是完整的输入原始特征输出概率分数。上线前你还要包一层 Docker把 Python 版本和依赖锁定上线后要记录每个预测对应的模型版本方便回溯。把 Notebook 变成服务这个过程是我个人认为“从零开始”最值得经历的部分——它会逼着你考虑环境、异常、请求格式、版本这些以前完全不在意的细节。4. 从零上手最容易踩的坑整理成一张排查清单项目做完不代表你没有踩坑。下面这几个问题是我们在实际项目中反复遇到的有些坑我踩过不止一次专门整理出来你可以当成一个“避坑清单”来用。4.1 数据泄漏训练集里混入了“未来的信息”数据泄漏的表现形式是训练时指标很好但线上效果远不如预期。最常见的原因包括在划分训练集之前就用全量数据做缺失值填充或标准化构造特征时用了目标变量相关的信息在时间序列场景里用未来的数据预测过去的事件。我自己的一个教训是有个项目里把“用户是否发过投诉工单”当成特征但某类工单只有在用户表达离网意向之后才会出现模型学到的其实是结果而不是原因。最后我在特征审计时才发现这个问题。建议每个特征都要问一句它在预测时点真的已经能拿到吗如果答案是“不确定”就回到业务源头确认。4.2 数据集划分的随机性问题很多人习惯直接train_test_split一把梭但不同数据场景对划分方式的要求是不同的。对于时间序列类数据随机划分会打乱时间顺序导致模型“偷看”未来信息正确做法是按时间前 80% 训练、后 20% 验证。对于有重复用户的数据要按用户 ID 分组建划分不能把同一个用户既放进训练集又放进验证集。对于类别不平衡的数据就要用stratify保证比例一致。这看起来都是小细节但每一条都关系到评估结果的真实性。我不能说这类坑最贵但它们确实最容易在最关键的节点坑你。4.3 可复现性随机种子不是“玄学开关”我发现很多同学喜欢在代码里写random_state42但对可复现性的理解只停留在“固定种子”上。其实固定种子只是第一步。更重要的是固定你的整体代码流程、依赖版本、数据文件版本。同一个训练脚本我用 PyTorch 1.13 在 GPU 上能复现换到 CPU 版本结果就可能不同同一份数据如果你在训练脚本里额外做了一次随机抽样种子也得同步固定。我在项目里会把三个东西记进实验记录代码 Git commit 版本、依赖版本、数据文件校验和后缀。这能让你在下一次调参时清楚知道自己“上一次到底跑出了什么结果”而不是凭记忆猜。4.4 评估指标和业务目标脱节AUC、准确率、F1这些都是统计指标它们不等于业务结果。很多项目在离线阶段 AUC 很高上线后 ROI 却很难看原因就是没有把评估指标翻译成业务语言。我建议你做一次“决策闭环”梳理模型预测结果出来后业务方会怎么用每召回一个流失用户运营成本是多少每漏掉一个流失用户收入损失是多少根据这个成本关系来找阈值。如果你发现某个阈值下净利润最高那才是你真正需要的模型配置而不是坐在那里调高 AUC。4.5 上线之后才开始考虑监控模型上线后并不是万事大吉相反监控工程师的生活这才刚刚开始。线上特征分布会随着用户结构变化而漂移业务策略变化会让历史标签失真模型本体的效果也会随着时间衰减。我建议从第一天就记录并监控几个量每日预测概率的均值、分布形状以及主要特征的口径分布。一旦预测均值突然从 0.3 跳到 0.6很可能说明线上的输入分布发生了大变化。另一个实际操作经验是每个返回结果都带上模型版本号之后排查问题时你能很快定位是旧模型还是新模型在响应。监控这件事一定要前置别等业务方用“最近预测怎么不准”来找你那时候你手里没有数据会非常被动。5. 越过第一道坎后我自己的一点真实感受走到这里如果你也跟着动手做了一个端到端项目那恭喜你你已经彻底越过了“从零到一”这道坎。你可以回头看看当初觉得头疼的“AI 工程”其实拆开就是一条链条链条上的每一环都不神秘把它们拼起来需要的只是耐心和动手能力。这是我做 AI 工程这几年最深的一点体会。5.1 最快的学习方式是逼自己交付我看了太多人把时间花在“反复从零开始”上今天看数学、明天看框架教程来回换资料就是不把一件事做完。直到我用两周时间逼自己从零交付了一个客服工单分类接口之后才真正理解“交付”这两个字有多值钱。交付逼出需求需求逼出学习学习才真正高效。如果你还在犹豫自己的第一个 AI 工程项目我的建议很简单挑一个你熟悉的业务场景用一张带标签的表格数据做一个完整的分类或预测服务。把范围缩到最小不要一上来就搞大模型不要梦想一步到位先把链条走通。5.2 记录和复盘是工程能力最便宜的放大器踩过几次坑之后我开始给自己的每个项目建立一个简短档案数据源、特征定义、模型版本、最终指标、上线时间、上线后踩过的问题。这个习惯并不需要很多时间但它会在半年后产生巨大回报。因为 AI 工程是一个高度依赖经验沉淀的领域很多问题你在现场可能花三天才能解决但如果记录下来下次同类问题只需要半小时定位。我现在遇到一个模糊的线上问题时第一反应不是上网搜而是翻自己的项目笔记因为大概率我在之前某个项目里已经遇到过类似的坑。记录本质上是在把时间变成可复用的资产。最后再分享一个个人习惯每次项目结束后我会把“如果重来一次哪里会做得不同”写在笔记最后。这句话比读十篇经验帖都有用因为它逼着你直面自己的选择和代价。AI 工程这条路很长但从零开始并没有想象中那么可怕。你只需要把第一个项目做得足够小、跑得足够完整然后让下一个项目在这个基础上站得更高一点。积累就开始运转了。