Granite Vision
下载模型文件并将 GRANITE_MODEL 环境变量指向模型路径。
$ git clone https://huggingface.co/ibm-granite/granite-vision-3.2-2b
$ export GRANITE_MODEL=./granite-vision-3.2-2b
1. 运行 llava surgery v2 脚本
首先,我们需要运行 llava surgery 脚本,如下所示:
python llava_surgery_v2.py -C -m $GRANITE_MODEL
运行完成后,你会在模型目录中看到两个新文件(llava.clip 和 llava.projector),如下所示。
$ ls $GRANITE_MODEL | grep -i llava
llava.clip
llava.projector
我们可以看到投影器(projector)和视觉编码器(visual encoder)被分离到 llava 文件中。快速检查确保文件内容不为空:
import os
import torch
MODEL_PATH = os.getenv("GRANITE_MODEL")
if not MODEL_PATH:
raise ValueError("环境变量 GRANITE_MODEL 未设置!")
encoder_tensors = torch.load(os.path.join(MODEL_PATH, "llava.clip"))
projector_tensors = torch.load(os.path.join(MODEL_PATH, "llava.projector"))
assert len(encoder_tensors) > 0
assert len(projector_tensors) > 0
如果你检查已加载张量的 .keys(),你会在 encoder_tensors 中看到大量的 vision_model 张量,在多模态 projector_tensors 中看到 5 个张量('multi_modal_projector.linear_1.bias'、'multi_modal_projector.linear_1.weight'、'multi_modal_projector.linear_2.bias'、'multi_modal_projector.linear_2.weight'、'image_newline')。
2. 创建视觉组件 GGUF 文件
接下来,创建一个新目录来存放视觉组件,并复制 llava.clip/projector 文件,如下所示。
$ ENCODER_PATH=$PWD/visual_encoder
$ mkdir $ENCODER_PATH
$ cp $GRANITE_MODEL/llava.clip $ENCODER_PATH/pytorch_model.bin
$ cp $GRANITE_MODEL/llava.projector $ENCODER_PATH/
现在,我们需要为视觉编码器编写配置文件。为了转换模型,请确保使用正确的 image_grid_pinpoints,因为这些参数可能因模型而异。你可以在 $GRANITE_MODEL/config.json 中找到 image_grid_pinpoints。
{
"_name_or_path": "siglip-model",
"architectures": [
"SiglipVisionModel"
],
"image_grid_pinpoints": [
[384,384],
[384,768],
[384,1152],
[384,1536],
[384,1920],
[384,2304],
[384,2688],
[384,3072],
[384,3456],
[384,3840],
[768,384],
[768,768],
[768,1152],
[768,1536],
[768,1920],
[1152,384],
[1152,768],
[1152,1152],
[1536,384],
[1536,768],
[1920,384],
[1920,768],
[2304,384],
[2688,384],
[3072,384],
[3456,384],
[3840,384]
],
"mm_patch_merge_type": "spatial_unpad",
"hidden_size": 1152,
"image_size": 384,
"intermediate_size": 4304,
"model_type": "siglip_vision_model",
"num_attention_heads": 16,
"num_hidden_layers": 27,
"patch_size": 14,
"layer_norm_eps": 1e-6,
"hidden_act": "gelu_pytorch_tanh",
"projection_dim": 0,
"vision_feature_layer": [-24, -20, -12, -1]
}
此时你的目录结构应该是这样的:
$ ls $ENCODER_PATH
config.json llava.projector pytorch_model.bin
现在将组件转换为 GGUF 格式。注意我们还将图像均值/标准差重写为 [.5,.5,.5],因为我们使用 SigLIP 视觉编码器 - 在 transformers 模型中,你可以在 preprocessor_config.json 中找到这些数值。
$ python convert_image_encoder_to_gguf.py \
-m $ENCODER_PATH \
--llava-projector $ENCODER_PATH/llava.projector \
--output-dir $ENCODER_PATH \
--clip-model-is-vision \
--clip-model-is-siglip \
--image-mean 0.5 0.5 0.5 \
--image-std 0.5 0.5 0.5
这将在 $ENCODER_PATH/mmproj-model-f16.gguf 创建第一个 GGUF 文件;我们将把这个文件的绝对路径记为 $VISUAL_GGUF_PATH。
3. 创建 LLM GGUF 文件
Granite Vision 模型包含一个 Granite LLM 作为其语言模型。目前,获取 LLM 的 GGUF 文件最简单的方法是在 transformers 中加载复合模型并导出 LLM,以便可以通过正常的转换路径直接转换。
首先,将 LLM_EXPORT_PATH 设置为导出 transformers LLM 的路径。
$ export LLM_EXPORT_PATH=$PWD/granite_vision_llm
import os
import transformers
MODEL_PATH = os.getenv("GRANITE_MODEL")
if not MODEL_PATH:
raise ValueError("环境变量 GRANITE_MODEL 未设置!")
LLM_EXPORT_PATH = os.getenv("LLM_EXPORT_PATH")
if not LLM_EXPORT_PATH:
raise ValueError("环境变量 LLM_EXPORT_PATH 未设置!")
tokenizer = transformers.AutoTokenizer.from_pretrained(MODEL_PATH)
# 注意:transformers 最近才添加了 granite vision 支持(4.49版本);
# 如果你遇到尺寸不匹配错误,说明你的版本太旧了。
# 如果你使用的是较旧版本,请设置 `ignore_mismatched_sizes=True`
# 如下所示;虽然模型不会正确加载,但我们要导出的模型的 LLM 部分
# 会正确加载。
model = transformers.AutoModelForImageTextToText.from_pretrained(MODEL_PATH, ignore_mismatched_sizes=True)
tokenizer.save_pretrained(LLM_EXPORT_PATH)
model.language_model.save_pretrained(LLM_EXPORT_PATH)
现在你可以使用 llama cpp 项目根目录中的常规转换器将导出的 LLM 转换为 GGUF 格式。
$ LLM_GGUF_PATH=$LLM_EXPORT_PATH/granite_llm.gguf
...
$ python convert_hf_to_gguf.py --outfile $LLM_GGUF_PATH $LLM_EXPORT_PATH
4. 量化
如果你想对 LLM 进行量化,可以像处理其他 LLM 一样使用 llama-quantize。例如:
$ ./build/bin/llama-quantize $LLM_EXPORT_PATH/granite_llm.gguf $LLM_EXPORT_PATH/granite_llm_q4_k_m.gguf Q4_K_M
$ LLM_GGUF_PATH=$LLM_EXPORT_PATH/granite_llm_q4_k_m.gguf
注意: 目前你无法量化视觉编码器,因为 Granite Vision 模型使用 SigLIP 作为视觉编码器,其张量维度不能被 32 整除。
5. 在 Llama cpp 中运行模型
正常构建 llama cpp;你应该有一个名为 llama-mtmd-cli 的目标二进制文件,你可以向其传递两个二进制文件。作为示例,我们传递 llama.cpp 横幅。
$ ./build/bin/llama-mtmd-cli -m $LLM_GGUF_PATH \
--mmproj $VISUAL_GGUF_PATH \
-c 16384 \
--temp 0