【免费下载链接】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 Vision | LM 新增交叉注意力层类型,读取独立的视觉 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)完成一次"空间下采样 + 维度投影":
- 重塑为空间网格:4096 个 patch 排成 64×64 网格;
- AvgPool 4×4:64×64 池化为 16×16,得到 256 个 Soft Token;
- RMSNorm:在视觉隐层轴上做归一化;
- 矩阵投影: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/1 | trait 表面 + Gemma 3 SigLIP 前缀拼接 | ✅ 已发布 |
| Phase 2 | Granite Vision 瓦片拼接压力测试 | ✅ 已发布 |
| Phase 3 | 序列中部文本-图像交错(chat template 占位符归属) | 设计中 |
| Phase 4 | Qwen2-VL(M-RoPE 三维位置 + 动态预算) | 设计中 |
| Phase 5 | 音频(Gemma 4 USM / Qwen2-Audio Whisper) | 设计中 |
| Phase 6 | Llama 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
相关推荐
Transformers 中 ModernVBert 视觉-语言编码器全解析:从 ModernBert + SigLIP 架构到掩码视觉语言推理实践
Transformers 中 ModernVBert 视觉 语言编码器全解析:从 ModernBert + SigLIP 架构到掩码视觉语言推理实践 导读 Mo
人工智能大模型深度学习NLP预训练微调模型推理服务Swagger-SpringMVC架构解析:从核心模型到多格式支持
Swagger SpringMVC架构解析:从核心模型到多格式支持 背景与设计理念 在现代API开发中,文档生成工具扮演着至关重要的角色。Swagger Spr
开发工具后端文档fabric多模态支持:文本、语音、图像全解析
fabric多模态支持:文本、语音、图像全解析 你是否还在为处理多种格式的内容而烦恼?fabric框架提供了一站式的多模态解决方案,让你轻松应对文本、语音和图像
AI 应用人工智能提示工程CLI本地部署
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考