Фино настройване на LLM с LoRA и QLoRA: Практическо ръководство с Unsloth

Практическо ръководство за фино настройване на LLM с LoRA и QLoRA чрез Unsloth. Стъпка по стъпка с код, оптимизация на хиперпараметри и съвети за избягване на грешки.

Въведение

Ако сте работили с големи езикови модели през последната година, вероятно знаете за какво говоря — моделите стават все по-мощни, но когато се опитате да ги накарате да правят нещо конкретно (да речем, класификация на клиентски запитвания на български или генериране на специализирани медицински отчети), резултатите от обикновения промптинг просто не стигат. Решението? Фино настройване (fine-tuning) — процесът, при който адаптирате предварително обучен модел към вашия конкретен домейн.

Проблемът обаче е, че пълното фино настройване на модел със 7 милиарда параметъра изисква 100–120 GB VRAM. Това е хардуер за над $50,000. Нека бъдем честни — повечето от нас нямат такъв бюджет.

Тук на помощ идват LoRA и QLoRA — две техники, които намаляват тези изисквания драстично, без съществена загуба на качество. А с инструменти като Unsloth, процесът стана 2 пъти по-бърз и с 70% по-малко памет. Лично аз бях скептичен в началото, но след няколко проекта мога да кажа — работи наистина добре.

В тази статия ще преминем през целия процес — от теорията зад LoRA и QLoRA, през практическата имплементация с код, до оптимизиране на хиперпараметри и избягване на най-честите грешки. Не ви трябва скъп GPU клъстер — едно обикновено RTX 4090 (или дори Google Colab) е напълно достатъчно.

Какво е фино настройване и защо е необходимо?

Фино настройването е форма на трансферно обучение (transfer learning). Накратко — вземате модел, който вече е обучен на огромен текстов корпус (стотици милиарди токени) и го дообучавате с по-малък, специализиран набор от данни. Моделът запазва общите си познания за езика, но се адаптира към вашия домейн.

Кога фино настройването има смисъл?

  • Специфичен домейн — юридически, медицински или технически текстове, където терминологията е критична
  • Консистентен формат — когато ви трябва строго определена структура на изхода
  • По-малки и по-бързи модели — фино настроен 7B модел може да надмине GPT-4 на специфичната ви задача, при значително по-ниска цена за inference (да, наистина)
  • Поверителност на данните — данните остават при вас, нищо не се изпраща към външен API
  • Намаляване на халюцинациите — моделът научава фактически точни отговори за вашия домейн

Алтернативите — промптинг и RAG (Retrieval-Augmented Generation) — имат своето място, но не решават всеки проблем. Промптингът е ограничен от контекстния прозорец и не може да промени вътрешното поведение на модела. RAG помага за достъп до актуална информация, но не подобрява самото разбиране на модела. Финото настройване е третият стълб — и честно казано, комбинацията от трите обикновено дава най-добрите резултати.

LoRA: Low-Rank Adaptation обяснено просто

Пълното фино настройване променя всички параметри на модела. При 7B модел това означава актуализиране на 7 милиарда числа — огромно натоварване.

LoRA (Low-Rank Adaptation) подхожда по съвсем различен начин. Вместо да променяте оригиналните тегла, вие:

  1. Замразявате всички оригинални параметри на модела
  2. Добавяте две малки матрици (A и B) към всеки целеви слой
  3. Обучавате само тези малки матрици — обикновено около 1% от общия брой параметри

Математически, вместо да обновявате цялата матрица на теглата W (размер d×d), LoRA я апроксимира чрез две матрици с нисък ранг: W + ΔW, където ΔW = A × B. Матрица A е с размер d×r, а B е с размер r×d, където r (рангът) обикновено е между 8 и 64 — драстично по-малко от d (който може да е хиляди).

И какво всъщност означава това на практика?

  • Памет: Вместо пълно копие на модела, съхранявате само малките LoRA адаптери (обикновено 10–100 MB)
  • Скорост: По-малко параметри за обучение = по-бърза тренировка
  • Гъвкавост: Можете да имате различни LoRA адаптери за различни задачи и да ги превключвате динамично — нещо като модулна система
  • Качество: LoRA постига 90–99% от качеството на пълното фино настройване, в зависимост от задачата и хиперпараметрите

QLoRA: Квантизация + LoRA за максимална ефективност

Ако LoRA е голяма стъпка напред, QLoRA (Quantized LoRA) отива още по-далеч. Идеята е елегантно проста: базовият модел се зарежда в 4-битова прецизност (вместо стандартната 16-битова), а LoRA адаптерите се тренират в по-висока прецизност.

