Модуль 5. Взаимная информация: сколько один объект говорит о другом
Сначала попробуйте решить упражнение. Ответы, подробные решения и проектные ориентиры раскрываются отдельно и сохраняют своё состояние при переключении RU/EN.
Скачать файлы курса (.zip) 33,9 КБАрхив содержит девять Python-файлов решений, общих для RU/EN, с сохранением структуры каталогов.
Упражнение 1. Нелинейная зависимость без корреляции
Пусть равномерно распределена на , а .
-
Найдите , и .
-
Найдите и в битах.
-
Объясните, почему пример не противоречит утверждению .
Показать ответ и решение
Пусть
Из симметрии
Далее,
Поскольку ,
Следовательно,
При этом принимает значения и с вероятностями , поэтому
Так как — детерминированная дискретная функция ,
и
Противоречия нет. Равенство нулю получила ковариация, а не MI. Переменные явно зависимы: зная , мы точно знаем . Поэтому , как и должно быть.
Упражнение 2. XOR и провал одномерного отбора признаков
Пусть и .
-
Покажите, что .
-
Найдите и .
-
Объясните, какой тип ошибки возникает при ранжировании признаков по .
Показать ответ и решение
Пусть и — независимые честные биты, а
Зафиксируем . Поскольку остаётся честным битом, также равномерна на . Значит, распределение не зависит от значения :
Следовательно,
По симметрии
Но пара однозначно определяет , а — честный бит. Поэтому
и
После фиксации значение однозначно определяет и наоборот. Поэтому
и
Индивидуальное ранжирование по присвоит обоим признакам нулевую релевантность и может удалить их. Ошибка состоит в игнорировании синергии: информация появляется только в комбинации признаков.
Упражнение 3. Сжатие нерелевантных факторов без потери целевой информации
Пусть и — независимые честные биты, а вход .
Рассмотрите представления
-
Найдите , , и .
-
Какое представление удаляет нерелевантный фактор, не теряя информацию о целевой переменной?
-
Почему желаемая компрессия относится к , а не к намеренному уменьшению ?
Показать ответ и решение
Имеем два независимых честных бита и , вход
и представления
Поскольку входит в без искажений,
Для :
Для :
— выход binary symmetric channel с crossover probability . При равномерном входе
Итак,
Именно удаляет нерелевантный фактор (nuisance) , не теряя информации о целевой переменной. Это видно и по объёму сохранённой информации о входе:
тогда как
Хорошее сжатие под конкретную задачу (task-specific compression) стремится сохранить близкой к , одновременно уменьшая ненужную часть . Намеренно уменьшать — значит выбрасывать информацию, полезную для предсказания целевой переменной.
Упражнение 4. Python: корреляция, оценка MI и перестановочная проверка
Сгенерируйте три набора данных:
где независимы.
Для нескольких размеров выборки:
-
посчитайте корреляцию Пирсона;
-
оцените MI методом ближайших соседей;
-
случайно переставьте значения и получите перестановочный контрольный уровень;
-
повторите эксперимент для нескольких начальных состояний генератора.
Цель — увидеть нелинейную зависимость при почти нулевой корреляции, ненулевой контрольный уровень на конечной выборке и нестабильность оценивателя при малом числе наблюдений.
Показать ответ и решение
Ниже один воспроизводимый вариант эксперимента. mutual_info_regression использует оцениватель на основе ближайших соседей (kNN estimator) и возвращает результат в натах. Отрицательные численные значения библиотека обрезает до нуля, поэтому перестановочная базовая линия (permutation baseline) обычно имеет небольшое положительное среднее.
import numpy as np
import pandas as pd
from sklearn.feature_selection import mutual_info_regression
def estimate_once(n: int, seed: int) -> list[dict[str, float | int | str]]:
rng = np.random.default_rng(seed)
x = rng.normal(size=n)
datasets = {
"linear": x + 0.5 * rng.normal(size=n),
"nonlinear": x**2 + 0.3 * rng.normal(size=n),
"independent": rng.normal(size=n),
}
rows = []
for name, y in datasets.items():
corr = float(np.corrcoef(x, y)[0, 1])
mi = float(
mutual_info_regression(
x.reshape(-1, 1),
y,
n_neighbors=5,
random_state=seed,
)[0]
)
y_perm = rng.permutation(y)
mi_perm = float(
mutual_info_regression(
x.reshape(-1, 1),
y_perm,
n_neighbors=5,
random_state=seed,
)[0]
)
rows.append(
{
"n": n,
"relation": name,
"corr": corr,
"mi_nats": mi,
"mi_perm_nats": mi_perm,
}
)
return rows
records = []
for n in (200, 1000, 5000):
for seed in range(30):
records.extend(estimate_once(n, seed))
df = pd.DataFrame(records)
summary = (
df.groupby(["n", "relation"])
.agg(
corr_mean=("corr", "mean"),
corr_sd=("corr", "std"),
mi_mean=("mi_nats", "mean"),
mi_sd=("mi_nats", "std"),
perm_mean=("mi_perm_nats", "mean"),
perm_sd=("mi_perm_nats", "std"),
)
.reset_index()
)
with pd.option_context("display.max_rows", None, "display.width", 160):
print(summary.round(4).to_string(index=False))Для указанного протокола с 30 начальными состояниями генератора получаем:
n relation corr_mean corr_sd mi_mean mi_sd perm_mean perm_sd
200 independent 0.0057 0.0686 0.0177 0.0287 0.0141 0.0204
200 linear 0.8934 0.0140 0.8143 0.0761 0.0070 0.0132
200 nonlinear -0.0556 0.1287 0.9368 0.0804 0.0151 0.0255
1000 independent 0.0041 0.0269 0.0060 0.0097 0.0045 0.0067
1000 linear 0.8950 0.0062 0.8235 0.0338 0.0086 0.0096
1000 nonlinear -0.0279 0.0688 1.0913 0.0392 0.0052 0.0076
5000 independent 0.0000 0.0131 0.0037 0.0050 0.0048 0.0052
5000 linear 0.8949 0.0024 0.8127 0.0127 0.0030 0.0050
5000 nonlinear 0.0011 0.0284 1.1378 0.0119 0.0013 0.0030Главные наблюдения:
-
Для линейной зависимости и корреляция, и MI велики.
-
Для средняя корреляция стремится к нулю, но MI остаётся высокой.
-
Для независимых данных оценка MI не обязана быть ровно нулевой на конечной выборке.
-
С ростом разброс уменьшается, а перестановочная базовая линия приближается к нулю.
-
Абсолютное значение зависит от оценивателя,
n_neighbors, масштабирования и размера выборки; здесь важнее сравнительная картина.
Упражнение 5. Python: чему равна граница InfoNCE на известном канале
Пусть равномерна на классах, а совпадает с с вероятностью и иначе равномерно выбирается среди остальных классов.
- Выведите точную MI:
- Используйте идеальную функцию совместимости
и методом Монте-Карло оцените InfoNCE-потерю для .
-
Сравните с точной MI.
-
Проверьте, что ограничивает эту границу, но не саму MI и не качество представления.
Математическое углубление. Неравенство Фано
Если принимает значений, а строится по , то
где . Используйте формулу, чтобы связать остаточную неопределённость с информационно-теоретическим ограничением на достижимую долю ошибок. Отдельно объясните, почему граница может быть слабой и не заменяет построение байесовски оптимального классификатора.
Показать ответ и решение
Пусть равномерна на классах, а канал оставляет класс без изменения с вероятностью и с вероятностью выбирает один из остальных классов равномерно.
Из симметрии также равномерна, поэтому
Условное распределение имеет одну вероятность и вероятностей . Следовательно,
и
В коде ниже используются натуральные логарифмы. Отрицательные примеры (negatives) независимо выбираются из маргинального распределения , а функция оценки (critic) равна идеальному логарифму отношения плотностей.
Можно проверить и или : в энтропии принимаем , а невозможной паре присваиваем critic . Положительная пара генерируется самим каналом, поэтому её score остаётся конечным и знаменатель InfoNCE не обращается в ноль.
import math
import numpy as np
from scipy.special import logsumexp
K = 64
epsilon = 0.25
rng = np.random.default_rng(20260712)
if K < 2 or not 0.0 <= epsilon <= 1.0:
raise ValueError("Use K >= 2 and 0 <= epsilon <= 1")
# Exact MI in nats for the K-class symmetric channel.
h_eps = -sum(p * math.log(p) for p in (epsilon, 1.0 - epsilon) if p > 0.0)
mi_exact = math.log(K) - h_eps - epsilon * math.log(K - 1)
def sample_positive(x: np.ndarray) -> np.ndarray:
"""Sample Y from p(y|x) without rejection loops."""
y = x.copy()
flip = rng.random(x.size) < epsilon
offsets = rng.integers(1, K, size=flip.sum())
y[flip] = (x[flip] + offsets) % K
return y
def estimate_infonce(n_candidates: int, total_trials: int = 100_000, chunk_size: int = 2_000) -> tuple[float, float]:
loss_sum = 0.0
loss_sq_sum = 0.0
seen = 0
log_ratio_match = math.log(K * (1.0 - epsilon)) if epsilon < 1.0 else -math.inf
log_ratio_mismatch = math.log(K * epsilon / (K - 1)) if epsilon > 0.0 else -math.inf
while seen < total_trials:
batch = min(chunk_size, total_trials - seen)
x = rng.integers(0, K, size=batch)
positive = sample_positive(x)
negatives = rng.integers(0, K, size=(batch, n_candidates - 1))
candidates = np.concatenate([positive[:, None], negatives], axis=1)
scores = np.where(
candidates == x[:, None],
log_ratio_match,
log_ratio_mismatch,
)
losses = logsumexp(scores, axis=1) - scores[:, 0]
loss_sum += float(losses.sum())
loss_sq_sum += float(np.square(losses).sum())
seen += batch
mean_loss = loss_sum / total_trials
variance = max(loss_sq_sum / total_trials - mean_loss**2, 0.0)
standard_error = math.sqrt(variance / total_trials)
return mean_loss, standard_error
print(f"Exact MI: {mi_exact:.6f} nats ({mi_exact / math.log(2):.6f} bits)")
print(" N loss bound log(N) SE(bound)")
for n_candidates in (4, 16, 64, 256):
loss, se = estimate_infonce(n_candidates)
bound = math.log(n_candidates) - loss
print(
f"{n_candidates:3d} {loss:9.6f} {bound:9.6f} "
f"{math.log(n_candidates):9.6f} {se:9.6f}"
)Результат:
Exact MI: 2.560764 nats (3.694402 bits)
N loss bound log(N) SE(bound)
4 0.429934 0.956360 1.386294 0.002479
16 0.991886 1.780702 2.772589 0.004612
64 1.867571 2.291312 4.158883 0.006330
256 3.061057 2.484121 5.545177 0.007008В этом запуске все оценки Монте-Карло для нижней границы лежат ниже точной MI. Теоретическая гарантия относится к population-границе; её конечновыборочная оценка может отклониться вверх, поэтому рядом указана стандартная ошибка. При увеличении граница становится плотнее, но для любого фиксированного
Важное различие: это потолок выбранной нижней границы, а не потолок истинной . В примере при
поэтому граница InfoNCE принципиально не может достичь истинного значения даже с идеальной функцией оценки.
Математическое углубление. Неравенство Фано
Из
следует: если после наблюдения остаётся большая неопределённость о , то вероятность ошибки не может быть произвольно малой. Например, при равномерном имеем
Чем меньше по сравнению с , тем больше неопределённости должен устранить классификатор без доступного сигнала — что невозможно.
Однако Фано обычно даёт нижнее ограничение, а не точное значение байесовской ошибки (Bayes error). Оно может быть слабым и не сообщает, какая модель или процедура обучения достигнет оптимума.