Asosiy qismga o'tish

CPU da tezlik: GGUF, Q4_K_M va kesh

6-bob 6 kod bloki 2125 so'z

Savol

Bankdagi kompyuterda videokarta bo'lishi kafolatlanmagan. Ko'pincha — oddiy ofis kompyuteri. Demak model CPU da ishlashi kerak. Va bu yerda eng katta xavf bor: tahlilchi savol yozdi va bir daqiqa kutdi — u ikkinchi marta yozmaydi.

Spike 2 ning savoli:

Bitta so'rovga javob CPU da qancha vaqt oladi — har bir nomzod model uchun?

Bu bobning raqamlari saqlangan o'lchovlardan olinadi, kitob yig'ilayotganda model ishga tushirilmaydi. Sabab: kitob yozilayotgan paytda xuddi shu kompyuterda keyingi model (tez10) o'qitilmoqda. Shu paytda o'lchangan tezlik yolg'on bo'lardi — protsessor band. Har bir jadval qaysi fayldan olinganini yozamiz.

GGUF va Q4_K_M

Avval model fayli haqida. Neyron tarmoq — milliardlab son (weight — vazn). Training paytida har bir son odatda 16 bit bilan saqlanadi. 4 milliard parametrli model uchun bu taxminan 8 GB — oddiy kompyuterning butun xotirasi.

Shuning uchun model quantization (kvantlash) qilinadi: har bir son kamroq bit bilan, taxminan yoziladi. Tez llama.cpp ishlatadi, va uning fayl formati — GGUF. Kvantlash usuli — Q4_K_M: vaznlar asosan 4 bitli bloklarda, ba'zi muhim qismlar esa ko'proq bit bilan.

Tezning o'z modellaridan birini kvantlash logida ko'raylik. Bu log tez9 modeli yasalgan kuni yozilgan:

python3 qator
for qator in open(Path.home() / "tez_runs/tez9/quant.log", encoding="utf-8"):
    if "model size" in qator or "quant size" in qator:
        print(qator.split(": ", 1)[1].strip())
Chiqish
model size  =  8023.67 MiB (16.00 BPW)
quant size  =  2572.86 MiB (5.13 BPW)

16 bitdan taxminan 5 bitga — fayl uch barobardan ko'proq kichraydi. Diskdagi modellarning haqiqiy hajmi:

python6 qator
import pandas as pd

modellar = Path("/mnt/d/Tez_models")
nomlar = ["Qwen3.5-0.8B-Q4_K_M.gguf", "qwen2.5-coder-1.5b-instruct-q4_k_m.gguf", "Qwen3.5-2B-Q4_K_M.gguf",
          "Qwen3.5-4B-Q4_K_M.gguf", "Tez-tez8-Q4_K_M.gguf"]
show(pd.DataFrame([{"fayl": n, "hajmi_MB": round((modellar / n).stat().st_size / 2**20)} for n in nomlar]))
faylhajmi_MB
Qwen3.5-0.8B-Q4_K_M.gguf508
qwen2.5-coder-1.5b-instruct-q4_k_m.gguf1 066
Qwen3.5-2B-Q4_K_M.gguf1 222
Qwen3.5-4B-Q4_K_M.gguf2 614
Tez-tez8-Q4_K_M.gguf2 583

Oxirgi qator — bugungi Tez modeli, tez8. U Qwen3.5-4B asosida o'qitilgan, shuning uchun hajmi deyarli bir xil.

Kvantlash bepul emas: sonlar taxminiy bo'ladi, va bu modelning aniqligiga ta'sir qilishi mumkin. Biz Tez modellarining 16 bitli va Q4_K_M versiyasini bir xil testda solishtirmaganmiz. Bu o'lchanmagan. Bizda bor narsa — Q4_K_M modelning o'zining test natijalari (6-qism).

To'rt model, uch sharoit

Spike 2 to'rtta nomzod modelni o'lchadi: Qwen3.5 ning 0.8B, 2B va 4B versiyalari va Qwen2.5-Coder-1.5B. Har biri CPU da 2 ta oqim, 4 ta oqim va hamma oqimlar bilan; har biri to'rt tilda bittadan so'rov bilan. Xom o'lchovlar — speed_cpu_raw.jsonl faylida, har bir so'rov alohida qator. Jadvalni shu fayldan qaytadan yig'amiz:

