VKВлад КуликовОткрытые лекции
Теория информации для машинного обучения8 / 16

Модуль 8. Максимальная энтропия, экспоненциальные модели и KL-регуляризация

Сначала попробуйте решить упражнение. Ответы, подробные решения и проектные ориентиры раскрываются отдельно и сохраняют своё состояние при переключении RU/EN.

Скачать файлы курса (.zip) 33,9 КБ

Архив содержит девять Python-файлов решений, общих для RU/EN, с сохранением структуры каталогов.

Упражнение 1. MaxEnt-кость

На X={1,2,3,4,5,6}\mathcal X=\{1,2,3,4,5,6\} найдите распределение максимальной энтропии при

E[X]=4.5.\mathbb E[X]=4.5.
  1. Покажите, что

    qλ(x)=eλx∑j=16eλj.q_\lambda(x)=\frac{e^{\lambda x}}{\sum_{j=1}^6e^{\lambda j}}.
  2. Докажите, что среднее строго возрастает по λ\lambda.

  3. Численно найдите λ\lambda, вероятности и энтропию.

  4. Покажите, что

    log⁡26−H2(q∗)=DKL,2(q∗∥U6).\log_2 6-H_2(q^*) = D_{\mathrm{KL},2}(q^*\|U_6).
Показать ответ и решение

Ищем распределение на {1,…,6}\{1,\ldots,6\}, максимизирующее

H(q)=−∑x=16q(x)log⁡q(x)H(q)=-\sum_{x=1}^6q(x)\log q(x)

при ограничениях

∑xq(x)=1,∑xxq(x)=4.5.\sum_xq(x)=1, \qquad \sum_xxq(x)=4.5.

1. Форма решения

Лагранжиан:

L=−∑xq(x)log⁡q(x)+λ(∑xxq(x)−4.5)+ν(∑xq(x)−1).\mathcal L = -\sum_xq(x)\log q(x) +\lambda\left(\sum_xxq(x)-4.5\right) +\nu\left(\sum_xq(x)-1\right).

Для внутреннего решения

−log⁡q(x)−1+λx+ν=0,-\log q(x)-1+\lambda x+\nu=0,

поэтому

qλ(x)=eλxZ(λ),Z(λ)=∑j=16eλj.q_\lambda(x) = \frac{e^{\lambda x}}{Z(\lambda)}, \qquad Z(\lambda)=\sum_{j=1}^6e^{\lambda j}.

2. Почему среднее строго возрастает по λ\lambda

Положим

A(λ)=log⁡Z(λ).A(\lambda)=\log Z(\lambda).

Тогда

A′(λ)=Eqλ[X],A'(\lambda)=\mathbb E_{q_\lambda}[X],

а

ddλEqλ[X]=A′′(λ)=Var⁡qλ(X)>0.\frac{d}{d\lambda}\mathbb E_{q_\lambda}[X] =A''(\lambda) =\operatorname{Var}_{q_\lambda}(X)>0.

Дисперсия положительна для каждого конечного λ\lambda, потому что все шесть вероятностей положительны и XX не является константой. Значит, уравнение

Eqλ[X]=4.5\mathbb E_{q_\lambda}[X]=4.5

имеет не более одного решения. Пределы среднего при λ→−∞\lambda\to-\infty и λ→+∞\lambda\to+\infty равны соответственно 1 и 6, поэтому решение существует и единственно.

3. Численный ответ

Корень уравнения моментов:

λ≈0.371048938081.\boxed{\lambda\approx0.371048938081}.

Вероятности:

q∗≈(0.054353168,0.078771546,0.114159977,0.165446803,0.239774440,0.347494066).\boxed{ q^*\approx (0.054353168, 0.078771546, 0.114159977, 0.165446803, 0.239774440, 0.347494066) }.

Проверка:

∑xxq∗(x)=4.5.\sum_xxq^*(x)=4.5.

Энтропия:

H2(q∗)≈2.327905448 бита.\boxed{H_2(q^*)\approx2.327905448\text{ бита}}.

4. Дефицит энтропии как KL к равномерному распределению

Для U6(x)=1/6U_6(x)=1/6:

