乐趣区

关于llm:LLM-模型融合实践指南低成本构建高性能语言模型

编者按:随着大语言模型技术的疾速倒退,模型交融成为一种低成本但高性能的模型构建新途径。本文作者 Maxime Labonne 利用 mergekit 库摸索了四种模型交融办法:SLERP、TIES、DARE 和 passthrough。通过配置示例和案例剖析,作者具体阐释了这些算法的原理及实际操作。

作者的外围观点是:相比训练全新模型,交融现有模型能够以更低计算成本获取相似或更优异的成果。

文章通过模型交融生成了性能优异的 Marcoro14-7B-slerp。在 Open LLM Leaderboard 和 NousResearch 两个基准测试上,它都是同参数量模型中的佼佼者。案例验证了作者主张的模型交融存在的高性价比。当然模型交融也存在肯定问题,如训练数据净化和可能在各种评测排行榜的分数偏高。本文提供了模型交融技术与工程实际的详尽指南,对 AI 实践者具备重要参考价值。

作者 | Maxime Labonne

编译 | 岳扬

Image by author

模型交融(Model merging)是一种将两个或更多个大语言模型(LLM)合并为一个模型的技术。这是一种绝对较新的实验性办法,能够以较低成本(无需 GPU)创立新模型。 令人诧异的是,这种技术的成果还比拟出奇,应用模型交融技术在 Open LLM Leaderboard[1]上产生了许多最先进的模型。

在本教程中,咱们将应用 mergekit [2]库来实现这一技术。更具体地说,咱们将回顾四种模型交融办法,并提供相干的配置示例。而后,咱们将应用 mergekit 创立一个模型:Marcoro14–7B-slerp[3],该模型已成为 Open LLM Leaderboard(02/01/24)上体现最佳的模型。

相干代码已上传至 GitHub[4] 和 Notebook[5]。集体倡议应用 LazyMergekit[6] 我的项目,来轻松运行 mergekit。

特别感谢 mergekit 库的作者 Charles Goddard[7] 审阅本文。

Image by author

01 🤝 交融算法

在本节,咱们将重点介绍 mergekit 库目前实现的四种模型交融办法。请留神,还有其余办法,比方 linear [8]和 Task Arithmetic [9]。如果你对模型交融的相干论文感兴趣,我举荐浏览 Hugging Face 上的这本优良论文集[10]。

1.1 SLERP

Spherical Linear Interpolation(SLERP)是一种用于在两个向量之间进行安稳和连贯地插值的办法。这种办法可能放弃恒定的变动速率,并保留向量所在球面空间的几何个性。

与应用传统的线性插值办法相比,SLERP 更受青眼的起因有几个。例如,在高维空间中,线性插值(linear interpolation)可能导致插值向量的大小(幅度)减小(即权重的规模减小)。此外,权重方向的变动往往比大小(幅度)的变动代表的信息更有意义(如特色学习(Feature Learning)和表征(Representation))。

SLERP 是通过以下步骤实现的:

  1. 对输出的向量进行归一化解决,使它们的长度(magnitude)变为单位长度(长度为 1)。这一步骤的目标是确保这些向量示意的是方向,而不是大小。
  2. 应用点积计算这些向量之间的角度。
  3. 如果这些向量简直平行,则默认应用线性插值以提高效率。如果输出的两个向量夹角较大,SLERP 将依据插值因子 t(插值因子 t 是一个介于 0 到 1 之间的值,用于指定插值的水平。t=0 示意齐全应用第一个向量,t=1 示意齐全应用第二个向量,而在 0 到 1 之间的值示意两个向量的混合水平。)和向量之间的夹角计算比例因子(Scale factor)。
  4. 这些因子用于给原始向量加权,而后求和来取得插值向量。

SLERP 目前是最风行的模型交融办法,但它仅限于一次合并两个模型。不过,依然能够通过分层交融多个模型,就像在 Mistral-7B-Merge-14-v0.1[11] 中所示。

配置示例:

slices:
  - sources:
      - model: OpenPipe/mistral-ft-optimized-1218
        layer_range: [0, 32]
      - model: mlabonne/NeuralHermes-2.5-Mistral-7B
        layer_range: [0, 32]
merge_method: slerp
base_model: OpenPipe/mistral-ft-optimized-1218
parameters:
  t:
    - filter: self_attn
      value: [0, 0.5, 0.3, 0.7, 1]
    - filter: mlp
      value: [1, 0.5, 0.7, 0.3, 0]
    - value: 0.5
