Модуль 15. Информационная геометрия: Fisher, естественный градиент и потоки распределений
Сначала попробуйте решить упражнение. Ответы, подробные решения и проектные ориентиры раскрываются отдельно и сохраняют своё состояние при переключении RU/EN.
Скачать файлы курса (.zip) 33,9 КБАрхив содержит девять Python-файлов решений, общих для RU/EN, с сохранением структуры каталогов.
Упражнение 1. Fisher-геометрия Bernoulli
Для :
-
выведите ;
-
перейдите к логиту и получите ;
-
покажите, что превращает элемент длины в ;
-
найдите расстояние Fisher–Rao между и .
Показать ответ и решение
Для
скор-функция по :
Так как ,
Для логита , , поэтому
Если
то
и
Следовательно,
Для и :
Упражнение 2. Естественный шаг из доверительной области
Пусть и . Решите
Получите направление и нормировочный множитель. Что меняется при сингулярной ?
Показать ответ и решение
Лагранжиан:
Стационарность:
поэтому
В оптимуме ограничение активно:
Отсюда
При сингулярной используют псевдообратную матрицу, ограничение на идентифицируемое подпространство или демпфирование . Если целевая функция меняется вдоль ядра матрицы, исходная локальная задача может быть неограниченной.
Упражнение 3. Экспоненциальное семейство и дивергенция Брэгмана
Для
покажите:
и
Показать ответ и решение
Нормировка даёт
Дифференцируя,
Ещё одна производная:
Для KL:
Порядок аргументов развёрнут, потому что expectation и касательная берутся в точке .
Упражнение 4. Модельная и эмпирическая Fisher
Для и модели :
-
получите модельную Fisher ;
-
найдите ;
-
определите, когда две величины совпадают;
-
объясните, почему совпадение в оптимуме не делает эмпирическую Fisher универсальной матрицей кривизны.
Показать ответ и решение
Для Bernoulli log-likelihood в логит-координате:
При :
При данных :
Прямое сравнение показывает, что равенство выполняется при , а также в отдельных случайных точках, например . Но это не универсальное равенство матриц на протяжении обучения.
В оптимуме корректно специфицированной модели часто , поэтому величины сближаются. Вдали от оптимума эмпирический второй момент может заметно переоценивать или иначе искажать геометрию.
Упражнение 5. TRPO, PPO и KL к референсу
-
Запишите ориентацию KL в практическом TRPO.
-
Постройте пример, где отношения PPO на выборке не меняются, а глобальный KL велик.
-
Сравните пошаговый KL и KL к фиксированному референсу в RLHF.
-
Объясните, почему DPO не является обновлением естественного градиента.
Показать ответ и решение
TRPO
Стандартное практическое направление KL:
Контрпример для PPO
Пусть
и пакет данных содержит только первое действие с положительным преимуществом. Рассмотрим
Отношение вероятностей фактически выбранного действия всегда равно 1, поэтому выборочная клиппированная цель не меняется. Но полная KL равна
При первое слагаемое расходится, а второе стремится к конечному , поэтому
Клиппирование не является глобальным ограничением на KL.
Две KL
- пошаговый KL к старой политике стабилизирует конкретное on-policy-обновление;
- KL к фиксированной SFT- или базовой политике ограничивает накопленный дрейф.
DPO
DPO использует идеализированную связь
и правдоподобие предпочтений, но не выполняет явного обновления естественного градиента или TRPO.
Упражнение 6. Flow Matching и связка
Пусть
Сравните:
-
общий шум , ;
-
независимые и .
Для
найдите среднее, дисперсию и кинетическое действие. Покажите, что связка с общим шумом даёт одномерную -геодезическую.
Показать ответ и решение
Связка с общим шумом
Пусть
Тогда
где
Следовательно,
Скорость постоянна:
Кинетическое действие:
Это одномерная оптимальная связка и геодезическая смещения.
Независимая связка
Если независимы,
Кинетическое действие:
Она превышает оптимум на
Конечные маргинальные распределения те же, но связка и весь промежуточный путь отличаются.
Упражнение 7. Python
Запустите
M15_information_geometry_experiments.pyСкрипт:
-
сравнивает точную Bernoulli-KL с квадратичной Fisher-аппроксимацией;
-
показывает зависимость обычного градиента от координат и согласованность потока естественного градиента;
-
сравнивает модельную Fisher со вторым моментом градиента по данным;
-
строит контрпример для PPO;
-
сравнивает оптимальную и независимую связки для гауссовского пути.
Показать ответ и решение
Для квадратичная аппроксимация Fisher приближается к точному KL при уменьшении :
| Точная KL | Квадратичная | Отношение | |
|---|---|---|---|
| 0.1 | 0.02573209 | 0.03125000 | 0.8234 |
| 0.01 | 0.00030499 | 0.00031250 | 0.9760 |
| 0.001 | 0.0000031172 | 0.0000031250 | 0.9975 |
Обычные градиентные траектории в - и логит-координатах заметно различаются. Две эйлеровы дискретизации векторного поля естественного градиента отличаются только из-за конечного шага; при уменьшении шага расхождение стремится к нулю.
При , :
В PPO-примере выборочное отношение вероятностей остаётся 1, а
достигает примерно ната.
Эксперимент с гауссовскими связками показывает разницу кинетических действий между независимой связкой и связкой с общим шумом.
Программная часть решения · один исходный файл для RU/EN
from __future__ import annotations
from pathlib import Path
import math
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import numpy as np
ROOT = Path(__file__).resolve().parent
OUT = ROOT / 'generated_experiment'
OUT.mkdir(exist_ok=True)
def bernoulli_kl(p: np.ndarray | float, q: np.ndarray | float) -> np.ndarray:
p = np.asarray(p, dtype=float)
q = np.asarray(q, dtype=float)
eps = 1e-15
p = np.clip(p, eps, 1 - eps)
q = np.clip(q, eps, 1 - eps)
return p*np.log(p/q) + (1-p)*np.log((1-p)/(1-q))
def local_kl_experiment() -> dict[str, float]:
p0 = 0.2
deltas = np.logspace(-5, -1, 160)
exact = bernoulli_kl(p0, p0+deltas)
fisher = 1/(p0*(1-p0))
quadratic = 0.5*fisher*deltas**2
fig, ax = plt.subplots(figsize=(8.8, 5.3))
ax.loglog(deltas, exact, label='exact KL')
ax.loglog(deltas, quadratic, '--', label=r'$\frac{1}{2}F(p)\delta^2$')
ax.set_xlabel(r'$\delta$')
ax.set_ylabel('nats')
ax.set_title('Bernoulli: local KL and Fisher quadratic approximation')
ax.legend()
fig.tight_layout()
fig.savefig(OUT/'M15_local_kl.png', dpi=190)
plt.close(fig)
print('LOCAL KL')
for d in (0.1, 0.05, 0.01, 0.001):
e=float(bernoulli_kl(p0,p0+d)); q=0.5*fisher*d*d
print(f'delta={d:g} exact={e:.10f} quadratic={q:.10f} ratio={e/q:.6f}')
return {'fisher': fisher, 'ratio_1e3': float(bernoulli_kl(p0,p0+1e-3)/(0.5*fisher*1e-6))}
def sigmoid(z: np.ndarray | float) -> np.ndarray:
return 1/(1+np.exp(-np.asarray(z,dtype=float)))
def logit(p: float) -> float:
return math.log(p/(1-p))
def reparameterization_experiment() -> dict[str,float]:
target=0.8; p0=0.1; dt=0.01; steps=300
time=np.arange(steps+1)*dt
p_nat=np.empty(steps+1); p_nat[0]=p0
z_nat=np.empty(steps+1); z_nat[0]=logit(p0)
for t in range(steps):
p_nat[t+1]=p_nat[t]-dt*(p_nat[t]-target)
p=float(sigmoid(z_nat[t]))
z_nat[t+1]=z_nat[t]-dt*(p-target)/(p*(1-p))
pz_nat=sigmoid(z_nat)
# Coordinate-dependent ordinary-gradient examples.
p_gd=np.empty(steps+1); p_gd[0]=p0
z_gd=np.empty(steps+1); z_gd[0]=logit(p0)
for t in range(steps):
p=np.clip(p_gd[t],1e-6,1-1e-6)
grad=(p-target)/(p*(1-p))
p_gd[t+1]=np.clip(p-0.002*grad,1e-6,1-1e-6)
p=float(sigmoid(z_gd[t])); z_gd[t+1]=z_gd[t]-0.08*(p-target)
pz_gd=sigmoid(z_gd)
exact=target+(p0-target)*np.exp(-time)
fig, ax = plt.subplots(figsize=(9.2,5.4))
ax.plot(time,p_gd,label='ordinary GD in p')
ax.plot(time,pz_gd,label='ordinary GD in logit')
ax.plot(time,p_nat,'--',label='natural Euler in p')
ax.plot(time,pz_nat,':',label='natural Euler in logit')
ax.plot(time,exact,color='black',linewidth=1.2,label='exact natural flow')
ax.set_xlabel('time / iteration scale'); ax.set_ylabel('Bernoulli p')
ax.set_ylim(0,0.9); ax.set_title('Reparameterization: vector field vs finite steps')
ax.legend(fontsize=9); fig.tight_layout(); fig.savefig(OUT/'M15_reparameterization.png',dpi=190); plt.close(fig)
nat=float(np.max(np.abs(p_nat-pz_nat))); ordinary=float(np.max(np.abs(p_gd-pz_gd)))
print('\nREPARAMETERIZATION')
print(f'max ordinary trajectory difference={ordinary:.6f}')
print(f'max natural Euler difference={nat:.6f}')
return {'ordinary_difference':ordinary,'natural_difference':nat}
def empirical_fisher_experiment() -> dict[str,float]:
q=0.8
p=np.linspace(0.01,0.99,300)
model=p*(1-p)
data=q*(1-p)**2+(1-q)*p**2
fig, ax=plt.subplots(figsize=(8.8,5.2))
ax.plot(p,model,label=r'model Fisher $p(1-p)$')
ax.plot(p,data,label='gradient second moment under data')
ax.axvline(q,color='black',linewidth=0.8,alpha=0.5)
ax.set_xlabel('model probability p'); ax.set_ylabel('matrix entry / second moment')
ax.set_title('Model Fisher and observed-gradient second moment differ')
ax.legend(); fig.tight_layout(); fig.savefig(OUT/'M15_empirical_fisher.png',dpi=190); plt.close(fig)
print('\nMODEL VS EMPIRICAL')
vals={}
for p0 in (0.2,0.5,0.8):
f=p0*(1-p0); e=q*(1-p0)**2+(1-q)*p0**2
vals[p0]=(f,e); print(f'p={p0:.1f} model={f:.6f} data={e:.6f}')
return {'model_p02':vals[0.2][0],'data_p02':vals[0.2][1]}
def categorical_kl(p: np.ndarray,q: np.ndarray)->float:
return float(np.sum(p*np.log(p/q)))
def ppo_counterexample() -> dict[str,float]:
old=np.array([0.50,0.49,0.01])
q1=np.geomspace(0.49,1e-8,240)
policies=np.column_stack([np.full_like(q1,0.50),q1,0.50-q1])
kl=np.array([categorical_kl(old,q) for q in policies])
ratio=policies[:,0]/old[0]
surrogate=np.minimum(ratio,np.clip(ratio,0.8,1.2))
x=-np.log10(q1)
fig, ax=plt.subplots(figsize=(9.2,5.3))
ax.plot(x,kl,label=r'$D_{KL}(\pi_{old}\|\pi_{new})$')
ax.set_xlabel(r'$-\log_{10}\pi_{new}(a_1)$'); ax.set_ylabel('KL, nats')
ax2=ax.twinx(); ax2.plot(x,surrogate,'--',color='black',label='sampled PPO surrogate')
ax2.set_ylabel('sampled surrogate')
lines,labels=ax.get_legend_handles_labels(); lines2,labels2=ax2.get_legend_handles_labels()
ax.legend(lines+lines2,labels+labels2,loc='upper left',fontsize=9)
ax.set_title('PPO clipping on sampled actions is not a global KL constraint')
fig.tight_layout(); fig.savefig(OUT/'M15_ppo_counterexample.png',dpi=190); plt.close(fig)
m=float(kl.max()); print('\nPPO COUNTEREXAMPLE'); print(f'max KL={m:.6f} nats; sampled ratio={ratio[0]:.1f}')
return {'max_kl':m}
def flow_coupling_experiment() -> dict[str,float]:
mu0,s0=-1.0,0.7; mu1,s1=2.0,1.4
t=np.linspace(0,1,200)
var_ot=((1-t)*s0+t*s1)**2
var_ind=(1-t)**2*s0**2+t**2*s1**2
action_ot=(mu1-mu0)**2+(s1-s0)**2
action_ind=(mu1-mu0)**2+s0**2+s1**2
fig, ax=plt.subplots(figsize=(8.8,5.2))
ax.plot(t,var_ot,label='shared-noise / OT coupling')
ax.plot(t,var_ind,label='independent coupling')
ax.set_xlabel('t'); ax.set_ylabel(r'$\mathrm{Var}(X_t)$')
ax.set_title('The coupling determines the Gaussian interpolation path')
ax.legend(); fig.tight_layout(); fig.savefig(OUT/'M15_flow_couplings.png',dpi=190); plt.close(fig)
print('\nFLOW COUPLINGS')
print(f'OT action={action_ot:.6f}; independent action={action_ind:.6f}; gap={action_ind-action_ot:.6f}')
return {'ot_action':action_ot,'ind_action':action_ind,'gap':action_ind-action_ot}
def main()->None:
local_kl_experiment(); reparameterization_experiment(); empirical_fisher_experiment(); ppo_counterexample(); flow_coupling_experiment()
print(f'\nSaved plots to {OUT}')
if __name__=='__main__':
main()
Бонус. Probability-flow ODE
Для
выведите уравнение Фоккера—Планка и покажите, что ODE
порождает те же маргинальные распределения . Почему траектории различаются?
Показать ответ и решение
Уравнение Фоккера—Планка для
при скалярном, не зависящем от состояния :
Пусть
Тогда уравнение непрерывности для ODE:
Оно совпадает с уравнением Фоккера—Планка, поэтому маргинальные распределения одинаковы. Траектории различаются: SDE содержит броуновскую случайность, а ODE детерминирована при фиксированном начальном состоянии.