DKL,2(q∗∥U6)=∑xq∗(x)log⁡2q∗(x)1/6=∑xq∗(x)log⁡2q∗(x)+log⁡26=log⁡26−H2(q∗).\begin{aligned} D_{\mathrm{KL},2}(q^*\|U_6) &=\sum_xq^*(x)\log_2\frac{q^*(x)}{1/6}\\ &=\sum_xq^*(x)\log_2q^*(x)+\log_2 6\\ &=\log_2 6-H_2(q^*). \end{aligned}

Численно:

log⁡26−H2(q∗)≈0.257057052 бита.\boxed{ \log_2 6-H_2(q^*) \approx0.257057052\text{ бита} }.

Это точное равенство использует равномерную базу на конечном пространстве. Для общего базового распределения аналогичной величиной является DKL(q∥m)D_{\mathrm{KL}}(q\|m), а не просто разность энтропий.

Упражнение 2. Одно среднее — два пространства состояний

Пусть n=10n=10 и E[K]=7\mathbb E[K]=7.

  1. Найдите MaxEnt-форму на пространстве K∈{0,…,10}K\in\{0, \ldots,10\} при равномерной базе.

  2. Найдите MaxEnt-распределение на {0,1}10\{0,1\}^{10} при ограничении E[∑iXi]=7\mathbb E[\sum_iX_i]=7.

  3. Выведите индуцированное распределение числа единиц.

  4. Объясните роль множителя (10k)\binom{10}{k} и почему два ответа не противоречат друг другу.

Показать ответ и решение

Пусть n=10n=10 и E[K]=7\mathbb E[K]=7.

1. Равномерная база на пространстве счётчиков

Если элементарными состояниями являются сами значения

K∈{0,1,…,10},K\in\{0,1,\ldots,10\},

то MaxEnt-решение имеет вид

qA(k)=eλk∑j=010eλj.q_A(k) = \frac{e^{\lambda k}} {\sum_{j=0}^{10}e^{\lambda j}}.

Из условия среднего получается

λ≈0.218710977833.\boxed{\lambda\approx0.218710977833}.

Вероятности приблизительно равны

qA≈(0.02423503,0.03015981,0.03753302,0.04670878,0.05812775,0.07233833,0.09002299,0.11203105,0.13941946,0.17350355,0.21592023).\begin{aligned} q_A\approx(&0.02423503,0.03015981,0.03753302,0.04670878,\\ &0.05812775,0.07233833,0.09002299,0.11203105,\\ &0.13941946,0.17350355,0.21592023). \end{aligned}

Это усечённое геометрическое распределение.

2. Равномерная база на бинарных микросостояниях

Теперь элементарное состояние — вектор

x∈{0,1}10,K(x)=∑i=110xi.x\in\{0,1\}^{10}, \qquad K(x)=\sum_{i=1}^{10}x_i.

MaxEnt-решение:

qB(x)∝exp⁡(λ∑ixi)=∏ieλxi.q_B(x) \propto \exp\left(\lambda\sum_i x_i\right) = \prod_i e^{\lambda x_i}.

Следовательно, координаты независимы и одинаково распределены:

Xi∼Bernoulli⁡(p),10p=7,X_i\sim\operatorname{Bernoulli}(p), \qquad 10p=7,

то есть

p=0.7.\boxed{p=0.7}.

3. Индуцированное распределение числа единиц

Поэтому

K∼Binomial⁡(10,0.7)\boxed{ K\sim\operatorname{Binomial}(10,0.7) }

и

qB(k)=(10k)0.7k0.310−k.q_B(k) = \binom{10}{k}0.7^k0.3^{10-k}.

4. Почему ответы различаются

В первой постановке каждому kk соответствует ровно одно состояние. Во второй одному и тому же kk соответствуют

(10k)\binom{10}{k}

микросостояний. Поэтому референс, индуцированный на пространстве счётчиков, не равномерен, а пропорционален (10k)\binom{10}{k}:

qB(k)∝(10k)eλk.q_B(k) \propto \binom{10}{k}e^{\lambda k}.

Противоречия нет: это две разные вероятностные модели. Ограничение на среднее не определяет MaxEnt-ответ без выбора пространства состояний и базовой меры.

Упражнение 3. Вариационная формула softmax

Для τ>0\tau>0 докажите

