延々と「ちょまてよ!」を繰り返すQwen3.5

昔取った杵柄

今日、半分試運転の意味でllama.cppを自宅マシンに突っ込みまして。
そしてQwen3.5-2Bモデルをぶち込んでみました。

ざっくり速度評価:正直奇跡だと思った

今回の場合、大体トークン処理速度は 6 toks/s なので決して遅くはないです。むしろCPUとしては上出来・・というか、Sandy Bridgeとしては上出来という感じですね。最近のCPUだともっと高速に出力でき、たぶん桁違いのスピードになるはずです。これはメモリ速度が遅い上に演算速度が純粋に遅いというのもありますね。

まず、DDR3規格のメモリ速度は 2133 MT/s でして、大体現行メモリの1/2から1/3ぐらいの速度です。ただ、単位がMT/sなので、実際の容量的な帯域としてはもっと大きな広がりがあると考えられます。
加えてCPU性能に関しては推して知るべし・・というか。

メモリ使用量の内訳

続いてメモリ使用量をのぞいてみます。llama.cppは起動時にいろんな情報を出力します。その内訳の情報をいろいろ眺めてみたいと思います。

重みバッファ:モデルファイルの容量

これはモデルファイルそのものの容量を意味しています。
本来のモデルファイルサイズはbf16モデルとしては4.57GBのようですが、今回はUnslothが供給する4.5bit量子化モデルというものを使用しており、そのサイズが1.2GB強ぐらいにサイズダウンしています。
モデルが取り扱う数値型をBrainFloat-16bit型から4bit型に変換することで、サイズダウンだけでなく、そこにかかわる計算処理を可能な限り軽減するような措置が取られています。今回CPUはあまりその恩恵は得られませんが、GPUでは効果絶大で、最近のGPUではTensorCoreという低精度演算を得意とする計算回路が実装されることで、16bit演算からリニアではなく、もっと異次元に高速な演算を実現させています。

load_tensors: loading model tensors, this can take a while... (mmap = true, direct_io = false)
load_tensors:   CPU_Mapped model buffer size =  1267.23 MiB

KVキャッシュ(バッファ)サイズ:推論時に使う計算キャッシュ

非常に重要な機構で、本来推論をする際は「質問+直前まで出力された内容」をニューラルネットワークに突っ込んで計算し、その結果として「次に出力されると予想したトークン」を返します。ただ、これを馬鹿正直にやると非常に出力が遅くなります(いくらGPUが動いたとしても)。
そこで、計算結果のうち、すでに分かり切ってるものについてはKVキャッシュに放り込み、すぐにメモリ上から読み取りできるようにします。KVはそれぞれTransformerモデルで使用するKey, Valueというデータを指しています。
KVキャッシュは3GiB使用されており、かなり大きく確保されていることが分かります。これは、モデルのコンテキスト長が256kと非常に長いためです。
例えば64Kトークンを上限にするなど設定することで、かなり減らすことが可能になります。

RSバッファ:再帰処理で使用するデータのバッファ領域
再帰処理はTransformerとは対をなす仕組みのことですけれど、今回の場合ほとんど使用されることはなく、わずか72MiBのみ保持される格好となります。

llama_kv_cache:        CPU KV buffer size =  3072.00 MiB
llama_kv_cache: size = 3072.00 MiB (262144 cells,   6 layers,  4/1 seqs), K (f16): 1536.00 MiB, V (f16): 1536.00 MiB
llama_memory_recurrent:        CPU RS buffer size =    77.06 MiB
llama_memory_recurrent: size =   77.06 MiB (     4 cells,  24 layers,  4 seqs), R (f32):    5.06 MiB, S (f32):   72.00 MiB

なお、上限を64Kに減らすとこうなります。1/4に減ったことが分かりますね。

llama_kv_cache:        CPU KV buffer size =   768.00 MiB
llama_kv_cache: size =  768.00 MiB ( 65536 cells,   6 layers,  4/1 seqs), K (f16):  384.00 MiB, V (f16):  384.00 MiB
llama_memory_recurrent:        CPU RS buffer size =    77.06 MiB
llama_memory_recurrent: size =   77.06 MiB (     4 cells,  24 layers,  4 seqs), R (f32):    5.06 MiB, S (f32):   72.00 MiB

ここからさらに、Keyキャッシュ・Valueキャッシュ単位でバッファを量子化(Quantize)することで、さらにメモリを半分以下にすることも可能です。デフォルトではこのバッファは 単精度浮動小数型16bit(f16)に設定されています。

ビジョン用各種バッファサイズ:画像を取り込みバッファ

