llama: default qwen2.5vl window attention metadata (#16868)
Existing qwen2.5vl GGUFs can contain an empty qwen25vl.vision.fullatt_block_indexes array. The compat layer translated the projector metadata but left clip.vision.n_wa_pattern unset, causing llama-server to fail loading the CLIP model. Default the runtime compat value to the standard Qwen2.5-VL pattern when the key cannot be derived, and make the converter emit the same default for nil or empty fullatt block metadata. Fixes #16540
This commit is contained in:
@@ -39,10 +39,6 @@ func (q *qwen25VLModel) KV(t *Tokenizer) KV {
|
||||
}
|
||||
}
|
||||
|
||||
if q.VisionModel.FullAttentionBlocks == nil {
|
||||
kv["qwen25vl.vision.fullatt_block_indexes"] = []int32{7, 15, 23, 31}
|
||||
}
|
||||
|
||||
kv["qwen25vl.vision.block_count"] = cmp.Or(q.VisionModel.Depth, 32)
|
||||
kv["qwen25vl.vision.embedding_length"] = q.VisionModel.HiddenSize
|
||||
kv["qwen25vl.vision.attention.head_count"] = cmp.Or(q.VisionModel.NumHeads, 16)
|
||||
@@ -53,12 +49,19 @@ func (q *qwen25VLModel) KV(t *Tokenizer) KV {
|
||||
kv["qwen25vl.vision.window_size"] = cmp.Or(q.VisionModel.WindowSize, 112)
|
||||
kv["qwen25vl.vision.attention.layer_norm_epsilon"] = cmp.Or(q.VisionModel.RMSNormEps, 1e-6)
|
||||
kv["qwen25vl.vision.rope.freq_base"] = cmp.Or(q.VisionModel.RopeTheta, 1e4)
|
||||
kv["qwen25vl.vision.fullatt_block_indexes"] = q.VisionModel.FullAttentionBlocks
|
||||
kv["qwen25vl.vision.fullatt_block_indexes"] = q.fullAttentionBlocks()
|
||||
kv["qwen25vl.vision.temporal_patch_size"] = cmp.Or(q.VisionModel.TemporalPatchSize, 2)
|
||||
|
||||
return kv
|
||||
}
|
||||
|
||||
func (q *qwen25VLModel) fullAttentionBlocks() []int32 {
|
||||
if len(q.VisionModel.FullAttentionBlocks) > 0 {
|
||||
return q.VisionModel.FullAttentionBlocks
|
||||
}
|
||||
return []int32{7, 15, 23, 31}
|
||||
}
|
||||
|
||||
func (q *qwen25VLModel) Tensors(ts []Tensor) []*ggml.Tensor {
|
||||
var out []*ggml.Tensor
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package convert
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestQwen25VLFullAttentionBlockDefaults(t *testing.T) {
|
||||
tokenizer := &Tokenizer{Vocabulary: &Vocabulary{}}
|
||||
|
||||
for _, tt := range []struct {
|
||||
name string
|
||||
blocks []int32
|
||||
want []int32
|
||||
}{
|
||||
{
|
||||
name: "nil",
|
||||
want: []int32{7, 15, 23, 31},
|
||||
},
|
||||
{
|
||||
name: "empty",
|
||||
blocks: []int32{},
|
||||
want: []int32{7, 15, 23, 31},
|
||||
},
|
||||
{
|
||||
name: "custom",
|
||||
blocks: []int32{5, 17},
|
||||
want: []int32{5, 17},
|
||||
},
|
||||
} {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
model := &qwen25VLModel{}
|
||||
model.VisionModel.FullAttentionBlocks = tt.blocks
|
||||
|
||||
got, ok := model.KV(tokenizer)["qwen25vl.vision.fullatt_block_indexes"].([]int32)
|
||||
if !ok {
|
||||
t.Fatalf("fullatt_block_indexes has unexpected type %T", got)
|
||||
}
|
||||
if !slices.Equal(got, tt.want) {
|
||||
t.Fatalf("fullatt_block_indexes = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Vendored
+1
@@ -2958,6 +2958,7 @@ void handle_qwen25vl_clip(gguf_context * meta, ggml_context * ctx) {
|
||||
gguf_set_val_u32(meta, "clip.vision.n_wa_pattern", (uint32_t)(arr[0] + 1));
|
||||
}
|
||||
}
|
||||
inject_u32_if_missing(meta, "clip.vision.n_wa_pattern", 8);
|
||||
|
||||
// Default image_size = 560 (Qwen2VLVisionModel default, no image_size in HF config).
|
||||
inject_u32_if_missing(meta, "clip.vision.image_size", 560);
|
||||
|
||||
Reference in New Issue
Block a user