τlog⁡∑iezi/τ=max⁡q∈Δ{q⊤z+τH(q)}.\tau\log\sum_i e^{z_i/\tau} = \max_{q\in\Delta} \{q^\top z+\tau H(q)\}.

Покажите также, что разрыв для произвольного qq равен

τDKL(q∥softmax⁡(z/τ)).\tau D_{\mathrm{KL}} \left(q\|\operatorname{softmax}(z/\tau)\right).
Показать ответ и решение

Пусть

si=ezi/τ∑jezj/τ,τ>0.s_i = \frac{e^{z_i/\tau}} {\sum_j e^{z_j/\tau}}, \qquad \tau>0.

Для любого q∈Δq\in\Delta:

q⊤z+τH(q)=∑iqizi−τ∑iqilog⁡qi=τ∑iqilog⁡ezi/τqi=τ∑iqilog⁡Zsiqi=τlog⁡Z−τDKL(q∥s),\begin{aligned} q^\top z+\tau H(q) &=\sum_iq_iz_i-\tau\sum_iq_i\log q_i\\ &=\tau\sum_iq_i \log\frac{e^{z_i/\tau}}{q_i}\\ &=\tau\sum_iq_i \log\frac{Zs_i}{q_i}\\ &=\tau\log Z -\tau D_{\mathrm{KL}}(q\|s), \end{aligned}

где

Z=∑iezi/τ.Z=\sum_i e^{z_i/\tau}.

Следовательно,

q⊤z+τH(q)≤τlog⁡Z,q^\top z+\tau H(q) \le \tau\log Z,

причём равенство достигается единственно при q=sq=s. Значит,

τlog⁡∑iezi/τ=max⁡q∈Δ{q⊤z+τH(q)}.\boxed{ \tau\log\sum_i e^{z_i/\tau} = \max_{q\in\Delta} \{q^\top z+\tau H(q)\} }.

Кроме того, точный разрыв равен

τlog⁡∑iezi/τ−(q⊤z+τH(q))=τDKL(q∥s).\boxed{ \tau\log\sum_i e^{z_i/\tau} - \bigl(q^\top z+\tau H(q)\bigr) = \tau D_{\mathrm{KL}}(q\|s) }.

Эта формула сильнее простого указания оптимума: она измеряет, насколько произвольное распределение хуже softmax-решения по заданной целевой функции.

Упражнение 4. Условный MaxEnt и согласование моментов

Для условной лог-линейной модели

qθ(y∣x)=eθ⊤f(x,y)∑y′eθ⊤f(x,y′)q_\theta(y\mid x) = \frac{e^{\theta^\top f(x,y)}} {\sum_{y'}e^{\theta^\top f(x,y')}}

выведите градиент условного логарифмического правдоподобия и сформулируйте точные условия, при которых нулевой градиент означает совпадение эмпирических и модельных средних признаков.

Показать ответ и решение

Для одного объекта

log⁡qθ(yi∣xi)=θ⊤f(xi,yi)−log⁡∑y′eθ⊤f(xi,y′).\log q_\theta(y_i\mid x_i) = \theta^\top f(x_i,y_i) - \log\sum_{y'}e^{\theta^\top f(x_i,y')}.

Дифференцируя:

