ARTICLE DETAIL

资讯详情

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

AI工程从零到一:数据、训练、部署与监控全链路实战指南

AI工程从零到一:数据、训练、部署与监控全链路实战指南 1. 先别急着写代码AI工程和调模型是两码事我见过太多人栽在同一个地方以为AI工程就是从零开始学PyTorch、把公开数据集跑通、然后顺手刷个准确率。等真正进了项目组才发现自己连从零搭建一套AI系统的门槛都没摸到。这里说的AI工程不是把模型训练出来就交差而是从业务问题出发把数据、模型、训练、评估、部署、监控这条链路完完整整地搭建起来并且让它稳定、可复现、能迭代。这篇内容就是我从零开始趟这条路的完整记录适合刚入门的算法工程师、想转AI方向的后端开发以及所有被模型跑通了但上不了线折磨过的人。先说清楚一个容易被忽略的事实AI工程和算法研究是两种不同的游戏。研究追求的是在标准数据集上刷出更好的指标哪怕实现过程混乱也无所谓只要论文能复现结论。工程追求的是在真实业务场景里稳定产出价值代码要能维护数据要能追溯换个人接手也能继续往下走。如果你的目标是把AI能力落地成产品那么打通全链路这件事比把某个指标刷到SOTA重要得多。一个完整的AI工程生命周期大概是这样的业务需求拆解 - 数据采集与清洗 - 标注与质量校验 - 特征工程/预处理 - 模型选型与训练 - 离线评估 - 部署上线 - 在线监控 - 持续迭代。这个环里每个环节都可能让你翻车而且后端的坑往往比前端更难排查。我在早期犯过最蠢的错误就是一头扎进模型训练结果数据管道在测试集上恰好能用换到真实数据就崩了。所以这篇内容我不会只讲训练而是把整条链路上我认为最重要的东西拆开来说环境怎么配、数据怎么管、训练怎么记、服务怎么上、坑怎么避。每一条都是踩过之后才真正理解的希望能帮你省掉几个月的摸索时间。2. 从零起步先把数据-训练-评估这条主链路跑通2.1 环境准备用顺手的最小组合从零起步环境搭建是第一道门槛。很多人卡在CUDA版本、Python版本、依赖冲突上一折腾就是两三天极其消磨耐心。我现在的标准组合是Python 3.10 PyTorch 2.x conda或uv管理环境。没必要追求最新版本稳定是关键。创建环境的命令很简单但有个细节值得注意conda create -n ai_eng python3.10 -y conda activate ai_eng pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118这里有个经验PyTorch的CPU版本和CUDA版本是两套独立的安装源如果你不确定机器有没有N卡先装CPU版跑通代码后面再换CUDA版。另外不要用conda安装大型深度学习框架conda在依赖解析上慢且容易锁版本pip配合venv反而更利索。2024年之后用uv管理依赖体验更佳它是rust写的速度比pip快一个数量级一条命令就能创建虚拟环境uv venv ai_eng --python 3.10 source ai_eng/bin/activate uv pip install torch transformers4.38 datasets关于CUDA我的建议是在项目初期就把版本钉死。一个可以长期用下去的配置是CUDA 11.8 PyTorch 2.1.x这个组合兼容性极好几乎所有主流模型库都支持。升级CUDA是大坑你不仅得重装PyTorch还得检查所有编译过的扩展包比如flash-attention搞不好就是半天没了。2.2 数据准备才是真正的重头戏环境配好之后新手最常犯的错误是立刻翻模型的训练代码想赶紧看效果。这个冲动我理解但真实项目的经验是数据准备的时间占整个项目60%以上而且数据的质量直接决定模型的天花板。至少在三件事上下功夫。第一给数据定schema。即使做得粗糙也建议在项目开始就用JSON Schema或类似方式定义字段格式和约束不要用哪个脚本能跑就按哪个逻辑处理数据。否则后面每加一个数据源清洗代码就要重写一遍。我的做法是先写一份data_schema.yamlfields: - name: id type: string required: true - name: text type: string required: true - name: label type: integer enum: [0, 1, 2] required: true - name: source type: string required: false第二把数据校验做成自动化的一步。不要相信这次数据是干净的那种鬼话。用pandas-profiling或great-expectations跑一下profile检查缺失率、类别分布、id重复情况。我见过最离谱的情况是训练集里有一条重复了上万次的样本模型在验证集上表现正常线上全乱套。第三手工看一眼原始数据一定要看原始样本而不是聚合统计。统计只能告诉你分布不能告诉你具体内容里有什么异常。比如很多文本分类项目里标签和文本长度存在隐含的相关性而这种相关性在真实推理时根本不存在。不看原始样本你根本发现不了这种毒数据。2.3 训练脚本的工程化习惯当模型代码准备跑之前建议先把下面几个习惯刻进肌肉记忆不然项目推进到中后期你会痛苦不堪。固定随机种子。这句话说了无数遍但我还是见了很多代码里只设了torch.manual_seed(42)忘了np.random.seed(42)也没设random.seed(42)。这三个都要设。更严谨的做法还包括设置torch.cuda.manual_seed_all(42)以及把DataLoader的worker_init_fn也绑定种子。不这样做的话你训练十次得到十个不同的结果连bug都复现不了。Checkpoint策略必须提前设计。只存最后一个epoch的权重是灾难。至少要存两份一份是按验证集指标选的最优模型一份是最后一轮结束时的模型两者都要带上optimizer状态和当前的epoch数。这样一来训练中断了能恢复指标异常了能回溯。日志要能直接对比。建议用一个简单但固定的格式记录epoch、train_loss、val_loss、每个关键指标、学习率、显存占用。我习惯把日志直接打成一个CSV文件同时在不破坏结构的前提下允许读取。为什么这样因为后面做实验对比时我从来不需要重新跑一遍模型来回忆上一次的参数是多少。2.4 评估指标及格线意识训练跑通之后第一个要冷静思考的问题是我的模型到底及格了没有不是在验证集上看了个90%准确率就高兴了。要结合业务判断这个准确率是随机猜出来的吗样本类别均衡吗正例占比多少有一个更细致的问题是评估集太大或太小。通常训练集如果有10万条验证集至少应该留出1万条太少的话指标方差会特别大。造成的结果是上一次epoch模型A指标是0.91下一次epoch模型B指标是0.92你都没法判断是B真的变好了还是随机波动。关于这个环节我的建议是刚起步时不要拘泥于复杂的metric体系先盯紧一个主指标和一个辅助指标。比如分类任务只看F1和AUC排序任务只看NDCG或AUC有一致性判断就加一个Grouped AUC。主指标是给项目评审看的辅助指标是给你自己判断模型有没有学坏用的。等以后项目复杂了再逐步建立完整的多指标看板。3. 关键一跃从跑通脚本到可复现的机器学习流水线3.1 实验追踪没有记录等于没做跑通数据-训练-评估闭环之后你的工作模式大概是改一个参数跑一把看指标。这个循环进行到第30次问题就来了你还记得第17次实验用了什么学习率、什么batch size、什么数据预处理方式吗大部分人已经记不清了。解决这个问题的标准答案是实验追踪工具。开源方案里我推荐MLflow它轻量、容易上手一条命令就能起一个tracking servermlflow server --backend-store-uri sqlite:///mlruns.db --default-artifact-root ./mlruns在训练代码里只需要做一件事——把所有的超参数和指标传给它import mlflow with mlflow.start_run(): mlflow.log_param(learning_rate, lr) mlflow.log_param(batch_size, batch_size) mlflow.log_param(model_name, model_name) mlflow.log_metric(val_f1, val_f1) mlflow.log_artifact(confusion_matrix.png)有了记录你就能在两分钟之内回答到底哪个配置最好为什么好这个问题。不要高估自己的记性也不要高估代码注释的可靠性实验记录落库是工程素养的开端。3.2 版本管理代码、数据、模型各管各的代码用Git管理这件事不需要我多说。但很多人忽略了数据和模型也需要版本管理。数据版本管理我的方案是DVC。它和Git的配合逻辑很清晰数据文件不直接压在Git仓库里而是放到本地目录或云存储DVC会在Git仓库里记录一个指向数据文件的元信息文件。当你恢复一个旧的代码commit时DVC会把对应的数据版本一起拉下来。dvc init dvc add data/raw git add data/raw.dvc .dvc/config git commit -m add raw dataset v1模型版本管理最省事的做法是跟实验追踪体系绑定。MLflow的Model Registry能把每一个训练出来的模型都登记注册锁定生产版本预发布版本等状态。任何一次上线、回滚都有据可查。这个习惯在项目只有你一个人的时候体现不出价值一旦开始多人协作或跨团队交接就知道这东西有多重要。3.3 流水线编排把散落的步骤变成标准流程当你面对的不再是一个训练脚本而是数据清洗 - 数据切分 - 特征构建 - 模型训练 - 评估 - 生成报告六个步骤时是时候把它们编排成一条标准流水线了。给两个层级的选择。如果只是个人项目和规模不大用Makefile就能把串行步骤组织得很好prepare: python scripts/01_prepare_data.py split: python scripts/02_split_data.py train: python scripts/03_train.py evaluate: python scripts/04_evaluate.py pipeline: prepare split train evaluate当你需要处理多级依赖、失败重试、调度监控或者需要跨机器执行时再上Prefect或Airflow。我个人更倾向Prefect因为它用Python原生代码定义流程对数据团队的认知负担更小。from prefect import flow, task task def prepare_data(): ... task def train_model(): ... flow def ai_engineering_pipeline(): prepare_data() train_model() if __name__ __main__: ai_engineering_pipeline()流水线的价值不只是自动化它强迫你把人肉跑了一百遍的脚本变成一段稳定可复用的流程。把你的个人经验沉淀到代码里这才是工程化和从零起步的质变。4. 真正意义的上线模型部署与推理服务化4.1 先想清楚你需要的部署形态是哪一种很多初学者以为部署就是把模型封装成一个HTTP接口调用一下就行。实际上部署形态的选择直接影响你的整体架构用错了一开始轻松后面会非常痛苦。场景推荐形态典型工具/框架在线实时推理同步HTTP/gRPC服务FastAPI Triton / TorchServe离线批量推理异步任务队列Airflow / Prefect 批处理脚本边缘/端侧转换轻量化ONNX Runtime / TensorRT / TFLite大模型场景动态批处理显存优化vLLM / Text Generation Inference我个人的建议如果你只是把一个中小规模的模型上线FastAPI是最快的起点。它原生支持异步文档自动生成方便别人调试。但FastAPI本身不负责模型管理所以深水区的做法是接上Triton或TorchServe做模型加载、并发调度和GPU显存管理。4.2 模型服务的基本功接口约定与容错设计接口约定这部分是新人最容易忽略却最影响联调的。一个模型服务的接口设计至少要考虑以下几件事请求和响应schema要固定。用Pydantic写清楚字段类型、约束、默认值。比如文本分类接口的输入就是一个text: str输出是label: int和probability: float。不要返回一个松散的字典那样下游能容忍但迟早出乱子。批量推理要作为第一公民来设计。单个请求走一次模型推理在工程上非常浪费GPU的利用率很低。所以服务端应该支持传入一个list内部做动态padding和batch。下面是典型的批量推理伪代码from fastapi import FastAPI from pydantic import BaseModel app FastAPI() class BatchInferRequest(BaseModel): texts: list[str] app.post(/predict) async def predict(req: BatchInferRequest): results model.batch_predict(req.texts) return {results: results}错误处理要有策略。网络超时、下游依赖挂掉、输入格式异常这些都要在服务层兜住。最简单的方式是统一返回一个错误码结构并配上HTTPException或者自定义的响应模型。不要因为一条坏数据让整个服务进程崩溃。4.3 性能优化三板斧量化、批处理与缓存模型服务上线之后最先被挑战的一定是延迟和吞吐。就我的实战经验而言性能优化优先从以下三板斧开始。第一板斧批处理。GPU推理的吞吐和单条延迟并不是线性关系把batch size从1提到8延迟可能只增加30%但吞吐提升好几倍。实现方式有两种要么在API层让客户端显式传批量数据要么在服务端做连续请求的动态攒批。动态攒批工程上更复杂但能真正提升在线场景的资源利用效率。第二板斧量化和精度折中。如果延迟还是压不下来考虑用ONNX Runtime或TensorRT做加速甚至做int8量化。一个实际经验是很多模型在int8下的精度损失小于0.5%但推理速度可以快2-3倍。做之前先在离线评估集上验证精度别上线了才发现效果崩了。第三板斧缓存。对文本分类、推荐这类请求结果可能有重复的场景做一个以输入文本哈希为key的Redis缓存命中率提升后平均延迟能降低一个数量级。不要所有请求都打到模型上不是所有的查询都值得从头推理一遍。5. 从零到一最常见的五个坑和我的排查思路5.1 环境依赖彻底乱成一锅粥这是从零起步的必经之路几乎没办法完全避免。PyTorch的每个大版本升级可能都会带来ABI不兼容而在此基础上再叠加numpy、pandas、transformers各自的依赖约束就是你看到的dependency hell。我的排查思路从来不是去手动解决每个冲突而是推倒重来。如果发现当前环境已经乱到连pip check都跑不稳就新建一个干净的虚拟环境按requirements.txt重新装。为了让这个过程可控从项目第一天开始就建议用pip freeze requirements.txt记录依赖。如果装了conda还可以额外记录一份conda env export把conda和pip两套依赖都锁住。我在实际项目里还见过更稳的做法用Dockerfile把环境固化下来每次开发直接在镜像里跑彻底摆脱我机器上可以的这种尴尬。5.2 训练loss正常线上效果一塌糊涂这个坑的比例极高。训练和验证阶段模型表现得挺好一上真实请求就露馅。根本原因大多是离线数据分布与线上数据分布不一致也就是常说的distribution shift。我经历过的典型案例训练数据主要来自某几个渠道线上突然出现了一种书写风格完全不同的新渠道数据。模型在书面上看起来泛化能力不错上线后被新的数据模式打崩。要解决这个问题不能只在离线评估集上自我感动。要给线上数据留一条影子通道上线之前把真实流量数据用脚本记录下来脱敏后作为新的评估集跑一遍。这种用线上数据做离线评估的做法是判断模型真实能力最靠谱的手段之一。记住一句话模型通用的基线不是标准的测试集分数而是真实业务分布下的鲁棒性。5.3 评估集作弊数据泄漏才是最隐蔽的杀手评估集上的指标异常偏高有时候是因为数据泄漏。泄漏的形式多种多样最经典的是在切分数据之前做了全局归一化或标准化导致测试集的信息通过均值和方差潜入了训练过程评估自然失真。还有一种更隐蔽的情况实体级别的泄漏。比如做用户行为预测时同一用户的多个样本被同时切到训练集和测试集模型通过用户ID这个特征直接学到了它的行为习惯。训练集和测试集在这种情况下根本没有严格隔离评估结果必然乐观。治本的方法是设计数据切分时遵循实体隔离原则。即按用户ID或物品ID来切分而不是按样本行切分。只要涉及任何用户或物品的抽象表示都要确保一个实体的数据不会同时出现在训练集和测试集。这个原则知道的人多真做对的人少。5.4 评审总问模型挂了怎么办把监控当功能做模型上线后的监控是AI工程里最容易被轻视的环节。我见过太多项目卡在评审会上模型演示效果不错指标也很漂亮但业务方问输入数据变了怎么办、模型效果下降了怎么感知、推理服务挂了谁来报警全场鸦雀无声。把监控当成生产代码的一部分来做而不是有时间再加。至少需要覆盖三个层面服务层监控延迟P99、请求量、5xx错误率用Prometheus Grafana全家桶。模型层监控预测结果分布、平均置信度每天对比和历史基线漂移明显就发钉钉或企微告警。数据层监控输入特征的缺失率、类别分布、业务关键字段的有效率。这里给出一个极简的做法用一个定时脚本每天跑一遍数据统计把结果写入一个指标表然后接一个告警规则。不用一开始就上复杂的MLOps平台先把坏了能知道这件事解决是性价比最高的投资。5.5 个人英雄主义式开发走不远最后一个坑其实不是技术坑是团队协作坑。从零起步做AI工程一开始往往是一人挑大梁代码自己写、数据自己管、模型自己训。这种模式在前三个月没有问题但一旦项目进入维护期或者需要别人接手问题就集中爆发代码没有注释、实验没有记录、数据脚本没有版本化管理、线上服务没有运维文档。我的建议是哪怕只有你一个人开发也要按照随时可以交接的标准来写代码和文档。每个关键目录放一个README说明数据和模型文件的来龙去脉代码注释写清楚为什么这样写而不是在做什么实验记录按时间线整理完毕。这些事看起来繁琐但能防止项目因为一个人的离开而停摆。最后再分享一个心得。从零开始学AI工程最大的障碍其实不是技术难度而是过早地陷入某个框架的某种用法里。你真正应该盯住的是数据、模型、评估、部署、监控这条完整链路的稳定闭环。把链条上每个环节的为什么都理解一遍再一头扎进工具和代码后面你会走得特别稳。
返回列表