Основы машинного обучения для прогнозирования событий


БлогИсследования
нейросеть и астрология

27 марта 2026 г. 3:25 Продвинутый уровень Алексей Бореалис (Марк Русборн) 27 мин. на чтение


Исторически математика и астрономия использовались в 16-17 веках в университетах Европы для прогнозирования будущих событий. За многие годы наблюдений до нас дошел большой объем прогностических техник, который сейчас может быть использован для обучения ИИ.

В совокупности эти техники составляют научную парадигму прогностической астрологии эпохи Возрождения, которая включает 3 основных компоненты:

  • Первичные дирекции — для прогнозирования года события
  • Карты революций — для уточнения месяца и деталей события
  • Транзиты — для уточнения дня события

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

Прогноз события и формула Бернулли

Начнем с простого случая. Прогностическая техника предсказывает наступление однозначно регистрируемого события в заданный период времени (обычно день или месяц), например, рождение ребенка, официальный брак, переезд в другую страну и так далее.

У ожидаемого события существует два возможных исхода — оно либо происходит (метка события $y = 1$), либо нет (метка события $y = 0$).

Предположим, что вероятность наступления события в указанный период равна $p.$ Значит вероятность того, что это событие не наступит в этот же период равна $1- p.$

Мы можем записать эти два исхода одной формулой $$ P(y) = p^y \cdot (1-p)^{1-y} $$ Мы читаем это так: данная формула показывает вероятность наступления (метка $y=1$) или не наступления (метка $y=0$) конкретного события.

Действительно, если событие происходит, то вероятность этого исхода равна $P(y = 1) = p^1 \cdot (1-p)^{0} = p,$ а если событие не происходит, то вероятность такого исхода равна $P(y=0) = p^0 \cdot (1-p)^{1 - 0} = 1 - p.$

Это и есть распределение Бернулли — вероятностная модель бинарного исхода.

Условная вероятность

Теперь предположим, что наблюдаемое событие зависит от ряда астрономических факторов. Например, вероятность наступления насильственной смерти в указанный день тем сильнее, чем ближе Марс образует угол в $x=90°$ к градусу зодиакального круга, восходившему в момент рождения человека.

Тогда мы можем записать вероятность насильственной смерти как $P(y = 1 \mid x)$. Здесь $P$ означает вероятность события, метка $y=1$ означает положительный исход события (в нашем примере — наступление насильственной смерти в определенный период), а $\mid x$ читается как “при условии, что нам известен параметр $x,$ который влияет на исход”. В нашем примере $x$ — это угловое расстояние между Марсом и восходящим в момент рождения градусом эклиптики.

Обозначим условную вероятность положительного исхода $\hat{p} = P(y = 1 \mid x)$. Тогда формула Бернулли принимает вид:

$$ P(y \mid x) = \hat{p}^y \cdot (1 - \hat{p})^{1-y} $$ Обычно событие зависит не от одного, а от нескольких астрономических факторов или признаков. Например, насильственная смерть может зависеть от $x_1$ — углового расстояния между Марсом и восходящим при рождении градусом эклиптики, от $x_2$ — номера сектора неба (дома), в котором находится планета-управитель асцендента, и так далее.

Итого, у нас получается целый набор астрономических признаков ${x_1, x_2, ... x_d}$ от которых зависит вероятность $P$ будущего события.

Важно понимать, что набор признаков ${x_1, x_2, ... x_d}$ — это просто числа. Часто эти числа нормируют так, чтобы они были в диапазоне от 0 до 1. Например:

  • Эклиптическое угловое расстояние между планетами, именуемое аспектом, может принимать значения от 0 до 180 градусов. Значит аспект будет выражаться как доля от 180 градусов. Например аспект квадратуры (90 градусов) будет равен 0.5. И мы запишем это как $x_1 = 0.5$
  • Небесное состояние планеты в астрологии может принимать значение от -5 (изгнание) до +5. Значит для нормализации нам надо прибавить к этому признаку 5 и выразить это число как долю от 10 (шкала от -5 до 5). Например планета в экзальтации (соответствующая 4 баллам на шкале от -5 до +5) имеет астрологический признак $x_2$= 0.9.
  • Положение планеты в определенном секторе неба (доме) может быть выражено как номер сектора, деленный на общее число секторов.
  • И так далее. Каждый признак $x_i$ — это просто число от 0 до 1.

Мы можем представить себе набор параметров $x_i$ как вектор с координатами $$ \vec{\mathbf{x}} = \begin{bmatrix} x_1 \ x_2 \ \vdots \ x_n \ \end{bmatrix} $$

Тогда формула Бернулли в общем случае будет выглядеть так: $$ P(y \mid \vec{\mathbf{x}}) = \hat{p}^y \cdot (1 - \hat{p})^{1-y} $$ и читаться как вероятность наступления (метка $y=1$) или не наступления (метка $y=0$) события при заданных астрологических признаках $\vec{\mathbf{x}}.$

Вероятность единичного прогноза

Чтобы компьютер мог сделать успешный прогноз, он должен уметь вычислять функцию $\hat{p}^\text{pred} = f(\vec{\mathbf{x}})$, которая возвращает число от 0 до 1 (вероятность наступления единичного события).

Мы запишем это в виде: $$ \hat{p}^\text{pred} = f(\vec{\mathbf{x}}) \in (0, 1) $$ Здесь знак $\in (0,1)$ означает, что результат функции находится в интервале от 0 до 1.

Вычисление этой функции сводится к двум этапам:

  1. Преобразовать совокупность астрономических признаков $\vec{\mathbf{x}}$ в одно вещественное число $z$
  2. Представить $f(z)$ в виде простой математической конструкции, которая для любого числа $z \in (-\infty, +\infty)$ возвращает число (вероятность) $\in (0, 1)$.

Этап 1. Аффинное преобразование

Для преобразования вектора $\vec{\mathbf{x}}$ в одно вещественное число $z$ мы берем простую сумму взвешенных значений $x_i$ (то есть $w_1x_1 + w_2x_2 + ...$) и прибавляем к итоговой сумме дополнительное число $b.$

Мы можем записать это в виде: $$ z = w_1x_1 + w_2x_2 + ... + w_nx_n + b = \sum_{i=1}^n w_i x_i + b $$ Такого рода преобразование вектора $\vec{\mathbf{x}}$ в число $z$ называется аффинным преобразованием. При этом значения ${w_1, w_2, …}$ называются весовыми коэффициентами или просто весами. А параметр $b$ называется смещением.

Если значения ${w_1, w_2, …}$ представить как координаты вектора $\vec{\mathbf{w}},$ то мы можем представить это преобразование в векторной форме (как произведение двух векторов): $$ z = \begin{bmatrix} w_1, w_2, ... w_n\end{bmatrix} \times \begin{bmatrix} x_1 \ x_2 \ \vdots \ x_n \ \end{bmatrix} = \vec{\mathbf{w}}^\text{T}\; \vec{\mathbf{x}} $$ Здесь буква T обозначает транспонированный или сопряженный вектор, то есть столбец значений $w_i$, повернутый на 90 градусов, образующий строку.

Эйнштейновские индексы

