ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

从零开始学AI工程:数据清洗、模型训练与部署实战

从零开始学AI工程:数据清洗、模型训练与部署实战

很多人刚开始接触 AI 工程时,都容易陷入一个误区:以为要先看完吴恩达全套课程、刷完几百道算法题、再背熟 PyTorch 源码,才配得上“开始”。我自己也是从这条路上跌跌撞撞走过来的,所以看到“从零开始”这四个字特别有感触。“ai-engineering-from-scratch” 这个命题,本质上不是教你怎么背 API,而是帮你建立一套从问题定义、数据准备、模型训练到部署落地的完整工程闭环。这篇文章面向的读者很明确:编程基础薄弱、刚转行入门、或者已经在用现成模型但总觉得哪里没吃透的人。我会结合自己做过的实际项目,把 AI 工程这条路拆成一幅可执行的地图,告诉你每一步真正需要解决的问题是什么,以及为什么很多关键节点,表面上叫“技术问题”,本质却是“思路问题”。

1. 为什么是“从零开始”:AI 工程的学习地图与思路

很多人以为“从零开始”就是从 Python 语法开始。但我的经验恰恰相反,真正的零起点,是从“搞清楚你要用 AI 解决什么问题”开始的。一个合格的 AI 工程,不是把模型跑通就算完,而是要让模型在真实环境里稳定、可靠、可维护地发挥作用。

1.1 先搞清楚 AI 工程到底在解决什么

AI 工程和算法研究有一个本质区别:研究关注的是“模型能不能更准”,工程关注的是“整套系统能不能稳定跑起来”。举个例子,你在 Kaggle 上拿了个高分模型,精度从 92% 提到了 93%,非常亮眼。但到了生产环境,你需要面对的是数据分布漂移、推理延迟超标、内存泄漏、模型版本回滚困难、监控告警缺失这一系列问题。这些问题任何一个冒出来,都会让那 1% 的精度提升显得毫无意义。

我自己接过一个“看起来很简单”的项目:给客服工单做自动分类。模型选型很直接,用个预训练的中文 BERT 微调一下就行。但真正动手才发现,数据是脏的(标签互相矛盾、文本有大量脱敏占位符、类别分布极度不均),标注是乱的(不同时期标注标准不一致),线上文本和训练文本的分布差距也很大。这些工程问题几乎消耗了项目 70% 的时间。

所以理解 AI 工程的第一个关键点,就是把它看成一个“数据 + 模型 + 系统工程”三合一的复合型工作。你不需要一开始就面面俱到,但必须建立起这个全局认知。很多人学了很久还觉得 AI 工程虚无缥缈,就是因为一直在单点技术里打转,没建立起“端到端”的思维框架。

1.2 打破“从零”的迷思:学习路线的三种选择

市面上关于 AI 学习路线的说法五花八门,我用自己带人的经验总结下来,其实就三条主线,你任选一条都可以入行,但每条的侧重点完全不同。

第一条是“工具驱动型”。你先学会用 HuggingFace、LangChain 这些现成框架,跑通一个 demo,再回头补理论基础。优点是见效快,适合已经有编程基础、想快速做出东西的人。后劲不足的地方在于,一旦遇到框架不支持的场景,容易抓瞎。

第二条是“理论驱动型”。先啃机器学习经典教材,把线性代数、概率论、损失函数、优化器这些底子打牢,再动手写代码。优点是基础扎实,后期理解深刻模型、论文复现都很快。缺点也很明显,如果不搭配实战,很容易在枯燥的理论中失去动力,学一年还在“准备中”。

第三条是我个人最推荐的项目驱动型。找一个极小的端到端任务,比如“预测某城市的共享单车需求量”,然后逼自己从数据采集、清洗、特征工程、模型训练到部署上线完整走一遍。过程中用到什么就查什么,缺什么补什么。这条路效率最高,因为每个知识点都有具体的上下文,你不仅知道“是什么”,还知道“为什么需要”。但前提是你能忍受初期大量的挫败感,因为不会的东西太多了。

我自己的经历就是第三条路的典型。当时接到的第一个真实任务是用回归模型预测门店销售额,代码写得惨不忍睹,但这个过程让我第一次理解了什么叫“数据质量决定模型上限”,这个认知在后面的所有项目中都在发挥作用。所以别纠结哪条路“最正确”,选一条能让你坚持走下去的,先开始再说。

2. 地基工程:从 Python 到算法,先解决“能不能跑”

如果说 AI 工程是一座大楼,那 Python 基础就是地基,机器学习核心概念就是承重墙。这一节我讲的不是教科书上的定义,而是实际工程中“真正被用到的那一部分”。