VISIONと私はよく言いますが、人工知能に画像を読み取らせる場合、基本的にはテキストモデルのAttention構造に入る前まで処理を分岐させ、テキストエンコーダと並走させる形でVision向けエンコーダを組み込む構成をとります。
この時エンコーダモデルとして取り込むのが写像モデルの重みづけデータであり、これはllama.cppの場合、mmprojモデルとして別モデルで構成されたものを読み込みます。
実は文字をテンソルに変換するほうが情報量としては多く、画像そのものはがそのデータをそのまま決められたサイズのテンソルとしてEmbedding化すればよいため、モデルサイズは小さくなることが多いです。以下に記述されているのはそのモデルデータおよび計算用バッファのサイズです。

  • 写像モデルのサイズは637.25MBほど
  • 写像モデルのメタデータサイズは100KiBほど
  • イメージ取り込み時の分解能は1,472px四方
  • 計算バッファとして押さえられるCPUバッファサイズは223.3MiB
load_hparams: model size:         637.25 MiB
load_hparams: metadata size:      0.10 MiB
warmup: warmup with image size = 1472 x 1472
alloc_compute_meta:        CPU compute buffer size =   223.30 MiB

プロンプトキャッシュ:いわゆる会話履歴をSlotごとにキャッシュする領域

なお、私はこれをサービスとして払い出す場合は無効化します。

srv    load_model: prompt cache is enabled, size limit: 8192 MiB

実メモリとしては5.1GiB押さえているようです。

 98106 aiuser    20   0 7091596   5.1g   1.2g S   0.0  21.6   0:07.65 llama-server

Reasoning型として使用するには無理。

結論としては使用に耐えるものではないということで、少なくともThinkingモードはNGとなりました。
理由はこれまたQwenならではの気質ですけど、考え込み「すぎ」なのです。挨拶するだけで5分以上の長考になりました。以下、その過程を強制停止させるまでの5分間分ノーカットで掲載します。ご覧ください。

私からの発言は「こんにちわ。お元気ですか?」です。

Thinking Process:

1.  **Analyze the Input:**
    *   Language: Japanese (Hello/Conversational).
    *   Content: "こんにちわ。お元気ですか?" (Konichiwa. Ogenki desu ka?)
    *   Meaning: "Hello. Are you well?" (A standard greeting).
    *   Tone: Casual, polite, friendly.

2.  **Determine the Appropriate Response:**
    *   Acknowledge the greeting.
    *   Express well-being (politely).
    *   Offer assistance if needed.
    *   Maintain a friendly, helpful tone.

3.  **Drafting the Response:**
    *   *Standard:* こんにちは。お元気ですか?元気です。どうお過ごしですか?
    *   *Polite:* こんにちは。お元気ですか?元気です。今日もご苦労ですか?
    *   *Friendly:* こんにちは!お元気ですか?
    *   *Action:* Since I am an AI, I should express that I'm doing well, but also offer help.

4.  **Refining the Response (in Japanese):**
    *   Greeting: こんにちは! (Konnichiwa!)
    *   Health check: お元気ですか? (Ogenki desu ka?)
    *   Response: 元気です。ありがとうございます。 (Genki desu. Arigatou gozaimasu.)
    *   Offer help: 何かお手伝いできますか? (Nanika otte ndai dekinai ka?)
    *   Closing: ご挨拶ください (Goai de ka da nui) / 元気なことです (Genki na de kudasai) -> No, simpler is better.

    *   *Selected version:*
        こんにちは!お元気ですか?
        元気です。お疲れ様でした。
        何かお手伝いできることがあれば、言ってくださいね。
        よろしくお願いします。

5.  **Final Polish:**
    *   Keep it concise and warm.
    *   Ensure natural Japanese.
    *   *Revised:* こんにちは!お元気ですか?元気です。今日も素敵な一日になりますように。何かお手伝いできることがあれば、いつでも言ってください。

6.  **Final Check:** Does this match the persona of a helpful AI assistant? Yes.

    *   Wait, let's make it slightly more conversational.
    *   "こんにちは!お元気ですか?お疲れ様です。今日も何かお手伝いできますか?"