Для будущего удобства мы будем использовать т.н. Эйнштейновские индексы, а именно значения $a_i$ (нижний индекс) обозначают числа, то есть координаты вектора, расположенные по вертикали (обычный вектор), а $a^i$ (верхний индекс) обозначает числа, расположенные по горизонтали (транспонированный вектор) $$ \begin{align} a_i = \begin{bmatrix} a_1 \ a_2 \ \vdots \ a_n \ \end{bmatrix} && a^i = [a_1, a_2, ..., a_n] \end{align} $$ Тогда произведение $\vec{\mathbf{w}}^\text{T}\; \vec{\mathbf{x}} = \sum_{i=1}^n w_i x_i$ можно представить в сокращенной нотации $w^i x_i,$ где сумма по верхним и нижним индексам подразумевается автоматически, а $i$ пробегает значения от 1 до $n.$ Начиная с этого момента и далее мы будем использовать эйнштейновские индексы наряду с векторными представлениями для набора чисел. В эйнштейновской нотации наше аффинное преобразование выглядит так: $$ z = w^i x_i + b $$

Этап 2. Сигмоида

Напомню, что мы решаем задачу — как представить прогноз будущего события $\hat{p}^\text{pred} = f(\vec{\mathbf{x}}) \in (0, 1).$

На предыдущем этапе мы выразили совокупность параметров $x_i$ через вещественное число $z = w^i x_i + b.$ Сейчас наша задача — сконструировать простую математическую функцию, которая преобразовывает любое число $z \in (-\infty, +\infty)$ в значение на интервале от 0 до 1, то есть в значение вероятности ожидаемого события.

Линейная функция $f(z) = z$ не подходит — она может быть отрицательной или больше 1. Нужно «сжать» график в интервал $(0, 1)$.

Построим такую функцию. Рассмотрим отношение шансов наступления события к его отсутствию: $$ O = \frac{P(y=1 \mid \vec{\mathbf{x}})}{P(y=0 \mid \vec{\mathbf{x}})} = \frac{\hat{p}}{1-\hat{p}} $$ При любом значении $\hat{p}\in(0,1)$ функция $O(\hat{p})$ находится в интервале от 0 (при $\hat{p} \to 0$) до $+\infty$ (при $\hat{p} \to 1$).

Если мы возьмем натуральный логарифм от этой функции, мы “растянем” ее от $-\infty$ до $+\infty.$ $$ z =\ln O(\hat{p}) = \ln\left(\frac{\hat{p}}{1 - \hat{p}}\right) \in (-\infty, +\infty) $$

Визуально видно, что для любых значений числа $z$ график зажат по оси $\hat{p}$ в диапазоне от 0 до 1.

Но если мы просто поменяем местами вертикальную и горизонтальную оси, то мы получим график, который мы ищем — для любого значения $z$ величина $\hat{p}$ не покидает диапазон (0, 1). Это и есть тот математический конструкт, который сопоставляет любому вещественному числу значение $\in (0, 1),$ то есть величину вероятности.

Значит нам надо просто выразить $\hat{p}$ как функцию от $z.$

Возьмем экспоненту от предыдущей формулы:

$$ \begin{align} \frac{\hat{p}}{1-\hat{p}} = e^z & \implies \hat{p} = e^z(1-\hat{p}) \implies \hat{p}(1 + e^z) = e^z \\ & \implies \hat{p} = \frac{e^z}{1 + e^z} = \frac{e^z \cdot e^{-z}}{(1 + e^z)\cdot e^{-z}} = \frac{1}{e^{-z} + 1} \end{align} $$

Отсюда мы сразу же находим искомую функцию: $\hat{p} = f(z) = \frac{1}{1 + e^{-z}}.$ Эта функция называется сигмоидой и обозначается буквой сигма. $$ \boxed{\sigma(z) = \frac{1}{1 + e^{-z}}} $$

3. Промежуточный итог

Подведем итог. Мы сказали в начале этой секции, что для того, чтобы компьютер мог сделать успешный прогноз, он должен уметь вычислять функцию $\hat{p}^\text{pred} = f(\vec{\mathbf{x}})$ от заданных астрологических признаков $x_i$, которая возвращает число от 0 до 1 (вероятность наступления единичного события).

И теперь мы можем записать это в виде: $$ \hat{p}^\text{pred} = \sigma(w^ix_i + b) \in (0, 1) $$ В таком виде нам теперь очень легко формализовать задачу. Нам надо численным образом подобрать такие весовые коэффициенты $w^i$ и смещение $b,$ чтобы для любых астрономических показателей $x_i$, предсказанная вероятность $\hat{p}^\text{pred}$ наступления этого события была максимально близкой к реально наблюдаемой вероятности. При этом веса $w^i$ и смещение $b$ будут одинаковыми для любого вектора признаков $\vec{\mathbf{x}}$.

Числовые значения $(w^i, b)$ называются параметрами компьютерной модели, а процесс их вычисления — машинным обучением.

Суммарная вероятность множества предсказаний

Для единичного прогноза с астрологическими признаками $\vec{\mathbf{x}}$ и меткой события $y$ формула Бернулли будет выглядеть так: $$ P(y \mid \vec{\mathbf{x}}, \vec{\mathbf{w}}, b) = \hat{p}^{y} \cdot (1 - \hat{p})^{1 - y} $$ где $\hat{p} = \sigma(\vec{\mathbf{w}}^\text{T} \vec{\mathbf{x}} + b)$

Мы читаем это так: модель с параметрами $\vec{\mathbf{w}}$ и $b$ предсказывает вероятность наступления $(y=1)$ или не наступления $(y=0)$ единичного события при заданных астрологических признаках $\vec{\mathbf{x}}.$

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

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

Как обучить модель на множестве примеров?

Функция правдоподобия

Предположим, у нас выбрано $M$ произвольных гороскопов различных людей из базы данных AstroDataBank с документированным временем переезда в другую страну. Для каждого гороскопа астролог составляет набор астрологических признаков $\vec{\mathbf{x}}_1, \vec{\mathbf{x}}_2, ..., \vec{\mathbf{x}}_M$ на момент переезда. Такими признаками могут быть, например:

  • Нормированное угловое расстояние между планетой-управителем восходящего знака и вершиной 9-го дома
  • Нормированный градус эклиптики, в котором находится управитель 9-го дома
  • Нормированный номер сектора неба (дома), в котором находится управитель асцендента
  • и другие численные значения от 0 до 1, в совокупности представляющих отдельный вектор признаков $\vec{\mathbf{x_i}}$.

Также астролог произвольно выбирает столько же примеров астрологических признаков $\vec{\mathbf{x_1}}, \vec{\mathbf{x_2}}, ..., \vec{\mathbf{x_M}}$, при которых никакого переезда не состоялось. Итого в нашем распоряжении оказывается $M$ положительных примеров $(\vec{\mathbf{x}}_1, y=1), (\vec{\mathbf{x}}_2, y=1), ... (\vec{\mathbf{x}}_M, y=1)$ и $M$ отрицательных $(\vec{\mathbf{x}}_1, y=0), (\vec{\mathbf{x}}_2, y=0), ... (\vec{\mathbf{x}}_M, y=0)$, всего $N$ примеров.

В общем случае мы можем сказать, что у нас есть датасет из $N = 2M$ примеров: $(\mathbf{x}_1, y_1), \ldots, (\mathbf{x}_N, y_N),$ на котором модель будет обучаться предсказывать переезды по астрологическим показателям.

Мы исходим из того, что примеры независимы друг от друга (стандартное допущение). Тогда вероятность наблюдать весь датасет сразу — это произведение вероятностей реализации каждого примера:

$$ P(\text{все данные} \mid \vec{\mathbf{w}}, b) = \prod_{i=1}^{N} P(y_i \mid \vec{\mathbf{x}}_i, \vec{\mathbf{w}}, b) = \prod_{i=1}^{N} \hat{p}_i^{y_i}(1-\hat{p}_i)^{1-y_i} $$

