☰
从纯文本到视觉理解:LARQL多模态支持(SigLIP)架构解析
2026/10/15 10:34:17 网站建设 项目流程

【免费下载链接】larql

项目地址:https://gitcode.com/gh_mirrors/la/larql
点击查看免费下载

LARQL 是一个用 Rust 编写的本地大模型推理框架,它的多模态功能基于 SigLIP 视觉编码器,只需一条命令就能让 Gemma 3 这样的模型"看懂"图片。本文将完整拆解这套多模态架构:从图片文件如何变成 256 个视觉 Soft Token,再如何拼接到语言模型的输入序列中,以及跨模型家族的扩展设计。

如何开启 LARQL 多模态图片输入:一条命令让模型看图

在多模态支持上线后,LARQL 的run子命令新增了--image参数,支持一次传入多张图:

larql run gemma-3-4b-it --image cat.jpg --image stop_sign.jpg "describe both"

这是目前最直观的使用方式:图片路径和文本提示词一起进入推理管线。几个值得注意的边界约束(来自 run_cmd.rs 的校验逻辑):

  • --image必须搭配文本提示词,纯图片对话属于后续阶段的工作;
  • 与--ffn、--experts等实验性开关互斥,保证多模态路径独立可测。

对新手来说,只需记住:文本负责"问什么",--image负责"看什么",输出依然是纯文本(如图片描述)。

LARQL 的三种多模态接入模式:为什么选择 Embed-Splice

在设计文档 docs/multi-modal.md 中,LARQL 调研了把视觉信息送入解码器的三种结构:

接入模式代表模型特点LARQL 状态
Embed-Splice(嵌入拼接)Gemma 3、Granite Vision、Qwen-VL编码器输出投影到 LM 隐层维度,在占位符位置插入序列;LM 前向过程不变✅ v1 采用
Cross-Attention(交叉注意力)Llama 3.2 VisionLM 新增交叉注意力层类型,读取独立的视觉 KV cache⏸️ 刻意推迟
Prefix-Only(纯前缀)BLIP-2 时代视觉嵌入放在序列开头被 Embed-Splice 自然覆盖

LARQL 选择 Embed-Splice 作为 v1 范围的判断很务实:一套拼接机制就能覆盖 Gemma 3、Granite、Qwen-VL、音频模型等五个模型家族,且不需要改动层图(layer graph)。更关键的是,文档明确指出 Llama 3.2 Vision 的交叉注意力方案"不是 trait 扩展,而是前向传播重构"——它要求把层循环变成多态的、把 KV cache 从单一缓冲变成集合,因此被单独列为 Phase 6 的独立决策。

SigLIP 视觉编码器:像素如何变成特征矩阵

视觉塔的代码分为两层:

  • 配置与权重加载:vision_tower.rs(larql-models侧)负责从模型config.json的vision_config子对象解析 SigLIP 参数(patch 大小、隐层维度、归一化类型等),并按 HuggingFace 的 tensor key 约定从 safetensors 中加载vision_tower.vision_model.*权重。
  • CPU 前向传播:vision_tower.rs(larql-compute侧)实现了完整的 SigLIP 前向。与语言模型的关键区别有三点:双向注意力(无因果 mask)、GELU 激活、带 bias 的 LayerNorm。

对于 Gemma 3 4B,SigLIP 的输出是一个(4096, 1152)矩阵:整张图被切成 4096 个 patch,每个 patch 映射为 1152 维向量。

图片的预处理同样讲究。image_input.rs 用双三次插值把 JPEG/PNG/WebP 图片缩放为编码器期望的正方形,而归一化常数(mean=0.5, std=0.5)被刻意内嵌在编码器内部——源码注释特别强调这不是 ImageNet 统计量,用错会"悄无声息地产生略错的嵌入",导致图片描述质量下降且难以归因。这是一个很好的工程提醒:多模态管线里最危险的不是报错,而是静默的数值漂移。

Connector 投影器:4096 patch 到 256 token 的四步通路

语言模型不能直接消费 4096 个 patch 向量。Gemma 3 的视觉投影器(projector.rs)完成一次"空间下采样 + 维度投影":

  1. 重塑为空间网格:4096 个 patch 排成 64×64 网格;
  2. AvgPool 4×4:64×64 池化为 16×16,得到 256 个 Soft Token;
  3. RMSNorm:在视觉隐层轴上做归一化;
  4. 矩阵投影:1152 维 → 2560 维(Gemma 3 文本隐层维度)。
(4096, 1152) → AvgPool(4×4) → (256, 1152) → RMSNorm → matmul → (256, 2560)

这里有一个精细的"缩放归属"设计:投影器内部自带 RMSNorm 与投影矩阵,因此协议声明PrecomputedScaling::None——宿主侧不再叠加任何embed_scale。文档 docs/multi-modal.md 刻意没有开放Custom(f32)这种"裸标量"选项,理由是:模型相关的缩放因子若散落在协议层,每个实现者都要记住它、复制时会静默失效,正确归属地是随模型权重走、自带隐层维度信息的project()。

EmbeddingPlan 拼接机制:视觉嵌入如何"入队"