dtype: bfloat16

这是一种经典的 SLERP 配置,SLERP 被利用于模型的每一层,以实现整体的模型交融。请留神,咱们为插值因子 t 输出了一系列梯度值(gradient of values)。自注意力层和 MLP 层的参数将应用 OpenPipe/mistral-ft-optimized-1218[12] 和 mlabonne/NeuralHermes-2.5-Mistral-7B[13] 的不同组合。

能够在 Hugging Face Hub 上找到最终训练实现的模型,位于 mlabonne/NeuralPipe-7B-slerp[14]。

1.2 TIES

TIES-Merging 由 Yadav 等人在这篇论文 [15] 中引入,TIES-Merging 旨在将多个特定任务模型高效地合并为一个多任务模型。它解决了模型交融中的两大难题:

  • 模型参数的冗余:它可能辨认并打消特定任务模型中的冗余参数。具体做法是在模型微调(fine-tuning)的过程中,关注模型参数产生的变动,对微调过程中产生的变动进行排序,并抉择那些对模型性能影响最显著的前 k% 的变动,并疏忽那些在微调中变动较小或对性能影响较小的局部。
  • 模型参数的符号之间存在一致:当不同模型对同一参数提出相同的调整倡议时,就会产生抵触。TIES-Merging 通过创立一个对立的符号向量来解决这些抵触,该向量示意所有模型中变动方向的最显著方向。

TIES-Merging 分为以下三个步骤:

  • Trim(修剪):只保留一部分最重要的参数(密度参数(density parameter)),并将其余参数重置为零,从而缩小特定任务模型中的冗余参数。
  • Elect Sign(确定符号):通过确定模型中哪些方向上的变动(正向或负向)是最为显著或最主导的,创立一个对立的符号向量,以解决不同模型之间的符号抵触。
  • Disjoint Merge:在合并过程中,仅思考那些与先前创立的对立符号向量统一的参数值,并计算这些值的平均值。在计算平均值时,不思考原始参数值为零的状况。

与 SLERP 不同,TIES 能够一次性合并多个模型。

配置示例:

models:
  - model: mistralai/Mistral-7B-v0.1
    # no parameters necessary for base model
  - model: OpenPipe/mistral-ft-optimized-1218
    parameters:
      density: 0.5
      weight: 0.5
  - model: mlabonne/NeuralHermes-2.5-Mistral-7B
    parameters:
      density: 0.5
      weight: 0.3
merge_method: ties
base_model: mistralai/Mistral-7B-v0.1
parameters:
  normalize: true
dtype: float16

在此配置下,咱们应用 Mistral-7B 作为根底模型来计算 delta 权重。咱们交融了两个模型:mistral-ft-optimized-1218(50%)[12]和 NeuralHermes-2.5-Mistral-7B(30%)[13],并进行了归一化解决。这里的“density”参数意味着咱们只保留了每个模型 50% 的参数(另一半来自根底模型)。

请留神,在配置中权重之和不等于 1,但 normalize: true 参数会主动在外部将其归一化。 此配置的灵感来自 OpenHermes-2.5-neural-chat-7b-v3-1-7B[16] 这个模型的作者提供的参数。

你能够在 Hugging Face Hub 上的 mlabonne/NeuralPipe-7B-ties[17] 找到最终训练实现的模型。

1.3 DARE

DARE[18] 由 Yu 等人(2023 年)提出,应用了与 TIES 相似的办法,但有两个次要区别:

  • Pruning:DARE 随机将微调后的权重重置为原始值(根底模型的权重)。
  • Rescaling:DARE 从新调整权重,以放弃模型输入的期望值大抵不变。它将两个(或更多)模型的重调整后的权重与根底模型的权重相加,并应用了一个比例因子。

Mergekit 对这种办法的实现有两种:有 TIES 的确定符号步骤(dare_ties)或没有 TIES 的确定符号步骤(dare_linear)。

配置示例:

models:
 - model: mistralai/Mistral-7B-v0.1
 # No parameters necessary for base model
 - model: samir-fama/SamirGPT-v1
 parameters:
 density: 0.53
 weight: 0.4
 - model: abacusai/Slerp-CM-mist-dpo
 parameters:
 density: 0.53
 weight: 0.3
 - model: EmbeddedLLM/Mistral-7B-Merge-14-v0.2
 parameters:
 density: 0.53
 weight: 0.3
merge_method: dare_ties
base_model: mistralai/Mistral-7B-v0.1
parameters:
 int8_mask: true
