MLX-VLM 中的 Phi-4 Multimodal(phi4mm):文本 / 图像 / 音频三模态推理与量化实战
2026/9/18 16:31:55 网站建设 项目流程

MLX-VLM 中的 Phi-4 Multimodal(phi4mm):文本 / 图像 / 音频三模态推理与量化实战

【免费下载链接】mlx-vlmMLX-VLM is a package for inference and fine-tuning of Vision Language Models (VLMs) on your Mac using MLX.项目地址: https://gitcode.com/GitHub_Trending/ml/mlx-vlm

本文基于 mlx-vlm 仓库中 phi4mm 模型文档 及其配套源码,系统讲解 Phi-4 Multimodal 三模态架构(文本 + 图像 + 音频)在 Apple Silicon / MLX 上的加载、推理、LoRA 模态切换与量化流程。读完本文,你将掌握通过mlx_vlm.generate命令行处理纯文本、图像、音频及「图像 + 音频」混合输入的方法,理解set_modality()自动切换 Vision / Speech LoRA 的底层机制,并能用mlx_vlm.convert产出 4-bit 量化模型。

模型概述

Phi-4 Multimodal 是一个三模态(tri-modal)模型,同时支持文本、图像与音频理解。它在 MLX-VLM 中的实现位于 mlx_vlm/models/phi4mm/,模块内部结构如下:

  • phi4mm.py:顶层Model组装(语言模型 + 视觉塔 + 音频编码器 + 双投影器)与 LoRA 切换逻辑;
  • language.py:Phi-4 语言模型主干(含 partial rotary、KV cache);
  • vision.py:SigLIP-2 视觉编码器(支持 NaFlex 变长 patch);
  • audio.py:Cascades Conformer 音频编码器与双分支音频投影;
  • processing_phi4mm.py:NaFlex 图像处理器 + Mel 频谱音频特征提取器 + 统一 Processor;
  • config.py:文本 / 视觉 / 音频三套配置 dataclass。

原始 checkpoint 的远程 processor 代码已被移植到仓库内(见 processing_phi4mm.py),因此加载模型时--trust-remote-code变为可选参数,而非必需。

架构组成

组件明细

组件细节
语言模型Phi-4(32 层,hidden 3072,24 头,8 个 KV 头)
视觉编码器SigLIP-2(27 层,hidden 1152,16 头)
音频编码器Cascades Conformer(24 个 block,dim 1024,16 头)
视觉投影器2 层 MLP(1152 → 3072 → 3072,GELU)
音频投影器2 层 MLP,带 speech / vision 两种模式

以上参数与 config.py 中的默认值一一对应:TextConfigmax_position_embeddings=131072VisionConfighidden_size=1152num_hidden_layers=27num_attention_heads=16patch_size=14image_size=448ModelConfighidden_size=3072num_hidden_layers=32num_attention_heads=24num_key_value_heads=8vocab_size=200064mm_projector_type="mlp2x_gelu"

从源码看各子模块

视觉塔(SigLIP-2 + NaFlex):vision.py 中的VisionTower包装了SigLip2VisionModel,通过select_layer = -2选取倒数第二层 hidden states 作为图像特征;NaFlex 路径会根据每张图的spatial_shapes(patch 高度/宽度)动态 resize 二维位置编码,再去除 padding token,返回每张图长度可变的特征列表。这一设计让小图不会被强行放大(详见下文图像处理器)。

音频编码器(Cascades Conformer):audio.py 中的ConformerEncoder完整复刻了 NeMo 风格的流水线:

  1. MeanVarianceNormLayer全局均值/方差归一化;
  2. NemoConvSubsampling卷积下采样(time_reduction=8,深度可分离卷积);
  3. AbsolutePositionalEncoding正弦绝对位置编码;
  4. T5RelativeAttentionLogitBias非对称 T5 相对注意力偏置;
  5. 24 个ConformerEncoderLayer(前馈 + 多头注意力 + 卷积模块,每层遵循x += 0.5*FFN_in; x += Attn; x += Conv; x += 0.5*FFN_out的残差结构)。

对超过 500 帧的长序列,编码器会自动按 chunk 展开计算再折叠回原长度(max_seq_len = 500分块逻辑见 audio.py)。