2.1 编程与数学,哪个更重要

被问得最多的问题是:数学不好能学 AI 吗?我的答案是:能,但你不能永远躲着数学。做工程做久了你会发现,80% 的日常任务确实只需要调包、调参、排错,这时候 Python 的能力更重要。但到了性能优化、自定义损失函数、理解模型为何不收敛的时候,数学功底就决定你是“真正在调模型”还是“在碰运气调参”。

举个具体例子。有一次训练一个语义相似度模型,loss 一直居高不下。我检查了数据、学习率、优化器,都没发现问题。最后发现是 embedding 初始化的问题,模型用了随机初始化的向量去计算余弦相似度,初始状态就有偏差,导致训练极不稳定。要理解这个问题,你得懂一点向量分布和相似度计算的内在关系。这不是高深的数学,但足够说明:数学不好,你连“排查问题的思路”都建立不起来。

如果你现在数学基础薄弱,我的建议是不要从头啃教材。有针对性的补:线性代数只需要掌握向量、矩阵、点积、范数;微积分只需要理解导数和链式法则(反向传播的核心);概率统计只需要掌握均值、方差、分布、条件概率。这些够你撑到中级工程水平。更高阶的,等遇到具体问题再往深学。

2.2 第一个模型之前必须掌握的实操清单

我见过很多人卡在“我该学什么”上,迟迟不肯动手。这里给你一份可以直接抄作业的清单,按顺序过一遍,你就有能力完成第一个完整项目了。

第一,Python 基本功。不是说要学到精通,而是会写函数、类、文件读写、列表推导式、字典操作,会用 pandas 做 DataFrame 处理,会用 numpy 做基本数组运算。这些都是 AI 工程中每天重复使用的工具。

第二,机器学习框架的核心组件。以 PyTorch 为例,你需要弄清楚 Tensor 是什么、DataLoader 怎么用、如何自定义 Dataset、model.train() 和 model.eval() 的区别、loss.backward() 和 optimizer.step() 之间的逻辑关系。不需要背诵全部 API,但核心执行流程必须烂熟于心。

第三,训练循环的手写能力。虽然 HuggingFace 的 Trainer 已经封装得很好,但我依然强烈建议你至少手写过一次完整的训练循环。因为只有手写你才会真正理解梯度下降、学习率、批大小这些概念是如何在代码层面流动的。

第四,模型保存和加载。不要小看这一步,很多人模型跑完就关了,第二天想继续训练都不知道该从哪里恢复。你需要知道 state_dict 是什么,checkpoint 里应该保存哪些内容(模型权重、优化器状态、epoch、损失值),以及推理时如何只加载权重不走训练流程。

完成这四个清单,你就具备了一个入门 AI 工程师最基本的“肌肉记忆”。这不是知识性的记忆,而是手和脑配合的熟练度。练不到这个程度,你会在后续的每个环节反复卡壳。

3. 数据与模型之间的桥:工程化思维的养成

很多教程会花大量篇幅讲模型结构,但真实项目中,模型只是流水线上的一个环节。2023 年以后,用现成预训练模型做微调已经不是什么难事,真正的工程化分水岭,在于你如何处理数据、如何管理实验、如何保证结果可复现。

3.1 数据清洗与特征工程:80% 的脏活

我在文章开头提到客服工单项目,这里可以展开讲讲数据工程在其中的比重。原始数据里,同一个用户提问在一天内可能出现几十次,但对应的标记者可能是不同的人,给出的标签也可能相互矛盾。用户用词极度口语化,还有大量类似“!!!!!”这样的噪声符号。如果你直接拿这批数据去训练,模型学到的全是错乱信息。

我花了两天时间做数据清洗,包括:去除 HTML 标签、统一全半角符号、把重复文本聚合、对标签不一致的样本做投票处理、用简单的正则把脱敏占位符筛出来。整个过程非常枯燥,但做完之后,同样的模型结构,准确率直接提升了 8 个百分点。这就是数据质量的价值。

特征工程这个环节,现在因为深度学习的自动化特征提取能力,看似被弱化了,但实际上你仍然需要做大量“人肉特征工程”。日期是不是周末、工单文本有多长、用户是不是重复提问,这些小信号在很多任务里仍然有效。更重要的是,你需要培养一种嗅觉:拿到一个数据集时,先翻一翻统计量,看看数值分布,看看类别频次,而不是上来就喂给模型。

3.2 模型训练的版本管理:从 Notebook 到脚本