∇θlog⁡qθ(yi∣xi)=f(xi,yi)−∑y′qθ(y′∣xi)f(xi,y′).\begin{aligned} \nabla_\theta\log q_\theta(y_i\mid x_i) &=f(x_i,y_i)\\ &\quad- \sum_{y'}q_\theta(y'\mid x_i)f(x_i,y'). \end{aligned}

После суммирования по выборке:

∇θℓ(θ)=∑if(xi,yi)−∑iEY∼qθ(⋅∣xi)[f(xi,Y)].\boxed{ \nabla_\theta\ell(\theta) = \sum_i f(x_i,y_i) - \sum_i\mathbb E_{Y\sim q_\theta(\cdot\mid x_i)}[f(x_i,Y)] }.

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

∑if(xi,yi)=∑iEqθ(⋅∣xi)[f(xi,Y)].\sum_i f(x_i,y_i) = \sum_i\mathbb E_{q_\theta(\cdot\mid x_i)}[f(x_i,Y)].

Для условной лог-линейной модели логарифмическое правдоподобие вогнуто по θ\theta, поэтому любая конечная стационарная точка является глобальным максимумом. Единственность параметров дополнительно требует минимальности или идентифицируемости набора признаков.

Оговорки:

  • при L2L_2-регуляризации правая и левая части различаются на градиент штрафа;
  • при разделимых данных конечная оценка максимального правдоподобия может отсутствовать;
  • линейно зависимые признаки допускают разные θ\theta с одним и тем же распределением;
  • для глубокой сети аналогичное равенство для всех внутренних признаков не следует, потому что модель не является линейной экспоненциальной семьёй по всем весам.

Упражнение 5. Gibbs-оптимум политики

Пусть

πref=(0.6,0.3,0.1),r=(0,1,3),β=1.\pi_{\mathrm{ref}}=(0.6,0.3,0.1), \qquad r=(0,1,3), \qquad \beta=1.
  1. Найдите π∗\pi^*.

  2. Посчитайте ZZ и оптимальное значение J(π∗)J(\pi^*).

  3. Для π=(1/3,1/3,1/3)\pi=(1/3,1/3,1/3) проверьте

    J(π∗)−J(π)=βDKL(π∥π∗).J(\pi^*)-J(\pi) = \beta D_{\mathrm{KL}}(\pi\|\pi^*).
  4. Объясните, что произойдёт с действием, которому базовая политика назначает нулевую вероятность.

Показать ответ и решение

Дано

πref=(0.6,0.3,0.1),r=(0,1,3),β=1.\pi_{\mathrm{ref}}=(0.6,0.3,0.1), \qquad r=(0,1,3), \qquad \beta=1.

1. Оптимальная политика

Ненормированные веса:

πref,ieri=(0.6,0.3e,0.1e3).\pi_{\mathrm{ref},i}e^{r_i} = (0.6,0.3e,0.1e^3).

Нормировочная константа:

Z=0.6+0.3e+0.1e3≈3.424038241.\boxed{ Z=0.6+0.3e+0.1e^3 \approx3.424038241 }.

Следовательно,

π∗≈(0.17523169,0.23816456,0.58660376).\boxed{ \pi^* \approx (0.17523169, 0.23816456, 0.58660376) }.

Высокая награда третьего действия перевешивает его малую вероятность в базовой политике, но сама база всё ещё участвует мультипликативно.

2. Оптимальное значение

При β=1\beta=1:

J(π∗)=log⁡Z≈1.230820627.\boxed{ J(\pi^*)=\log Z\approx1.230820627 }.

3. Проверка разрыва целевой функции

Для

π=(1/3,1/3,1/3)\pi=(1/3,1/3,1/3)

получаем

J(π)=Eπ[r]−DKL(π∥πref)≈1.092817782.J(\pi) = \mathbb E_\pi[r] -D_{\mathrm{KL}}(\pi\|\pi_{\mathrm{ref}}) \approx1.092817782.

Поэтому

J(π∗)−J(π)≈0.138002845.J(\pi^*)-J(\pi) \approx0.138002845.

Прямой расчёт даёт

DKL(π∥π∗)≈0.138002845,D_{\mathrm{KL}}(\pi\|\pi^*) \approx0.138002845,

что подтверждает

J(π∗)−J(π)=βDKL(π∥π∗).J(\pi^*)-J(\pi) = \beta D_{\mathrm{KL}}(\pi\|\pi^*).

4. Нулевая вероятность в базовой политике

Если

πref(y)=0,\pi_{\mathrm{ref}}(y)=0,

то Gibbs-формула даёт

π∗(y)=0\pi^*(y)=0

для любой конечной награды. Любая политика с π(y)>0\pi(y)>0 имеет

DKL(π∥πref)=+∞.D_{\mathrm{KL}}(\pi\|\pi_{\mathrm{ref}})=+\infty.

То есть KL-регуляризованная оптимизация не создаёт носитель, отсутствующий у базовой политики. В практической LLM softmax обычно назначает всем токенам положительную вероятность, но для последовательностей эффективный носитель может быть ограничен маскированием, грамматическими ограничениями или нулевой машинной вероятностью.

Упражнение 6. Python: MaxEnt, softmax и переоптимизация награды

Запустите файл M8_maxent_softmax_policy.py. Он выполняет четыре воспроизводимых эксперимента:

  1. решает MaxEnt-задачу для кости со средним 4.54.5;

  2. сравнивает усечённое геометрическое и биномиальное распределения при одном среднем;

  3. численно проверяет вариационную формулу softmax;

  4. строит траекторию регуляризации для намеренно неточной прокси-награды с действием, эксплуатирующим модель награды (reward hacking).

Объясните, почему последний эксперимент демонстрирует возможность, а не универсальную неизбежность ухудшения истинного качества.

Показать ответ и решение

Полное проверенное решение находится в файле

text
M8_maxent_softmax_policy.py

Он создаёт четыре PNG-файла в каталоге generated_experiment/ и печатает воспроизводимые численные результаты.

Ключевые значения:

text
MaxEnt die:
lambda = 0.371048938081
entropy = 2.327905448379 bits
log2(6) - H = KL(p || uniform) = 0.257057052342 bits

Count state space, n=10, E[K]=7:
lambda = 0.218710977833
entropy = 3.158029428 bits

Binary microstate space:
K ~ Binomial(10, 0.7)
entropy of K = 2.566668139 bits

Softmax identity:
objective gap for random q
= tau * KL(q || softmax)
= 0.582011246754

Reward-overoptimization toy:
true reward peaks near beta = 0.397530
peak expected true reward = -0.272802779
at beta = 0.02 the hacking action receives probability ~1,
proxy reward tends to 0.8, while true reward tends to -3.

Последний эксперимент намеренно построен так, чтобы существовало редкое действие с высокой прокси-наградой и плохой истинной наградой. По мере уменьшения β\beta политика в конце концов концентрируется на нём.

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

ddαEπα[rtrue]=Cov⁡πα(rtrue,rproxy).\frac{d}{d\alpha} \mathbb E_{\pi_\alpha}[r_{\mathrm{true}}] = \operatorname{Cov}_{\pi_\alpha} (r_{\mathrm{true}},r_{\mathrm{proxy}}).

Программная часть решения · один исходный файл для RU/EN

PythonM8_maxent_softmax_policy.pyСкачать .py
"""Reproducible experiments for Module 8.

The script demonstrates:
1. maximum entropy on a die with a prescribed mean;
2. dependence of MaxEnt on the chosen state space;
3. the softmax variational identity;
4. a finite-action reward-overoptimization path.

Run:
    python M8_maxent_softmax_policy.py

Dependencies:
    numpy, scipy, matplotlib
"""

from __future__ import annotations

from pathlib import Path
import math

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
from scipy.optimize import brentq
from scipy.special import logsumexp


OUTPUT_DIR = Path(__file__).resolve().parent / "generated_experiment"
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)


