☰
AI工程实战:从数据清洗到模型部署的完整链路
2026/9/29 18:59:02 网站建设 项目流程

写在前面:这不是一篇教你数学原理的文章

我见过太多人收藏了一堆“从入门到精通”的教程,报名了好几个训练营,最后却连一个能稳定跑起来的AI项目都拿不出来。原因不是他们笨,而是大多数教程都在讲模型内部怎么工作,很少有人讲“AI工程”本身该怎么落地。

我说的AI工程,不是调一次API、跑通一段notebook,而是从数据准备、模型选型、微调实验、评估上线到日常维护的完整链路。这个项目的名字很长——ai-engineering-from-scratch,翻译过来就是“从零开始搞AI工程”。它是我自己维护的一套实战笔记和代码骨架,目标很简单:让一个只有基础Python能力的人,也能按步骤搭出一套可运行、可迭代、可评估的AI系统,而不是停留在“跑通demo就结束”的阶段。

如果你是从传统软件开发转过来,或者已经在用大模型API、但想往前再走一步,这篇文章会非常适合你。下面我会把这条链路里的关键节点、我踩过的坑、以及我认为最值得投入的方向,按实战顺序完整讲一遍。

1. 为什么想写“ai-engineering-from-scratch”这个项目

1.1 网络上教程的断层:大家都在讲架构,没人讲搬砖

你随便搜一下“大模型入门”,出来的内容基本都是Transformer结构、注意力机制、损失函数推导。这些东西重不重要?重要。但对于一个想真正动手做项目的人来说,它们不是第一步。

真正的第一步是什么?是拿到一台干净的机器,把Python环境装好,把模型权重下载下来,把一份训练数据整理成模型能吃的格式,然后跑通一个最小的训练流程。这个过程看起来一点不“高级”,但它决定了你后面所有工作能不能顺利进行。

我见过不少朋友在Kaggle或GitHub上找到一个不错的项目,兴奋地clone下来,结果卡在环境依赖上两天,最后不了了之。教程断层就在这里——几乎没有人认真讲“如何让一个AI项目可复现、可维护”,而这恰恰是工程的核心。

1.2 AI工程和传统软件工程最本质的区别:不确定性管理

我在写这个项目之前有将近十年的后端开发经验。传统软件工程里,代码行为是确定的:输入A,走完逻辑,输出B。你可以写单元测试,可以精确控制流程。

AI系统完全不是这样。同一个Prompt,同一个模型,温度调到0它偶尔还会给你不同的答案。数据一更新,模型精度可能突然掉了几个点。上周测得好好的功能,这周用户反馈变了个说法,效果立刻变差。

所以AI工程的核心不是“实现功能”,而是“管理不确定性”。我们要做的所有工作——数据版本化、评估集设计、灰度发布、监控告警——本质上都是在把不可控的因素逐步关进笼子里。这是我写这个项目时最重要的一条主线,想明白了这一点,后面每一步你都知道自己为什么要做。

1.3 这个项目到底适合谁来学

先说结论:适合三类人。

第一类是从传统开发转过来的工程师。你已经具备代码能力,缺的是AI工程里数据怎么管、模型怎么选、部署形态怎么定这些“非模型知识”。第二类是在用API但想深入一步的产品或自学爱好者。你已经知道Prompt怎么写,但还想搞清楚微调是怎么一回事、什么时候该微调、什么时候不该微调。第三类是刚入行的学生,学校教了理论,但你需要一个可落地的最小闭环来建立体感。

不适合谁呢?如果你想从零推导数学公式,那出门左转去看论文。我这里只讲怎么把东西做出来、怎么让它稳定跑下去,不搞学术。

2. 环境与基建:先让项目达到“克隆即运行”

2.1 为什么我放弃conda和pip,选了uv做环境管理

先说一个现实问题:Python环境管理是AI项目里最烦人的事情之一。早期我习惯用conda,但conda装包慢、环境大,而且依赖解析经常撞车。后来项目多了,我彻底转到了uv这套工具上。

uv是一个用Rust写的Python包管理器,兼容pip和requirements.txt,但速度快得多。实测下来,一个常见的transformers环境,uv安装速度能比pip快五到十倍,锁文件清晰,还能自动管理Python版本。最关键的是它支持pyproject.toml——项目依赖写在声明文件里,任何人clone下来后只需要两条命令:

uv sync uv run python scripts/train_lora.py

这基本实现了“克隆即运行”。我强烈建议所有新项目都从uv init开始,而不是继续手工维护那份容易过期的requirements.txt。

