ARTICLE DETAIL

资讯详情

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

AI工程全流程实战:从零搭建到稳定上线的完整记录

AI工程全流程实战:从零搭建到稳定上线的完整记录 做一个能真正落地运行的AI项目和在学校跑通一个课程实验完全是两码事。项目代号ai-engineering-from-scratch就是我真刀真枪从零走一遍AI工程全流程的记录。从需求拆解、数据标注、模型微调到最终部署上线、监控迭代每一环我都自己亲手折腾过这篇文章把完整过程、踩过的坑和最终沉淀的方法论一次性讲清楚。适合那些写过Python脚本、会调用现成库但还没独立完成过整套AI服务的人。1. AI工程不是写模型代码而是把模型变成稳定服务先纠正一个常见的认知偏差很多人以为AI工程的重心在模型训练那一步其实恰恰相反。模型训练只占整个工程很小比例的时间真正复杂的是模型之外的数据管道、交付部署和持续维护。我自己的感受是用开餐厅来类比特别贴切——模型训练是研发一道新菜把菜做出来只是第一步但AI工程要考虑的是怎么稳定高效地把这道菜做成流程化的生意涉及供应链管理、后厨流程设计、出餐质量控制、服务异常应对。一个只会在实验室炒菜的人离经营一家餐厅还很远。1.1 从Zero到One我给自己定的完整路径这个项目最初的目标很简单做一个文本多分类模型能自动把客服工单归入指定类别然后以API的形式给内部系统调用。听起来不难但当我拆解完整个链路后发现真正要做的事远不止训练一个模型这么简单。完整路径分成了六个阶段需求拆解明确业务边界、确定输入输出、定义模型的评价标准数据工程采集、清洗、标注、切分形成合格的数据集模型开发基线模型、预训练模型微调、超参数调整评估验证不只测准确率围绕业务实际关注精确率、召回率、鲁棒性部署落地模型封装成API、容器化、上线运行监控迭代跟踪线上表现、发现模型漂移、制定重新训练的策略每个阶段之间都有反馈回路。比如在评估阶段发现某些类别数据太少得回头补数据部署阶段发现预处理逻辑和训练时不一致得统一代码。这些反复横跳的经验才是AI工程里最不值钱也最值钱的教训。1.2 为什么时间都花在数据和管道上我统计过这个项目的时间分配数据清洗和标注占了大概四成时间模型训练和调优只占两成部署和联调又占两成剩下两成分给需求沟通和监控配置。为什么数据处理这么耗时因为真实世界的数据永远是脏的。我用的是历史工单数据第一天拿到手就发现了典型问题一是编码混乱有些csv文件是UTF-8有些是GBK直接读取就是乱码二是字段不对齐同样写提交时间有的带时分秒有的只有日期还有些干脆是空字符串或者不详三是标签不一致同样的内容有人标成咨询有人标成售后服务甚至还有标成无意义数字的。这些问题不解决模型训练就是垃圾进垃圾出。数据管道这一步也容易低估。模型需要的数据是一张规整的表业务系统里的数据却是散落在各个角落的。我得写脚本定期从数据库拉增量数据做去重、清洗再转换成训练集格式还要保证清洗逻辑和线上推理时的预处理逻辑完全一致。这个一致性是AI工程里最容易出问题的环节之一后面我会专门讲。1.3 AI工程的五个核心模块把这个项目拆到底就是五个模块的组合模块核心任务常见工具/方法需求端定义问题边界、明确接口与指标业务沟通、文档记录、阈值设定数据端采集、清洗、标注、版本管理Python脚本、Label Studio、DVC模型端选型、训练、调参、评估PyTorch、HuggingFace、Optuna交付端模型服务化、容器化、资源调度FastAPI、Docker、Kubernetes监控端量测反馈、数据漂移、迭代机制Prometheus、Grafana、自定义巡检这五个模块里前两个最容易被轻视后一个最容易被完全忽略。但恰恰是这一前一后决定了一个AI项目能不能长期稳定运行。很多项目死在模型效果还不错的阶段就是因为没人想过上线之后怎么维护。2. 技术栈选型没有银弹只有取舍做AI工程很容易陷入工具选择困难症。我自己的选型原则很简单优先选社区活跃、文档齐全、自己已经熟悉的工具不要为了追新而引入不必要复杂度。技术栈选型的本质是在快速上手和长期维护之间取平衡点。2.1 环境与依赖build从头到尾的环境管理项目一开始我就被Python环境坑过一次。当时机器上有系统自带的Python 3.8还有之前实验装的Anaconda加上项目需要Python 3.10以上才能跑新版PyTorch版本冲突让我直接重装了系统。后来我强制规定所有项目都用虚拟环境隔离并且把依赖统一记录在requirements.txt里。具体做法是用一个conda虚拟环境管理Python版本环境里只装必要依赖。训练服务器的环境和后续部署的Docker基础镜像保持同一套依赖规范。这样可以避免一个经典问题——训练时没问题一到部署就缺库或者版本不兼容。还有个细节容易被忽略GPU驱动和CUDA版本一定要提前确认。PyTorch官方文档里各个版本的CUDA支持列表写得很清楚选错版本会导致导入torch时报错找不到CUDA。我吃过这个亏白折腾了一晚上。2.2 模型训练框架为什么选了PyTorch选PyTorch几乎是顺理成章的。理由很简单生态成熟、调试体验好、动态图机制写起来更像普通Python代码对快速迭代最友好。TensorFlow不是不好但社区趋势和推理生态目前明显偏向PyTorch一方。HuggingFace的transformers库让我在预训练模型这一层节省了大量时间。它封装了完整的模型架构、tokenizer、训练接口我要做的主要工作是整理数据格式、调整超参数、对接训练循环。做文本分类选一个中文预训练模型比如bert-base-chinese做基座再在顶部加一个分类头微调几个epoch就能达到可用的效果。在这也提醒一句不要一上来就追求最顶级的AI模型。先从老实的基线模型开始——比如简单打baseline的FastText或者一个结构非常简单的神经网络把整个流程先跑通然后再用更强模型去提升效果。工程上最怕的是在第一步就卡住流程越早跑通心智负担越小。2.3 数据存储与标注工具数据存储这块原始数据放数据库处理后的数据我建议放对象存储版本管理。对象存储适合保存大批量文本文件和模型的checkpoint处理后的训练集则用版本管理工具跟踪变更方便回溯是哪个版本的数据训练出了当前版本模型。标注工具我选了Label Studio。它是开源的支持多人在线标注而且内置了文本分类、命名实体等多种标注场景导出格式也灵活。当时团队标注了大概三千条工单数据用Label Studio的智能标注模式可以预标注一批人工只做校正效率大概提升了三倍。有一点后悔的是没有更早设计标注规范文档——最开始标注标准不统一后期还得返工重标这个教训让我在之后每个项目里都把标注规范先行当作铁律。2.4 实验追踪与训练流程编排一开始我记录的实验只是记在一个markdown文件里写着模型v1、lr 2e-5、效果还行。过了两周再看根本记不清还行是多好和哪个数据版本对应。后来我上了MLflow用它的Tracking功能统一记录每次实验的配置、指标还有模型产物和数据集版本这一下把实验管理从记忆驱动换成了记录驱动。训练流程编排这块Airflow我一直觉得重了单个AI项目其实用不到那么复杂的DAG调度。我实际使用的是Makefile加Python脚本的组合Makefile把数据预处理、训练、评估、部署这些阶段串起来每个阶段执行固定脚本既可以本地一键跑也方便后续迁移到CI/CD。工程化的本质不是上多复杂的工具而是让流程可重复、可追踪。工具越简单越容易坚持执行。3. 实操过程一个工单分类项目的完整落地空谈方法论没有意义我把整个项目的重要实操段完整记录下来。每一步都给出了我的实际做法和关键参数你可以直接参考这套流程去套你自己的场景。3.1 先定需求再谈技术功能边界与评价指标业务方最初的需求就一句话帮我们把工单自动分类。这句话如果不细化项目根本没法落地。我拉着业务方开了两次会把需求变成了几条可验证的条目输入一条工单文本一般是标题用户描述长度在几百字以内输出预定义分类体系里的一个类别初始定义8个类性能指标整体精确率不低于85%用户投诉类别的召回率不低于90%这类漏掉了风险大接口形式HTTP接口单次请求耗时小于500ms需求里其实隐含了工程决策。比如用户投诉召回率不低于90%意味着不能只优化整体准确率因为8个类别分布不均衡整体准确率很可能被大类主导小类投诉、售后纠纷等效果差但整体看不太出来。这个指标定义影响了后面损失函数和评估逻辑的设计。3.2 数据准备清洗、标注、切分三件套数据准备是花时间最多的阶段。我整理了数据清洗的几个固定步骤做成重复使用的脚本文本去重同一用户提交的完全重复工单只保留一条编码统一检测并转换编码统一输出为UTF-8格式规范化统一繁体简体、大小写、数字格式把多种占位符统一替换缺失值处理至少保留30个字才能进入训练集否则归入低质量样本池敏感信息脱敏手机号、身份证号、地址等一律正则替换成占位符清洗后一共得到9800条可用样本。接下来做标注我利用Label Studio预标注加人工校正获得了最终标注结果。说实话如果一开始就是纯人工标注时间至少翻一倍。数据切分时我遵循了三个子集的逻辑训练集、验证集、测试集。按6:2:2划分并且保证类别的分布和整体一致。有个坑必须提醒测试集一定要在全部训练结束后再使用不能反复拿它来调参否则评估结果会虚高到失去参考意义。3.3 环境搭建与训练脚本具体实现细节模型用bert-base-chinese做base加一层线性分类头。训练环境配置如下# 环境依赖核心部分 python3.10 torch2.1.0 transformers4.36.0 datasets2.16.0 mlflow2.8.0 scikit-learn1.3.2项目目录结构. ├── configs/ # 超参数和路径配置 ├── data/ # 原始数据和处理后数据 ├── scripts/ │ ├── clean_data.py # 数据清洗 │ ├── build_dataset.py # 构建训练集 │ ├── train.py # 训练脚本 │ ├── evaluate.py # 评估脚本 │ └── serve.py # 推理服务 ├── models/ # 模型产物存放 └── Makefile # 流程编排训练脚本的关键参数我直接给实际值learning_rate: 2e-5 batch_size: 16 num_epochs: 5 max_seq_len: 256 weight_decay: 0.01 warmup_ratio: 0.1训练时我用了三招提升效果稳定性和资源利用率一是混合精度训练在PyTorch里开启AMP显存占用直接降一半速度还快一截二是早停机制当验证集loss连续3个epoch不下降就停止训练避免过拟合三是梯度裁剪把梯度范数限制在1.0以内减少loss大跳变的可能。这些参数不是凭感觉拍的。learning rate 2e-5是预训练模型微调的标准区间max_seq_len 256是因为工单文本大部分在200字内留出余量batch size 16是显存允许范围内的合理值。这些参数是最佳实践提供的起点但我强调一点最终参数值还是要通过验证集表现来定。3.4 评估阶段不要只看准确率训练完成后我在测试集上的表现是整体准确率87%和目标基本吻合。但准确率不说明一切我拉出混淆矩阵看了一下发现咨询和售后服务两个类互相混淆明显边界案例确实容易分不清。另外投诉类的召回率只有82%没达90%的要求模型会对一些比较隐晦的投诉文本识别不出来。为了解决这个短板我做了两件事一是调整类别权重在loss里给投诉类分配更高的权重让模型训练时更偏向这个类别二是补充了120条投诉类的困难样本做二次训练数据重点关注那些语气委婉但实际是投诉的表达。第二轮迭代后投诉类召回率到了91%整体准确率微降到86.5%但业务方很满意——因为指标对齐了业务风险偏好。评估阶段我还做了一件事值得推荐准备了一小批漂移测试集也就是线上最可能出现的、格式和训练分布略有差异的真实样本。这些样本不进训练集只做评估能提前发现自己模型的泛化边界在哪里。3.5 部署落地FastAPI、容器化与监控模型训练好之后要把模型变成线上服务。我选择FastAPI写推理API服务化代码很简洁from fastapi import FastAPI, Request from pydantic import BaseModel from transformers import AutoTokenizer, AutoModelForSequenceClassification import torch app FastAPI() tokenizer AutoTokenizer.from_pretrained(./models/final) model AutoModelForSequenceClassification.from_pretrained(./models/final) model.eval() class InputText(BaseModel): text: str app.post(/predict) async def predict(req: InputText): # 预处理逻辑必须和训练时完全一致 cleaned preprocess(req.text) encoded tokenizer(cleaned, truncationTrue, max_length256, return_tensorspt) with torch.no_grad(): outputs model(**encoded) probs torch.softmax(outputs.logits, dim-1) pred int(torch.argmax(probs, dim-1)) conf float(probs[0, pred]) return {label: pred, confidence: conf, probs: probs.tolist()[0]} def preprocess(text: str) - str: # 与训练阶段的clean_text保持一致 # 包括脱敏、去空格、统一标点等 return text这个API代码里有几个关键点预处理函数直接复用训练阶段的代码文件保证线上和线下逻辑完全一致推理时不用BatchNorm等训练态逻辑用eval模式输出概率而不是只输出标签让调用方自己按阈值决定处理策略接口更灵活。这里特别要重视预处理复用独立复制一份代码再改写两三个月后就会发现线上和线下的效果对不上这是AI工程里最常见的翻车点之一。部署用Docker打包推理服务。Dockerfile里用带CUDA的Python官方镜像做基础镜像装上依赖再把模型文件复制进去。模型的checkpoint文件在GPU服务器上打包镜像时要注意上下文大小别把整个虚拟环境打进去只复制必要文件。上线后的监控分两层技术层监控用PrometheusGrafana做接口延迟、错误率、QPS的看板业务层我写了一个巡检脚本每天从线上抽样200条预测结果让人工快速复核把置信度分布和类别分布趋势记下来。一旦发现某一类的置信度整体下滑或者类别分布明显偏移训练分布就触发数据漂移告警提示需要准备新训练数据了。4. 常见问题与排查技巧实录这一部分我整理了这个项目里最典型的坑和排查过程。这些问题每一个单独拿出来都值得聊一聊我把判断过程和解决办法都写清楚。4.1 训练集和验证集分布不一致第一次训练完我观察到一个诡异现象训练集的loss一直在降但验证集准确率在某个epoch之后不升反降。一开始以为是过拟合加了正则化也没用。后来我随机抽了几条被分错的验证集样本发现它们很多都是格式比较怪的历史数据比如掺杂了大量特殊符号的长文本。排查下来的结论是训练集和验证集在文本长度、标点习惯上的分布不一致——清洗时我按时间排序切分而早年的工单格式和近年差异很大导致验证集混入了大量分布外样本。解决办法是重做切分用stratified shuffle替代简单顺序切分保证两个集合在类别和文本长度维度上都分布一致。这个教训让我建立了数据版本校验的习惯每次切分后先对比两个集合的特征分布确认一致再开始训练。4.2 显存溢出OOM怎么解决训练刚开始我就遇到了CUDA out of memory。我是在一张8GB显存的消费级显卡上训练batch size 16直接爆显存。解决的方案按优先级排序先开混合精度训练显存占用立减接近一半再降batch size到8如果还不够用梯度累积保持等效batch size不变。梯度累积的做法是每4个step更新一次参数梯度等价于batch size 32的效果但显存只按batch size 8消耗。有一点要说明混合精度训练时某些模型层对低精度数值敏感可能出现梯度轻微不稳定的情况。解决方法是保持关键层用float32计算或者使用AMP的graceful降级策略。实在不行就切换成纯float32训练速度慢一点但稳定。4.3 线上预测与线下效果不一致这是部署阶段最头痛的问题。测试集评估精确率有86%多但上线第一周业务反馈明显觉得效果不如实验时。排查后发现是两个原因叠加一是线上输入的文本很多没经过脱敏清洗而训练时文本都做了脱敏导致格式分布差异二是线上文本实时性很强有很多新词和网络用语训练集里完全没有。解决方法是双管齐下线上预处理直接复用训练脚本的clean_text函数确保清洗逻辑完全一致另外缩短了数据更新周期每两周从线上抽取一批新样本补充进训练集迭代出新的模型版本。这个做法直接升级成项目后续的持续学习机制。4.4 类目不平衡的坑我的数据类别分布天然不均衡最多的类有3000多条投诉类只有900条。直接用原始数据训练小类效果会很差。我处理类目不平衡的经验分三层最简单的是类别权重法按类别的逆频率设置loss权重更进一步做数据增强对文本做同义词替换、回译、随机插入等扩充小样本再进阶就是学习策略调整比如用focal loss让模型重视难分类样本。要注意增强不是越多越好。我试过物理回译扩充了3000条投诉类数据反而降低了精确率——因为增强样本和原始样本分布有偏差模型学到了这个风格就是投诉的错误特征。后来控制增强倍数在1.5倍以内并且和原始数据混合采样效果才稳定住。类目不平衡的处理要小步验证不能一口气拉满。4.5 模型的更新节奏别动不动就重训模型上线一段时间后业务反馈也稳定下来了。我后来调整了更新策略正常的情况下每两周基于新采集的标注数据做一次微调每两个月评估一次是否需要做全量重训。而且每次更新模型前必须跑一遍固定的回归测试集——这个回归集里有上线以来的典型边界样本保证新模型在这些样本上不至于变差。这一步是为了避免新版本修复了A类老问题但弄坏了B类原本正常的情况。5. 三个让我少走弯路的实操心得写到最后我把这个项目里最值钱的体会提炼成三条都是文档里不会写的东西。第一先定指标再定任务。AI工程所有环节都是跟着评价指标走的。如果你连业务方实际关心的是精确率还是召回率都没搞清楚后面做的很多工作都会白费。指标定义了数据标注的重点、模型优化的方向、评估方法的设计就都有了锚。第二线上和线下的一致性是一票否决项。数据预处理、模型推理、阈值设置训练和上线要共用一套代码。独立的线上逻辑再正确也会因为各种细节不一致导致效果衰减。把预处理写成一个固定模块训练、评估、推理三处都调用它是最省心的做法。第三维护比构建重要。模型上线只是起点不是终点。数据漂移、业务变化、用户表达习惯演变都会让模型效果慢慢衰减。持续监控、周期性重训、回归集测试这才是AI工程真正长期运转的动力系统。我在实际项目中体会最深的就是AI工程的成功力在构建时体现一半在维护时体现另一半。这个项目的代码和文档我后来整理成了一个更通用的脚手架从一个具体的工单分类扩展成一套可复用的从数据到服务的AI工程模板。整个过程走完一遍之后再看任何AI项目我的第一反应都是先看它的数据管道和部署方案而不是急着问用了什么模型。模型只是整个系统里一颗螺丝钉而这个认知本身就是这个项目带给我最大的价值。
返回列表