ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

AI工程从零实战:搭建可落地的模型生产系统

2026/10/3 21:00:40 拓冰建站 浏览量
AI工程从零实战:搭建可落地的模型生产系统 “ai-engineering-from-scratch”这个标题我理解起来不是“从零学AI”而是“从零开始做AI工程”。很多人一开始就被Transformer、大模型、SOTA这些词吸引但我做了几年AI服务落地之后越来越确认一件事AI工程的核心不在模型而在模型周围那一整套系统。它能解决的问题是把一个能跑的模型变成一条能扛住真实请求、能持续迭代、出问题后可以快速排查的服务线而不是让模型只活在Jupyter Notebook里。这里我不讲“一个月从入门到精通”之类的套路只讲我自己从零搭AI服务时真正会用到的路径、组件和坑。整条路线围绕一个具体项目展开商品评论情感分类服务从数据清洗到最终部署上线每一步都会给出可复现的代码片段和配置。这套方法对做推荐、搜索、智能客服的人同样适用你跟着走完就拥有了一套自己的AI工程骨架后面换模型、换场景往这套骨架里填东西就行。1. 先想清楚AI工程和算法工程师是两码事1.1 模型精度只是起点上线才是终点模型精度是很多人做第一个AI项目时唯一盯着的数字。我最早也一样在测试集上把准确率从0.88提到0.91开心得不行。但当你真正要把这个模型塞进业务流程里会发现要回答的问题根本不是“准确率多少”而是单个请求要多久能返回同时来100个请求会不会拖死服务挂了怎么恢复新数据来了要不要重新训练旧模型怎么平滑下线这些问题的答案才决定模型能不能真的产生价值。我自己做过一个排序服务模型离线AUC刷到了0.83结果一上线发现请求平均响应时间900ms业务方直接摇头。后来用了两周优化特征存储和推理逻辑把P95压到200ms模型精度一点没变但最终效果完全不同。所以如果目标是做AI工程建议你调整一下关注点精度合格后立刻把精力转到延迟、吞吐、稳定性、可维护性这些工程指标上。它们比0.01个百分点的准确率提升重要得多。1.2 从零起步最常见的三个误区误区一一上来就追SOTA。我见过很多同学第一周就扎进最新的模型论文结果光下载权重就卡了好几天后面训练根本跑不动。新手做AI工程不是先选最先进的模型而是先跑通一个最简单但完整的链路数据读取、模型训练、接口封装、本地预测。等这条链路通了再考虑要不要换更强的模型。否则你会在模型选型上花80%的时间最后连服务都起不来。误区二把Notebook当生产系统。PyTorch在Notebook里跑通很容易CtrlEnter就能出数字。可生产系统需要可重入的训练脚本、固定的依赖版本、标准化的输入输出。Notebook的交互性恰恰是工程化最大的敌人状态散落、顺序依赖、难以复现。我的建议是Notebook只用来做探索性分析和画图一旦实验跑通马上用Python脚本重写数据加载和训练循环。误区三忽视数据和特征直接堆模型。很多新手拿到数据就开干缺失值不处理、文本不统一、标签有噪声。真实业务里模型提升的上限往往由数据和特征质量决定。你花一周清洗数据、统一归一化逻辑可能比换一个更大规模的模型效果更明显。2. 从零搭建AI工程能力地图2.1 五层核心能力怎么搭把AI工程能力拆成五层从底层到顶层分别是数据工程、模型开发、模型服务、MLOps、评估与监控。每层都有明确的技能点也都有对应的“坑”。数据工程负责数据采集、清洗、采样、特征构建与存储。做文本分类要掌握分词、停用词处理、类别分布检查做图像要关注数据增强、标注格式统一。这一层的核心产出是“一份稳定可复现的数据集”而不是一份一个样的CSV。模型开发不只是调算法还包括训练框架使用、超参数管理、实验记录、模型文件管理。要能回答“上次训练用的什么配置”和“怎么恢复到一个断点继续训练”。模型服务是把训练好的模型封装成HTTP接口、gRPC接口或者异步队列任务。需要懂并发、请求校验、性能优化、容器化部署。这是最容易被低估的一层很多人觉得“不就是加个fastapi包一层”实际上线上大部分的崩溃和延迟问题都出在这一层。MLOps包括版本管理、CI/CD、流水线、灰度发布。模型也是代码的一部分需要有完整的发布流程不是本地改一改就上线。评估与监控包含离线评估、线上效果分析、数据漂移检测、告警。这一层决定你业务出问题时是“多云里雾里”还是“十分钟定位”。这五层不需要全部精通但至少要在项目里完整走过一遍知道每一层解决什么问题。具体路径下面说。2.2 学习路径建议先横着走再竖着磨我建议先横着走也就是用一个小型端到端项目把五层全部趟一遍不求精但求每个环节都认识。然后根据你实际工作卡在哪一层再竖着深入下去。比如发现线上老是延迟高就去把并发和性能优化吃透发现模型经常跑偏就去把监控和漂移检测吃透。为了达到“先横着走”的目标你的第一个项目不要选太复杂的任务不要选那种需要三天分布式训练的。最好满足三个条件数据能公开获取任务目标明确模型复杂度适中单机可训练。文本情感分类、垃圾邮件识别、图像二分类都非常合适。在技术栈上我推荐这样起步Python PyTorch 做模型pandas 做数据处理FastAPI 做服务框架Docker 做部署再加简单的日志和指标记录。这套组合足够轻量又能覆盖AI工程的主要环节。不要一开始就上Kubernetes、MLflow这些重武器它们的复杂度会让新手误以为“工程”就等于“配置一堆工具”。工程能力是从解决问题里长出来的不是从配置文件里装出来的。为什么用PyTorch而不是TensorFlow不是谁绝对更好而是PyTorch的调试体验对新手更友好生态里模型代码更容易读懂。在这个阶段能把细节看明白比性能高一点更重要。2.3 技术栈选型我实际用下来的感受下面这个表是我做中小型项目时常用的默认组合你可以直接抄作业环节工具理由数据处理pandas NumPy轻量、调试方便数据量巨大再换Spark模型框架PyTorch调试体验好ONNX导出生态成熟服务框架FastAPI自带OpenAPI文档异步支持好起步成本低任务队列Redis RQ多数场景用不上Kafka轻量够用部署Docker Compose单机多容器编排够用别一上来就K8s监控Prometheus Grafana开源方案指标采集成熟我拿这套组合做过不止一个内部工具。优点是灵活、每层可替换缺点是监控和告警需要自己写不少胶水代码。作为从零起步胶水代码本身就是最好的学习材料。等真正理解了每一层的职责再上重型工具就不会被工具牵着鼻子走。3. 从零做一个端到端项目商品评论情感分类3.1 项目选题要选有工程味道的问题我选商品评论情感分类作为示范项目原因有三第一公开数据集好找中文用公开的电商评论语料即可第二任务边界清晰——判断一段评论是正向还是负向第三模型不需要大一个词向量加TextCNN或者一个很小的Transformer就足够单卡几分钟就能训练完。正因为模型小你才有精力把周围那一圈工程做好而不是把时间浪费在看loss曲线上。重点是题目要有“工程味道”也就是天然带几个硬指标接口响应要快总不能让用户等3秒才看到一个标签并发要稳不能来两个请求就超时模型版本要能换不能改代码才能更新模型。如果只是“训练完画个准确率”那还是算法课作业不是AI工程。3.2 目录结构与环境准备拿到项目先别急着写模型把目录搭好。我常用的结构project/ ├── configs/ # 所有可配置参数 │ └── config.yaml ├── data/ │ ├── raw/ # 原始数据只读不修改 │ └── processed/ # 清洗后的数据 ├── src/ │ ├── data/ # 数据处理代码 │ │ └── preprocess.py │ ├── models/ # 模型定义 │ ├── train.py # 训练入口 │ └── predict.py # 加载模型做推理 ├── app/ │ ├── __init__.py │ ├── predictor.py # 模型预测封装 │ └── server.py # FastAPI 服务 ├── tests/ # 单测和接口测试 └── Dockerfile为什么要把数据分成raw和processed因为原始数据是资产清洗逻辑是代码。processed可以随时重新生成不要手工改原始文件。训练入口只从config读取参数不把batch_size、lr这些散落在代码里。这些看起来是小事但等你要复现三个月前的实验时会发现它们是救命的事。环境准备上用Python 3.10以上版本把依赖写进requirements.txt并固定版本。注意不仅固定numpy、torch这类直接依赖传递依赖也要尽可能锁住。生产环境复现不了八成是依赖版本不一致。3.3 数据管线训练稳定的地基数据管线看起来不炫但它是整个项目最容易出错的地方。以中文评论为例我会做这几步统一格式、清洗噪声、分词、切分训练验证集。其中有一个容易被忽略的点训练时的文本预处理函数必须和预测时用同一个函数。很多人训练时用了一个清洗正则部署时偷懒直接往模型里塞原始字符串结果线上精度直接崩。# src/data/preprocess.py import re import jieba STOP_WORDS set([的, 了, 吗, 呢, 吧, 啊, 哈]) def clean_text(text: str) - str: text text.strip() text re.sub(r\s, , text) return text def tokenize(text: str): cleaned clean_text(text) tokens [w for w in jieba.cut(cleaned) if w not in STOP_WORDS] return tokens清洗函数就像调酒里的“摇匀”这步简单但必须严格执行。写好的clean_text应该同时被训练脚本和app/server.py引用而不是在服务端复制一份改两行。如果你换了最新版jieba分词结果变了线上效果也会跟着变。所以依赖版本锁死不是洁癖是底线。3.4 训练与实验管理不靠感觉调参训练脚本里除了模型结构还需要固定这些隐藏项随机种子、数据顺序、显存分配策略。否则你会在不同时间跑出不同结果很难判断是改代码导致的还是运气导致的。最简单的做法是在训练入口固定随机种子# src/train.py def set_seed(seed: int 42): import random import numpy as np import torch random.seed(seed) np.random.seed(seed) torch.manual_seed(seed) torch.cuda.manual_seed_all(seed)实验管理上不需要一上来就上MLflow。用一个表格记录每次实验的配置和关键指标就够了。我在本地用一套命名规则experiment_20250101_0930_lr3e-4_bs32日志和checkpoint按这个子目录存。这样即使不借助任何平台也能回溯每一次改动。config.yaml类似这样model: name: textcnn embedding_dim: 128 num_filters: 256 window_sizes: [3, 4, 5] train: batch_size: 32 lr: 3e-4 epochs: 10 seed: 42 early_stop: true patience: 2 data: max_len: 128 min_freq: 2训练时加早停可以防止过并节省时间checkpoint保留best和last两个副本best用于上线last用于继续训练。这个习惯帮我避免了很多次“想从上次中断点恢复结果只有best没有last”的窘境。训练完成后把模型文件、配置、预处理代码放到同一个版本目录里上线时整体发布避免文件散落各处。4. 把模型搬到生产环境4.1 模型服务选型小项目别一上来就上重武器模型服务是AI工程和“算法作业”分道扬镳的地方。对于当前这种单模型、低并发场景我用FastAPI写了一个server.py同时承担几个重要工作加载模型、输入清洗和分词、推理、输出结构统一。为什么不用Triton这类高性能推理框架因为目标是先跑通。Triton适合模型很多、服务要求高的场景它的部署配置复杂度对新手是双重负担。等单机方案验证了业务价值再上更重的服务不迟。一个重要的实践把模型推理放到独立类中和路由层分离。接口层只负责解析HTTP请求和校验参数真正的业务逻辑在Predictor里。# app/server.py from fastapi import FastAPI from pydantic import BaseModel from app.predictor import Predictor app FastAPI() predictor Predictor(checkpoints/best.pt) class ReviewItem(BaseModel): text: str app.post(/predict) def predict(item: ReviewItem): label, score predictor.predict(item.text) return {label: label, score: score}请求体用pydantic做校验text为空直接返回明确错误而不是等到模型里才报一个莫名其妙的KeyError。Predictor内部可以加一个简单缓存同一句话在短时间内重复请求直接返回缓存结果能省不少计算量。这个优化做起来很简单但很多人会忽略。4.2 容器化部署Dockerfile里的教训部署的第一优先级是“可复现”而不是“镜像小”。在保证可复现的基础上尽量减小体积也值得做。一个能直接用的DockerfileFROM python:3.10-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY src ./src COPY app ./app COPY configs ./configs COPY models/best.pt ./models/best.pt RUN useradd -m appuser USER appuser CMD [uvicorn, app.server:app, --host, 0.0.0.0, --port, 8000]几个我踩过的坑第一不要用python:latest这种浮动标签否则某天基础镜像更新你的服务在完全没改代码的情况下行为变了。第二pip install要加--no-cache-dir镜像能少几百MB。第三.dockerignore里一定要排除data/raw、tests、.git否则会把一堆垃圾带进构建上下文构建慢还容易泄密。进一步优化可以用多阶段构建第一阶段装编译依赖第二阶段只拷贝site-packages和代码到slim镜像。这样镜像更小攻击面也更小。但多阶段构建要额外注意模型文件不要漏掉我在多个阶段之间拷文件时就漏过一次best.pt服务起来后一直报加载失败。4.3 监控、灰度与回滚上线后必须有三个层次的监测基础健康包括进程存活、内存、CPU业务指标包括QPS、P95延迟、失败率模型指标包括预测标签分布、输入文本长度分布有没有异常偏移。这些指标未必都要上Grafana。最开始可以用一个脚本定时打印日志再记录到本地文件。重要的是先有数据长什么样。等达到瓶颈再接入Prometheus。灰度发布上服务端最简做法是“两个容器端口分离滚动发布”。新版模型先在一个容器加载让少量测试流量过去观察五分钟后再切全量。回滚就是保留上一版镜像和模型文件一条命令重启。我吃过一个亏没保留旧模型文件新版模型上线后发现精度崩了却没有旧模型可以立刻回滚。从那以后每次上线前我都会把旧镜像先打标签存下来宁可多占点磁盘也不要让自己处于“只能硬着头皮修”的被动局面。5. 常见问题与排查实录5.1 训练时显存爆掉别急着换显卡显存爆掉是训练时最常碰到的一关。盲目换大显存显卡当然有效但成本太高。先试三个手段减小batch_size、开混合精度、使用梯度累积。混合精度在PyTorch里用torch.autocast即可支持比较成熟。梯度累积的逻辑就是每隔n个batch更新一次参数相当于用时间换空间。示例scaler torch.cuda.amp.GradScaler() accumulation_steps 4 for i, batch in enumerate(dataloader): with torch.autocast(device_typecuda): loss loss_fn(model(batch), batch[label]) scaler.scale(loss / accumulation_steps).backward() if (i 1) % accumulation_steps 0: scaler.step(optimizer) scaler.update() optimizer.zero_grad()注意loss要除以累积步数否则等效学习率会变大BN层在梯度累积时效果会受影响但多数文本模型影响不大。如果这些手段都试过还是OOM再考虑换卡或者改小模型结构。5.2 模型本地很好上线后变差这是我被问得最多的一个问题。原因通常不出这三个特征预处理不一致线上请求数据和训练数据分布差异太大上线时模型文件加载错了。排查顺序建议是先在服务里写一个单独测试用例用训练集样本作为请求看输出是否和离线predict一致。如果输出对不上说明预处理链路有问题。如果输出对得上再看线上请求分布。一个很隐蔽的坑线上用了新版分词库或依赖版本被更新导致分词结果和训练时完全不一样。解决方法是把tokenizer和模型一起版本化依赖锁死在requirements.txt里。上线后还可以提前记录每条请求的摘要特征比如文本长度、预测置信度。如果某天业务反馈“效果变差了”你能先查分布差异而不是去猜模型是不是坏了。5.3 服务延迟抖动严重如果服务P99延迟经常飙到3秒而P50只有50ms大概率不是模型变慢而是资源竞争。常见原因模型首次加载时冷启动数据库或缓存连接池太浅线程池被慢请求占满。在FastAPI里如果用的是同步函数每个请求会占用一个线程池模型推理本身是CPU密集同步处理会阻塞事件循环。推荐把模型推理放到独立进程或者用队列把请求转成异步任务控制并发数。我用过一个简单办法在预处理、推理、响应三个阶段分别打印耗时日志先从时间分布找瓶颈。不要凭感觉优化先量化。有一次我发现服务慢不是模型推理而是每次请求都重新加载了一遍词典后来把词典放在模块全局变量里P95直接降了一截。5.4 排查速查表症状可能原因优先检查训练OOMbatch过大 / 未开混合精度 / 梯度累积未生效显存占用日志线上精度崩预处理不一致 / 依赖版本漂移用训练集样本直接打接口对比接口偶发超时线程池不足 / 连接池不足 / 冷启动并发时段耗时分布模型无法加载checkpoint路径不一致 / 文件损坏打印模型文件hash线上标签分布异常数据漂移 / 模型版本发错预测类别比例对比训练集这个表可以直接贴在项目README里。遇到问题先按表排除再往深层看。排查问题最怕的是“觉得没问题”有记录、有日志、有可对比的基线是AI工程的基本素养。6. 最后再分享几个小经验6.1 先跑通再优化别指望一次完美我最早做AI工程项目时总想一次性把监控、CI、多环境全部配好结果每天在配置文件里打转连模型的接口都没跑出来。后来改成“最小可用版本优先”今天能用一个curl调通接口就是胜利然后一点一点把缺失的部分补上。工程能力是“被迫优化”出来的不是设计出来的。6.2 每一次失败都值得留点痕迹建议大家按项目建一个LOG.md记录每次遇到的核心问题和解决过程。这个文件会从记录工具变成你的学习资产。两个月后再看你会发现自己踩过的坑越来越少。我记录的很多排查套路后来都变成了上表中的固定条目。6.3 模型很小的时候工程问题会显得“不必要”但当你开始处理更大模型、更多请求、更复杂的业务规则早期那些看起来“麻烦”的工程习惯——固定依赖、统一预处理、保存旧模型、留日志——会突然变成救命稻草。我从一开始就坚持这套习惯后来接手的几个老项目能迅速定位问题靠的并不是模型调参能力而是可以把问题快速定位、可以在五分钟内回滚的工程闭环。从零开始做AI工程真正值得你投入时间的永远是这套闭环本身。