ARTICLE DETAIL

资讯详情

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

AI工程从零实战:从数据管道到线上监控的完整闭环

AI工程从零实战:从数据管道到线上监控的完整闭环 如果你也是那种看了一堆AI教程、本地能跑通模型、却始终不敢把系统真正丢到线上的人这篇文章应该能帮你省下几个月的试错时间。“AI工程从零开始”不是口号也不是一门看完就会的网课而是一套必须亲手落地才能建立的能力。我把它当一个正式项目推进了四个月从数据管道到模型训练再到线上服务和监控告警完整走了一轮。下面这份复盘就是我从立项、拆解、选型到排障的全过程记录。1. 项目缘起一次上线翻车逼我从“跑通模型”转向“做成系统”1.1 本地95%的准确率上线第二天掉到70%我必须承认我对AI工程的认真是被一次失败逼出来的。当时我负责一个小功能对用户评论做分类判断哪些需要人工跟进。我用了当时很成熟的预训练模型做微调离线测试集上准确率接近95%我觉得稳了直接打包上线。第二天一看监控分类准确率掉到70%。排查了半天最后发现原因很朴素线上真实的用户评论大量使用口语缩写、中英混写、表情符号而我的训练数据来自一个相对规范的公开语料几乎没有这类噪声。模型在“考卷”上表现优秀但“考场”和“生活”根本不是一回事。这件事让我意识到模型训练只是AI系统里很小的一环。真正的AI工程是让模型在整个系统中持续稳定地输出价值。也就是从那几天开始我决定把“AI工程”当作一项独立能力去系统化构建而不是零散地学几个库。这个项目代号就叫 ai-engineering-from-scratch目标是用有限资源走通从原始数据到可迭代上线系统的完整链路。1.2 调模型和做AI工程到底差在哪我先说一个粗浅的类比。传统软件工程讲究代码的可读性、可测试性、可维护性而AI工程的复杂性在于系统的行为不仅由代码决定还由数据和模型共同决定。换句话说你不光要管理代码版本还要管理数据版本和模型版本你不仅要写单元测试还要建立评估集和回归基准。很多人把AI工程等同于“会调参”“会微调”“会用某个框架”这是把“调模型”和“做系统”混为一谈了。调模型解决的是“给定数据集怎么把指标跑高”做AI工程解决的是“从原始数据到稳定线上服务再到可持续迭代”的整条链路。跑通一个Notebook只证明你能复现别人的实验把系统稳定运行三个月以上才算真正入门AI工程。我给自己定的项目边界也很明确不追最新模型不搞分布式集群只求在受限资源下完成一个从原始数据到线上API、并且可以持续迭代的最小真实系统。四个月后回头看这个边界定得太对了——它让我把所有精力都压在了工程骨架上而不是被模型能力的花活带偏。2. 起步决策真正动手前我先把三个关键问题想清楚2.1 选什么任务我为什么选了文本分类而不是生成式AIAI工程的第一步不是装环境而是选一个能覆盖全链路的任务。我建议所有想从零开始的人都别一上来就搞大模型生成、Agent这类重任务原因很现实它们会把你的注意力从“工程体系”转移到“模型能力”上。提示词写得好不好、上下文怎么塞、幻觉怎么抑制这些确实重要但如果你还没建立起一条可靠的数据、评估、部署闭环这些探索很难沉淀成工程资产。我最终选的是文本分类。它看起来简单但它几乎涵盖AI工程的所有关键环节数据清洗与标注、类别不平衡处理、离线评估、模型导出、线上推理、数据漂移监测。做完这个任务整套骨架就成型了。之后再迁移到更复杂的任务只是替换若干模块而已。类比的例子很好懂你想学做菜不会直接挑战烤全羊而是先练番茄炒蛋。番茄炒蛋需要洗菜、切菜、调味、控火候这些基本功可以迁移到任何菜系。文本分类就是AI工程里的番茄炒蛋。2.2 数据从哪来公开数据打底自建“验收集”把关数据是很多人起步时最纠结的。我的经验是先别等完美数据集用公开的、质量尚可的数据把链路跑通然后用大约10%的精力按业务场景手工补充“验收集”。验收集的作用是守住底线公开数据集上的漂亮指标只能参考验收集上的表现才是判断能不能上线的依据。我选了一个公开的情感分类数据集做训练另外收集了一部分真实场景语料做验收测试并且人工清洗了一遍。这个动作在后来的线上问题排查中救了我很多次——它是判断“模型本身退步了”还是“线上数据分布变了”的关键锚点。没有验收集你面对线上准确率下跌时就只能靠猜。2.3 算力边界别在起步阶段就去碰GPU集群个人或者小团队做AI工程最容易犯的错是过度配置。我见过有人从第一天就搭K8s GPU集群结果光运维就耗尽精力模型一个没训。真实需求往往一张消费级显卡或一块按小时计费的云端GPU就能覆盖。我的选择是先租云GPU按小时跑训练本地做开发和推理验证。这样做的好处是成本可控而且训练、推理环境相对干净避免在本地装一堆二进制依赖。后面有明确吞吐需求了再考虑常驻实例这是后话。记住一句话工程能力是构建出来的不是配置出来的。一开始堆硬件只会掩盖流程上的问题。3. 技术选型复盘我选工具的标准是“坏处已知”不是“功能最多”3.1 选型总原则新工具的光鲜不值得用项目周期去赌做AI工程越久越能体会选型的核心原则不追新选生态成熟的。新工具通常学习成本高、文档不全、社区回答少出了问题很难定位。我宁可选择缺陷已经被人吐槽烂了的工具至少踩坑前有人给过路标。这个原则听起来保守但它能救你的项目周期。具体到这次项目我把整条链路的工具列了一个清单每个位置都做了候选对比然后才拍板。下面直接给出最终选型和理由。3.2 数据管道Polars做预处理DVC管数据版本数据预处理我选了Polars而不是Pandas。原因很直接当我处理几百万行文本数据时Pandas转个列类型都慢得难受Polars天然利用多核性能好得多而且API风格接近迁移成本很低。如果你的数据量在几十万行以内Pandas也完全够用选型不必教条。比行列工具更重要的是数据版本管理。我用了DVC它不存数据本身只对数据文件做哈希快照再配合Git记录依赖关系。这样每次训练用的数据集是哪一版能精确复现。有人觉得多此一举但一旦模型上线你会天天追问“这个模型是用哪份数据训出来的”此时答案必须可查。我见过太多团队在模型出问题时翻遍聊天记录最后也没找到训练数据的长什么样。3.3 实验追踪与模型注册MLflow比自建Excel靠谱训练实验如果没有追踪你会很快陷入“这个准确率是哪个参数跑出来的”的混乱。我用MLflow做三件事记录超参数和指标管理模型产物登记模型版本。它的模型注册功能在后面的部署环节非常有用——每次部署都从注册中心拉固定版本而不是从某个文件夹随便找权重。这里多说一句。很多项目混乱的起点就是权重文件的各种命名备份。你会在 weights_old、weights_final、weights_final_v2 之间迷失只是时间问题。用MLflow以后模型版本和实验参数天然绑定回滚时直接选一个已注册的版本即可不需要跟文件名搏斗。3.4 模型训练与推理训练用PyTorch部署换成ONNX训练阶段没有悬念PyTorch Transformers。生态最全遇到问题几乎都能搜到答案。难的不是训练代码而是后面的工程化。推理部署我特意做了转换把PyTorch模型导出为ONNX再用ONNX Runtime跑。原因有三一是推理速度通常有30%-50%提升二是部署环境不需要装PyTorch全家桶依赖体积小很多三是ONNX格式统一以后换推理引擎更灵活。转换过程需要处理动态轴、算子兼容等细节但收益非常值得。3.5 服务框架与监控FastAPI当门面Prometheus兜底线上API我用FastAPI成熟稳定自带OpenAPI文档调试方便。监控方案没有用很复杂的东西Prometheus采集指标Grafana画大盘再加几个自定义脚本来检测数据漂移。下面这张表可以快速回顾我的选型模块候选方案我的选择核心理由数据处理Pandas / PolarsPolars大数据量性能好语法迁移成本低数据版本无 / DVCDVC哈希快照能精确回答“哪份数据”实验追踪自记Excel / MLflowMLflow模型注册功能与部署链路衔接好模型训练PyTorch / TensorFlowPyTorch生态最成熟调试资料最全推理引擎PyTorch / ONNX / TensorRTONNX速度稳定提升部署依赖干净服务框架Flask / FastAPIFastAPI性能好自带文档上手快监控告警Prometheus Grafana同上社区资料多指标可自定义4. 最小闭环实操从原始文本到线上接口我一共分了五步4.1 数据清洗先拿统计说话再写规则这一步的核心是别靠感觉。我会先打印数据的基础统计类别分布、文本长度分位数、空值比例然后再决定清洗规则。这是我在真实项目中常用的清洗流程import pandas as pd df pd.read_csv(raw_reviews.csv) print(df.shape) print(df[label].value_counts()) print(df[text].str.len().quantile([0.5, 0.9, 0.99])) df df.dropna(subset[text, label]) df df[df[text].str.len() 2] # 基础清洗去空白、统一小写、收敛特殊符号 df[clean_text] ( df[text] .str.strip() .str.lower() .str.replace(r[^0-9A-Za-z\u4e00-\u9fff。、% ], , regexTrue) ) df.to_parquet(clean_data.parquet)注意我保留了中文标点和百分号因为情感分类场景里“”和“%”往往携带情绪信息一刀切删掉反而丢特征。这个细节是当初在错误样本分析里发现的。4.2 训练与实验记录脚本要短记录要全训练脚本本身并不复杂。我用Transformers的Trainer做了微调核心代码如下from datasets import Dataset from transformers import ( AutoTokenizer, AutoModelForSequenceClassification, Trainer, TrainingArguments, ) dataset Dataset.from_parquet(clean_data.parquet) tokenizer AutoTokenizer.from_pretrained(bert-base-chinese) def tokenize(batch): return tokenizer(batch[clean_text], truncationTrue, max_length128) dataset dataset.map(tokenize, batchedTrue) dataset dataset.train_test_split(test_size0.2, seed42) model AutoModelForSequenceClassification.from_pretrained( bert-base-chinese, num_labels2 ) args TrainingArguments( output_dir./checkpoints, num_train_epochs3, per_device_train_batch_size32, per_device_eval_batch_size64, evaluation_strategyepoch, logging_dir./logs, ) trainer Trainer( modelmodel, argsargs, train_datasetdataset[train], eval_datasetdataset[test], ) trainer.train()注意真实项目里我不会只留这一个脚本还会把数据集的版本哈希、模型参数、评估指标全部报给MLflow。你可以简单理解为训练过程每一步都有“档案”而不是只留下最终权重文件。这样万一哪次实验出了问题你能快速回到某一个状态重新跑。4.3 评估准确率达到95%不能上线我只看这四样东西很多初学的人只看整体准确率这是上线前最危险的错觉。在类别不平衡的场景里就算准确率95%可能只是把多数类预测对了少数类几乎全错。我每次上线前必做四件事第一打印每个类别的精确率、召回率和F1值第二打印混淆矩阵看哪些类别容易被混淆第三逐条看错误样本记录错误模式第四在自建验收集上重新评估。下面这段代码就是标准流程from sklearn.metrics import classification_report, confusion_matrix import numpy as np preds trainer.predict(dataset[test]) y_pred np.argmax(preds.predictions, axis1) y_true dataset[test][label] print(classification_report(y_true, y_pred, target_names[normal, need-follow], digits3)) print(confusion_matrix(y_true, y_pred)) test_df dataset[test].to_pandas() test_df[pred] y_pred wrong test_df[test_df[label] ! test_df[pred]] for _, row in wrong.head(20).iterrows(): print(row[clean_text][:80], 真实:, row[label], 预测:, row[pred])不要小看错误样本逐条看这一步。它比任何指标都更能告诉你模型学会了什么、漏掉了什么。我就是在这一步发现模型对“太慢了等了三天没发货”这类评论总是误判为正常因为它学习的公开语料里缺少这种“负面隐含在陈述里”的表达。4.4 模型导出与接口封装ONNX加FastAPI一条龙上线评估通过后进入模型导出环节。把PyTorch模型转成ONNX的代码如下from transformers import AutoModelForSequenceClassification import torch model AutoModelForSequenceClassification.from_pretrained(./checkpoints/checkpoint-best) model.eval() dummy_input torch.zeros(1, 128, dtypetorch.long) torch.onnx.export( model, dummy_input, model.onnx, input_names[input_ids], output_names[logits], dynamic_axes{input_ids: {0: batch_size, 1: seq_len}}, opset_version14, )转换完成后用FastAPI封装线上接口。核心逻辑如下import numpy as np import onnxruntime as ort from fastapi import FastAPI from pydantic import BaseModel from transformers import AutoTokenizer app FastAPI() tokenizer AutoTokenizer.from_pretrained(bert-base-chinese) sess ort.InferenceSession(model.onnx, providers[CPUExecutionProvider]) class Req(BaseModel): text: str def softmax(x): x x - x.max() return np.exp(x) / np.exp(x).sum() app.post(/predict) def predict(req: Req): length len(req.text) if length 2 or length 512: return {label: -1, confidence: 0.0, error: invalid input length} inputs tokenizer(req.text, truncationTrue, max_length128, return_tensorspt) logits sess.run( [logits], { input_ids: inputs[input_ids].numpy(), attention_mask: inputs[attention_mask].numpy(), }, )[0] prob softmax(logits[0]) label int(prob.argmax()) return {label: label, confidence: float(prob[label])}上线前还应该在接口里加超时控制、日志记录和限流但它作为MVP已经够了。先跑通再加固。4.5 最小监控上线第一天就盯着四个指标模型上线不是终点。我第一天就在监控大盘上加了四个指标请求量、推理时延、置信度分布、预测类别占比。每个指标都有明确意义请求量告诉你系统有没有被调用时延告诉你用户体验置信度分布能反映模型是否开始对输入“含糊其辞”预测类别占比则直接指向业务分布的变化。比如某天“need-follow”类占比突然从30%涨到60%可能是业务规则变了也可能是线上来的用户结构变了这时候就要警觉。我还会把这些指标按小时聚合存下来做趋势对比。这是后续数据漂移判断的数据基础。5. 上线三个月后那些真正让我头疼的工程问题5.1 数据漂移系统不是死在模型上是死在分布变化上上线三个月里模型本身没有变但线上准确率还是出现了几次明显波动。最典型的一次是某天平台上线了新功能用户评论里突然出现大量新术语模型没见过很多样本被预测错。这属于典型的数据漂移训练分布和线上分布不一致。应对方法无外乎两条路被动检测和主动闭环。被动检测是监控线上输入的词频分布、句长分布、预测置信度分布一旦偏移超过阈值就告警。主动闭环是定期抽取线上样本人工标注后回流到训练集让模型持续适应新分布。没有这个反馈机制任何模型上线时间长了都会“生锈”。我当时写了一个轻量级漂移检测脚本用PSIPopulation Stability Index群体稳定性指标对比线上特征分布和训练集特征分布的差异。PSI超过0.1就告警超过0.25强制触发人工评估。这个数字不需要很精确但必须有一个客观的告警标准否则你根本不知道模型什么时候开始退化。5.2 “这个模型是怎么来的”没有版本关联出问题只能抓瞎上线后有一回线上模型表现异常我需要快速回滚到上一个稳定版本。如果当时没有做模型版本管理我可能要在十几个权重文件里翻找半天。但因为从训练开始就把数据集、代码、超参数、模型产物在MLflow里打成了一个整体回滚只是修改路由配置的事。这件事给我的教训是模型本身可以迭代但每个版本的“血缘关系”必须清楚。数据是哪份、代码是哪版、指标是多少、谁训练的、什么时候上线这些信息在出问题时就是救命稻草。5.3 GPU预算失控一个挂着没关的实例能烧掉一台电脑训练阶段用云GPU按小时计费看起来不贵但如果你忘了关实例一个月就是一笔不小的开销。我有一次跑完实验忘记释放实例第二天发现多挂了一整夜成本抵得上好几顿饭钱。后来我学乖了做了两件事第一训练脚本结束自动释放实例不放手动依赖第二给云资源设置预算告警超过阈值立刻通知。成本控制不是财务问题它直接影响你能否长期做工程实践。没有预算意识项目很容易中途夭折。5.4 灰度与回滚不是可选项是保命项新版本模型上线我从来不直接全量切换。我的做法是先用Nginx把5%的流量切到新模型观察一小段时间的置信度分布和业务指标确认没问题再逐步放量到50%、100%。如果新模型出现问题立刻把流量切回旧版本整个过程可以在分钟级完成。很多团队觉得灰度麻烦直接全量上线。但在AI系统里模型是概率输出你永远无法保证它在所有输入上都正确所以必须假设新版本可能出问题。灰度与回滚是底线工程。6. 项目复盘这套体系能迁移到哪下一步我要做什么6.1 骨架的通用性换任务不换框架四个月跑完我最深刻的感受是AI工程的骨架是可迁移的。从文本分类换到命名实体识别、图像分类、回归预测变的只是数据管道的细节和模型类型数据版本管理、实验追踪、评估体系、服务封装、监控告警这五个环节是完全通用的。这套骨架还可以直接迁移到大模型应用场景。比如做RAG检索增强生成流程变成文档解析、向量化、检索、生成但后面的服务封装和监控逻辑几乎不变。做Agent也一样复杂的是任务编排和工具调用但数据的版本意识、实验的追踪意识、线的回滚能力仍然是根基。6.2 下一步从分类任务走向RAG与Agent这个项目结束以后我开始往里加生成式AI的能力。第一步是接RAG用私有知识库给大模型补充上下文。迁移的过程比我预想的顺利因为数据管道的骨架已经在了文档加载、切分、向量化、存储这些本质上还是数据工程检索结果的质量评估用的是我在分类任务里建立的评估方法论。再往后是Agent但我不打算一上来就做复杂编排。我会继续守住这个从零搭建的体系先把工具调用和记忆管理纳进来保持每一版都可追踪、可回滚、可评估。AI工程最大的好处在于体系一旦建立新能力都只是插到固定位置上的新模块。6.3 给同样想从零开始的人三条最实在的建议第一条先跑通闭环再去优化细节。数据别等完美机器别等够好任务别等复杂。你需要的只是一个能完整跑起来的MVP它会逼你把所有环节都暴露出来。第二条把所有环节做成可复现的。代码版本、数据版本、模型版本、评估结果都要能回溯。哪怕前期慢一点也值得。第三条把监控当作上线的一部分而不是上线后的可选优化。没有监控的AI系统就像没有仪表盘的飞机飞起来容易降不降得下来完全看运气。如果让我重新开始这个项目我只会调整一件事把更多时间花在观察数据上。模型可以换框架可以换真正决定系统上限的始终是你对数据的理解深度。AI工程不是把模型放上服务器就结束而是让它在真实世界的噪声里持续站得住脚。愿你少踩我踩过的坑也愿你比我更早体会到这套体系带来的底气。
返回列表