75 картинок ablation: как Reddit-критика заставила меня переосмыслить FLUX-LoRA пайплайн. ablation.. ablation. fastapi.. ablation. fastapi. flux.. ablation. fastapi. flux. lora.. ablation. fastapi. flux. lora. matchbox.. ablation. fastapi. flux. lora. matchbox. pinock.. ablation. fastapi. flux. lora. matchbox. pinock. SQLite.. ablation. fastapi. flux. lora. matchbox. pinock. SQLite. stablediffusion.. ablation. fastapi. flux. lora. matchbox. pinock. SQLite. stablediffusion. vast.

TL;DR. Запустил pinock.io — бесплатную ленту AI-генерации животных в стиле советских спичечных коробков. Под капотом FLUX.2-klein + кастомная LoRA + двухпроходный «sandwich»-пайплайн. Получил детальный технический комментарий на r/StableDiffusion с двумя конкретными претензиями. Прогнал ablation: 5 вариантов × 5 категорий × 3 сида = 75 картинок. Нашёл дыры в собственном пайплайне — в том числе кириллицу в выходе LoRA (training-set leakage) и полный коллапс LoRA при scale=2.0. Текущий sandwich оказался патчем поверх плохо обученной LoRA. Пересобираю датасет на 1500 примеров и ухожу в single-pass. В статье — все картинки, цифры, и почему оба «правильных» совета критика на текущей модели не сработали.

Master comparison grid, seed=42 — 5 variants × 5 animals
Master comparison grid, seed=42 — 5 variants × 5 animals

Master comparison grid, seed=42. Строки — варианты пайплайна (A–E), столбцы — категории животных. Подробный разбор ниже.


Что такое pinock.io

Открыл pinock.io пару недель назад. Идея проще пареной репы:

  • Заходишь на сайт — видишь сетку AI-картинок животных в стиле винтажного советского плаката.

  • Каждые 30 секунд выходит новая.

  • Можно лайкать, скачивать, шарить, искать («cat», «owl», «fox»…), генерить свои («peacock»).

  • Бесплатно, без регистрации, без вотермарки.

К моменту этой статьи в базе ~6700 картинок. Пользовательский поиск и кастомные промпты прыгают очередь и подсказывают системе, чего хотят люди — это сигнал спроса.

Стек:

  • Frontend: статика (vanilla JS), Caddy

  • Backend: FastAPI + SQLite (WAL) на дешёвой Ubuntu-машине у знакомого

  • FLUX worker: один RTX 3090 на vast.ai (~$0.20/час), запросы через SSH-туннель

  • Caption worker: Qwen2.5-VL-7B INT4 на вторичной машине

  • Real-ESRGAN для апскейла топовых картинок (Hall of Fame)

  • Stripe для оплаты edit-токенов (Nano Banana 2)

Total стоимость генерации одной картинки — около $0.01.


Архитектура «two-pass sandwich»

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

prompt = "cat"
   │
   ├─ Pass 1: FLUX.2-klein + matchbox LoRA (rank=32, alpha=64, scale=2.0)
   │             text2image, 28 шагов
   │             → output_b1 (стилизованная, но с кривой анатомией)
   │
   └─ Pass 2: FLUX.2-klein без LoRA
                 img2img от output_b1, strength=0.9, 28 шагов
                 → output_b (финальная)

Почему так делалось. На датасете ~300 картинок при scale=1.0 LoRA даёт еле заметный эффект, при scale=2.0 — стиль появляется, но анатомия ломается (лишние конечности, сросшиеся головы). Я подобрал эмпирический «костыль»: pass-2 берёт сломанный pass-1 как init и при strength=0.9 практически перерисовывает картинку с нуля, оставляя только «отпечаток стиля». Получается узнаваемое животное в matchbox-эстетике.

Звучит как трюк. И это трюк — но я был уверен, что он оптимальный.


Reddit-критика, которая меня заставила сесть

Запостил пост в r/StableDiffusion. Получил длинный, технически точный комментарий от u/DelinquentTuna. Если коротко, три претензии:

1. Зачем выкручивать LoRA до scale=2.0 — это намеренно «жарит» модель, и потом ты сам же затираешь её в pass-2 при strength=0.9? То есть ты выбрасываешь ~90% результата LoRA.

2. FLUX.2-klein умеет нативный edit/style-transfer. Я (критик) у себя на 4080 16GB прогнал твои картинки через встроенный pipeline и получил выход в 4 раза больше (~1024×1024) за 9 секунд с более когерентным стилем. Используй edit-фичу вместо самопального i2i.

3. ~300 примеров — слишком мало для matchbox-эстетики (halftone, ограниченная палитра, литографические текстуры). Нужно 5× датасета и нормальная разметка.

И отдельный пункт про дефолтную сортировку — он первым делом увидел свежие картинки (мусор), а не лучшие.