QLoRA комбинира три ключови иновации:

  • 4-bit NormalFloat (NF4) квантизация — специално оптимизирана за нормално разпределени тегла на невронни мрежи, намалява паметта с около 75% спрямо 16-битово представяне
  • Двойна квантизация (Double Quantization) — дори константите на квантизацията се квантизират, спестявайки допълнителна памет (квантизация на квантизацията — звучи забавно, но работи)
  • Странирани оптимизатори (Paged Optimizers) — при пикове в паметта, състоянието на оптимизатора се прехвърля към CPU, предотвратявайки out-of-memory грешки

Резултатът? Модел с 65 милиарда параметъра може да се фино настрои на един GPU с 48 GB VRAM. А 7B модел — на обикновена потребителска видеокарта с 16 GB VRAM.

Изследванията показват, че QLoRA постига качество, сравнимо с пълното 16-битово фино настройване на повечето бенчмаркове. Това е доста впечатляващо, като се има предвид разликата в ресурсите.

LoRA vs QLoRA: Кога кое да изберете?

Изборът между LoRA и QLoRA зависи от няколко фактора:

КритерийLoRA (16-bit)QLoRA (4-bit)
Използвана VRAM~4× повече~4× по-малко
Скорост на тренировкаПо-бърза (~30–40%)По-бавна (квантизация/деквантизация)
Качество90–99% от пълно FT80–95% от пълно FT
Минимален GPU24 GB (за 7B)16 GB (за 7B)
Препоръчителен заКогато имате достатъчно VRAMОграничен бюджет / по-големи модели

Правило от практиката: Ако имате GPU с 24+ GB VRAM и работите с модел до 13B параметъра — започнете с LoRA. За по-големи модели или ограничен хардуер — QLoRA е очевидният избор. Ако не сте сигурни — просто тръгнете с QLoRA, по-рядко ще сгрешите.

Хардуерни изисквания и реални разходи

Едно от големите предимства на LoRA/QLoRA е достъпността. Ето конкретни числа за 2026 г.:

МетодМодел (7B)Модел (13B)Модел (70B)
Пълно FT~120 GB VRAM~240 GB VRAM~1.2 TB VRAM
LoRA (16-bit)~24 GB VRAM~40 GB VRAM~160 GB VRAM
QLoRA (4-bit)~6–8 GB VRAM~12–16 GB VRAM~24–48 GB VRAM

Какво означава това на практика? С QLoRA и Unsloth, 7B модел може да се тренира на RTX 3060 (12 GB). А 13B модел — на RTX 4090 (24 GB). За 70B модели ще ви трябва A100 или L40S, но те са достъпни в облачни услуги.

Ориентировъчни разходи при облачни GPU:

  • Google Colab Pro (~$10/месец) — достатъчен за QLoRA на 7B модели с безплатен T4 или A100
  • RunPod / Lambda Labs — RTX 4090 от ~$0.40/час, A100 80 GB от ~$1.50/час
  • Пълно фино настройване на 13B на облачен A100: ~$50–100 за един тренировъчен цикъл
  • QLoRA на същия модел: ~$5–15 — намаление с порядък

Разликата е огромна. И това е причината, поради която QLoRA стана толкова популярен.

Стъпка по стъпка: Фино настройване с Unsloth и QLoRA

Unsloth е една от най-популярните библиотеки с отворен код за фино настройване на LLM през 2026 г. Тя предлага 2× по-бързо тренировка, 70% по-малко VRAM и нулева загуба на точност спрямо стандартния QLoRA подход. Работи чрез оптимизирани Triton ядра и ръчно деривирана обратна пропагация — но за нас като потребители API-то е изключително просто.

Нека минем стъпка по стъпка през целия процес.

Стъпка 1: Инсталация на зависимости

# В Google Colab или локална среда
pip install unsloth
pip install --upgrade trl transformers datasets accelerate bitsandbytes

Ако работите локално, уверете се, че имате инсталиран PyTorch с CUDA поддръжка. Unsloth поддържа NVIDIA GPU от архитектура Ampere (RTX 3000 серия) нагоре.

Стъпка 2: Зареждане на модел с 4-битова квантизация

from unsloth import FastLanguageModel
import torch

# Конфигурация
max_seq_length = 2048  # Максимална дължина на последователност
load_in_4bit = True     # QLoRA 4-bit квантизация

