Перейти к содержанию
Новое AiManual теперь в MAX Подписаться
Публикация AiManual

Fine-tuning Qwen2-0.5B для автоматизации задач: полный гайд с LoRA, квантованием GGUF и запуском на CPU

Пошаговый гайд по fine-tuning Qwen2-0.5B с помощью LoRA, квантованию в формат GGUF и запуску на обычном CPU для автоматизации задач. Актуально на март 2026.

Коротко

Что будет в материале

  1. 01

    Почему все еще пытаются запустить 7B модели для парсинга логов?

  2. 02

    Где все ломается: частые косяки

  3. 03

    А что если нужно больше скорости?

  4. 04

    Итог: что у нас в руках?

Почему все еще пытаются запустить 7B модели для парсинга логов?

Это безумие. Каждый день вижу, как люди пытаются запихнуть Qwen3.5 или Llama 7B на свой VPS с 2 ядрами и 4 ГБ RAM, а потом жалуются, что инференс занимает минуту на токен. Задача-то простая: прочитать лог Nginx, выделить IP, URL и статус. Или классифицировать тикет в «Срочно» и «Можно подождать». Для этого не нужен ChatGPT уровня. Нужна маленькая, злая, заточенная под конкретную работу модель.

Вот вам рабочий рецепт на 2026 год. Берем Qwen2-0.5B – одну из самых сбалансированных tiny-моделей по состоянию на 19 марта 2026 года. Учим ее на своих данных методом LoRA, чтобы не перегружать память. Затем квантуем в GGUF формат (берем Q4_K_M, и не спорьте). И запускаем на любом Intel или AMD процессоре через llama.cpp. Результат: модель отвечает за секунды, жрет 500 МБ ОЗУ и делает ровно то, что вы просили.

Погнали разбирать по косточкам.

1 Зачем Qwen2-0.5B и что с ней вообще можно сделать?

Qwen2-0.5B – это 500 миллионов параметров. Для сравнения, Qwen2.5-0.5B уже устарел, а свежие модели серии 2 показывают лучшую стабильность в llama.cpp (вспомните ту проблему с бессмыслицей после нескольких ответов). Ее хватит для:

  • Классификации текстов (спам/не спам, срочность, тематика).
  • Извлечения структурированных данных из логов, писем, тикетов.
  • Генерации простых шаблонных ответов (ответы на FAQ, комментарии).
  • Базового анализа настроения (positive/neutral/negative).

Ключевое – ее можно дообучить на 100-200 примерах и получить эксперта в вашей узкой области. И все это на коленке, без A100.

Актуальность на 19.03.2026: На Hugging Face присутствуют как оригинальная Qwen2-0.5B, так и более свежие инкарнации. Для гайда используем базовую версию `Qwen/Qwen2-0.5B`. Убедитесь, что не скачиваете устаревшие чекпоинты 2024 года – у них другой токенизатор и могут быть проблемы с конвертацией.

2 Подготовка поля боя: железо и софт

Вам не нужен GPU. Серьезно. Для обучения LoRA хватит CPU, но процесс будет медленным. Если есть возможность арендовать GPU на час – сделайте это. Я пользуюсь Vultr (партнерская ссылка) с инстансом на RTX 4090. Обучение на 1000 примеров занимает 15 минут и стоит копейки.

Что ставим локально:

# Базовые пакеты
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu
pip install transformers==4.45.0 peft==0.12.0 datasets accelerate
pip install sentencepiece protobuf

# Для конвертации в GGUF
git clone https://github.com/ggerganov/llama.cpp
cd llama.cpp && make

# Убедитесь, что у вас llama.cpp версии не ниже 0.5.7 (актуально на март 2026)
# В ней исправлены критические баги с KV cache для Qwen.

3 Готовим датасет. Не так, как все