7.  **Output Generation:** (Matches the thought process above).
    *   Start with greeting.
    *   Check status.
    *   Offer help.
    *   Sign off with a polite phrase.

    *   *Japanese:* こんにちは!お元気ですか?
    *   *Response:* 元気です。お疲れ様でした。
    *   *Offer:* 何かお手伝いできることがあれば、言ってくださいね。
    *   *Closing:* よろしくお願いします。

    Let's combine these smoothly.

    *   こんにちは!お元気ですか?
    *   元気です。今日も良い一日になりますように。
    *   何かお手伝いできることがあれば、いつでも言ってくださいね。

    Actually, keeping it very standard and polite is safer.
    "こんにちは!お元気ですか?お疲れ様でした。何かお手伝いできることがあれば、いつでも言ってくださいね。よろしくお願いいたします。"

    Let's go with a friendly version.

    *   こんにちは!お元気ですか?
    *   元気です。今日も素敵な一日になりますように。
    *   何かお手伝いできることがあれば、いつでも言ってください。

    Okay, ready to write.

    *Wait, one more check on the greeting.* "こんにちわ" is "Konichiwa". "お元気ですか" is "Are you well".
    My response should be in Japanese.
    I should also consider the context of the user (likely asking about my status or just chatting).
    Since I am an AI, I should clarify I'm an AI but I'm doing well.

    *Revised Plan:*
    1. Acknowledge greeting.
    2. Say I'm doing well.
    3. Offer help.

    *Draft:*
    こんにちは!お元気ですか?
    元気です。今日も良い一日になりますように。
    何かお手伝いできることがあれば、いつでも言ってくださいね。

    Let's refine the politeness level.
    "こんにちは!お元気ですか?"
    "元気です。"
    "お疲れ様でした" (If it's work related, but general greeting is fine).
    "何かお手伝いできますか?"
    "よろしくお願いいたします"

    Okay, final version.
    "こんにちは!お元気ですか?"
    "元気です。今日も素敵な一日になりますように。"
    "何かお手伝いできることがあれば、いつでも言ってくださいね。"

    Wait, "こんにちわ" is a bit informal for "konnichiwa" (usually written as こんにちわ or コンニチワ). I will use standard "こんにちは".

    Final decision:
    こんにちは!お元気ですか?
    元気です。今日も素敵な一日になりますように。
    何かお手伝いできることがあれば、いつでも言ってくださいね。
    よろしくお願いします。

    Wait, I should avoid being too robotic.
    Let's keep it concise.

    Okay, generating response.
    "こんにちは!お元気ですか?"
    "元気です。今日も素敵な一日になりますように。"
    "何かお手伝いできることがあれば、いつでも言ってください。"
    "よろしくお願いいたします。"

    Okay, I will formulate the response now.
    Structure:
    1. Greeting.
    2. Self-status.
    3. Offer help.
    4. Polite closing.

    Wait, I need to make sure I don't sound like a robot too much.
    "こんにちは!お元気ですか?"
    "元気です。今日も素敵な一日になりますように。"
    "何かお手伝いできることがあれば、いつでも言ってください。"
    "よろしくお願いいたします。"

    Okay.
    Wait, "こんにちわ" is a typo for "こんにちは". I will use "こんにちは" in my response to be correct.

    Final check on tone: Friendly, helpful, polite.

    Okay, proceeding.
    "こんにちは!お元気ですか?"
    "元気です。今日も素敵な一日になりますように。"
    "何かお手伝いできることがあれば、いつでも言ってください。"
    "よろしくお願いいたします。"

    Wait, "こんにちわ" is "Konichiwa". I will use "こんにちは" in response.
    Also, "お元気ですか" is standard.
    Okay.

    Let's write it down.
    "こんにちは!お元気ですか?"
    "元気です。今日も素敵な一日になりますように。"
    "何かお手伝いできることがあれば、いつでも言ってください。"
    "よろしくお願いいたします。"

    Wait, I should check if there's any specific context. No.
    Okay, just a standard friendly response.
    "こんにちは!お元気ですか?"
    "元気です。今日も素敵な一日になりますように。"
    "何かお手伝いできることがあれば、いつでも言ってください。"
    "よろしくお願いいたします。"

    Actually, looking at the user's "こんにちわ", it's a casual greeting.
    So my response should be casual but polite.
    "こんにちは!お元気ですか?"
    "元気です。今日も素敵な一日になりますように。"
    "何かお手伝いできることがあれば、いつでも言ってください。"
    "よろしくお願いいたします。"

    Okay.
    Wait, I should not just repeat the greeting too much.
    "こんにちは!お元気ですか?"
    "元気です。今日も素敵な一日になりますように。"
    "何かお手伝いできることがあれば、いつでも言ってください。"

こんな感じで、悩みすぎなのです。いったいどこまで「Wait, 」を繰り返すのか。
永遠と頭足らずの提案者と頭足らずの批判者がやいのやいの言い合うステージが続きます。なので、使うにしてもInstructモード限定だなって感じです。

ただ、意味のキャッチ、再考の気付きやその考慮事項はかなり的確で、2Bモデルでもかなりレベルの高いReasoningができていることが分かります。GPU処理にさせることでこのあたりは非常に高速な処理が期待できるのではないかと踏んでいます。

安価なのでもいいから1枚買ってみようかどうしようか・・・・うーむ。
とりあえずは6GB以上のVRAMがないことには厳しいかな・・

コメント

タイトルとURLをコピーしました