使用 MergeKit 创建专家混合模型

社区文章 发布于 2024年3月28日

image/jpeg

得益于 Mixtral 的发布,**专家混合模型 (MoE)** 架构在最近几个月受到了广泛关注。这种架构提供了一种有趣的权衡:以增加 VRAM 使用为代价实现更高的性能。虽然 Mixtral 和其他 MoE 架构是从零开始预训练的,但最近出现了一种新的创建 MoE 的方法。多亏了 Arcee 的 MergeKit 库,我们现在可以通过组合多个预训练模型来创建 MoE。这些通常被称为 **frankenMoEs** 或 **MoErges**,以区别于预训练的 MoE。

本文将详细介绍 MoE 架构的工作原理以及 frankenMoE 的创建方式。最后,我们将使用 MergeKit 构建我们自己的 frankenMoE,并在多个基准上进行评估。代码可在 Google Colab 中以 LazyMergeKit 包装器形式获取。

特别感谢 MergeKit 的创建者 Charles Goddard 对本文的审阅。

🔀 专家混合模型 (MoE) 简介

专家混合模型是一种旨在提高效率和性能的架构。它使用多个专业化的子网络,称为“**专家**”。与整个网络都被激活的稠密模型不同,MoE 仅根据输入激活相关的专家。这可以加快训练速度并提高推理效率。

MoE 模型的核心有两个组成部分

  1. **稀疏 MoE 层**:这些层取代了 Transformer 架构中的稠密前馈网络层。每个 MoE 层包含多个专家,并且对于给定的输入,只有这些专家中的一部分被激活。
  2. **门控网络或路由器**:此组件确定哪些 token 由哪些专家处理,确保输入的每个部分都由最合适的专家处理。

在以下示例中,我们展示了 Mistral-7B 模块如何通过稀疏 MoE 层(前馈网络 1、2 和 3)和路由器转换为 MoE 模块。此示例表示一个包含三个专家的 MoE,其中两个专家当前处于激活状态(FFN 1 和 FFN 3)。

MoE 也面临着自身的挑战,尤其是在微调和内存需求方面。由于模型的复杂性,微调过程可能很困难,需要在训练过程中**平衡专家使用**,以正确训练门控权重以选择最相关的专家。在内存方面,即使在推理期间只使用了总参数的一小部分,整个模型(包括所有专家)也需要**加载到内存中**,这需要高 VRAM 容量。

更具体地说,MoE 有两个基本参数

  • **专家数量**(`num_local_experts`):这决定了架构中专家的总数(例如,Mixtral 为 8)。专家数量越多,VRAM 使用量越高。
  • **每个 token 的专家数量**(`num_experts_per_tok`):这决定了每个 token 和每个层激活的专家数量(例如,Mixtral 为 2)。在为了精度而增加每个 token 的专家数量(但收益递减)与为了快速训练和推理而减少专家数量之间存在权衡。

历史上,MoE 的表现一直不如稠密模型。然而,2023 年 12 月 Mixtral-8x7B 的发布改变了局面,并展示了令人印象深刻的性能。此外,GPT-4 也被认为是 MoE,这很合理,因为与稠密模型相比,它将大大降低 OpenAI 的运行和训练成本。除了这些近期优秀的 MoE 之外,我们现在还有一种使用 MergeKit 创建 MoE 的新方法:frankenMoE,也称为 MoErges。

🧟‍♂️ 真正的 MoE 与 FrankenMoE

真正的 MoE 和 frankenMoE 的主要区别在于它们的训练方式。对于真正的 MoE,专家和路由器是联合训练的。对于 frankenMoE,我们对现有模型进行升级再利用,然后初始化路由器。

换句话说,我们从基础模型复制层归一化和自注意力层的权重,然后复制每个专家中的 FFN 层的权重。这意味着除了 FFN 之外,所有其他参数都是共享的。这解释了为什么具有八个专家的 Mixtral-8x7B 的参数不是 8*7 = 56B,而是大约 45B。这也是为什么每个 token 使用两个专家可以提供 12B 稠密模型而不是 14B 的推理速度 (FLOPs)。