Типичная ошибка – пытаться скормить модели сырые логи или CSV. Она сойдет с ума. Данные нужно привести к формату диалога. Qwen2 ожидает промпт в формате `<|im_start|>user\n{вопрос}<|im_end|>\n<|im_start|>assistant\n{ответ}<|im_end|>`.

Пример для задачи извлечения IP из лога:

{
  "messages": [
    { "role": "user", "content": "Извлеки IP адрес из строки лога: 192.168.1.1 - - [19/Mar/2026:10:15:32 +0000] \"GET /api/test HTTP/1.1\" 200 1234" },
    { "role": "assistant", "content": "{ \"ip\": \"192.168.1.1\" }" }
  ]
}

Соберите 200-500 таких примеров. Сохраните в JSONL файл. Этого достаточно для LoRA. Если примеров меньше 100, модель будет переобучаться на шум.

4 Код тонкой настройки LoRA. Без воды

Вот скрипт обучения. Обратите внимание на параметры: `lora_r=16`, `lora_alpha=32`. Для tiny-моделей не нужно задирать `r` выше 32 – вы проучите не адаптеры, а весь вес модели.

from transformers import AutoModelForCausalLM, AutoTokenizer, TrainingArguments
from peft import LoraConfig, get_peft_model, TaskType
from trl import SFTTrainer
from datasets import load_dataset
import torch

model_name = "Qwen/Qwen2-0.5B"
tokenizer = AutoTokenizer.from_pretrained(model_name, trust_remote_code=True)
tokenizer.pad_token = tokenizer.eos_token

model = AutoModelForCausalLM.from_pretrained(
    model_name,
    torch_dtype=torch.float16,
    device_map="auto",
    trust_remote_code=True
)

# Конфиг LoRA
lora_config = LoraConfig(
    task_type=TaskType.CAUSAL_LM,
    r=16,
    lora_alpha=32,
    lora_dropout=0.05,
    target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],
    bias="none"
)
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()  # Должно показать ~1% обучаемых параметров

dataset = load_dataset("json", data_files={"train": "train.jsonl"})

def format_chat_template(example):
    text = tokenizer.apply_chat_template(example["messages"], tokenize=False)
    return {"text": text}

dataset = dataset.map(format_chat_template)

training_args = TrainingArguments(
    output_dir="./qwen2-0.5b-lora-automation",
    num_train_epochs=5,
    per_device_train_batch_size=2,
    gradient_accumulation_steps=4,
    warmup_steps=50,
    logging_steps=10,
    save_strategy="epoch",
    learning_rate=2e-4,
    fp16=True,
    optim="adamw_8bit"
)

trainer = SFTTrainer(
    model=model,
    args=training_args,
    train_dataset=dataset["train"],
    max_seq_length=1024,
    tokenizer=tokenizer,
    packing=True
)

trainer.train()
trainer.save_model("qwen2-0.5b-lora-automation-final")

Запускаем. Если на GPU – летает. Если на CPU – идите выпейте чай. Минут 40.

Осторожно! Не используйте старые версии `peft` или `transformers`. В версиях начала 2025 года был баг с загрузкой весов LoRA для Qwen2, который приводил к полному игнорированию адаптеров. Вы обучаете, сохраняете, а модель ведет себя как базовая. Проверьте версии.

5 Слияние LoRA с базовой моделью и квантование в GGUF

Обученные адаптеры – это всего 5-10 МБ. Но для запуска в llama.cpp нужно объединить их с базовой моделью и сконвертировать в GGUF.

Сначала склеиваем:

from peft import PeftModel
base_model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen2-0.5B", torch_dtype=torch.float16)
lora_model = PeftModel.from_pretrained(base_model, "./qwen2-0.5b-lora-automation-final")
merged_model = lora_model.merge_and_unload()
merged_model.save_pretrained("./qwen2-0.5b-merged", safe_serialization=True)
tokenizer.save_pretrained("./qwen2-0.5b-merged")

Теперь конвертируем в GGUF. Используем скрипт конвертации из llama.cpp. Важно: на март 2026 года скрипт поддерживает Qwen2 из коробки.

