ARTICLE DETAIL

资讯详情

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

从零搭建AI工程体系:先跑通最小闭环,再逐层加深

从零搭建AI工程体系:先跑通最小闭环,再逐层加深 1. 从零搭建AI工程体系为什么我劝你别一上来就啃论文ai-engineering-from-scratch这个标题我第一次看到的时候会心一笑。因为过去三年里我带过不下十个想转AI工程方向的同事和实习生几乎所有人踩的第一个坑都是一样的——打开某篇经典论文从注意力机制公式开始硬啃啃了两周公式能背了但你让他跑一个能用的推理服务他连显存怎么估、batch怎么调、tokenizer怎么选都说不清楚。这就是从零这两个字最容易被误解的地方。AI工程不是AI研究它要解决的核心问题不是这个模型为什么有效而是这个模型怎么在真实业务里稳定、便宜、可维护地跑起来。前者是科学后者是工程。你不需要发明发动机但你必须会造一辆能上路、能拉货、坏了能修的车。所以这篇内容我想聊的是我理解的从零搭建AI工程能力到底应该长什么样。它适合三类人一是刚入行、被各种框架名词绕晕的新人二是从后端/数据方向转过来、有工程底子但缺AI直觉的老手三是想自己动手做点AI产品、但不想被云厂商绑死的独立开发者。我会把整个体系拆成数据、训练、推理、评估、部署这几块每一块讲清楚为什么这么设计和具体怎么落地中间穿插我自己踩过的坑和实测有效的参数。先把结论摆前面从零做AI工程正确的顺序是先跑通一条最小闭环再逐层加深而不是先把理论学完再动手。这条闭环哪怕再简陋——一个几百条样本的数据集、一个预训练小模型、一个本地推理脚本、一个简单的评估指标——只要它端到端跑通了你后面所有的学习都会变得有的放矢。下面我就按这个思路一层一层拆给你看。2. 整体设计思路AI工程的最小闭环长什么样2.1 为什么是闭环而不是流水线很多人脑子里对AI项目的想象是一条直线收集数据 → 清洗 → 训练 → 评估 → 上线做完就结束了。但真实项目里它是个圈上线之后你会收到bad casebad case变成新数据新数据触发重新训练重新训练又要重新评估。如果你一开始就按直线思维搭系统你会发现每次迭代都要推倒重来。我见过最典型的反面案例是一个团队花两个月搭了一套完美的训练流水线数据格式、特征工程、模型结构全部硬编码。结果上线第一周产品经理说能不能加个新字段整个流水线改了三周。这就是没有按闭环设计的代价。闭环设计的核心思想是把每个环节都做成可替换的模块模块之间用稳定的接口通信。数据层对外只暴露给我一批样本的接口训练层只关心给我样本我吐模型推理层只关心给我模型我吐结果。这样任何一层换实现其他层都不用动。2.2 模块划分与选型逻辑我把最小闭环拆成五个模块下面这张表是我实际项目里反复验证过的划分方式模块核心职责常见选型选型关键考量数据层存储、清洗、版本管理本地文件 / 对象存储 版本工具小项目别上大数据栈够用就行训练层微调、全量训练、实验管理PyTorch 轻量实验记录优先选生态成熟、社区活跃的推理层加载模型、批量推理、流式输出原生推理 / 专用推理引擎看延迟和吞吐哪个是瓶颈评估层自动指标 人工抽检指标脚本 标注表格自动指标只能筛不能定生死部署层服务化、监控、灰度简单Web服务 日志先能跑再谈高可用这张表里我想特别强调两点。第一数据层不要过早引入重型工具。我见过有人为了一个几千条样本的项目上分布式数据框架光环境配置就花了一周纯属自找麻烦。第二评估层是最容易被忽视但最值钱的一层。没有评估你根本不知道模型是变好了还是变坏了所有迭代都是盲人摸象。2.3 从零起步的推荐路径如果你现在完全零基础我建议按这个顺序推进每一步都要能跑出可见的结果再进下一步第一步跑通推理。找一个开源小模型写个脚本让它对几句话做推理理解token、上下文长度、生成参数这些基本概念。第二步跑通评估。给上一步的模型准备20条测试输入人工看输出记录哪些好哪些坏形成你的第一份评估集。第三步跑通微调。用这20条数据对20条就够起步做一次微调对比微调前后的输出差异。第四步跑通服务。把模型包成一个HTTP接口用curl能调用。第五步加监控和日志。记录每次请求的输入输出和耗时为后续迭代攒数据。这个路径看起来简单但每一步都藏着细节。下面几章我会逐个展开。3. 数据层从零构建可迭代的数据资产3.1 数据格式的统一比数据量更重要新手最容易犯的错是拿到什么数据就直接喂给模型格式五花八门。今天用CSV明天用JSON后天直接贴文本。结果就是每换一批数据就要改一次代码训练脚本里全是格式判断的if-else。我的做法是在项目最开始就定义一个内部统一的数据结构所有外部数据进来先转成这个结构。对于大多数文本类AI任务这个结构可以简单到只有三个字段{ id: sample_0001, input: 用户输入的原始内容, output: 期望的模型输出 }别小看这个统一。它带来的好处是训练代码只认这一种格式评估代码也只认这一种格式数据清洗脚本的职责就是把各种来源转成这个格式。职责单一了出问题的时候定位范围就小。提示如果你的任务没有标准答案比如开放式生成output字段可以留空或者放一个参考示例评估时改用人工打分。不要为了凑格式硬编一个假答案。3.2 数据清洗的优先级排序数据清洗是个无底洞你永远可以洗得更干净。从零起步的时候我建议按这个优先级来做完一层再考虑下一层第一优先级去重。重复样本会让模型过拟合到特定表达而且浪费训练算力。简单的文本去重按完全匹配就行进阶一点可以用相似度阈值。第二优先级去噪。去掉明显的乱码、HTML标签残留、超长无意义重复。这一步用正则就能解决大部分问题。第三优先级格式规范化。统一标点、统一空格、统一大小写策略。这一步是为了让模型学到的模式更一致。第四优先级质量筛选。这一步最难也最需要人工介入。我的经验是先用规则筛掉明显低质的再人工抽检。这里有个反直觉的点清洗不是越狠越好。我曾经把一个数据集清洗得太干净把一些口语化的、带错别字的样本全删了结果模型上线后遇到真实用户的错别字输入就懵了。真实数据里的脏有时候恰恰是分布的一部分删太狠反而让模型脱离了真实场景。3.3 数据版本管理别用文件名区分data_v1.csv、data_v2_final.csv、data_v2_final_真的最终版.csv——这种命名方式我猜很多人都干过。它的致命问题是你永远说不清某个模型到底是用哪份数据训出来的。从零起步不需要上专业的数据版本工具但至少要建立一个简单的约定。我的做法是给每次数据变更打一个内容哈希用一个清单文件记录# 生成数据指纹 sha256sum data/train.jsonl data/train.jsonl.sha256 # 清单文件记录 echo 2024-01-15, train.jsonl, $(cat data/train.jsonl.sha256), 微调v1, 新增200条badcase data/CHANGELOG.md这样任何时候你都能追溯到这个模型对应哪份数据。等团队大了、数据多了再迁移到专业工具也不迟。先有纪律再有工具顺序反了就是给自己找罪受。3.4 小数据集也能起步的实操心得很多人卡在我没有数据这一步。我的观点是从零做AI工程50条高质量数据比5000条垃圾数据有用得多。我做过一个实验同一个任务用50条精心构造的样本微调和用5000条网络爬来的样本微调在人工评估上前者明显更好。原因是小数据集你能完全掌控质量而大数据集里噪声的比例往往超出你的想象。构造小数据集的技巧覆盖典型场景把任务拆成几类典型输入每类至少放5条。包含边界情况空输入、超长输入、特殊字符输入这些都要有。正负样本搭配如果任务涉及判断正例反例都要给。留出验证集哪怕只有50条也要留10条不参与训练专门用来验证。4. 训练层微调不是玄学是参数和数据的配合4.1 全量训练还是参数高效微调这是从零起步必须做的第一个技术决策。全量训练就是更新模型所有参数参数高效微调比如只训练一小部分附加参数只更新很少一部分。两者的取舍我用一张表说清楚维度全量训练参数高效微调显存需求高通常是模型参数的数倍低接近推理需求训练速度慢快效果上限高数据充足时更好略低但小数据下差距不大灾难性遗忘风险高低适合场景数据量大、任务差异大数据量小、任务接近预训练目标我的建议很直接从零起步先用参数高效微调。原因有三一是显存友好一张消费级显卡就能跑二是训练快你可以在几十分钟内看到结果迭代节奏快三是它不容易把预训练模型原有的能力破坏掉对新手更友好。4.2 关键参数的计算与选择微调里最让人头疼的是各种超参数。我不打算给你一堆经验值而是讲清楚每个参数背后的逻辑你自己就能推。学习率这是最重要的参数。参数高效微调的学习率通常比全量训练大一个数量级因为可训练参数少需要更大的步长才能有效更新。常见范围是1e-4到5e-4。判断标准很简单训练loss如果震荡不降调小如果几乎不动调大。批次大小受显存限制。有个实用技巧是梯度累积——显存装不下大batch就分几次前向反向把梯度攒起来再更新一次。效果等价于大batch代价是慢一点。计算公式有效批次大小 单次批次大小 × 梯度累积步数比如单次只能放4条累积8步有效批次就是32。训练轮数小数据集上通常3到5轮就够。判断过拟合的信号是训练loss持续下降但验证loss开始上升。这时候就该停了。我一般会设置一个早停机制验证loss连续两轮不降就自动停。序列长度这个参数直接决定显存占用而且影响很大。序列长度翻倍显存占用可能翻好几倍因为注意力机制是平方复杂度。从零起步时先统计你数据里输入输出的长度分布取95分位数作为序列长度别一上来就拉满。4.3 训练过程的监控要点训练不是启动了就不管。我每次训练都会盯这几个指标loss曲线训练loss和验证loss要一起看。健康的曲线是两者都下降且差距不大。学习率曲线如果用warmup或衰减策略确认它按预期变化。梯度范数如果梯度爆炸说明学习率太大或者数据有问题。显存占用留出余量别跑到99%否则容易OOM。注意训练日志一定要落盘保存不要只打印在终端。我吃过亏训练跑了一晚上终端会话断了日志全没了只能重跑。现在我的习惯是每个实验一个目录日志、配置、模型权重全放一起。4.4 实验管理从零也要有纪律没有实验管理你会陷入这个模型好像比上个好但我说不清为什么的困境。从零起步不需要专业平台一个简单的目录约定就能解决experiments/ exp_20240115_lr1e4/ config.json # 所有超参数 train.log # 训练日志 metrics.json # 最终指标 checkpoint/ # 模型权重 notes.md # 这次实验的假设和结论关键是那个notes.md。每次实验前写下你的假设我猜学习率调到2e-4会更好实验后写下结论。坚持一个月你会积累出一份属于自己的调参直觉这比看任何教程都值钱。5. 推理层让模型真正跑起来的关键细节5.1 推理引擎的选择逻辑训练完的模型怎么跑推理这里有个容易忽略的决策。原生框架推理最省事但性能和资源利用率往往不是最优。专用推理引擎能带来明显的加速但引入额外的转换步骤和兼容性问题。我的判断标准是如果只是本地测试、验证效果用原生框架别折腾。如果要上线服务、有延迟要求考虑专用推理引擎。如果模型结构比较特殊先确认引擎是否支持不支持就老实用原生。从零起步阶段我强烈建议先用原生推理把效果验证清楚确认模型本身没问题了再考虑性能优化。顺序反了的话你会分不清是模型的问题还是引擎的问题。5.2 生成参数的调优实战推理时的生成参数对输出质量影响巨大但很多人直接用默认值。我挑几个最关键的讲温度temperature控制输出的随机性。温度趋近0时模型总是选概率最高的词输出稳定但可能呆板温度调高输出多样但可能跑偏。我的经验是事实性任务用低温0.1到0.3创意性任务用高温0.7到1.0。top_p核采样从累积概率达到p的最小词集合里采样。它和温度配合使用。一个实用组合是温度0.7加top_p 0.9兼顾多样性和合理性。最大生成长度一定要设否则模型可能陷入重复循环一直生成。设多少取决于任务一般留出期望长度的1.5倍余量。重复惩罚对已经出现过的词降低概率缓解重复问题。但别设太高太高会导致输出不连贯。1.1到1.2是比较安全的范围。这些参数没有万能组合必须针对你的任务实测。我的做法是固定一批测试输入用不同参数组合各跑一遍人工对比选出最合适的。5.3 批处理与流式输出的取舍批处理是把多条请求攒一起推理吞吐高但单条延迟大。流式输出是边生成边返回用户体验好但实现复杂。从零起步我建议先做批处理后做流式。批处理的实现简单而且离线场景下吞吐就是一切。等你的服务面向交互式场景了再考虑流式。批处理有个坑不同长度的输入padding到一起会浪费算力。优化方法是按长度分桶把长度相近的放一批。这个优化能带来可观的吞吐提升实现也不复杂。5.4 显存估算上线前必须算清楚上线前不算显存等于开车不看油表。推理显存主要由三部分组成推理显存 ≈ 模型权重 KV缓存 激活值模型权重好算参数量乘以精度字节数。比如70亿参数用16位精度大约14GB。KV缓存和序列长度、批次大小成正比长上下文场景下这部分可能比权重还大。激活值相对小但也不能忽略。我的经验是留出至少20%的显存余量因为实际运行时的峰值往往比你估算的高。如果显存不够优先降批次大小其次降序列长度最后才考虑量化模型权重。6. 评估层没有评估就没有迭代6.1 自动指标能做什么、不能做什么自动指标比如各种相似度、准确率的好处是快、可复现、能批量跑。但它的局限也很明显它只能衡量像不像参考答案不能衡量好不好。我见过太多团队被自动指标误导。指标涨了上线后用户投诉变多。原因是自动指标对某些类型的错误不敏感比如事实性错误、逻辑矛盾这些指标可能完全看不出来。所以我的原则是自动指标用来筛人工评估用来定。自动指标帮你从100个实验里筛出10个候选人工评估帮你从10个里选出1个上线。6.2 构建评估集的实操方法评估集的质量直接决定你迭代的方向对不对。构建评估集我遵循几个原则分层采样按任务类型、难度、输入长度分层每层都要有代表。包含历史badcase每次线上发现的badcase都要加进评估集防止回归。规模适中从零起步50到100条就够太多你评估不过来。持续更新评估集不是一次性的要随着业务变化不断补充。评估集要单独存放绝对不能混进训练数据。我建议在代码层面就做隔离训练脚本根本读不到评估集的文件路径从物理上杜绝数据泄漏。6.3 人工评估的标准化人工评估最大的问题是主观。不同人标准不一样同一个人不同时间标准也不一样。解决办法是制定评分标准并做校准。一个简单可用的评分维度维度说明分值正确性内容是否准确无误1-5相关性是否切题1-5流畅性表达是否自然1-5完整性是否覆盖要点1-5评分前先找几条样本大家一起打对齐标准。评分时不要看模型来源避免先入为主。我一般会把不同模型的输出打乱顺序盲评。6.4 评估结果的分析与归因评估完不是看个平均分就完事。要按维度、按样本类型拆开看。可能整体分数差不多但A模型在长输入上更好B模型在短输入上更好。这种细分信息才指导下一步优化。我习惯做一个错误分类统计把badcase按错误类型归类看哪类错误最多。如果某类错误占比特别高那下一步优化就聚焦解决它。这比漫无目的地调参高效得多。7. 部署层从能跑到好用的最后一公里7.1 服务化的最小实现把模型包成HTTP服务从零起步用最简单的Web框架就够。核心就三个接口健康检查、单条推理、批量推理。# 伪代码示意展示接口设计思路 from fastapi import FastAPI app FastAPI() model load_model() app.get(/health) def health(): return {status: ok} app.post(/predict) def predict(request): result model.generate(request.input) return {output: result}别小看这个简单实现它已经能满足大部分内部场景。等有性能压力了再考虑加缓存、加队列、加多实例。7.2 日志与监控为迭代攒数据服务上线后每一次请求的输入输出都要记录。这些日志是你后续迭代最宝贵的数据来源。记录内容包括请求时间、输入内容、输出内容、耗时、参数配置。隐私相关的处理要提前考虑敏感信息在落盘前就要脱敏。这个不是可选项是必须项。监控方面从零起步盯这几个指标就够请求量、平均延迟、错误率、显存占用。任何一个异常波动都值得排查。7.3 灰度与回滚新模型上线不要一次性全量替换。先切一小部分流量观察指标没问题再逐步放大。同时保留旧模型一旦新模型出问题能立刻切回去。回滚机制要在上线前就准备好而不是出问题了才临时想。我见过太多团队上线时手忙脚乱就是因为没准备回滚方案。7.4 成本控制的实际经验AI服务的成本主要来自算力。控制成本的手段有几个按性价比排序请求合并把短时间内的多条请求攒批处理吞吐提升明显。结果缓存相同或相似输入直接返回缓存结果省掉重复计算。模型分级简单请求用小模型复杂请求才用大模型。量化在可接受的精度损失下降低模型精度减少显存和算力。这些手段我都在实际项目里用过效果最立竿见影的是请求合并和缓存。量化要谨慎一定要在评估集上验证精度损失可接受再上。8. 常见问题与排查技巧实录8.1 训练不收敛的排查顺序训练loss不降按这个顺序排查数据检查输入输出是否对应正确有没有大量空样本我遇到过标签错位的问题查了半天才发现是数据拼接时索引错了。学习率检查太大导致震荡太小导致不动。先试一个中间值。模型加载检查预训练权重是否正确加载有没有静默失败损失函数检查是否和任务匹配有没有mask错位置8.2 推理结果异常的常见原因推理输出乱七八糟常见原因有训练和推理的预处理不一致这是最高频的坑。训练时怎么处理输入推理时必须一模一样。生成参数设置不当温度太高导致胡言乱语或者重复惩罚太高导致不连贯。模型权重加载错误加载了错误的checkpoint或者权重没加载全。上下文超长超过模型最大长度后面的内容被截断。8.3 显存不足的应急处理OOM是家常便饭应急手段按优先级手段效果代价减小批次大小立即见效吞吐下降缩短序列长度效果明显可能截断信息梯度累积保持有效批次训练变慢梯度检查点大幅省显存训练变慢混合精度省显存且加速可能影响精度模型量化省显存精度损失8.4 我踩过的几个典型坑坑一评估集泄漏。有一次我不小心把评估集的一条样本混进了训练集结果那个模型在评估集上分数奇高上线后原形毕露。后来我在代码里加了断言训练前检查训练集和评估集的交集必须为空。坑二随机种子没固定。同一个配置跑两次结果不一样排查了半天才发现是随机种子的问题。现在我的训练脚本第一行就是固定所有随机源。坑三忽略了数据顺序。训练数据如果按类别排好序模型会学到错误的顺序信息。一定要打乱。这个坑很隐蔽因为loss曲线看起来正常但泛化能力差。坑四过早优化。一开始就追求极致性能结果模型效果还没验证清楚白费功夫。先跑通再优化这个顺序不能反。9. 写在最后从零到一之后的路把上面这套闭环跑通你就已经跨过了AI工程最难的那道门槛——从只会看论文到能做出东西。后面的事无非是在每个模块上加深数据层引入更专业的版本管理和质量筛选训练层尝试更复杂的微调策略推理层上专用引擎做性能优化评估层建立更完善的自动化体系部署层做高可用和弹性伸缩。但我想提醒一句不要为了用新技术而用新技术。我见过太多项目本来一个简单方案就能解决非要上最时髦的架构结果维护成本高得吓人效果还没提升多少。工程的核心永远是解决问题不是炫技。如果你现在正准备动手我的建议是今天就找一个最小的任务按第2章那个五步路径走一遍。哪怕模型只有几百万参数哪怕数据只有几十条只要闭环跑通了你就已经比90%停留在看教程阶段的人走得远了。剩下的就是在一次次迭代里把直觉磨出来。这个东西没有捷径但每一步都算数。
返回列表