LM Studio 中统一多模态 MLX 引擎架构的介绍

2025-05-30

undefined

LM Studio 的 MLX 引擎 (MIT) 利用了两个强大的 Python 包,用于在 Apple Silicon M 芯片上高效运行 LLM:mlx-lm 用于文本生成(由 @awni@angeloskath、Apple 开发)和 mlx-vlm 用于支持视觉的语言模型(由 @Blaizzy 开发)。

mlx-engine 提交 f98317e(应用内引擎 v0.17.0 及更高版本)开始,我们迁移到了一个新的统一架构,该架构将这些包的底层组件融合在一起。现在,mlx-lm 的文本模型实现始终被使用,而 mlx-vlm 的视觉模型实现则以“附加组件”的形式模块化使用,以生成可被文本模型理解的图像嵌入。

undefined

新的 mlx-engine 统一视觉模型架构。mlx-lm 文本模型通过 mlx-vlm 视觉附加组件进行扩展

这极大地提高了使用多模态 MLX VLM(例如 Google 的 Gemma 3)时的性能和用户体验。例如,与 VLM 进行纯文本聊天现在可以受益于提示缓存——以前仅限于纯文本 LLM 的功能——从而显着加快后续响应速度。这使得 MLX VLM 可以与纯文本 LLM 无缝互换用于文本任务,同时提供视觉功能作为额外优势。

👓 继续阅读,深入了解问题、解决方案以及我们如何在 LM Studio 的 MLX 引擎中实现这一统一架构。

👷 我们非常欢迎对 LM Studio 的 MLX 引擎 进行开源贡献!如果您想帮助我们将统一架构扩展到更多模型,请参阅此 GitHub 问题,这是一个很好的起点。

什么是多模态模型?

多模态 LLM 是一种可以接受多种模态输入的 LLM。这意味着除了能够处理文本输入之外,LLM 还可以接受图像和/或音频输入。

新的 MLX 引擎尚不支持音频,但我们计划这种方法也适用于音频输入。


通常,具备视觉能力的 LLM 通过以下流程摄取图像输入

undefined

多模态视觉 LLM 的一般操作流程:将图像转换为可被文本模型理解的嵌入,使用文本模型生成输出

  • 提示中包含文本和图像
  • (1a) 模型的“文本”部分将文本编码到模型的嵌入空间中
    • "这是什么?"[0.83, 0.40, 0.67, ...]
  • (1b) 模型的“视觉”部分将图像编码到文本模型的嵌入空间中。这会将图像转换为文本模型可以理解的格式
    • image.png[0.28, 0.11, 0.96, ...]
  • (2) 文本和图像嵌入合并
    • [0.83, 0.40, 0.67, ...] + [0.28, 0.11, 0.96, ...][0.83, 0.40, 0.67, ..., 0.28, 0.11, 0.96, ...]
  • (3) 合并后的嵌入通过文本模型,模型根据文本和图像中的信息生成文本

如果提示中没有图像,则“合并后的嵌入”仅仅是文本嵌入。


MLX 生态系统中的模型实现

MLX Python 生态系统中,有两个主要的库提供模型实现和与 Apple Silicon 上的 LLM 交互的基础设施

  • mlx-lm:文本模型实现和与之交互的基础设施
  • mlx-vlm:文本模型实现、视觉模型实现和与之交互的基础设施

历史上,mlx-lm 包含了没有多模态功能的纯文本模型实现,而 mlx-vlm 则是 MLX VLM 实现的实际归宿。

undefined

mlx-lm 和 mlx-vlm 中的模型实现组件。黄色 = 文本模型组件。蓝色 = 视觉模型组件

LM Studio 的 mlx-engine 最初是使用简单的“分叉”架构开发的,以支持纯文本模型和具备视觉功能的模型

undefined

mlx-engine 的分叉视觉模型架构。黄色 = 文本模型组件。蓝色 = 视觉模型组件

如果模型具备视觉功能,则将专门使用 mlx-vlm 模型实现(文本 + 视觉)。如果模型是纯文本,则将专门使用 mlx-lm 模型实现(文本)。

在每个路径中都使用了一个独特的(不同的)文本模型实现(来自 mlx-lm 或来自 mlx-vlm)。