cd llama.cpp
python convert-hf-to-gguf.py ../qwen2-0.5b-merged --outtype q4_k_m

# Ждем появления файла qwen2-0.5b-merged-q4_k_m.gguf
# Квантование Q4_K_M – оптимальный выбор для CPU.
# Q2_K слишком грубое, Q8 – избыточно для 0.5B модели.

Файл на выходе будет весить около 350-400 МБ. Это то, что мы будем запускать.

6 Запуск на CPU и интеграция в скрипты

Собираем llama.cpp с поддержкой AVX2 (если процессор Intel старше 5 лет, используйте `make LLAMA_NO_AVX2=1`).

Базовый запуск для теста:

./main -m ./qwen2-0.5b-merged-q4_k_m.gguf \
  -p "<|im_start|>user\nИзвлеки IP из лога: 203.0.113.5 - - [19/Mar/2026:12:34:56 +0000] \"POST /login HTTP/1.1\" 404 -<|im_end|>\n<|im_start|>assistant\n" \
  -n 50 -t 4 --cache-type-k bf16

Ключевые флаги:

  • `-t 4` – количество потоков CPU. Ставьте по количеству физических ядер.
  • `--cache-type-k bf16` – обязательно. Без этого через несколько запросов модель начнет нести чушь, как это бывало с Qwen3.5.
  • `-c 1024` – размер контекста. Для 0.5B модели больше 2048 не нужно – память съест, а толку ноль.

Для продакшена оберните это в простой Python скрипт с subprocess. Модель отвечает за 1-3 секунды на одном ядре старого Xeon.

Где все ломается: частые косяки

1. Модель выдает рандомные символы после конвертации. Вы забыли `--cache-type-k bf16`. Или использовали старую версию llama.cpp, где не зафиксили проблему с KV cache для Qwen. Качайте свежую.

2. LoRA не влияет на вывод. Вы не склеили адаптеры с моделью перед конвертацией в GGUF. Или использовали не те `target_modules`. Для Qwen2 всегда указывайте проекции q, k, v, o.

3. Скорость инференса 1 токен в минуту. Вы запустили модель без `-t` флага, и она использует одно ядро. Укажите `-t` по количеству физических ядер. И не берите квантование выше Q4_K_M для CPU – Q6_K и Q8_K будут медленнее без видимой пользы.

4. Модель не понимает формат промпта. Вы не использовали `apply_chat_template` при подготовке датасета или в инференсе. Qwen2 строго следит за структурой диалога.

А что если нужно больше скорости?

Встройте модель в пайплайн как сервис. Используйте `llama.cpp` серверный режим (`./server`) и отправляйте запросы по HTTP. Один инстанс на 4 ядрах может обрабатывать 5-10 запросов в минуту – для автоматизации внутренних задач хватит за глаза.

Или посмотрите в сторону замены облачного LLM на Qwen3-0.6B – принцип тот же, но модель чуть мощнее.

💡
Лайфхак: Если ваша задача – только классификация (например, определить срочность тикета), не заставляйте модель генерировать текст. Настройте вывод как logits для определенных токенов ("Срочно", "Не срочно") и берите аргмакс. Это ускорит работу в разы. В llama.cpp для этого есть флаг `--logit-bias`.

Итог: что у нас в руках?

Вы получаете модель-специалиста размером с полгигабайта, которая:

  • Работает на любом процессоре, выпущенном за последние 10 лет.
  • Не требует интернета и не шлет ваши логи в облако.
  • Выполняет четко поставленную задачу с точностью 85-95% (если датасет был хорошим).
  • Интегрируется в bash-скрипты, Python-программы и CI/CD пайплайны.

Это не замена GPT-5. Это рабочий инструмент, как молоток или шестигранник. Он делает одну операцию, но делает ее дешево, быстро и без лишней мишуры. Остальное – уже от лукавого.

Подписаться на канал