提示:如果是在国内网络环境下,安装uv可以用cargo install uv或者直接下载发布包,速度都还不错。Python官方源如果太慢,记得把pip或者uv的index换到国内镜像,这点后面很多操作都会受益。

2.2 一个最小AI项目骨架长什么样

很多初学者拿到一个AI项目后第一反应是“代码在哪个文件”。但工程化之后,项目结构比代码本身更能决定你的效率。我现在的标准骨架大致是这样的:

ai-engineering-from-scratch/ ├── pyproject.toml ├── Makefile ├── src/ │ └── app/ │ ├── data/ │ │ ├── load.py │ │ ├── clean.py │ │ └── versions.py │ ├── model/ │ │ ├── infer.py │ │ └── train_lora.py │ └── eval/ │ └── run_eval.py ├── scripts/ │ ├── download_weights.sh │ └── run_web.sh ├── tests/ │ ├── test_data.py │ └── test_infer.py ├── data/ │ ├── raw/ │ ├── processed/ │ └── versions/ └── artifacts/ ├── checkpoints/ └── eval_results/

几个关键模块我解释一下。

pyproject.toml是项目声明的核心,依赖、元数据、运行命令统统记在里面。Makefile则是给“记不住命令的人”准备的入口。比如我会把常用操作写成:

BIN := uv run train: $(BIN) python scripts/train_lora.py eval: $(BIN) python scripts/run_eval.py serve: $(BIN) python -m app.web.server

这样你不需要记住去哪看命令行,只用make train、make eval就完事了。

data/raw和data/processed分开存放很重要——原始数据永不改动,任何清洗都生成新文件放到processed。这个习惯让我避免了很多“数据被覆盖后追不回来”的悲剧。

2.3 模型权重下载:Hugging Face CLI与镜像加速

环境搞定后,第一步就是下载底座模型。很多人卡在Hugging Face下载太慢,其实解法很简单:只用一行环境变量切到镜像站点。

export HF_ENDPOINT=https://hf-mirror.com

然后命令行登录一次:

huggingface-cli login

输入你的Access Token即可。有些模型(比如Llama系列)需要先接受使用许可,用CLI下载时如果遇到401或403,先去Hugging Face官网的模型页面点击同意协议,再回来重试。

之后下载权重就非常直接了:

huggingface-cli download Qwen/Qwen2.5-7B-Instruct --local-dir models/Qwen2.5-7B-Instruct

提示:--local-dir而不是--cache-dir的好处是,权重文件会以一个清晰明了的目录结构存在项目的models/下,不会被Hugging Face默认的缓存目录打散成一堆哈希文件夹。做工程,目录可读性比省几GB磁盘重要得多。

2.4 实验追踪:为什么用MLflow而不是TensorBoard

刚开始做微调实验的时候,我只是把loss打到终端日志里,结果第二天就忘了哪个参数组跑出了最好的结果。后来换成了MLflow做实验追踪。

选择MLflow而不是TensorBoard,最大理由是它的Tracking模块能同时记录参数、指标和产物,而且支持本地方案,不依赖云服务。我用一个最简单的初始化就能开跑:

import mlflow mlflow.set_experiment("qwen2.5-sft-v1") with mlflow.start_run(): mlflow.log_param("lora_r", 16) mlflow.log_param("lora_alpha", 32) mlflow.log_param("learning_rate", 2e-4) mlflow.log_metric("eval_f1", 0.83) mlflow.log_artifact("artifacts/checkpoints/best_model", artifact_path="checkpoints")

记录完之后,浏览器打开http://localhost:5000就能看到每次实验的参数和指标对比。这一步看起来简单,但长期价值非常大——你以后做模型迭代时,能一眼看出上一次成功的配置是什么,不用再靠猜。

3. 数据工程:真正的护城河

3.1 为什么数据工程要放在模型前面

微调过模型的人都知道一个体会:模型效果不好,90%的情况不是模型不强,而是数据不行。脏数据、不均衡数据、错误标注,这些问题会直接反映到最终行为上。你想靠调参挽回数据问题,基本是徒劳。

所以我写这个项目时,把数据工程放到了所有模型操作的优先级前三位。这不是说模型不重要,而是模型已经是公开的商品化的能力,你对效果的控制权其实绝大部分来自数据。

3.2 数据清洗的具体规则

我用的清洗流程,大概分为五步,每一步都很朴素但不做不行。

去重:用text的哈希值做主键去掉完全重复的样本,再用模糊相似度(例如rapidfuzz的token set ratio)去掉语义重复的样本,阈值一般定在85分以上。

