Оптимизация размера и скорости: GGUF, GPTQ, AWQ, INT8/INT4, trade-offs accuracy vs speed
Уменьшение размера модели и ускорение inference: GGUF, GPTQ, AWQ, INT8/INT4
Квантование (quantization) — процесс уменьшения битности весов модели для сокращения размера и ускорения inference. Вместо хранения весов в 32-bit floating point (FP32), мы используем 8-bit integers (INT8) или даже 4-bit (INT4).
Зачем квантование:
Trade-off:
FP32 (полная точность)
↓
FP16 (половина размера, минимальная потеря)
↓
INT8 (4x меньше, небольшая потеря accuracy)
↓
INT4 (8x меньше, заметная но приемлемая потеря)
Full precision → Half precision
# FP32: 32-bit floating point
# Диапазон: ±3.4×10^38
# Точность: ~7 десятичных знаков
# Размер: 4 байта на параметр
# FP16: 16-bit floating point
# Диапазон: ±65504
# Точность: ~3 десятичных знака
# Размер: 2 байта на параметрХарактеристики:
8-bit integer quantization
FP32 веса → INT8 веса
[-2.5, 2.5] → [-128, 127]
scale = (max - min) / 255
quantized = round(weight / scale)
Характеристики:
4-bit integer quantization
FP32 веса → INT4 веса
[-2.5, 2.5] → [-8, 7]
scale = (max - min) / 15
quantized = round(weight / scale)
Характеристики:
Формат для llama.cpp — наиболее популярный для локального запуска.
Уровни квантования GGUF:
| Уровень | Размер (7B) | Accuracy | Speed | RAM |
|---|---|---|---|---|
| Q2_K | 2.5GB | Низкая | Быстро | 3GB |
| Q3_K_S | 3.0GB | Ниже средней | Быстро | 4GB |
| Q3_K_M | 3.3GB | Средняя | Быстро | 4GB |
| Q4_K_S | 3.8GB | Хорошая | Оптимально | 5GB |
| Q4_K_M | 4.2GB | Очень хорошая | Оптимально | 5GB |
| Q5_K_S | 4.5GB | Отличная | Медленнее | 6GB |
| Q5_K_M | 4.8GB | Почти FP16 | Медленнее | 6GB |
| Q6_K | 5.5GB | ~FP16 | Медленно | 7GB |
| Q8_0 | 6.7GB | =FP16 | Медленно | 8GB |
Рекомендации:
# 1. Клонирование llama.cpp
git clone https://github.com/ggerganov/llama.cpp
cd llama.cpp
# 2. Установка зависимостей
pip install -r requirements.txt
# 3. Конвертация HF модели в GGUF
python convert-hf-to-gguf.py \
meta-llama/Llama-2-7b-hf \
--outfile models/llama-2-7b-f16.gguf
# 4. Квантование
# Q4_K_M
./quantize models/llama-2-7b-f16.gguf \
models/llama-2-7b-Q4_K_M.gguf \
Q4_K_M
# Q5_K_M
./quantize models/llama-2-7b-f16.gguf \
models/llama-2-7b-Q5_K_M.gguf \
Q5_K_M# Через llama.cpp Python bindings
import llama_cpp_python as llama
llm = llama.Llama(
model_path="./models/llama-2-7b-Q4_K_M.gguf",
n_ctx=4096, # контекст
n_threads=8, # CPU threads
n_gpu_layers=0 # 0 = CPU, >0 = GPU offload
)
output = llm(
"Расскажи о Python",
max_tokens=256,
temperature=0.7
)
print(output["choices"][0]["text"])# Через CLI llama.cpp
./main -m models/llama-2-7b-Q4_K_M.gguf \
-p "Расскажи о Python" \
-n 256 \
--temp 0.7GPU-оптимизированный формат — для NVIDIA GPU.
Характеристики:
Создание GPTQ модели:
from transformers import AutoTokenizer, AutoModelForCausalLM
from auto_gptq import AutoGPTQForCausalLM, BaseQuantizeConfig
# Загрузка модели
model_name = "meta-llama/Llama-2-7b-hf"
tokenizer = AutoTokenizer.from_pretrained(model_name)
quantize_config = BaseQuantizeConfig(
bits=4, # 4-bit quantization
group_size=128,
damp_percent=0.01,
desc_act=False,
)
# Загрузка для квантования
model = AutoGPTQForCausalLM.from_pretrained(
model_name,
quantize_config=quantize_config
)
# Квантование на примере данных
import json
with open("calibration_data.json") as f:
examples = json.load(f)
model.quantize(examples)
# Сохранение
model.save_quantized("./llama-2-7b-gptq-4bit")Запуск GPTQ:
from auto_gptq import AutoGPTQForCausalLM
model = AutoGPTQForCausalLM.from_quantized(
"./llama-2-7b-gptq-4bit",
device="cuda:0",
use_triton=True
)
# Инференс аналогично transformersУлучшенный GPTQ — учитывает активации для сохранения accuracy.
Характеристики:
Создание AWQ:
# Установка
pip install autoawq
# Квантование
from awq import AutoAWQForCausalLM
model = AutoAWQForCausalLM.from_pretrained(
"meta-llama/Llama-2-7b-hf"
)
model.quantize(
tokenizer,
quant_config={
"zero_point": True,
"q_group_size": 128,
"w_bit": 4,
"version": "GEMM"
}
)
model.save_quantized("./llama-2-7b-awq-4bit")| Формат | CPU | GPU | Размер (7B) | Accuracy | Tools |
|---|---|---|---|---|---|
| GGUF Q4_K_M | ✅ | ✅ | 4.2GB | Хорошая | llama.cpp, Ollama |
| GPTQ 4-bit | ❌ | ✅ | 4.0GB | Очень хорошая | text-gen-webui |
| AWQ 4-bit | ❌ | ✅ | 4.0GB | Отличная | vLLM, text-gen-webui |
| FP16 | ✅ | ✅ | 14GB | 100% | Все |
Рекомендации:
QLoRA fine-tuning — 4-bit quantization для обучения.
from transformers import BitsAndBytesConfig
import torch
# Конфигурация 4-bit quantization
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4", # normal float 4-bit
bnb_4bit_compute_dtype=torch.float16,
bnb_4bit_use_double_quant=True, # double quantization
)
# Загрузка модели
from transformers import AutoModelForCausalLM
model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-2-7b-hf",
quantization_config=bnb_config,
device_map="auto" # automatic device placement
)
# Модель занимает ~5GB вместо 28GB
# Готова к fine-tuning с LoRA8-bit inference:
# 8-bit для inference (меньше потеря accuracy)
model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-2-7b-hf",
load_in_8bit=True,
device_map="auto"
)import ollama
# Запуск модели (автоматическая загрузка)
response = ollama.chat(
model='llama3.1:8b-q4_K_M',
messages=[{
'role': 'user',
'content': 'Напиши функцию на Python для сортировки списка'
}]
)
print(response['message']['content'])
# Список доступных моделей
models = ollama.list()
print(models)
# Удаление модели
ollama.pull('llama3.1:8b-q5_K_M')# CLI
ollama run llama3.1:8b-q4_K_M
ollama list
ollama rm llama3.1:8b-q4_K_M1. Скачать LM Studio (https://lmstudio.ai/)
2. Search models → выбрать модель (Llama, Mistral)
3. Выбрать quantization level (Q4_K_M recommended)
4. Download
5. Chat interface готова
API сервер:
# LM Studio предоставляет OpenAI-compatible API
from openai import OpenAI
client = OpenAI(
base_url="http://localhost:1234/v1",
api_key="not-needed"
)
response = client.chat.completions.create(
model="local-model",
messages=[{"role": "user", "content": "Привет!"}]
)# Установка
pip install vllm
# Запуск сервера с AWQ моделью
python -m vllm.entrypoints.api_server \
--model mistralai/Mistral-7B-Instruct-v0.2 \
--quantization awq \
--port 8000# Клиент
from vllm import LLM, SamplingParams
llm = LLM(
model="mistralai/Mistral-7B-Instruct-v0.2",
quantization="awq"
)
prompts = ["Напиши функцию Python", "Объясни OOP"]
sampling_params = SamplingParams(
temperature=0.7,
max_tokens=256
)
outputs = llm.generate(prompts, sampling_params)
for output in outputs:
print(output.outputs[0].text)| Модель | Формат | Device | Tokens/sec |
|---|---|---|---|
| Llama-2-7B | FP16 | A100 | 150 |
| Llama-2-7B | GGUF Q4_K_M | M2 Max | 45 |
| Llama-2-7B | GGUF Q4_K_M | CPU (8 cores) | 12 |
| Llama-2-7B | GPTQ 4-bit | RTX 4090 | 180 |
| Llama-2-7B | AWQ 4-bit | A100 | 200 |
import psutil
import torch
def get_memory_usage():
process = psutil.Process()
memory = process.memory_info().rss / 1024 / 1024 # MB
gpu_memory = torch.cuda.memory_allocated() / 1024 / 1024 if torch.cuda.is_available() else 0
return f"CPU: {memory:.0f}MB, GPU: {gpu_memory:.0f}MB"
# FP32 7B модель
# CPU: 28000MB, GPU: 0MB
# GGUF Q4_K_M 7B модель
# CPU: 4200MB, GPU: 0MB
# GPTQ 4-bit 7B модель
# CPU: 500MB, GPU: 4000MBНужен ли CPU inference?
├─ Да → GGUF
│ ├─ Есть 16GB+ RAM? → Q5_K_M или Q6_K
│ ├─ Есть 8-16GB RAM? → Q4_K_M (recommended)
│ └─ Есть <8GB RAM? → Q3_K_M
│
└─ Нет (только GPU) → GPTQ/AWQ
├─ NVIDIA GPU 8GB+? → AWQ 4-bit или GPTQ 4-bit
├─ NVIDIA GPU 4-8GB? → GPTQ 4-bit
└─ NVIDIA GPU <4GB? → QLoRA + offloading
Для разработки:
# Ollama с Q4_K_M — баланс скорости и качества
ollama run llama3.1:8b-q4_K_MДля production на GPU:
# vLLM с AWQ — максимальная throughput
llm = LLM(model="mistral-7b", quantization="awq")Для edge devices:
# llama.cpp с Q3_K_M — минимальный размер
llm = llama.Llama(model_path="model-Q3_K_M.gguf", n_threads=4)from fastapi import FastAPI
from pydantic import BaseModel
import llama_cpp_python as llama
app = FastAPI()
# Загрузка квантованной модели
llm = llama.Llama(
model_path="./models/llama-3.1-8b-Q4_K_M.gguf",
n_ctx=4096,
n_threads=8
)
class GenerationRequest(BaseModel):
prompt: str
max_tokens: int = 256
temperature: float = 0.7
class GenerationResponse(BaseModel):
text: str
tokens_per_second: float
@app.post("/generate", response_model=GenerationResponse)
async def generate(request: GenerationRequest):
import time
start = time.time()
output = llm(
request.prompt,
max_tokens=request.max_tokens,
temperature=request.temperature
)
elapsed = time.time() - start
tokens = len(output["choices"][0]["text"].split())
return GenerationResponse(
text=output["choices"][0]["text"],
tokens_per_second=tokens / elapsed
)Квантование позволяет запускать большие модели на потребительском железе. Вы изучили:
В следующей теме вы изучите Локальный запуск LLM — Ollama, llama.cpp, vLLM для production.
Вопросы ещё не добавлены
Вопросы для этой подтемы ещё не добавлены.