# Зареждане на модел
model, tokenizer = FastLanguageModel.from_pretrained(
    model_name="unsloth/Llama-3.3-70B-Instruct-bnb-4bit",  # или по-малък модел
    max_seq_length=max_seq_length,
    load_in_4bit=load_in_4bit,
    dtype=None,  # Автоматично определяне (float16 за повечето GPU)
)

print(f"Модел зареден! Параметри: {model.num_parameters():,}")

Unsloth автоматично открива вашия GPU и оптимизира зареждането. Ако работите с по-малък GPU, опитайте с unsloth/Llama-3.2-3B-Instruct-bnb-4bit или unsloth/Mistral-7B-Instruct-v0.3-bnb-4bit.

Стъпка 3: Конфигуриране на LoRA адаптерите

# Добавяне на LoRA адаптери
model = FastLanguageModel.get_peft_model(
    model,
    r=16,                    # Ранг на LoRA — балансът между качество и ефективност
    lora_alpha=32,           # Alpha = 2 * r е добра начална стойност
    lora_dropout=0.05,       # Dropout за предотвратяване на overfitting
    target_modules=[         # Слоеве, към които прилагаме LoRA
        "q_proj", "k_proj", "v_proj", "o_proj",  # Attention слоеве
        "gate_proj", "up_proj", "down_proj",       # MLP слоеве
    ],
    bias="none",
    use_gradient_checkpointing="unsloth",  # Спестява още 30% памет
    random_state=42,
)

# Проверка колко параметъра обучаваме
trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad)
total_params = sum(p.numel() for p in model.parameters())
print(f"Обучаеми параметри: {trainable_params:,} ({100*trainable_params/total_params:.2f}%)")

Типично ще видите, че обучаемите параметри са под 2% от общия брой. Тоест при 7B модел обучавате само ~100–200 милиона параметъра. Остатъкът си стои замразен и непроменен.

Стъпка 4: Подготовка на набора от данни

from datasets import load_dataset

# Зареждане на dataset (примерен — заменете с вашия)
dataset = load_dataset("yahma/alpaca-cleaned", split="train")

# Дефиниране на шаблон за промпт
prompt_template = """### Инструкция:
{instruction}

### Вход:
{input}

### Отговор:
{output}"""

# Форматиране на данните
def format_prompts(examples):
    instructions = examples["instruction"]
    inputs = examples["input"]
    outputs = examples["output"]

    texts = []
    for instruction, inp, output in zip(instructions, inputs, outputs):
        text = prompt_template.format(
            instruction=instruction,
            input=inp if inp else "Няма допълнителен вход.",
            output=output,
        )
        texts.append(text + tokenizer.eos_token)

    return {"text": texts}

dataset = dataset.map(format_prompts, batched=True)
print(f"Подготвени {len(dataset)} примера за тренировка.")

Важно: Качеството на данните е критичен фактор — и не мога да подчертая това достатъчно. Предпочитайте по-малък, но чист и релевантен dataset пред голям и шумен. Дори 1,000–5,000 качествени примера могат да дадат отлични резултати за конкретна задача.

Стъпка 5: Конфигуриране и стартиране на тренировката

from trl import SFTTrainer
from transformers import TrainingArguments

trainer = SFTTrainer(
    model=model,
    tokenizer=tokenizer,
    train_dataset=dataset,
    dataset_text_field="text",
    max_seq_length=max_seq_length,
    dataset_num_proc=2,
    packing=True,  # Обединява кратки примери за ефективност
    args=TrainingArguments(
        per_device_train_batch_size=2,
        gradient_accumulation_steps=4,  # Ефективен batch size = 2 * 4 = 8
        warmup_steps=10,
        num_train_epochs=3,
        learning_rate=2e-4,
        fp16=not torch.cuda.is_bf16_supported(),
        bf16=torch.cuda.is_bf16_supported(),
        logging_steps=10,
        optim="adamw_8bit",
        weight_decay=0.01,
        lr_scheduler_type="cosine",
        seed=42,
        output_dir="outputs",
        save_strategy="steps",
        save_steps=100,
    ),
)

# Стартиране на тренировката
print("Стартиране на тренировката...")
trainer_stats = trainer.train()
print(f"Тренировката завърши! Време: {trainer_stats.metrics['train_runtime']:.0f}s")

При типичен dataset от 50,000 примера, тренировка за 3 епохи на RTX 4090 с Unsloth отнема приблизително 2–4 часа. Без Unsloth, същото би отнело 5–8 часа. Разликата е осезаема.

Стъпка 6: Тестване и експорт на модела

# Тестване на модела
FastLanguageModel.for_inference(model)

