Textual Inversion для Stable Diffusion
Textual Inversion обучает новый токен в текстовом пространстве CLIP, который «кодирует» конкретный субъект или стиль. Самый лёгкий метод персонализации SD: файл embedding весит 50–100 KB, обучается за 30–60 минут, применяется как обычное слово в промпте.
Принцип работы
Textual Inversion не меняет веса модели — он находит новый вектор в CLIP embedding space, который наилучшим образом описывает обучающие изображения. Токен <my-style> добавляется в словарь и используется как обычное слово.
from diffusers import StableDiffusionPipeline
import torch
# Обучение через diffusers скрипт
# accelerate launch textual_inversion.py \
# --pretrained_model_name_or_path="runwayml/stable-diffusion-v1-5" \
# --train_data_dir="./ti_images" \
# --learnable_property="style" \ # style или object
# --placeholder_token="<mystyle>" \
# --initializer_token="painting" \
# --resolution=512 \
# --train_batch_size=1 \
# --max_train_steps=3000 \
# --learning_rate=5.0e-04 \
# --output_dir="./ti_output"
# Применение обученного embedding
pipe = StableDiffusionPipeline.from_pretrained(
"runwayml/stable-diffusion-v1-5",
torch_dtype=torch.float16
).to("cuda")
# Загружаем embedding
pipe.load_textual_inversion("./ti_output/learned_embeds.bin")
# Используем токен в промпте
image = pipe(
"a portrait in <mystyle> style, dramatic lighting",
num_inference_steps=50,
guidance_scale=7.5
).images[0]
Python обучение
from diffusers import StableDiffusionPipeline
from transformers import CLIPTextModel, CLIPTokenizer
import torch
from torch.optim import AdamW
from torch.utils.data import Dataset, DataLoader
from PIL import Image
import os
class TextualInversionDataset(Dataset):
def __init__(self, images_dir: str, tokenizer, placeholder_token: str, size: int = 512):
self.images = [os.path.join(images_dir, f) for f in os.listdir(images_dir)
if f.endswith((".jpg", ".png", ".webp"))]
self.tokenizer = tokenizer
self.placeholder_token = placeholder_token
self.size = size
def __len__(self):
return len(self.images)
def __getitem__(self, idx):
img = Image.open(self.images[idx]).convert("RGB").resize((self.size, self.size))
# Простые промпты с placeholder токеном
prompts = [
f"a photo of {self.placeholder_token}",
f"{self.placeholder_token} in the scene",
f"an image featuring {self.placeholder_token}"
]
import random
prompt = random.choice(prompts)
return {"image": img, "prompt": prompt}
Сравнение методов персонализации
| Метод | Размер файла | Время обучения | Качество | Совместимость |
|---|---|---|---|---|
| Textual Inversion | 50–100 KB | 30–60 мин | Умеренное | Любая SD |
| LoRA | 10–150 MB | 30–120 мин | Хорошее | Совместимая архитектура |
| DreamBooth (full) | 4–7 GB | 60–120 мин | Отличное | Конкретная версия |
| DreamBooth + LoRA | 50–150 MB | 30–60 мин | Хорошее | Совместимая |
Textual Inversion выигрывает по размеру и портативности: embedding можно поделиться одним файлом в 100 KB. Для точной передачи лиц или сложных стилей — LoRA или DreamBooth LoRA предпочтительнее. Сроки обучения одного embedding — 30–60 минут, интеграция в pipeline — 1 час.







