尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

AI工程从零开始:从数据链路到模型部署的完整实战指南

AI工程从零开始:从数据链路到模型部署的完整实战指南 如果只说一句AI engineering from scratch 最难的不是模型而是“不知道从哪里下手”。我从第一次跑通手写识别的小 demo到后来在真实业务里上线评论分析接口中间隔的不只是一行 load_dataset而是把数据、训练、部署、监控串起来的一整套工程方法。这篇文章不会有“30天精通”的承诺我会按自己真实走过的路线把为什么选这些工具、每一步该盯什么、以及那些容易让人整晚睡不着的坑都摊开讲清楚。适合已经会基础 Python、能跑通教程却还不敢独立搭项目的人也适合团队里刚接手 AI 工程的新人。1. AI 工程先想清楚你缺的不是模型而是一条能自洽的链路1.1 从“写出模型”到“跑起系统”差距到底在哪我见过不少简历上写着“会训练模型”的人给他们一个真实需求——比如做一个商品评论情感分析接口每天处理几万条文本还要能上线持续运行——立刻就卡住了。卡住的点往往不是不会写 Transformer而是心里默认 AI 工程 模型训练。实际上模型只是整条流水线中间的一环。用开餐厅打比方更直观模型是菜谱数据是食材后厨是数据管道传菜是推理服务食客反馈是监控指标。菜谱再惊艳没有洗菜、切菜、配菜、掌勺、上菜这一整套后厨体系客人是不会满意的。AI 工程从零开始说白了就是先学会经营这个后厨。具体到落地上一个能自洽的链路至少要覆盖四层数据层原始数据怎么存、怎么清洗、怎么标注、怎么版本化。实验层特征怎么造、模型怎么训、指标怎么算、实验怎么记录。服务层模型怎么部署成接口、并发怎么扛、异常怎么兜底。监控层线上指标怎么看、数据漂移怎么发现、模型什么时候该重训。我在最早搭项目时犯过一个典型错误花大量时间在一个小数据集上调模型把准确率从 0.94 提到 0.96非常兴奋。可等我把它放进 API 里才发现接口在压测下响应要 120ms用户刷个页面都要等。调那 0.02 的准确率在真实场景里根本感受不到反而把整体健壮性忽略了。所以我说真正符合“从零开始”的工程观不是把模型调到极致而是先建立起一条能自洽的链路数据可溯源、训练可重复、结果可对比、服务可回滚。这四条比单点准确率稀缺得多。1.2 先把三个容易走窄的念头排掉误区一总觉得要从底层数学推导开始。原理当然值得学但如果目标是要尽早独立交付一个可用的 AI 服务那“公式推导导向”会让你一直停在准备阶段。更好的节奏是由用到理先跑通一个很小的任务跑的过程中遇到瓶颈再回头补相关数学。工程上的学习是需求驱动的强行按“先磨刀再砍柴”来留给你的大概率是刀磨了一年柴一根没砍。误区二什么都想自己造。看到别人用开源框架你非要自己实现一个推理引擎这会占用大量本应投资在业务问题上的精力。真要自己动手创造的应该是业务数据和模型之间那些难以标准化的部分比如清洗逻辑、评估口径、异常处理。这些才会慢慢变成你真正的工程资产而不是重复造轮子。误区三想一步到位。我见过不少人第一周就设计 K8s 集群、监控大盘、多任务调度结果模型都还没跑起来先被运维成本压垮。第一版应该朴素到可以用一条命令从数据跑到 API当你发现这条链路的某个环节总是要人盯着再针对它加自动化。架构是长出来的不是一开始设计出来的。这三点理顺之后再往下看路线和选型你会笃定很多。2. 动手之前先把路线图和工具链定下来2.1 四段式路线从最小闭环到持续迭代我把“从零开始”落地拆成四个阶段每个阶段结束都要有一个“能跑的东西”拿来验收而不是“我学完了某个章节”。阶段一打通最小闭环。用一个小型公开数据集把“读数据 → 训练 → 保存模型 → 加载模型 → 写一个预测函数”完整跑一遍。这段代码可能只有几十行也可能很粗糙但闭环一旦形成后面所有工程复杂度才有承载基础。不要小看这一步很多人真的没耐心做完“保存模型再 load 回来”这件小事跳过去之后后面的选型基本都是空中楼阁。阶段二给脚本上工程骨架。把第一阶段的脚本拆成数据、模型、训练、评估、配置五个模块超参数、数据路径、输出目录全部抽成配置文件。这个阶段开始产生“别人也看得懂”“换数据也能跑”的效果。阶段三做推理服务与自动化。用 FastAPI 或类似轻量框架把训练好的模型包成 HTTP 接口加上基本的请求校验、超时、日志再用 curl 把“从训练到部署”的整个流程串起来。阶段四反馈轮与持续迭代。为线上请求积累日志定时统计数据分布、计算指标当指标下降时触发重训。做到这一步你才算是把 AI 工程自己驱动起来了。这四段路线最大的特点是每步都可以验收。我后来带人入门也是要求他们先按这个顺序做做不完不许聊架构。2.2 工具链怎么选我的取舍标准工具选择不是越火越好而是要看你处于什么阶段。从零开始、可能只有一个人的项目核心诉求是“生态好”“可替换”“能快速跑通”。框架之争不值得花太多时间重要的是每个环节选一个够用的工具并知道它能干什么、不能干什么。我给出一个常用的起步选型表供你参考环节起步用进阶可以换为什么数据清洗与转换pandas / polarsspark / dask单机先把逻辑写对换大数据时要换的不只是工具还有思维模型实验scikit-learn PyTorchLightning / Transformers起步阶段用原生 API保留对训练细节的控制力推理服务化FastAPI ONNX RuntimeTriton / vLLMFastAPI 部署简单ONNX 是避免训练与部署环境不一致的好路径实验与版本记录MLflow甚至一个表格自建记录平台第一步其实只需要面向“未来的你”可复现环境管理conda requirements.txtuv / poetry先把环境锁定再谈优雅这套选型不是所有场景都正确但非常适合同样是从零开始且没有专职运维支持的个人项目。ONNX Runtime 不是必须的如果你只在 GPU 上训练并直接用 PyTorch 做服务很多项目也能跑起来。但一旦你想同时提供 CPU 降级、多语言客户端、批次提速ONNX 的跨平台确定性会让你少掉不少头发。另一个原则能外包给框架的就不要自己重复实现。工具链的意义在于你在设计系统和排布流程时知道哪些环节已经有人替你解决哪些环节只能靠自己。像数据处理管道中的复杂逻辑别人很难照抄但模型推理引擎这种成熟领域值得先信任社区。2.3 容易被忽略的工程基础设施路线上除了主线还有三条容易被忽略的“基础设施”。环境固定。虚拟环境加锁文件是必须的训练结果必须能离线重现。依赖版本不一致是零起步团队返工的一大来源训练机是 CUDA 11.8部署机是 CUDA 12.x实验结果可能悄悄就变了。你可以把 Python 版本、关键库版本、框架配置记成一个“环境指纹”和 checkpoint 放在一起。数据版本。小项目至少把原始数据按日期存比如data/raw/20250601_review.csv不要覆盖同名文件。中大型项目可以引入 DVC 之类工具。数据没有版本一切实验结论都像沙子建塔风一吹就散。实验日志。哪怕只是“日期 数据版本 参数 JSON 指标 JSON”四个文件名也要有。你要具备“三周后还能说清当时为什么这个指标高”的能力。这些基础设施不花钱花的是习惯。养成习惯后你会发现那些看起来复杂的 AI 工程不过是在这堆积木之上叠东西。3. 从零搭一个能上线的 AI 工程我用评论文本分类举例3.1 最小项目结构长什么样如果从零教一个人搭项目我不会一上来端出六层架构而是先给一个最简单的目录sentiment_ai/ ├── configs/ │ └── train.yml ├── data/ │ ├── raw/ │ └── processed/ ├── src/ │ ├── data_prep.py │ ├── train.py │ ├── evaluate.py │ └── serve.py ├── models/ │ └── checkpoints/ └── logs/每个目录职责明确configs把可变参数隔离出来data/raw坚持原始数据不可污染src保持扁平源码方便定位models只放产物logs放训练日志和指标曲线。真正的工程化不是目录数量多而是修改一处不影响另一处。我建议初次动手的人优先选择文本分类作为演练项目因为文本不像图片那样需要大量增强和复杂预处理可以把精力集中在这条工程链路上。以“商品评论情感分类”为例数据就是两列一列是label一列是text绝大多数模型都能直接吃进去。3.2 数据预处理顺序是工程基本功处理数据时最容易忽略的一步是“必须先拆分再进入预处理”。很多人习惯拿到数据先做清洗、特征工程最后才划分训练集和测试集。这在统计上会让测试集偷看到训练阶段的信息是典型的评估泄漏。比如文本向量化时正确做法是把fit限制在训练集上from sklearn.model_selection import train_test_split train_df, test_df train_test_split(df, test_size0.2, random_state42) # 先对训练集拟合再对 train/test 分别 transform vectorizer.fit(train_df[text]) X_train vectorizer.transform(train_df[text]) X_test vectorizer.transform(test_df[text])这段代码看起来极其简单但很多“从零开始”的人写着写着就会变成vectorizer.fit_transform(df[text])放在拆分前面。严格来说这时的测试集已经不是真正的 unseen 数据了。如果后续模型效果高得不正常先怀疑这里再去怀疑模型。3.3 训练脚本里我会坚持的五个检查点写训练脚本时架构可以借鉴开源但下面这五个检查点建议自己盯紧。第一随机种子固定。Python、NumPy、PyTorch 要分别固定同时把num_workers固定下来否则多进程加载也可能带来不确定因素。种子不固定同样的代码跑两遍结果可以不一样。第二损失曲线落盘。每 N 步把 loss 和验证指标写入logs/train.log不要只看最后两个 print。训练过程是否收敛曲线比单点数字可靠得多。第三验证时要用model.eval()。这句话很多人真的会忘。没关 dropout 和 batch norm 的训练行为会让验证 loss 虚高或虚低看起来像玄学其实是状态搞错了。第四设定 checkpoint 策略。每个 epoch 可以保存一份或者至少保存“验证指标最好”和“最新”两份。不然进程崩溃之后你只能重新开始算力之旅。第五早停要和最佳 checkpoint 对应。早停条件触发后要确保你载入的是验证指标最优的那份参数而不是最后一个 epoch 的参数。在脚本里这两者很容易错位。另外一个很务实的建议如果你还在“抄代码”阶段就把日志系统和评估函数写得比模型架构更仔细。模型结构可以抄别人的但日志和评估体现的是你对自己业务判断的把握。3.4 推理接口模型变成服务前的最后一道闸从训练到推理最考验人的是把模型重新封装成服务这一步。以 FastAPI 为例核心是拆分“预处理”和“模型推理”两个阶段并分别打日志from fastapi import FastAPI from pydantic import BaseModel app FastAPI() class Item(BaseModel): text: str app.post(/predict) def predict(item: Item): # step1: 数据校验与预处理 tokens preprocess(item.text) # step2: 模型推理 prob run_model(tokens) return {label: int(prob 0.5), prob: round(float(prob), 4)}这里有两个基本功把输入为 None、超长文本、空白文本都当作异常分支处理模型要全局加载一次而不是每个请求都 load。我踩过最低级的坑就是最初把模型加载放进每个predict函数里单机压测只有 1 个 QPSCPU 先爆了。正确做法是在服务启动阶段加载一次模型常驻内存或显存请求只做预处理和推理。3.5 上线前必须过的五个检查项模型在测试集上再好看都只是开始真正上线前我建议过一遍这个表检查项为什么种子和环境指纹已记录如果之后无法复现线上出问题很难排查预处理和训练时完全一致很多线上效果下降根源是部署时的预处理和训练时不一致模型只加载一次且失败可恢复避免启动即 OOM或者加载失败后无法优雅退出用真实请求做一次压测找到内存、CPU、GPU 的真实瓶颈有日志和指标系统后续评估、告警、发布决策都依赖它这就引出一个观点“从零到一”收尾的标志不是训练出了最好的模型而是你能让一个简单模型在真实访问下稳定运行出问题时有日志可查。4. 从零开始路上我替你踩过的几个坑4.1 最隐蔽的元凶数据泄漏我印象很深的一次情感分类项目的测试 AUC 到了 0.97明显高得不正常。排查到半夜发现原因是我在做分词过滤时用整个数据集去构建了一个词表train 和 test 共用同一个词表测试集信息已经被模型提前看到了。这种泄漏对新手最坑的地方在于它不报错反而让你的效果展示非常好让人误以为自己是调参天才。修复之后指标掉回 0.86那才是真实水平。同类陷阱还有全量归一化、全量缺失值填充、全量目标编码。凡是用到整个数据集信息的预处理都要在 train/test 拆分之后做。这是一个值得刻在工位上的原则。4.2 训练时很好上线后一塌糊涂这多半不是玄学通常是三种原因之一。第一线上预处理和训练不一致。训练时写了strip().lower()部署时为了省事少了一步或者中文分词器版本换了文本切分结果随之变化效果直接崩。第二推理状态错误。该用model.eval()的时候忘了关 dropout把随机性带进了线上结果。这类问题很常见排查方法就是固定一个种子把你训练时的预处理输出和线上打印出来的特征做对比。第三线上输入分布偏移。真用户的输入往往比测试集更脏、更口语导致特征分布漂移。解决方式不是祈祷而是从上线第一天就保存推理日志定期和训练数据分布做对比再针对差异补样本。4.3 环境不同结果对不上同一段代码训练机是 GPU部署机是 CPU中间还做了 ONNX 转换精度可能从 99.2% 掉到 98.6%。这不是算子 bug而是浮点计算顺序不同导致的微小差异。应对方式很明确转换之后在完整测试集上跑一遍对比原模型输出最大误差控制在 1e-4 量级同时记录 tokenizer 版本因为分词库升级后词表会变。很多人以为这是大公司才会遇到的问题其实个人项目换个云端环境就出现结果不一样大概率也是环境没有完整复刻。建议把conda env export或 requirements-lock 文件和 checkpoint 放在一起保存。这种习惯一开始不值钱等你撞过一次墙就知道值钱了。4.4 没有实验记录等于白跑从零开始的项目里最贵的是算力和调试时间。没有记录两周后回头看自己调过什么只剩下一堆 git 碎片。随手记一行“dataset V2 max_len 256 lr 3e-5”都是赚的。我更推荐建一个experiments/目录每个文件夹里放 config、指标日志和一句备注。三个月后你还能立刻说清为什么当时选中这个方案。这件事的价值在早期看不到要等开始二次迭代时才爆发——我见过太多人调了一堆参最后根本不知道哪个组合最有效只能重新再来一遍那才是真的浪费。5. 如果回头我会把“可复现的可靠基线”放在首位5.1 用基线奠定整个项目的可比性在把模型调到最好之前先用一个标准模型加默认超参数做出一个“不丢人”的基线并把产物保存好。随后的每次改动只围绕一个变量变化。我知道这听起来很无聊但从零开始的人最容易犯的错恰恰是在链路还没闭环时试图同时解决“准确率不高”“推理慢”“接口不好看”三个问题结果全崩。基线的存在就是用来区分“预期会变”和“回退版本”。没有它所有实验判断都是自欺。5.2 完整优先于漂亮先能跑再优化在真实项目里我看到最多人卡住的原因是今天在纠结用什么框架、怎么封装才叫工程化。“能不能跑”和“跑得好”根本是两个阶段应该有时间界线。我给自己立的规则是第一阶段最多花三天三天内必须做到 train、serve、curl 全通。不管模型多普通。因为项目的掌控感来自完整可见而不是局部精雕。先接受一个粗糙但完整的环节再让它在一次次迭代里变漂亮。跳过完整闭环谈优化大概率是空中楼阁。5.3 我每次从零开始都要回答的五个问题最后分享一个我坚持了很久的小习惯项目启动当天把下面五个问题写在笔记最上面每完成一个版本就重新答一遍。原始数据存在哪能不能随时回滚如果现在关机或中断这个项目还能继续吗从一条线上请求到预测返回中间哪个环节是手写的、最不可靠这些指标是怎么算出来的只看日志未来的我能不能理解如果效果下降我第一步会去看哪个日志这五个问题帮我挡掉了很多虚无的焦虑。很多新人的问题不是能力不够而是没有一条清晰、可检验的链路兜底。如果你能把前面这些经验带进第一个项目那你从零开始的起点大概率比我当年要高不少。
返回列表