python9 qator
TEZ_SPEED = TEZ / "speed_cpu_raw.jsonl"
xom = pd.read_json(TEZ_SPEED, lines=True)
print(f"o'lchovlar: {len(xom)}")
jadval = (xom[xom["mode"] == "constrained"]
          .pivot_table(index=["model", "condition"], columns="cache", values="seconds", aggfunc="median")
          .rename(columns={False: "sovuq_s", True: "iliq_s"})
          .round(2)
          .reset_index())
show(jadval, 20)
Chiqish
o'lchovlar: 192
modelconditionsovuq_siliq_s
qwen2.5-coder-1.5bcpu:212.361.84
qwen2.5-coder-1.5bcpu:47.011.12
qwen2.5-coder-1.5bcpu:all6.981.21
qwen3.5-0.8bcpu:26.160.94
qwen3.5-0.8bcpu:43.380.65
qwen3.5-0.8bcpu:all3.350.66
qwen3.5-2bcpu:213.291.95
qwen3.5-2bcpu:47.251.24
qwen3.5-2bcpu:all7.191.21
qwen3.5-4bcpu:234.554.61
qwen3.5-4bcpu:418.613.05
qwen3.5-4bcpu:all18.552.95

Ikki ustun: sovuq va iliq. Ular orasidagi farq — bu bobning eng muhim saboqi. Uni biroz keyin ochamiz. Avval oqimlar.

2 ta oqim — eng sekin. 4 ta oqim — ancha tezroq. Hamma oqimlar esa 4 ta bilan deyarli bir xil: 4 tadan keyin tezlik o'smaydi. Bu xotiraning o'tkazish qobiliyatiga bog'liq bo'lishi mumkin, lekin biz buni alohida o'lchamaganmiz.

Va 4-bobdagi saboqni eslang: 2 ta oqimli i9 kuchsiz kompyuter emas. Bu jadvaldagi "cpu:2" satri — kuchsiz noutbukning tezligi emas, faqat shu kuchli kompyuterning ikki yadrosi.

Sovuq va iliq: kesh

Tezning har bir so'rovida model bir xil uzun matnni o'qiydi: toollar ro'yxati, jadval tavsifi, qoidalar, misollar. Spike da bu taxminan 1 500 token edi. Foydalanuvchining o'z so'rovi esa — 15–30 token.

Sovuq so'rovda model hammasini noldan o'qiydi. Iliq so'rovda esa server oldingi so'rovdan qolgan hisobni (prompt cache — prompt keshi) qayta ishlatadi va faqat yangi qismni — foydalanuvchi so'rovini — o'qiydi. Jadvalda farq besh barobardan yetti yarim barobargacha.

Bu qanday ko'rinishini real hayotda ko'raylik. Quyida tez8 modeli bilan test paytidagi llama.cpp server logi. Bu log oxirgi ishga tushirilgan test to'plamiga tegishli (20 ta savol), va biz uning nusxasini kitob papkasida saqladik:

python12 qator
import re

log = (DATA / "accuracy_tez-tez8_cpu_all.log").read_text(encoding="utf-8")
yuklash = next(q for q in log.splitlines() if "model loaded" in q).split()[0]
oqish = re.findall(r"prompt eval time =\s+([\d.]+) ms /\s+(\d+) tokens", log)
yozish = re.findall(r"\|\s+eval time =\s+([\d.]+) ms /\s+(\d+) tokens", log)
daqiqa, soniya = yuklash.split(".")[:2]
print(f"server ishga tushganidan model yuklanguncha: {daqiqa} daqiqa {soniya} soniya")
print()
print("so'rov  prompt_tokenlar  prompt_s  javob_tokenlar  javob_s")
for i, ((ms, n), (ems, en)) in enumerate(list(zip(oqish, yozish))[:6], 1):
    print(f"{i:>5}  {n:>15}  {float(ms) / 1000:>8.2f}  {en:>14}  {float(ems) / 1000:>7.2f}")
Chiqish
server ishga tushganidan model yuklanguncha: 5 daqiqa 49 soniya

so'rov  prompt_tokenlar  prompt_s  javob_tokenlar  javob_s
    1             1292     13.37              23     1.56
    2               15      0.36              23     1.58
    3               25      0.45              27     1.86
    4               19      0.38              35     2.44
    5               20      0.36              20     1.36
    6               16      0.36              24     1.64

Uchta narsa ko'rinadi.

Birinchi: model yuklanishi — daqiqalar. Bu WSL dan D: diskni sekin ko'prik orqali o'qish bilan bog'liq (xatolar jurnalida bunday holat qayd etilgan). Muhimi: bu vaqt test hisobiga kirmaydi — test server tayyor bo'lgandan keyin boshlanadi.