dtype: bfloat16

在这个配置示例中,咱们应用 dare_ties 合并了基于 Mistral-7B 的三个不同模型。这次,我抉择的权重总和为 1(总和应在 0.9 和 1.1 之间)。density 参数略高于论文中倡议的值(<0.5),但看起来它能继续提供更好的后果(参见此探讨[19])。

你能够在 Hugging Face Hub 的 mlabonne/Daredevil-7B[20] 上找到它。它也是本文中最好的合并模型,甚至优于 Marcoro14-7B-slerp。

1.4 Passthrough

Passthrough 办法与前几种办法有很大不同。通过连贯来自不同 LLMs 的模型层,这种办法能够生成具备奇怪参数数量的模型(例如,应用两个 7B 参数模型能够生成 9B 模型)。 这些模型通常被称为 “Frankenmerges “ 或 “Frankenstein 模型 ”。

这种技术极具实验性,但可能胜利地创立一些令人印象粗浅的模型,比方应用两个 Llama 2 70B 模型交融而成的 goliath-120b。最近公布的 SOLAR-10.7B-v1.0 也应用了同样的思维,在他们的论文中这种技术称为 ”depth-up scaling”。

配置示例:

slices:
  - sources:
    - model: OpenPipe/mistral-ft-optimized-1218
      layer_range: [0, 32]
  - sources:
    - model: mlabonne/NeuralHermes-2.5-Mistral-7B
      layer_range: [24, 32]
merge_method: passthrough
dtype: bfloat16

由此产生的 frankenmerge 模型将蕴含第一个模型的全副 32 层和第二个模型的 8 个附加层。这将创立一个总共有 40 层和 8.99B 参数的 frankenmerge。此配置的灵感来自于 GML-Mistral-merged-v1[21]。

您能够在 Hugging Face Hub 上的 mlabonne/NeuralPipe-9B-merged [22]找到最终模型。

02 💻 交融咱们本人的模型

在本节中,咱们将应用 mergekit 库加载一个模型交融配置,运行它并将生成的后果模型上传到 Hugging Face Hub。

首先,咱们间接通过源代码装置 mergekit,步骤如下:

!git clone https://github.com/cg123/mergekit.git
!cd mergekit && pip install -q -e .

在上面的代码块中,咱们将以 YAML 格局加载模型交融配置。还在此指定了实现模型交融后模型的名称,以备未来应用。您能够在此复制 / 粘贴上一节中的任何配置。

这次,咱们将应用两个不同的模型:Marcoroni-7B-v3[23] 和 Mistral-7B-Merge-14-v0.1[24] 并用 SLERP 办法进行模型交融。而后将配置保留为 yaml 文件,以便用作模型交融命令的输出。

import yaml

MODEL_NAME = "Marcoro14-7B-slerp"
yaml_config = """
slices:
  - sources:
      - model: AIDC-ai-business/Marcoroni-7B-v3
        layer_range: [0, 32]
      - model: EmbeddedLLM/Mistral-7B-Merge-14-v0.1
        layer_range: [0, 32]
merge_method: slerp
base_model: AIDC-ai-business/Marcoroni-7B-v3
parameters:
  t:
    - filter: self_attn
      value: [0, 0.5, 0.3, 0.7, 1]
    - filter: mlp
      value: [1, 0.5, 0.7, 0.3, 0]
    - value: 0.5
dtype: bfloat16

"""

# Save config as yaml file
with open('config.yaml', 'w', encoding="utf-8") as f:
    f.write(yaml_config)

咱们会应用以下参数运行模型交融命令:

  • –copy-tokenizer 用于从根底模型复制分词器
  • –allow-crimes 和 –out-shard-size 可用于将模型划分为较小的分片(shards),可在内存较小的 CPU 上进行计算
  • –lazy-unpickle 用于启用实验性的 lazy unpickler(译者注:”lazy unpickler” 指的是一种实验性的、可能以一种惰性或提早加载的形式执行反序列化操作的机制。),以升高内存使用率

此外,某些模型可能还须要 –trust_remote_code 参数(Mistral-7B 不须要)。

该命令将下载模型交融配置中列出的所有模型的权重,并运行所选的模型交融办法(应该须要约 10 分钟)。

# Merge models
!mergekit-yaml config.yaml merge --copy-tokenizer --allow-crimes --out-shard-size 1B --lazy-unpickl

当初,模型曾经交融并保留在 merge 目录中。在上传之前,咱们能够创立一个蕴含复现该模型交融操作所需信息的 README 文件。以下代码块定义了一个 Jinja 模板,并主动将模型交融配置中的数据填入其中。