inputs = tokenizer(
    "### Инструкция:\nОбясни какво е machine learning на прост език.\n\n### Отговор:\n",
    return_tensors="pt"
).to("cuda")

outputs = model.generate(
    **inputs,
    max_new_tokens=256,
    temperature=0.7,
    top_p=0.9,
)
print(tokenizer.decode(outputs[0], skip_special_tokens=True))

# Запазване на LoRA адаптерите
model.save_pretrained("my-finetuned-model-lora")
tokenizer.save_pretrained("my-finetuned-model-lora")

# Експорт в GGUF формат за Ollama / llama.cpp
model.save_pretrained_gguf(
    "my-finetuned-model-gguf",
    tokenizer,
    quantization_method="q4_k_m",  # Добър баланс качество/размер
)
print("Модел експортиран в GGUF формат!")

След експорт в GGUF формат, можете да заредите модела в Ollama или llama.cpp и да го ползвате локално без GPU. Това е изключително удобно за deployment — буквално стартирате с една команда.

Оптимизиране на ключови хиперпараметри

Добре, стигнахме до частта, която прави разликата между „работи някак" и „работи отлично". Изборът на правилни хиперпараметри е критичен. Ето конкретни препоръки, базирани на стотици експерименти.

Ранг (r) на LoRA

Рангът определя размера на LoRA матриците — по-висок ранг означава повече обучаеми параметри и потенциално по-добро качество, но и по-висок риск от overfitting.

  • r = 8: Добър за прости задачи (класификация, анализ на тоналност)
  • r = 16: Балансирана стойност за повечето задачи — препоръчителен старт
  • r = 32–64: За сложни задачи, изискващи значителна адаптация (генериране на код, превод)
  • r > 64: Рядко необходимо; увеличава риска от overfitting без пропорционално подобрение

Alpha (lora_alpha)

Alpha контролира скалирането на LoRA актуализациите. Правилото е: alpha = 2 × r е добра начална стойност. Ако alpha/r е твърде високо, обучението може да стане нестабилно. Ако е твърде ниско — моделът почти не се адаптира. Намерете баланса.

Скорост на обучение (Learning Rate)

  • Начална стойност: 2e-4 за повечето случаи с LoRA/QLoRA
  • По-ниска (5e-5 – 1e-4): Ако моделът вече е близо до желаното поведение
  • По-висока (3e-4 – 5e-4): Ако dataset-ът е много различен от предварителното обучение
  • Scheduler: Cosine scheduler с warmup обикновено дава най-добри резултати

Брой епохи

За повечето instruction-based задачи, 1–3 епохи са достатъчни. Повече от 3 рядко дават значимо подобрение и увеличават риска от overfitting. Наблюдавайте validation loss — ако започне да расте, докато training loss намалява, това е сигнал да спрете.

Целеви модули (Target Modules)

Прилагането на LoRA към повече слоеве подобрява качеството. Препоръчителната конфигурация включва както attention, така и MLP слоевете:

target_modules = [
    "q_proj", "k_proj", "v_proj", "o_proj",   # Attention
    "gate_proj", "up_proj", "down_proj",        # MLP
]

Ако паметта е критична, можете да се ограничите само до attention слоевете (q_proj и v_proj), но ще загубите от качество.

Най-чести грешки и как да ги избегнете

Тази секция е може би най-важната в цялата статия. След работа с десетки проекти за фино настройване, ето грешките, които виждам отново и отново.

1. Катастрофално забравяне (Catastrophic Forgetting)

Моделът научава новата задача, но „забравя" общите си познания. Случвало ми се е — моделът перфектно класифицира домейн-специфични запитвания, но изведнъж не може да води нормален разговор. Решения:

  • Използвайте по-нисък learning rate (1e-5 – 5e-5)
  • Включете разнообразни данни в training set-а, не само domain-specific примери
  • LoRA по своята природа защитава от catastrophic forgetting, тъй като оригиналните тегла остават замразени — това е едно от основните й предимства

2. Overfitting при малки datasets

При dataset с по-малко от 1,000 примера моделът бързо запомня данните наизуст. Контрамерки:

  • Намалете броя епохи до 1–2
  • Увеличете lora_dropout до 0.1
  • Намалете ранга (r)
  • Използвайте early stopping — спрете, когато validation loss започне да расте

3. Неправилен формат на данните

Тази грешка е изненадващо честа. Моделът очаква определен формат на промптите — ако тренировъчните данни не следват формата, който ще се използва при inference, резултатите ще бъдат непоследователни. Винаги използвайте същия шаблон при тренировка и при inference. Без изключения.

4. Прекалено висок learning rate