音频投影器(双分支):audio.py 中的AudioProjection包含speechvision两个独立的AudioProjectionBranch,每个分支都是Linear(audio_dim, hidden) → GELU → Linear(hidden, hidden),通过mode参数在运行时选择。视觉投影器build_mm_projector(见 phi4mm.py)结构与之一致,均为「Linear + GELU + Linear」两层 MLP 且带 bias,与 checkpoint 中img_projection.0 / .2的权重布局吻合。

LoRA 模态切换机制

双 LoRA 适配器

原始 checkpoint 在 LLM 主干上自带两个 LoRA 适配器

  • Vision LoRA(r=256,alpha=512)——加载时默认合并进主干权重;
  • Speech LoRA(r=320,alpha=640)——保留原始权重,供运行时切换。

在 phi4mm.py 的sanitize()中,加载权重时会解析vision_lora/speech_lora配置,按scale = alpha / r计算合并系数:Vision LoRA 被立即合并(merged = base_w + vision_lora_scale * (lora_b @ lora_a)),而 Speech LoRA 的 A/B 矩阵与 base 权重被存入模型实例(_speech_lora_a_speech_lora_b_base_weights),_active_lora默认置为"vision"

set_modality() 自动切换

set_modality()会根据输入类型自动选择正确的 LoRA(见 phi4mm.py):

输入类型目标 LoRA调用的方法
仅文本无(base 权重)apply_base_weights()
仅图像visionapply_vision_lora()
仅音频speechapply_speech_lora()
图像 + 音频两者同时apply_both_loras()

切换采用「惰性」策略:只有当目标模态与当前_active_lora不同时才真正改写 LLM 权重。该自动切换由 phi4mm.py 中的get_input_embeddings()触发——只要检测到pixel_values(有图)或input_audio_embeds(有音频),就会先调用set_modality(),随后才把图像/音频特征与文本 embedding 在特殊 token 位置拼接。仓库测试 test_phi4mm_sanitize_lora_keys 验证了默认合并 Vision LoRA、Speech LoRA 保留待切换的行为。

命令行推理

使用前请确认已安装 mlx-vlm 及其依赖(参考 安装文档 与 使用文档)。

纯文本理解

mlx_vlm.generate \ --model microsoft/Phi-4-multimodal-instruct \ --prompt "Explain the theory of relativity in simple terms." \ --max-tokens 256

图像理解

mlx_vlm.generate \ --model microsoft/Phi-4-multimodal-instruct \ --image /path/to/image.jpg \ --prompt "Describe this image." \ --max-tokens 256

音频理解

mlx_vlm.generate \ --model microsoft/Phi-4-multimodal-instruct \ --audio /path/to/audio.wav \ --prompt "" \ --max-tokens 256

多模态(图像 + 音频)

mlx_vlm.generate \ --model microsoft/Phi-4-multimodal-instruct \ --image /path/to/image.jpg \ --audio /path/to/audio.wav \ --prompt "" \ --max-tokens 256

音频输入约定为16 kHz 单声道波形,处理器会自动完成重采样(详见「处理器细节」一节);--max-tokens可自行调整以控制生成长度。

Python API 推理

在脚本中按如下方式组合loadapply_chat_templategenerate

from mlx_vlm import load, generate from mlx_vlm.prompt_utils import apply_chat_template model, processor = load("microsoft/Phi-4-multimodal-instruct") image = ["/path/to/image.jpg"] audio = ["/path/to/audio.wav"] prompt = "What animals are in the image?" formatted_prompt = apply_chat_template( processor, model.config, prompt, num_images=len(image), num_audios=len(audio), ) result = generate( model=model, processor=processor, prompt=formatted_prompt, image=image, audio=audio, max_tokens=256, temperature=0.0, ) print(result.text)

几个关键点:

  • num_images/num_audios必须与实际传入的文件数量一致,apply_chat_template会据此插入<|image_1|>/<|audio_1|>占位符(相关实现见 prompt_utils.py 中apply_chat_templatenum_audios参数流,phi4mm 属于NUMBERED_IMAGE_TOKENS格式);
  • 占位符随后由 processing_phi4mm.py 中的正则替换为内部 token:<|image_N|><image>IMAGE_TOKEN_INDEX = -200),<|audio_N|><|endoftext11|>AUDIO_TOKEN_INDEX = 200011),并在送入模型前按音频实际 token 数展开;
  • 纯文本输入走常规 tokenization 分支,无需占位符。

处理器细节(NaFlex 图像 + Mel 音频)