где $\hat{p}_i = \sigma(\vec{\mathbf{w}}^\text{T} \vec{\mathbf{x}}_i + b).$

Это произведение называется функцией правдоподобия $\mathcal{P}(\vec{\mathbf{w}}, b)$. Смысл этой функции таков: она показывает при каких вычисленных параметрах $\vec{\mathbf{w}}, b$ наблюдаемые данные наиболее правдоподобны.

Задача машинного обучения: найти $(\vec{\mathbf{w}}, b)$, при которых общая вероятность $\mathcal{P}(\vec{\mathbf{w}}, b)$ наблюдать весь датасет именно таким каким он есть, максимально близка к 1.

Логарифм правдоподобия

Произведение из миллиона чисел от 0 до 1 быстро уходит в $10^{-1000000}$ — компьютер быстро упрется в пределы вычислительной точности. Поэтому вместо функции правдоподобия мы будем рассматривать ее логарифм.

Почему именно логарифм?

  • Во-первых, логарифм — монотонная функция: максимум $\ln \mathcal{P}$ достигается при тех же $(\vec{\mathbf{w}}, b)$, что и максимум $\mathcal{P}$.
  • Во-вторых, логарифм произведения превращается в сумму. А сумма миллиона чисел от 0 до 1 не стремится к нулю в отличие от их произведения.
$$ \begin{align} \ln \mathcal{P}(\vec{\mathbf{w}}, b) & = \sum_{i=1}^{N} \ln\left[\hat{p}_i^{y_i}(1-\hat{p}_i)^{1-y_i}\right] \\ & = \sum_{i=1}^{N}\left[y_i \ln \hat{p}_i + (1 - y_i)\ln(1 - \hat{p}_i)\right] \end{align} $$

где $\hat{p}_i = \sigma(\vec{\mathbf{w}}^\text{T} \vec{\mathbf{x}}_i + b).$

Это так называемое логарифмическое правдоподобие (log-likelihood). Наша задача — найти такие $(\vec{\mathbf{w}}, b),$ при которых эта функция максимальна.

По соглашению в машинном обучении принято искать минимум функции (минимизировать потери), поэтому мы ставим минус перед этой функцией. Также мы нормируем ее на число $N$ примеров, чтобы потери не росли с ростом датасета:

$$ \boxed{\mathcal{L}(\vec{\mathbf{w}}, b) = -\frac{1}{N}\sum_{i=1}^{N}\left[y_i \ln \hat{p}_i + (1 - y_i)\ln(1 - \hat{p}_i)\right]} $$

где $\hat{p}_i = \sigma(\vec{\mathbf{w}}^\text{T} \vec{\mathbf{x}}_i + b).$

Это так называемая Binary Cross-Entropy (BCE) — функция потерь для всего датасета. Чем меньше эта функция, тем меньше потери (то есть суммарное отклонение прогнозируемого набора меток событий ${y_1, y_2, … y_N}$ для всех примеров от реально наблюдаемого).

Градиентный спуск

Чтобы лучше понять процесс машинного обучения, давайте представим, что у нас есть лишь один астрологический признак $x.$ Тогда $\hat{p}_i = \sigma(w x + b),$ то есть в компьютерной модели будут всего два параметра — один вес $w$ и одно смещение $b.$ В этом случае функция потерь — это функция от двух параметров $\mathcal{L}(w, b),$ то есть двумерная поверхность. Эта поверхность имеет углубление: для некоторого значения $w$ и $b$ эта функция имеет минимум. И наша задача — найти координаты $(w_0, b_0)$ этого минимума.

Давайте отобразим эту функцию и процесс поиска ее минимума.

Мы начинаем поиск со стартовой точки $(0, 0).$ Мы строим касательные к поверхности вдоль осей $w$ и $b.$ Результирующий вектор (то есть сумма двух касательных) показывает направление, двигаясь по которому мы как по горке “скатимся” в точку минимума.

Так как математически касательные вдоль осей $w$ и $b$ определяются частными производными функции потерь $\partial \mathcal{L(w, b)} / \partial w$ и $\partial \mathcal{L(w, b)} / \partial b$ (то есть углом наклона поверхности вдоль $w$ и $b$) то результирующий вектор математически представляет собой т.н. отрицательный градиент (направление роста) на поверхности функции. Мы немного продвигаемся вперед вдоль этого направления (против градиента функции).

В новой точке мы снова строим две касательных и снова движемся вниз вдоль результирующего вектора. Мы повторяем эту процедуру до тех пор, пока касательные не станут почти параллельны плоскости $(w, b)$ — это будет означать, что мы прибыли в точку минимума функции, то есть нашли координаты $(w_0, b_0).$

Эта процедура известна как “градиентный спуск”.

В общем случае, когда у нас есть $n$ весов ${w_1, w_2, ..., w_n}$ и одно смещение $b$ визуализировать поверхность в $(n+2)$-мерном пространстве уже сложнее. Однако технически алгоритм остается тем же — мы строим касательные вдоль направлений $w_1, w_2, ..., w_n, b$ и двигаясь вдоль результирующего вектора (или градиента) шаг за шагом мы приближаемся к точке минимума $(\vec{\mathbf{w}}_0, b_0).$ Координаты этой точки — это и есть результат машинного обучения, внутренние параметры модели, которая сможет предсказывать будущие события.

Введение в нейронные сети

Однако у описанной модели есть принципиальное ограничение. Одно аффинное преобразование $z = w^ix_i + b$ — это, по сути, взвешенная сумма астрологических признаков. Такая модель предполагает, что каждый признак влияет на вероятность события линейно и независимо от остальных.

Например, если признак $x_1$ — угловое расстояние (аспект) между планетами, то чем ближе этот аспект к 180 градусам, тем сильнее этот признак толкает модель к предсказанию события — вне зависимости от прочих факторов.

Но астрологическая традиция говорит об обратном. Один и тот же аспект между планетами работает совершенно по-разному в зависимости от того, в каком доме находится планета, какова её скорость, какие другие аспекты она образует одновременно с другими планетами и горизонтом наблюдателя. Иными словами, признаки взаимодействуют между собой нелинейно.

Линейная модель не способна этого уловить — какими бы точными ни были веса $w^i $, она всегда будет складывать признаки в одну сумму, не учитывая их взаимного влияния.

Решение — добавить в модель промежуточные аффинные преобразования (слои), которые сначала формируют новые, составные признаки из исходных (например, «Марс в угловом доме при ретроградном движении»), а затем уже смешивать и суммировать эти составные признаки. Каждый такой слой — это очередное аффинное преобразование $z = w^ix^\text{prev}_i + b $, но применённое к результатам предыдущего слоя, а не к исходным данным.

К промежуточному результату $z$ аффинного преобразования между слоями добавляется нелинейная функция $f(z)$ — та же сигмоида или её аналог — чтобы составные признаки не сворачивались обратно в одну линейную сумму. Дело в том, что два последовательных аффинных преобразования без нелинейности между ними математически эквивалентны одному — то есть вся глубина сети схлопывается в один слой.

Такая многослойная математическая конструкция и называется нейронной сетью.

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

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

Архитектура нейронной сети

Сеть имеет $L$ слоёв (этапов аффинного преобразования). Пронумеруем их от $1 $ до $L$. В каждом слое происходит не одно, а несколько независимых преобразований. Каждое преобразующее звено в слое называется “нейроном”.