!pip install -qU huggingface_hub

from huggingface_hub import ModelCard, ModelCardData
from jinja2 import Template

username = "mlabonne"

template_text = """
---
license: apache-2.0
tags:
- merge
- mergekit
- lazymergekit
{%- for model in models %}
- {{model}}
{%- endfor %}
---

# {{model_name}}

{{model_name}} is a merge of the following models using [mergekit](https://github.com/cg123/mergekit):

{%- for model in models %}
* [{{model}}](https://huggingface.co/{{ model}})
{%- endfor %}

## 🧩 Configuration

```yaml
{{- yaml_config -}}
```
"""

# Create a Jinja template object
jinja_template = Template(template_text.strip())

# Get list of models from config
data = yaml.safe_load(yaml_config)
if "models" in data:
    models = [data["models"][i]["model"] for i in range(len(data["models"])) if "parameters" in data["models"][i]]
elif "parameters" in data:
    models = [data["slices"][0]["sources"][i]["model"] for i in range(len(data["slices"][0]["sources"]))]
elif "slices" in data:
    models = [data["slices"][i]["sources"][0]["model"] for i in range(len(data["slices"]))]
else:
 raise Exception("No models or slices found in yaml config")

# Fill the template
content = jinja_template.render(
    model_name=MODEL_NAME,
    models=models,
    yaml_config=yaml_config,
    username=username,
)

# Save the model card
card = ModelCard(content)
card.save('merge/README.md')

当初咱们有了 model card,就能够将整个文件夹推送到 HuggingFace Hub。

from google.colab import userdata
from huggingface_hub import HfApi

username = "mlabonne"

# Defined in the secrets tab in Google Colab
api = HfApi(token=userdata.get("HF_TOKEN"))

api.create_repo(repo_id=f"{username}/{MODEL_NAME}",
    repo_type="model"
)
api.upload_folder(repo_id=f"{username}/{MODEL_NAME}",
    folder_path="merge",
)

该模型当初能够在 Hugging Face Hub 上找到,位于 mlabonne/Marcoro14–7B-slerp[25]。在另一个 notebook 中,咱们能够应用以下代码尝试在收费的 T4 GPU 上运行该模型:

!pip install -qU transformers accelerate

from transformers import AutoTokenizer
import transformers
import torch

model = "mlabonne/Marcoro14-7B-slerp"
messages = [{"role": "user", "content": "What is a large language model?"}]

tokenizer = AutoTokenizer.from_pretrained(model)
prompt = tokenizer.apply_chat_template(
    messages,
    tokenize=False,
    add_generation_prompt=True
)
pipeline = transformers.pipeline(
    "text-generation",
    model=model,
    torch_dtype=torch.float16,
    device_map="auto",
)

outputs = pipeline(prompt, max_new_tokens=256, do_sample=True, temperature=0.7, top_k=50, top_p=0.95)

咱们向 LLM 提出了 ”What is a Large Language Model?” 这个问题,并取得了这样的输入后果:

A large language model is a type of artificial intelligence (AI) system that has been trained on vast amounts of text data. It’s designed to understand and generate human-like language, making predictions on what words or phrases might come next in a sentence or document. These models use complex algorithms and neural network architectures to learn from the data and improve their performance over time. Some well-known large language models include GPT-3 from OpenAI and BERT from Google.

交融后的模型看起来不错,但咱们须要更全面的评估。对于这种通用型模型,有一些乏味的基准测试:

  1. Chatbot Arena[26],它通过人类的投票编制 LLMs 排行榜。人们对不同语言模型的体现进行投票,而后应用 Elo 算法对这些模型进行排名。
  2. MT-bench[26],它应用 GPT-4 作为评判员,对一组多轮问题上的模型答复进行评分。
  3. NousResearch benchmark suite[27],它汇总了四个基准测试:AGIEval、GPT4ALL、TruthfulQA 和 Bigbench。GPT4ALL 包含了 HellaSwag、OpenBookQA、Winogrande、ARC-Easy、ARC-Challenge、BoolQ 和 PIQA。
  4. Open LLM Leaderboard[28],它汇总了六个基准测试:ARC、HellaSwag、MMLU、Winogrande、GSM8K 和 TruthfulQA。

可怜的是,咱们无奈将该模型提交到 Chatbot Arena。不过,能够抉择应用 Open LLM Leaderboard 和 NousResearch 基准测试对其进行评估。

