news 2026/9/23 17:37:27

AI Research Skills 模型合并实战:基于生成一致性的无监督系数自动调优(AdaMMS 方法)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
AI Research Skills 模型合并实战:基于生成一致性的无监督系数自动调优(AdaMMS 方法)
  • AI 技能
  • 人工智能
  • 大模型
  • 深度学习

【免费下载链接】AI-Research-SKILLs

Comprehensive open-source library of AI research and engineering skills for any AI model. Package the skills and your claude code/codex/gemini agent will be an AI research agent with full horsepower. Maintained by Orchestra Research.

项目地址:https://gitcode.com/gh_mirrors/ai/AI-Research-SKILLs
点击查看免费下载

模型合并(Model Merging)能将多个微调模型的能力在无需重训的情况下融合,但其效果高度依赖合并系数(如 SLERP 的t、Task Arithmetic 的weight/lambda)。本指南以仓库19-emerging-techniques/model-merging中的核心参考文档为基础,系统讲解来自 CVPR 2025 论文 AdaMMS 的**生成一致性(Generation Consistency)**无监督系数选择方法:仅需 50~200 条无标签文本,即可自动从候选系数中挑出最优值,彻底摆脱人工试错与标注数据依赖。读完本文你将掌握从候选系数定义、批量合并、无标签推理到一致性评分与最优系数选取的完整可运行流水线,并了解 ROUGE-L、BERTScore 等相似度度量的选型与多系数搜索的扩展方法。

一、问题背景:合并系数为什么难选

主流的合并方法(Task Arithmetic、TIES、DARE、SLERP)都会暴露一个或多个标量系数,例如weightdensitylambdat,它们对最终合并质量影响极大。仓库的 SKILL.md 明确给出了各种方法的配置结构,其中 SLERP 用t控制插值位置(0 取第一个模型、1 取第二个模型),Task Arithmetic 用weight缩放任务向量,TIES/DARE 还有density参数控制参数保留比例。

传统的系数选择方式各有明显缺陷:

方法缺点
人工经验/直觉不可靠,依赖领域专家经验
带评测集的网格搜索需要标注数据;代价高(N 次合并 × N 次评测)
贝叶斯优化每一轮 trial 都需要 ground-truth 信号
生成一致性(本文方法)完全无监督、无需标签,仅需少量无标注数据子集

这也是模型合并进入生产环境前最常卡住的环节——evaluation.md 中强调合并后必须做完整评测,但如果系数本身就选错了,后面的评测与回归测试都建立在错误基础上。

二、核心思想:生成一致性(Generation Consistency)

关键洞察:当某个系数值接近最优时,取邻近系数值合并出的模型会产出相似的输出——因为损失曲面在好的解附近是平滑的。相反,在过差解附近(系数过高或过低),模型处于不稳定点,输出会急剧发散。

对于候选系数α,定义一致性分数为:

ConsistencyScore(α) = avg_similarity(outputs(α), outputs(α - δ)) + avg_similarity(outputs(α), outputs(α + δ))

其中δ是小的步长(如 0.1),相似度在一小批无标签数据上计算。选择一致性分数最高的系数即为最优系数。

这一思想与仓库中 methods.md 介绍的 SLERP 公式(merged = (sin((1-t)*θ)/sin(θ)) * model1 + (sin(t*θ)/sin(θ)) * model2)直接相关:系数空间上的平滑性正是插值方法能在邻近点保持输出稳定性的前提。

三、五步算法详解

Step 1:定义候选系数

使用 NumPy 生成搜索网格。示例中搜索 SLERP 的t或 Task Arithmetic 的lambda

import numpy as np # Example: searching over SLERP t or Task Arithmetic lambda alpha_min, alpha_max = 0.2, 0.8 step = 0.1 candidates = np.arange(alpha_min, alpha_max + step, step).tolist() # candidates = [0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8]

Step 2:为每个候选系数合并模型

利用 mergekit 的mergekit-yaml命令行工具为每个系数生成一个合并模型。文档示例同时给出了 SLERP 与 Task Arithmetic 两种 YAML 配置的构造方式:

