CPU da tezlik: GGUF, Q4_K_M va kesh
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:
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())
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:
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]))
| fayl | hajmi_MB |
|---|---|
| Qwen3.5-0.8B-Q4_K_M.gguf | 508 |
| qwen2.5-coder-1.5b-instruct-q4_k_m.gguf | 1 066 |
| Qwen3.5-2B-Q4_K_M.gguf | 1 222 |
| Qwen3.5-4B-Q4_K_M.gguf | 2 614 |
| Tez-tez8-Q4_K_M.gguf | 2 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:
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)
o'lchovlar: 192
| model | condition | sovuq_s | iliq_s |
|---|---|---|---|
| qwen2.5-coder-1.5b | cpu:2 | 12.36 | 1.84 |
| qwen2.5-coder-1.5b | cpu:4 | 7.01 | 1.12 |
| qwen2.5-coder-1.5b | cpu:all | 6.98 | 1.21 |
| qwen3.5-0.8b | cpu:2 | 6.16 | 0.94 |
| qwen3.5-0.8b | cpu:4 | 3.38 | 0.65 |
| qwen3.5-0.8b | cpu:all | 3.35 | 0.66 |
| qwen3.5-2b | cpu:2 | 13.29 | 1.95 |
| qwen3.5-2b | cpu:4 | 7.25 | 1.24 |
| qwen3.5-2b | cpu:all | 7.19 | 1.21 |
| qwen3.5-4b | cpu:2 | 34.55 | 4.61 |
| qwen3.5-4b | cpu:4 | 18.61 | 3.05 |
| qwen3.5-4b | cpu:all | 18.55 | 2.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:
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}")
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):
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}")
grammar bilan vaqt farqi, foizda: eng kami -5.5, mediana 2.2, eng ko'pi 18.6
| model | mode | to'g'ri_shakl |
|---|---|---|
| qwen2.5-coder-1.5b | constrained | 24/24 |
| qwen2.5-coder-1.5b | free | 24/24 |
| qwen3.5-0.8b | constrained | 24/24 |
| qwen3.5-0.8b | free | 18/24 |
| qwen3.5-2b | constrained | 24/24 |
| qwen3.5-2b | free | 18/24 |
| qwen3.5-4b | constrained | 24/24 |
| qwen3.5-4b | free | 24/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:
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")
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.1da 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.