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. На первом шаге истории нет, и контроллер слеп как раз там, где кривизна максимальна.

Статическая сетка из того же сигнала выигрывает
Если вопрос в том, куда ставить шаги, сетку можно построить заранее. Я взял средний темп поворота |v'|/|v| по остальным 11 юнитам, задал плотность шагов пропорциональной ему (равный угол поворота на шаг) и прогнал обычный Эйлер.
Относительно стандартного расписания ComfyUI при том же числе вызовов ошибка до сошедшегося решения ниже на 30-43% при 8, 12, 16 и 24 вызовах, и все доверительные интервалы исключают ноль. Если добавить измеренный профиль распространения ошибки (ошибка, сделанная в начале, весит в конце примерно в четыре раза больше), выигрыш 41-50%, и 8 вызовов дают меньшую ошибку, чем стандартное расписание при 24. Учебный показатель для Эйлера, 1/2, заметно лучше стандартного расписания только при 24 вызовах. В диапазоне 8-24 шагов задача далека от асимптотической.

На промпте с лицом стандартное расписание при 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% выше), хотя расстояние по латенту там улучшается.

Сетка ставит шаги рано, где решается композиция, и мало в конце, где проступает мелкая текстура. Низкие частоты она выравнивает лучше, а энергию текстуры теряет. Для текстурного контента нужен был бы перцептивный вес, и я его не строил.
Чего я бы не утверждал
Одна модель (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 (576x768, 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.
Комментарии 0