Open17

Qwen2-VL を llama.cpp で動かしたいメモ

syoyosyoyo
  • Conv3D
  • patch embed
  • RoPE(Qwen2VL はちょっと特殊?)
  • Dynamic resolution(model 内で画像リサイズ?)

あたりの pytorch(python)実装を C++ 移植すればいけるか?

syoyosyoyo

patch_size については 14x14 pixel. これを隣接する 2x2 でまとめて MLP で処理して 1 つの token に圧縮する(実質 28x28 pixels が 1 token)

2x2 tokens を 1 つに圧縮するのは, Visual tokens の数を減らすため.

syoyosyoyo

Multimodal Rotary Position Embedding (M-RoPE)

RoPE を temporal, width, height に分解する.
text input の場合は位置(width のみ利用かな?)で 1D RoPE
image の場合は width, height 利用
video の場合はさらに temporal 利用

テキストは対角で扱うなどしてテキストと画像(と video)の共存を可能とする.
元のアイデアは

https://zenn.dev/syoyo/scraps/83bdc1b7b62883

参照

syoyosyoyo

Unified Image and Video Understanding

画像と動画をミックスして扱えるように,
video は 2 frames/秒で抽出して, Conv3D で depth 2 で処理
画像は同じフレームとして処理(つまり depth 2 の Conv3D で処理するのは変わらない)

長い動画については, 解像度を調整して, 最大 token 数が 16384 までになるようにしている

syoyosyoyo

conv3d は deth 2(2 枚の画像フレームを convolve)なので, 2 つの conv2d で表現できる
あとは vol2col 相当を実装して matmul で処理でもいいかも

syoyosyoyo

Multimodal RoPE パラメータの mrope_section (rope_scale)は [16, 24, 24] になっている.
なぜこの分割なのかは不明.

LLM 側の Attention は nheads 12, hidden_size 1536. したがって head_dim = hidden_size / nheads = 128.

multimodal rope では mrope_section * 2 = [16, 24, 24, 16, 24, 24] で, sum = 128(= head_dim) になるようにして分割している.

https://github.com/huggingface/transformers/blob/f38531619ddff23a510d5f7ccbc257a1bb1a3cb7/src/transformers/models/qwen2_vl/modeling_qwen2_vl.py#L235

syoyosyoyo

Vision encoder 側の Attention の MLP では, activation は QuickGELU を使っている

syoyosyoyo

PatchEmbed

      (patch_embed): PatchEmbed(
        (proj): Conv3d(3, 1280, kernel_size=(2, 14, 14), stride=(2, 14, 14), bias=False)
      )

Conv3d があるが, depth 2 なので Conv2d を二回適用に分解することは容易.

syoyosyoyo

VisionModel(VisionEncoder) の処理フロー

  • PatchEmbed
  • Position Embedding(RoPE)
  • VisionBlock(Attention + MLP)
  • PatchMerger
syoyosyoyo

llama.cpp での qwen2 は以下の構成になっているので, qwen2-vl の LLM part の tensor name もこれに合わせる

          LLM_ARCH_QWEN2,
          {
              { LLM_TENSOR_TOKEN_EMBD,      "token_embd" },
              { LLM_TENSOR_OUTPUT_NORM,     "output_norm" },
              { LLM_TENSOR_OUTPUT,          "output" },
              { LLM_TENSOR_ATTN_NORM,       "blk.%d.attn_norm" },
              { LLM_TENSOR_ATTN_Q,          "blk.%d.attn_q" },
              { LLM_TENSOR_ATTN_K,          "blk.%d.attn_k" },
              { LLM_TENSOR_ATTN_V,          "blk.%d.attn_v" },
              { LLM_TENSOR_ATTN_OUT,        "blk.%d.attn_output" },
              { LLM_TENSOR_FFN_NORM,        "blk.%d.ffn_norm" },
              { LLM_TENSOR_FFN_GATE,        "blk.%d.ffn_gate" },
              { LLM_TENSOR_FFN_DOWN,        "blk.%d.ffn_down" },
              { LLM_TENSOR_FFN_UP,          "blk.%d.ffn_up" },
          },