ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

AI工程从零开始:数据、模型与服务的完整落地路径

AI工程从零开始:数据、模型与服务的完整落地路径 如果你在简历或项目列表里写过“ai-engineering-from-scratch”这个字样大概率会遇到两类追问一是“你训练的模型能达到什么效果”二是“你这条项目链路到底是怎么从零搭起来的”。第一类问题考验算法功底第二类问题考验工程能力。很多人在做AI项目时模型效果明明不错但一谈到“怎么稳定复现”“怎么上线服务”“数据变了怎么办”整个项目就开始站不住脚。这篇文章想表达的“from scratch”不是让你从反向传播推导开始也不是让你自己写一个深度学习框架而是指一条从数据到模型服务、从单机脚本到可交付项目的完整落地路径。这篇内容适合三类人刚从算法岗实习、只会用Notebook跑模型的同学后端工程师想转AI方向需要补全AI系统认知以及“一个人想独立完成一个能写进简历的AI项目”的从业者。我会按照自己的实践顺序把思路拆解、目录设计、核心代码逻辑、踩坑过程、工具选型一次讲清楚。文中不会有故弄玄虚的理论只讲你怎么抄、怎么改、怎么避坑。1. 先把“从零开始”这件事想明白1.1 AI工程不等同于训练模型我最早犯过的错误是把大量时间花在优化模型结构上结果项目整体推进非常慢。后来我才意识到在一个真实的AI工程项目里模型训练只是其中一条流水线。你还需要做数据清洗、特征逻辑开发、模型版本管理、离线评估、接口服务、监控告警。任何一个环节断掉项目都跑不到生产环境。这就像“盖房子”和“装修客厅”的关系你花很多时间把客厅装修得漂亮但水电没通、地基没打稳这个房子仍是不能住的。所以“AI工程”这个词重点在“工程”两个字。工程意味着重复可执行、边界清晰、可测试、可回滚。训练代码解决的是“用这批数据能得到什么模型”工程解决的是“这个模型能不能被可靠地生产出来、稳定地用下去”。从零开始的意思就是该补的工程地基一块也少不得。1.2 从零开始必须覆盖的完整路径我给自己定的最小闭环是定义业务问题、获取原始数据、数据清洗、特征工程、训练一个基线模型、离线评估、封装成接口、写清楚部署与监控方案、形成迭代记录。每一步都需要有一个可交付的产出物而不是只停留在分析阶段。如果你现在只会写“model.fit(x_train, y_train)”那这条路径还差得远。要先能回答下面这几个问题原始数据存在哪里是什么格式有没有脏数据特征逻辑写在哪训练和预测时是不是同一套代码训练完之后模型产物是什么怎么验证它没有损坏接口收到请求后怎么把输入转换成模型需要的张量预测结果对应业务什么指标线上数据和训练数据分布变了怎么办。这些问题全部打通才算一个真正“从零”跑完的AI工程。1.3 怎么判断你有没有真正跑通我常用一个简单标准在环境变量齐全的情况下运行一条命令就能完成从原始数据到训练产物再到启动服务的全流程再运行一条测试命令能自动校验模型输出格式和核心指标是否达到阈值。如果你做不到这两点你的项目还只是一堆脚本的集合不是工程。还有一点容易被忽略。你要说得清楚当前线上模型是哪个版本、用了哪份训练数据、跑出来多少指标。哪怕你只做了一个个人项目也应该把这些信息通过“实验记录文件”或“版本标签”固定下来。很多人最后面被面试官追问“你项目里的badcase怎么分析”“你有几个模型版本”答不上来就是因为没有把闭环跑透。2. 搭一个不劝退的AI工程骨架2.1 先定目录结构再写训练代码我记得自己第一次做完整项目时所有脚本都堆在一个文件夹里数据在data目录下可训练代码也在data下非常混乱。后来我参考了一些成熟开源项目的做法定了一套看起来不重、但对从零起步最友好的目录结构。project/ ├── configs/ # 参数配置不要写死在代码里 ├── data/ │ ├── raw/ # 原始数据只读不修改 │ ├── processed/ # 清洗后的数据 │ └── versions/ # 数据版本记录目录 ├── src/ │ ├── data/ # 数据加载、清洗逻辑 │ ├── features/ # 特征处理逻辑 │ ├── models/ # 模型定义和训练逻辑 │ ├── evaluation/ # 评估脚本与指标计算 │ └── serving/ # 模型服务封装 ├── tests/ # 单元测试与集成测试 ├── scripts/ # 一键运行脚本 └── notebooks/ # 探索性分析只做研究不做生产你可能觉得目录不重要但它决定了迭代效率。configs目录让参数管理和实验分离src/models和src/features分开避免训练代码里到处嵌特征逻辑tests在最早期就预留位置哪怕只写一个“输入输出形状对不对”的测试也好。坚持下来的结果是三个月后你再回到这个项目依然能在十分钟内定位到某个逻辑在哪里。2.2 训练、评估、服务三条流水线怎么拆模型的生命周期大致分三个阶段训练、评估、服务。我建议从最开始就把这三块拆开而不是在一个文件里又训练又评估又写接口。训练阶段只关心“从数据到模型权重”它的产物是一个model文件、一个指标记录、以及对应的特征依赖清单。评估阶段加载这个model和固定的测试集输出离线指标报告。服务阶段则加载评估通过的model通过HTTP接口对外提供预测。三个阶段的依赖方向是单向的评估依赖训练产物服务依赖评估通过的产物。这样做是为了避免一个经典灾难训练时用的特征转换函数是“一个版本”接口里用的是“另一个版本”。如果特征逻辑没有变成一个公共模块供训练和服务一起调用那么线上和线下的效果差距一定存在。我在3.3节会给出具体封装方式但一定要记住这个原则同一套特征代码只写一次训练预测共用。2.3 初版可以不放但不能不想的事很多人一提到工程化就想到Kubernetes和容器编排但一个从零开始的项目不需要在第一天就上这么重的设施。程序员得学会“激进留白”。我最初的版本只有四样东西一个是Python虚拟环境一个是Git一个是configs目录下的yaml配置文件还有一个是requirements.txt。所有东西都在本机跑服务用FastAPI启动。但我一定会在代码里预留一些接口。例如训练脚本会把“是否使用GPU”“批量大小”“数据路径”都读成配置项而不会写死模型加载时会检查输入维度和特征名服务启动时会读取一个模型版本号并记录在日志里。这些东西看着不复杂但等你要加容器化、要扩大训练规模、要多人协同时它们就是现成的地基。3. 实操把第一个端到端闭环跑起来3.1 先定义业务指标而不是只盯准确率我第一次做分类模型时只看准确率模型达到了96%结果拿到业务场景一测完全不能用因为样本本身极度不平衡就算全部预测成多数类也能有95%的准确率。后来我学乖了第一步先和需求方对齐“什么算好”。这里有一个对照表可以用来纠偏业务场景离线常用指标线上验证指标商品推荐RecallK, NDCGK点击率、转化率风控二分类AUC, F1, KS误杀率、社区发现率文本分类准确率、F1、宏平均用户投诉率、人工抽检合格率搜索排序MAP, MRR点击率、跳过率、搜索成功占比选指标不是越复杂越好关键是离线指标能反映业务结果。我倾向于先找三个以内、能直接对应到业务收益的数字。比如做用户增长模型推荐结果离线看Recall20线上就要看“首页曝光到点击的转化率”。如果你只有一个准确率那你要回头想清楚这个模型在业务里到底在优化什么。3.2 一套最小可用的数据管道数据管道很容易被做成黑箱但其实从零开始并不需要复杂调度框架。我用过最简单的模式是这样有一个脚本负责把raw目录下的原始文件读进来做字段重命名、剔除明显异常值、对缺失值做策略性填充然后输出成统一的parquet或csv到processed目录。接着有一个数据校验函数检查每个字段的类型和取值区间如果发现和配置规则不匹配就抛错或写告警日志。这里最容易被忽略的是数据版本。第一次跑数据清洗后你改了清洗规则processed目录被覆盖了但你不会记得每个模型到底用的哪版数据。所以我后来会让每一个处理后的数据文件名带上时间戳和hash值。hash值可以通过对所有数据做一个sha256算出哪怕只改了一个样本文件名就会变。这样训练和评估时都能明确知道自己用的是什么数据才能保证实验可复现。如果数据量不大比如几万条到几十万条就用Pandas或Polars做处理就好不需要上Spark。关键是脚本要有“幂等性”重复跑一遍得到同样的结果。我是用“先清空processed下的同名文件再写入”的方式保证重复执行不会产生叠加数据。3.3 模型封装与上线前的检查模型服务化这一步我会用FastAPI实现最小封装。原因是Python生态好写起来轻而且自带数据校验。先看一个最简示例from fastapi import FastAPI from pydantic import BaseModel app FastAPI() class PredictRequest(BaseModel): user_id: int item_id: int context_feature: float app.post(/predict) def predict(req: PredictRequest): # 这里调用纯函数把请求转成特征向量再调用模型 vec build_features_from_request(req) score model.predict_proba(vec)[0] return {score: score, model_version: current_version}这段代码有几点值得注意。第一输入用Pydantic的BaseModel强校验字段类型不对时FastAPI会直接返回422不会进入模型。第二build_features_from_request是从src/features里导出的公共函数训练和预测必须用的是同一个函数。第三返回值里带上model_version排查问题时可以立刻判断线上打到的是哪个模型。上线前我有一份固定检查清单模型文件是否存在、模型版本号和训练记录是否一致、输入特征是否和特征清单完全一致、接口是否设置超时、异常捕获后是否返回可读错误、日志是否打印了每条请求的关键信息、是否做了最小并发压测。这些检查不用很复杂但至少要写进项目的README里防止上线时手忙脚乱。4. 踩坑记录AI工程最常见的五个问题4.1 离线成绩很好线上成绩稀烂这是几乎每个AI工程都要踩的坑我也没例外。当时我训练数据里有一个字段叫“用户最近一次购买时间”我在做特征工程时把未来一段时间的数据也当成已知信息填进了训练样本这就造成了特征泄漏。离线指标很漂亮但一旦上线拿到的是实时输入根本没有未来信息模型表现自然崩塌。排查这类问题的思路是走一遍完整的样本构建流程确认每一条特征在预测时确实能拿到。我后来会专门写一个“特征可用性检查脚本”模拟线上请求发送到服务端看服务端实际收到的特征分布和训练特征分布是否一致。如果差距明显优先怀疑特征时间窗口、排序逻辑和缺失值填充方式。4.2 特征漂移不知道啥时候发生模型部署之后并不会一劳永逸。用户行为变了节假日带来流量起伏或者某个外部数据源接口改版都会让线上输入分布发生变化。如果只看模型离线指标你根本不会发现问题直到业务反馈说“推荐不如以前准了”。我的做法是对关键特征做“抽检监控”。每次服务模块处理请求时把特征向量追加到一个监控日志。每过一段时间就统计特征均值、缺失率、取值区间和训练时的统计值比较如果波动超过预设阈值就触发告警。这个做起来不复杂但价值极大。它相当于在模型和复杂现实之间装了一个仪表盘让你在坏事发生之前先看到风向变了。4.3 实验记录一团乱说实话很多个人项目和团队项目的最大痛点不是模型不好而是没人记得之前做过什么。你可能跑了一百次实验最后只记住“精度是多少”但记不清“学习率是多少”“用了哪一版数据”“加了哪个特征”。我自己被这个问题折磨过很长一段时间。从零开始最直接的方案是每次训练脚本跑完后自动保存一个实验报告内容包括Git提交号、数据文件名hash、所有超参数、评估指标、模型产物路径。这个报告用JSON格式写入experiments目录名字包含实验的日期和提交号。这样哪怕不引入任何实验管理平台你也能随时回溯。等到团队大了或实验多了再考虑MLflow这类工具。但千万不要在第一步就上一堆平台如果你的基础记录习惯都没养成再好的工具也会变成摆设。4.4 训练任务吃内存机器先崩一个非常日常的坑训练脚本直接把全量csv读进内存然后做特征处理再进行模型训练。数据量小的时候没问题到了几百万行、几十个特征时内存直接爆掉。我后来习惯是先在数据处理阶段就分批读取或者使用支持lazy eval的框架至少要搞清楚数据量和特征类型在内存里大概占多少。还有一类是显存泄漏问题。如果你在循环里反复调用训练并创建新模型旧显存可能不会被及时释放。我的排查习惯是在训练脚本里固定打印显存占用观察每个epoch结束后是否会回落。如果只增不减就要检查是否在循环里保留了大量中间张量。对企业级项目来说这同样是一个不可忽略的稳定性问题。4.5 你说可复现别人跑了不一样我在协作时遇到过“你明明说用了随机种子42为什么我跑出来和你报告对不上”的情况。后来发现依赖包版本不一致就会导致结果差异训练脚本在本地用的pandas是2.x对方环境是1.x处理和排序逻辑稍有不同特征就变了。解决方法很简单用requirements.txt锁定全部依赖版本最好是再提供一个Dockerfile把运行环境也固定下来。训练脚本开头固定随机种子同时把“是否固定随机种子”作为一个配置项。你的目标不是让“任何环境都能复现”而是让“指定环境配合指定数据版本”可以复现。能做到这一点工程能力已经超过大多数项目。5. 工具链和协作方式怎么一步步补齐5.1 单机阶段够用的组合在项目初期我建议工具链尽量精简Python做语言主力Git做代码版本管理venv或Poetry做虚拟环境和依赖管理pytest做关键逻辑测试FastAPI做模型服务。数据版本管理如果不想额外学习可以用“数据文件名带hash”这种土办法先顶住实验记录先用JSON文件顶住调度任务先用cron或手动脚本顶住。这套组合的最大优势是认知成本低。你不会因为学了一堆复杂工具而把主要目标忘掉。很多从零开始做项目的人容易陷入“工具瘫痪”先研究Kubernetes再研究Airflow结果两个月过去了模型还没跑通一次。工程化的确重要但必须排在“主流程跑通”之后。5.2 哪些工具可以先不引入分布式训练一定不要第一版就引入。除非你的数据量已经单机训练跑不动否则分布式训练带来的环境同步、通信开销、调试复杂度绝对会让你怀疑人生。Kubernetes也可以先不上线。个人项目或小团队项目用一台服务器加systemd把API服务拉起来就行稳定性和效率比什么编排都实在。Feature Store这个名词看起来很美但如果你只有两三个特征来源自己维护一个特征定义文件反而更轻。引入一个中间件本质上是把简单的工具问题变成了另一个系统问题。我个人的判断标准很简单不引入这个工具我是不是已经卡住了如果只是觉得“别人都有所以我也要有”那就先不引入。5.3 代码评审与发布规范即便是单兵作战也应该有一种“假装的团队规范”。我给自己的要求是main分支永远可以跑通每次改动至少开一个分支合并前跑一遍tests模型文件不进Git改用Git LFS或外部存储发布服务前打一个tagtag里写清楚对应的代码版本和模型版本。这些规范的意义不是仪式感而是让你在项目变复杂后还能不慌。当你某一天线上服务出问题你需要立刻知道线上跑的是什么版本然后去查对应版本的日志和实验记录。如果这一切都没有排查会变成摸黑找路。我踩过几次夜班上线的坑之后才明白好的发布规范不是为了约束人而是为了在紧急时刻能快速给所有人一个安全感。6. 从零开始后我最重要的一个建议如果只能给一条建议我会说选一个真实但规模可控的问题把链条完整做完胜过去做十个“看起来不难”的半成品。我见过很多人觉得“AI工程”一定要用到大数据平台、分布式训练、微服务才能算有含金量。但实际上能把一个普通规模的分类问题做得可复现、可追溯、可监控已经能完胜大部分项目经验。在做完第一个闭环之后我建议你再回头做两件“不酷但有用”的事第一把整个项目的README写成一份“运维手册”让别人照着就能从零启动第二针对线上最容易坏的两个点各写一个自动化测试把风险前置到代码层面。我个人经验中深刻的一点是——AI工程真正的门槛不在于模型多新而在于你能否控制整个系统的变化。数据在变代码在变模型在变环境也在变谁能把这些变化有序管理起来谁才算真的入了门。
返回列表