Phi4MMProcessor 由三部分组成:

  • Phi4MMImageProcessor:NaFlex 风格,不做小图放大。图像先按 patch 大小(默认 14)计算实际 patch 数,若低于min_num_patches(默认 256)或高于max_num_patches(默认 3600)才调整目标尺寸;随后裁剪到 patch 对齐、归一化(mean/std 均为 0.5)、切 patch 并按max_num_patches补齐(同时产出pixel_attention_maskspatial_shapes)。因此每张图的 patch 数可变,这正是视觉塔需要按spatial_shapes动态 resize 位置编码的原因。
  • Phi4MMAudioFeatureExtractor:将任意采样率音频重采样到 16 kHz(8 kHz 会先升采样),经过预加重与 Hamming 窗分帧后,用与 SpeechLib 一致的 80 维 Mel 滤波器组(n_fft=512,fmax=7690)提取 log-Mel 频谱特征,并按time_reduction=8估算编码后的音频 token 数(audio_embed_sizes)。
  • 统一调度Phi4MMProcessor.__call__同时接受imagestextaudios三个入口(也兼容框架侧的audio单数参数),最终返回input_idsattention_maskpixel_valuespixel_attention_maskspatial_shapesinput_audio_embedsaudio_embed_sizes等字段组成的BatchFeature

量化

mlx_vlm.convert \ --model microsoft/Phi-4-multimodal-instruct \ -q \ --mlx-path Phi-4-multimodal-instruct-4bit

量化行为与 LoRA 机制密切相关,源码层面的执行顺序如下:

  1. 预合并双 LoRA:访问quant_predicate属性时(见 phi4mm.py),模型会先调用apply_both_loras()把 Vision 与 Speech 两套 LoRA 增量同时烘焙进 LLM 权重,随后清空所有 LoRA 缓存(_base_weights_speech_lora_a/b_vision_lora_a/b),因为量化权重上无法再做 LoRA 切换。
  2. 只量化语言模型quant_predicate返回的谓词对audio_encoderaudio_projectionmm_projectorvision_tower路径一律返回False(跳过),其余(即 Phi-4 LLM 主干)返回True参与量化。
  3. 多模态模块保持 bfloat16:视觉编码器、音频编码器、双投影器均以 bfloat16 精度保留。

仓库测试 test_phi4mm_quant_predicate_skips_multimodal 与 test_phi4mm_quant_predicate_clears_lora 分别验证了「多模态子模块不被量化」与「访问 quant_predicate 会合并并清空 LoRA」这两条行为。

因此量化完成后:

  • LoRA 切换被禁用(两套适配器已烘焙进权重,无需也不再可能切换);
  • 推理时set_modality()会因_base_weights为空而直接跳过 LoRA 逻辑,自动退化为纯权重推理(见 phi4mm.py 的保护判断)。

注意事项

  • 音频输入约定:输入为 16 kHz 单声道波形;若提供其他采样率(如 44.1 kHz),处理器会利用scipy.signal.resample_poly自动重采样,低于 8 kHz 的输入会直接报错(见 processing_phi4mm.py)。
  • 占位符处理<|image_1|>/<|audio_1|>这类带编号的占位符由apply_chat_template插入,并在 Processor 内部转换为模型可识别的内部 token,无需手动书写。
  • 长上下文TextConfig.max_position_embeddings=131072,且language.py中 Phi-4 注意力采用partial_rotary_factor=0.75(仅 75% 的头维度做 RoPE),相关长上下文行为有专门测试覆盖(见 test_phi4mm_longrope_uses_top_level_original_context_length)。
  • 远程代码可选:远程 processor 已移植进仓库并由install_auto_processor_patch注册到phi4mm模型类型(见 processing_phi4mm.py),因此--trust-remote-code非必需。

小结

Phi-4 Multimodal 在 mlx-vlm 中的实现覆盖了「三模态架构移植 + 双 LoRA 运行时切换 + 定制量化策略」三块核心工程:SigLIP-2(NaFlex 变长 patch)负责视觉,Cascades Conformer 负责音频,set_modality()按输入模态自动装配最合适的 LoRA 权重,mlx_vlm.convert -q则把双 LoRA 预合并后仅量化 LLM 主干。对需要在 Apple Silicon 上同时处理文本、图像与语音的开发者和研究者而言,这是一个开箱即用的三模态推理与量化参考实现。

【免费下载链接】mlx-vlmMLX-VLM is a package for inference and fine-tuning of Vision Language Models (VLMs) on your Mac using MLX.项目地址: https://gitcode.com/GitHub_Trending/ml/mlx-vlm

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询