很多人刚入门时非常依赖 Jupyter Notebook,这没什么问题,探索性数据分析阶段拿它很顺手。但一旦进入正式训练,必须尽早把代码从 Notebook 里搬进 Python 脚本,并通过配置文件管理实验参数。这里面有一个核心原因:可复现性。

我有一次在 Notebook 里跑实验,同一段代码,上午跑和下午跑结果居然不一样。排查了半天,发现是上午的 session 里执行过一段变量覆盖的代码,下午重新运行的时候没清干净。如果一直在脚本环境里,这个问题根本不会发生。从那以后,我给自己立了个规矩:任何超过半小时的实验,必须进入脚本化流程。

所谓脚本化,不是简单地把 .ipynb 改成 .py,而是要做到几点:数据集路径、模型参数、训练超参数全部外置到配置文件;随机种子固定;每个实验的输出独立保存到单独的目录;训练日志要记录版本号、时间戳和关键指标。达到这个水平,你的实验系统才算是“线性可追溯的”。否则就是在盲目试错,跑完一次不知道上次做了什么,下次还要从头再来。

3.3 数据版本与实验记录的配套方案

数据本身也是需要版本管理的。这个知识点很多人是在踩了坑之后才学到的。我经历过一次典型的“数据翻车”:训练模型用到的是一个新导出的数据版本,但评测时用的是旧版本数据,两个版本里的标签规范已经改过了,导致评测结果完全不可信,白白浪费了一周时间。

我的解决方案不复杂,就是给每个数据文件加日期和版本号,同时在实验记录里写明用了哪一版数据。刚开始你会觉得很琐碎,但坚持下来会发现,这是整个 AI 工程里性价比最高的习惯。哪怕只是笔记里随手记一行“用了 20240615_clean_v3 版本”,未来排查问题时都能节省大量时间。

实验记录方面,我也没有用特别复杂的工具,早期就是一个 markdown 表格,把每次实验的哈希值、数据版本、模型结构、关键超参和最终指标都记下来。后来项目多了,才慢慢迁移到专门的实验管理工具。对于刚入门的人,我反而建议先用最笨的办法把习惯养成,工具永远只是放大你已有的好习惯,不会凭空替你管理实验。

4. 实战走一遍:从零训练并部署一个文本分类模型

前面讲了很多理念,这一节我用一个完整的实战案例,把你需要掌握的技能串起来。任务很简单:训练一个情感分类模型,判断一段用户评论文本是正向还是负向,然后部署成一个 API 服务。数据我用的是自己收集的一个中文商品评论数据集,你也可以用网上公开的情感语料替代。

4.1 项目设计与环境搭建

第一步是定义问题边界。我的目标不是做一个“最好”的模型,而是做一个“能上线、结构清晰、便于后续迭代”的最小可用版本。因此我定了几个约束:使用预训练的中文 BERT 作为底座,做微调;推理延迟在 CPU 环境下控制在 200ms 以内;部署成 HTTP 接口,支持批量请求。

环境搭建上,我用 conda 创建了独立环境,Python 版本固定为 3.10,PyTorch 用 CPU 版本开发调试,训练时再切换到 GPU 机器。这里有一个细节:开发环境和训练环境最好分离。我当时在本地 Mac 上调试代码,用 CPU 跑一个很小批的试运行,确认逻辑没问题后再丢到服务器上用 GPU 训练。这个习惯帮我避免了很多“本地能跑、服务器报错”的尴尬时刻。

依赖管理中我坚持用 requirements.txt 加精确版本号,而不是用那种范围版本号(比如 torch>=2.0)。因为 PyTorch 的 API 在几个大版本之间有过不兼容的改动,如果不锁定版本,几个月后想复现线上结果可能直接跑不起来。锁定版本虽然保守,但可靠。

4.2 训练与调参:手写一个微调循环

这里我不用 Trainer,直接手写微调逻辑,核心是让你理解每一行代码在做什么。大致流程是:加载 BERT tokenizer 和模型,把评论数据切成训练集和验证集,构建 DataLoader,然后进入训练循环。

一个很容易被忽略的细节是数据预处理必须和训练保持一致。我犯过的错误是,验证时忘记做和训练时相同的文本截断处理,导致输入序列长度不匹配,模型直接崩掉。这个错误虽然低级,但排查起来非常耗时。正确的做法是,把预处理逻辑封装成同一个函数,训练和验证都调用它,保证两边完全一致。

调参方面,我先用 BERT 官方推荐的学习率(2e-5)跑一遍基线,再尝试 3e-5 和 1e-5。这里有个经验:学习率对 BERT 微调的影响很明显,太大(超过 5e-5)容易让预训练权重被冲坏,太小(低于 1e-5)又让收敛变得极慢。我在 1e-5 和 2e-5 之间比较过,最终 2e-5 配合三个 epoch 的效果最好。