Ikkinchi: birinchi so'rov — sovuq. Model 1 292 tokenli promptni to'liq o'qiydi va bunga o'n soniyadan ko'p ketadi.

Uchinchi: keyingi so'rovlarda faqat 15–25 token o'qiladi — soniyaning bir qismida. Endi vaqtning asosiy qismi — javobni yozish: 20–35 token, har biri taxminan 70 millisekund.

Biz bu yerda xato qilgan edik

1-bobning birinchi versiyasida biz birinchi javobdagi uzoq vaqtni "model faylini yuklash" deb tushuntirgan edik. Bu noto'g'ri edi — logni o'qimasdan aytilgan taxmin. Log boshqacha ko'rsatdi: sovuq prompt. 1-bob tuzatildi, xato esa jurnalga 18-raqam bilan yozildi. Qoida: vaqtni faqat log yoki o'lchov kodi bilan tushuntir, taxmin bilan emas.

Mahsulot uchun xulosa (qarorlar jurnalida D-15): tizim prompti o'zgarmas bo'lishi kerak, va fayl ochilganda uni orqa fonda "isitib" qo'yish kerak — toki tahlilchining birinchi savoli ham iliq bo'lsin. Bu ilovaning ishi, va ilova hali yo'q.

Grammar qancha turadi

3-bobda grammar model chiqishini qanday cheklashini ko'rdik. Cheklash bepulmi? Spike har bir so'rovni ikki marta o'lchadi: grammar bilan (constrained) va grammarsiz (free):

python7 qator
holat = xom.groupby(["model", "mode"])["valid"].agg(["sum", "count"]).reset_index()
holat["to'g'ri_shakl"] = holat["sum"].astype(str) + "/" + holat["count"].astype(str)
show(holat[["model", "mode", "to'g'ri_shakl"]])

tezlik = xom.pivot_table(index=["model", "condition", "cache"], columns="mode", values="seconds", aggfunc="median")
farq = (tezlik["constrained"] / tezlik["free"] - 1) * 100
print(f"grammar bilan vaqt farqi, foizda: eng kami {farq.min():.1f}, mediana {farq.median():.1f}, eng ko'pi {farq.max():.1f}")
Chiqish
grammar bilan vaqt farqi, foizda: eng kami -5.5, mediana 2.2, eng ko'pi 18.6
modelmodeto'g'ri_shakl
qwen2.5-coder-1.5bconstrained24/24
qwen2.5-coder-1.5bfree24/24
qwen3.5-0.8bconstrained24/24
qwen3.5-0.8bfree18/24
qwen3.5-2bconstrained24/24
qwen3.5-2bfree18/24
qwen3.5-4bconstrained24/24
qwen3.5-4bfree24/24

Grammarsiz ikki model har to'rt javobdan birida noto'g'ri shakl yozdi — engine uni umuman bajara olmaydi. Grammar bilan — har bir javob to'g'ri shaklda.

Vaqt farqining medianasi — 2 foiz atrofida, ba'zan manfiy (ya'ni grammar bilan tezroq). Lekin eng ko'p holatda farq 18.6 foizga yetdi. Spike hisobotida "grammar 5 foizdan kam vaqt oladi" deb yozilgan — bu mediana uchun to'g'ri, har bir holat uchun emas. Har bir holat atigi to'rtta so'rovdan iborat va iliq so'rovlar bir soniya atrofida, shuning uchun bunday kichik vaqtlarda bir necha o'n millisekundlik tasodifiy farq ham katta foiz bo'lib ko'rinadi.

Qaror aniq edi: grammar har doim yoqilgan.

Bugungi model qanchalik tez

Spike — bitta kunda, to'rt so'rov bilan o'lchangan. Endi bugungi modelning haqiqiy ishini ko'raylik: tez8 ning 6-qismdagi katta testdagi har bir javobi, 20 ta test to'plami, CPU da, 4 ta oqim bilan. Har to'plamning birinchi javobi — sovuq, shuning uchun uni alohida sanaymiz:

python13 qator
import json
import statistics

iliq, sovuq = [], []
for fayl in sorted((TEZ / "results").glob("tez8g_cpu_*.jsonl")):
    vaqtlar = [json.loads(q)["seconds"] for q in open(fayl, encoding="utf-8")]
    sovuq.append(vaqtlar[0])
    iliq.extend(vaqtlar[1:])

