ARTICLE · INTELLIGENCE

战地情报 · 详情页

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

AI工程从零到部署:完整实践指南与踩坑记录

AI工程从零到部署:完整实践指南与踩坑记录 老实说《ai-engineering-from-scratch》这个标题看起来像是一个短期突击计划但真正把它做完之后我觉得它更像一面镜子——照出一个新手在AI工程这条路上一路踩坑、填坑、重新爬起来的全过程。过去大半年我基本就是这个状态零基础起步不靠现成的高层封装一步步把数据、模型、训练、评估、部署、监控这些环节整个过了一遍。现在回头看最值钱的东西反而不是跑了多少模型而是那些失败和调试经历帮我建立的底层判断力。这篇文章我尽量把整个过程还原出来包括思路、实操、踩过的坑和沉淀下来的方法希望能给也想走这条路的人一些参考。这篇文章适合谁如果你正处在装好环境但不知道下一步干什么的阶段或者已经能跑通demo但总觉得理解很浅那这篇文章应该对你有用。我会从AI工程到底是什么讲起再拆解具体的实操路线最后把我遇到的典型问题和解决方案整理成速查表。不绕弯子都是实际跑过的经验。1. 先搞清楚AI工程到底在做什么很多人提起AI工程第一反应就是用Python调一下模型接口。这确实是最外层的形态但工程实践远不止这一步。我得先把这个概念剥开看它底下到底压着哪些东西因为理解不对后面所有的学习路径都会跟着歪掉。1.1 AI工程不是调接口那么简单如果只调用现成API你实际接触的不过是AI系统的最后一层。真正的AI工程至少包含数据采集与清洗、特征处理、模型选型、训练调优、推理优化、服务部署、监控告警、迭代更新这几个环节。每一个环节单独拿出来都可以写一本书但工程实践要求的是把它们串成一个闭环。我从零开始折腾第一个完整项目时才发现一个残酷现实模型训练只占整个时间的大概三成剩下的时间几乎都花在数据处理、环境配置、调试问题和服务化封装上。提示这个比例不是随口说的是我几个项目实测下来的平均结果。如果你也在AI工程入门建议先做好心理预期——不是只有训练才叫AI工程前面后面那些脏活累活才是工程的主体。我把AI工程拆成四个轮子数据、模型、算力、评估。数据对应输入质量模型对应算法能力算力对应资源边界评估对应质量底线。任何一个轮子扁平了整个系统就跑不顺畅。新手最容易犯的错就是把所有精力押在模型上结果数据一团糟、评估靠肉眼最后模型上线了根本不敢用。1.2 为什么强调from scratchfrom scratch不是要求你从反向传播手写一个Transformer才算数——当然如果你想深挖底层手写是很好的练习——但它确实强调一个核心态度不要只满足于能用要明白为什么这么用。举个例子。用Hugging Face的pipeline接口加载一个BERT模型做情感分类三行代码就跑通了。但如果你的任务不是通用的情感分类而是某个特定行业里很冷门的文本判断任务你会发现预训练模型直接迁移的效果常常不理想。这时候如果你懂数据采样、tokenizer词表扩展、微调策略这几个底层环节你就知道问题出在哪词表里没有领域词汇、数据分布和预训练语料差异过大、超参数没有适配任务规模。这就是from scratch的价值——它培养的是排查链路问题的能力而不是复制Demo的能力。我自己实践下来如果只看教程跟着跑确实很快但只要环境和数据稍作变化马上就会卡住。而一旦卡住才是学习真正开始的地方。2. 从零开始的第一步环境与工具链搭建标题里既然写了from scratch那环境搭建自然是绕不开的苦功。这一节我把我的搭建过程和遇到的取舍写清楚特别是那些别人很少提但很重要的细节。2.1 硬件配置与开发环境的取舍我入手时没有太多预算手头只有一台普通游戏本CPU是i7-12700HGPU是RTX 3060 Laptop6GB显存内存32GB。在此之前我一度觉得没A100就学不了AI但事实是6GB显存在入门阶段完全够用前提是你得知道怎么在资源边界内工作。如果你有独立显卡哪怕显存只有4GB也建议优先把CUDA环境配好因为很多调试问题只有在真实GPU上跑过才会理解。如果是纯CPU环境照样能学但推理和训练的速度会慢到令人崩溃所以我建议至少有一块支持CUDA的NVIDIA显卡显存6GB以上是舒适区。开发环境方面我强烈建议用LinuxUbuntu 22.04 LTS原因不是Windows跑不了而是大量模型仓库、部署脚本和运维工具都是优先支持Linux的。如果你只有Windows机器也不用急着装双系统Windows Subsystem for LinuxWSL2是一个很实用的过渡方案CUDA支持也成熟了。我是直接在Windows上用的WSL2日常开发体验和Linux基本一致。2.2 版本匹配问题Python、CUDA、PyTorch三方博弈整个入门过程中最折磨我的不是模型不是算法是版本匹配。PyTorch、CUDA、Python三者的对应关系非常严格。装错一版要么检测不到GPU要么运行时报错要么性能莫名其妙下降。我的建议是不要追求最新要追求稳定组合。我目前在用的一个稳定组合供参考组件版本选择说明Ubuntu22.04 LTS长期支持官方文档示例最多的版本Python3.10绝大多数库兼容性最好的版本CUDA11.8或12.1看PyTorch官方对应表PyTorch2.x稳定版安装时用官方命令生成器选对应版本cuDNN与CUDA配套用pip安装时一般自动处理我踩过最惨的一次坑是直接把CUDA升级到12.4结果项目里一个老版本的TensorFlow直接无法调用GPU最后只能花时间重建环境。从那以后我立了一个规矩——任何大版本升级前先看依赖库的官方兼容性声明再决定动还是不动。还有一个实用习惯每个项目开一个独立的Python虚拟环境。我用的是conda因为它在管理Python版本和CUDA依赖方面更顺手。每开一个项目第一件事就是conda create -n project_name python3.10然后在虚拟环境里装依赖。这样项目之间互不污染即使某个环境被我搞坏了也不会影响其他项目。3. 从零到第一个AI模型完整实操路径环境准备好以后就可以开始正经的项目实操。我把路径拆成三个阶段跑通推理、训练小模型、服务化封装。每阶段都卡过也都有值得说的细节。3.1 第一步跑通一个开源模型的推理我选的首个模型是bert-base-chinese——一个中文预训练模型用途是文本分类。选择它的理由很朴素体量小约110M参数推理快能在CPU上跑且中文生态资料多出了问题好排查。跑通推理之前先要理解tokenizer和模型输入的套路。文本不能直接以字符串形式喂给模型必须经过tokenizer转成张量。这个转换过程包含分词、切subword、加special token、截断或padding。很多新手上来就报错dimension mismatch多半就是没搞明白padding和truncation的作用。我的实操流程是用Hugging Face加载模型和tokenizer对输入文本做tokenization得到input_ids、attention_mask把张量喂给模型拿到logits对logits做softmax得到概率分布按索引取标签代码如下from transformers import AutoTokenizer, AutoModelForSequenceClassification import torch model_name bert-base-chinese tokenizer AutoTokenizer.from_pretrained(model_name) model AutoModelForSequenceClassification.from_pretrained(model_name, num_labels2) text 这家餐厅的菜很好吃但服务态度一般。 inputs tokenizer(text, truncationTrue, paddingTrue, return_tensorspt) with torch.no_grad(): outputs model(**inputs) logits outputs.logits probs torch.softmax(logits, dim-1) print(probs)看起来简单但这一步卡住我的点在于很多人会忽略return_tensorspt这个参数。如果不加返回的是Python列表没法直接喂给PyTorch模型。这种细节教程里不一定提但实际跑的时候都会遇到。跑通这一步之后我对模型推理建立了完整的直觉模型不神秘它就是一个接收张量、输出张量的函数tokenizer是文本与张量之间的桥梁。3.2 第二步自己训练一个文本分类小模型跑通别人的模型只是热身真正价值出现在自己动手微调模型。我做的第一个训练项目是一个酒店评论情感分类任务正负样本各两千条。数据是从一个公开的评论集里整理出来的先做了去重、清理特殊字符、统一格式然后按7:2:1切分为训练集、验证集、测试集。训练代码的核心骨架from datasets import Dataset from transformers import TrainingArguments, Trainer train_dataset Dataset.from_pandas(train_df) val_dataset Dataset.from_pandas(val_df) training_args TrainingArguments( output_dir./results, evaluation_strategyepoch, save_strategyepoch, learning_rate2e-5, per_device_train_batch_size16, per_device_eval_batch_size32, num_train_epochs3, weight_decay0.01, logging_dir./logs, ) trainer Trainer( modelmodel, argstraining_args, train_datasettrain_dataset, eval_datasetval_dataset, ) trainer.train()这里有个关键问题learning_rate2e-5这个值不是随便拍的。微调预训练模型时学习率通常要比从头训练小一个量级因为预训练权重已经学到了通用特征学率太大会导致灾难性遗忘——模型把之前学到的通用知识全忘掉。我一开始试过1e-4明显看到验证集loss剧烈波动改回2e-5以后才稳定下降。另一个容易被忽略的参数是evaluation_strategyepoch它代表每个训练epoch结束后用验证集评估一次。千万不要只在所有训练结束后评估一次那样中间出现的问题没法及时发现。我后来甚至把日志频率调到每一步都记录loss可以实时观察训练状态。训练结束以后我做了两件新手常忽略的事用专门的测试集做最终评估以及把模型保存到本地并用它对真实样本做推理验证。测试集的作用是模拟模型在没见过的数据上的表现这才是真实场景下的效果而不是训练集上的自我感觉良好。3.3 第三步把模型封装成一个可用服务训练完模型之后下一步是部署。如果不做这一步模型永远只是笔记本里的一个产物不算一个工程。我用FastAPI封装成了RESTful接口代码很简单from fastapi import FastAPI, Request from pydantic import BaseModel from transformers import pipeline app FastAPI() classifier pipeline(text-classification, model./results/checkpoint-xxx) class Item(BaseModel): text: str app.post(/predict) async def predict(item: Item): result classifier(item.text)[0] return {label: result[label], score: result[score]}这里最需要注意的是模型加载时机。如果每次请求都加载一次模型服务和死掉差不多。正确的做法是在服务启动时把模型加载进内存然后接口只做推理。这个细节虽然小但我在头几次部署时确实犯过——在接口函数里加载模型结果第一个请求花了十几秒后面的请求报告内存一直在涨。服务化还有一个现实问题并发。如果只处理单条请求用上面的写法完全够用。但一旦有并发请求就需要考虑批量推理、异步任务队列、负载均衡。入门阶段建议先把单服务跑通理解整个调用链路再考虑更复杂的架构。4. 从零学习中最容易踩的坑承诺一下这一节的东西几乎都是我从实际报错和反复排查里攒下来的比看十篇教程都有用。我按出现频率排了序。4.1 版本地狱库、驱动、系统的三角关系这个问题几乎人人会碰。我的经历是这样的某次升级依赖之后PyTorch开始报CUDA error: no kernel image is available for execution on the device查了半天发现是CUDA版本和显卡驱动不匹配。报错信息大概率原因解决方向CUDA error: no kernel image...CUDA版本和驱动不匹配查驱动支持的CUDA版本重装对应CUDA或PyTorchlibcudnn.so.8: cannot open shared object filecuDNN缺失或版本不对在conda环境里安装匹配的cuDNNRuntimeError: CUDA out of memory显存不足减小batch size、用梯度累积、换半精度Expected all tensors to be on the same device张量在CPU/GPU混用显式调用.to(device)统一设备ImportError: cannot import name x from transformerstransformers版本过旧升级或按官方文档回退到指定版本建议所有初学者建立起一个意识每一次环境变动都做记录。我后来把所有依赖写进requirements.txt而且固定版本号不写范围版本这样环境才能可复现。尤其是多人协作或者隔一段时间重新跑项目时这个习惯能救人一命。4.2 数据问题比模型问题更致命而且更隐蔽我在训练第一个模型时犯过一个很经典的错误数据没做shuffle。数据里前2000条都是正样本后2000条都是负样本训练时模型先大量看到正样本loss震荡很厉害最后模型严重偏向预测正类。后来我总结出一个规律数据层面出问题往往模型输出的整体表现看起来还行但细看各类别的指标就会露馅。比如准确率看着有85%一看precision和recall负类的recall只有30%——这样的模型上线之后就只能不停地误判。处理数据的基本流程我整理为五步去重去掉完全重复或近似重复的文本清洗去掉HTML标签、噪声符号、乱码字符标准化统一大小写中文不需要、统一全半角符号标签校验检查标签分布是否有错标漏标划分按类别分层采样确保训练、验证、测试集中各类别比例一致这五步看着简单实际做下来每一步都需要亲手写代码检查我甚至专门写了几个小函数来统计标签分布、文本长度分布、重复率确保数据的每个维度都心里有数。4.3 显存不够时的几种现实解法6GB显存做小模型微调还算够用但稍微模型大一点就尴尬了。我刚入门时想试试更大的模型结果直接被OOMOut Of Memory劝退。后来学会了几招现在整理出来方法思路代价减小batch size一次喂给模型更少样本训练变慢需配合梯度累积梯度累积每N步更新一次参数训练时间变长混合精度训练用FP16代替FP32精度略降但训练加速模型量化用INT8/INT4代替FP16精度损失需验证清理缓存torch.cuda.empty_cache()在中间步骤清理治标不治本我用的最顺手的方案是batch size设为8梯度累积步数设为2这样等效到16的批量大小显存却不爆。如果你也想优化显存第一条先改batch size很多情况下就能解决。注意显存不够不要急着买新显卡先把batch size降下来试试。很多人习惯网上抄一个batch size值就直接跑跑挂了还以为是模型问题。实际上batch size是训练里最不敏感的参数量级只要不是太大了对效果影响有限。5. 我沉淀下来的几条经验法则最后分享几个经过反复验证的经验不是套话是每个决策背后都有实际教训才总结出来的。5.1 用项目驱动代替漫无目的地刷课说真的AI工程这个方向信息量太密如果没有明确目标很容易陷入看视频一时爽合上电脑全忘光的假学习循环。我的建议是一开始就定一个可以交付的项目哪怕是做一个小模型走完整条链路——数据、训练、评估、部署——做完一遍之后再去补理论你会发现自己吸收知识的效率完全不同。我见过不少朋友花两三个月看深度学习理论课代码一行没写最后让他跑个模型完全懵。反过来先动手把流程跑通再回头补为什么梯度下降收敛为什么激活函数要选ReLU这些理论理解深度完全不一样因为你有真实体验打底了。第一个项目不要选太复杂。我最推荐的入门项目是中文文本分类数据好找模型不重链路完整踩坑密度刚刚好。图像方向可以选CIFAR-10做分类但环境依赖要多一些上手成本更高。如果能坚持做完一个文本分类项目AI工程的基础框架就有了。5.2 记录实验日志养成工程习惯刚开始训练模型时我试了很多组超参数但全凭印象过一周之后根本想不起来哪组参数好、为什么好。吃了不少亏之后我建了一个实验记录表每跑一次实验记一行实验编号与目标数据集版本与切分方式模型结构与参数数量超参数配置学习率、batch size、epochs等训练结果loss曲线、评估指标复现命令或代码位置备注当时的异常表现偶然发现这个习惯的收益是滚雪球式的。三个月以后我翻记录还能准确知道当时某个实验为什么失败哪些参数组合值得延续。这比任何AI辅助调参工具都更贴近工程实践本质——可复现、可追溯、可信赖。5.3 学习节奏与心态建议AI工程是一条很长很长的路入门期45天到90天不等取决于基础和时间投入。如果之前写过Python过程会顺一大截如果完全零编程基础建议先花一两周熟悉Python语法和基础库再正式进入AI工程环节。心态上最重要的一个建议报错不是灾难而是线索。我最早遇到报错就想放弃后来逐渐把它当成排查游戏。每个报错都在告诉你系统内部某个环节的状态顺着报错追下去你会对框架的理解越来越深。到现在遇见没见过的报错我甚至有点兴奋——因为我知道解决它之后认知又会提升一截。最后一个小建议多在社区写分享、看别人的提问。我很多经验其实来源于帮别人排查问题普适性极强的问题往往意味着很多人都会踩。试着答一道新区块链跑出来的问题你就知道自己是否真正理解了一个知识点。从零开始这条路不短但它带来的底层判断力是不可替代的。前期慢一点没关系持续跑总会到达自己最初想去的地方。
RELATED READING

延伸阅读

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