尧图精选

AI工程从零搭建:环境、数据、训练、部署全链路实践

🕒 发布时间:2026/10/1 6:09:32 📁 来源:尧图网络
从ai-engineering-from-scratch这个标题说开去。很多人一听到AI工程这四个字第一反应是“我是不是得先读个博士”或者“先刷完几百节视频课”。实际上这两年我自己带过不少转行的朋友也帮团队搭建过完整的AI落地方案最大的感触是AI工程根本不是某个神秘算法而是一整套从空目录开始把一个想法变成稳定服务的工程体系。市面上多数教程只教你怎么调模型却没人告诉你模型之外还有数据、实验、部署、监控这一大堆事。这篇就完全基于“从零开始”的视角把整条链路拆开揉碎讲清楚适合准备入行AI工程、或者已经在做机器学习但觉得乱成一团的朋友。1. AI工程到底在解决什么问题1.1 它不是“调包”而是“造系统”先说个容易误解的点。很多人以为AI工程就是把sklearn或PyTorch装好然后运行一段别人写好的训练代码等loss降下去就算完事了。真做起来完全不是这回事。我见过太多的demo在Jupyter Notebook里跑得欢天喜地到生产环境就翻车。原因是Notebook里你只需要跑一个单元格但生产环境要处理的是数据从哪里来、代码怎么打包、模型怎么上线、接口怎么监控、效果变差了怎么回滚。这一整套东西就是AI工程。和普通软件开发对比会更直观普通后端工程输入输出相对固定逻辑可预期测试断言好写。AI工程的输入是数据数据会变、会脏、会缺输出是有概率性的预测不能保证百分百正确。普通工程重构代码就完事AI工程重构完了还得重新验证模型效果。所以AI工程的核心词是“不确定性管理”。这不是一句空话。数据分布会漂移模型效果会衰减你以为训练好的模型能一直用实际上三个月之后它的准确率可能就掉得一塌糊涂。工程化的意义就是在这些不确定性面前还能保证系统稳定运行、出了问题能快速定位修复。1.2 一条完整的AI工程链路如果只给一个总览我通常会画这样一条链路业务问题 → 数据采集 → 数据清洗 → 特征工程 → 模型训练 → 实验评估 → 模型部署 → 服务监控 → 反馈迭代这个链路里每一个环节都可能卡住整个项目。我自己见过最典型的卡点是团队花了80%的时间搞模型调参结果上线后才发现数据源头有一个隐藏的bug导致训练和推理时的数据分布根本对不上——之前所有调参的工作全部白费。所以说AI工程最重要的是“全局视角”。你要知道现在卡在哪个环节也要知道每个环节跟其他环节怎么衔接。数据格式定义错了后面整个管线跟着遭殃评估指标选错了模型看起来分高业务上却一点价值都没有。1.3 工程化思维的三个支柱总结我自己的经验AI工程化想得通就靠三件事第一可复现。今天跑出的结果三个月后、换一台机器、换个同事来跑应该能拿到一模一样的结果。这点比想象中难涉及随机种子、环境版本、数据版本多个方面。一旦做不到可复现后面所有优化和排错都会变成玄学。第二可观测。模型不光要在训练时看loss曲线上线后还要看线上指标。预测耗时、请求量、置信度分布、数据漂移程度这些都得有监控。否则模型哪天悄悄变傻了你只能等客户投诉了才发现。第三可维护。代码要模块化数据要版本化配置要声明式。换一个人也能看懂你的流程调整一个参数不用改代码这样项目才能持续演进。很多初学者的项目就是一个巨大的Notebook堆满了变量这种代码两周之后连自己都看不懂了。2. 从零搭建AI工程环境2.1 环境管理的第一道坑Python版本混乱真正从零开始做AI工程第一个拦路虎不是显卡而是Python环境。不同框架对Python版本要求不同老项目可能卡在3.8新项目升到3.11以上效果更好。PyTorch和TensorFlow各自的版本又和CUDA版本绑定。如果直接用系统Python装几个项目就乱套了。我个人的建议是直接用版本管理工具不要手动改系统Python。macOS/Linux推荐pyenv可以随时切换全局或目录级别的Python版本。装完pyenv后pyenv install 3.11.9然后在项目目录执行pyenv local 3.11.9就锁定了这个项目的解释器版本。这个步骤看起来基础但非常重要。见过太多人跑到项目里直接python train.py结果用的根本不是项目想要的Python版本各种ModuleNotFoundError或编译错误蹦出来排查半天发现是环境串了。2.2 依赖管理venv该退场了环境光有Python版本还不够依赖包的管理同样关键。网上老教程都教你python -m venv .venv然后pip install -r requirements.txt这个流程能跑但在AI项目里有个致命的痛点速度慢、不讲版本卫生。pip install在安装大量依赖时容易产生依赖冲突而且requirements.txt只锁版本不锁传递依赖的版本。你今天装出来能跑同事下个月装同样的requirements就报错了因为某个间接依赖更新了。我这两年已经全面换到uv这个工具链上它带来的改变非常大# 初始化新项目 uv init ai-engineering-demo # 添加PyTorch等深度学习依赖 uv add torch torchvision pytorch-lightning # 一键同步环境 uv syncuv比pip快到几个量级而且它生成的uv.lock会锁定所有传递依赖的精确版本真正实现“同一份锁文件全世界跑出同样的环境”。另外uv底层自动做了缓存复用多台机器之间拷个缓存目录离线也能装新项目这对团队协作很有用。如果你是初学者踏踏实实用uv比用conda省心得多。conda的base环境经常被搞成大型垃圾场一个环境里装了几十个互不相关的包出问题了谁都说不清是怎么来的。2.3 目录结构从第一天就要规划好从零开始做项目目录结构决定后期的维护心态。散落的脚本文件和无数个test_final_v2.py会在项目第三天开始就让你想跑路。建议直接采用分层目录结构ai-engineering-demo/ ├── configs/ # 存放配置文件 ├── data/ # 数据文件原始/清洗/特征 ├── models/ # 模型保存目录 ├── notebooks/ # 探索性分析的Notebook ├── src/ # 真正会进生产环境的源码 │ ├── data/ # 数据加载与清洗逻辑 │ ├── features/ # 特征工程代码 │ ├── models/ # 模型定义与训练逻辑 │ └── serve.py # 推理服务入口 ├── tests/ # 单元测试 ├── pyproject.toml └── uv.lock这个结构的核心思想是“数据和代码分离、实验和生产分离”。你可以在notebook里随便试但定型之后就收进src进到正经的工程流程里去。同时data目录里我会约定原始数据只读所有衍生数据不要覆盖原始文件这样出了任何问题都可以溯源。3. 数据工程AI项目真正的分水岭3.1 数据比你想象的更值钱很多入门者和半吊子项目死在数据这一关。模型结构抄一抄开源实现训练代码网上遍地都是但数据质量和数量是你和别人的本质区别。同样一个文本分类任务有人用几千条标注数据就能吊打用几万条数据却标得乱七八糟的模型。数据工程在AI工程里地位很高但经常被低估。一个完整的项目数据准备的耗时通常会占六成以上。不要觉得这是枯燥的体力活它实际上决定了你的模型上线后能走多远。3.2 数据获取与初探第一步当然是获取数据。常用渠道开源数据集平台如Kaggle、Hugging Face Datasets适合入门和打比赛。业务库这才是工业场景的主流。直接查数据库或者用API拉取日志最贴近真实分布。爬虫法律合规前提下做少量补充自己用可以商用要慎重。拿到数据后别急着建模。先把数据好好看过一遍字段类型、缺失比例、类别分布、时间范围、单条样本长什么样。比如文本数据实际打印几十条出来读一读你会立刻感受到什么叫脏数据——表情符、HTML标签、重复字符、错误编码全部都要处理。数据可行性分析这块我会直接记几个核心指标总样本量正负样本比例缺失值比例最高的字段类别字段的基数这些指标会决定你要不要换解题方向。正负比例严重失衡就别用直接交叉熵了缺失值高达90%的字段最好的处理方式可能就是丢弃而不是绞尽脑汁填充。3.3 清洗与处理的实用套路数据清洗没有银弹但有一套固定的套路可以套用。我通常按这个顺序走第一步去重。按关键业务键去重而不是按整行完全重复去重。比如用户点击日志同一个用户同一秒的点击可能就是重复上报但内容可能不同。第二步处理缺失值。先区分缺失的语义是“用户没填”还是“系统没记录”这两类缺失的填充方式完全不同。数值型可以用中位数或均值填充但分布偏斜严重的时候中位数比均值靠谱得多。第三步异常值处理。用百分位数或者3-sigma法粗筛一遍但别机械地删除。很多异常值本身携带着信号比如支付金额特别高的用户删除之前先想想是不是大客户。第四步类型统一。日期字符串统一格式文本统一大小写或正则清洗类别标签统一映射表。这步看起来笨却能省掉调试时一多半的报错。一个实用的技巧清洗逻辑必须写成函数并且有测试。我见过太多人在Notebook里手搓清洗步骤换一个数据集就全重新来过。写成src/data/clean.py里的clean_raw_data(df) - df然后用pytest跑几个断言保证重复执行永不出错。这样每次能节省大量时间。3.4 数据版本管理被忽略但极其重要普通的工程问题里大家会把代码纳入git但很少会把数据也纳入版本管理。这个遗漏在AI工程里是个隐患。数据集是会更新的今天和昨天的数据不一样跑出来的模型也不一样。如果你的队友A用昨天的数据训练你用今天的数据训练两个人拿到不同的指标就存在复现风险。处理方案是引入数据版本管理工具目前比较主流的是DVCData Version Control。它的工作方式是这样的数据本身不直接扔git里而是上传到远程存储本地磁盘、S3、NAS都行git里只记录一个指向具体数据版本的元信息。# 在项目中启用DVC dvc init # 将data/目录加入版本管理 dvc add data/raw/dataset_v1.parquet # 推送远程存储 dvc remote add myremote /mnt/nas/dvc-store dvc push之后每次更新数据集都会在git里多出对应的.dvc文件。想复现某个旧实验只需要git checkout到对应的commit然后dvc pull拉回当时的数据模型结果就完全可复现了。这套机制在团队协作里的价值非常大。3.5 训练集、验证集、测试集是怎么划分的划分数据集的正确姿势比很多人想的讲究得多。普通的随机划分在大部分场景下能用但有几个坑必须避开。第一个坑是时间穿越。预测未来的模型不能用未来的数据训练。像时序预测或者金融风控这种场景必须按时间切分早段时间做训练晚段时间做验证和测试严禁随机打乱。第二个坑是数据泄露。如果同一个用户出现在训练集和测试集里模型实际上就是在背答案。我自己的项目一般用这样的划分策略基础随机划分适合数据之间相互独立的场景。按时间划分适合有明显时序依赖的场景。按业务键分组划分比如按用户ID分组保证同一个用户的数据不会横跨训练集和测试集。每种划分都用代码写清楚并记录下划分的随机种子。不然换个随机状态指标可能就差了几个点你根本不知道是模型问题还是数据划分问题。4. 模型训练与实验管理让过程可追踪、结果可比较4.1 先跑通基线再谈调优新手常见的毛病是一上来就想用最先进的模型花了几个星期搭结构结果效果还打不过一个简单的逻辑回归。我的建议永远是先用最朴素的方案快速做基线。文本分类先用TF-IDF 逻辑回归图像分类先跑通一个ResNet的预训练版本推荐系统先用一个矩阵分解。跑通的最短路径有一个巨大的好处——它让你快速验证数据管线有没有问题、评估流程是否正确、服务端能不能接住输出。基线模型不需要多强够用就行。等基线跑通后你再往深度学习的方向换模型结构这时所有的对比都有了一个锚点永远知道“新方案比基线好多少”。4.2 实验追踪工具的选取逻辑模型训练是个探索过程你一定会改参数、换结构、调数据。如果不记录很快就会陷入混乱。我见过有人用文件名记录实验版本model_v1.py、model_v2_final.py、model_v3_actual_final.py。用不了多久就彻底不知道哪个文件对应哪组实验了。正确做法是用实验追踪工具集中记录每一次实验的配置、指标、产物。当前主流的选择我做个简单对比工具特点适用场景MLflow开源免费支持实验追踪、模型注册、部署一站式团队协作的通用选择Weights Biases可视化漂亮数据看板交互流畅个人研究者和小团队TensorBoard随TensorFlow/PyTorch集成启动最快快速看loss曲线和梯度分布DVC一体化管数据和实验已经用DVC管理数据的项目以MLflow为例在你的训练脚本里加入几行import mlflow with mlflow.start_run(): # 记录超参数 mlflow.log_param(learning_rate, 0.001) mlflow.log_param(batch_size, 64) # 训练... # 记录指标 mlflow.log_metric(val_accuracy, 0.832) mlflow.log_metric(val_loss, 0.244) # 保存模型文件 mlflow.pytorch.log_model(model, model_dir)跑完训练后你用浏览器打开UI所有实验的指标一目了然还能按参数字段筛选排序直接找到效果最好的一组。4.3 实验设计的工程规范实验管理工具解决的是“记录”问题但要让实验结果真正有说服力还得在实验设计上建立规范。我自己在项目里强制执行几条规则每次实验只改一个变量。同时改了模型结构和学习率出了好的结果你也不知道是谁的功劳。固定随机种子。至少在数据划分、参数初始化、数据增强这几个环节固定种子否则同样配置跑两遍指标都不同对比没有意义。每个实验必须记录数据版本、代码版本、依赖版本。这是可复现的地基。定期清理无效实验。UI里堆了上百个以untitled命名的实验谁看了都头大不如每次实验命名带上日期和改动点比如20250412-lr001-baseline。4.4 训练监控与性能分析方法训练过程不是丢个脚本让它跑就完事了中间要盯好几个信号。第一条信号是loss曲线。下图只是直觉描述你可以用TensorBoard观察如果训练loss持续下降而验证loss也在持续下降说明模型没毛病继续跑如果训练loss下降、验证loss不降反升典型的过拟合信号马上准备加正则化或提前停止若两个loss都不降先看是不是学习率设置得过大或过小。第二个信号是数据加载速度。AI工程里GPU空转是常见的浪费。看到gpu utilization在20%而dataloader在忙碌说明瓶颈在数据侧。我会用nvidia-smi观察GPU利用率如果长期上不去优先排查数据读取的多进程num_workers够不够以及磁盘IO是不是瓶颈。数据预处理尽量在GPU训练前做完别每次迭代都去读一遍原始文件。第三条是验证集上的各项指标。光看loss不够分类问题要看准确率、精确率、召回率、F1回归问题要看MAE/RMSE。指标要根据业务选比如正负样本极不平衡的广告点击预测准确率是没意义的基本看AUC和召回率。4.5 训练不收敛的常规排查法训练几小时下来loss还是震荡不动这事很常见。给个我自己的排查顺序先把学习率调小比如现有的除以10。学习率过大是最常见的直接原因。检查数据标签有没有错。亲手抽20条训练数据看输入输出有时候是数据读取时索引错位了。初始化改为更稳定的方式或直接加载在ImageNet/其他大语料上预训练权重。如果使用的是PyTorch Lightning或Keras可以把batch_size调成1跑两步看单样本能不能过拟合。如果单样本都学不进去问题基本出在模型结构或者标签处理逻辑上。5. 模型部署与服务化5.1 从训练脚本到推理服务中间隔着一层“导出”很多人训练完模型就算跑完流程了但AI工程必须打通最后一公里把模型变成一个能对外提供服务的东西。第一步是模型导出。PyTorch的model.state_dict()只是权重字典推理时要免去动态图开销通常导出为TorchScript或者ONNX格式。ONNX的好处在于中立的开放式标准方便之后在CPU和不同框架上优化推理。import torch model load_best_model() # 动态输入维度转静态 dummy_input torch.randn(1, 3, 224, 224) model.eval() torch.onnx.export( model, dummy_input, model.onnx, input_names[input], output_names[output], dynamic_axes{input: {0: batch_size}, output: {0: batch_size}}, )导出后可以先用onnxruntime加载验证一次结果跟PyTorch一致不一致顺便测下推理延迟。我遇到很多次导出后精度掉了一两个点多半是模型里用了一些不支持导出的自定义算子这时就要检查一下模型结构。5.2 用FastAPI搭一个标准推理接口模型的对外服务我用FastAPI居多写起来快、自带文档、性能也不差。一个典型预测接口长这样from fastapi import FastAPI from pydantic import BaseModel app FastAPI() class PredictRequest(BaseModel): text: str class PredictResponse(BaseModel): label: str confidence: float model load_onnx_model() app.post(/predict, response_modelPredictResponse) async def predict(req: PredictRequest): probs model.predict([req.text])[0] label probs.argmax().item() return PredictResponse(labellabels[label], confidenceprobs.max().item())这些看似普通的接口代码里我一般会埋几个“工程细节”请求里带request_id日志、错误追踪、监控都能串起来。推理耗时超过某个阈值要打印慢请求日志方便优化。模型加载不要放在请求处理器里应该进程启动时加载一次全局复用。模型版本号要体现在接口上要么通过请求字段要么通过URL路径否则模型悄悄变了都不知道。5.3 推理性能优化从冷启动到高并发模型服务刚上线时常遇到的性能问题是第一次请求特别慢有时候要好几秒。这个叫冷启动主要是模型加载和初始化也需要时间。解决办法是提前预热启动时故意跑一次全流程推理让所有缓存路径都走一遍再对外开始接流量。高并发场景下常用的优化有批处理。把多个请求攒起来一次性送进模型GPU和CPU利用率都更好。代价是单请求延迟会上升一点点但吞吐量大幅提升。模型量化。将float32的权重转为int8常见工具如ONNX Runtime的量化接口模型体积缩小约四倍速度提升几倍代价是准确率可能会掉一两个点业务上能接受就做。缓存常用结果。如果业务里很多请求内容相同可以加一个LRU缓存这类查询直接返回性能提升非常明显。我做文本分类项目时最常见的优化组合是ONNX Runtime 3倍精度量化 动态批处理单核CPU上的吞吐量跟原始PyTorch相比提升了一个数量级延迟还能控制在几十毫秒级别。5.4 容器化与部署部署的基本单元我建议用Docker。写一个干净的Dockerfile把模型、依赖、服务代码全部打进去。FROM python:3.11-slim WORKDIR /app COPY --fromghcr.io/astral-sh/uv:latest /uv /uvx /bin/ COPY pyproject.toml uv.lock ./ RUN uv sync --frozen --no-dev COPY src/ src/ COPY models/ models/ EXPOSE 8000 CMD [uv, run, python, src/serve.py]几个实际踩坑的经验镜像里别装GPU相关的开发包。NVidia驱动是宿主机提供的容器里只需要装CUDA runtime和cuDNN就够用Conda全家桶全部带进镜像的话镜像体积直接翻好几倍。正式环境用--frozen安装依赖保证锁文件与pyproject.toml完全一致不产生意外升级。模型文件使用硬链接或直接挂载避免镜像反复构建导致模型重复拷贝每次构建都推几个GB的包到处都是构建时间长了谁也受不了。5.5 线上监控与模型回滚部署只是起点。模型服务一旦上线就要时刻盯住质量。我通常搭建这么一套监控指标业务指标预测准确率或人工反馈的用户满意度有标注回流机制最好。延迟指标P50、P95、P99响应时间。P99波动比平均值更灵敏。流量指标QPS、请求来源分布。数据漂移指标线上输入特征分布与训练时的分布做差异检测用PSI或KS统计量。调用方异常码5xx比例突然上升先查代码和依赖再查模型。漂移指标我格外重视。模型的效果衰减往往始于输入数据分布的漂移等业务指标变差才被发现时往往附近很多问题已经产生了。建议定期比如每天或每周生成一份线上特征分布对训练集分布的PSI报告结合告警阈值一旦超过警报就触发人工校验。模型版本管理与回滚机制要与部署流程深度绑定。新版模型先发灰度流量比如切10%流量跑几天看指标稳定后再逐步放大到100%。一旦发现效果回退直接把流量切回稳定版本。这个流程听起来繁琐但能救这个项目。6. 常见问题与排查技巧实录6.1 环境类问题速查表症状典型原因排查与解法安装包时报ETX/protocol错误pip/uv需要走镜像源配置国内镜像源或公司内网源CUDA out of memory学习率/batch过大或显存碎片调小batch_size / 开启梯度累积cuDNN版本不一致CUDA runtime与框架冲突检查torch.version.cuda和nvidia-smi是否对齐容器内尤其注意模型推理GPU不跑torch默认用了CPU检查torch.cuda.is_available()、张量设备device训练卡在数据加载DataLoader多进程设置不够或磁盘IO慢调大num_workers/prefetch_factor换SSD环境类问题往往是最浪费时间的解决思路就一条从一开始用锁文件和容器固化环境别让“这机器上能跑那机器上跑不了”成为项目常态。6.2 数据与实验中的隐蔽大坑数据泄露是我在讲座和答疑时最常讲的一个问题。简单说如果模型在训练时已经“见过”测试集的信息那它在测试集上的分数是虚高的上线之后会原形毕露。举一个真实的例子之前做一个用户画像项目样本划分时没按用户ID分组只是简单随机划分。同一个用户的多条行为记录分到了训练集和测试集两边模型学到的其实是对单个用户的记忆而不是对某个群体的规律。离线AUC高达0.95上线后直接崩塌到0.6左右。排查了整整一周最后发现就是数据划分的问题。另一个隐蔽坑是label泄漏进特征。做预估类任务时特征处理发生在样本点“当前时刻”之后等于用了未来信息。比如用整段会话的统计量当作某条行为的特征但实际预测时根本拿不到整段会话的完整信息。这类问题建模时想不到一定要反复审计特征的时间语义。6.3 实操中的排查技巧合集排查训练问题我常年靠一张“二分排查”的心法训练效果有问题先确认数据没问题再怀疑模型先确认代码能复现再怀疑调参水平。具体操作上数据侧写一个快速脚本打印10条训练数据的输入、输出、shape对照看一遍。环境侧重新从锁文件安装一个新环境跑一次小规模的训练排除本机器“缓存脏环境”导致的干扰。模型侧拿一条固定输入比较PyTorch推理和ONNX推理的结果数值上误差在1e-5级别才是正常。服务侧单次POST请求测试然后做并发测试观察延迟和错误率定位是模型慢还是框架限流。这些技巧越用越熟练之后排查定位会越来越快。说实话AI工程里的错误大多不是什么高深的理论问题就是一些工程细节被忽略了数据没对齐、环境不一致、版本没锁住、监控没跟上。我自己在这条路上踩过的最大的坑是早期一个项目跑完训练后没有保留数据集版本后来想复现当时的实验结果怎么搞指标都对不上最后花了整整三天重新凑数据。所以我现在对每一批数据都强制打上版本代码也是模型也是——这个习惯让我在后面所有项目里都省了不少无用功。如果你是刚开始学AI工程也把这套习惯从第一天建立起来把环境、数据、代码、模型都当作需要版本化的对象来管理你的项目会少掉一大半让人头疼的问题。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →