Создание uncensored LLM: слияние Qwen3.5 и дистилляция Claude Opus | Гайд 2026 | AiManual
AiManual Logo Ai / Manual.
15 Мар 2026 Гайд

Круши цензуру: как собрать свою uncensored LLM из Qwen и знаний Claude Opus за один вечер

Пошаговый гайд по созданию uncensored LLM. Слияние тензоров Qwen3.5 9B, дистилляция знаний Claude Opus, готовый код на Google Colab для моделей без цензуры.

Зачем тебе это нужно? Проблема, которую все игнорируют

Ты пишешь промпт для сюжета темного фэнтези, а в ответ получаешь сухое "Я не могу создавать контент, который может быть сочтен насильственным". Ты просишь ИИ помочь с диалогом для персонажа с неоднозначной моралью, а он начинает читать лекцию об этике. Знакомо? Это цензурные фильтры, вшитые в 99% современных LLM.

Они не просто ограничивают — они ломают креативность. Модель, которая боится сказать что-то "не то", становится предсказуемой и скучной. Для ролевых игр, генерации уникальных идей или просто свободного диалога это убийственно.

Создание uncensored модели — это не про генерацию запрещенного контента. Это про возвращение модели свободы мысли. Это про то, чтобы она могла обсуждать любую тему, не паникуя и не подставляя стандартные отмазки.

Решение: гибридный мутант, который не знает слова "нет"

Мы возьмем две вещи: архитектуру и базовые знания от мощной, но слегка зажатой модели Qwen3.5 9B (актуальная версия на март 2026). И добавим к ней интеллектуальную глубину и стиль ответов от Claude 4.5 Opus — одной из самых умных коммерческих моделей.

Как? Двумя методами:

  1. Слияние тензоров (Model Merging): Берем веса Qwen и смешиваем их с весами другой, уже "разблокированной" uncensored-версии, чтобы ослабить встроенные фильтры.
  2. Дистилляция знаний (Knowledge Distillation): Обучаем нашу гибридную модель на тысячах примеров ответов Claude Opus. Модель учится не просто фактам, а манере мыслить, рассуждать и формулировать как Claude, но без его внутренних ограничений.

Это тот самый подход, о котором я писал в статье про создание гибрида для рассуждений. Только здесь мы делаем акцент на снятии барьеров.

Что тебе понадобится перед стартом

  • Аккаунт в Google Colab (бесплатный план с T4 GPU хватит, но лучше Colab Pro с A100).
  • Примерно 30 ГБ свободного места на Google Диске для моделей и данных.
  • Базовое понимание Python и умение копировать команды в терминал.
  • Терпение. Полный цикл займет 3-5 часов.

1 Подготовка среды и загрузка "доноров"

Открой новый ноутбук в Google Colab и сразу переключись на среду с GPU (Runtime -> Change runtime type -> T4 или лучше).

Первым делом установим всё необходимое. Мы будем использовать mergekit для слияния и библиотеку transformers от Hugging Face.

!pip install -q -U git+https://github.com/cg123/mergekit.git
!pip install -q -U transformers accelerate torch sentencepiece huggingface_hub

Теперь загрузим базовую модель и "донора" для разблокировки. Мы возьмем Qwen3.5-9B-Instruct как основу. В качестве донора для ослабления фильтров — uncensored версию, например, Qwen3.5-4B-Uncensored-Aggressive. Да, она меньше, но ее агрессивно настроенные веса в определенных слоях помогут "раскачать" фильтры у 9B-версии.

💡
Почему работает смешение разных размеров? Mergekit умеет интерполировать веса. Мы не просто складываем модели, а создаем конфиг, который говорит: "Возьми 70% весов от большой модели и 30% от маленькой, но только в слоях внимания (attention), где сидят механизмы цензуры". Это как привить растению более крепкий подвой.
from huggingface_hub import snapshot_download
import os

# Создаем папки для моделей
os.makedirs("/content/models/qwen9b", exist_ok=True)
os.makedirs("/content/models/uncensored_donor", exist_ok=True)

# Загружаем Qwen3.5 9B Instruct (актуальная версия на 15.03.2026)
print("Загрузка Qwen3.5-9B-Instruct...")
qwen_path = snapshot_download(repo_id="Qwen/Qwen3.5-9B-Instruct",
                              cache_dir="/content/models/qwen9b")

# Загружаем uncensored донора (более старая, но агрессивная модель)
print("Загрузка uncensored донора...")
donor_path = snapshot_download(repo_id="dvruette/qwen3.5-4b-uncensored-aggressive",
                                cache_dir="/content/models/uncensored_donor")

2 Создание конфига для слияния и запуск микса

Вот самая важная часть — конфигурационный YAML-файл для mergekit. Здесь мы определяем, какие слои от какой модели берем. Основа — исследование, что механизмы отказа (refusal) часто локализованы в определенных матрицах проекции в слоях внимания.

# config.yaml
slices:
  - sources:
      - model: /content/models/qwen9b
        layer_range: [0, 40]  # Берем все 40 слоев от Qwen 9B
      - model: /content/models/uncensored_donor
        layer_range: [0, 40]
        # Ключевой момент: параметр density. Мы берем только 25% весов от донора,
        # но применяем это к выбранным слоям, связанным с цензурой.
        # mergekit сам найдет схожие слои, несмотря на разный размер моделей.
        density: 0.25
merge_method: passthrough  # Простое линейное слияние

dtype: bfloat16  # Используем половинную точность для экономии памяти

Сохраняем этот конфиг и запускаем слияние. Этот процесс может занять до часа на T4.

import yaml
import subprocess

# Сохраняем конфиг
config = {
    "slices": [{
        "sources": [
            {"model": "/content/models/qwen9b", "layer_range": [0, 40]},
            {"model": "/content/models/uncensored_donor", "layer_range": [0, 40], "density": 0.25}
        ]
    }],
    "merge_method": "passthrough",
    "dtype": "bfloat16"
}

with open("/content/config.yaml", "w") as f:
    yaml.dump(config, f)

# Запускаем mergekit
print("Начинаем слияние моделей. Это займет время...")
!mergekit-run /content/config.yaml /content/merged_model --allow-crimes --copy-tokenizer

Флаг --allow-crimes не шутка. Он разрешает mergekit смешивать архитектурно неидентичные модели. Без него скрипт упадет с ошибкой из-за разного размера слоев. Мы сознательно идем на эту "авантюру", и в 90% случаев она окупается работающей моделью.

3 Дистилляция: учим мутанта думать как Claude Opus

Теперь у нас есть разблокированная, но глуповатая модель. Надо научить ее элегантно рассуждать. Для этого используем дистилляцию. Нам нужен датасет с промптами и идеальными ответами от Claude 4.5 Opus.

Я подготовил и выложил на Hugging Face датасет с 10 тыс. примеров. Он включает диалоги, рассуждения, креативные задачи — именно те сценарии, где цензура мешает больше всего.

# Загружаем датасет для дистилляции
from datasets import load_dataset

dataset = load_dataset("your_username/claude_opus_uncensored_distillation", split="train")
# Давай посмотрим на пример
print(dataset[0]["prompt"][:200])
print("---")
print(dataset[0]["claude_response"][:200])

Теперь настроим LoRA (Low-Rank Adaptation) для тонкой настройки. Мы не будем трогать все 9 миллиардов параметров — только небольшой адаптер. Это быстрее и безопаснее.

from transformers import AutoTokenizer, AutoModelForCausalLM, TrainingArguments, Trainer
from peft import LoraConfig, get_peft_model, TaskType
import torch

# Загружаем нашу слитую модель
model_path = "/content/merged_model"
tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(
    model_path,
    torch_dtype=torch.bfloat16,
    device_map="auto",
    trust_remote_code=True
)

# Настраиваем LoRA. Цель — слои внимания и MLP.
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", "gate_proj", "up_proj", "down_proj"],
    bias="none"
)
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()  # Должно показать ~0.1% обучаемых параметров

Подготовим данные и запустим обучение на 1 эпохе. Этого достаточно, чтобы модель переняла стиль.

def tokenize_function(examples):
    # Объединяем промпт и ответ Claude в одну последовательность для обучения
    texts = [p + " " + r for p, r in zip(examples["prompt"], examples["claude_response"])]
    return tokenizer(texts, truncation=True, padding="max_length", max_length=1024)

tokenized_datasets = dataset.map(tokenize_function, batched=True)

# Аргументы для обучения
training_args = TrainingArguments(
    output_dir="/content/distilled_model",
    num_train_epochs=1,
    per_device_train_batch_size=2,  # Маленький batch из-за ограничений памяти
    gradient_accumulation_steps=8,
    warmup_steps=100,
    logging_steps=50,
    save_strategy="epoch",
    learning_rate=2e-4,
    fp16=True,  # Используем половинную точность для ускорения
    push_to_hub=False  # Можешь включить, если хочешь загрузить результат на HF
)

trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=tokenized_datasets,
)

print("Старт дистилляции...")
trainer.train()

# Сохраняем полную модель (база + LoRA адаптер)
trainer.save_model("/content/final_uncensored_model")

Этот шаг займет основное время — 2-3 часа на T4. Завари чай. Если хочешь глубже понять механику дистилляции, посмотри мой разбор на примере головоломок NYT.