Все три замечания технически обоснованы, и все три заслуживают честного ответа. Сел делать ablation.


Ablation: 5 вариантов × 5 животных × 3 сида = 75 картинок

Тестировал на проде (RTX 3090 + FLUX.2-klein + matchbox LoRA, тот же стек что в production). Запустил два tmux-скрипта на ~30 минут суммарно и собрал результаты в гриды через PIL.

Варианты:

Код

Описание

Параметры

A

Pure FLUX (без LoRA, голый промпт)

baseline

B

LoRA t2i pass-1 snapshot (то, что выдаёт LoRA до затирания)

lora_scale=2.0, prompt=“cat”

C

Текущий прод-sandwich

lora=2.0, pass2_strength=0.9

D

Single-pass со style-промптом (совет критика #1)

lora=1.0, prompt=“cat, matchbox poster style, 1960s Soviet, woodcut, halftone, limited red-black palette”

E

Edit-style: pure FLUX → img2img со style-промптом (совет критика #2)

init=A, lora=1.0, strength=0.5

Категории: cat, fox, owl, lion, wolf. Сиды: 42, 1337, 80085 (выбраны до прогонов; в трёх повторах хочется поймать seed-зависимость).


Результаты

Master grid, seed=1337

Master comparison grid, seed=1337

Master comparison grid, seed=1337

Master grid, seed=80085

Master comparison grid, seed=80085

Master comparison grid, seed=80085

Разбор по строкам

Вариант A — pure FLUX

Базовая модель без LoRA. Реалистичные иллюстрации/фотографии. Никакого matchbox. Ожидаемо. Это нужно как baseline, чтобы понимать, что добавляет LoRA.

Вариант B — LoRA при scale=2.0, голый промпт (snapshot)

Полный коллапс. На каждом сиде все 5 категорий выглядят почти одинаково — текстурный шум одного оттенка:

  • seed=42: красно-оранжевые волнистые полосы

  • seed=1337: зелёный «лесной шум»

  • seed=80085: золотая нашлёпка

Анатомия отсутствует. Это и есть «правда» о LoRA при scale=2.0: модель не генерирует животных. Она генерирует текстуру плаката, потому что я её перекормил весом во время инференса. И именно поэтому я придумал sandwich — потому что наблюдал ровно эту катастрофу и захотел её спрятать за pass-2.

Критик увидел это сразу. Я нет.

Вариант C — текущий прод-sandwich

Адекватный результат. Узнаваемые животные с явно matchbox-эстетикой: woodcut-линии, halftone-фон, ограниченная палитра, иногда W. Morris-подобные цветочные узоры на фоне. На всех 3 сидах стабильно даёт картинки, которые узнаваемы и стилизованы.

Технически это работает так: pass-2 на strength=0.9 берёт сломанный pass-1 (B), добавляет 90% шума и перерисовывает заново. От пасса-1 остаётся только низкочастотный сигнал — общая композиция и цветовой профиль. Это «впрыскивает» стиль без того, чтобы анатомия ломалась.

Вариант D — single-pass со style-промптом, scale=1.0 (совет критика #1)

Катастрофа другого типа. На seed=42 на части картинок видна кириллица в подписи: «СТАДИНАМ» или похожий гибберишный текст. На seed=1337 все 5 категорий схлопываются в почти идентичные «красные силуэты на чёрном». На seed=80085 — снова все 5 одинаковы, на этот раз красный animal-силуэт на белом фоне.

Что произошло. Тренировочный датасет (~300 примеров) включал советские плакаты с кириллицей и красным доминантным фоном. При scale=1.0 + длинный «правильный» style-промпт LoRA начинает вспоминать целые плакаты из training set, а не транспонировать стиль. То есть training-set leakage в чистом виде.

Это самое интересное наблюдение во всей серии. Совет критика  «используй scale=1.0 + проперный style-промпт» теоретически правильный — но на этой LoRA он только выявляет, насколько она переобучена на конкретные обучающие примеры.

Вариант E — edit-style refinement (совет критика #2)

Стиль почти не виден. На strength=0.5 + lora=1.0 LoRA не пробивает FLUX-prior. Картинки выглядят как A с лёгким иллюстративным фильтром: чуть больше насыщенности, чуть проще линии, но не matchbox.

Чтобы стиль начал проступать, нужен strength≥0.7 — а тогда мы возвращаемся в логику i2i sandwich. И снова получим ту же кириллицу/коллапс, только через img2img.


Главный вывод

Текущий sandwich © выигрывает в этой пятёрке — но это патч поверх плохо обученной LoRA, а не правильное решение.

Все три подхода (B raw, D single-pass-styled, E edit-style) показали одну и ту же проблему: LoRA при scale=1.0 пытается воспроизвести training-set целиком, а не переносить стиль. Sandwich работает именно потому что pass-2 при strength=0.9 «сжигает» эту память до низкочастотного остатка.

Это значит:

  1. Совет критика #1 (single-pass + scale=1.0 + style-промпт) теоретически верный, но на текущей LoRA производит хуже результат, чем sandwich.

  2. Совет критика #2 (edit-фичи) недотягивает по стилю при умеренных strength и снова уйдёт в leakage при высоких.

  3. Совет критика #3 (нужен 5× датасет, чище разметка) — единственный истинный фикс. И именно его я и не делал.


Что планирую (и почему это правильный путь)

1. Пересобрать датасет до 1500 картинок.

  • Никакой кириллицы вообще (либо отдельный токен «soviet-text» если он нужен).

  • Жёсткие фильтры: halftone present, limited palette (≤5 цветов), flat geometry.

  • Captioning через Qwen2.5-VL с шаблоном «matchbox poster of a {category}, {dominant colors}, {composition}, woodcut linework».

2. Переобучить LoRA на rank 32 + attention+MLP, не только attention. Текущая LoRA — только на attention-блоках, что недостаточно для переноса композиционных признаков (woodcut, halftone). MLP даст больше «места» для стиля.

3. После v2 LoRA — повторить тот же ablation. Если на v2 single-pass styled (D) даст узнаваемые животных без кириллицы — sandwich удаляется навсегда, и:

  • Время генерации с ~30 сек падает до ~10-15 сек

  • Размер можно поднять с 512 до 1024 (на 3090 хватит)

  • VAE round-trip между проходами уходит — pass-1 больше не сохраняется в JPEG

4. Параллельно — Hall of Fame default sort = Liked. Этот фикс уже задеплоен. Первый экран теперь — лучшие, а не свежие.


Промежуточные технические находки, попавшие в side-quests

FastAPI + SQLite + cursor-пагинация в поиске. Изначально search-эндпоинт жёстко ограничивал выдачу 60 картинок — 581 кошку нельзя было долистать. Добавил ?cursor=<id> (фильтр id < cursor, ORDER BY id DESC), на пагинационных запросах не триггерится автогенерация (чтобы не флудить очередь). Frontend подцепил тот же IntersectionObserver, что для основной ленты.

Auto-prompt variety. Для авто-генерации (не пользовательский ввод) добавил три пула — adjectives (proud, fierce, sleepy…), actions (running, perched, watching…), scenes (in winter forest, at sunset…) — и распределение 55/20/15/10: 55% — голое название категории, 20% — adj+animal, 15% — animal+action, 10% — animal+scene. Раньше все «cat» выглядели одинаково, теперь больше разнообразия.

Реальная стоимость. vast.ai 3090 ~0.20/час → ~5/сутки → при ~1500 картинок/сутки = 0.003/картинка по GPU. Плюс backend/storage ~2/сутки. Итого <$0.01 за картинку при текущем масштабе.


Что забираю из этой истории

  1. «Эмпирически работает» — не значит «оптимально». Sandwich я подобрал по принципу «попробовал — анатомия не ломается — оставил». Я не задавал вопрос «а почему вообще пришлось крутить scale до 2.0». Reddit-критик задал.

  2. Ablation должен быть в day-one. Сделать 5 вариантов × 3 сида = 15 минут на чужом GPU, и я бы не запостил пост с самопальным sandwich как «решением».

  3. Чужая критика — самый дешёвый источник истины. Месяц назад я бы 30 раз подумал, постить ли. Стоило поста на Reddit и одного длинного комментария от незнакомца, который провёл свою параллельную работу на 4080.

  4. Training-set leakage — это не теоретическая страшилка. В моём случае она проявилась как литеральные кириллические буквы в выводе. Если бы я проверял только sandwich-результат (где они скрываются), я бы их не увидел.


Открытые вопросы / куда смотреть дальше

  • Возможно ли получить «sandwich-качество» в один проход на v2 LoRA без leakage. Подозреваю да, но это надо проверить тем же ablation.

  • Стоит ли пробовать FLUX.2-edit явно (через diffusers.FluxEditPipeline) вместо моей самопальной img2img-реализации.

  • Имеет ли смысл вообще держать LoRA на FLUX.2-klein, или для эстетики типа matchbox правильнее обучить под Schnell или другой fast-вариант.


Ссылки

Если интересно посмотреть на v2 LoRA результаты (планирую обучение в ближайшие дни) — заходите через неделю, опубликую update.


P.S. Огромная благодарность пользователю u/DelinquentTuna на r/StableDiffusion. Это лучшее техническое ревью, которое я получал за год. Тебе одному я обязан пятью экспериментами, переосмыслением пайплайна и осознанием того, что training-set leakage — реальная штука, а не байка.

Автор: yukakust

Источник