问题:分叉架构

mlx-engine 的简单分叉架构存在以下问题

  • mlx-lmmlx-vlm 的功能不完全一致或行为存在细微差异时,我们应该使用哪个?
    • 我们如何限制多模态模型与纯文本模型交互体验的差异?
    • 假设一个实现比另一个性能更好,或者一个包含另一个没有的 bug。我们如何始终选择使用 mlx-lm 还是 mlx-vlm?我们是否应该根据请求在两者之间热切换加载(复杂)?
  • 如果我们在这两者之间进行任何切换,或者支持使用多模态模型的纯文本变体(例如,此 Gemma 3 纯文本模型),那么给定模型的 bug 和维护范围将翻倍。这是因为存在两个共同存在的实现,它们有条件地用于推断相同的底层模型。因此,我们必须确保两个单独的模型都没有 bug,才能在 LM Studio 中提供无 bug 的体验。
undefined

同一个文本模型存在两个独立的版本

我们的解决方案:两全其美

我们寻求将 mlx-lmmlx-vlm 的核心组件结合起来,为所有 MLX LLM 和 VLM 创建一个“统一”(无分叉)推理引擎。

在与 @awni@Blaizzy 进行宝贵的讨论后,我们通过以下贡献实现了这一点

mlx-lm

mlx-vlm

undefined

新的 mlx-engine 统一视觉模型架构。mlx-lm 文本模型通过 mlx-vlm 视觉附加组件进行扩展

在这个统一架构中,始终从 mlx-lm (2) 加载多模态 LLM 的核心文本模型,不再可能从 mlx-vlm 加载略有不同的文本模型。

然后我们有条件地加载一个 VisionAddOn,它使用 mlx-vlm 功能 (3,4) 从图像生成可被 mlx-lm 文本模型理解的嵌入(参见 mlx-engine 中的 Gemma3VisionAddOn 实现)。

通过这种设置,我们能够以流线型和单一路径的方式推断多模态模型。这有助于我们发布更清晰、更易维护的 LM Studio MLX 引擎,并提高性能。

Gemma 3 12B QAT,均在 M3 MacBook Pro 上使用 MLX 4 位。统一架构使后续 TTFT 速度提高约 25 倍


详细信息:mlx-engine 中的 VisionAddOns

LM Studio 新 mlx-engine 统一架构的要点在于,它允许我们对所有多模态模型使用 mlx-lm 的文本模型实现,同时仍然能够利用 mlx-vlm 的视觉模型组件来生成可被文本模型理解的图像嵌入。

这是通过引入 VisionAddOns源代码)实现的,它们是模块化组件,可用于为多模态模型生成图像嵌入。这些 VisionAddOns 实现了一个由 BaseVisionAddOn 抽象类定义的通用接口,例如

class BaseVisionAddOn:
    """
    Base class that defines the interface for a VisionAddOn.
    """

    @abstractmethod
    def __init__(self):
        """
        Where load of vision model components is intended to occur.
        """

    @abstractmethod
    def compute_embeddings(
        self,
        text_model: nn.Module,
        prompt_tokens: mx.array,
        images_b64: List[str],
    ) → mx.array:
        """
        Returns input embeddings for the language model after
        text/image merging of the prompt
        """

VisionAddOns 生成图像嵌入,这些嵌入可以作为 mlx-lmstream_generate() 函数的新 input_embeddings 参数的输入(参见对 mlx-lm 的提交)。

目前,Gemma 3(Gemma3VisionAddOn)和 Pixtral(PixtralVisionAddOn)是唯二已迁移到统一架构的模型。然而,该架构的设计使得可以轻松将更多 VisionAddOns 添加到 mlx-enginevision_add_ons 目录,然后连接到 ModelKit 此处

    VISION_ADD_ON_MAP = {
        "gemma3": Gemma3VisionAddOn,
        "pixtral": PixtralVisionAddOn,
    }

我们非常欢迎向我们的开源仓库 https://github.com/lmstudio-ai/mlx-engine 贡献以扩展此模式!例如,请参阅 mlx-engine 问题 将 VisionAddOn 模式扩展到 Qwen2.5VL #167


反馈与贡献