去污染:如果样本里包含改写过的Prompt痕迹、或者候选答案本身就是从某大模型生成的,这类数据对微调有潜在风险。我一般会做一个规则过滤,去掉含有“作为AI助手”“我不能帮助”这类标志性短语的样本。

去除低质量内容:按长度过滤,过短样本去掉,过长样本截断到目标上下文长度的1.5倍再保留。统一编码为UTF-8,去掉不可见字符和控制符号。

个人信息脱敏:手机号、邮箱、身份证号这类信息用正则替换成占位符,别指望模型为你保密,训练数据里不应出现真实隐私信息。

最后人工抽检:清洗完后随机抽100条人工过目,确认过滤规则没有误伤正常数据。这个抽检比例不高,但能拦住八成规则拍脑袋写错的情况。

3.3 标注策略:先用弱监督,再用人工校验

做监督微调(SFT)必须要标签,但AI项目里数据量大、标注成本高是永远的痛点。我的策略是“弱监督规则标注 + 人工抽样校验”。

比如做一个客服意图分类的数据集,我先写一批简单规则:

def weak_label(text: str) -> str: if any(kw in text for kw in ["退货", "退款", "换货"]): return "after_sales" if any(kw in text for kw in ["密码", "登录", "账号"]): return "account_issue" if "多少钱" in text or "价格" in text: return "price_query" return "other"

先用这类规则给大量未标注数据打粗标签,然后人工抽300条做准确率评估。如果某个类别准确率低于80%,我就加规则或人工重标那一类。这样能花比较少的钱,得到一份质量可控的训练集。

注意:弱监督规则不能只标“对”的样本。很多新手会忽略“负样本”和“拒答样本”,比如用户表达感谢、闲聊时,应该引导模型给出恰当回应,而不是硬塞到业务分类里。训练数据里如果没有这类样本,模型上线后很容易把无关对话也强行归类。

3.4 数据版本化:别再用文件名记版本了

刚开始做项目时,我处理数据的习惯是train_v2.csv、train_final_final.csv,后来自己都分不清哪个是哪个。现在我用一套非常轻量的版本化方案。

核心思路是:每次生成新数据集时,同时生成一份dataset_card.json,记录来源、清洗规则、样本数、时间戳和hash值。

{ "name": "helpdesk_intent_v3", "source": "customer_logs_20250201", "clean_rules": ["dedup", "personally_identifiable_information_masking", "length_filter_8_1024"], "num_samples": 12580, "created_at": "2025-02-05T10:00:00Z", "sha256": "a3f2c8e1b9..." }

真正训练的时候,直接在配置里写数据集版本的路径,模型训练完也把对应的dataset_card.json记录到实验追踪系统里。这样任何一个效果变差的问题都能反过来追溯:是数据变了,还是模型变了,还是代码变了。这一招在后续迭代里帮了我大忙。

4. 模型选型与微调:用决策树代替盲目试错

4.1 先算账再选模型

很多人选模型只看排行榜,谁分高用谁。但工程必须看约束条件。我的选型计算从三件事开始:显存、延迟、许可证。

显存估算有个非常实用的公式:加载FP16格式的模型大约需要参数量 × 2字节。一个7B模型大概需要14GB显存才能跑推理,13B需要26GB,34B需要68GB。如果做推理服务还需要加上KV Cache,实际占用量会比这个基础值再高一些。

我做了一张常用的估算表供参考:

模型规模参数量FP16推理显存估算微调建议
7B/8B约70~80亿14~16GB单张24GB卡可跑QLoRA
13B/14B约130~140亿26~28GB单张48GB卡或双卡分布式
34B/72B340亿以上68GB以上多卡或托管API

然后算延迟。同样一个请求,7B模型在单张A10上大约几十毫秒到几百毫秒,72B模型即使有A100,延迟也可能翻几倍。你的业务场景如果要求首字延迟低于200毫秒,那72B可能一开始就不该进候选名单。

最后看许可证。不同模型的商用条款差异很大,比如部分模型仅限研究使用,部分允许商用但要求履行额外披露义务。选型之前,把许可证条款和法务过一遍,别等上线了再发现不能用。

4.2 Base模型与Chat模型的差别

Hugging Face上同一个系列通常会出两种版本:Base版和Instruct/Chat版。新手经常搞混:是不是微调就应该从Base开始?

我的建议是:绝大多数业务场景,直接用Chat/Instruct版继续做LoRA,不要从Base重新训。