还有一个关键点是早停(early stopping)。我在训练最后一个 epoch 时发现验证集 loss 已经不再下降了,就果断中断保存了上一个 checkpoint。这避免了过拟合导致的验证集指标下渗。不要觉得“把预设的 epoch 数跑完才算训练完”,模型工程里没有这个规矩,一切以验证集指标为准。

4.3 部署到 API 服务:模型落地的最后一公里

模型训练完成后,部署这一步是很多教程里被一笔带过、但实际最容易翻车的环节。我用 FastAPI 搭了一个简单的推理服务,核心逻辑是:加载 tokenizer 和模型权重,接收请求文本,完成预处理、推理、后处理,返回正负情感的概率值。

这里我要提醒几个实际部署中必踩的坑。第一个是设备问题。如果训练时用的是 GPU,保存的模型权重可能在 GPU 上,推理时没有 GPU 或者只有 CPU,就必须显式指定 map_location='cpu'。第二个是并发问题。FastAPI 默认是同步处理的,如果线上 QPS 高,需要把模型推理放到线程池或者直接用异步处理,否则线程会被占用。第三个是模型装载次数,启动时只加载一次模型,不要在每次请求里都 load 一遍,那是灾难性的慢。

部署完成后,我给服务加了一个健康检查接口和一个版本号字段。这样线上出问题时,我可以快速确认当前服务跑的到底是哪个模型版本。这个小功能在调试和回滚时发挥了巨大作用。最后用 Docker 把环境和代码一起打包,避免“在我电脑上明明是好的”这类扯皮问题。Dockerfile 里注意把模型权重文件复制进去,别用挂载宿主机路径的方式,否则生产环境会找不到模型。

5. 常见问题与排查技巧实录

熟练的 AI 工程师和菜鸟之间最大的差别,不是写代码更快,而是遇到问题时能更快缩小排查范围。这一节我把最常遇到的几类问题整理成速查表,每一类都是我真实经历过的。

5.1 训练不收敛的经典原因

loss 不下降是最让人头痛的问题。我的排查顺序是:先看数据,再看模型,最后看超参数。

数据层面,最常见的问题是标签和特征错位。有一次我把 DataFrame 索引重排后忘记 reset_index,导致标签和文本对不上,模型训练时 loss 一会高一会低,完全不收敛。排查了很久才发现是数据对齐问题。现在我在训练前必定要做一次 shuffle 后的人工抽查,确保文本和标签真的对应。

模型层面,输出层和损失函数不匹配是新手常踩的坑。比如二分类任务,模型输出维度应该是 2(接 CrossEntropyLoss),而不是 1(接 BCELoss)。这两个奖励函数对输入格式的要求完全不同。我见过很多代码,loss 写的是 BCELoss,但模型 final layer 输出的是两个 logits,跑起来会报错或训得很不稳定。

超参数层面,学习率过大是最常见的原因。梯度在更新时一步跨太远,loss 直接爆炸成 NaN。如果你看到 loss 变成 nan,第一时间调低学习率,通常能解决。另外,数据里有 NaN 值也会造成这个问题,所以要养成在喂给模型前检查数据是否有缺失值的习惯。

现象主要原因快速排查方法
loss 不降数据对齐错误、学习率过小人工抽查数据;打印梯度范数
loss 变成 NaN学习率过大、数据含 NaN降低学习率;检查输入数据
训练正常但验证差过拟合、数据泄漏增大正则;检查数据切分逻辑

5.2 代码层面最耗时的三类 Bug

AI 项目的代码 bug 通常不是语法错误,而是逻辑错误。最常见的是 shape 不匹配。PyTorch 里 tensor 的维度搞错,报错信息虽然直观,但有时候你根本想不明白为什么维度会变成那样。我的经验是,在关键操作前后打印 shape,一步步确认,很快就能定位到哪一步出了问题。

第二类是设备不一致。模型在 cuda:0 上,输入数据在 cpu 上,一跑就报错。报错信息已经写得很清楚了,但我还是一次次犯。现在我的习惯是脚本开头就设置一个 device 变量,数据和模型都统一用 device 来指定,而不是到处硬编码 cuda。

第三类是“静默错误”。代码没报错,但结果明显不对。这类 bug 最难查,因为它需要你主动怀疑自己的数据或逻辑。有一次我发现模型 loss 在下降,但准确率一直在 50% 左右徘徊,后来发现是我把 label 的映射关系搞反了,pandas 里 1 和 0 的标注本身就和模型输出相反。这种问题,光靠看代码很难发现,一定要打印几个实际案例的预测结果,人工看一眼。