def normalize_log_weights(log_weights: np.ndarray) -> np.ndarray:
    """Return normalized probabilities from unnormalized log weights."""
    log_weights = np.asarray(log_weights, dtype=float)
    return np.exp(log_weights - logsumexp(log_weights))


def entropy_bits(probabilities: np.ndarray) -> float:
    """Shannon entropy in bits."""
    p = np.asarray(probabilities, dtype=float)
    positive = p > 0
    return float(-np.sum(p[positive] * np.log2(p[positive])))


def kl_nats(p: np.ndarray, q: np.ndarray) -> float:
    """KL(p || q) in nats for strictly positive q on the support of p."""
    p = np.asarray(p, dtype=float)
    q = np.asarray(q, dtype=float)
    positive = p > 0
    if np.any(q[positive] <= 0):
        return float("inf")
    return float(np.sum(p[positive] * (np.log(p[positive]) - np.log(q[positive]))))


def solve_exponential_tilt(states: np.ndarray, target_mean: float) -> tuple[float, np.ndarray]:
    """Solve p_lambda(x) proportional to exp(lambda*x) with a target mean."""
    states = np.asarray(states, dtype=float)
    if states.ndim != 1 or states.size == 0 or not np.all(np.isfinite(states)):
        raise ValueError("states must be a nonempty finite one-dimensional array")
    low, high = states.min(), states.max()
    if not (low <= target_mean <= high):
        raise ValueError("target_mean must lie in the convex hull of states")

    if low == high:
        return 0.0, np.full(states.size, 1.0 / states.size)
    if target_mean == low:
        p = (states == low).astype(float)
        p /= p.sum()
        return float("-inf"), p
    if target_mean == high:
        p = (states == high).astype(float)
        p /= p.sum()
        return float("inf"), p

    # Rescale the statistic; this leaves the distribution unchanged and makes
    # bracketing independent of the units in which states are measured.
    scaled = (states - low) / (high - low)
    target = (target_mean - low) / (high - low)

    def probabilities(eta: float) -> np.ndarray:
        return normalize_log_weights(eta * scaled)

    def residual(eta: float) -> float:
        return float(probabilities(eta) @ scaled - target)

    left, right = -1.0, 1.0
    while residual(left) > 0:
        left *= 2.0
    while residual(right) < 0:
        right *= 2.0
    root = brentq(residual, left, right, xtol=1e-12)
    return float(root / (high - low)), probabilities(root)


def experiment_die(target_mean: float = 4.5) -> dict[str, object]:
    """MaxEnt distribution on a six-sided die with a prescribed mean."""
    states = np.arange(1, 7, dtype=float)
    lam, p = solve_exponential_tilt(states, target_mean)
    entropy = entropy_bits(p)
    uniform = np.full_like(p, 1.0 / len(p))
    deficit = np.log2(len(p)) - entropy
    kl_to_uniform_bits = kl_nats(p, uniform) / np.log(2.0)

    assert np.isclose(p.sum(), 1.0, atol=1e-12)
    assert np.isclose(p @ states, target_mean, atol=1e-11)
    assert np.isclose(deficit, kl_to_uniform_bits, atol=1e-12)

    print("\n=== Experiment 1: MaxEnt die ===")
    print(f"lambda = {lam:.12f}")
    print("probabilities =", np.array2string(p, precision=9))
    print(f"mean = {p @ states:.12f}")
    print(f"entropy = {entropy:.12f} bits")
    print(f"log2(6) - H = {deficit:.12f} bits")
    print(f"KL(p || uniform) = {kl_to_uniform_bits:.12f} bits")

    plt.figure(figsize=(7.2, 4.6))
    plt.bar(states.astype(int), p)
    plt.xlabel("Die face")
    plt.ylabel("Probability")
    plt.title(f"Maximum-entropy die with E[X] = {target_mean:g}")
    plt.tight_layout()
    plt.savefig(OUTPUT_DIR / "M8_die_maxent.png", dpi=180)
    plt.close()

    return {
        "lambda": lam,
        "probabilities": p,
        "entropy_bits": entropy,
        "deficit_bits": deficit,
    }


def experiment_state_space() -> dict[str, object]:
    """Compare two MaxEnt problems with n=10 and E[K]=7."""
    n = 10
    target_mean = 7.0
    counts = np.arange(n + 1, dtype=float)

    # Model A: the count k itself is one state, with a uniform reference on counts.
    lam_count, p_count = solve_exponential_tilt(counts, target_mean)

    # Model B: uniform reference over 2^n binary microstates.
    bernoulli_p = target_mean / n
    p_binomial = np.array(
        [
            float(math.comb(n, int(k)))
            * bernoulli_p**k
            * (1.0 - bernoulli_p) ** (n - k)
            for k in counts
        ],
        dtype=float,
    )

    p_binomial /= p_binomial.sum()

    assert np.isclose(p_count @ counts, target_mean, atol=1e-10)
    assert np.isclose(p_binomial @ counts, target_mean, atol=1e-10)
    assert not np.allclose(p_count, p_binomial)

    print("\n=== Experiment 2: one mean, two state spaces ===")
    print(f"count-space lambda = {lam_count:.12f}")
    print("count-space probabilities =", np.array2string(p_count, precision=8))
    print("binomial probabilities   =", np.array2string(p_binomial, precision=8))
    print(f"count-space entropy = {entropy_bits(p_count):.9f} bits")
    print(f"binomial entropy    = {entropy_bits(p_binomial):.9f} bits")

    width = 0.38
    plt.figure(figsize=(8.2, 4.8))
    plt.bar(counts - width / 2, p_count, width=width, label="Uniform reference on counts")
    plt.bar(counts + width / 2, p_binomial, width=width, label="Uniform reference on microstates")
    plt.xlabel("Number of ones K")
    plt.ylabel("Probability")
    plt.title("Same mean E[K] = 7, different state spaces")
    plt.legend()
    plt.tight_layout()
    plt.savefig(OUTPUT_DIR / "M8_state_space.png", dpi=180)
    plt.close()

    return {
        "lambda_count": lam_count,
        "count_probabilities": p_count,
        "binomial_probabilities": p_binomial,
    }