iliq.sort()
print(f"to'plamlar: {len(sovuq)}, iliq javoblar: {len(iliq)}")
print(f"iliq javob: mediana {statistics.median(iliq):.2f} s, 90% javob {iliq[int(0.9 * len(iliq))]:.2f} s gacha, eng sekini {iliq[-1]:.2f} s")
print(f"sovuq (birinchi) javob: {min(sovuq):.1f} s dan {max(sovuq):.1f} s gacha")
Chiqish
to'plamlar: 20, iliq javoblar: 725
iliq javob: mediana 2.82 s, 90% javob 4.35 s gacha, eng sekini 7.09 s
sovuq (birinchi) javob: 14.5 s dan 32.2 s gacha

Iliq javob — o'rtacha uch soniya atrofida, eng sekini ham o'n soniyadan kam. Bu chat uchun qabul qilsa bo'ladigan tezlik — kuchli kompyuterda. Sovuq birinchi javob esa sezilarli uzun, va aynan shu sabab "isitish" kerak.

Sovuq javoblar to'plamdan to'plamga 14 dan 32 soniyagacha farq qiladi. Farqning sababini har bir to'plam uchun alohida tekshirmaganmiz: server logi faqat oxirgi to'plam uchun saqlangan, unda sovuq prompt 1 292 token edi.

Qaysi model

Spike 2 dan keyingi qaror (D-16) shunday edi: trainingsiz versiya uchun — Qwen3.5-4B; training uchun maqsad — 2B ni o'qitib, 4B darajasiga olib chiqish. Chunki 2B iliq so'rovda taxminan ikki yarim barobar tez va xotirani yarmicha oladi.

Bu plan amalga oshmadi: bugungi Tez modeli 4B asosida. Nega — 7-bobda (trainingsiz modellarning aniqligi) va 18-bobda (2B bilan birinchi training) ko'ramiz. Hozircha faqat bitta gap: tezlik — yagona mezon emas. Tez, lekin noto'g'ri javob — eng qimmat javob.

Hali o'lchanmagan

Bu bobdagi hamma raqam kuchli kompyuterda (i9-14900K, WSL) o'lchangan. Ochiq qolgan narsalar:

  • Kuchsiz kompyuter. Haqiqiy eski ofis protsessorida o'lchanmagan.
  • 8 GB xotira. Spike da 4B model taxminan 4.4 GB xotira egalladi (4 096 token kontekst bilan). Windows 11, model va katta fayl birga 8 GB ga sig'adimi — ochiq savol (Q-14), 26-bob.
  • GPU tezligi. Spike planida bor edi, lekin CUDA build WSL dagi tizim kutubxonasi bilan mos kelmadi. Keyinroq bitta model GPU da aniqlik bo'yicha sinalgan, lekin GPU tezligi alohida hisobot sifatida yozilmagan — shuning uchun bu kitobda GPU tezlik raqami yo'q.
  • Server xavfsizligi. Yuqoridagi logning boshida llama-server ogohlantiradi: API kaliti yo'q va CORS hamma manbaga ruxsat beradi. Server faqat 127.0.0.1 da tinglaydi, lekin bu kompyuterdagi brauzerda ochilgan sahifa ham unga so'rov yubora olishi mumkin. Mahsulotda bu yopilishi kerak (27-bob).

Xulosa

  • GGUF Q4_K_M modelni 16 bitdan taxminan 5 bitga kichraytiradi: 4B model taxminan 2.6 GB. Kvantlashning aniqlikka ta'siri biz uchun o'lchanmagan.
  • 4 tadan ko'p oqim tezlikni oshirmadi. 2 ta oqimli kuchli protsessor kuchsiz kompyuter emas.
  • Eng katta tezlik manbai — prompt keshi: iliq so'rov sovuqdan besh-yetti barobar tez. Tizim prompti o'zgarmas bo'lishi va oldindan isitilishi kerak.
  • Grammar har bir javobni to'g'ri shaklda qildi; vaqt narxi medianada 2 foiz atrofida.
  • Bugungi tez8 iliq javobda o'rtacha uch soniya atrofida — kuchli kompyuterda. Kuchsiz kompyuter, 8 GB, GPU va server xavfsizligi — ochiq.

Keyingi bobda tezlikdan aniqlikka o'tamiz: o'qitilmagan modellar Tezning vazifasini qanchalik yaxshi bajaradi.

Bu bobni bepul o'qidingiz.

Kitob o'rgatadi, lekin sizning kodingiz nega ishlamaganini ayta olmaydi. Kursda uy vazifangiz tekshiriladi.

Telegramda yozish Narxlar