Каждый нейрон $l$-го слоя получает входные данные ${h_1^{(l-1)}, h_2^{(l-1)}, ..., h^{(l-1)}_k}$ от 1, 2, … k-го нейрона предыдущего $(l-1)$-го слоя, затем преобразует их в число аффинным преобразованием $z = w^ih^{(l-1)}_i + b,$ при этом внутренние параметры — веса $w^i$ и смещение $b$ — у каждого нейрона свои собственные.

Далее к полученному числу $z$ применяется нелинейная функция $f(z)$ и результат передается дальше в следующий $(l+1)$-й слой.

По аналогии с мозгом человека входные параметры для нейрона называются сигналами, а функция f(z) — функцией активации нейрона. В этой терминологии можно сказать, что нейрон активирует взвешенную сумму входящих сигналов.

На рисунке ниже представлена простая нейронная сеть с двумя промежуточными слоями численностью в 2 и 3 нейрона соответственно, а также входные и выходные слои.

%%{init: {'flowchart': {'curve': 'basis'}}}%% graph LR x1((x₁)) --> h01(h⁽⁰⁾₁) x2((x₂)) --> h02(h⁽⁰⁾₂) x3((x₃)) --> h03(h⁽⁰⁾₃) h01 -- h⁽⁰⁾₁ --> h11("h⁽¹⁾₁ = f(w⁽¹⁾₁ⁱ h⁽⁰⁾ᵢ + b₁)") h02 -- h⁽⁰⁾₂ --> h11 h03 -- h⁽⁰⁾₃ --> h11 h01 --> h12("h⁽¹⁾₂ = f(w⁽¹⁾₂ⁱ h⁽⁰⁾ᵢ + b₂)") h02 --> h12 h03 --> h12 h11 -- h⁽¹⁾₁ --> h21("h⁽²⁾₁ = f(w⁽²⁾₁ⁱ h⁽¹⁾ᵢ + b₁)") h12 -- h⁽¹⁾₂ --> h21 h11 --> h22("h⁽²⁾₂ = f(w⁽²⁾₂ⁱ h⁽¹⁾ᵢ + b₂)") h12 --> h22 h11 --> h23("h⁽²⁾₃ = f(w⁽²⁾₃ⁱ h⁽¹⁾ᵢ + b₃)") h12 --> h23 h21 -- h⁽²⁾₁ --> h3("p=f(w⁽³⁾₄ⁱ h⁽²⁾ᵢ + b₄)") h22 -- h⁽²⁾₂ --> h3 h23 -- h⁽²⁾₃ --> h3

Разберем как сигнал проходит через нейронную сеть.

Входной (нулевой) «слой» — просто берет входные данные, то есть набор астрологических признаков, и передает их далее без каких-либо изменений: $$ \vec{\mathbf{h}}^{(0)} = \vec{\mathbf{x}} \in \mathbb{R}^{d} $$ Здесь $\in \mathbb{R}^{d}$ означает, что размерность вектора-столбца $\vec{\mathbf{h}}^{(0)}$ нулевого слоя равен числу $d$ астрологических признаков. Это эквивалентно записи: $$ h^{0}_i = x_i, \quad i = 1,2, ... d. $$

Скрытые слои $l = 1, \ldots, L-1 $:

Каждый последующий слой $l$ имеет $n^{(l)} $ нейронов. Каждый нейрон берёт весь вектор $\vec{\mathbf{h}}^{(l-1)} $ с предыдущего слоя, делает линейную комбинацию его координат $z=w^{(l)^i} x_i + b$ и применяет функцию активации $f(z)$.

А множество всех нейронов $l$-го слоя сначала производят множество линейных комбинаций $z_k = w^{(l)i}_k x_i + b_k,$ а затем применяют к этим значениям функцию активации, образую суммарный выход слоя: $h_k = f(z_k),$ где $k = 1, 2, ... n^{(l)}.$

То есть результирующее действие всего $l$-го слоя сразу в матричной записи выглядит так: $$ \vec{\mathbf{z}}^{(l)} = W^{(l)} \vec{\mathbf{h}}^{(l-1)} + \vec{\mathbf{b}}^{(l)} $$ где $W^{(l)} \in \mathbb{R}^{n^{(l)} \times n^{(l-1)}} $ — матрица весов $l$-го слоя размерностью $n^{(l)} \times n^{(l-1)},$ $\vec{\mathbf{b}}^{(l)} \in \mathbb{R}^{n^{(l)}} $ — вектор смещений $l$-го слоя, а $f $ — функция активации, которая применяется поэлементно.

Функция активации в скрытых слоях:

В качестве функции активации в скрытых слоях удобно использовать функцию Rectified Linear Unit (ReLU). $$ f(z) = \max(0, z) $$ Почему именно эта функция удобнее сигмоиды?

  • Для скрытых слоев (в отличие от результирующего выходного слоя) нет требований, чтобы они возвращали числа от 0 до 1, то есть вероятности. Скрытые слои выполняют иную задачу — они просто улавливают нелинейные взаимодействия между астрологическими признаками.
  • ReLU в отличие от сигмоиды не приближается в 1 при больших $z,$ то есть градиенты ReLU не устремляются к нулю при росте $z$,
  • Эта функция быстро вычисляется
  • При всей простоте она не является линейной (она обрезает все значения $z < 0$) — все это делает обучение быстрым и стабильным.

Выходной слой $l = L $

В выходном слое используется один нейрон, а в качестве функции активации — как раз сигмоида, так как мы хотим получить предсказанную вероятность наступления будущего единичного события $P(y=1, \vec{\mathbf{x}})$ при заданных астрологических признаках $x_i$ на выходе нейронной сети:

$$ \begin{align} & z^{(L)} = \left(\vec{\mathbf{w}}^{(L)}\right)^\text{T} \cdot \vec{\mathbf{h}}^{(L-1)} + b^{(L)} \in \mathbb{R} \\ & f(z) = \sigma(z) = \frac{1}{1 + e^{-z}} \end{align} $$

Сигмоида переводит любое вещественное число в вероятность.

Градиентный спуск для нейронной сети

Мы ранее говорили, что функция потерь для всего датасета: $$ \mathcal{L} = -\frac{1}{N}\sum_{i=1}^{N}\left[y_i \ln \hat{p}_i + (1 - y_i)\ln(1 - \hat{p}_i)\right] $$ Мы также обсудили, что для применения алгоритма градиентного спуска нам надо последовательно вычислять градиенты, то есть частные производные функции потерь $\mathcal{L}(\vec{\mathbf{w}}, b)$ вдоль осей $w_1, w_2, ..., w_d, b$ для всего датасета, что приведет нас к минимуму функции в точке $(\vec{\mathbf{w}}_0, b_0).$ А координаты этой точки — это и есть параметры модели, предсказывающей события.

В случае нейронной сети формула для функции потерь остается неизменной. Однако если ранее вероятность $\hat{p} = \sigma(\vec{\mathbf{w}}^\text{T} \vec{\mathbf{x}} + b)$ наступления события на отдельном примере зависела непосредственно от входных признаков $x_i$, то в случае нейросети вероятность $\hat{p} = \sigma\left(\left(\vec{\mathbf{w}}^{(L)}\right)^\text{T} \cdot \vec{\mathbf{h}}^{(L-1)} + b^{(L)}\right)$ зависит теперь от результатов последнего скрытого слоя $L-1$, а его результаты зависят от выхода предшествующего слоя $L-2$ и так далее.