多模态的 trait 表面定义在 multimodal.rs,核心抽象只有几个:

  • ModalEncoder:与语言模型无关的模态编码器(SigLIP 喂给 Gemma 3 或 PaliGemma 都是同一个 SigLIP);
  • Connector:投影器接口,实现有线性、MLP-GELU、MLP+像素重排等变体;
  • TokenBudget:一张图占用多少占位位置——Fixed(256)(Gemma 3)、PerTile{729}(Granite 每瓦片)、Dynamic(Qwen-VL 运行时决定);
  • PlaceholderProtocol:占位符协议。Gemma 3 采用"三明治"结构:<start_of_image>+ 256 ×<image_soft_token>+<end_of_image>。

CLI 侧的编排入口是 run_cmd_image.rs,它把每张图组装成一个三段的EmbeddingPlan:

Tokens([<start_of_image>]) Precomputed { 256 行投影后的视觉嵌入, modality: Image } Tokens([<end_of_image>]) Tokens(文本 token)

真正"外科手术"的位置是前向传播的嵌入边界:原来tokens → 嵌入表 → 层循环的硬编码路径,被替换为接受分块计划的embed_plan。纯文本路径只构造一个Tokens块,多模态路径则构造多个块——文本路径在位级上与旧实现保持完全一致,这是保证重构安全的验收契约。

一个常被忽视的设计收益:vindex 存储层(larql-vindex)本来就是模态无关的,它存的是按(layer, feature_index)索引的残差形状向量,完全不关心这些向量来自文本还是图片。所以多模态化不需要动存储、KV cache 形状或lm_head,只动了两个"接缝":嵌入边界和位置编码。

跨模型家族扩展:Granite Vision 的 AnyRes 瓦片压力测试

Phase 2(PR #144)引入 Granite Vision 4.1,它用 SigLIP2 编码器 + MLP GELU 连接器 + AnyRes 动态瓦片,被文档明确称为"拼接机制的压力测试":

  • Gemma 3 每张图只有1 个拼接点(256 token 一块);
  • Granite 每张图有N+1 个拼接点(1 个基础瓦片 + N 个细节瓦片,每瓦片 729 token)。

如果EmbeddingPlan的拼接机制有 bug,Granite 会立刻暴露它,而 Gemma 的简单前缀结构发现不了。同时 AnyRes 瓦片数必须来自模型声明的合法集合(valid_tile_counts()),选错数量会直接破坏占位符核算——这体现了"共享机制、按模型配置"的分层思路。

端到端行为由 multimodal_e2e.rs 守护:用三张证据强度递增的参考图(简单色块 → 图形 → 真实照片)跑图片描述,确保"说得出图里有什么"而不只是模型在背诵"描述图片"的口头禅。

性能路线:CPU 编码器 + Metal LM 的混合管线

当前生产管线是一条清晰的 CPU→GPU 混合流水线:

CPU 编码器 → CPU 连接器 → embed_plan(CPU) → Metal LM prefill → Metal decode

这与大多数本地多模态运行时的起步方式一致。关键数字(来自 docs/multi-modal.md 的测算):

项目数值
SigLIP 参数量约 400M(27 层,hidden=1152)
内存占用(f16)约 800 MB
CPU 前向(实测)约 30 秒/张(Release 构建)
Metal 编码器预期1–3 秒/张

好消息是:现有 Metal shader 库存已经覆盖了 SigLIP 管线的绝大部分算子(sgemm、layer_norm、activation),唯一的结构性缺口是双向注意力——而它只是现有因果注意力 shader 去掉一行if col > row { -inf }掩码条件的变体,不是新内核。因此 Metal 编码器被列为 Phase 2 之后的独立后续 PR,届时将消除 GPU→CPU→GPU 的往返拷贝。

后续阶段路线与关键文件索引

按 ROADMAP.md 的进度,多模态目前Phase 0+1 已发布(trait 表面 + Gemma 3 SigLIP + CLI--image),Phase 2 已发布(Granite Vision SigLIP2 + AnyRes),Phase 3–6 处于设计阶段:

阶段内容状态
Phase 0/1trait 表面 + Gemma 3 SigLIP 前缀拼接✅ 已发布
Phase 2Granite Vision 瓦片拼接压力测试✅ 已发布
Phase 3序列中部文本-图像交错(chat template 占位符归属)设计中
Phase 4Qwen2-VL(M-RoPE 三维位置 + 动态预算)设计中
Phase 5音频(Gemma 4 USM / Qwen2-Audio Whisper)设计中
Phase 6Llama 3.2 Vision 交叉注意力(前向重构)独立决策

📌 关键文件速查:

  • 设计文档:docs/multi-modal.md
  • trait 表面(ModalEncoder/Connector/TokenBudget):multimodal.rs
  • SigLIP 配置与权重加载:encoders/vision_tower.rs
  • SigLIP CPU 前向:encoders/vision_tower.rs
  • Gemma 3 视觉投影器:connectors/projector.rs
  • CLI 多模态编排:run_cmd_image.rs
  • 图片解码与缩放:image_input.rs

小结:一套机制,五个模型家族

LARQL 的多模态架构给新手的最大启示是接缝思维:视觉理解能力不是"加一个模块",而是在嵌入边界和位置编码两处打准切口,让编码器、连接器、占位符协议各自可替换。这也是为什么 LARQL 敢承诺:新增第三个编码器家族(Whisper、USM 等)只是"新架构代码",而不需要再动前向传播。从一条--image命令,到未来音频输入与跨注意力模型的完整路线,这条路径已经铺好。👀

【免费下载链接】larql

项目地址:https://gitcode.com/gh_mirrors/la/larql
点击查看免费下载

相关推荐

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

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

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

立即咨询