Flow-модель рисует картинку, интегрируя обыкновенное дифференциальное уравнение, и каждый шаг стоит прохода через большую сеть. Многошаговый сэмплер вроде Адамса-Башфорта и так хранит предыдущую скорость. Косинус между старой и новой скоростью получается бесплатно. Если он близок к 1, траектория прямая и шаг можно увеличить. Если направление резко меняется, шаг нужно дробить.
Я хотел превратить эту идею в строгую теорию и проверить её на настоящей модели. Результат вышел менее аккуратным, чем идея, так что отчасти это рассказ о том, какая половина идеи выжила.
Коротко:
-
косинус измеряет поворот скорости, но не изменение её длины; на Anima вторая часть мала в середине траектории и заметна на концах;
-
оценка
E = (h/2)·|v|·θхорошо предсказывает реальную ошибку шага Эйлера (медианное отношение 0,80-0,84, корреляция между юнитами 0,91-0,92); -
контроллер «шаг по косинусу» проигрывает стандартному расписанию, и причина в слепом первом шаге;
-
статическая сетка с шагами «по равному углу» даёт на 30-43% меньшую ошибку, чем стандартное расписание ComfyUI при том же числе вызовов сети;
-
подстройка под конкретную картинку не добавила ничего, что я смог бы измерить;
-
на текстурном промпте метрика LPIPS говорит обратное метрике по латенту.
Идея не новая
Сначала оговорка. Адаптивный шаг без обучения и без дополнительных вызовов сети для flow matching уже предложен: CAT-Flow (Li и др., arXiv 2609.01746) оценивает кривизну по конечной разности поля скоростей во времени и сообщает до 40% экономии шагов на четырёх моделях text-to-image. Я нашёл эту работу после экспериментов, прочитал только аннотацию и введение и с ней не сравнивался. Статические расписания, подобранные под модель или под эталонные траектории, тоже есть (Align Your Steps, Optimal Stepsize Distillation, Bespoke Solvers). Ниже измерительное исследование на одной модели, и я скажу, где, по-моему, оно что-то добавляет.
Что видит косинус, а что нет
Для любых двух векторов квадрат разности точно раскладывается на изменение длины и поворот:
|v_n - v_(n-1)|² = (s_n - s_(n-1))² + 4·s_n·s_(n-1)·sin²(θ/2)
Косинус знает только второе слагаемое. Течение может быть идеально прямым и при этом разгоняться вдоль линии, и косинус этого не заметит. Одна гауссиана показывает обратную проблему: её скорость меняет знак в минимуме дисперсии, косинус равен −1, а локальная ошибка при этом мала.
Насколько это важно на реальной модели? На Anima изменение скорости несёт 0,3-0,4% энергии ускорения на большей части траектории и 8-9% (до 23%) в первых и последних 5% времени. В середине косинус почти вся картина. На концах нет.
Хороший предсказатель ошибки
Я посчитал истинную локальную ошибку Эйлера по эталонной сошедшейся траектории и сравнил с E = (h/2)·|v|·θ, где θ — угол между соседними скоростями. На 12 юнитах (4 промпта, 3 сида) и шагах от 0,01 до 0,12 интервала времени медианное отношение оценки к истине равно 0,80-0,84, а корреляция логарифмов между юнитами 0,91-0,92. Достаточно калибровочной константы. Эта часть предложения работает.
В скоростях спрятан пол шума
Anima работает в bf16 с classifier-free guidance. У скорости там угловой шум около 1,7° между соседними вычислениями и около 3,4° в середине длинной траектории. С шагом он не уменьшается. Я проверил, откуда он берётся: вычисление детерминировано, а возмущение входа на относительные 1e-5 уже меняет направление на 1,7°. Эффект остаётся плоским до 1e-3. Это признак округления, а не шероховатости потока. Без guidance пол примерно в четыре раза ниже.
Контроллер, который держит угол на шаг ниже этого пола, цели не достигает и схлопывается к минимальному шагу. Родственная ловушка: норма производной, оценённой по зашумлённым значениям в 110 000 измерений, смещена вверх на мощность шума, поэтому сырая конечная разность показывает плато, которого нет. Помогает вычитание остаточной дисперсии локальной полиномиальной подгонки.
Первый контроллер проиграл, и причина в первом шаге
Контроллер я собрал, как предлагалось: h_next = h · (целевой_угол / измеренный_угол) с ограничениями. При равном числе вызовов его ошибка оказалась хуже стандартного расписания, причём, как ни странно, она не зависела ни от целевого угла, ни от числа вызовов. Она зависела от начального шага: средняя ошибка 0,80 при начальном шаге 0,06, 0,51 при 0,02 и 0,26 при 0,01. На первом шаге истории нет, и контроллер слеп как раз там, где кривизна максимальна.
error_vs_first_step
Статическая сетка из того же сигнала выигрывает
Если вопрос в том, куда ставить шаги, сетку можно построить заранее. Я взял средний темп поворота |v'|/|v| по остальным 11 юнитам, задал плотность шагов пропорциональной ему (равный угол поворота на шаг) и прогнал обычный Эйлер.
Относительно стандартного расписания ComfyUI при том же числе вызовов ошибка до сошедшегося решения ниже на 30-43% при 8, 12, 16 и 24 вызовах, и все доверительные интервалы исключают ноль. Если добавить измеренный профиль распространения ошибки (ошибка, сделанная в начале, весит в конце примерно в четыре раза больше), выигрыш 41-50%, и 8 вызовов дают меньшую ошибку, чем стандартное расписание при 24. Учебный показатель для Эйлера, 1/2, заметно лучше стандартного расписания только при 24 вызовах. В диапазоне 8-24 шагов задача далека от асимптотической.
face_s42: стандартное расписание (сверху) и статическая сетка (снизу) при 8, 12, 16 шагах
На промпте с лицом стандартное расписание при 8, 12 и 16 шагах рисует другую композицию. Статическая сетка добирается до композиции сошедшегося изображения уже за 8 шагов.
Подстройка под картинку ничего не добавила
Два прямых теста. Первый: я взял сетку шагов, которую контроллер выдал для одной картинки, и применил её к другой. Ошибка осталась такой же, как со своей сеткой (лог-отношение −0,01…−0,02, интервал около ±0,07). Второй: контроллер с малым начальным шагом оказался равен статической сетке, которую он подразумевает (лог-отношение −0,02, интервал от −0,09 до +0,04). Выигрыш несёт форма сетки, а подстройка под отдельную траекторию не несёт информации, которую я смог бы обнаружить. Сравнение контроллера со стандартным расписанием этого показать не может, потому что стандартное расписание само по себе плохая сетка. Что дал бы такой тест для других контроллеров, я не знаю.
Всё зависит от метрики
Лучшую сетку я проверил по LPIPS относительно сошедшегося изображения. При 8, 12 и 16 вызовах LPIPS в среднем ниже на 30-35% (побед 11 из 12, 9 из 12 и 9 из 12 юнитов). По промптам она выигрывает на плоской заливке, лицах и геометрических сетках (на 39-48% ниже) и проигрывает на текстурном промпте (примерно на 16% выше), хотя расстояние по латенту там улучшается.
fur_s42: статическая сетка получается более гладкой
Сетка ставит шаги рано, где решается композиция, и мало в конце, где проступает мелкая текстура. Низкие частоты она выравнивает лучше, а энергию текстуры теряет. Для текстурного контента нужен был бы перцептивный вес, и я его не строил.
Чего я бы не утверждал
-
Одна модель (Anima), одно разрешение, один масштаб guidance, 12 юнитов. Эффекты меньше примерно 5-8% не обнаруживаются.
-
Основная метрика: расстояние по латенту до сошедшегося решения, у которого собственная ошибка около 5%. LPIPS проверен только для одной сетки.
-
Гипотезы не были записаны в файл с порогами до измерений. Находка про первый шаг пришла из результата, похожего на баг, поэтому она post hoc, а контрольные эксперименты я придумывал уже после неё.
-
Профиль распространения ошибки, который использует лучшая сетка, измерен на двух из 12 оценочных юнитов. Чистые сетки по темпу поворота построены без участия оцениваемого юнита (leave-one-out).
-
Только Эйлер и Адамс-Башфорт 2. Из-за быстрого роста шага в начале эти сетки делают Адамса-Башфорта 2 хуже Эйлера.
-
Сдвиг сигм на относительные 1e-7 меняет итоговый латент на 3-10% в bf16. Числа детерминированы для этого кода, но между реализациями есть разброс в несколько процентов на юнит.
Как пользоваться
В репозитории код, 30 тестов, все таблицы и рисунки, а также списки сигм для Anima при 8, 12, 16 и 24 вызовах. Их можно подать в ноду ручных сигм со стандартным сэмплером euler (576×768, CFG 5, только Anima). Ноды для ComfyUI нет. Чтобы получить сетку для другой модели, нужны эталонные траектории этой модели, из которых считается профиль темпа поворота; на одну картинку здесь уходит около двух минут. Сколько картинок достаточно, я не проверял.
Полная запись с таблицей гипотез и всеми интервалами: README в репозитории. Препринт: 10.5281/zenodo.24143896. Код: YakovSava/zero-cost-proxy-research.
Работа продолжает мои предыдущие: adaptive-step-samplers, Flow-WLS и Flow-WLS под квантованием.
Источники
-
Li, Q., Cisneros-Velarde, P., Fu, K., Miranda, S. A., Vaswani, S., & Zhang, H. (2026). CAT-Flow: Curvature-adaptive steps for flow matching. https://arxiv.org/abs/2609.01746
-
Sabour, A., Fidler, S., & Kreis, K. (2024). Align your steps: Optimizing sampling schedules in diffusion models. ICML, PMLR 235. https://proceedings.mlr.press/v235/sabour24a.html
-
Pei, J., Hu, H., & Gu, S. (2025). Optimal stepsize for diffusion sampling. https://arxiv.org/abs/2503.21774
-
Shaul, N., Perez, J., Chen, R. T. Q., Thabet, A., Pumarola, A., & Lipman, Y. (2024). Bespoke solvers for generative flow models. ICLR. https://arxiv.org/abs/2310.19075
-
Hairer, E., Nørsett, S. P., & Wanner, G. (1993). Solving ordinary differential equations I: Nonstiff problems (2nd rev. ed.). Springer.
-
Zhang, R., Isola, P., Efros, A. A., Shechtman, E., & Wang, O. (2018). The unreasonable effectiveness of deep features as a perceptual metric. CVPR. https://doi.org/10.1109/CVPR.2018.00068
Полный список в стиле APA 7 с уровнем проверки каждой записи и картой «что где цитировать»: publication/references_apa.md.
Источник: habr.com