def softmax(logits: np.ndarray, temperature: float = 1.0) -> np.ndarray:
    """Stable softmax."""
    if temperature <= 0:
        raise ValueError("temperature must be positive")
    z = np.asarray(logits, dtype=float) / temperature
    return normalize_log_weights(z)


def entropy_nats(probabilities: np.ndarray) -> float:
    """Shannon entropy in nats."""
    p = np.asarray(probabilities, dtype=float)
    positive = p > 0
    return float(-np.sum(p[positive] * np.log(p[positive])))


def experiment_softmax_variational() -> dict[str, float]:
    """Numerically verify the softmax variational formula."""
    logits = np.array([2.0, 0.5, -1.0, 1.2], dtype=float)
    temperature = 0.7
    optimum = softmax(logits, temperature)
    optimum_value = temperature * logsumexp(logits / temperature)
    direct_value = float(optimum @ logits + temperature * entropy_nats(optimum))

    rng = np.random.default_rng(8)
    candidate = rng.dirichlet(np.ones(len(logits)))
    candidate_value = float(candidate @ logits + temperature * entropy_nats(candidate))
    gap = optimum_value - candidate_value
    predicted_gap = temperature * kl_nats(candidate, optimum)

    assert np.isclose(optimum_value, direct_value, atol=1e-12)
    assert gap >= -1e-12
    assert np.isclose(gap, predicted_gap, atol=1e-12)

    print("\n=== Experiment 3: softmax variational identity ===")
    print("softmax optimum =", np.array2string(optimum, precision=9))
    print(f"tau * logsumexp(z/tau) = {optimum_value:.12f}")
    print(f"objective at optimum    = {direct_value:.12f}")
    print(f"gap for random q        = {gap:.12f}")
    print(f"tau * KL(q || softmax)  = {predicted_gap:.12f}")

    return {
        "optimum_value": optimum_value,
        "candidate_gap": gap,
    }


def experiment_reward_overoptimization() -> dict[str, object]:
    """A finite-action example in which proxy reward rises while true reward eventually falls."""
    regular_action = np.linspace(-2.0, 2.0, 100)
    true_regular = -(regular_action - 0.75) ** 2
    proxy_regular = true_regular + 0.03 * np.sin(5.0 * regular_action)

    # Add a rare action that looks attractive to the proxy but is poor under the true objective.
    true_reward = np.concatenate([true_regular, np.array([-3.0])])
    proxy_reward = np.concatenate([proxy_regular, np.array([0.8])])
    reference = np.concatenate([np.full(100, 0.999 / 100.0), np.array([0.001])])

    betas = np.geomspace(3.0, 0.02, 120)
    inverse_betas = 1.0 / betas
    expected_proxy = np.empty_like(betas)
    expected_true = np.empty_like(betas)
    kl_to_reference = np.empty_like(betas)
    hack_probability = np.empty_like(betas)

    for i, beta in enumerate(betas):
        policy = normalize_log_weights(np.log(reference) + proxy_reward / beta)
        expected_proxy[i] = policy @ proxy_reward
        expected_true[i] = policy @ true_reward
        kl_to_reference[i] = kl_nats(policy, reference)
        hack_probability[i] = policy[-1]

    # As beta decreases, alpha=1/beta increases. The exact identities predict monotone
    # proxy reward and monotone KL along this finite Gibbs path.
    assert np.all(np.diff(expected_proxy) >= -1e-10)
    assert np.all(np.diff(kl_to_reference) >= -1e-10)
    peak_index = int(np.argmax(expected_true))
    assert 0 < peak_index < len(expected_true) - 1
    assert expected_true[-1] < expected_true[peak_index] - 1.0

    print("\n=== Experiment 4: reward overoptimization ===")
    print(f"true reward peaks at beta = {betas[peak_index]:.6f}")
    print(f"peak expected true reward = {expected_true[peak_index]:.9f}")
    print(f"final expected true reward = {expected_true[-1]:.9f}")
    print(f"final expected proxy reward = {expected_proxy[-1]:.9f}")
    print(f"final KL(policy || reference) = {kl_to_reference[-1]:.9f} nats")
    print(f"final reward-hacking action probability = {hack_probability[-1]:.9f}")

    plt.figure(figsize=(7.6, 4.8))
    plt.plot(inverse_betas, expected_proxy, label="Expected proxy reward")
    plt.plot(inverse_betas, expected_true, label="Expected true reward")
    plt.xscale("log")
    plt.xlabel("Optimization pressure 1/beta")
    plt.ylabel("Expected reward")
    plt.title("A possible reward-overoptimization path")
    plt.legend()
    plt.tight_layout()
    plt.savefig(OUTPUT_DIR / "M8_reward_overoptimization.png", dpi=180)
    plt.close()

    plt.figure(figsize=(7.6, 4.8))
    plt.plot(inverse_betas, kl_to_reference, label="KL to reference")
    plt.plot(inverse_betas, hack_probability, label="Probability of hacking action")
    plt.xscale("log")
    plt.xlabel("Optimization pressure 1/beta")
    plt.ylabel("Value")
    plt.title("Policy drift along the Gibbs path")
    plt.legend()
    plt.tight_layout()
    plt.savefig(OUTPUT_DIR / "M8_policy_drift.png", dpi=180)
    plt.close()

    return {
        "betas": betas,
        "expected_proxy": expected_proxy,
        "expected_true": expected_true,
        "kl_to_reference": kl_to_reference,
        "hack_probability": hack_probability,
        "peak_index": peak_index,
    }