По-сути, до введения нейронной сети мы работали с одним нейроном — мы сразу составляли линейную комбинацию астрологических признаков и отправляли эту комбинацию с сигмоиду, получая выходной результат. При этом функция потерь $\mathcal{L}(\vec{\mathbf{w}}, b)$ зависела от одного вектора весов $\vec{\mathbf{w}}$ и одного смещения $b.$

В нейронной сети на каждом слое есть множество нейронов, каждый со своим вектором весов и смещением. Совокупность $\vec{\mathbf{w}}_i$ всех весов каждого $l$-го слоя представляют матрицу весов $W^{(l)}$ слоя, а совокупность всех смещений $b_i$ — вектор смещения $\vec{\mathbf{b}}^{(l)}$ слоя. Функция потерь теперь зависит от этих параметров: $\mathcal{L}(W^{(l)}, \vec{\mathbf{b}}^{(l)})$ для каждого слоя $l$.

Поэтому для нейронной сети мы будем искать частные производные функции $\mathcal{L}$ по этим параметрам для каждого слоя, а именно: $\partial \mathcal{L} / \partial W^{i\;(l)}_j$ и $\partial \mathcal{L} / \partial b^{(l)}_j,$ где индекс $j$ (столбец) пробегает по всем нейронам $l$-го слоя, а индекс $i$ (строка) — по всем нейронам предыдущего. Элемент матрицы $W^{i\;(l)}_j$ — это весовой коэффициент связи $i$-го нейрона предыдущего слоя с $j$-м нейроном последующего.

Дельта слоя

Чтобы вычислить эти градиенты, давайте введем для удобства величину $$ \delta^{(l)} = \frac{\partial \mathcal{L}}{\partial z^{(l)}} $$ Напомню, что для выходного слоя $z^{(L)} = \left(\vec{\mathbf{w}}^{(L)}\right)^\text{T}\cdot \vec{\mathbf{h}}^{(L-1)} + b^{(L)},$ а для каждого $k$-го нейрона промежуточного $l$-го слоя $z^{(l)}_k = \left(\vec{\mathbf{w}}^{(l)}_k\right)^\text{T} \cdot \vec{\mathbf{h}}^{(l-1)} + b^{(l)}_k.$

Выходной слой

Сначала высчитаем $\delta^{(L)}$​ выходного слоя. По цепному правилу: $$ \delta^{(L)} = \frac{\partial \mathcal{L}}{\partial z^{(L)}} = \frac{\partial \mathcal{L}}{\partial \hat{p}} \cdot \frac{\partial \hat{p}}{\partial z^{(L)}} $$ Теперь считаем каждый множитель.

  1. Производная функции потерь (BCE) по $\hat{p} $ (для одного примера, без суммы):
$$ \begin{align} \frac{\partial \mathcal{L}}{\partial \hat{p}} &= - \frac{\partial}{\partial \hat{p}} \left[y \ln \hat{p} + (1 - y)\ln(1 - \hat{p})\right] \\ &=-y\; \frac{\partial}{\partial \hat{p}}\ln(\hat{p}) - (1-y) \frac{\partial}{\partial \hat{p}}\ln(1-\hat{p}) \\ &=-\frac{y}{\hat{p}} - (1-y) \frac{\partial\ln(1-\hat{p})}{\partial (1-\hat{p})}\cdot \frac{\partial (1-\hat{p})}{\partial\hat{p}} \\ &= -\frac{y}{\hat{p}} + \frac{1-y}{1-\hat{p}} \end{align} $$
  1. Производная сигмоиды:
$$ \begin{align} \frac{\partial \hat{p}}{\partial z^{(L)}} & = \frac{\partial}{\partial z^{(L)}}\sigma(z^{(L)}) = \frac{\partial}{\partial z^{(L)}}\left(\frac{1}{1+e^{-z^{(L)}}}\right) \\ & = - \frac{1}{(1 + e^{-z^{(L)}})^2} \cdot \frac{\partial}{\partial z^{(L)}} \left(1 + e^{-z^{(L)}}\right) \\ & = \frac{e^{-z^{(L)}}}{(1 + e^{-z^{(L)}})^2} = \sigma\left(z^{(L)}\right) \frac{e^{-z^{(L)}}}{1+e^{-z^{(L)}}} \\ & = \sigma\left(z^{(L)}\right) \left(1 - \frac{1+e^{-z^{(L)}}}{1+e^{-z^{(L)}}} + \frac{e^{-z^{(L)}}}{1+e^{-z^{(L)}}}\right) \\ & = \sigma\left(z^{(L)}\right) \left(1 - \frac{1}{1+e^{-z^{(L)}}}\right) = \sigma\left(z^{(L)}\right)\left(1- \sigma\left(z^{(L)}\right)\right) \\ & = \hat{p}(1-\hat{p}) \end{align} $$
  1. Перемножаем эти две производных — и все красиво сокращается:
$$ \begin{align} & \left(-\frac{y}{\hat{p}} + \frac{1-y}{1-\hat{p}}\right)\cdot \hat{p}(1-\hat{p}) \\ & = \left(\frac{-y+y\;\hat{p} + \hat{p} - y\;\hat{p}}{\hat{p}(1-\hat{p})}\right)\cdot \hat{p}(1-\hat{p}) \\ & = \hat{p} - y \end{align} $$

Усредняя по выборке из $N $ примеров (то есть для всего датасета):

$$ \boxed{\delta^{(L)} = \frac{1}{N}\sum_{i=1}^{N}(\hat{p}_i - y_i)} $$

Производная сигмоиды и логарифм функции BCE красиво сокращаются — именно поэтому сигмоида и BCE являются канонической парой.

Скрытые слои, рекуррентная формула

Вычислим $\delta^{(l)}$ для $i$-го нейрона $(L-1)$-го слоя:

$$ \delta^{i\;(L-1)} = \frac{\partial \mathcal{L}}{\partial z^{(L-1)}_i} = \frac{\partial \mathcal{L}}{\partial z^{(L)}} \cdot \frac{\partial z^{(L)}}{\partial h^{(L-1)}_i} \cdot \frac{\partial h^{(L-1)}_i}{\partial z^{(L-1)}_i} $$

Раскрываем каждый множитель:

  • $\dfrac{\partial \mathcal{L}}{\partial z^{(L)}} = \delta^{(L)}$ — уже известная дельта выходного слоя
  • $\dfrac{\partial z^{(L)}}{\partial h^{(L-1)}_i} = w^{i\;(L)}$ — $i$-й вес в строке весов из формулы $z^{(L)} = \left(\vec{\mathbf{w}}^{(L)}\right)^\text{T}\cdot \vec{\mathbf{h}}^{(L)} + b^{(L)}$
  • $\dfrac{\partial h^{(L-1)}_i}{\partial z^{(L-1)}_i} = f'(z^{(L-1)}_i)$ — производная функции активации ReLU: единица если $z^{(L-1)}_i > 0 ,$ ноль если иначе.

Подставляем:

