ARTICLE DETAIL

资讯详情

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

AI工程从零到实战:模型部署、MLOps与完整项目拆解

AI工程从零到实战:模型部署、MLOps与完整项目拆解 ai-engineering-from-scratch这个标题在技术社区里越来越常见我第一次看到时其实有点好奇——它不是learn-ai或者ai-for-beginners而是特意强调了engineering。两者的差别非常大前者可能只是让你会用工具后者是真的在教你如何像工程师一样思考、设计和交付一个能扛住真实压力的AI系统。这篇文章我想把自己从零搭建AI工程能力的过程、踩过的坑、以及最终沉淀下来的那套方法论完整拆开来讲内容比较长但都是实操过的希望能帮你少走弯路。这个from scratch到底是从哪条线开始我的理解是不依赖任何现成的端到端平台不靠拖拽式工具而是从Python基础、数据处理、模型训练、服务部署一路亲手做下来。目标是让你在读完、练完这套内容之后即使把你扔进一个完全没有AI基础设施的团队你也能自己从零搭出一套可用、可维护的AI服务。适合谁看如果你刚工作一两年被安排去做AI相关项目但之前主要写业务代码或者你是学生想系统性地进入AI工程这个方向又或者你已经能做模型训练但一说到上线就头疼——这篇文章和这套实践路径就是冲着你的需求来的。1. 内容整体设计与思路拆解1.1 为什么强调engineering而不是modeling我的第一个观点可能会让不少人意外在真实的生产环境里模型训练代码通常只占整个系统代码量的10%-20%。剩下的是什么是数据管道、验证逻辑、监控告警、容器编排、接口服务、配置管理、测试覆盖还有最容易被低估的——文档。很多自学AI的人陷入一个误区以为会训练模型就等于会AI工程。结果面试造火箭入职拧螺丝发现自己真正要写的代码全是高速缓存、队列、重试机制、多进程调度这些东西。这就是标题里engineering的深意它要求你把AI当作一个软件工程问题来对待而不是一个算法问题。所以我做这套ai-engineering-from-scratch实践时设计原则非常明确每个项目都必须从真实业务场景出发而不是用玩具数据集刷精度每一步都要有可运行、可测试的代码而不是只写伪代码模型能力只是中间产物交付物永远是一个服务、一个系统或一条完整链路注意这里不是说算法不重要而是说算法之外的那部分工程能力才是大多数公司真正缺的、也是你个人竞争力的核心护城河。1.2 从零构建的核心路线图如果给ai-engineering-from-scratch画一条学习路径结合我自己的经验应该是这样的第一层是根基Python高级语法、类型标注、单元测试、虚拟环境管理。这部分很多人跳过了后面才会不断返工。第二层是数据工程思维包括清洗、特征工程、数据版本管理、数据校验。现实中的数据永远是脏的、乱的、有偏的处理它的能力直接决定模型上限。第三层是模型训练全流程这里不只是跑通一个notebook而是要用脚本化方式管理训练、评估和实验记录保证每次实验结果可复现。第四层是模型服务化包括REST API设计、性能优化、并发处理、模型版本管理等。第五层是MLOps闭环包含CI/CD、监控、告警、模型漂移检测和自动重训。很多人学AI只停留在第三层而from scratch的意义恰恰是把第四层、第五层也亲手搭建一遍。你可能觉得这些不太酷但在招聘市场上能完整走完五层的人比只会训练模型的人值钱得多因为你可以独立负责一整条AI产品线。1.3 工具选型为什么是这套技术栈我的开源技术栈选择经过几次项目沉淀后基本固定了Python 3.10作为主力语言Poetry做依赖管理和打包Pandas和Polars做数据处理Scikit-learn和PyTorch做建模FastAPI做服务层Docker做环境一致性MLflow做实验追踪和模型注册GitHub Actions做CI/CDPrometheus加Grafana做监控。这个选型的逻辑很简单每个工具都经过大规模生产验证不是一堆GitHub上几千star但没人敢上线的玩具项目它们之间有非常活跃的社区连接比如MLflow原生支持FastAPI模型的部署学习资料质量高遇到问题更容易搜索到解决方案有人可能会问为什么不用Airflow或Kubeflow这类重型工具。我的回答是新手阶段用重型工具弊大于利你需要先理解编排、调度、状态管理这些底层概念是怎么工作的被它们虐过一遍之后再用框架就是降维打击了。2. 核心细节解析与实操要点2.1 一个AI工程项目的完整构成是什么我习惯把一个AI工程项目拆成五个核心包这也是我每次从零搭建新项目时的标准骨架data/数据获取、清洗、校验、版本管理的代码features/特征工程的代码models/模型定义、训练脚本、评估脚本api/模型服务层代码infra/Docker配置、部署脚本、监控配置这个分层看起来简单但每加一层约束就能在后面的迭代中省掉大量问题。比如data/和models/严格分离数据集的改动就不会污染模型代码的git历史回滚某个坏版本就特别干净。对应到实际工作中的体会是很多人喜欢把所有脚本塞进一个src/目录里前期方便后期灾难。当你的代码超过5000行、模型迭代超过20个版本之后没有一个清晰分层你会花大量时间在找代码、猜依赖上而不是真正的算法调优。2.2 数据模块比算法重要十倍的工程环节任何AI工程新手第一个真正的拦路虎都是数据处理。我在from-scratch实践项目的第一个模块就放了一道非常真实的题给定一份带缺失值、异常值、重复样本和不平衡类别的真实数据集要求产出一个干净、可复用的训练集。这里的工程体现在几个关键决策上数据版本管理。训练数据不是一成不变的如果某天最新拉取的数据集质量下降你要能明确知道是哪一批数据引入的问题。我给每个数据集加一个带哈希值的元数据文件类似Git commit的机制每次数据变更都生成新的版本号后面模型训练时把数据版本记录到MLflow中问题溯源就是几秒钟的事。数据校验。很多人会把校验放在训练之后等模型效果崩了才发现是数据出了问题。正确的做法是在数据管道的出口就设置校验规则比如字段类型、取值范围、缺失率上限、分布偏移阈值。我用pydantic加自定义断言实现了一套轻量级校验每次跑数据管道时如果数据分布和上一个版本偏差太大管道直接fail。避免数据泄漏。这个坑几乎所有新手都踩过包括我。最典型的错误是在做特征工程之前就把全量数据的统计量算好并填充到特征里而正确的做法是只用训练集的统计量来填充验证集和测试集。在工程项目里我规定所有fit操作只能在训练数据上执行transform则可以应用到任何数据这个约束用抽象基类来强制保证。注意数据泄漏不会让你的模型效果变差反而会让你的模型效果异常好——好到不真实。上线后立马原形毕露这比效果差更可怕因为它给了你虚假的安全感。2.3 模型训练模块脚本化是基本门槛from-scratch实践项目要求训练代码必须是可脚本化运行的禁止在Jupyter Notebook里跑完整训练。原因很简单Notebook天然不可复现你很难保证上次运行时的环境状态和这次运行时完全一致。我的训练脚本框架长这样python -m train \ --experiment-name icefall_forecast \ --data-version 2024.06.01 \ --model-type gradient_boosting \ --n-estimators 500 \ --cross-validation folds5每次实验前需要先想清楚三个问题这次实验要验证什么假设比如换用新的特征A能否提升准确率基线是什么必须跑过之前至少一个模型作为对照成功标准是什么比如线上指标提升至少2个百分点才算有效这就是工程化训练和玩耍式训练的本质区别前者每次实验都在为一个明确的问题寻找答案后者只是不断调参数碰运气。2.4 模型部署模块从notebook到生产环境在这里我来拆解一下模型部署的完整心法。很多人在这个环节翻车不是模型推理写错了而是根本不知道生产环境对模型服务的要求远不止能返回预测结果。第一是延迟SLA。你的API必须保证P99延迟在200ms以内这意味着你不能每次请求都重新加载模型权重而是在服务启动时预加载到内存中。第二是吞吐量。你需要考虑并发请求场景FastAPI的async特性配合进程池通常能解决大多数问题。第三是可观测性。一个没有指标监控的模型服务等于裸奔请求量、延迟、错误率、CPU和内存使用率这些基础指标必须一应俱全。我部署AI服务的基本流程模型导出训练结束后把模型权重和预处理参数一起打包为model.joblib或model.pt文件服务封装编写FastAPI服务加载模型并进行推理容器化写Dockerfile保证开发环境和生产环境的Python版本和依赖完全一致本地压测用locust做并发测试验证延迟和吞吐量是否达标灰度发布新模型先切5%流量观察确认稳定后再放量2.5 评估指标不要只看准确率在ai-engineering-from-scratch实践里我特意花了大量篇幅讲评估体系因为这是很多人轻视的部分。不同业务场景需要不同的指标组合分类问题准确率Accuracy、精确率Precision、召回率Recall、F1值、AUC回归问题MAE、MSE、RMSE、R-squared排序问题NDCG、MAP、MRR业务最终指标转化率、留存率、收入提升工程项目的关键在于建立离线指标和线上指标的相关性追踪。你离线评估提升了上线后用户指标真的提升了吗如果相关说明你的评估体系建得对如果不相关说明你评估的方式有刻舟求剑的问题需要尽快调整。3. 实操过程与核心环节实现接下来这一部分比较硬核讲的是我如何从零完整实现一个真实项目的全过程。选的项目是供应链需求预测服务。这个项目既有时间序列处理、特征工程、模型选择的复杂度也有部署监控的实际业务价值非常适合作为ai-engineering-from-scratch的压轴实践。3.1 项目骨架初始化和环境搭建环境搭建这步看似简单但很多人就是在这里翻车。我用Poetry做依赖管理的理由很简单能把虚拟环境、依赖版本、锁定文件统一起来配合Docker就能实现我本机能跑线上也一定能跑的效果。初始化项目poetry new supply-demand-forecast cd supply-demand-forecast poetry add pandas polars scikit-learn pydantic fastapi uvicorn mlflow docker这里有个细节值得说为什么需要PolarsPandas处理百万行数据时明显卡顿而Polars使用Rust内核和惰性计算性能提升好几倍。在真实业务里数据量动辄上亿行Pandas会成为瓶颈Polars是更优的选择。当然如果团队主要用Pandas你也可以保留。工具是死的业务是活的。注意Poetry生成项目结构后需要手动创建data/、features/、models/、api/、infra/这几个目录因为在AI工程项目中按职能分包比按技术分包更符合实际情况。3.2 数据管道实现从CSV到特征矩阵我拿到的原始数据是某零售品牌过去3年的SKU级日销量包含促销活动、节假日、天气、库存水平等信息。数据结构长这样字段类型示例store_idstringST3847sku_idstringSKU88421datedate2024-03-15sales_quantityint156promo_flagboolTrueholiday_flagboolFalseweather_conditionstringsunnyinventory_levelfloat0.78第一步我用Polars写了一个数据加载和清洗的管道指定好每列的数据类型遇到无法解析的日期直接fail避免脏数据悄悄溜进后面的流程。清洗规则体现了那个宁可挂掉也不要产出错误数据的取舍缺失率超过20%的特征直接丢弃缺失率低于20%的特征用训练集统计量填充这主要是为了保证后面推理时的一致性重复样本按日期和SKU去重。跑完清洗原始2000万行数据剩下1750万行13个特征保留到11个。第二步窗口特征工程。需求预测的难点在于单一SKU的销售序列非常稀疏且有强季节性直接喂给模型效果很差。我采用的做法是加滞后特征、滚动统计特征和外部特征。def create_features(df, config): # 滞后特征过去7/14/28天的销量 for window in config.lag_windows: df df.with_columns( pl.col(sales_quantity) .shift(window) .over([store_id, sku_id]) .alias(fsales_lag_{window}) ) # 滚动统计7/14天的均值、标准差、最小值、最大值 for window in config.rolling_windows: df df.with_columns( pl.col(sales_quantity) .rolling_mean(window) .over([store_id, sku_id]) .alias(frolling_mean_{window}) ) return df特征工程的核心理念是人机协同让机器做重复性、大规模的计算让人做特征选择、业务理解的判断。加特征要多但要想想特征的实际意义。比如季度末促销这个特征是我观察数据分布时发现的规律这种带有业务洞察的特征往往比单纯加更多窗口特征更有效。第三步数据集划分。为了避免数据泄漏我严格按时序切分训练集取自项目前两年的数据验证集和测试集分别是最后两个独立月份。这样做模拟了真实的预测场景训练过去的预测未来的。3.3 模型训练和调优流程模型选型上我对比了三类算法线性回归、梯度提升树LightGBM/XGBoost、以及一个简单的时间序列基准模型。没有选择深度学习方法的原因有两个数据量还不够支撑复杂模型杀鸡用牛刀且难以维护第二个是业务上需要模型可解释性每一条预测都能追到特征归因上树模型在这方面有天然优势。训练的核心代码用MLflow进行实验追踪with mlflow.start_run(run_namefxgboost_{timestamp}): mlflow.log_params(params) model XGBRegressor(**params) model.fit(X_train, y_train, eval_set[(X_val, y_val)]) # 记录离线指标 for metric_name, value in eval_metrics.items(): mlflow.log_metric(metric_name, value) # 记录模型和预处理流程 mlflow.sklearn.log_model(model, model) mlflow.log_artifact(preprocessor.pkl)调优过程的经验是不要一开始就Random Search或者Grid Search先手动跑几组极端参数理解参数方向和指标变化趋势然后再用贝叶斯优化收窄搜索空间。手动探索加自动搜索的组合能在时间成本可控的前提下拿到更好的结果。最终模型的表现验证集RMSE比基线下降了23%准确率提升到89.4%。但注意这只是线下数据上的表现真正的考验在线上。3.4 模型服务化和容器化部署训练完成之后进入我反复强调过的第四层——模型服务化。我选择了FastAPI性能好、文档自动生成、用起来很顺手而且对新手友好。核心文件代码如下from fastapi import FastAPI, HTTPException from pydantic import BaseModel import joblib app FastAPI() model joblib.load(models/model.joblib) scaler joblib.load(models/scaler.pkl) class PredictionInput(BaseModel): store_id: str sku_id: str date: str # 其他特征字段 class PredictionOutput(BaseModel): prediction: float skus: list[str] app.post(/predict, response_modelPredictionOutput) async def predict(input_data: PredictionInput): try: features preprocessor.transform(input_data) prediction model.predict(features) return PredictionOutput(predictionprediction) except Exception as e: raise HTTPException(status_code500, detailstr(e))把模型服务封装成API后就轮到Docker上场了。很多新手只知道用Docker是为了环境一致但具体怎么写Dockerfile就没概念了。一个高效的Dockerfile应该把依赖层和代码层分开因为依赖层很少变更可以直接用缓存镜像构建速度会快很多。FROM python:3.10-slim as builder WORKDIR /app COPY pyproject.toml poetry.lock ./ RUN pip install poetry poetry config virtualenvs.create false poetry install --no-dev FROM python:3.10-slim WORKDIR /app COPY --frombuilder /usr/local/lib/python3.10/site-packages /usr/local/lib/python3.10/site-packages COPY . . EXPOSE 8000 CMD [uvicorn, api.main:app, --host, 0.0.0.0, --port, 8000]部署到测试环境后我做的第一件事不是发预测请求而是跑压测。用locust模拟50个并发用户持续5分钟观察P99延迟是否在SLA以内。实测结果QPS达到800P99延迟156ms平均延迟38ms完全达标。这才是能上生产的模型服务而不是一个看起来能跑的脚本。3.5 监控和告警链路搭建模型上线之后你以为就结束了恰恰相反真正的工作才刚刚开始。我搭建了一个模型健康监控仪表盘盯四个方面请求数据监控每天的调用量、单位时间内的请求分布。如果某天突然跌掉80%大概率是上游系统出了问题可能与模型本身无关。响应质量离线评估是死的线上反馈才是活的。我设计了一个异步反馈闭环用户侧会把实际需求和预测结果的差值回传我们实时计算线上MAE一旦连续3天超过阈值就自动触发告警。数据漂移生产环境的输入数据分布和训练数据的分布可能随着时间推移而逐渐偏离。我用PSI群体稳定性指数实时监测关键特征的漂移程度。当PSI超过0.2时就会在仪表盘上标红提示需要重新培训模型。模型切换开关在FastAPI服务里内置一个配置开关可以将流量指向新模型或回滚到旧模型。这个开关配合CI/CD让模型版本的发布和回滚像日常发版一样简单。4. 常见问题与排查技巧实录4.1 冷启动和特征依赖实话说做AI工程最容易翻车的地方往往不在模型本身而在特征对齐这个环节。我遇到过一种非常经典的问题训练代码里的特征名和推理API里收到的特征名对不上。训练阶段用days_since_last_order部署阶段传成了days_since_last_purchase差一个字母模型静默返回了一个随机结果根本没有任何报错。这个问题的根源在于训练和推理的特征管道用两套代码维护后期一改动必然不同步。我的解决方案是把特征工程代码抽成一个独立的包训练和推理都import同一个包从源头锁死不一致的可能再在代码里加上特征完整性校验上线前跑一次训练实列与推理实列对齐测试。4.2 模型性能下降但没人发现这种情况比较隐蔽。你的模型在跑API也没有报错但是业务方开始抱怨预测越来越不准。当你去查看监控时发现模型指标确实在下降但要追溯到是什么时候开始下降的就成了一个让人头疼的问题。解决这个问题最好的方式是上线之初就记录基线预测分布。在模型第一次部署时把线上输入特征分布和预测结果分布保存下来之后每周自动对比一次。一旦当前分布和基线分布的差异超过阈值就发告警让模型训练团队开始准备新数据。这是我用血的教训换来的经验一开始没做后面排查问题全部靠猜痛苦不堪。注意模型漂移不是一个异常现象而是必然现象。数据分布会变用户行为会变环境会变模型效果劣化是时间问题。工程上的重点是让发现劣化的反应时间尽量短。4.3 依赖地狱AI项目的依赖管理比传统Web项目要痛苦得多Pandas、NumPy、Scikit-learn这些核心库之间互相钳制版本PyTorch和TensorFlow还会争夺GPU资源同时你的服务端还要求Python版本不能太新以免某个库不兼容。我给出的建议是项目一开始就锁定所有依赖版本永远用Poetry管理而不是手工pip安装。每次更新依赖的时候单独跑一次完整的回归测试确保核心流程不受影响。升级依赖是立项级操作不要顺手就做特别是涉及NumPy和Pandas的大版本升级。4.4 FastAPI部署时的坑如果直接用uvicorn main:app --reload命令启动服务并把它放到生产环境那就埋下了一颗雷。--reload是为开发设计的每次保存代码都会自动重启启动前还有代码扫描开销在生产环境既慢又不稳定。生产环境应该用uvicorn main:app --workers 4根据CPU核心数调整来充分利用多核。另一个容易踩的坑Docker内部运行FastAPI时默认绑定127.0.0.1但这个地址在容器外部是访问不到的。必须指定--host 0.0.0.0否则你本地压测全部失败还以为代码有问题。4.5 在线评估业务指标连着天离线模型评估做得再好最后还是要接受真实业务的检验。我在项目上线一个月后做了一次线上评估结果令人警醒线上MAE和离线验证结果相比高了将近30个百分点。原因让人意外业务方在旺季加大了促销力度导致销量分布偏离训练集模型完全没跟上节奏。从那以后我建立了一个机制每月固定做一次线上数据分布回顾结合业务日历大促、节假日、清仓等提前预判数据跳变必要时提前用最新数据重新训练模型。AI工程不是一次性交付它是一条持续运营的链路。5. 学习路径建议与扩展方向5.1 每周动手计划如果你完全是零基础想通过ai-engineering-from-scratch这条路径走下来我推荐一个13周的计划第1-2周Python基础知识补漏重点掌握类型、装饰器、生成器、上下文管理器第3-4周Pandas/Polars数据处理完成10个练习题第5周探索性数据分析学会画直方图、箱线图、相关性热力图第6-7周Scikit-learn建模逻辑回归、决策树、随机森林第8周特征工程专题处理缺失值、编码、缩放、特征选择第9周模型评估体系交叉验证、混淆矩阵、PR曲线、ROC曲线第10周FastAPI部署把第9周的模型封装成API第11周Docker容器化第12周MLflow实验追踪第13周完成一个完整项目从数据到模型到部署到监控全链路打通每个阶段都要有输出物可以是代码仓库、部署链接、或者技术博客。没有输出物的学习通通是假装学习。5.2 项目选题推荐练习AI工程最好的项目必须满足数据真实、流程完整、有部署价值三条标准。我的推荐电商销售预测可以在Kaggle上找真实数据客户流失预警电信/银行领域的数据集很丰富房价预测训练集干净适合练习特征工程IT日志异常检测和运维结合实战价值很高做项目的过程中切记不要只追求精度数字。把模型效果做到不差之后立刻把精力转向部署、监控、自动重训等工程环节你的收获会大得多。结尾一些值得分享的个人体会做了这么多年AI工程我最大的体会是这个领域最稀缺的不是懂算法的科学家也不是只会敲代码的普通程序员而是能把算法变成稳定在线业务系统的复合型人才。from-scratch的价值恰恰在于它逼着你把每一块地基都亲手打一遍。你用Docker部署服务的经历会帮助你理解为什么公司需要一套统一的运行时环境你手动搭监控告警的经历会帮助你理解平台团队为什么要维护一套监控体系你被数据分布偏移折磨过的经历会帮助你更珍惜数据治理和模型生命周期管理这些平时不起眼的基建工作。如果你正在走这条路我的建议很简单先把一个端到端的小项目完整跑通哪怕它只是一个连GUI都没有的命令行工具。只有当你亲身体验过从一团乱麻的数据到上线一个被真实用户调用的服务的全过程你才会真正理解AI工程到底是什么也才能在未来面对更复杂系统时游刃有余。希望这些踩坑经验和实操细节对你有一点帮助。祝你在AI工程这条路上少踩坑多沉淀持续进步。
返回列表