ARTICLE DETAIL

建站实战干货

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

AI工程从零搭建:数据清洗、模型训练到部署全流程实战

2026/9/30 4:31:11 拓冰建站 浏览量
AI工程从零搭建:数据清洗、模型训练到部署全流程实战 我自己完整踩过一遍“从零搭AI工程能力”这条路今天把整个过程、思路和避坑记录整理出来完全是我的个人实操总结不涉及任何环境依赖和特殊工具。先说清楚这个内容到底解决什么问题如果你不是科班出身或者虽然有基础但从来没完整做过一个AI工程化项目那么你需要的不是再刷一门“机器学习入门课”而是一条能直接照做的、从代码能力到模型落地的完整路径。它适合三类人想转行做AI工程的学生、刚入职但只会调包的数据分析师、以及想在公司内部把算法项目真正跑起来的后端工程师。为什么用“ai-engineering-from-scratch”这个名字来组织这次复盘因为我发现大多数人最大的障碍根本不是“不懂算法”而是工程化链条上每个环节都缺一点数据不会清洗、模型训练完不知道怎么部署、部署完不知道怎么看效果每一环都断一点整体就卡住了。这个内容就是把整条链路补全告诉你每一环需要什么能力、做到什么程度算过关、实操时最容易死在什么地方。1. 内容整体设计与思路拆解1.1 从零开始的本质不是“学”是“搭”我见过太多人把“从零开始学AI工程”理解成了“从零开始学人工智能理论”结果花了三个月啃完《深度学习》到公司发现连一个离线训练任务都跑不起来。从工程角度来看AI工程能力是组合出来的不是学出来的。你需要搭建的是四层能力第一层是数据层包括数据采集、清洗、特征构造、数据版本管理。这一层决定了模型的上限但在多数学习路线里被严重跳过了。第二层是算法层不是让你从张量推导开始而是掌握主流模型的适用场景、训练技巧和评估方法。第三层是工程层包括训练任务的编排、GPU资源调度、模型打包、API服务化。第四层是运维层包括监控、日志、告警、模型版本回滚。四层缺一不可。很多人算法课学得不错但把数据当垃圾往里灌模型上线之后没人知道效果跌了也有人工程能力很强但对模型的偏差方差一无所知调参全靠运气。这两种人我都面试过无一例外都会在真实项目里吃大亏。所以这个内容真正的设计思路是以“完成任务”为主线而不是以“学完课程”为主线。每一步都带着一个具体目标去补能力比如“把一份脏数据变成可训练的数据集”、“把训练好的模型封装成一个HTTP服务”。目标完成后能力自然沉淀。1.2 为什么先通盘再动手我在一开始犯过一个特别蠢的错误拿到任务后直接开始写代码结果写到模型部署环节发现数据格式不符合上线要求前面白干。后来我养成了一个习惯不管项目大小动手前先花半天时间通盘上下游。通盘具体做什么我会画三条线数据从哪里来经过哪些处理最终以什么格式交给模型模型用什么框架训练输出什么格式的产物产物如何被服务加载服务以什么协议暴露给上游调用方线上日志和指标从哪来。画完这三条线之后再去动手你会发现很多后续问题在开工前就能暴露。比如模型输出的概率值需要配合业务阈值做二分类但上游系统只接受0和1的标签再比如训练时用的字段名和线上特征管道产出的字段名不一致导致推理时报错。这些问题全部可以在通盘阶段定位换个思路就省了返工一周的代价。现在的项目里我带的实习生我都会强迫他们先写一页纸的数据流和调用链说明写不清楚的不许碰代码。这不是形式主义是因为能写清楚就说明已经避开了绝大多数工程坑。1.3 选型背后的三个判断这套思路里我做了几个关键选型决定各自都有原因。第一个决定是数据工作默认用Python做不用Scala或Java系。原因不是Python更好而是整个生态里数据处理到模型训练的无缝衔接只有Python能做到。Pandas、PyArrow、NumPy处理完的数据可以直接喂给PyTorch或LightGBM中间不用跨语言序列化。第二个决定是模型训练和推理服务分开。训练用离线任务的方式跑跑完把模型产物存储下来推理用独立服务加载模型文件不依赖训练环境。分开之后的好处是训练资源跑满不影响线上服务模型升级只需要替换产物不需要重新发布整个服务。第三个决定是默认使用规则模型兜底。如果机器学习模型效果波动过大或者线上特征质量下降导致预测失真必须有一个基于规则的兜底逻辑保证业务不挂。这个思路在很多大厂内部是通用做法但个人开发者自己搭项目时几乎不会考虑等到上线出问题才慌。这三个决定背后其实是一个更基础的原则工程链路里每个环节都不能有单点依赖。你依赖某个同学写的脚本依赖某台服务器的GPU依赖某个框架的特性都要提前想好替代方案。2. 核心细节解析与实操要点2.1 数据处理才是真正的重头戏坦白说AI工程里最常见的失败原因不是模型不够好而是数据根本没法用。我处理过一份从业务系统导出的用户行为日志日期字段混着三种格式有的带时区有的不带行为类型字段里有大小写不一致还有中文符号一个用户ID因为埋点版本不同出现了新旧两套体系。这类数据如果不做预处理直接训练模型性能一定惨不忍睹。我在数据处理环节里的固定流程大致如下第一步是数据概要分析用df.describe()、df.info()和df.isnull().sum()快速摸清数据规模、缺失率、字段类型这一步不用写复杂代码但非常重要。第二步是字段类型修正日期统一成datetime64分类字段转成category类型数值字段确认精度。第三步是缺失值处理不是无脑填均值而是先分清楚是随机缺失还是有业务含义的缺失比如用户未登录时行为为空那就该单独建一个布尔特征而不是填充0。第四步是数据划分按时间切而不是随机切避免未来信息泄露到训练集。特征工程这一块我的原则是先做业务理解再做特征。别一上来就扔给自动特征工程工具工具生成的几百个特征你根本没法解释线上出问题也无从排查。我通常只做三类特征用户基本属性类、行为统计类、时序趋势类每类控制在十来个加上交叉特征总共五十个以内足够应付绝大多数任务。数据版本管理这个点做个人项目的人容易忽略但它是工程化非常重要的环节。我自己用的是DVC来管数据集版本每次清洗逻辑变更都会产生新版本数据跑完的实验记录里会写清楚用的哪一版数据。这样别人复现你的实验时不会一头雾水。2.2 模型训练时的关键设置与参数选择拿我自己复现率最高的一个推荐召回模型举例模型结构不复杂双塔结构用户侧塔和物品侧塔分别编码最后点积算相似度。但训练时的几个参数设置我是踩过坑之后才确定下来的。第一是负样本采样比例。最开始我只用了等量的正负样本训练出来模型精度还行但线上A/B测试效果很差。后来想明白了线上场景绝大多数请求是负样本训练分布和线上分布不一致模型自然会漂。我把负样本比例调到5倍之后线上指标立刻有了明显提升。第二是学习率与批次大小配合。我习惯用Warmup策略前几百步从低学习率线性升到目标学习率后面再按余弦曲线衰减。初始批次大小设为256显存不够就减小批次但同步降低学习率这个配合逻辑在Adam优化器下基本能得到一个稳定的训练过程。第三是早停策略和模型保存。我用的是验证集AUC作为监控指标如果连续五个epoch没有提升就停止训练保存效果最好的那个checkpoint而不是最后一个。这些代码在PyTorch里写起来都不复杂但这几个小设置组合在一起能让训练时间大幅缩短模型的最终效果也更稳定。第四是评估指标的选取。很多教程告诉你用准确率但实际业务里准确率是最容易骗人的指标。比如点击率预测任务负样本占九成以上模型全预测负样本也能拿到90%的准确率但业务完全没用。我固定用的是AUC和GAUCGAUC能按用户分组消除用户差异化带来的虚高比单纯的AUC可靠得多。模型效果评估这关不过绝对不进下一步。2.3 推理服务与模型产物管理模型训练完之后的产物在企业里有专门的模型仓库来管个人项目里至少也要做到目录清晰、版本明确。我自己的目录结构大约是models/ recall_v1/ model.pt config.json metrics.json recall_v2/ model.pt config.json metrics.json每个版本文件夹里除了模型文件一定要放config和metrics。config记录训练时的超参数metrics记录该版本的评估指标。别小看这两个JSON文件线上模型效果出现问题需要回滚时你靠的就是它们判断哪个版本更可靠。推理服务我用的是FastAPI加Gunicorn每个模型版本启动一个独立的服务实例通过服务名加版本号来路由。比如http://recall-v1.internal/api/recall和http://recall-v2.internal/api/recall这样A/B测试或者灰度发布都很方便只需要在网关层调整流量比例。服务上线之后我还有一套自检脚本每小时请求一次线上服务用预设的样本输入跑通比较输出结果和期望的差异。如果差异过大自动告警。这套自检脚本成本极低但在模型输入格式发生变化时能第一时间发现问题。2.4 从训练到部署的工程链路搭建打通训练到部署的链路是这个内容里最需要耐心的一步。训练在离线环境部署在线上环境两者经常系统版本不同、依赖库版本不同甚至Python版本都不同。我第一次部署时就是吃了这个亏本地跑得好好的一到服务端就报依赖冲突。我的做法是固定用Docker做运行环境。训练阶段和推理阶段各自构建镜像推理镜像尽量精简只保留模型推理必需的依赖减小安全风险和启动时间。训练镜像可以大一点把常用的科学计算库都装好。模型热加载这个功能要特别留意外卖。如果我们训练了一个新模型希望不中断服务就切换版本需要在服务里实现模型文件的动态加载。简单方案是服务启动时从模型目录加载指定版本版本切换时直接更新目录里的软链接并触发重新加载。这个方案做起来容易踩的坑是并发请求在重加载过程中会短暂报错所以重加载逻辑要加锁或者采用双Buffer切换的方式。这一套链路跑通之后从训练完模型到上线新版本的发布时间可以从动手改服务代码加发布的一两天压缩到只需要上传一个模型文件加一个配置更新五分钟左右完成切换。3. 实操过程与核心环节实现3.1 实操目标与环境准备下面进入可以照做的环节。我以降维方式复现一个实际的AI工程链路为例目标是训练一个召回模型并把它部署成可用的HTTP推理服务。严格来说这个任务能覆盖AI工程全链路中的核心环节而且是个人电脑上就能完成的。环境准备方面需要的工具是Python 3.10以上版本、PyTorch、Pandas、Scikit-learn、FastAPI、Uvicorn、DVC。如果机器有CUDA显卡就用GPU训练没有也没关系这个规模的数据用CPU也能跑只是时间稍长。这里有一个特别重要的环境细节我建议新建一个干净的虚拟环境不要直接在系统Python环境里装包。因为我踩过太多次版本冲突的坑比如Numpy版本不满足PyTorch要求或者OpenSSL版本不匹配导致Pandas无法导入。虚拟环境下所有依赖都能锁定版本就算整个环境坏了重建也只需要几分钟。3.2 清洗一份模拟数据数据没有现成的我自己构造了一份模拟用户行为日志包含字段用户ID、行为时间、行为类型、物品ID、行为数值。构造数据的目的是演示真实的清洗流程方法完全来自实际操作中总结出来的固定套路。清洗流程按照下面几步操作第一步加载数据后先做初步探查重点看每个字段的非空数量、类型和几个特征值。这个环节耗时很短但能帮你确认数据到底是什么样子。第二步对行为时间做时区归一化模拟数据里故意混入了UTC时间和本地时间统一换算成北京时间。第三步处理行为类型字段做大小写统一并对未知行为类型做映射。第四步处理异常用户ID过滤掉明显是测试账号或异常批次的记录。完成清洗之后把用户行为按时间排序构造每个用户的特征向量历史行为次数、行为类型分布熵、最近一次行为距今天数、平均行为数值等。这些特征直接存储成训练用宽表一行一个用户。3.3 模型训练与效果评估特征宽表准备好之后进入模型训练环节。我用的是简单的双塔结构用户特征经过两层全连接得到用户向量物品特征经过两层全连接得到物品向量训练目标让正样本的用户物品向量相似度尽量高负样本尽量低。训练代码的核心片段可参考下面的写法import torch import torch.nn as nn class UserTower(nn.Module): def __init__(self, input_dim, hidden_dim, output_dim): super().__init__() self.net nn.Sequential( nn.Linear(input_dim, hidden_dim), nn.ReLU(), nn.Linear(hidden_dim, hidden_dim), nn.ReLU(), nn.Linear(hidden_dim, output_dim) ) def forward(self, x): return self.net(x) class ItemTower(nn.Module): def __init__(self, input_dim, hidden_dim, output_dim): super().__init__() self.net nn.Sequential( nn.Linear(input_dim, hidden_dim), nn.ReLU(), nn.Linear(hidden_dim, output_dim) ) def forward(self, x): return self.net(x)训练时我用的损失函数是BCEWithLogitsLoss因为双塔输出的相似度分数直接过Sigmoid就是点击概率和二分类任务天然匹配。优化器用AdamW加了Weight Decay做正则化防过拟合的效果比单纯Adam要好。评估阶段先看AUC再看每个用户分组内的排序质量。如果AUC在0.85以上这个模型基本可用。训练完的产物保存为model.pt文件同时保存一份config.json记录超参数方便后续版本对比。3.4 封装推理服务并验证模型训练好之后我们把它封装成一个HTTP接口。以下是FastAPI服务的核心片段from fastapi import FastAPI from pydantic import BaseModel import torch app FastAPI() class PredictRequest(BaseModel): user_features: list[float] item_features: list[float] model UserTower(...) model.load_state_dict(torch.load(models/recall_v1/model.pt)) model.eval() app.post(/predict) def predict(req: PredictRequest): with torch.no_grad(): user_vec model(torch.tensor(req.user_features)) item_vec model(torch.tensor(req.item_features)) score torch.sigmoid((user_vec * item_vec).sum()) return {score: float(score)}启动命令是uvicorn main:app --host 0.0.0.0 --port 8000。启动之后用curl发一个带用户特征和物品特征的请求能正常返回0到1之间的分数就说明推理服务通了。这一步完成后整个从数据处理到模型上线的最小闭环就算走通了。你已经完整经历了一个AI工程项目的生命周期后续所有能力都能在这个骨架上迭代叠加。4. 常见问题与排查技巧实录4.1 数据层面的坑我在整个实操过程中遇到最多的问题集中在数据层在这里集中列出几个高发问题以及我的排查经验。缺失值处理不当导致的训练报错是最常见的。Pandas里缺失值默认是NaN但如果某个字段被读成了字符串类型缺失值会变成字符串nan模型训练时直接报类型错误。排查方法是训练前打印df.dtypes和df.isnull().sum()一旦发现字段类型不合理立即回溯数据处理逻辑。日期解析失败是另一个高频问题常见报错是ParserError: Unknown string format。通常原因是日期字符串里混入了不同的格式或者包含非法日期。排查时先把日期字段转为字符串然后用pd.to_datetime(..., errorscoerce)解析解析失败的行单独拉出来看格式规律。还有一类问题很难察觉就是特征分布异常。比如一个本来应该是正数的字段出现了负数或者数值字段出现极大异常值。这类问题模型不会报错但会严重影响效果。我养成的习惯是训练前对每个特征画出分布直方图所有分布和你业务预期对不上的一律先定位原因再训练。4.2 模型训练和推理环节的坑模型训练阶段最常见的问题是Loss不降。发生这种情况我通常按照以下顺序检查先确认数据标签没有全部是同一个类别再确认输入特征做过了标准化不是原始值直接进网络接着确认学习率不是过大导致Loss震荡最后确认模型结构输出维度和标签维度匹配。推理服务阶段最挫败的问题就是本地正常但部署后报错。排查经验是先用pip freeze对比本地和部署环境的包版本差异重点看PyTorch、Numpy、Pydantic这三个。如果版本差异大直接在部署环境重装依赖。其次确认服务运行时当前工作目录正确如果模型文件路径是相对路径而服务是通过systemd启动的很容易因工作目录不同而找不到模型文件。服务内存泄漏也是一个容易忽视的问题。如果你的推理服务在处理几万个请求后内存持续增长大概率是推理循环里反复创建了张量没有释放。解决办法是在推理函数里检查是否创建了不必要的中间变量以及是否为每个请求都加载了模型。模型只应该加载一次而不是每个请求都加载一遍。4.3 常见问题速查表问题现象可能原因推荐排查步骤训练Loss不下降数据标签失衡/学习率过大/特征未标准化先看标签分布再看Loss曲线最后试调低学习率模型精度高但线上效果差训练集与线上分布不一致检查数据划分是否随机切割改成按时间切割部署后报模块找不到依赖环境版本不一致用pip freeze对比环境确认版本后再部署推理结果总是不变模型权重没加载成功/输入被缓存打印模型参数检查是否非空确认输入特征有变化内存不断增长推理循环中创建了未释放的张量用tracemalloc定位内存分配检查推理函数服务启动慢且无响应模型加载过大/依赖初始化慢打点观测各步骤耗时把模型加载放到启动钩子中4.4 几个值得单独说明的避坑心得关于数据泄露这个坑伤害最大。如果做特征时用的统计值包含了预测时刻之后的信息比如用用户全量历史均值来做预测当天的特征模型在训练集上会表现得非常好上线立刻崩溃。解决办法是特征构造时严格按截止时间截断数据保证特征只使用预测时刻之前的信息。这个点我一般做两遍检查一遍是业务层面的自检一遍是代码层面的自检。关于模型回滚这是工程上成熟的机制但个人项目也要有。每次模型上线前保存上一个版本的备份观察指标发现异常时立即把流量切回旧版本。不要想着“再调调就好了”线上问题每多一分钟都是业务损失。关于日志和监控这是最容易被忽略的。个人项目里的监控没必要做得很重但至少要记录每次请求的输入特征、返回分数和耗时。因为当模型效果出问题时你只能靠日志反推问题原因。完全没有日志的线上模型出问题等同于盲人摸象。5. 一条可以直接照抄的学习路线和时间规划根据这套思路我整理一条适合所有人的实操路线每周投入约10到15小时的情况下八周内可以完成从零到完整项目落地。第一周Python数据生态和版本管理练习。要求能独立用Pandas完成数据加载、清洗、聚合操作并掌握虚拟环境下管理和导出依赖文件。第二到三周机器学习基础加一个完整比赛项目。Scikit-learn里的模型足够用重点是把特征工程、交叉验证、指标评估这一套流程跑顺。第四到五周深度学习入门加一个CV或NLP小项目。PyTorch是首选不用追最新模型先跑通训练和推理的最小平装。第六周工程化链路搭建。用Docker容器化训练环境和推理服务把模型通过API服务化并完成调用测试。第七周完整项目实操。把之前的数据处理、模型训练、服务化部署全部串起来要求从一份脏数据开始到可调用API结束全程自己完成。第八周稳定性加固。加上监控、日志和模型回滚机制并给自己出一个线上模型失效的模拟场景练习排查流程。时间表只是个参考核心思想是每个阶段必须产出真实可用物而不是只完成了课程练习。课程练习和可用项目之间的差距就是AI工程经验本身。写在最后的一些个人体会再分享一个我在整个实操过程中最有价值的习惯转变从遇到问题先查资料改成遇到问题先复现。以前我写代码碰到报错就打开浏览器去搜索效率很低而且看完别人的解决方法未必能理解根源。后来我强迫自己先看报错栈把出错的变量打印出来在本地最小化复现问题再去查文档和资料。这个习惯让我对很多问题的理解深了很多而且解决问题的速度反而更快了。另一个切身体会是AI工程不是“调包”也不是“训练模型”它是把数据、算法、服务、运维串起来的完整链条。链条上任意一环薄弱整个项目的可靠性就被拉低。每次你打通一个环节你就离真正能独立交付AI项目的工程师更近一步。最后一步的经验是我后来带人时一定会强调的做完一个项目一定要复盘。我会问自己三个问题——数据环节花的精力是否超出了计划模型训练阶段的迭代次数是否合理部署和排查问题的时间是否值得优化。这三个问题追着问了几个项目之后我对AI工程的节奏感和判断力会明显变好别人还在迷雾里摸索你已经能大致估算出哪些环节会耗时间、哪些环节必须投入。