原因很简单。Chat版在发布前就已经做过大规模的监督微调和偏好对齐,它知道怎么回答问题、怎么拒绝不合适的请求。你的领域任务是在这个良好基础上做“风格和内容迁移”,而不是从零教它说话。从Base开始意味着你得自己准备大量通用对话数据,成本极高,而且很容易训出“能说领域话、但整体对话能力下降”的模型。

4.3 LoRA实操:一套能直接抄的配置

选完底座,微调本身我建议从LoRA入手,不要上来就全参数微调。LoRA的优势是显存占用低、训练快、一个底座可以同时挂多个任务适配器,切换部署非常方便。我更进一步的实践是直接用QLoRA,把底座量化到4bit再挂LoRA适配器,一张24GB的卡就能训7B模型。

参考配置:

model_name: Qwen/Qwen2.5-7B-Instruct lora_r: 16 lora_alpha: 32 target_modules: - q_proj - k_proj - v_proj - o_proj - gate_proj - up_proj - down_proj lr: 2e-4 batch_size: 4 gradient_accumulation_steps: 4 num_epochs: 1 warmup_ratio: 0.03 max_seq_len: 1024

这些参数的相对关系比绝对值重要。lora_alpha一般设成lora_r的2倍,学习率在1e-4到5e-4之间比较稳。训练一个epoch就够了,SFT阶段真不需要多轮训练,轮数多了模型很容易在训练集上过拟合,导致通用能力下降。

训练时我还会固定住一个关键开关:use_chat_template必须开启。Chat模型的训练数据要按它的对话角色格式组织,如果丢给模型一段裸文本,它会非常困惑。数据格式大致是:

<|im_start|>user 如何查询我的订单状态?<|im_end|> <|im_start|>assistant 您好,请提供您的订单号,我帮您查看。<|im_end|>

每一行都要严格走这个模板。用transformers自带的apply_chat_template方法生成,千万别手写拼接,手写必错。

5. 评估体系:别让“感觉变聪明了”骗了你

5.1 为什么训练loss下降不代表效果变好

这个问题我踩过很大的坑。有一次我微调完一个模型,训练loss从2.1降到了0.8,当时觉得稳了。结果一上测试集,意图识别的准确率反而掉了3个百分点。

原因后来分析清楚了:loss下降只能说明模型在训练数据上的拟合度提高,不代表它学会了“泛化”。尤其是生成任务里,模型经常会把答案背下来,而不是理解规律。所以评估必须用独立于训练集的、贴近真实分布的评测集,绝不能只看训练曲线。

5.2 离线评估:Golden Set怎么设计

我的做法是维护一个“黄金评测集”,目标是覆盖真实业务中最重要的场景和已知的模型弱点。这个评测集不大,通常几百到一千条,但每条都经过人工复核标注。

设计Golden Set时我坚持三条原则。

第一,贴近真实分布。用线上日志样本的抽样和标注结果做了100多条真实用户问题,其余再手工补充边界情况。不要用模型自己生成的数据评估自己,那会高估效果。

第二,包含已知弱点。比如我发现模型经常把“改地址”的意图误判成“物流查询”,就往评测集里专门加一批这类难样本,防止以后迭代时这类问题悄悄复发。

第三,定期更新。每次上线的新功能、每收到一批典型badcase,都要补充进评测集。Golden Set需要跟着业务长,不是做一次就放那吃灰。

对于生成任务,我倾向用“规则化指标 + 关键要素匹配”。例如客服回答质量,我预先定义3到5个必须覆盖的要素(订单号、解决方案、致歉),检查模型输出是否包含这些要素。这种方法比直接让大模型打分更稳定,也更容易定位问题。

5.3 在线评估和灰度发布

离线评估过完,线上依然要有护栏。不要让你的模型一上线就面对100%流量。

我第一次上线时直接全量切过去,结果半天后收到用户投诉“回答像换了一个人”。虽然离线指标都是好的,但线上用户有些固定表达方式,Golden Set没有覆盖到。

从此以后我坚持灰度流程:先切5%流量观察一小时,没有明显异常(报错率、用户差评率)再逐步扩大到30%、100%。同时把每一次线上应答日志回流到数据池,作为下一轮微调的数据来源。这个循环一旦跑起来,你的系统就像有了“自我进化”的能力,每个月的效果都会往上走。

6. 从实验到生产:部署形态、失效边界与成本权衡

6.1 三种部署形态,各有什么代价

模型训好了,部署方式的选择会直接影响体验和账单。我见过团队把一个7B模型用CPU服务,结果单次推理响应时间冲到几十秒,用户自然留不住。部署不能拿“能跑”当唯一标准,必须在延迟、成本、数据隐私这几个维度之间做权衡。