FrankenMoE 主要是选择最相关的专家并正确初始化它们。MergeKit 目前实现了三种初始化路由器的方法

  1. **随机**:随机权重。使用时要小心,因为每次都可能选择相同的专家(这需要进一步微调或 `num_local_experts = num_experts_per_tok`,这意味着您不需要任何路由)。
  2. **廉价嵌入**:它直接使用输入 token 的原始嵌入,并对所有层应用相同的转换。这种方法计算成本低,适用于性能较差的硬件。
  3. **隐藏**:它通过从 LLM 的最后一层提取正面和负面提示列表的隐藏表示。它们被平均和归一化以初始化门。有关此方法的更多信息可在 Charles Goddard 的博客上找到。

正如您所料,“隐藏”初始化是正确将 token 路由到最相关专家的最有效方法。在下一节中,我们将使用这种技术创建我们自己的 frankenMoE。

💻 创建一个 FrankenMoE

要创建我们的 frankenMoE,我们需要选择 `n` 个专家。在这种情况下,我们将依赖 Mistral-7B,因为它很受欢迎且体积相对较小。然而,像 Mixtral 那样有八个专家是相当多的,因为我们需要将它们全部加载到内存中。为了效率,本例中我将只使用四个专家,其中每个 token 和每个层都激活其中两个。在这种情况下,我们将得到一个参数为 24.2B 的模型,而不是 4*7 = 28B 参数的模型。

在这里,我们的目标是创建一个功能全面的模型,可以胜任各种任务:编写故事、解释文章、用 Python 编程等。我们可以将此要求分解为四个任务,并为每个任务选择最佳专家。我是这样分解的

  • **聊天模型**:一种通用模型,用于大多数交互。我使用了 mlabonne/AlphaMonarch-7B,它完美满足了要求。
  • **代码模型**:一个能够生成良好代码的模型。我对基于 Mistral-7B 的代码模型经验不多,但我发现 beowolx/CodeNinja-1.0-OpenChat-7B 特别好。
  • **数学模型**:数学对 LLM 来说很棘手,这就是我们为什么需要一个专门从事数学的模型。由于其 MMLU 和 GMS8K 分数高,我选择了 mlabonne/NeuralDaredevil-7B 来实现此目的。
  • **角色扮演模型**:此模型的目标是编写高质量的故事和对话。我选择了 SanjiWatsuki/Kunoichi-DPO-v2-7B,因为它声誉良好且 MT-Bench 分数高(8.51 对 Mixtral 的 8.30)。

现在我们已经确定了要使用的专家,我们可以创建 MergeKit 将用于创建我们的 frankenMoE 的 YAML 配置。这使用了 MergeKit 的 mixtral 分支。您可以在此页面上找到有关如何编写配置的更多信息。这是我们的版本

base_model: mlabonne/AlphaMonarch-7B
experts:
  - source_model: mlabonne/AlphaMonarch-7B
    positive_prompts:
    - "chat"
    - "assistant"
    - "tell me"
    - "explain"
    - "I want"
  - source_model: beowolx/CodeNinja-1.0-OpenChat-7B
    positive_prompts:
    - "code"
    - "python"
    - "javascript"
    - "programming"
    - "algorithm"
  - source_model: SanjiWatsuki/Kunoichi-DPO-v2-7B
    positive_prompts:
    - "storywriting"
    - "write"
    - "scene"
    - "story"
    - "character"
  - source_model: mlabonne/NeuralDaredevil-7B
    positive_prompts:
    - "reason"
    - "math"
    - "mathematics"
    - "solve"
    - "count"

对于每个专家,我提供五个基本的正面提示。如果您愿意,可以更花哨一些,写出完整的句子。最好的策略是使用应该触发特定专家的真实提示。您还可以添加负面提示来做相反的事情。