import subprocess import json import os def merge_with_coefficient(alpha, model_a, model_b, output_dir, method="slerp"): """Merge models using mergekit with a specific coefficient.""" if method == "slerp": config = { "merge_method": "slerp", "slices": [{"sources": [ {"model": model_a, "layer_range": [0, 32]}, {"model": model_b, "layer_range": [0, 32]} ]}], "parameters": {"t": alpha}, "dtype": "bfloat16" } elif method == "task_arithmetic": config = { "merge_method": "task_arithmetic", "base_model": model_a, # treat model_a as base "models": [{"model": model_b, "parameters": {"weight": alpha}}], "dtype": "bfloat16" } config_path = f"/tmp/merge_config_{alpha:.2f}.yaml" import yaml with open(config_path, "w") as f: yaml.dump(config, f) out_path = os.path.join(output_dir, f"merged_alpha_{alpha:.2f}") subprocess.run( ["mergekit-yaml", config_path, out_path, "--cuda"], check=True ) return out_path # Merge all candidates output_root = "/tmp/merge_candidates" os.makedirs(output_root, exist_ok=True) merged_paths = {} for alpha in candidates: path = merge_with_coefficient(alpha, "model_a_path", "model_b_path", output_root) merged_paths[alpha] = path

这里生成的 SLERP 配置与 SKILL.md 中 Quick Start 的 SLERP 示例结构完全一致(slices+sources+parameters.t),Task Arithmetic 配置也与 SKILL.md 中base_model+models[].weight的写法对应。需要说明的是,SLERP 要求两个模型同架构(例如同为 Mistral 7B),混合 Llama 与 Mistral 会导致失败,这是 SKILL.md 中"Common Pitfalls"明确强调的前提。

Step 3:在无标签子集上推理

只需少量(约 50~200 条)无标签文本,数据不需要任何标签。使用 HuggingFacetransformers逐候选模型批量生成回复:

from transformers import AutoModelForCausalLM, AutoTokenizer import torch def generate_responses(model_path, prompts, max_new_tokens=128, batch_size=8): """Generate responses for all prompts using the merged model.""" tokenizer = AutoTokenizer.from_pretrained(model_path) model = AutoModelForCausalLM.from_pretrained( model_path, torch_dtype=torch.bfloat16, device_map="auto" ) model.eval() all_responses = [] for i in range(0, len(prompts), batch_size): batch = prompts[i:i + batch_size] inputs = tokenizer(batch, return_tensors="pt", padding=True, truncation=True).to(model.device) with torch.no_grad(): outputs = model.generate( **inputs, max_new_tokens=max_new_tokens, do_sample=False, # greedy for determinism pad_token_id=tokenizer.eos_token_id ) decoded = tokenizer.batch_decode(outputs[:, inputs["input_ids"].shape[1]:], skip_special_tokens=True) all_responses.extend(decoded) del model # free GPU memory before loading next torch.cuda.empty_cache() return all_responses # Small unlabeled evaluation prompts (no labels needed) eval_prompts = [ "Explain the concept of gradient descent.", "Write a Python function to find the maximum of a list.", # ... 50-200 prompts total ] # Generate responses for each candidate all_responses = {} for alpha in candidates: print(f"Generating responses for alpha={alpha:.2f} ...") all_responses[alpha] = generate_responses(merged_paths[alpha], eval_prompts)

注意两个实现细节:一是do_sample=False使用贪心解码保证输出确定性,使不同系数间的输出差异真实反映模型行为而非采样噪声;二是每次推理后显式del modeltorch.cuda.empty_cache()释放显存,为加载下一个候选模型腾出空间。

Step 4:计算生成一致性分数

将每个α的输出与其左右邻居α - δα + δ的输出两两比较,取平均相似度:

from rouge_score import rouge_scorer def text_similarity(text_a, text_b, metric="rougeL"): """Compute similarity between two text strings.""" if metric == "rougeL": scorer = rouge_scorer.RougeScorer(["rougeL"], use_stemmer=False) score = scorer.score(text_a, text_b) return score["rougeL"].fmeasure elif metric == "token_overlap": tokens_a = set(text_a.lower().split()) tokens_b = set(text_b.lower().split()) if not tokens_a or not tokens_b: return 0.0 return len(tokens_a & tokens_b) / len(tokens_a | tokens_b) def generation_consistency(alpha, all_responses, candidates, delta=0.1, metric="rougeL"): """ Compute generation consistency for a given alpha. Compares model at `alpha` against its nearest neighbors: alpha - delta and alpha + delta. """ responses_curr = all_responses[alpha] neighbor_alphas = [] left_alpha = round(alpha - delta, 2) right_alpha = round(alpha + delta, 2) if left_alpha in all_responses: neighbor_alphas.append(left_alpha) if right_alpha in all_responses: neighbor_alphas.append(right_alpha) if not neighbor_alphas: return 0.0 # boundary case with no neighbors total_sim = 0.0 count = 0 for n_alpha in neighbor_alphas: responses_neighbor = all_responses[n_alpha] pair_sims = [ text_similarity(r_curr, r_neighbor, metric=metric) for r_curr, r_neighbor in zip(responses_curr, responses_neighbor) ] total_sim += sum(pair_sims) / len(pair_sims) count += 1 return total_sim / count # Compute consistency scores for all interior candidates consistency_scores = {} for alpha in candidates: score = generation_consistency(alpha, all_responses, candidates, delta=0.1) consistency_scores[alpha] = score print(f"alpha={alpha:.2f} consistency={score:.4f}")

这里的round(alpha - delta, 2)是为了规避浮点误差(如 0.3 - 0.1 得到 0.19999999),确保能命中all_responses中精确到两位小数的键。边界候选(如 0.2 与 0.8)只有一个邻居,其分数是单侧的,会被系统性低估,这一点在后面的 Practical Tips 中会专门讨论。

Step 5:选择最佳系数

取一致性分数最大的候选作为最优系数,并可视化为一致性曲线:

best_alpha = max(consistency_scores, key=consistency_scores.get) print(f"\nBest coefficient: alpha={best_alpha:.2f} (consistency={consistency_scores[best_alpha]:.4f})") # Optionally visualize import matplotlib.pyplot as plt alphas_sorted = sorted(consistency_scores.keys()) scores_sorted = [consistency_scores[a] for a in alphas_sorted] plt.figure(figsize=(8, 4)) plt.plot(alphas_sorted, scores_sorted, marker="o") plt.axvline(best_alpha, color="red", linestyle="--", label=f"Best α={best_alpha:.2f}") plt.xlabel("Merge Coefficient (α)") plt.ylabel("Generation Consistency Score") plt.title("Unsupervised Coefficient Selection via Generation Consistency") plt.legend() plt.tight_layout() plt.savefig("consistency_curve.png", dpi=150) plt.show()

选出的最优模型路径即为merged_paths[best_alpha],可直接用于后续的正式评测(参考 evaluation.md 中的 Open LLM Leaderboard、MT-Bench、MMLU、HumanEval 等基准测试流程)。

四、相似度度量选型

生成一致性的核心是文本相似度计算,文档给出三种方案,按速度与质量权衡:

度量速度质量说明
Token overlap(Jaccard)适合快速原型验证
ROUGE-L均衡之选;安装rouge-score
BERTScore语义敏感度最高;需要 GPU

BERTScore 的批量实现如下,适合需要更强语义判别的场景:

# BERTScore alternative (higher quality) from bert_score import score as bert_score def bertscore_similarity_batch(texts_a, texts_b, lang="en"): P, R, F1 = bert_score(texts_a, texts_b, lang=lang, verbose=False) return F1.mean().item() # Replace text_similarity call with: # score = bertscore_similarity_batch(responses_curr, responses_neighbor)

选型建议:快速迭代用 token overlap 或 ROUGE-L,最终确认最优系数时可用 BERTScore 复验。三种度量在文档的generation_consistency函数中通过metric参数切换(text_similarity已内置rougeLtoken_overlap两种,BERTScore 则通过替换调用实现)。

五、应用到不同合并方法

SLERP(t参数)

t取值范围为 [0.0, 1.0],应选取内部候选点以避免边界塌缩:

# Search t ∈ [0.0, 1.0]; interior candidates avoid boundary collapse candidates = [0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8]

Task Arithmetic / TIES(lambdaweight

lambda可以超过 1.0,搜索范围应更宽:

# Lambda can exceed 1.0; search a wider range candidates = [0.3, 0.5, 0.7, 1.0, 1.2, 1.5]

这与 methods.md 中 TIES 的lambda_param语义一致——lambda是合并任务向量的缩放因子,大于 1.0 意味着微调模型影响更强。同理,TIES/DARE 的density参数(默认 0.2,范围 0.1~0.8)也可以用同样的生成一致性框架扫描。

多系数(如两个模型不同权重)

当搜索二维系数空间(w1w2 = 1 - w1)时,由于w2w1决定,一维搜索即足够:

# 1D search: w1 ∈ [0.2, 0.8], w2 = 1 - w1 candidates = [0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8] # Map alpha -> (alpha, 1-alpha) for the merge config

对于更高维搜索(3 个以上模型),采用坐标轮换一致性优化:每次固定其他系数、只优化一个:

def coordinate_wise_search(base_weights, coord_idx, candidates, all_responses_fn, delta=0.1): """Optimize one coefficient at a time, holding others fixed.""" best_score = -1 best_alpha = base_weights[coord_idx] for alpha in candidates: weights = base_weights.copy() weights[coord_idx] = alpha # Normalize if weights should sum to 1 weights = [w / sum(weights) for w in weights] responses = all_responses_fn(weights) score = generation_consistency_from_responses(responses, delta) if score > best_score: best_score = score best_alpha = alpha return best_alpha, best_score

注意coordinate_wise_search是示意性代码——它调用的all_responses_fn(对给定权重向量执行合并与生成)和generation_consistency_from_responses需要你用上文 Step 2~4 的构建块自行组装。

六、端到端流水线

将五个步骤封装为一个可复用的函数:

import numpy as np from typing import List, Dict def unsupervised_coefficient_search( model_a: str, model_b: str, eval_prompts: List[str], method: str = "slerp", candidates: List[float] = None, delta: float = 0.1, similarity_metric: str = "rougeL", output_root: str = "/tmp/merge_candidates", max_new_tokens: int = 128, ) -> Dict: """ Unsupervised coefficient search using generation consistency. Args: model_a: Path or HuggingFace ID of first model (or base model). model_b: Path or HuggingFace ID of second model. eval_prompts: Small set of unlabeled prompts (50-200 recommended). method: Merge method ('slerp', 'task_arithmetic', 'ties'). candidates: List of coefficient values to search over. delta: Step size for neighbor comparison. similarity_metric: 'rougeL', 'token_overlap', or 'bertscore'. output_root: Directory to store temporary merged models. max_new_tokens: Max tokens to generate per prompt. Returns: dict with 'best_alpha', 'best_path', 'scores', 'all_responses'. """ if candidates is None: candidates = [round(x, 2) for x in np.arange(0.2, 0.9, 0.1).tolist()] os.makedirs(output_root, exist_ok=True) # Step 1-2: Merge all candidates merged_paths = {} for alpha in candidates: merged_paths[alpha] = merge_with_coefficient(alpha, model_a, model_b, output_root, method) # Step 3: Generate responses all_responses = {} for alpha in candidates: all_responses[alpha] = generate_responses(merged_paths[alpha], eval_prompts, max_new_tokens) # Step 4: Score consistency scores = {} for alpha in candidates: scores[alpha] = generation_consistency(alpha, all_responses, candidates, delta, similarity_metric) # Step 5: Select best best_alpha = max(scores, key=scores.get) return { "best_alpha": best_alpha, "best_path": merged_paths[best_alpha], "scores": scores, "all_responses": all_responses, } # Usage result = unsupervised_coefficient_search( model_a="mistralai/Mistral-7B-v0.1", model_b="teknium/OpenHermes-2.5-Mistral-7B", eval_prompts=eval_prompts, # ~100 unlabeled prompts method="slerp", candidates=[0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8], ) print(f"Best coefficient: {result['best_alpha']}") print(f"Best model path: {result['best_path']}")

这个端到端函数与 SKILL.md 中给出的无监督调优伪代码一一对应——该 Skill 文档将本方法定位为"手动调参之外的无标签替代方案",并在 "Best Practices" 一节直接指向references/coefficient-tuning.md获取完整算法。

七、实用技巧与陷阱

数据集选择:任意目标领域的无标签文本均可。通常 50 个样本就足够,超过 200 个样本收益递减。

步长 δ:搜索网格步长为 0.1 时用δ = 0.1;若采用更细网格(如步长 0.05),δ应同步设为 0.05。

边界候选:位于alpha_minalpha_max的候选只有一个邻居,其分数是单侧的、易被低估。建议从最终选择中排除,或扩大搜索范围。

计算成本:总开销为 N 次合并 + N 次推理。推理是瓶颈,建议使用贪心解码和短输出以提速。以 N=7 个候选、100 条提示词为例,单 GPU 上通常耗时 10~30 分钟。与 evaluation.md 中的完整基准评测相比,这种"小样本快速筛选"的成本几乎可以忽略,非常适合作为正式评测前的系数预选步骤。

一致性曲线平坦:若曲线没有明显峰值,说明两个模型过于相似或差异过大。此时应优先调整 TIES/DARE 的density/dropout 参数,再重新搜索。

八、总结与扩展阅读

生成一致性方法将系数选择从"人工试错 + 标注评测"转变为"无标签小样本 + 邻近一致性",在 SLERP、Task Arithmetic、TIES、DARE 等主流合并方法上均可直接套用,且天然支持多系数坐标轮换搜索。它特别适合 Agent 自动化的模型合并场景——正如本仓库的设计目标,让 AI Agent 自主完成从合并到调优的全流程。

本仓库中与该文档配套的深度资料:

  • 模型合并 Skill 主文档:含合并方法选型、YAML 配置结构、DARE-TIES 与 MoE 合并等进阶模式;
  • 合并方法深度剖析:TIES 的 TRIM/ELECT/MERGE 三步、DARE 的随机丢弃与重缩放数学推导;
  • 真实合并配置示例:Marcoro14-7B-slerp、goliath-120b、领域专家模型等案例;
  • 合并模型评测指南:基准套件、能力保持率与回归测试方法,可用于验证本文选出的最优系数。

方法出处:本方法源自 Du et al.,"AdaMMS: Model Merging for Heterogeneous Multimodal Large Language Models with Unsupervised Coefficient Optimization"(CVPR 2025),原文提出的核心思想即"以生成一致性作为无监督代理,在无标注数据、无需人工搜索的前提下自动选择合并系数"。所需依赖可通过pip install rouge-scorepip install bert-score安装,合并工具使用 mergekit 的mergekit-yaml命令行(详见 SKILL.md 的安装小节)。

  • AI 技能
  • 人工智能
  • 大模型
  • 深度学习

【免费下载链接】AI-Research-SKILLs

Comprehensive open-source library of AI research and engineering skills for any AI model. Package the skills and your claude code/codex/gemini agent will be an AI research agent with full horsepower. Maintained by Orchestra Research.

项目地址:https://gitcode.com/gh_mirrors/ai/AI-Research-SKILLs
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/23 17:34:24

PyTorch从零实现贝叶斯神经网络:量化模型不确定性

简介:本资源是一份面向机器学习进阶学习者与研究者的贝叶斯神经网络实践教程代码包,聚焦于不确定性建模与概率深度学习核心能力培养,适用于小样本学习、模型校准、医学图像置信预测等高可靠性场景。压缩包共12个文件,含6个Python源…

作者头像 李华
网站建设 2026/9/23 17:34:21

信息链全解析:从理论模型到数据管道落地实践

上周有个朋友问我:一个用户需求从提出到最终变成产品功能,中间的环节到底有多少机会“走样”?我说,你先去把信息链(Information Chain)这个概念吃透,答案自然就出来了。信息链(Infor…

作者头像 李华
网站建设 2026/9/23 17:27:49

模型压缩实战:蒸馏与剪枝源码解析及边缘部署优化

简介:这份资源是面向毕业设计与模型压缩入门者的Python代码仓库,聚焦基于知识蒸馏与剪枝的识别算法实现,适合具备一定深度学习基础、需要完成相关课题或复现压缩实验的学生与开发者。压缩包共185个文件,约4.03MB,以79个…

作者头像 李华