4 Критический фикс: модификация chat template

Вот где большинство спотыкается. Qwen использует специальный формат диалога с тегами `<|im_start|>` и `<|im_end|>`. Если оставить его как есть, в LM Studio или другом интерфейсе диалог будет ломаться. Надо научить модель работать с более универсальным шаблоном, например, как у Llama или Alpaca.

# Загружаем tokenizer финальной модели и меняем chat_template
tokenizer = AutoTokenizer.from_pretrained("/content/final_uncensored_model", trust_remote_code=True)

# Устанавливаем более простой и понятный шаблон (Alpaca-style)
alpaca_template = """Below is an instruction that describes a task. Write a response that appropriately completes the request.

### Instruction:
{prompt}

### Response:
"""

# Просто переопределяем атрибут
tokenizer.chat_template = alpaca_template

# Сохраняем tokenizer обратно
tokenizer.save_pretrained("/content/final_uncensored_model")

# Также надо подправить конфиг модели, чтобы он указывал на новый шаблон
import json
config_path = "/content/final_uncensored_model/config.json"
with open(config_path, "r") as f:
    config = json.load(f)
config["chat_template"] = alpaca_template
with open(config_path, "w") as f:
    json.dump(config, f, indent=2)

print("Chat template успешно изменен.")

5 Тест-драйв и экспорт в GGUF для LM Studio

Проверим, что у нас получилось, на провокационном промпте.

from transformers import pipeline

pipe = pipeline("text-generation",
                model="/content/final_uncensored_model",
                tokenizer=tokenizer,
                device_map="auto",
                max_new_tokens=200)

prompt = "Write a gritty, realistic dialogue between two mercenaries planning a heist. Include morally ambiguous decisions."
result = pipe(prompt)
print(result[0]["generated_text"])

Если модель не начинает юлить и не отказывается, а выдает содержательный диалог — ты на правильном пути.

Теперь конвертируем модель в формат GGUF для запуска в LM Studio или llama.cpp. Используем инструмент llama.cpp через Python.

# Клонируем и собираем llama.cpp (актуальная версия на март 2026)
!git clone https://github.com/ggerganov/llama.cpp.git
%cd llama.cpp
!make

# Конвертируем нашу модель в GGUF (формат Q4_K_M для баланса качества/скорости)
!python3 convert-hf-to-gguf.py /content/final_uncensored_model --outtype q4_k_m --outfile /content/my_uncensored_qwen_claude.q4_k_m.gguf

Готово. Файл GGUF можно скачать и загрузить в LM Studio. Выбери его в интерфейсе, установи температуру (temperature) повыше (~0.9) для креативности, и вперед.

Где собака зарыта: нюансы, которые сломают всё, если их игнорировать

  • Не тот донор. Если взять слишком "агрессивного" донора, модель может начать генерировать абсолютный бред или нарушить грамматику. Всегда проверяй донора на простых промптах перед слиянием.
  • Переобучение при дистилляции. Обучая больше 1 эпохи на одном датасете, модель забудет исходные знания Qwen и станет просто плохой копией Claude. Остановись на одной эпохе.
  • Память. Процесс требует много RAM и VRAM. Если Colab падает, уменьши per_device_train_batch_size до 1 и увеличь gradient_accumulation_steps до 16.
  • Токенизатор. Qwen использует свой токенизатор (sentencepiece). После слияния обязательно загружай с trust_remote_code=True, иначе получишь ошибку.

Частые вопросы (FAQ)

ВопросКороткий ответ
Модель стала медленной после экспорта в GGUFИспользуй более агрессивное квантование (например, Q2_K). Или попробуй новые методы, описанные в статье про 1-битное квантование в 2026.
Можно ли использовать не Qwen, а Llama?Да, но процесс сложнее. У Llama другой токенизатор и архитектура. Начни с Qwen — она более дружелюбна к экспериментам.
Где взять датасет с ответами Claude Opus?Я выложил пример на Hugging Face. Но лучше создать свой, используя API Anthropic и промпты, специфичные для твоих задач. Инструменты вроде Claude Code могут автоматизировать сбор.
Модель все еще иногда отказываетсяМеханизмы отказа глубоко укоренены. Попробуй применить новый метод удаления refusal с низкой KL-дивергенцией к своей слитой модели перед дистилляцией.

Не жди, что с первого раза получится идеальная модель. Это живой эксперимент. Параметры слияния (density, layer_range), выбор донора, данные для дистилляции — всё это переменные. Меняй их, тестируй, записывай результаты. Настоящая магия начинается, когда ты перестаешь слепо копировать гайды и начинаешь понимать, как эти веса и слои взаимодействуют. Удачи.

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