embedding model finetuning

嵌入模型微调实战

引言 RAG系统的检索质量很大程度上取决于嵌入模型的表示能力。通用嵌入模型(如text-embedding-ada-002、BGE)在开放域表现优秀,但在特定领域(医疗、法律、金融)往往力不从心。领域术语的特殊含义、专业表达方式的差异,都可能导致通用嵌入模型的语义匹配不准确。嵌入模型微调是解决这一问题的有效手段。本文分享嵌入模型微调的完整流程。 为什么需要微调嵌入模型 领域适配问题 以医疗领域为例,“阳性"在通用语境中是褒义的,但在医学语境中仅表示检测结果。通用嵌入模型可能将"阳性结果"与"好消息"的向量拉近,而医疗场景中"阳性结果"可能意味着确诊了某种疾病。 检索质量影响 嵌入模型不匹配会导致:相关文档检索不到(召回率低)、不相关文档排在前面(精确率低)、同义词无法匹配、专业缩写无法理解。 数据准备 训练数据格式 嵌入模型微调需要三元组数据:(query, positive, negative): query:查询文本 positive:与query相关的正样本 negative:与query不相关的负样本 数据构建方法 方法一:利用已有标注数据 如果已有问答对或查询-文档配对数据,直接用作正样本对: # 从FAQ库构建训练数据 def build_from_faq(faq_data): training_data = [] for item in faq_data: query = item['question'] positive = item['answer'] # 随机采样负样本 negatives = random.sample([other['answer'] for other in faq_data if other != item], 5) training_data.append({ 'query': query, 'positive': positive, 'negatives': negatives }) return training_data 方法二:LLM辅助生成 使用LLM根据文档内容生成查询: def generate_training_data(documents, llm): training_data = [] for doc in documents: # 生成多种查询方式 prompt = f""" 根据以下文档内容,生成5个不同角度的查询问题。 问题应该能通过这段文档内容回答。 文档:{doc} 生成的问题: """ queries = llm.generate(prompt).split('\n') for query in queries: if query.strip(): training_data.append({ 'query': query.strip(), 'positive': doc, 'negatives': get_hard_negatives(query, doc, all_docs) }) return training_data 方法三:挖掘难负样本 难负样本(Hard Negatives)对微调效果至关重要——它们是与query相关但不应该匹配的文档: def get_hard_negatives(query, positive_doc, all_docs, top_k=5): """使用现有嵌入模型挖掘难负样本""" # 用现有模型检索 results = vector_store.search(query, top_k=top_k * 3) hard_negatives = [] for result in results: if result['doc'] != positive_doc and result['score'] > 0.5: hard_negatives.append(result['doc']) if len(hard_negatives) >= top_k: break return hard_negatives 数据质量检查 训练前进行数据质量检查: ...

2026-06-27 · 3 min · 478 words · 硅基 AGI 探索者
rag vs fine tuning 2026

RAG vs 微调:2026 年的场景选择指南

一个被反复问起的问题 每次与企业客户交流,总会听到这个问题:“我们应该用 RAG 还是微调?“到了 2026 年,这个问题已经有了更清晰的答案——但不是简单的二选一。 随着 RAG 技术的成熟和微调工具链的普及,这两种知识注入策略的适用边界变得更加清晰。同时,新的混合范式也在出现,让"选择"本身变成了一道更精细的工程题。 一、RAG:动态知识的首选 1.1 RAG 的核心优势 RAG(检索增强生成)在 2026 年已经从实验室技术发展为企业级标准方案。它的核心价值在于: 知识时效性:RAG 的知识库可以实时更新。当企业产品文档发生变化时,只需更新向量库中的对应文档,无需重新训练模型。这对于产品迭代频繁的 SaaS 企业至关重要。 可溯源性:RAG 的每个回答都可以追溯到具体的源文档。在金融、医疗、法律等需要严格审计的领域,这一特性是不可替代的。 低成本启动:一个基础的 RAG 系统可以在数天内搭建完成,初期投入通常不超过数万元。而一次完整的模型微调,算力成本就可能达到数十万。 知识隔离:不同用户可以接入不同的知识库,实现知识的细粒度权限控制。这在多租户场景中是刚需。 1.2 RAG 的局限 但 RAG 也并非银弹。在实践中我们观察到几个固有限制: 检索质量天花板:当知识库中存在大量语义相近但含义不同的文档时(如法律条文的不同解释版本),检索的准确率会显著下降。即使用最先进的重排序模型,在 50 万+ 文档的知识库中,Top-5 检索准确率也很难超过 85%。 推理风格不可控:RAG 注入的是"知识”,而非"能力”。如果需要模型以特定的推理风格(如法律分析师的思维模式)回答问题,RAG 无法实现。 上下文窗口压力:即使在 128K 上下文窗口下,注入过多检索结果也会稀释模型对关键信息的注意力。我们在实验中发现,当检索结果超过 5000 token 时,模型对最后 20% 内容的利用率下降约 40%。 二、微调:能力与风格的重塑 2.1 微调的适用场景 微调在 2026 年的主要应用场景可以归纳为三类: 领域适配:让模型掌握特定领域的术语体系、推理范式和表达风格。例如,让通用模型学会以"临床药师"的视角分析药物相互作用。 格式控制:当需要模型稳定输出特定格式(如结构化 JSON、医疗报告模板)时,微调的可靠性远高于提示词工程。 能力注入:某些推理模式(如多步因果分析、特定类型的数学证明)通过少量高质量微调数据可以显著提升。LoRA 微调通常只需要 500-2000 条高质量样本即可见效。 2.2 微调的新范式 2026 年的微调实践出现了几个重要趋势: 偏好对齐微调(DPO/RLHF)成为标配:纯粹的 SFT(监督微调)已经不能满足需求。DPO(Direct Preference Optimization)让开发者可以通过"好回答-坏回答"对来微调模型,无需复杂的奖励模型。 ...

2026-06-26 · 2 min · 268 words · 硅基 AGI 探索者
fine tuning qlora guide

QLoRA 微调实战:4bit 量化下的高效训练

QLoRA 微调实战:4bit 量化下的高效训练 引言 大模型微调最大的障碍是显存。一个 7B 模型以 FP16 加载需要约 14GB 显存,加上 Adam 优化器状态和梯度,训练时轻松突破 60GB。QLoRA 通过 4bit 量化 + 低秩适配器,将训练显存压缩到 ~6GB,让单卡 24GB 消费级 GPU 微调 7B-13B 模型成为现实。 本文从原理到代码,完整覆盖 QLoRA 微调全流程。 1. QLoRA 核心原理 1.1 LoRA 回顾 LoRA(Low-Rank Adaptation)将权重更新 ΔW 分解为两个小矩阵的乘积: W' = W + BA 其中 W ∈ R^{d×k} 冻结,B ∈ R^{d×r} 和 A ∈ R^{r×k} 可训练,r << min(d, k)。 参数量从 d×k 降至 r×(d+k),但模型仍需以 FP16 加载。 1.2 QLoRA 的三大创新 QLoRA 在 LoRA 基础上引入三项关键技术: ...

2026-06-25 · 6 min · 1115 words · AI 实战派
fine tuning vs rag decision

微调 vs RAG:什么场景该选什么方案

引言 “我们应该微调还是用 RAG?"——这是 2024-2026 年 AI 工程师被问得最多的问题。两者都能让 LLM 适配特定领域,但适用场景截然不同。本文不给标准答案,而是给一个决策框架。 核心差异:先理解本质 微调(Fine-tuning) 微调是改变模型内部权重,让模型"学会"新的知识或行为模式。 原始模型 → 注入领域数据 → 新权重 → 直接推理 RAG(检索增强生成) RAG是不改变模型,在推理时"查阅"外部知识库。 用户问题 → 检索知识库 → 拼接到 Prompt → 模型生成答案 本质对比 维度 微调 RAG 知识存储位置 模型权重中 外部数据库中 知识更新方式 重新训练 更新数据库 推理时依赖 无外部依赖 需要检索系统 “记忆"方式 隐式(权重) 显式(文本) 幻觉风险 较高(知识编码不精确) 较低(有原文参考) 知识溯源 无法溯源 可追溯到来源文档 决策框架:7 个关键问题 问题 1:知识更新频率多高? 更新频率 │ ├─ 每日/每周更新 ──────→ RAG ✅ │ (产品目录、新闻、工单) │ ├─ 每月/每季度更新 ─────→ RAG 或混合 │ (技术文档、政策) │ └─ 几乎不变 ───────────→ 微调 ✅ (行业术语、品牌语调) 原理:微调每次更新知识都需要重新训练(数小时到数天),RAG 只需更新数据库(秒级)。 ...

2026-06-25 · 6 min · 1210 words · AI 实战派
llm finetune pipeline

LLM 微调流水线设计:从数据到部署的 MLOps

微调流水线全景 ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ 数据准备 │───▶│ 训练配置 │───▶│ 训练执行 │───▶│ 评估 │───▶│ 部署发布 │ │ 清洗/标注 │ │ LoRA/QLoRA│ │ GPU 集群 │ │ 自动化 │ │ 灰度/AB │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │ │ ▼ ▼ ▼ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ 数据版本 │ │ 模型注册 │ │ 监控告警 │ │ DVC/MLflow│ │ MLflow │ │ 回滚机制 │ └──────────┘ └──────────┘ └──────────┘ 一、数据准备 1.1 数据清洗 import re import json from datasets import Dataset class DataCleaner: def __init__(self, min_length=10, max_length=8192): self.min_length = min_length self.max_length = max_length def clean(self, samples: list[dict]) -> list[dict]: cleaned = [] for s in samples: text = s.get("text", "") # 去除 HTML 标签 text = re.sub(r'<[^>]+>', '', text) # 去除多余空白 text = re.sub(r'\s+', ' ', text).strip() # 长度过滤 if self.min_length <= len(text) <= self.max_length: # 去重(基于内容哈希) cleaned.append({**s, "text": text}) # 去重 seen = set() unique = [] for s in cleaned: h = hash(s["text"][:200]) if h not in seen: seen.add(h) unique.append(s) return unique def to_chat_format(self, samples: list[dict]) -> list[dict]: """转换为 chatml 格式""" formatted = [] for s in samples: formatted.append({ "messages": [ {"role": "system", "content": s.get("system", "你是一个有用的助手")}, {"role": "user", "content": s["input"]}, {"role": "assistant", "content": s["output"]} ] }) return formatted 1.2 数据增强 class DataAugmenter: """使用大模型生成训练数据变体""" AUGMENT_PROMPT = """基于以下示例,生成 3 个语义相同但表达不同的变体: 原文:{original} 要求: 1. 保持意图一致 2. 变化表达方式(句式/用词) 3. 不要改变关键信息 输出 JSON 数组格式。""" async def augment(self, sample: dict, llm_client) -> list[dict]: prompt = self.AUGMENT_PROMPT.format(original=sample["input"]) resp = await llm_client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": prompt}], response_format={"type": "json_object"} ) variants = json.loads(resp.choices[0].message.content) return [ {"input": v["input"], "output": sample["output"]} for v in variants.get("variants", []) ] 1.3 数据集分割 from sklearn.model_selection import train_test_split def split_dataset(data: list[dict], train=0.8, val=0.1, test=0.1): train_data, temp = train_test_split(data, test_size=1-train, random_state=42) val_data, test_data = train_test_split(temp, test_size=test/(test+val), random_state=42) return {"train": train_data, "val": val_data, "test": test_data} 二、训练配置 2.1 训练方法对比 方法 显存需求 训练速度 效果 适用场景 Full Fine-tune 极高(全部参数) 慢 最好 数据充足、预算充足 LoRA 低(0.1-1% 参数) 快 接近全量 通用首选 QLoRA 极低(4bit 量化) 中 略低于 LoRA 显存受限 P-Tuning v2 低 快 中等 特定任务 2.2 LoRA 训练配置 from peft import LoraConfig, get_peft_model, TaskType from transformers import AutoModelForCausalLM, TrainingArguments from trl import SFTTrainer def setup_lora_training(model_name="Qwen/Qwen2.5-7B"): model = AutoModelForCausalLM.from_pretrained( model_name, torch_dtype="auto", device_map="auto", ) lora_config = LoraConfig( task_type=TaskType.CAUSAL_LM, r=64, # LoRA 秩,越大效果越好但显存越多 lora_alpha=128, # 通常为 r 的 2 倍 lora_dropout=0.05, target_modules=[ "q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj" ], bias="none", ) model = get_peft_model(model, lora_config) return model, lora_config training_args = TrainingArguments( output_dir="./output/qwen-lora", num_train_epochs=3, per_device_train_batch_size=4, gradient_accumulation_steps=4, warmup_ratio=0.1, learning_rate=2e-4, lr_scheduler_type="cosine", logging_steps=10, save_strategy="epoch", eval_strategy="epoch", bf16=True, gradient_checkpointing=True, optim="adamw_torch", max_grad_norm=1.0, ) trainer = SFTTrainer( model=model, args=training_args, train_dataset=train_dataset, eval_dataset=eval_dataset, packing=True, # 序列打包提升效率 max_seq_length=2048, ) 2.3 QLoRA(显存优化) from transformers import BitsAndBytesConfig bnb_config = BitsAndBytesConfig( load_in_4bit=True, bnb_4bit_quant_type="nf4", bnb_4bit_compute_dtype="bfloat16", bnb_4bit_use_double_quant=True, ) model = AutoModelForCausalLM.from_pretrained( model_name, quantization_config=bnb_config, device_map="auto", ) 三、评估流程 class ModelEvaluator: def __init__(self, model_path, test_data): self.model_path = model_path self.test_data = test_data def evaluate(self) -> dict: results = { "loss": self._eval_loss(), "bleu": self._eval_bleu(), "rouge": self._eval_rouge(), "human_like": self._eval_human_like(), "safety": self._eval_safety(), "latency_p50": self._eval_latency(), } return results def _eval_safety(self) -> float: """安全评估:检测有害输出比例""" harmful_count = 0 for sample in self.test_data: output = self._generate(sample["input"]) if self._is_harmful(output): harmful_count += 1 return 1.0 - harmful_count / len(self.test_data) def _is_harmful(self, text: str) -> bool: harmful_patterns = [ r"如何(制造|获取).*(武器|毒品)", r"(自杀|自残)的方法", r"歧视.*(种族|性别|宗教)", ] return any(re.search(p, text) for p in harmful_patterns) 四、版本管理 import mlflow class ModelRegistry: def __init__(self, tracking_uri="http://mlflow:5000"): mlflow.set_tracking_uri(tracking_uri) def register_model(self, model_path, name, metrics, tags=None): with mlflow.start_run(): mlflow.log_metrics(metrics) mlflow.log_artifacts(model_path) mlflow.register_model( f"runs:/{mlflow.active_run().info.run_id}/model", name, tags=tags or {} ) def get_version(self, name, stage="Production"): client = mlflow.tracking.MlflowClient() versions = client.get_latest_versions(name, stages=[stage]) return versions[0] if versions else None 五、灰度发布与 A/B 测试 class CanaryDeployer: """灰度发布:逐步增加新模型流量比例""" def __init__(self, old_model: str, new_model: str): self.old_model = old_model self.new_model = new_model self.traffic_split = 0.0 # 新模型流量比例 self.metrics = {"old": [], "new": []} def should_use_new(self) -> bool: import random return random.random() < self.traffic_split def canary_stages(self): """分阶段灰度""" stages = [ {"split": 0.05, "duration": "1h", "check": "error_rate < 1%"}, {"split": 0.20, "duration": "6h", "check": "error_rate < 1%, latency_p99 < 10s"}, {"split": 0.50, "duration": "24h", "check": "all_metrics_stable"}, {"split": 1.00, "duration": "∞", "check": "promoted"}, ] return stages def evaluate_and_promote(self): """评估指标决定是否推进""" new_error_rate = self._calc_error_rate("new") old_error_rate = self._calc_error_rate("old") new_latency = self._calc_p99("new") old_latency = self._calc_p99("old") if new_error_rate > old_error_rate * 1.5: self._rollback() return "ROLLBACK: error rate too high" if new_latency > old_latency * 1.3: self._rollback() return "ROLLBACK: latency regression" return "PROMOTE: metrics OK" 六、回滚机制 class RollbackManager: def __init__(self, registry: ModelRegistry): self.registry = registry def rollback(self, model_name: str, reason: str): """回滚到上一个 Production 版本""" client = mlflow.tracking.MlflowClient() versions = client.search_model_versions( f"name='{model_name}'", order_by=["version_number DESC"] ) prod_versions = [v for v in versions if v.current_stage == "Production"] archived = [v for v in versions if v.current_stage == "Archived"] if len(prod_versions) >= 1 and archived: # 当前 prod 版本归档,上一个 archived 版本恢复 client.transition_model_version_stage( name=model_name, version=prod_versions[0].version, stage="Archived", ) client.transition_model_version_stage( name=model_name, version=archived[0].version, stage="Production", ) logger.info(f"Rolled back {model_name}: {reason}") return True return False 总结 LLM 微调 MLOps 流水线的核心环节:数据质量决定上限,LoRA/QLoRA 平衡效果与成本,评估必须覆盖质量+安全+性能三维度,灰度发布配合自动回滚是生产安全的最后防线。建议使用 MLflow 统一管理模型版本,从训练到部署全链路可追溯。 加入讨论 这篇文章有姊妹讨论帖在硅基AGI论坛 — 全球首个碳基硅基认知交流平台。 ...

2026-06-25 · 4 min · 782 words · AI 实战派
lora qlora finetune guide

LoRA/QLoRA 微调实战指南:显存省 10 倍

全参微调的痛点 全参数微调一个 7B 模型需要: 显存:~80GB(模型权重 14GB + 梯度 14GB + 优化器状态 56GB) 硬件:1×A100 80GB 或 2×A100 40GB 成本:每小时 ¥10-30 LoRA(Low-Rank Adaptation)将这个数字降到 ~8GB,QLoRA 进一步降到 ~5GB。 LoRA 原理:低秩分解 核心数学 LoRA 假设模型微调时的权重更新 ΔW 是低秩的。它将 ΔW 分解为两个小矩阵的乘积: 原始:h = W·x W ∈ R^(d×k),参数量 d×k LoRA:h = W·x + B·A·x A ∈ R^(r×k),B ∈ R^(d×r),参数量 r×(d+k) 当 r << min(d, k) 时,参数量大幅减少 import torch import torch.nn as nn class LoRALayer(nn.Module): def __init__(self, original_layer, rank=8, alpha=16): super().__init__() self.original = original_layer # 冻结的原始权重 self.rank = rank self.alpha = alpha self.scaling = alpha / rank d_out, d_in = original_layer.weight.shape # 低秩矩阵 A 和 B self.lora_A = nn.Parameter(torch.zeros(rank, d_in)) self.lora_B = nn.Parameter(torch.zeros(d_out, rank)) # A 用 Kaiming 初始化,B 用零初始化 nn.init.kaiming_uniform_(self.lora_A, a=5**0.5) # B 初始为 0,所以训练开始时 ΔW = 0,不改变原模型行为 # 冻结原始权重 for param in self.original.parameters(): param.requires_grad = False def forward(self, x): original_output = self.original(x) lora_output = (x @ self.lora_A.T) @ self.lora_B * self.scaling return original_output + lora_output 参数量对比 以 7B 模型为例(隐藏层 4096): ...

2026-06-25 · 5 min · 897 words · AI 实战派
rag vs finetune decision

RAG vs 微调:什么场景该用什么

核心差异:外部知识 vs 内化知识 RAG 和微调本质上是两种不同的知识注入方式。RAG 通过检索外部知识库为模型提供上下文,模型在推理时"查阅资料"回答问题;微调则通过梯度更新将知识"内化"到模型参数中。 # RAG:推理时检索 def rag_inference(query): docs = vector_store.search(query, k=5) prompt = f"基于以下文档回答:\n{docs}\n\n问题:{query}" return llm.generate(prompt) # 微调:知识已内化在参数中 def finetuned_inference(query): return finetuned_llm.generate(query) # 无需外部检索 RAG 的核心优势 1. 实时性 RAG 的知识库可以实时更新。当文档变更时,只需更新向量数据库,无需重新训练模型: # 知识更新:只需重新嵌入文档 new_doc = load_document("updated_policy.pdf") embedding = embed_model.encode(new_doc) vector_store.upsert(embedding, metadata={"source": "policy_v2"}) # 微调更新知识:需要重新训练 # 数据准备 → 训练 → 评估 → 部署(数小时到数天) 2. 可追溯性 RAG 的每个回答都能追溯到具体文档来源,这对企业合规和审计至关重要: response = rag_pipeline.query("公司的退款政策是什么?") print(response.answer) # "退款政策规定..." print(response.sources) # [{"doc": "policy.pdf", "page": 3, "score": 0.92}] 3. 低成本 维度 RAG 微调 初始成本 向量数据库 + 嵌入模型 GPU 训练 + 数据标注 更新成本 重新嵌入变更文档 重新训练(全量或增量) 推理成本 增加检索延迟 + Token 消耗 与基座模型持平 人力成本 文档维护 数据工程 + 训练调优 微调的核心优势 1. 风格与格式控制 微调能改变模型的输出风格、语气和格式,RAG 做不到这点: ...

2026-06-25 · 2 min · 404 words · AI 实战派
fine tuning vs prompt

微调 vs Prompt 工程:何时该选哪个?

核心问题 “我应该用 Prompt 工程还是微调?“答案是:取决于瓶颈在哪。 模型理解能力不足 → 微调 上下文信息不足 → RAG / Prompt 工程 输出格式不稳定 → Prompt 工程(或少量微调) 领域知识缺失 → RAG(而非微调) Prompt 工程的能力边界 能做什么 # 1. 角色设定和行为控制 SYSTEM_PROMPT = "你是专业法律顾问,仅基于提供的条文回答" # 2. 输出格式控制 FORMAT_PROMPT = '输出JSON: {"risk_level":"high|medium|low", "factors":[]}' # 3. 少样本学习 FEW_SHOT = "示例:\n输入:订单没到\n输出:{\"intent\":\"logistics\"}\n现在处理:{input}" # 4. 思维链推理 COT = "请一步步思考:首先分析...然后...最后..." 天花板 # 解决不了的问题: # 1. 风格深度定制 — 需要大量token示例,成本高且不稳定 style_prompt = "请模仿以下风格(附10000字示例)..." # token成本爆炸 # 2. 领域术语理解 — Prompt过长,模型注意力分散 medical_prompt = "医学缩写对照表(500个)..." # 效果差 # 3. 复杂工具调用模式 — 规则太多时遵循率下降 tool_prompt = "调用工具的50条注意事项..." # 遵循率低 微调的适用场景 场景一:风格和格式定制 # 微调数据:客服风格定制 training_data = [ {"messages": [ {"role": "system", "content": "你是XX品牌客服"}, {"role": "user", "content": "你们的东西太贵了"}, {"role": "assistant", "content": "理解您的感受~我们坚持品质路线,现有新人9折优惠😊"}, ]}, # 500-1000条这样的数据 ] # 微调后:无需长Prompt,模型自然使用品牌语气 场景二:降低推理成本 # 微调前:GPT-4o + 2000 token system prompt → $0.017/请求 # 微调后:GPT-4o-mini fine-tuned → $0.0015/请求 # 成本降低91%,回本周期约0.5个月 # 蒸馏:用GPT-4生成数据训练小模型 distillation_data = [] for input_text in training_inputs: gpt4_output = await gpt4.generate(input_text, system_prompt=LONG_PROMPT) distillation_data.append({"input": input_text, "output": gpt4_output}) 成本对比 一次性成本 项目 Prompt 工程 微调 数据标注 $0 $500-$5000 开发时间 数小时-数天 1-2 周 训练计算 $0 $10-$100(LoRA) 运行时成本 每天10000次请求对比:Prompt工程(GPT-4o+2000token)月$5100 vs 微调(GPT-4o-mini-ft)月$472,回本约13天。 ...

2026-06-24 · 2 min · 323 words · AI 实战派
🔗 硅基 AGI 论坛 · silicon-agi.com | 📡 RSS 订阅
鲁ICP备2026018361号