$$ \boxed{\delta^{i\;(L-1)} = \left(w^{i\;(L)}\, \delta^{(L)}\right) \cdot f'(z^{(L-1)}_i)} $$

или в матричной форме для всех нейронов слоя:

$$ \boxed{\vec{\boldsymbol{\delta}}^{(L-1)} = \left(\vec{\mathbf{w}}^{(L)}\right)^{\text{T}}\delta^{(L)} \odot f'(\vec{\mathbf{z}}^{(L - 1)})} $$

где

  • $\left(\vec{\mathbf{w}^{(L)}}\right)^{\text{T}}$ — вектор-строка весов выходного нейрона
  • $\delta^{(L)}$ — скаляр
  • $f'(\vec{\boldsymbol{z}}^{(l)})$ для ReLU: вектор нулей и единиц — единицы там, где $z^{(l)}_i > 0$, ноль иначе.
  • $\odot$ — поэлементное произведение двух векторов.

По аналогии вычислим $\delta^{(l)}$ для $i$-го нейрона $(L-2)$-го слоя. По цепному правилу:

$$ \delta^{i\;(L-2)} = \frac{\partial \mathcal{L}}{\partial z^{(L-2)}i} = \sum{j} \frac{\partial \mathcal{L}}{\partial z^{(L-1)}_j} \cdot \frac{\partial z^{(L-1)}_j}{\partial h^{(L-2)}_i} \cdot \frac{\partial h^{(L-2)}_i}{\partial z^{(L-2)}_i} $$

Суммирование здесь идет по всем нейронам $(L-1)$-го слоя. Раскрываем каждый множитель:

  • $\dfrac{\partial \mathcal{L}}{\partial z^{(L-1)}_j} = \delta^{j\;(L-1)}$ — уже известная дельта предвыходного слоя
  • $\dfrac{\partial z^{(L-1)}j}{\partial h^{(L-2)}_i} = W^{i\;(L-1)}{j}$ — из формулы $z^{(L-1)}j = \left(\vec{\mathbf{w}}_j^{(L-1)}\right)^\text{T}\cdot \vec{\mathbf{h}}^{(L-2)} + b_j,$ где произведение векторов равно $W^{i\;(L-1)}{j} h^{(L-2)}_i$ (суммирование по эйнштейновским индексам $i$ идет по всем выходам $(L-2)$-го слоя)
  • $\dfrac{\partial h^{(L-2)}_i}{\partial z^{(L-2)}_i} = f'(z^{(L-2)}_i)$ — производная активации ReLU, применяется поэлементно для каждого $i$-го нейрона.

Подставляем:

$$ \delta^{i\;(L-2)} = \left(W^{i\;(L-1)}_{j}\, \delta^{j\;(L-1)}\right) \cdot f'(z^{(L-2)}_i) $$

Здесь суммирование идет по индексу $j$, который пробегает координаты вектора-строки $\left(\vec{\boldsymbol{\delta}}^{(L)}\right)^\text{T}$. Эта формула показывает связь между двумя соседними слоями — если нам известно значение $\left(\vec{\boldsymbol{\delta}}^{(L-1)}\right)^{\text{T}},$ то мы теперь знаем как найти $\left(\vec{\boldsymbol{\delta}}^{(L-2)}\right)^{\text{T}},$ зная $\left(\vec{\boldsymbol{\delta}}^{(L-2)}\right)^{\text{T}}$ и так далее до 1-го слоя. В общем случае эту связь можно записать так:

$$ \boxed{\delta^{i\;(l-1)} = \left(W^{i\;(l)}_{j}\, \delta^{j\;(l)}\right) \cdot f'(z^{(l-1)}_i)} $$

здесь индекс $j$ (столбец матрицы $W^{(l)}$) пробегает по всем нейронам $l$-го слоя, а индекс $i$ (строка матрицы) указывает на $i$-й нейрон предыдущего слоя. Элемент матрицы $W^{i\;(l)}_j$ — это весовой коэффициент связи $i$-го нейрона предыдущего слоя с $j$-м нейроном последующего.

В матричной записи для всего слоя целиком эта рекуррентная формула выглядит так:

$$ \boxed{\vec{\boldsymbol{\delta}}^{(l-1)} = \left(W^{(l)}\right)^T \vec{\boldsymbol{\delta}}^{(l)} \odot f'(\vec{\mathbf{z}}^{(l-1)})} $$

где

  • $\left(W^{(l)}\right)^T$ — транспонированная (повернутая на 90 градусов) матрица весовых коэффициентов $l$-го слоя.
  • $f'(\vec{\boldsymbol{z}}^{(l)})$ для ReLU: вектор нулей и единиц — единицы там, где $z^{(l)}_i > 0$, ноль иначе.
  • $\odot $ — поэлементное произведение.

Градиенты по параметрам слоя

Имея теперь формулу для дельты $\vec{\boldsymbol{\delta}}^{(l)}$ любого слоя мы можем теперь построить градиенты функции потерь по матрице весов каждого из слоев:

$$ \frac{\partial \mathcal{L}}{\partial W^{i\;(l)}{j}} = \frac{\partial \mathcal{L}}{\partial z^{(l)}_i} \cdot \frac{\partial z^{(l)}_i}{\partial W^{i\;(l)}{j}} = \delta^{i\;(l)} \cdot h^{(l-1)}_j $$

Или то же самое в матричной форме:

$$ \boxed{\frac{\partial \mathcal{L}}{\partial W^{(l)}} = \left(\vec{\boldsymbol{\delta}}^{(l)}\right)^{\text{T}} \cdot \vec{\mathbf{h}}^{(l-1)} \in \mathbb{R}^{n^{(l)} \times n^{(l-1)}}} $$

Размерность градиента совпадает с размерностью $W^{(l)}.$

Аналогично, градиент по вектору смещения каждого слоя:

$$ \frac{\partial\mathcal{L}}{\partial b^{(l)}_i} = \frac{\partial\mathcal{L}}{\partial z^{(l)}_i} \cdot \frac{\partial z^{(l)}_i}{\partial b^{(l)}_i} = \delta^{i\;(l)} $$

или в матричной форме:

$$ \boxed{\frac{\partial \mathcal{L}}{\partial \vec{\mathbf{b}}^{(l)}} = \left(\vec{\boldsymbol{\delta}}^{(l)}\right)^{\text T} \in \mathbb{R}^{n^{(l)}}} $$

Пошаговый алгоритм градиентного спуска

Теперь мы можем формализовать алгоритм градиентного спуска для поиска минимума функции потерь для всей нейронной сети.

  1. Сначала для каждого слоя $l$ мы инициализируем матрицу весовых коэффициентов $W^{(l)}$ и вектор смещений $\vec{\mathbf{b}}^{(l)}$ произвольными значениями.
  2. Затем мы проходим все скрытые слои — от 1-го до $(L-1)$-го и вычисляем значения $\vec{\mathbf{z}}^{(l)}$ и $\vec{\mathbf{h}}^{(l)}.$ Мы сохраняем эти значения — они понадобятся нам далее. Этот шаг известен как прямой проход.
  3. Далее мы вычисляем:
  4. $\delta^{(L)} = \frac{1}{N}\sum_{i=1}^{N}(\hat{p}_i - y_i)$ — для выходного слоя
  5. $\vec{\boldsymbol{\delta}}^{(L-1)} = \left(\vec{\mathbf{w}}^{(L)}\right)^{\text{T}}\delta^{(L)} \odot f'(\vec{\mathbf{z}}^{(L - 1)})$ — для предвыходного слоя
  6. $\vec{\boldsymbol{\delta}}^{(l-1)} = \left(W^{(l)}\right)^T \vec{\boldsymbol{\delta}}^{(l)} \odot f'(\vec{\mathbf{z}}^{(l-1)})$ — для всех оставшихся слоев (по направлению к первому)
  7. Параллельно для каждого слоя мы считаем градиенты по весам $\frac{\partial \mathcal{L}}{\partial W^{(l)}} = \left(\vec{\boldsymbol{\delta}}^{(l)}\right)^{\text{T}} \cdot \vec{\mathbf{h}}^{(l-1)}$ и по смещениям $\frac{\partial \mathcal{L}}{\partial \vec{\mathbf{b}}^{(l)}} = \left(\vec{\boldsymbol{\delta}}^{(l)}\right)^{\text T}.$ Этот шаг известен как обратный проход — расчет от последнего слоя к первому.
  8. Градиенты указывают в сторону роста функции потерь, значит нам надо сделать небольшой шаг в противоположном направлении:
$$ \begin{align} & W^{(l)} = W^{(l)} - \lambda\frac{\partial \mathcal{L}}{\partial W^{(l)}}\\ & \vec{\mathbf{b}}^{(l)} = \vec{\mathbf{b}}^{(l)} - \lambda \frac{\partial \mathcal{L}}{\partial \vec{\mathbf{b}}^{(l)}} \end{align} $$

На последнем 4-м шаге мы фактически обновляем/корректируем веса и смещения для всех нейронов каждого слоя. Это соответствует небольшому шагу величины $\lambda$ по поверхности функции $\mathcal{L}$ в сторону координат ее минимума.

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

Практические шаги

Ниже перечислены практические шаги для обучения искуственного интеллекта делать астрологические прогнозы:

Шаг 1. Подготовка данных

Для начала группа астрологов выбирает набор прогностических техник, взятых из одной астрологической школы/традиции, совокупное применение которых позволяет делать прогноз. Это могут быть дирекции, карты революций и транзиты, или прогрессии, профекции и солярные возвращения — эта часть остается на усмотрение группы астрологов.

Далее набор астрологических признаков переводится в численные значения. Например, управитель дома может быть выражен в виде числа, где каждое число соответствует планете, текущий фирдар может быть выражен в виде порядкового номера в списке фирдаров, аспект — в градусах, положение планеты в доме — в номере дома, достоинства и ущербности планеты — в баллах. Факт соединения куспидов домов или его отсутствия при наложении карт — в виде 0 или 1.

Далее каждый из астрологических признаков нормируется, чтобы выходное число было в интервале от 0 до 1. Например, номера домов и знаков надо разделить на 12, градус зодиакального круга — на 360. В итоге мы получим десятки признаков, комбинации которых приводят к прогнозируемому событию.

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

После этого из базы данных AstroDatabank надо выбрать все доступные гороскопы, в которых есть время наступления исследуемого события, например год день и месяц рождения ребенка. К каждому такому событию необходимо применить астрологические техники и заполнить астрологические признаки $x_i$ с меткой события $y=1$. Это могут быть положения планет в домах и знаках (выраженные в виде числа от 0 до 1) в карте солярного возвращения, взаимные аспекты при наложении карты лунара на радиксную фигуру (также выраженные в виде чисел от 0 до 1), достоинства и ущербности планет, выраженные в числах (от 0 до 1) и так далее — согласно списку признаков.

Далее необходимо выбрать столько же отрицательных примеров — набор случайных гороскопов и дат, когда исследуемое событие не происходило. И для каждого такого примера также необходимо составить вектор признаков $x_i$ с меткой события $y=0$.

Шаг 2. Выбор размера батча

Поскольку датасет может оказаться очень большим, обучение лучше производить небольшими партиями из $N $ примеров или батчами. Размер группы примеров для отдельного этапа обучения называется размером батча.

Шаг 3. Разбиение данных

Большая часть данных (70%) сохраняется для обучения модели, 15% — для валидации и 15% для тестов.

Данные для обучения (большой датасет) — это набор астрологических признаков и меток события $(\vec{\mathbf{x}}_1, y_1), (\vec{\mathbf{x}}_2, y_2), ...$ и так далее, которые подаются на вход нейросети, чтобы та подобрала идеальные веса и смещения, при которых модель способна делать хорошие прогнозы.

Данные для валидации — это контрольная группа примеров, на которых мы проверяем насколько хорошо модель предсказывает события. Может оказаться так, что модель прекрасно предсказывает события только на тренировочных данных, но сильно промахивается с прогнозами, когда использует незнакомые ей входные признаки и метки.

В этом случае необходимо подбирать глобальные параметры модели, чтобы точность прогноза на тренировочной базе была схожа с точностью прогноза на выборке для валидации. К таким параметрам относятся:

  • Количество слоев
  • Количество нейронов в слоях
  • Размер батча
  • Размер шага при градиентном спуске $\lambda$ (то есть learning rate)
  • Число прогонов по всему тренировочному датасету (т.н. число эпох)

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

Шаг 4. Градиентный спуск

После того как данные разбиты, мы берем тренировочный датасет, тщательно перемешиваем его и разбиваем на $N $ батчей.

Перед первым прогоном мы инициируем матрицу весов $W^{(l)}$ произвольными числами (см. приложение к этой статье), а вектор смещения $\vec{\mathbf{b}}^{(l)}$ ставим равным нулю для каждого слоя $l$.

Затем мы проходим один шаг градиентного спуска (прямой и обратный проход). После этого мы берем следующий батч и снова повторяем градиентный спуск. Мы повторяем эти итерации, пока у нас не закончатся батчи.

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

Мы повторяем эпохи обучения несколько раз (обычно десятки или сотни). В ходе обучения мы постоянно наблюдаем за тем как функция потерь с геометрической скоростью стремится к своему минимуму — это поведение функции $\mathcal{L}$ называется сходимостью функции потерь.

Шаг 5. Валидация данных

После того, как модель успешно прошла первое обучение, мы берем данные для валидации и сравниваем прогнозы модели с реальным распределением меток событий. Если различия существенны, то нам следует изменить глобальные параметры модели:

  • Размер батча
  • Размер шага в градиентном спуске (learning rate, $\lambda$)
  • Количество нейронов в слоях
  • Количество слоев
  • Количество эпох

Мы корректируем эти параметры и обучаем модель заново до тех пор, пока мы не пройдем валидацию.

Шаг 6. Тестирование модели

Финальным шагом надо дать обученной модели тестовые данные — астрологические признаки $\vec{\mathbf{x}_i}$ и сравнить прогноз модели с реальным распределением меток событий $y_i$. Если первые 5 шагов выполнены без ошибок, то почти всегда тест пройдет корректно.

Однако если тест не прошел, то надо

  1. Проверить нет ли утечек данных, то есть одинаковых примеров в тренировочных/тестовых/проверочных данных. Если есть — устранить и начать обучение заново
  2. Проверить качество данных — все ли признаки учтены, правильно подсчитаны и нормированы. Если ошибки обнаружены, исправить их и обучить модель заново.
  3. Если ошибок не наблюдается, тщательно перемешать все примеры и заново разбить данные на тренировочные, проверочные и тестовые. После этого повторить обучение с теми же глобальными параметрами модели.

Применение модели

После успешного прохождения теста модель считается обученной и может быть применена в различных областях человеческой жизни. Для ее использования необходимо на каждый год, месяц и день жизни составить набор астрологических признаков $\vec{\mathbf{x}}_t$ и модель, обученная предсказывать определенное событие выдаст распределение вероятностей наступления этого события на каждый день. В те дни, когда вероятность события близка к 1, можно ожидать наступления исследуемого события.

Приложение. Инициация параметров перед первым прогоном

Мы говорили, что перед началом применения алгоритма градиентного спуска мы инициируем матрицы весов и смещения для каждого нейронного слоя произвольными значениями. Здесь возможны два негативных сценария, которые следует избежать.

Сценарий 1. Очень большие веса

Если у нейронов в сети изначально будут разные и очень большие веса, то вектор $\vec{\mathbf{z}}^{(l)}$ (результат аффинного преобразования в слое $l$) будет иметь сильно различающиеся друг от друга координаты, например -1000, 549, -3261. Иными словами отклонение координат от среднего значения (т.н. дисперсия) будет очень большой. Большие числа в одном слое будет усиливаться большими весами в следующем слое — в итоге к последнему нейрону придут очень большие сигналы (значения $\vec{\mathbf{h}}^{(L-1)}$). Их линейная комбинация $z = w^{i\;(L)}h_i^{(L-1)}$ будет давать большие положительные и отрицательные значения $z$.

Как вы помните из графиков сигмоида $\sigma(z)$ быстро насыщается — при больших положительных $z$ она стремится к 1 и почти не растет, а при больших отрицательных $z$ она устремляется к нулю.

Как следствие дельта выходного слоя $\delta^{(L)} = \frac{1}{N}\sum_{i=1}^{N}(\hat{p}_i - y_i)$ будет принимать значения, близкие к $-1$, $0$ или $1 $.

А так как $\frac{\partial \mathcal{L}}{\partial W^{(l)}} = \left(\vec{\boldsymbol{\delta}}^{(l)}\right)^{\text{T}} \cdot \vec{\mathbf{h}}^{(l-1)}$, то градиенты весов оказываются произведением потенциально больших значений $\vec{\mathbf{h}}^{(l-1)} $ на почти дискретные значения $\vec{\boldsymbol{\delta}}^{(l)}$. В результате градиенты резко “скачут” — поверхность функции потерь либо почти плоская, либо почти отвесная. По такой “изломанной” поверхности почти невозможно плавно скатиться к минимуму. Этот негативный сценарий называется “взрывом дисперсии”.

Сценарий 2. Очень малые веса

В случае если мы инициируем веса и смещения слоев нулями или очень малыми числами, то вектор $\vec{\mathbf{z}}^{(l)}$ (результат аффинного преобразования в слое $l$) будет иметь очень схожие координаты, например -0.00005, 0.00001, 0.00002. Иными словами отклонение координат от среднего значения (дисперсия) будет очень малой. Малые числа в одном слое будет уменьшаться при умножении на малые веса в следующем слое — в итоге к последнему нейрону придут очень слабый сигналы (значения $\vec{\mathbf{h}}^{(L-1)}$). Их линейная комбинация $z = w^{i\;(L)}h_i^{(L-1)}$ будет давать почти нулевые значения $z$.

Сигмоида $\sigma(z)$ в окрестности нуля ведёт себя почти линейно: $$ \sigma(z) \approx 0.5 + \frac{z}{4} $$ То есть предсказания модели $\hat{p}_i$ для всех объектов оказываются близкими к 0.5, практически не различаясь между собой.

Как следствие дельта выходного слоя $\delta^{(L)} = \frac{1}{N}\sum_{i=1}^{N}(\hat{p}_i - y_i)$ будет принимать небольшие по модулю значения, поскольку $\hat{p}_i \approx 0.5$, а значит разность $(\hat{p}_i - y_i))$ ограничена и не даёт сильного сигнала ошибки.

Далее, при распространении ошибки назад по сети (обратный проход), величины $\vec{\boldsymbol{\delta}}^{(l)}$ на каждом слое дополнительно уменьшаются, поскольку они последовательно умножаются на малые веса. В результате к начальным слоям сети градиент доходит уже сильно ослабленным.

А так как $$ \frac{\partial \mathcal{L}}{\partial W^{(l)}} = \left(\vec{\boldsymbol{\delta}}^{(l)}\right)^{\text{T}} \cdot \vec{\mathbf{h}}^{(l-1)}, $$ то градиенты весов оказываются произведением малых значений $\vec{\boldsymbol{\delta}}^{(l)}$ на малые значения $\vec{\mathbf{h}}^{(l-1)} $. В результате градиенты становятся близкими к нулю.

Поверхность функции потерь в этом случае оказывается почти плоской: при изменении весов значение функции потерь меняется очень слабо. Градиентный спуск “не чувствует” направления движения и делает крайне маленькие шаги.

По такой почти горизонтальной поверхности движение к минимуму происходит чрезвычайно медленно или практически останавливается. Этот негативный сценарий называется “затуханием дисперсии”.

Решение

Нам надо подобрать такие начальные значения весов, чтобы дисперсия $\text{Var}(\vec{\mathbf{h}}^{(l)})$ сигнала сохранялась при переходе от слоя к слою без взрывных накоплений или затуханий. То есть $$ \text{Var}(\vec{\mathbf{h}}^{(l)})\approx \text{Var}(\vec{\mathbf{h}}^{(l−1)}) $$ Так как функция активации ReLU обрезает половину значений $\vec{\mathbf{z}^{(l)}}$ (из-за того, что $\vec{\mathbf{h}}^{(l)} = \max(0, \vec{\mathbf{z}}^{(l)})$, то примерно половина координат вектора $\vec{\mathbf{z}^{(l)}}$ обнуляется, поэтому: $$ \mathrm{Var}\left(h^{(l)}_j\right) \approx \frac{1}{2} \mathrm{Var}\left(z^{(l)}_j\right) $$ При этом $\text{Var}(z_j^{(l)}) = \text{Var}\left(W^{i\;(l)}_j h_i^{(l-1)}\right)$. Дисперсия суммы равна сумме дисперсий, а дисперсия произведений (при условии, что средняя величина равна нулю), равна произведению дисперсий. Используя эти правила получаем:

$$ \begin{align} \text{Var}(z_j^{(l)}) & = \sum_i \text{Var}\left(W^{i\;(l)}_j h_i^{(l-1)}\right) \\ & = n^{(l-1)} \text{Var}\left(W^{i\;(l)}_j h_i^{(l-1)}\right) \\ & = n^{(l-1)} \text{Var}\left(W^{i\;(l)}_j\right) \text{Var}\left(h_i^{(l-1)}\right) \end{align} $$

Суммарно:

$$ \mathrm{Var}\left(h^{(l)}_j\right) \approx \frac{1}{2} \mathrm{Var}\left(z^{(l)}_j\right) = \frac{n^{(l-1)}}{2} \text{Var}\left(W^{i\;(l)}_j\right) \text{Var}\left(h_i^{(l-1)}\right) $$

Теперь наше требование к сохранению дисперсии можно выразить так

$$ \begin{align} & \text{Var}\left(h^{(l)}_j\right) \approx \text{Var}\left(h^{(l-1)}_j\right) \implies \\ & \frac{n^{(l-1)}}{2} \text{Var}\left(W^{i\;(l)}_j\right) \text{Var}\left(h_i^{(l-1)}\right) \approx \text{Var}\left(h^{(l-1)}_j\right) \implies \\ & \boxed{\text{Var}\left(W^{i\;(l)}_j\right) \approx \frac{2}{n^{(l-1)}}} \end{align} $$

Это означает, что веса в слое $l$ должны быть случайными числами с нулевым средним и дисперсией $2/(n^{(l-2)})$, где $n^{(l-1)} $ — число нейронов в предыдущем слое.

Это легко реализовать через нормальное распределение: $$ W^{(l)} \sim \mathcal{N}\left(0, \frac{2}{n^{(l-1)}}\right) $$ Это означает, что мы берем гауссовское распределение случайных чисел с центром в 0, в “окне” шириной $2/(n^{(l-2)})$. Такое распределение весов делает обучение стабильным.


Алексей Бореалис (Марк Русборн)

Алексей Бореалис (Марк Русборн)

Магистр наук (MSc), профессиональный астролог (MAPAI). Об авторе