Как цепочка обратимых отображений превращает сферическую гауссиану в модель с точным правдоподобием, почему в формуле замены переменных появляется определитель Якоби и почему потоки требуют треугольных матриц Якоби.
GAN порождает сэмплы, но не может сказать, насколько вероятно данное изображение. VAE может, но только через нижнюю оценку. Нормализующие потоки (normalizing flows) идут третьим путём (Rezende and Mohamed, 2015; Papamakarios et al., 2021; Kobyzev, Prince and Brubaker, 2021): берут простую плотность, пропускают её через обратимое отображение и точно вычисляют плотность результата по формуле замены переменных. В этой заметке мы выводим эту формулу, собираем из неё поток, сравниваем потоки с другими семействами моделей и заканчиваем приёмом, который делает потоки вычислимыми: треугольной матрицей Якоби. Как строить слои с этим свойством, разбирается в следующей заметке, Архитектуры потоков.
Обозначения. Отображение f всегда идёт из латентного пространства в пространство данных, f:Z→X, так что x=f(z) порождает данные, а z=f−1(x) их кодирует. pZ — базовая (латентная) плотность, pθ(x) — плотность данных в модели. Для отображения g через Jg(u)=∂g(u)/∂u обозначена его матрица Якоби в точке u.
Возьмём непрерывную случайную величину z с простой плотностью — сферической гауссианой (spherical Gaussian):
z∼pZ(z)=N(z;0,I).
«Сферическая» означает, что ковариационная матрица пропорциональна единичной (здесь в точности равна I): разброс одинаков во всех направлениях, а линии уровня — сферы. Данные получаются цепочкой из K отображений,
x=fθ(z)=fK∘⋯∘f2∘f1(z),
и каждое fi обратимо (биективно): каждая точка выходного пространства — образ ровно одной входной точки. Для формулы плотности ниже каждое fi должно быть ещё и дифференцируемым с дифференцируемым обратным (диффеоморфизмом). Тогда обратима и вся цепочка, причём f−1=f1−1∘f2−1∘⋯∘fK−1. Для диффеоморфизма нужно dimz=dimx. Цепное правило, применённое к f−1∘f=id и f∘f−1=id, даёт Jf−1Jf=I и JfJf−1=I, а ранг произведения матриц размеров m×n и n×m не больше min(m,n), поэтому оба тождества могут выполняться, только когда m=n. Значит, в отличие от VAE, латентный вектор имеет ту же размерность, что и данные.
Рисунок
У каждой точки Z ровно одна парная точка в X
Поток f переводит каждую латентную точку z в точку данных x = f(z) (синие стрелки, слева направо), а обратное отображение f⁻¹ возвращает её назад (зелёные стрелки, справа налево). Поскольку каждое fᵢ в цепочке — биекция, никакие две точки Z не попадают в один и тот же x и ни один x не остаётся без прообраза. Положения точек в X вычислены по тому же обратимому двумерному отображению, которое дальше используется, чтобы показать матрицу Якоби как локальную геометрию.
Нам нужна pθ(x). Первая догадка — отобразить x обратно и взять значение базовой плотности, pθ(x)=?pZ(f−1(x)). Это неверно, потому что ответ зависит от того, как ведёт себя f: насколько оно растягивает или сжимает пространство вокруг каждой точки.
Контрпример в одну строку: в одном измерении пусть z∼N(0,1) и x=f(z)=2z. Наивная догадка даёт pZ(x/2); интеграл этой функции по всей прямой равен 2, так что это даже не плотность. Истинное распределение — x∼N(0,4) с плотностью pZ(x/2)/2. В точке x=1 наивное значение равно 0.352, а правильное — 0.176: f распределяет ту же вероятность по вдвое большей длине, поэтому плотность должна уменьшиться вдвое.
Пусть отображение f:Z→X обратимо, дифференцируемо и имеет дифференцируемое обратное, а pZ(z) — плотность, заданная на z∈Z.
Одно измерение. Масса вероятности сохраняется. Маленький интервал ширины ∣dz∣ вокруг z содержит массу pZ(z)∣dz∣, и f переводит его в интервал ширины ∣dx∣ вокруг x=f(z), который должен содержать ту же массу:
pθ(x)∣dx∣=pZ(z)∣dz∣,
откуда
pθ(x)=pZ(z)dxdz=∣f′(f−1(x))∣pZ(f−1(x)).
Любая размерность. Ширины превращаются в объёмы, и нам нужно знать, во сколько раз f меняет малый объём. Вблизи точки z отображение приблизительно линейно, f(z+δ)≈f(z)+Jf(z)δ, с матрицей Якоби
где f(1),…,f(d) — выходные координаты f: строка k матрицы Jf содержит частные производные выхода k, столбец j — производные по входу zj. Поэтому крошечный куб со стороной ε в точке z, натянутый на векторы εe1,…,εed, переходит в крошечный параллелепипед, натянутый на εJf(z)e1,…,εJf(z)ed, то есть на столбцы Jf(z), умноженные на ε. Объём параллелепипеда, натянутого на столбцы матрицы A, равен ∣detA∣, так что f умножает малые объёмы вблизи z на ∣detJf(z)∣. В двух измерениях маленький квадрат становится маленьким параллелограммом, площадь которого в ∣detJf(z)∣ раз больше площади квадрата.
Рисунок
Матрица Якоби — это локальная геометрия
Квадратная сетка в Z, пропущенная через обратимое нелинейное отображение f. Здесь f(z) = (z₁ + 0.35·sin(1.2·b), b), где b = z₂·e^(0.35·z₁) + 0.2·z₁, так что det J_f(z) = e^(0.35·z₁). Каждая маленькая ячейка становится почти параллелограммом. Её площадь умножается на |det J_f| в этой точке: янтарная ячейка (z₁ ≈ −1.25) сжимается до 0.65 от исходной площади (det ≈ 0.65), а красная ячейка (z₁ ≈ 1.25) вырастает в 1.55 раза (det ≈ 1.55).
Масса сохраняется так же, как в одном измерении: куб объёма εd содержит массу pZ(z)εd, и его образ объёма ∣detJf(z)∣εd должен содержать ту же массу. Следовательно, при z=f−1(x)
Две формы говорят одно и то же. Матрица Якоби обратного отображения вычисляется в точке данных, а матрица Якоби f — в латентной точке z=f−1(x). Их связывают теорема об обратной функции, Jf−1(x)=[Jf(z)]−1, и равенство det(A−1)=1/detA. Множитель ∣detJf∣ измеряет расширение или сжатие пространства вокруг точки (Papamakarios et al., 2021, §2.1). Там, где f расширяет пространство, та же вероятность распределяется по большему объёму и плотность падает; там, где f сжимает пространство, плотность растёт:
∣detJf∣=2: объёмы удваиваются, а плотность уменьшается вдвое;
∣detJf∣=0.5: объёмы уменьшаются вдвое, а плотность удваивается;
∣detJf∣=1: объёмы сохраняются (форма при этом может меняться), а с ними и плотность;
detJf=0: объём схлопывается в ноль. Линейное отображение Jf(z) необратимо, и тогда у f нет дифференцируемого обратного в точке f(z), поскольку дифференцируемое обратное давало бы Jf−1Jf=I. Множитель плотности 1/∣detJf∣ здесь бесконечен, и формула выше неприменима. Поэтому слои потоков строят так, чтобы всюду было detJf=0.
Рисунок
Что |det J| делает с маленьким квадратом
Штриховой единичный квадрат и его образ (с заливкой) при четырёх линейных отображениях. |det J| — множитель, на который умножаются площади, а в d измерениях — объёмы. Сдвиг может полностью изменить форму и при этом иметь det = 1. При det = 0 квадрат схлопывается в отрезок: два измерения сжимаются в одно, и линейное отображение нельзя обратить. У слоя с det J = 0 в некоторой точке там нет дифференцируемого обратного, поэтому формула замены переменных к нему неприменима; вот почему в слоях потоков всюду det J ≠ 0.
Базовая плотность p_Z = N(0, 1) (внизу), монотонный поток x = f(z) = z + 0.8·tanh(2z) (в центре) и получающаяся плотность модели p_θ(x) = p_Z(f⁻¹(x)) / f′(f⁻¹(x)) (справа, повёрнута набок). Полоса z ∈ [0.1, 0.5] содержит вероятность 0.152; f растягивает её до отрезка x ∈ [0.258, 1.109], примерно в 2.13 раза более широкого, поэтому плотность там должна быть во столько же раз ниже. Вблизи начала координат f′(0) = 2.6, и это превращает один горб гауссианы в два.
Возьмём pZ=N(0,1) и отображение с рисунка,
f(z)=z+0.8tanh(2z),f′(z)=1+1.6sech2(2z)>0.
Производная всюду положительна, поэтому f строго возрастает и, значит, обратимо (обратное не выражается в замкнутой форме, но легко находится численно). Тогда pθ(x)=pZ(z)/f′(z) при z=f−1(x):
z
x=f(z)
f′(z)
pZ(z)
pθ(x)=pZ(z)/f′(z)
0
0
2.600
0.3989
0.1534
0.5
1.109
1.672
0.3521
0.2106
1
1.771
1.113
0.2420
0.2174
2
2.799
1.002
0.0540
0.0539
Вблизи начала координат f растягивает пространство в 2.6 раза, поэтому плотность там опускается намного ниже пика гауссианы; в хвостах f′≈1 и плотность почти не меняется. Один горб превращается в два с максимумами в x≈±1.50 (то есть z≈±0.77), pθ≈0.234. Даже одно гладкое обратимое отображение может превратить унимодальное базовое распределение в бимодальную модель. В логарифмической форме при z=0.5: logpθ(x)=logpZ(z)−logf′(z)=−1.044−0.514=−1.558.
Полоса z∈[0.1,0.5] содержит вероятность Φ(0.5)−Φ(0.1)=0.152. Её образ — x∈[0.258,1.109], в 2.13 раза шире, поэтому средняя плотность на нём падает с 0.379 до 0.178, в то же число раз: та же масса вероятности, другая ширина.
Простейший многомерный случай — линейное отображение x=Jz, матрица Якоби которого ∂x/∂z=J всюду одна и та же. При
J=[2002]
каждое направление растягивается в 2 раза, и detJ=2⋅2−0⋅0=4. Единичный квадрат в Z становится квадратом 2×2 в X, так что
pθ(x)=pZ(z)det(∂z∂x)1=4pZ(z).
Проверим на z∼N(0,I) в двух измерениях: x=2z∼N(0,4I), и действительно pθ(0)=2π⋅41=0.0398=41⋅2π1.
Ориентация не важна. Для
J=[200−2],detJ=−4,
квадрат ещё и отражается, но его площадь по-прежнему равна 4. Знак определителя фиксирует ориентацию; изменение объёма, а значит и плотности, зависит только от ∣detJ∣=4. Поэтому в формуле стоит модуль.
Рисунок
Матрица Якоби, растягивающая каждое направление в 2 раза
Вверху: J = diag(2, 2) переводит единичный квадрат из Z в квадрат 2 × 2 в X. Площадь растёт с 1 до det J = 4, поэтому та же вероятность распределяется по вчетверо большей площади и плотность падает вчетверо: p_θ(x) = p_Z(z) / |det J|. Внизу: J = diag(2, −2) ещё и отражает вторую ось. Янтарная стрелка в четверть оборота теперь идёт по часовой стрелке, det J = −4, но площадь и множитель плотности зависят только от |det J| = 4.
Теперь применим формулу ко всей цепочке z∼pZ(z), x=fθ(z)=fK∘⋯∘f2∘f1(z). Обозначим промежуточные точки
z0=z,zi=fi(zi−1)(i=1,…,K),
так что zK=x. Обратное отображение проходит цепочку в обратном порядке, f−1=f1−1∘⋯∘fK−1, и по цепному правилу его матрица Якоби — произведение матриц Якоби слоёв, каждая из которых берётся в своей промежуточной точке: Jf−1(x)=Jf1−1(z1)Jf2−1(z2)⋯JfK−1(zK). Поскольку det(AB)=detAdetB,
Чтобы вычислить её, мы проводим x через слои в обратном направлении, zK=x→zK−1→⋯→z0, и собираем по одному определителю на слой. Эта конструкция и есть нормализующий поток: плотность перетекает через последовательность обратимых отображений, а цепочка, прочитанная в обратную сторону, нормализует данные, превращая их (приближённо, когда модель обучена) в выборку из стандартного нормального базового распределения. Название популяризовали в машинном обучении Rezende and Mohamed (2015), опираясь на более ранние работы по оцениванию плотности через преобразование данных к гауссиане (Tabak and Vanden-Eijnden, 2010). Потоки Rezende и Mohamed состояли из планарных и радиальных отображений, определители которых дёшевы по другой причине, чем у треугольных слоёв ниже (Архитектуры потоков, часть Ж).
Для сэмплирования нужно только прямое направление: берём z∼N(0,I) и вычисляем x=fθ(z); для вычисления плотности нужно обратное. Какое из двух направлений вычисляется дёшево, зависит от архитектуры: маскированные авторегрессионные потоки (masked autoregressive flows, MAF; Papamakarios, Pavlakou and Murray, 2017) быстро вычисляют плотность и медленно сэмплируют, обратные авторегрессионные потоки (inverse autoregressive flows, IAF; Kingma et al., 2016) — наоборот, а слои связи (coupling layers) делают дешёвым и то и другое (см. Архитектуры потоков). Сэмплирование превращает простое z в сэмпл из сложного распределения; формула плотности — то, что позволяет обучитьfθ так, чтобы это распределение совпало с данными.
Это точное вычисление логарифма правдоподобия, без оценок снизу и без сэмплирования. Обучение — обычный метод максимального правдоподобия, который минимизирует прямую KL-дивергенцию (forward KL) от распределения данных к модели (почему мы минимизируем именно кросс-энтропию). Как и в заметке о подгонке распределений, плотность данных неизвестна, поэтому мы минимизируем отрицательный логарифм правдоподобия по набору данных x(1),…,x(N), теперь уже с нейросетевой моделью:
где zi(n) — промежуточные точки для x(n): zK(n)=x(n) и zi−1(n)=fi−1(zi(n)), так что z0(n)=fθ−1(x(n)).
Два слагаемых тянут в разные стороны. Первое хочет, чтобы каждая закодированная точка лежала вблизи центра гауссианы. Второе штрафует обратное отображение за сжатие пространства, поэтому модель не может улучшить целевую функцию, схлопнув все данные в z=0.
Изображения: деквантование, масштабирование и биты на размерность#
Дополнительный материал: предобработка, которая превращает плотность потока на изображениях в публикуемое число бит на размерность, с разобранной проверкой поправки. Остальная часть заметки на него не опирается.
Формула даёт плотность, поэтому она точна для непрерывных данных. Изображения дискретны, например 8-битные значения пикселей x∈{0,…,255}d, и непрерывная плотность могла бы принимать в этих точках сколь угодно большие значения: поток, обученный на исходных значениях, может неограниченно повышать своё правдоподобие, стягивая массу вероятности на эти точки. Поэтому потоки обучают на деквантованных (dequantized) данных x~=x+u с равномерным шумом u∼U[0,1)d. Вероятность, которую модель даёт дискретному значению x, равна P(x)=∫[0,1)dpθ(x+u)du, и неравенство Йенсена даёт Eu[logpθ(x+u)]≤logP(x): непрерывный логарифм правдоподобия деквантованных данных — нижняя оценка дискретного логарифма правдоподобия (Theis, van den Oord and Bethge, 2016).
Биты на размерность (bits per dimension). Правдоподобие моделей изображений сообщают как −log2P(x)/d — число бит на координату, которое нужно коду без потерь, построенному по модели (см. биты на размерность в заметке об авторегрессионных моделях). Модель, распределяющая вероятность равномерно по 256 значениям, получает ровно 8. Для потока сообщают число −Eu[log2pθ(x+u)]/d, которое по неравенству выше является верхней оценкой дискретного значения.
Масштабирование меняет плотность. Сетям редко подают на вход значения пикселей из [0,256). Распространённый выбор — x′=x~/127.5−1, что переводит данные примерно в [−1,1]. Тогда поток моделирует плотность x′, а замена переменных связывает её с плотностью x~: у отображения x~↦x′ постоянная матрица Якоби 127.51I, поэтому
Число бит на размерность в единицах пикселей равно числу в единицах модели плюс log2127.5≈6.99. В общем случае, если после предобработки один уровень квантования имеет ширину a, сдвиг равен −log2a бит на размерность (Kingma and Dhariwal, 2018, §2, формула 2): log2255≈7.99 для x/255, как у распространённых загрузчиков изображений, и ровно 8 для x/256. Например (игрушечные числа), поток, который сообщает −3.50 бита на размерность для x′, имеет в единицах пикселей −3.50+6.99=3.49 бита на размерность. Отрицательное значение в единицах модели нормально, потому что плотность на узком диапазоне может превышать 1; с опубликованными результатами, например с таблицей правдоподобий в заметке об авторегрессионных моделях, можно сравнивать только число в единицах пикселей.
Шум должен быть в тех же единицах. Шум деквантования должен покрывать ровно один уровень того, что видит сеть. При x′=x/127.5−1 это значит прибавлять u/127.5 с u∼U[0,1)d, что равносильно прибавлению u до масштабирования. Шум ширины 1 в единицах x′ размазал бы каждое значение примерно на 127 уровней, а без шума правдоподобие снова становится неограниченным. NICE, например, деквантует CIFAR-10 равномерным шумом ширины 1281 на данных, приведённых к [−1,1], где ширина одного уровня равна 2562=1281 (Dinh, Krueger and Bengio, 2015, §5.1).
Логит-преобразование. Значения пикселей ограничены, а гауссовская базовая плотность задана на всём Rd. Чтобы ослабить краевые эффекты, RealNVP моделирует логит масштабированных пикселей (Dinh, Sohl-Dickstein and Bengio, 2017, §4.1):
y=logit(p)=log1−pp,p=α+(1−2α)256x~,α=0.05.
Сдвиг α держит p внутри [0.05,0.95), так что y остаётся в пределах примерно ±2.94, а не уходит в ±∞ на чёрных и белых пикселях. Это форма из эталонной реализации; в статье напечатано 1−α вместо 1−2α. Производная преобразования по каждой координате равна dy/dx~=(1−2α)/(256p(1−p)), поэтому
В отличие от линейного масштабирования, поправка зависит от пикселя: около 6.15 бита для среднего серого значения (x~=128.5) и 3.80 бита для самых тёмных и самых светлых значений (x~=0.5 или 255.5). RealNVP включает это слагаемое в свои биты на размерность. В статье о Glow сказано, что она следует предобработке RealNVP (§5); её эталонный код вместо этого переводит данные в [−21,21), добавляет шум ширины 2561 и прибавляет к логарифму правдоподобия константу −dlog256, то есть реализует линейный случай выше с a=2561. Потокам, построенным для изображений, посвящена часть Е заметки «Архитектуры потоков».
Сводка: три способа связать данные x и латентную переменную z
GAN: генератор отображает шум z в сэмпл x′, а дискриминатор лишь помечает входы как настоящие или подделку, поэтому нет ни правдоподобия, ни пути назад из x в z. VAE: стохастический энкодер q_φ(z|x) и декодер p_θ(x|z) дают приближённое апостериорное распределение и нижнюю оценку (ELBO) для log p(x). Поток: одно обратимое отображение; f⁻¹ точно переводит x в z, а f переводит z обратно в x, поэтому z имеет ту же размерность, что и x, а логарифм правдоподобия точен. Стрелки следуют соглашению заметки f: z → x.
GAN (Goodfellow et al., 2014; см. заметку о GAN). Логарифм правдоподобия не вычисляется: генератор задаёт своё распределение только через сэмплы. Вывода латентных переменных тоже нет: ничто не отображает x обратно в z, и ничто не гарантирует, что генератор покрывает весь носитель (support) распределения данных, так что у настоящего x может вовсе не быть латентного кода.
VAE (Kingma and Welling, 2014; см. заметку о VAE). Он оптимизирует нижнюю оценку логарифма правдоподобия (evidence lower bound, ELBO) и выводит латентные переменные через приближённое апостериорное распределениеqϕ(z∣x).
Всё сказанное выше держится на ∣detJ∣: если его можно вычислить, плотность точна; если его нельзя вычислить дёшево, правдоподобие на практике не удаётся ни вычислять, ни использовать для обучения в масштабе изображений. Для произвольной сети это дорого. Вычисление определителя произвольной матрицы d×d стоит O(d3) методом Гаусса или через LU-разложение (Golub and Van Loan, 2013); наивное разложение по алгебраическим дополнениям (cofactor expansion) ещё хуже, O(d!). Возьмём d=1000 (у полутонового изображения 32×32 уже d=1024): это порядка 109 операций (около 32d3≈6.7×108 для LU) на каждый пример, каждый слой и каждый шаг обучения. Для произвольной сети даже построение J требует d обратных проходов, по одному на строку.
Приём, благодаря которому потоки применимы на практике, — сделать матрицу Якоби треугольной. Для (нижне)треугольной матрицы разложение определителя по первой строке и повторение этого шага для оставшегося блока оставляет только диагональ:
что стоит O(d): около 103 операций вместо примерно 109, и нужны только диагональные элементы.
Треугольная J к тому же обратима тогда и только тогда, когда все её диагональные элементы ненулевые, а это легко гарантировать по построению. Для самого отображения это условие даёт больше. Если Jf всюду нижнетреугольная, выход i зависит только от входов 1,…,i. Если каждый диагональный элемент непрерывен и всюду ненулевой, он сохраняет знак, так что каждый выход строго монотонен по своему входу. Тогда отображение инъективно и обращается по одной координате за раз: z1 из x1, затем z2 из x2 и уже известного z1, и так далее. Оно является биекцией Rd, когда каждое из этих одномерных отображений сюръективно, то есть принимает все значения из R, как аффинные отображения zi↦eszi+t в слоях связи.
Рисунок
Почему треугольная матрица Якоби — ключевой приём
Если все элементы выше диагонали равны нулю, определитель равен произведению диагональных элементов, так что log|det A| = Σ log|aᵢᵢ|. При d = 1000 определитель произвольной матрицы (через LU-разложение) стоит порядка d³ = 10⁹ операций на каждый пример и каждый слой, тогда как для треугольной нужно d = 10³ умножений. Столбцы показаны в логарифмической шкале по основанию 10.
Ноутбук о нормализующих потоках из репозитория HSE-LAMBDA Deep Generative Models (ссылка закреплена на конкретном коммите; ячейки нумеруются с 1, считая все ячейки подряд, включая текстовые) опирается на библиотеку Андрея Карпаты pytorch-normalizing-flows (Karpathy, 2019), включённую в репозиторий как nflib. В ноутбуке нет сохранённых выходов ячеек, поэтому он документирует процедуры, а не результаты.
Ячейка 2 записывает замену переменных с порождающей стороны, logp(zK)=logp(z0)−∑ilog∣det∂fi/∂zi−1∣.
Ячейки 4–5 задают три двумерных игрушечных набора данных: две луны (two moons; используется именно он), смесь четырёх гауссиан со стандартным отклонением 0.5 и набор точек SIGGRAPH, которому нужен файл siggraph.pkl, отсутствующий в репозитории.
Ячейка 7 строит модель: логистическое априорное распределение — равномерное, пропущенное через обратную сигмоиду, с logpZ(z)=∑i[−log(1+ezi)−log(1+e−zi)] (гауссовское априорное распределение закомментировано). NICE использует то же априорное распределение и предпочитает его, потому что оно даёт градиенты с лучшим поведением (Dinh, Krueger and Bengio, 2015, §3.4). Формула замены переменных верна для любой базовой плотности, так что меняется только logpZ. Поток состоит из трёх блоков, в каждом из которых идут ActNorm, обратимая «свёртка» 1×1 и слой связи на нейросетевых сплайнах (neural spline coupling; 8 интервалов сплайна, граница сплайна (tail bound) 3, ширина скрытого слоя 16); все эти слои описаны в заметке Архитектуры потоков.
Ячейки 9–11 обучают модель с Adam (шаг обучения 10−4, затухание весов (weight decay) 10−5) на функции потерь выше: zs, prior_logprob, log_det = model(x_batch), logprob = prior_logprob + log_det, loss = -torch.sum(logprob), в течение 1000 итераций, каждая на свежем батче из 128 сэмплов. Функция потерь суммируется по батчу, а не усредняется, как в L(θ), что лишь масштабирует градиент. nflib называет направления наоборот по сравнению с этой заметкой: её forward идёт от данных к латентному пространству (наше f−1) и накапливает наше ∑ilog∣det∂fi−1/∂zi∣, а model.sample берёт сэмплы из априорного распределения и запускает inverse (наше f).
Ячейки 13–17 отображают данные в латентное пространство, а сэмплы из априорного распределения — в данные и показывают, как сетка деформируется слой за слоем. Ячейка 13 берёт точки из априорного распределения вызовом model.prior.sample([128, 2]); у априорного распределения уже две координаты, поэтому получается тензор 128×2×2, и на диаграмме рассеяния оказывается 256 точек вместо 128. Это безвредно (sample((128,)) даёт задуманную форму). Ячейка 17 выполняет ещё один шаг обучения и рисует деформацию сетки один раз: её цикл на 500 шагов прерывается при k=0.
Ячейка 19 раскрашивает сетку по prior_logprob.exp(), то есть по pZ(f−1(x))без логарифма определителя. Это ровно наивная формула из начала заметки; плотность модели — (prior_logprob + log_det).exp().
Потокам нужны слои, которые достаточно выразительны и при этом по построению имеют дешёвый определитель Якоби. Такие слои строит заметка Архитектуры потоков: слои связи (NICE, RealNVP), маскированные авторегрессионные слои (MAF, IAF) и обратимые свёртки 1×1 из Glow, затем сплайновые слои связи, ActNorm и многомасштабные потоки для изображений и, наконец, планарные и радиальные потоки Rezende и Mohamed.