我曾经将该模型提交到了 Open LLM Leaderboard[28],它在该排行榜上被评为最佳的 7B 参数模型。以下是具体情况截图:

Image by author

Open LLM Leaderboard 的问题在于这些基准测试是公开的。这意味着人们能够在测试数据上训练语言模型,以取得更好的后果。交融这些最佳的模型,也会净化模型。能够必定的是,Marcoro14-7B-slerp 受到了净化,而且这次交融中应用的一些模型应该是在这些评估测试集上训练过的。 如果你想创立最好的模型而非仅仅在排行榜上体现较好的模型,我倡议只应用非交融的模型来创立本人的交融模型。

这就是为什么咱们不能只依赖于 Open LLM Leaderboard。在 NousResearch benchmark suite 中,我应用了 🧐 LLM AutoEval [29]来主动计算评估分数。以下是与体现优良的 OpenHermes-2.5-Mistral-7B [30]进行比拟的后果:

Image by author

与该模型相比,咱们在每项基准测试中都获得了显著提高。请留神,NousResearch benchmark suite 与 Open LLM Leaderboard 共享了一些评估工作:ARC-Challenge,TruthfulQA,HellaSwag 和 Winogrande。据我所知,Bigbench 是惟一齐全不同的基准测试(如果状况不是这样,请随时去原文链接与作者分割)。然而,在此次模型交融中应用的模型之一仍可能是在 Bigbench 的评测数据集上训练过的。

03 总结

在这篇文章中,咱们介绍了应用四种不同的办法去交融 LLMs。具体阐明了 SLERP、TIES、DARE 和 passthrough 的工作原理,并提供了相干的配置示例。最初,咱们通过 mergekit 库应用 SLERP 办法训练出了 Marcoro14–7B-slerp,并将其上传到 Hugging Face Hub。咱们在两个基准套件上都取得了杰出的性能:Open LLM Leaderboard(该模型是该榜单性能最佳的 7B 模型)和 NousResearch。

Thanks for reading!

END

参考资料

[1]https://huggingface.co/spaces/HuggingFaceH4/open_llm_leaderboard

[2]https://github.com/cg123/mergekit

[3]https://huggingface.co/mlabonne/Marcoro14-7B-slerp

[4]https://github.com/mlabonne/llm-course/blob/main/Mergekit.ipynb

[5]https://colab.research.google.com/drive/1_JS7JKJAQozD48-LhYde…

[6]https://colab.research.google.com/drive/1obulZ1ROXHjYLn6PPZJw…

[7]https://www.linkedin.com/in/charles-goddard-7b6797b/

[8]https://github.com/cg123/mergekit/tree/1011ef3a84e4c5545473602baf7ef32d535044a9#linear

[9]https://arxiv.org/abs/2212.04089

[10]https://huggingface.co/collections/osanseviero/model-merging-…

[11]https://huggingface.co/EmbeddedLLM/Mistral-7B-Merge-14-v0.1

[12]https://huggingface.co/OpenPipe/mistral-ft-optimized-1218

[13]https://huggingface.co/mlabonne/NeuralHermes-2.5-Mistral-7B

[14]https://huggingface.co/mlabonne/NeuralPipe-7B-slerp

[15]https://arxiv.org/abs/2306.01708

[16]https://huggingface.co/Weyaxi/OpenHermes-2.5-neural-chat-7b-v…

[17]https://huggingface.co/mlabonne/NeuralPipe-7B-ties

[18]https://arxiv.org/abs/2311.03099

[19]https://github.com/cg123/mergekit/issues/26

[20]https://huggingface.co/mlabonne/Daredevil-7B

[21]https://huggingface.co/zyh3826/GML-Mistral-merged-v1

[22]https://huggingface.co/mlabonne/NeuralPipe-9B-merged

[23]https://huggingface.co/AIDC-ai-business/Marcoroni-7B-v3

[24]https://huggingface.co/EmbeddedLLM/Mistral-7B-Merge-14-v0.1

[25]https://huggingface.co/mlabonne/Marcoro14-7B-slerp

[26]https://chat.lmsys.org/

[27]https://github.com/teknium1/LLM-Benchmark-Logs

[28]https://huggingface.co/spaces/HuggingFaceH4/open_llm_leaderboard

[29]https://github.com/mlabonne/llm-autoeval

[30]https://huggingface.co/teknium/OpenHermes-2.5-Mistral-7B

原文链接:

https://towardsdatascience.com/merge-large-language-models-wi…

退出移动版