Симптоми: training loss е нестабилен, скача нагоре-надолу или направо дивергира. Решението е просто — намалете learning rate и увеличете warmup стъпките. Ако loss не намалява стабилно след 50–100 стъпки, learning rate-ът вероятно е прекалено висок.

5. Пренебрегване на оценката

Ниският training loss не означава, че моделът е добър. Сериозно. Винаги отделяйте validation set (поне 10% от данните) и оценявайте генерираните отговори качествено, не само по метрики. Ръчният преглед на 50–100 примера може да разкрие проблеми, които автоматичните метрики пропускат напълно.

Разширени техники: Отвъд основното фино настройване

GRPO и подравняване чрез подкрепящо обучение

През 2026 г. GRPO (Group Relative Policy Optimization) стана особено популярен за създаване на модели, които „разсъждават" преди да отговорят — по подобие на DeepSeek-R1. Unsloth поддържа GRPO с едва 5 GB VRAM, което означава, че можете да създадете reasoning модел дори на потребителски хардуер. Доста вълнуващо развитие.

DPO — Direct Preference Optimization

DPO позволява подравняване на модела с потребителски предпочитания без нужда от reward model. Данните съдържат двойки — предпочитан и отхвърлен отговор. Моделът научава да генерира отговори, които са по-близо до предпочитаните. По-просто е от RLHF и резултатите са сравними.

Spectrum — Селективно фино настройване на слоеве

Spectrum е по-нов метод, който анализира Signal-to-Noise Ratio (SNR) на отделните слоеве и фино настройва само най-информативните. Все още е сравнително нов, но ранните резултати са обещаващи — качество, сравнимо с пълното фино настройване, при още по-малко ресурси.

Производствено внедряване на фино настроения модел

Добре, тренирали сте модела. Сега трябва да стигне до потребителите. Ето основните опции:

  • Ollama + GGUF: Най-лесният начин за локален deployment. Експортирайте модела в GGUF формат и го стартирайте с ollama run my-model
  • vLLM / SGLang: За high-throughput сценарии с множество едновременни потребители. Поддържа continuous batching и tensor parallelism
  • Text Generation Inference (TGI): Решение на Hugging Face за production deployment с оптимизирана производителност
  • API обвивка: FastAPI или аналогичен framework, който извиква модела и предлага REST API за интеграция с други системи

Ключов съвет: винаги тествайте модела интензивно преди production deployment. Създайте набор от тестови случаи, включително edge cases и потенциално проблемни входове. Автоматизирайте тези тестове в CI/CD pipeline — ще ви спести много главоболия по-късно.

Често задавани въпроси

Колко данни са необходими за фино настройване на LLM?

Зависи от задачата, но с LoRA/QLoRA можете да постигнете значими подобрения дори с 500–1,000 качествени примера. За по-сложни задачи, 5,000–50,000 примера дават отлични резултати. Ключовото е качеството, не количеството — 1,000 внимателно подбрани примера често превъзхождат 100,000 шумни такива.

Може ли да се направи фино настройване на LLM без GPU?

Технически е възможно на CPU, но е изключително бавно и непрактично за модели над 1B параметъра. Най-достъпният вариант е Google Colab, който предоставя безплатен достъп до T4 GPU (16 GB VRAM) — достатъчен за QLoRA на модели до 7B. Colab Pro ($10/месец) дава достъп до A100 за по-големи модели.

Каква е разликата между фино настройване и RAG?

Фино настройването промeня вътрешното знание и поведение на модела — идеално за стил, формат и доменна експертиза. RAG добавя външна, актуална информация при всяка заявка — идеално за достъп до променящи се данни. Двете техники не са взаимно изключващи се и комбинацията им (фино настроен модел + RAG) често дава най-добрите резултати.

Колко време отнема фино настройването с QLoRA?

При QLoRA с Unsloth на RTX 4090, 7B модел с 10,000 примера за 3 епохи се тренира за приблизително 1–2 часа. Без Unsloth, същото отнема 3–5 часа. За 13B модел — удвоете тези числа. Времето зависи от дължината на последователностите, batch size и конкретния хардуер.

Безопасно ли е да се използва фино настроен модел в production?

Да, при спазване на добрите практики. Ключови мерки: оценка за bias и токсичност, тестване с adversarial входове, мониторинг на изхода в production и добавяне на guardrails (филтри за безопасност). NVIDIA NeMo Guardrails и LLM Guard са популярни инструменти за тази цел. Не пропускайте safety evaluation преди пускане в production — не си струва рискът.

За Автора Editorial Team

Our team of expert writers and editors.