def main() -> None:
    experiment_die()
    state_space = experiment_state_space()
    softmax_result = experiment_softmax_variational()
    reward_path = experiment_reward_overoptimization()

    print("\nGenerated figures:")
    for path in sorted(OUTPUT_DIR.glob("*.png")):
        print(" -", path.name)

    # Compact final checks that also serve as machine-readable invariants.
    assert np.isclose(state_space["count_probabilities"].sum(), 1.0)
    assert softmax_result["candidate_gap"] >= 0
    assert reward_path["expected_true"][-1] < reward_path["expected_true"][reward_path["peak_index"]]


if __name__ == "__main__":
    main()

Бонус. Гауссовское распределение как MaxEnt через KL

Пусть pp — плотность с конечной дифференциальной энтропией, средним μ\mu и дисперсией σ2\sigma^2. Используя

DKL(p∥N(μ,σ2))≥0,D_{\mathrm{KL}} \bigl(p\|\mathcal N(\mu,\sigma^2)\bigr) \ge0,

докажите

h(p)≤12log⁡(2πeσ2),h(p) \le \frac12\log(2\pi e\sigma^2),

и найдите условие равенства.

Показать ответ и решение

Пусть

g(x)=12πσ2exp⁡[−(x−μ)22σ2].g(x) = \frac1{\sqrt{2\pi\sigma^2}} \exp\left[-\frac{(x-\mu)^2}{2\sigma^2}\right].

Для любой плотности pp с теми же средним и дисперсией:

DKL(p∥g)=∫plog⁡p−∫plog⁡g≥0.D_{\mathrm{KL}}(p\|g) = \int p\log p-\int p\log g \ge0.

Первый член равен −h(p)-h(p). Для второго:

∫p(x)log⁡g(x) dx=−12log⁡(2πσ2)−12σ2Ep[(X−μ)2]=−12log⁡(2πσ2)−12.\begin{aligned} \int p(x)\log g(x)\,dx &=-\frac12\log(2\pi\sigma^2) - \frac1{2\sigma^2} \mathbb E_p[(X-\mu)^2]\\ &=-\frac12\log(2\pi\sigma^2)-\frac12. \end{aligned}

Следовательно,

DKL(p∥g)=−h(p)+12log⁡(2πeσ2)≥0.D_{\mathrm{KL}}(p\|g) = -h(p)+\frac12\log(2\pi e\sigma^2) \ge0.

Отсюда

h(p)≤12log⁡(2πeσ2).\boxed{ h(p) \le \frac12\log(2\pi e\sigma^2) }.

Равенство в неравенстве Гиббса возможно тогда и только тогда, когда p=gp=g почти всюду. Значит, гауссовское распределение — единственный максимизатор при заданных среднем и дисперсии.