☰
AI工程从零开始:端到端机器学习项目实战
2026/9/28 17:38:27 网站建设 项目流程

第一次看到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(normalize=True)) # 目标分布

这几行代码足够回答前面提到的基础问题。比如如果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_size=0.2, stratify=y, random_state=42 )

这里的stratify=y是为了保证训练集和验证集中正负样本比例一致。如果不做这一步,可能随机切分后训练集里流失用户特别少,模型压根学不到流失模式。

接着把预处理写进ColumnTransformer:

from 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_unknown="ignore"), categorical_cols), ] )

handle_unknown="ignore"很重要,它保证线上新出现的类别值不会让代码崩溃。这种细节在离线实验里看不出来,一上线就会变成凌晨两点的报警电话。数据划分和预处理本身没有多高深,但顺序错了、边界模糊了,评估出的指标就不真实。

3.3 训练与评估:准确率是最容易欺骗你的指标

我先用一个逻辑回归作为基准,因为它的训练速度快、结果稳定,而且可以作为后续复杂模型是否需要提升的参照。在类别不平衡的情况下,还可以给少数类加权重:

from sklearn.pipeline import Pipeline from sklearn.linear_model import LogisticRegression model_lr = Pipeline( steps=[ ("prep", preprocessor), ("clf", LogisticRegression(max_iter=1000, class_weight="balanced", random_state=42)), ] ) model_lr.fit(X_train, y_train)

之后再用 LightGBM 这类梯度提升树模型来对比。你会很惊讶地发现,在某些表格数据上,GBDT 类模型往往比深度神经网络更高效:

import lightgbm as lgb model_lgb = Pipeline( steps=[ ("prep", preprocessor), ("clf", lgb.LGBMClassifier(random_state=42, n_estimators=300, learning_rate=0.05)), ] ) model_lgb.fit(X_train, y_train)

我强调评估指标的选择。很多新手喜欢用准确率,但它在类别不平衡的场景下几乎是骗人的:如果 73% 的用户不流失,一个“永远预测不流失”的模型也有 73% 的准确率。对流失预测,我更推荐ROC_AUC和Precision-Recall Curve:

from 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(f"Validation 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_state=42,但对可复现性的理解只停留在“固定种子”上。其实固定种子只是第一步。更重要的,是固定你的整体代码流程、依赖版本、数据文件版本。同一个训练脚本,我用 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 工程这条路很长,但从零开始并没有想象中那么可怕。你只需要把第一个项目做得足够小、跑得足够完整,然后让下一个项目在这个基础上站得更高一点。积累就开始运转了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询