准备好后,您可以将配置保存为 `config.yaml`。在同一文件夹中,我们将下载并安装 mergekit 库(mixtral 分支)。

git clone -b mixtral https://github.com/arcee-ai/mergekit.git
cd mergekit && pip install -e .
pip install -U transformers

如果您的计算机有足够的 RAM(大约 24-32 GB RAM),您可以运行以下命令

mergekit-moe config.yaml merge --copy-tokenizer

如果您的 RAM 不足,您可以分片模型,如下所示(这会花费更长时间)

mergekit-moe config.yaml merge --copy-tokenizer --allow-crimes --out-shard-size 1B --lazy-unpickle

此命令会自动下载专家并在 `merge` 目录中创建 frankenMoE。

或者,您可以将您的配置复制到 LazyMergekit,这是我为了简化模型合并而制作的一个包装器。在此 Colab notebook 中,您可以输入模型名称,选择 `mixtral` 分支,指定您的 Hugging Face 用户名/token,然后运行单元格。创建 frankenMoE 后,它还会将其上传到 Hugging Face Hub,并附带格式精美的模型卡片。

我将我的模型命名为 Beyonder-4x7B-v3,并使用 AutoGGUF 创建了它的 GGUF 版本。如果您无法在本地机器上运行 GGUF 版本,您也可以使用此 Colab notebook 执行推理。

为了更好地了解其功能,它已在三个不同的基准上进行评估:Nous 的基准套件、EQ-Bench 和 Open LLM 排行榜。该模型并非旨在在传统基准中表现出色,因为代码和角色扮演模型通常不适用于这些上下文。尽管如此,由于强大的通用专家,它表现出色。

**Nous**:Beyonder-4x7B-v3 是 Nous 基准套件中最好的模型之一(使用 LLM AutoEval 进行评估),并且显著优于 v2。在此处查看完整的排行榜

**EQ-Bench**:它也是 EQ-Bench 排行榜上最好的 4x7B 模型,超越了旧版本的 ChatGPT 和 Llama-2-70b-chat。Beyonder 非常接近 Mixtral-8x7B-Instruct-v0.1 和 Gemini Pro,它们(据称)是更大的模型。

**Open LLM 排行榜**:最后,它在 Open LLM 排行榜上也是一个强大的表现者,显著优于 v2 模型。

除了这些定量评估之外,我建议您使用 LM Studio 上的 GGUF 版本以更定性的方式检查模型的输出。测试这些模型的常用方法是收集一组私有问题并检查它们的输出。通过这种策略,我发现 Beyonder-4x7B-v3 对用户和系统提示的更改具有很强的鲁棒性,而其他模型(包括 AlphaMonarch-7B)则不然。这非常酷,因为它总体上提高了模型的实用性。

FrankenMoE 是一种有前景但仍处于实验阶段的方法。更高的 VRAM 需求和更慢的推理速度等权衡可能使其难以看到其相对于 SLERP 或 DARE TIES 等更简单合并技术的优势。特别是,当您只使用两个专家时,frankenMoE 的性能可能不如您简单合并这两个模型。然而,frankenMoE 在知识保留方面表现出色,这可以产生更强大的模型,如 Beyonder-4x7B-v3 所证明的那样。通过合适的硬件,这些缺点可以有效地缓解。

结论

在本文中,我们介绍了专家混合架构。与从零开始训练的传统 MoE 不同,MergeKit 通过组合专家来促进 MoE 的创建,提供了一种改进模型性能和效率的创新方法。我们详细介绍了使用 MergeKit 创建 frankenMoE 的过程,强调了选择和组合不同专家以生成高质量 MoE 的实际步骤。

感谢您阅读本文。我鼓励您尝试使用 LazyMergeKit 制作您自己的 FrankenMoE:选择几个模型,根据 Beyonder 的配置创建您的配置,然后运行 notebook 以创建您自己的模型!如果您喜欢这篇文章,请在 Hugging Face 和 X/Twitter @maximelabonne 上关注我。

参考

社区

注册登录 发表评论