ARTICLE DETAIL

资讯详情

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

从零搭建AI工程能力:避开论文陷阱,两周落地可复现的AI应用骨架

从零搭建AI工程能力:避开论文陷阱,两周落地可复现的AI应用骨架 1. 从零搭建AI工程能力为什么我劝你别一上来就啃论文这两年“AI工程师”这个岗位被炒得火热招聘JD上动不动就是“熟悉Transformer、有LLM微调经验、掌握RAG架构”。很多人一看就慌了转头去啃《Attention Is All You Need》结果公式推导看了三遍连一个能跑起来的demo都没写出来。我自己带过几个刚入行的同学也见过不少转岗的朋友最大的误区就是把“AI工程”等同于“AI研究”。这两件事的差别比“造车”和“修车”的差别还大。ai-engineering-from-scratch这个标题说白了就是一条从零开始、把AI工程能力真正落地到项目里的路径。它要解决的核心问题不是“模型为什么有效”而是“模型怎么在我的机器上跑起来、怎么接进业务、怎么在成本可控的前提下稳定输出”。适合看这篇内容的人有三类一是刚转行想做AI应用开发的程序员二是有后端或数据背景、想补齐AI工程链路的工程师三是带小团队、需要快速验证AI产品可行性的技术负责人。如果你指望看完就能发顶会论文那可以关掉了但如果你想在两周内搭出一套能用的AI应用骨架下面这些内容应该能帮你少走不少弯路。我先把结论摆在这儿AI工程能力的构建核心不在于你懂多少数学而在于你能不能把“数据—模型—服务—评估”这条链路串起来并且每一环都有可复现的操作。接下来我会按我实际带人做项目的顺序把这条链路拆开讲清楚。2. 整体思路拆解为什么我不建议从训练模型开始2.1 先搞清楚“工程”和“研究”的边界在哪很多人一上来就想自己训一个模型觉得这样才叫“从零开始”。我试过也带人试过结论很明确对绝大多数应用场景来说自己从头训练模型是性价比最低的选择。原因很简单训练一个可用的基础模型光是数据清洗和算力成本就够一个小团队喝一壶的而且训出来的效果大概率还不如开源模型微调一下。AI工程的“从零”指的是从工程链路的零开始而不是从模型参数的零开始。你要从零搭建的是数据怎么进来、模型怎么调用、结果怎么评估、服务怎么部署、成本怎么控制。这五件事里只有“模型怎么调用”和模型本身强相关其余四件都是纯工程问题。我见过太多人把80%的时间花在调模型上结果上线时发现连个并发请求都扛不住这就是典型的用力用错了地方。所以我的建议是除非你的业务场景非常垂直、开源模型完全无法覆盖否则优先走“开源模型微调工程优化”的路线。这条路的好处是每一步都有成熟的工具和社区支持你踩的坑大概率别人已经踩过了。2.2 技术选型的三个核心考量维度选型这件事我一般看三个维度效果下限、工程复杂度、成本可控性。效果下限决定这个方案能不能用工程复杂度决定你要花多少人天成本可控性决定这个方案能不能长期跑下去。拿模型选型举例。如果你做的是中文问答7B到14B参数量的开源模型在大多数场景下效果下限是够的工程复杂度中等一张消费级显卡就能跑推理。如果你非要上70B效果确实会好一些但工程复杂度直接翻倍成本也不是一个量级。我实测下来很多业务场景用7B微调后的效果和直接调70B的差距并没有想象中那么大但成本差了将近十倍。再拿框架选型举例。推理框架这块vLLM和TGI是目前比较主流的选择。vLLM的PagedAttention对显存利用率提升明显适合并发量稍大的场景TGI部署简单适合快速验证。我的经验是验证阶段用TGI生产阶段如果并发上来了再切vLLM不用一上来就追求最优解。提示选型时不要只看benchmark上的数字一定要拿你自己的业务数据跑一遍。我见过太多次benchmark排名第一的模型在实际业务数据上表现还不如排名第五的。2.3 一条可复现的AI工程链路长什么样我把这条链路拆成五层从下往上依次是基础设施层、模型服务层、应用逻辑层、评估监控层、迭代优化层。每一层都有明确的输入输出和验收标准。基础设施层负责GPU资源管理、容器化部署、网络配置这一层的验收标准是“能稳定跑起来一个推理服务”。模型服务层负责模型加载、推理加速、请求调度验收标准是“单请求延迟和并发吞吐达到预期”。应用逻辑层负责prompt组装、上下文管理、工具调用验收标准是“业务逻辑能正确执行”。评估监控层负责效果评估、日志采集、异常告警验收标准是“能发现效果退化”。迭代优化层负责数据回流、模型更新、A/B测试验收标准是“新版本能灰度上线”。这条链路的好处是每一层都可以独立验证出了问题也容易定位。我见过不少人把应用逻辑和模型服务混在一起写结果调prompt的时候把服务搞崩了排查半天才发现是耦合太深。3. 核心细节解析数据、模型、服务三件套怎么落地3.1 数据准备别小看清洗和格式转换数据这块我踩过最大的坑就是“拿到数据直接喂”。不管是微调还是RAG原始数据里一定有大量噪声重复内容、格式错乱、无关信息。我一般会走三步去重、清洗、格式化。去重不是简单的完全匹配去重而是要做语义去重。比如“今天天气怎么样”和“今天天气如何”字面不同但语义一样这种在训练数据里多了会让模型学到冗余模式。我常用的是基于embedding的相似度去重阈值一般设在0.95左右具体看数据分布调整。清洗主要是处理特殊字符、HTML标签、乱码。这块没什么技术含量但特别耗时间。我的经验是写一套正则规则批量处理然后人工抽检5%左右。如果抽检发现问题再补充规则重新跑。格式化是最关键的一步。微调数据一般要转成instruction-input-output的格式RAG数据要切成合适大小的chunk。chunk大小这个参数很讲究太小了上下文不完整太大了检索精度下降。我一般从512 token开始试根据实际效果在256到1024之间调整。# 一个简单的数据清洗示例 import re def clean_text(text): # 去除HTML标签 text re.sub(r[^], , text) # 去除多余空白 text re.sub(r\s, , text) # 去除特殊字符 text re.sub(r[^\w\s\u4e00-\u9fff.,!?;:], , text) return text.strip()注意清洗规则一定要在验证集上验证别把有用信息洗掉了。我有一次把代码块里的特殊符号洗没了导致模型学出来的代码全是错的。3.2 模型微调LoRA是首选但别迷信参数微调这块LoRA基本是现在的标配。它的核心思想是在原模型旁边加一个小矩阵只训练这个小矩阵不动原模型参数。好处是显存占用小、训练快、可以多个LoRA切换。LoRA有几个关键参数rank、alpha、dropout、target_modules。rank决定小矩阵的维度一般8到64之间任务越复杂rank越大。alpha是缩放因子通常设成rank的两倍。dropout防过拟合0.05到0.1之间。target_modules决定哪些层加LoRA一般q_proj和v_proj是必加的。我实测下来rank16、alpha32、dropout0.05这组参数在大多数中文任务上表现稳定。但这不是金科玉律你得根据任务复杂度调。任务简单就降rank任务复杂就升rank但别超过64再大就失去LoRA的意义了。训练数据量这块我的经验是至少500条高质量样本起步2000条左右能看到明显效果。但质量比数量重要500条精标数据的效果往往好过5000条粗标数据。我见过有人拿几万条爬来的数据训结果模型学会了各种奇怪的口癖。# LoRA配置示例 from peft import LoraConfig lora_config LoraConfig( r16, lora_alpha32, target_modules[q_proj, v_proj, k_proj, o_proj], lora_dropout0.05, biasnone, task_typeCAUSAL_LM )3.3 推理服务延迟和吞吐的平衡术推理服务这块核心矛盾是延迟和吞吐。延迟是单个请求的响应时间吞吐是单位时间能处理的请求数。这两个指标往往是矛盾的批量处理能提高吞吐但会增加单个请求的延迟。我的做法是分场景处理。如果是实时对话场景优先保延迟batch size设小一点一般1到4。如果是离线批处理场景优先保吞吐batch size可以设到16甚至32。vLLM的continuous batching机制能在这两者之间做个平衡它会动态调整batch不用你手动设死。显存管理是另一个大坑。模型权重、KV cache、中间激活值都要占显存。7B模型用fp16加载大概占14G加上KV cache和激活值一张24G的卡跑推理是够的但并发一高就容易OOM。我的经验是留20%的显存余量别把卡跑满。# vLLM启动示例 python -m vllm.entrypoints.openai.api_server \ --model /path/to/model \ --tensor-parallel-size 1 \ --gpu-memory-utilization 0.85 \ --max-model-len 4096 \ --dtype float16提示gpu-memory-utilization别设太高0.85到0.9之间比较稳。设到0.95以上容易在高峰期OOM。4. 实操过程从零搭一套可用的AI应用骨架4.1 环境准备与依赖安装环境这块我强烈建议用容器。不是因为它高级而是因为它能帮你省掉大量“在我机器上能跑”的扯皮时间。基础镜像选nvidia/cuda的官方镜像CUDA版本根据你的显卡驱动来定一般12.1以上比较稳。Python环境用conda或者venv都行我习惯用conda因为可以锁Python版本。依赖安装这块torch、transformers、peft、vllm这几个是核心版本兼容性要特别注意。我一般会先装torch再装transformers最后装vllm因为vllm对torch版本有要求。# 环境准备示例 conda create -n ai-eng python3.10 conda activate ai-eng pip install torch2.1.0 --index-url https://download.pytorch.org/whl/cu121 pip install transformers4.36.0 peft0.7.0 pip install vllm0.2.7依赖装完之后先跑一个最小验证加载一个7B模型跑一次推理看能不能出结果。这一步能帮你排除80%的环境问题。如果这一步就报错别急着往下走先把环境问题解决掉。4.2 数据管道的搭建与验证数据管道我一般分成三个模块采集、处理、存储。采集模块负责从各种来源拉数据处理模块负责清洗和格式化存储模块负责把处理好的数据存成训练或检索需要的格式。采集这块如果是内部数据直接读数据库或者文件系统就行。如果是外部数据注意遵守相关规范别乱爬。处理这块前面说的去重、清洗、格式化三步走。存储这块微调数据存成jsonlRAG数据存成向量库。向量库选型这块faiss、chroma、milvus都用过。faiss轻量适合单机小规模chroma上手快适合快速验证milvus功能全适合生产环境。我的建议是验证阶段用chroma生产阶段如果数据量上来了再切milvus。# 数据管道示例 import json from langchain.text_splitter import RecursiveCharacterTextSplitter def process_data(raw_texts, chunk_size512, chunk_overlap50): splitter RecursiveCharacterTextSplitter( chunk_sizechunk_size, chunk_overlapchunk_overlap ) chunks [] for text in raw_texts: cleaned clean_text(text) chunks.extend(splitter.split_text(cleaned)) return chunks # 存成jsonl with open(train_data.jsonl, w, encodingutf-8) as f: for chunk in chunks: f.write(json.dumps({text: chunk}, ensure_asciiFalse) \n)数据管道搭完之后一定要做验证。验证的方法是随机抽100条处理后的数据人工看一遍看格式对不对、内容有没有被洗坏。这一步花不了多少时间但能帮你避免后面训练出来的模型“学歪了”。4.3 模型微调与效果验证微调这块我用的是transformers的Trainer配合peft。训练参数这块learning rate一般设1e-4到5e-5之间batch size根据显存来epoch一般3到5轮。我实测下来3轮之后效果提升就很有限了再多容易过拟合。训练过程中要盯着loss曲线。如果loss下降很慢可能是learning rate太小如果loss震荡厉害可能是learning rate太大或者batch size太小如果loss先降后升那就是过拟合了赶紧停。# 训练配置示例 from transformers import TrainingArguments training_args TrainingArguments( output_dir./lora_output, per_device_train_batch_size4, gradient_accumulation_steps4, num_train_epochs3, learning_rate2e-4, warmup_ratio0.03, logging_steps10, save_strategyepoch, fp16True )训练完之后一定要做效果验证。验证的方法是准备一个测试集对比微调前后模型在测试集上的表现。测试集要和训练集分布一致但内容不重叠。我一般会看三个指标准确率、流畅度、指令遵循度。准确率看回答对不对流畅度看语句通不通顺指令遵循度看模型有没有按你要求的格式输出。如果效果不达预期先别急着调参。先检查数据质量再看prompt模板最后才调训练参数。我踩过的坑里80%的效果问题都是数据问题不是参数问题。4.4 服务部署与接口封装服务部署这块我用的是FastAPI加vLLM的组合。FastAPI负责接口层vLLM负责推理层。接口设计上我一般会提供两个端点一个同步接口一个流式接口。同步接口适合短文本流式接口适合长文本。# FastAPI接口示例 from fastapi import FastAPI from pydantic import BaseModel from vllm import LLM, SamplingParams app FastAPI() llm LLM(model/path/to/model) sampling_params SamplingParams(temperature0.7, max_tokens512) class Request(BaseModel): prompt: str app.post(/generate) async def generate(req: Request): outputs llm.generate([req.prompt], sampling_params) return {result: outputs[0].outputs[0].text}部署完之后要做压力测试。我用的是locust模拟并发请求看延迟和吞吐。压力测试的目的是找到系统的瓶颈是GPU算力不够还是网络带宽不够还是接口层处理太慢。找到瓶颈之后针对性优化别盲目加机器。注意压力测试别在生产环境做找个隔离环境跑。我有一次手滑在生产环境跑了压力测试直接把服务打挂了。5. 常见问题与排查技巧实录5.1 模型加载失败与显存不足模型加载失败最常见的原因是显存不够。7B模型fp16加载要14G左右加上KV cache和激活值24G的卡勉强够用。如果加载时报OOM先检查是不是有其他进程占了显存用nvidia-smi看一下。如果确实是模型太大可以试试量化加载4bit量化能把显存占用降到7G左右。# 4bit量化加载示例 from transformers import BitsAndBytesConfig bnb_config BitsAndBytesConfig( load_in_4bitTrue, bnb_4bit_compute_dtypefloat16, bnb_4bit_quant_typenf4 )量化会损失一点效果但大多数场景下损失可以接受。我实测下来4bit量化的效果损失在5%以内但显存占用降了一半性价比很高。5.2 推理结果不稳定与重复生成推理结果不稳定一般是采样参数的问题。temperature太高会导致输出随机性大top_p太低会导致输出多样性不足。我一般设temperature0.7、top_p0.9、repetition_penalty1.1这组参数在大多数场景下表现稳定。重复生成是另一个常见问题尤其是长文本生成时。原因是模型陷入了局部循环。解决办法是加repetition_penalty或者用no_repeat_ngram_size限制重复n-gram。我一般两个都用repetition_penalty设1.1no_repeat_ngram_size设3。# 采样参数配置 sampling_params SamplingParams( temperature0.7, top_p0.9, repetition_penalty1.1, no_repeat_ngram_size3, max_tokens1024 )5.3 常见问题速查表问题现象可能原因排查方法解决方案模型加载OOM显存不足nvidia-smi查看显存占用量化加载或换更大显存卡推理延迟高batch size太大查看GPU利用率减小batch size或换推理框架输出重复采样参数不当检查temperature和repetition_penalty调整采样参数效果不达预期数据质量问题人工抽检训练数据重新清洗数据服务崩溃并发过高查看服务日志加限流或扩容微调后效果变差过拟合看loss曲线减少epoch或加dropout这张表是我自己踩坑总结出来的基本上覆盖了80%的常见问题。遇到问题先查表查不到再深入排查。5.4 几个容易被忽略的实操心得第一个心得日志一定要打全。我见过太多人出了问题查不到原因就是因为日志打得太少。请求参数、响应内容、耗时、显存占用这些都要打。日志级别用INFO就行DEBUG太吵。第二个心得版本一定要锁死。AI这块的库更新太快了今天能跑的代码明天可能就报错。我一般会用requirements.txt锁死所有依赖版本包括间接依赖。别嫌麻烦这能帮你省掉大量“昨天还能跑”的问题。第三个心得评估一定要自动化。人工评估太慢而且不一致。我一般会写一套自动化评估脚本用规则或者小模型做打分。虽然不如人工准但胜在快和一致适合迭代阶段快速验证。第四个心得别追求一步到位。AI工程是个迭代的过程第一版能跑起来就行别想着一次做到完美。我见过太多人卡在“优化”阶段结果连第一版都没上线。先上线再优化这是最务实的做法。6. 迭代优化让AI应用越跑越好的几个抓手6.1 数据回流机制的搭建数据回流是迭代优化的核心。简单说就是把线上真实请求和用户反馈收集起来筛选出有价值的样本补充到训练数据里重新训练模型。这个闭环一旦跑通模型效果会随着时间越来越好。回流的数据要筛选不是所有数据都有价值。我一般会筛三类模型回答错误的、用户明确反馈不满意的、模型回答不确定的。这三类数据对模型提升最大。筛选可以用规则也可以用模型打分我一般两者结合。回流数据要脱敏尤其是涉及用户隐私的内容。脱敏这块我一般用正则替换把手机号、身份证号、邮箱这些敏感信息替换掉。别嫌麻烦这是合规底线。6.2 A/B测试与灰度发布新模型上线之前一定要做A/B测试。方法是把流量分成两组一组用旧模型一组用新模型对比两组的核心指标。核心指标根据业务来定对话场景看满意度和轮次问答场景看准确率和召回率。A/B测试的样本量要够太小了结果不可信。我一般会跑至少一周或者积累到1000个以上的样本。如果新模型指标明显优于旧模型再全量上线。如果差距不大那就看成本成本低的优先。灰度发布是A/B测试的下一步。全量上线之前先放10%的流量跑一天观察有没有异常。没问题再逐步放大到50%、100%。这个过程虽然慢但能帮你避免大事故。6.3 成本优化的几个实用手段成本优化这块我一般从三个方向入手模型压缩、请求合并、缓存复用。模型压缩包括量化和蒸馏。量化前面说过了4bit量化能省一半显存。蒸馏是用大模型教小模型让小模型达到接近大模型的效果。蒸馏这块我试过效果确实不错但训练成本不低适合长期跑的场景。请求合并是把多个短请求合并成一个长请求提高GPU利用率。这个适合离线批处理场景实时场景不太适用。缓存复用是把常见问题的回答缓存起来下次遇到同样的问题直接返回缓存结果。这个能大幅降低推理成本尤其是FAQ类场景。缓存用Redis就行key用问题的embeddingvalue用回答。提示缓存要注意失效策略。我一般设24小时过期太长了回答会过时太短了命中率低。6.4 监控告警体系的建立监控告警是保障服务稳定的最后一道防线。我一般监控四个维度服务可用性、推理延迟、显存占用、效果指标。服务可用性看接口成功率低于99%就告警。推理延迟看P99超过阈值就告警。显存占用看使用率超过90%就告警。效果指标看准确率和满意度连续下降就告警。告警渠道用邮件或者即时通讯工具都行关键是告警要能触达。我见过有人配了告警但没人看结果服务挂了半天才发现。告警一定要配到具体的人别配到群里。监控数据要存下来至少存一个月。这些数据不仅能用来排查问题还能用来分析趋势。比如你发现每周一延迟都高那可能是周一流量大提前扩容就行。这套东西搭下来一个可用的AI应用骨架就成型了。从数据到模型到服务到监控每一环都有可复现的操作。后面就是在这个骨架上不断迭代根据业务反馈优化效果和成本。我自己带人做项目时一般两周能搭出第一版一个月能跑通迭代闭环。这个速度不算快但胜在稳每一步都踩实了后面不容易翻车。
返回列表