5.3 部署与线上的常见坑

部署阶段的坑,更多是“环境差异”带来的。本地跑得好好的程序,一进 Docker 就崩溃,十有八九是环境依赖问题。解决方案是别在本地装东装西,把 requirements.txt 里的依赖版本和 Python 版本都在容器内重新验证一遍。

另一个坑是模型推理的延迟。CPU 上跑 BERT 虽然不至于特别慢,但如果批量请求太多,或者输入文本太长,延迟会急剧增加。我给线上服务加了一个输入长度的硬性限制,超过阈值就提示用户缩短文本。这看起来像是在“偷懒”,但实际上是在保护服务的稳定性。宁可拒绝一部分请求,也不要让单个慢请求拖垮整台机器。

还有一类问题是配置管理混乱。线上跑的是哪个模型版本、部署时间是什么时候、对应的数据版本是哪一版,这些信息如果不记录下来,出问题时就只能靠猜。我现在每次部署都会在服务启动日志里打上 git commit hash 和模型文件名,这样线上出了任何问题,我能立刻回溯到对应的代码和数据版本。

6. 从单模型到系统:AI 工程的进阶方向

当你完整走过一遍从零到部署的流程,对“AI 工程”这四个字的理解就会彻底不同。接下来要做的就是沿着已经打通的链路,往两个方向深化,一个方向是面向模型本身(LLM 应用开发),另一个方向是面向整个系统(MLOps)。

6.1 大语言模型时代的工程变化

2023 年之后,很多场景不再需要你从零训练一个模型,而是直接在已有的大语言模型之上做应用开发。这时候 AI 工程的重心发生了转移:从“怎么训”变成“怎么用”。你需要掌握提示词工程、上下文窗口管理、RAG(检索增强生成)、外部工具调用,以及如何处理模型的幻觉问题。

但别忘了我前面讲的所有基础工程能力,在 LLM 应用阶段依然全部适用。RAG 里的检索效果取决于你的文档切分和 embedding 策略,这本质上还是数据和向量检索的问题;构建 Agent 应用时,你依然需要做日志追踪、错误处理、版本管理和性能监控。所谓大模型时代降低了 AI 开发门槛,只是降低了“训练和调参”的门槛,工程难度转移到了“可靠性和组合复杂度”上。

我自己在做一个基于 LLM 的客服问答助手时,最大的体会是:模型选什么不是最难的,难的是如何让模型在业务规则约束下稳定输出。你需要做大量的提示词测试、few-shot 样例筛选、输出格式校验,以及兜底策略设计。这个过程,依然是典型的工程过程,和之前跑通 BERT 分类模型的逻辑并无二致。

6.2 MLOps 与全链路稳定性意识

往系统方向走,你会接触到 MLOps。这不是一个时髦的新概念,在我看来它是把传统软件工程的习惯引入 AI 项目:自动化测试、CI/CD、监控告警、模型回滚机制、线上数据反馈闭环。以前我做完一个项目交付了就结束,后来发现线上数据一变,模型效果就会慢慢衰减(这叫做模型漂移),所以必须建立监控机制。

一个务实的建议是,先别急着上各种复杂的 MLOps 平台,先把最基础的监控做起来:记录线上请求分布、预测结果分布、平均延迟、异常率。这些指标能让你在模型出问题时第一时间发现,而不是等用户投诉了才知道。我做情感分类服务时,每两周对线上预测结果做一次抽样人工复核,发现准确率明显下降就考虑用新数据微调。

CI/CD 对 AI 项目来说,核心是“把训练流水线和评估流水线自动化”。每次改数据集、改预处理逻辑,都要能自动重跑评估脚本,拿到可对比的指标。这个自动化跑起来之后,团队合作的效率提升是质变级的。你不必一次到位,但一定要有这个方向感。

结尾:一些个人体会

回看自己从零开始做 AI 工程的那段时间,最深的感触是:阻碍我前进的从来不是技术难度,而是“总觉得还没准备好”的心理。我想学完这再看那,等我查完这个 API 再动手,结果几个月过去了,项目还是空的。后来我逼自己用最笨的办法写一个线性回归、跑一个随机森林、部署一个最小的 Flask 接口,反而在两周内就打通了所有环节。AI 工程是一个需要“动起手来才能发现问题”的领域。你写的每一个报错、踩到的每一个坑,都会在下一个项目里变成你的判断力。如果这篇文章能带给你唯一的行动信号,那就是:选一个极小的端到端任务,今天就把环境装上,明天开始跑数据。剩下的,都是在路上学的。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进