ARTICLE DETAIL

资讯详情

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

从零开始学AI工程:环境搭建到部署监控的完整实践

从零开始学AI工程:环境搭建到部署监控的完整实践 做AI工程这一年多我把市面上能踩的坑基本都踩了一遍。从最开始只会调包跑模型到最后能独立把一套应用端到端落地中间没有哪一步是可以跳过去的。今天就拿我自己的学习路线当例子聊聊一个完全没有工程背景的人是怎么从零开始把AI工程这条线啃下来的。我默认你会Python基础但没做过真正的工程化项目。这篇内容会带你拆解AI工程到底是什么、为什么不能直接上手框架就完事、环境怎么搭、数据怎么管、训练和推理怎么权衡、上线之后怎么监控。每一个环节我都配了实际操作和参数选择逻辑你可以照着跑一遍也可以只挑自己缺的部分看。1. AI工程到底是什么它和调模型是两码事1.1 解决的不是能不能跑而是能不能一直跑很多人对AI工程有个误解觉得学会用transformers加载一个预训练模型能出结果就算入门了。但真的入职或者做项目之后你会发现能跑通一个demo和能支撑一个产品是两回事。AI工程的核心不是模型本身而是围绕模型构建的一整套系统数据怎么流转、训练资源怎么调配、推理服务怎么保证延迟和吞吐、效果怎么持续评估、出问题了怎么排查。我经常打的一个比方是算法研究像做一道拿手菜AI工程像开一家餐厅。菜谱可以固定但餐厅要解决供应链、后厨动线、出餐速度、食品安全、顾客反馈任何一个环节断了店就开不下去。从scratch学AI工程其实就是先别急着碰高端框架而是先把餐厅运营这些基础能力补上。你不需要从底层矩阵乘法手写一遍但你需要知道数据从哪来、模型怎么训练、推理怎么部署、线上怎么评估这几件事的先后顺序和依赖关系。1.2 适合谁看看完能得到什么这套内容适合几类人刚入行想做AI应用开发的同学在算法岗想往工程方向转的工程师以及团队里需要一个人把模型落地成服务的全栈型角色。看完之后你至少能回答这几个问题训练环境和推理环境为什么必须分开、数据版本管理解决什么痛点、Flask/FastAPI部署和真正的推理服务差在哪、为什么上线之后准确率会变、日志和追踪到底记录什么。我不会给你很多劝退压力但有一点提前说清楚AI工程的知识点很碎涉及容器、网络、存储、模型、前后端多个领域靠看文章是看不完的。最好的方式是搭一个最小闭环比如本地把一个模型封装成服务再接入监控之后每一步都在这条链路上加东西。我踩过的大多数坑都是在真实链路里暴露出来的光看文档根本发现不了。2. 从零起步先把开发环境和依赖管理理清楚2.1 为什么第一步是虚拟环境不是选框架我知道很多人会急着选PyTorch还是TensorFlow或者纠结用LangChain还是直接用原生接口。但以我实际带人的经验第一步应该解决的是环境隔离。AI项目依赖极其容易冲突你手上同时有好几个项目一个要numpy1.x一个要numpy2.x不隔离开几分钟就崩。我一开始图省事把所有包都装在全局结果某天升级了一个库另一个项目的推理结果静默变了排查了两天才发现是版本冲突。所以在任何项目动工前先建虚拟环境。我个人习惯用conda管理Python版本和底层库再用venv做项目级隔离。conda的好处在于能处理一些非Python的系统依赖比如cuda相关的包venv轻量、跟项目走更干净。新建环境时顺手把Python版本定死不要用默认的AI场景建议3.10或3.11太老的新库不支持太新的有些算子还没编译好。2.2 依赖锁定的实操细节环境建好之后第一件事不是pip install越大而全越好而是需要的时候再装。但有一个例外——先把requirements.txt或pyproject.toml约定好。你每装一个包都要把它记录进去。我见过太多项目代码能跑但你问他是哪几个版本说不清。等到换机器部署跑不起来才开始赌版本极其痛苦。好一点的实践是用pip-tools或uv这类工具做依赖锁定。pip freeze虽然直接但会把所有传递依赖都锁进去升级一个包动不动牵连一片。用pip-compile的话你只需要维护requirements.in里直接依赖的版本上限它自动把完整的锁定关系编译出来。比如你定义了transformers4.40它会依据当前的解析结果锁定到具体某个4.4x版本并且连带锁住tokenizers、safetensors这些子依赖。这样项目在任何机器上重建环境行为都是一致的。2.3 CUDA和GPU环境的判断做AI工程早晚要碰GPU。如果你本机没有NVIDIA显卡或显存不够我见过8G显存跑7B模型直接爆的不要硬刚直接用云GPU实例或者Colab先练手。问题在于GPU环境最大的坑是驱动、CUDA、PyTorch三者的版本匹配。你看着PyTorch官网一条命令装上去了但不能用多半是CUDA版本没对上。快速检查办法先跑nvidia-smi看驱动的Driver版本和支持的最高CUDA版本再跑python -c import torch; print(torch.version.cuda)看PyTorch实际编译用的CUDA版本两者不需要完全一致PyTorch自带的CUDA runtime可以低于驱动支持的最高版本但不能反过来。我踩过一次是驱动太老PyTorch编译的是CUDA 12.1结果所有模型加载都报sm_90 not compatible老老实实退了驱动才解决。新手别去折腾源码编译CUDA算子先保证官方wheel能跑起来比什么都重要。3. 数据先行没有干净的数据模型就是空中楼阁3.1 数据链路设计比模型选型更优先项目里最容易犯的错是一上来就微调模型数据用临时脚本随便处理一下。我不止一次因为数据问题返工训练时loss降得很好一上真实场景效果稀烂。后来复盘发现是训练集分布和线上分布严重不一致比如训练数据里用户输入都是规规矩矩的句子但线上用户输入全是口语和错别字。AI工程里的数据处理核心是建立一条可回放、可追踪的链路。原始数据、清洗脚本、清洗后数据、样本切分、增强策略每一环都要能追溯。我会在项目开始就定义一个数据目录结构raw、interim、processed分别放原始、中间、最终的数据再配一个DATA_README.md记录每份数据的来源、采集时间、清洗规则。听起来像文档功夫但在你调试模型效果异常的时候能省下大量时间。3.2 数据版本管理怎么落地数据集和代码一样需要版本管理但Git对大文件不友好。我试过几种方案DVC最常用它不直接存数据而是存数据的元信息和远程存储地址用的时候拉取对应版本。如果你的团队用云对象存储DVC配S3或者OSS很顺。另一个更轻量的办法是按日期加commit hash给数据目录命名比如data_20250112_a3f2c1配合一个映射表记录每个数据版本对应的代码版本。这个方法没有额外工具成本但需要团队自律我小项目一般用这个大到多人协作我才会引入DVC。清洗和增强要注意一个原则每一步都要可逆或至少可对比。不要在图省事的心态下直接把整份数据覆盖哪怕你心里认为清洗逻辑肯定不会错。我用过一个sentencepiece做中文分词运行时报OOV排查了两小时发现是之前的清洗脚本把部分标点转成了全角分词器词表里只有半角标点。这种问题只有保留中间产物才能快速定位。3.3 样本切分和评估集的学问很多教程会告诉你训练集、验证集、测试集七二一切分但在真实AI工程里更关键的是时间切分和分布切分。如果你的数据是按时间顺序产生的比如日志、用户行为乱序随机切分会让模型偷偷看到未来的数据线下指标虚高上线就现原形。我处理这类数据时严格按时间切窗口前70%训练中间15%验证最后15%测试。如果数据来自多个渠道或场景还要保证每个场景在三个集合里都有代表防止模型只在某类数据上有效。另外测试集和验证集不要用同一份也不要让它们在清洗、增强期间被模型以任何形式看到。听起来是常识但实际操作中因为复用处理脚本导致测试集泄漏的情况我见过太多。泄漏的直接后果是线上评估完全失真你自信满满地上线然后用户告诉你效果很差。4. 模型训练效果和容错的平衡4.1 从预训练模型出发的微调管线纯从零训练一个大模型的成本不是个人能承担的现在讲from scratch更多指把自己的工程链路从零搭起来而不是从随机权重开始训。我用得最多的路径是拿开源底座模型比如Qwen、Llama的指令版本做微调。微调前先确认三件事基座许可证允许商用、中文能力满足场景基础要求、显存或预算能支撑推理和训练的最低配置。微调工具有很多很多新手一上来就上全量微调折腾LoRA的时候又觉得效果不够好。我的建议是小规模数据几千条先试LoRA或QLoRA这样显存占用低、训练快而且不容易灾难性遗忘。如果你有几十万条高质量数据并且算力充足再考虑全量微调。LoRA的关键参数是秩rank我一般从8开始尝试在验证集上对比效果如果欠拟合再往上加到16或32并不建议一开始就追求高秩那是拿稳定性换表达力。4.2 训练脚本里的关键设置训练过程中最容易被忽略的是随机种子和梯度累积步数。固定种子比如42是为了实验结果可复现虽然GPU算子有一些不确定性但至少能把变化控制在可理解范围内。梯度累积是为了弥补batch size太小的问题比如你想模拟batch size为32的效果但显存只够8那就累积4步再更新一次参数。这个参数直接影响训练稳定性别把梯度累积和batch size之间的关系搞错——累积步数等于等效batch size除以单步batch size。另外不管用什么框架训练日志必须包含每一轮的loss、学习率、显存占用和吞吐量。我习惯把日志直接输出成结构化的JSON行每一行记录step、loss、lr、gpu_mem_mb、tokens_per_sec等字段。后续无论是画曲线还是定位某一步崩溃都有据可查。别只printprint的信息分散且难以二次分析。4.3 训练中途崩了怎么办AI训练崩溃非常常见最常见的几种显存不够、数据加载异常、loss变成NaN。显存不够就调小batch size或开梯度累积。loss变NaN先检查学习率是不是太高其次检查数据里有没有无穷值或缺失值特别是做文本特征时某些字段为空没处理干净会导致embedding结果异常。我遇到过一次很有意思的崩溃不是训练代码的问题是数据量太大把磁盘写满了checkpoint保存失败后继续训练模型输出全乱。后来我在训练脚本里加了磁盘余量检测低于阈值提前终止并告警这类问题就再也没遇到过。5. 推理与部署把模型变成真正的服务5.1 FastAPI封装和性能参数选择模型训练好之后下一步是部署。最基础的方案是FastAPI写一个HTTP接口内部加载模型接请求、跑推理、返回结果。单机demo这么玩没问题但如果你想上生产有几个细节必须调整。首先是模型加载方式。默认情况下每次启动都会从磁盘加载模型几百MB到几个GB的权重加载一次可能几十秒。生产环境建议用内存或共享缓存方式预热进程启动后立即加载模型而不是首个请求来了才加载。其次是并发控制GPU推理时显存是共享的来的请求太多同时跑会互相挤占。最简单有效的方式是设置一个信号量或队列控制同一时刻推理的并发数。我一般按显存余量动态算上限比如单卡24G模型本身占用14G每个请求峰值约4G那上限就是2个并发留一点余量。5.2 流式输出和长文本请求的处理如果做的是对话或生成类应用流式输出几乎是刚需。客户端希望看到字一个一个蹦出来而不是等十几秒拿到整段。FastAPI可以用StreamingResponse内部把模型的token逐个yield出去。这里有一个隐藏问题如果生成过程很长客户端的连接很可能断开接口层要能感知并停止生成否则后台一直在跑无效推理。处理长文本请求时输入长度受模型上下文窗口限制。超长输入要么截断要么做滑动窗口或摘要压缩。截断策略也有讲究不要只截尾部很多关键信息在中间或结尾。我一般会把文本按结构分段头尾保留、中间用抽取式摘要压缩再拼接成可接受的长度。这个策略比粗暴截断在真实效果上强不少。5.3 Docker镜像打包和依赖瘦身部署环节里Docker是绕不开的。一个典型的问题是镜像体积过大基础镜像PythonCUDA库模型依赖动辄四五个GB。体积大不仅占磁盘拉取和启动也慢。我的经验是分阶段构建第一阶段装完整依赖和编译工具第二阶段只拷贝编译好的包和代码。另外优先使用官方精简镜像比如python:3.11-slim作为基础层CUDA相关的库如果推理时不需要就不装。还有一个容易忽视的点——镜像内不要打包模型权重文件模型体积动不动几个GB应该通过挂载外部存储或初始化容器专门下载。这样代码版本更新时不用重新拉几个GB的镜像。6. 上线前的效果评估与回归测试6.1 评估集、人工评测和线上AB模型在测试集上指标好看不代表线上就能直接上。我的习惯是至少做三层评估第一层是自动化指标BLEU、ROUGE、准确率等快速判断有没有崩第二层是人工评测随机抽几百条真实输入按多个维度打分判断体验是否达标第三层是灰度上线后做AB对比把小流量切到新模型上和旧版本比线上指标和用户反馈。人工评测模板是我从几个项目里迭代出来的。每条样本至少标注回答是否相关、是否流畅、是否有有害内容、是否遵从指令。用打分制1到5分不直接用好/坏因为两边模型差异很细微时二值标注根本看不出差别。同时标注人不要只看模型答案也要看参考答案和输入上下文没有上下文的人工标注等于是在评判幻觉。6.2 防止静默回归——回归测试集的重要性AI模型每次更新最怕的是东边修好西边坏。老版本能答对的问题新版本突然答错了而且自动化指标整体还是涨的因为可能新版本在另一类问题上变得更强把平均值拉高了。这时候你需要一个固定的回归测试集里面包含历史上出过问题的样本、边界样本和典型用户输入。每次更新模型或改prompt先跑一遍回归测试集和上一次结果做diff。如果回归测试集里出现明显的badcase就要谨慎评估是否上线。我维护回归测试集的方式很简单但有效每次线上出现badcase我都会把它追加到回归集里并写一行说明这个case是哪个版本、哪个场景暴露的。半年下来回归集可能就几百条但它承载了你对模型的所有历史认知比任何花哨的评估框架都实用。7. 观测与监控上线只是开始7.1 日志里要埋哪些关键信息AI服务上线后最容易出的问题是线上效果和预期不符但你又不知道是哪一环出的问题。模型输入被改了吗前处理逻辑变了吗请求数据分布变了吗要回答这些问题日志必须记录下来。我的最低标准是每条推理请求都记下输入原文、输出结果、模型版本、prompt版本、耗时、token用量、输入长度、输出长度。不需要全量存但至少按比例采样比如10%配合全量错误日志才能在事后做归因。日志推荐直接输出为JSON格式每个字段名固定打点统一到同一个Logger里。这样后面接日志采集甚至搭建简单的检索分析系统时成本都很低。另一个细节是加trace_id每次请求生成一个唯一ID前后端联调时直接拿这个ID查全链路日志省去各种你那边报了什么错的扯皮。7.2 指标监控和告警阈值怎么定监控不仅看服务器资源CPU、内存、显存更要看业务指标。生成类服务至少应该盯这几个请求量、P95/P99延迟、token输出速率、错误率、队列积压量、平均输出长度。延迟和输出长度强相关所以只看P95延迟不全面最好同时看每token延迟。如果P99延迟突然飙高通常不是模型本身变慢而是并发排队或GPU利用率打满。告警阈值不要拍脑袋要根据历史基线定。比如过去两周的平均错误率是0.5%方差很小那阈值设在1%或2%就合理。如果阈值设得太紧比如比基线低一半你会被大量误报骚扰到无感最后真出了问题反而不看了。我知道做告警很容易犯完美主义但告警的价值是让值班的人在人少的时间段快速决策宁可少而精不要多而烂。7.3 长尾问题的排查套路线上效果差最经典的排查路径是先确认输入和线上日志里记录的输入是否一致再确认模型版本是否和预期一致接着看有没有前处理或后处理的逻辑改动最后再怀疑数据漂移。很多模型突然变蠢的案例最终定位到的是上游字段格式变化模型本身没动。如果遇到单个样本输出异常先手动复现拿日志里的原始输入在本地跑同一个模型、同一个prompt看是否还异常。本地不异常说明是运行环境或请求链路问题本地也异常那就是模型或prompt问题。这套排查思路非常朴素但异常高效。我见过很多人在线上debug半天却忘了先在本地把样本复现一遍。8. 常见问题速查我踩过的那些坑问题现象可能原因排查与解决训练loss下降但验证集效果差数据泄漏、分布不一致检查切分是否乱序、清洗过程是否混入未来信息模型上线后效果明显变差线上输入分布漂移、前处理差异对比线上日志与训练集分布逐段检查链路推理延迟突然飙升GPU排队、显存不足、日志IO阻塞看P99与队列长度检查并发上限设置容器启动后模型加载失败内存不足、权重路径错误、版本不匹配检查启动日志和挂载路径先本机验证一次生成内容频繁重复解码参数温度太低或频率惩罚不足调高温度、添加重复惩罚观察输出多样性多卡训练时速度上不去数据加载成为瓶颈、通信开销大开pin_memory和num_workers检查数据预取中文输入乱码或分词异常编码不一致、词表缺失统一UTF-8检查预处理是否规范化这类问题其实都有一个共同规律先怀疑自己的系统不要一上来就怪模型。模型通常不会无缘无故变差变差大概率是有条件变化了而条件变化的痕迹一定藏在日志里。还有一条避坑经验我想单独说写代码时多做防御性检查不要假设数据永远是干净的。用户的输入可能包含不可见字符、超长文本、纯标点模型服务对这些情况的处理方式应该明确要么安全拒绝要么走兜底逻辑千万不要让它带着异常输入硬算。我见过一次线上事故就是用户输入是一串特殊Unicode字符前处理没拦住导致推理进程OOM整台机器上的服务雪崩。宁可代码啰嗦一点也要把健壮性放在第一位。9. 继续往前这个项目还能怎么扩展从零搭完这一整套AI工程链路之后你手里其实已经握着一个可复用的脚手架。后续扩展的方向很多我大致梳理三条线。一是性能优化线。当前的推理服务能用但吞吐和成本还有优化空间。可以从模型量化入手如INT8、INT4或者尝试vLLM这类高吞吐推理框架。改动带来的收益是实打实的同样一张卡吞吐可能提升一到两倍。但注意量化后效果可能小幅下降上之前要用回归测试集过一遍不能只看跑分。二是数据闭环线。把线上真实反馈回流到数据管理和模型迭代流程里。比如做一个用户反馈打标的小工具把badcase标记后自动追加到回归测试集再定期触发新一轮微调或训练。这个闭环一旦跑起来你的模型会越用越贴合真实场景而不是停留在开发时的人工数据集里。三是多模型协作线。单一通用模型做不了所有事。更合理的架构是多个模型各司其职一个模型做意图识别一个做生成一个做安全审核再用一个调度层把它们串起来。这个方向对工程能力的要求会更高但当你发现一个模型在特定任务上效果卡住的时候多模型的编排几乎是必然的解法。我自己在做这个扩展时最大的体会是AI工程不像写算法题有标准解它的进展是在上线—发现问题—改进—再上线的循环里一点点磨出来的。你不需要第一版就做出惊天动地的效果但你要保证每一次迭代都是可观测、可对比、可回退的。迭代能力才是AI工程最值钱的能力。
返回列表