我整理了三种常见形态供对照:

部署方式优点缺点适合场景
托管API(如OpenAI、国内大模型API)上手最快,效果强,无需GPU管理单次调用费累积快,数据要出域,定制能力弱快速验证、并发低、数据不敏感
自托管开源模型(vLLM/TGI)推理成本可控,数据不出域,可挂LoRA适配器需要GPU机器,运维复杂度高有稳定流量、需要定制模型
CPU/边缘小模型成本极低、无GPU依赖延迟高,能力有限内部工具、离线批处理、强环境约束场景

如果你的业务已经过了验证期,我通常建议走“自托管+适配器”的路线。GPU用按需租用,单张A10或L20起步,先把吞吐指标跑出来,再决定要不要长期包机。

6.2 失效边界设计:超时、重试、缓存、降级

模型服务和普通API不一样,它不是每次都能给出正确格式。你的工程一定要预设“如果模型罢工了,系统怎么兜底”。

超时:首字返回超过10秒直接断开,重新走降级逻辑。不要无限等待,那会拖垮整个调用链路。

重试:重试策略要有上限,而且要在指数退避基础上加随机抖动,避免重试风暴。对生成任务,相同输入重试结果可能不同,所以重试时可以考虑把温度调低,提高稳定输出的概率。

缓存:对高频且可复现的查询做结果缓存。比如“客服入口在哪”“退货政策是什么”,这类问题的答案基本固定,直接打缓存能省大量推理资源。我用Redis做LRU缓存,缓存命中率一度超过30%,等于白赚了三分之一的机器预算。

降级:当模型服务异常、或结果无法通过校验时,回退到模板话术或人工介入。你要的不是“永远不失败”,而是“失败了用户也不至于滞留”。

另外,解析模型输出时不要只祈盼JSON格式完美。真实场景里,大模型给出的JSON基本都会掺杂多余文本、换行或注释符。不要无脑重试,而是要写一个鲁棒解析函数:先用正则抽取最外层的{...},再做二次解析,失败后才进入重试流程。

6.3 成本账单:一次真实的估算

很多读者会问,自托管模型到底要花多少钱?

我按一个小场景算给你看。假设你有10个并发用户,每个用户平均每天20次请求,每次请求约300个输入token、200个输出token。

用7B模型自托管,单张A10 24GB显卡大概能把吞吐跑到10~20请求/秒,足够覆盖这种并发需求。按租用价每小时10元来算,全天跑满也就240元一天。托管API按每百万token大约5元的价格,同样请求量一天可能要花50到80元。看起来API更便宜?别忘了还有调用频率上去了、数据出域、以及无法使用自定义LoRA的隐性成本。

如果每天请求量再翻十倍,托管API的费用会线性增长,而自托管只要把显卡从1张加到3张,单位成本反而下降。这就是为什么流量稳定后我普遍建议迁移到自托管的原因:固定成本你付得起,边际成本你压得下。

6.4 监控和维护:模型上线只是开始

模型上线后,维护工作其实比训练更多。我每天会看四类监控指标:延迟分位数(P50/P95,关注长尾,而不是平均)、输入分布漂移(用户的话术模式突变要及时识别)、结果拒绝率(格式解析失败的占比,上升说明模型行为在漂移)、以及用户反馈信号(点赞、点踩、人工介入率)。

这些指标一旦触发阈值,我不会直接盲目重训模型,而是先回看对应的badcase,确认是不是数据分布变了。如果是,把最近的新样本补充进Golden Set,先线上微调规则或Prompt缓解,攒够一批数据后再做下一轮LoRA微调。这整套“监控→回流→重训→灰度”的循环,才是一个AI系统真正可持续的形态。

提示:内容安全过滤不要放在模型之后才做,最好在请求进来时、模型输出后都做一遍关键词和分类器检查。这是底线工程,宁可误伤,不可漏过。

最后再分享一个我个人的习惯

这个项目做到现在,我最深的体会是:AI工程最大的敌人不是“模型效果不够好”,而是“你搞不清楚哪儿出了问题”。数据版本化、实验追踪、Golden Set、灰度发布,这套流程看起来不酷,但它们才是让AI项目从“能用”走向“可信赖”的真正杠杆。

如果你刚准备起步,我建议你从一份真实的数据集开始,先把“数据清洗→微调→评估→部署”的最小闭环跑通,不要一开始就追求72B大模型和复杂分布式训练。先把小模型的全链路摸透,再慢慢往上加规模。这条路我已经替你们踩过一遍了,按这个顺序走,你能少绕很多弯路。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询