尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

AI工程从零到上线:环境配置、数据处理与模型部署实践指南

AI工程从零到上线:环境配置、数据处理与模型部署实践指南 1. AI工程到底是什么先别急着写代码先说个很现实的事情很多人一听“ai-engineering”第一反应是“我要去学深度学习、调参、训练大模型”结果花了两周看神经网络数学推导最后连一个能用的服务都没跑起来。这个方向搞反了。AI工程不是算法研究它解决的是“怎么把一个AI想法稳定、可维护、能上线地做出来”这件事。说白了算法工程师负责“从0到1”AI工程师负责“从1到100”——把模型放进真实业务里让它跑得稳、跑得快、可监控、能迭代。我最初接触“from scratch”的时候也踩过类似的坑觉得所有代码都要自己写才算懂。后来发现AI工程化的核心不是重造轮子而是知道什么时候该用现成的轮子什么时候必须自己调螺丝。比如数据清洗用pandas几行代码搞定你非要手写个解析器那就是在给项目添堵。真正的from scratch是指你从零开始具备搭建整套数据流、训练管道、模型服务、监控体系的能力而不是从零开始实现反向传播。适合谁看这篇如果你刚转行做AI应用开发或者你在做毕业设计、公司内部工具、小规模模型服务发现自己卡在“模型训练完就不知道怎么用了”那这篇文章就是给你写的。我会把整个AI工程链路拆开包括环境搭建、数据处理、训练评估、模型部署、服务监控每个环节给出一套可以直接抄的实践方案再加上我实际跑项目时踩过的坑。2. 从零起步的学习路线不是从数学开始2.1 先定方向再定工具链AI工程领域现在很宽泛至少有四条不同路径一是做推理优化和部署比如用ONNX、TensorRT加速模型二是做MLOps平台管数据集、实验、模型版本三是做LLM应用工程比如RAG、Agent开发四是做传统机器学习系统的工程化特征是特征工程模型训练定时推理。这四个方向的工具链差异很大。我不建议一上来就把“AI工程”当做一个整体去学。你最好先想清楚自己手头要解决什么问题。比如我最初是从“训练好的分类模型要封装成HTTP接口给前端调用”开始的当时的目标很朴素把模型文件和预处理逻辑包成一个服务。这个目标不需要懂什么复杂理论反而逼着我去弄懂怎么用FastAPI加载模型、怎么做输入校验、怎么打日志。等这些跑顺了再去补数据管道和实验追踪就很自然。我对新人的建议是先选一个端到端的小项目比如“预测房价的线性回归模型API”然后把它做成一个完整的工程。所谓完整不是只有训练和推理还包括数据版本管理、结果可复现、服务可监控。这个项目做完你就知道AI工程的大致版图了。2.2 环境配置与依赖管理这是第一道门槛很多教程会直接让你装Anaconda然后pip install一堆包但实际工程里的环境管理要严格得多。我个人推荐用conda创建环境管理Python版本再用pip requirements.txt管理Python包依赖如果项目更正式加上Poetry或uv都行。关键是锁定版本因为AI生态里的库互相依赖太紧密了比如numpy版本变了可能连带影响pandas和scikit-learn。一个实际示范conda create -n ai_eng python3.10 -y conda activate ai_eng pip install --upgrade pip pip install numpy1.23.5 pandas2.0.3 scikit-learn1.3.0 fastapi0.104.0 uvicorn0.24.0 pydantic2.4.0锁版本不是强迫症。我遇到过项目卡在“torch和numpy的兼容性”上原因就是pip默认装了numpy最新版但torch编译时用是旧版接口。这种问题浪费半天查错不如一开始就写死版本号并且把requirements.txt提交到仓库里。另外建议用.gitignore把.conda目录、__pycache__、*.pyc都排除掉。模型文件甚至不要用git管理因为体积太大用dvc或者网盘单独存。2.3 数据集从哪里来以及怎么做版本控制AI工程里最容易被轻视的是数据。很多人在Kaggle上找个数据集run一把train.py就觉得自己会了但真实项目中数据是每天都在变的。今天有一套离线表格明天可能换成了数据库里的新字段后天又有外部接口的数据。如果不对数据做版本管理模型复现就是空话。我自己常用的一套轻量方案是把原始数据放在data/raw/目录处理后的数据放在data/processed/目录然后用DVC记录每个版本的元数据。DVC不是必须的但当你发现“这个模型上周跑出的结果怎么这周复现不了”时DVC就是你追责的最好工具。简单用法dvc init dvc add data/raw/input.csv执行之后会生成一个.dvc文件记录文件哈希。你把.dvc文件提交到git而真正的数据文件可以放在共享存储或云盘上别人拉下项目后运行dvc pull就能拿到对应版本的数据。不要觉得这个流程麻烦。真到了需要回滚、对比实验的时候你会发现没有版本管理等于裸奔。尤其是当你训练时间以小时计结果却因为数据不一致白跑一次那种痛我记忆犹新。3. 核心流程实操从原始数据到能上线的模型3.1 数据处理先把脏数据挡在门外数据清洗没有固定标准但有一些通用的工程化步骤移除重复项、处理缺失值、统一格式、检测离群点。每个步骤都要有相应的日志和输出统计这样后面模型出问题时能判断是数据阶段出了问题还是模型本身的问题。我习惯在每个清洗步骤前后打印行数比如“去掉重复项19871行 - 18752行”这样跟踪数据血缘时会清晰很多。这里有一段我常用的预处理函数骨架展示的是“可追溯”的写法而不是一行大而全的pandas链式调用def preprocess(df): steps {} steps[raw_rows] len(df) df df.drop_duplicates(subset[id]) steps[after_dedup] len(df) df df.dropna(subset[feature_a, feature_b]) steps[after_dropna] len(df) df[feature_c] df[feature_c].apply(lambda x: x.strip().lower()) steps[after_clean] len(df) return df, steps这样每一步都有记录出问题时可以逐步排查。还有一点我强烈建议把“特征处理逻辑”保存成独立的transform对象比如用scikit-learn的Pipeline。训练的时候把它fit到训练集推理的时候直接load进来这样训练/推理之间的预处理一致性才有保障——这是很多新手忽略的点训练时用df[x].mean()做填充推理时忘了保存这个均值导致请求进来后用不同的均值效果直接崩。3.2 训练实验追踪不要只盯loss曲线训练阶段很多人用Jupyter Notebook跑模型跑完就完事了。但工程化视角下你至少要记录实验时间、数据版本、代码commit号、参数配置、最终指标、模型文件路径。这些东西用MLflow能很容易记录自己写个装饰器也行。我用的比较笨但有效的方法是在每个训练脚本里加一个config字典然后把它序列化成JSON和模型文件存在同一个目录里。import mlflow mlflow.set_experiment(house_price_predictor) with mlflow.start_run(): mlflow.log_params({model_type: lgbm, n_estimators: 300, lr: 0.05}) mlflow.log_metric(rmse, rmse) mlflow.log_artifact(model.pkl)说实话一开始用MLflow会觉得很重但它有一个好处你跑了一百次实验后可以按指标排序找最优模型而不是靠记忆。而且它天然支持模型注册后续部署可以直接从注册中心拉模型不需要去翻硬盘找文件。另外关于训练数据的划分我要提醒一句不要在清洗后的整个数据集上直接train_test_split一定要在预处理之前先划分ID或者至少保证没有信息泄漏。比如做时序预测时随机打乱划分就是错误示范会造成未来数据泄漏指标虚高。AI工程里这条线非常细但后果很严重。3.3 模型部署别再“保存个pickle然后写个Flask”很多教程教你pickle.dump(model)后用Flask起个接口这在demo阶段没问题但生产环境会暴露一堆问题并发处理、请求超时、模型加载释放、异常处理、限流、日志。我现在的方案是如果模型较小且使用常规框架直接用FastAPI封装成异步接口如果模型较大或需要GPU推理拆分出一个独立的推理服务和业务服务分离。一个最小可行的FastAPI推理服务from fastapi import FastAPI from pydantic import BaseModel import joblib app FastAPI() model joblib.load(model.pkl) class InputData(BaseModel): feature_a: float feature_b: float app.post(/predict) def predict(data: InputData): x [[data.feature_a, data.feature_b]] pred model.predict(x)[0] return {prediction: float(pred)}注意我用了joblib而不是pickle因为joblib对numpy数组更友好序列化效率更高尤其是带数组特征的模型。接口命名不要用/api/predict太笼统最好带上版本号比如/v1/predict这样后面模型更新时还能兼容老版本。一个更容易踩坑的是模型按需加载。如果每次请求都load一次模型文件那服务压力一大就崩。正确做法是在应用启动时load一次或者用app.on_event(startup)做预加载。模型文件放在本地磁盘和代码解耦这样你更新模型时不需要重新发布代码。3.4 模型评估与监控上线只是开始模型部署后评估不是只跑一遍测试集就完事。你需要持续监控线上数据分布和模型表现是否退化。这个在真实项目里叫“模型漂移”。比如你训练时用的特征平均值是100线上数据漂移到150模型可能就会输出异常结果。最简单的办法是对线上请求的特征做分布统计比如日均值、最大值、空值率设置一个基线然后写个定时任务去比对。我实践过的轻量监控把每次预测的输入输出打印到日志然后用一个定时脚本采集当天日志计算特征的均值和标准差和训练集基线对比。一旦偏离超过阈值就往钉钉或飞书机器人发告警。这个没什么高深技术但非常实用。另外要注意评估指标的选择。分类模型别只看准确率如果样本不平衡准确率会欺骗你。工程上更多用precision、recall、F1、AUC这些指标要分group看比如按用户类型、地区等维度拆开算因为可能在整体看起来不错在某一个细分人群上你的模型就是乱猜。4. 实战项目脚手架设计直接抄作业4.1 目录结构从零开始就搭好框架很多人写AI项目从train.ipynb开始跑到后面乱七八糟最后连自己的代码都看不懂。我从一个完整的AI工程from scratch角度建议用下面的目录结构project/ ├── configs/ # 所有实验配置 ├── data/ │ ├── raw/ │ └── processed/ ├── src/ │ ├── features/ │ ├── models/ │ ├── train.py │ ├── predict.py │ └── utils/ ├── tests/ ├── deploy/ │ ├── api.py │ └── Dockerfile ├── requirements.txt ├── README.md └── .env这里的核心思想是配置和代码分离。所有超参数放在yaml或json里训练代码从配置中读取这样跑不同实验时只需要改配置文件不需要改代码。举个例子# configs/exp1.yaml model: name: lightgbm n_estimators: 300 max_depth: 7 training: test_size: 0.2 random_state: 42然后在代码里import yaml with open(configs/exp1.yaml) as f: cfg yaml.safe_load(f)这样写的好处是每个实验对应一份配置你随时可以回去复现某个实验。不要觉得这个结构前期费事当你需要跟别人协作或自己三个月后再看代码时这个结构能救你一命。4.2 训练脚本的工程化改造我们常见的训练脚本是model SomeModel(lr0.1) model.fit(X_train, y_train) print(model.score(X_test, y_test))这种脚本无法定位超参数、无法记录实验指标、无法复用。我把它改成模块化之后效果完全不同。改造后的train.py至少包含以下这些函数load_data()从数据目录中读取经过预处理的文件build_pipeline()定义特征处理方法与模型组成Pipelineevaluate_model()输出分类报告或回归指标save_artifacts()保存模型、预处理对象、指标、配置用函数划分逻辑而不是一百行代码从上写到下。这样你在train的时候可以单独测试某个函数比如只测load_data也可以在测试里直接调用。4.3 Docker部署的关键细节模型服务最终要跑在服务器上。用Docker可以屏蔽环境不一致但有几个坑必须避开。第一个是镜像大小如果你直接基于python:3.10装sklearn镜像可能有1GB以上。建议用slim或python:3.10-slim作为基础镜像然后安装所需依赖。第二个是机房内网不联网的问题所以pip install时最好在构建阶段完成不要容器运行时再装。第三个是非root用户运行容器增加安全性。一个示例DockerfileFROM python:3.10-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY ./deploy /app/deploy COPY ./models /app/models EXPOSE 8000 CMD [uvicorn, deploy.api:app, --host, 0.0.0.0, --port, 8000]这个Dockerfile在构建时会先把依赖装好然后拷贝代码和模型。模型文件很大建议用.dockerignore排除无关目录只留下必要内容。这些细节直接影响你上线速度和运维成本。5. 常见问题与排查技巧实录5.1 模型训练能跑通但推理结果不可复现这是最典型的AI工程问题。原因通常是几个方面预处理不一致比如推理时没有使用训练时拟合好的scaler或者随机种子没有固定或者数据版本变了。排查思路很简单把训练时的输入数据留一份推理时拿同一条数据输入对比结果。如果结果不一致先检查预处理对象是否与训练时使用的是同一个。我经常会加一个单元测试专门检查“处理同一条数据前后的特征是否一致”。固定随机种子也很重要尤其是在涉及数据拆分和模型初始化时。下面这段代码可以在任何训练脚本的开始处执行import random import numpy as np def set_seed(seed42): random.seed(seed) np.random.seed(seed) # 如果用了torch还要加 torch.manual_seed(seed)这样做至少保证在相同环境和数据下实验结果可以复现。如果你用PyTorch还需要处理CUDA的随机性比如torch.backends.cudnn.deterministic True否则GPU上依然可能有微小差异。5.2 API服务的并发与超时很多新手写推理服务时用的是同步defFastAPI默认在线程池中运行但如果模型推理本身很慢比如超过30秒前端就会直接超时。这时候需要用异步或单独的推理进程来避免阻塞。最简单的办法是把模型推理放到一个后台线程并通过队列去异步化或者直接用Ray Serve、BentoML这类工具管理并发、批处理和请求生命周期。我个人要求是单个预测请求的P95耗时要低于100ms否则就考虑加缓存或者做批处理。比如用了Transformer模型同时来了10个请求如果一个个过GPU性能很差如果可以用积攒请求的方式做动态batch吞吐量会提升很多倍。动态batch的实现在工程上有一定门槛但你可以用torch.utils.data或在框架层面用自动批处理方案。5.3 模型上线后效果和离线评估不一致这种情况十个项目九次会遇到。离线指标高线上效果差最常见的原因是线上数据特征分布和训练集不同。第二个常见原因是在线业务有反馈循环比如推荐模型会影响用户行为导致历史数据有偏这个在冷启动阶段尤其明显。我的建议是上线时先开启shadow mode即新模型与旧模型同时接收请求但新模型结果只记录不对外展示。跑几天之后对比新旧模型的预测分布与业务指标。这一步能有效减少“翻车事故”。另外要对每条预测记录一个业务唯一ID方便后面做标注和归因。6. 我自己做AI工程时养成的几个习惯做AI工程和写算法demo完全两回事。最近几年我从零开始搭过三四个AI项目踩了无数坑让我最受用的不是哪个模型用得巧妙反而是这套工程纪律。第一所有代码都有一个“被删掉的勇气”。AI项目变化极快一个数据处理逻辑今天觉得通用下周就发现需要重构。因此不要在代码里写死各种“临时”分支尽量保持函数小、职责单一。写注释时说明“为什么”而不是“是什么”比如“为什么这里要过滤掉年龄大于80的用户”——因为这可能是数据质量问题而不是业务规则。第二每完成一个阶段就做一次实验记录。哪怕是清洗数据花了半小时也应该在实验文档里记一笔写明数据来源、时间范围、当时的异常处理方式。后续分析模型表现时这些信息非常有用。我用的是一个简单的markdown表格每行记录时间、实验名称、数据版本、模型、指标、备注。第三生产优先。训练代码和部署代码在项目最开始就要一起考虑就算你只是做一个练习项目也要试图用FastAPI把服务跑起来。这能强制你去思考输入输出格式、异常处理、接口版本这些实际事务。我见过太多人在Notebook里调出一个90%准确率的模型最后卡在“怎么发给别人用”这一步。这不是模型问题是工程能力问题。如果你现在正准备从头做一个AI项目我的建议是先花一天时间把目录结构、环境、数据版本控制搭好再开始写训练代码。你会觉得前期慢但后期提速是几何级的。真正的from scratch不是说每个组件都自己写而是你清楚整个系统的每一层是怎么衔接的。希望这篇内容能帮你把这条链路走通。
返回列表