Содержание курса 19 / 27

19 / 27 · Нейронные сети

От линейных моделей к нейросетям

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

На этой странице 13

Предыдущая заметка закончилась самым сильным инструментом для таблиц: бустингом деревьев и стекингом поверх него. Все модели курса до сих пор получали признаки готовыми. Метод наименьших квадратов и логистическая регрессия подгоняют линейную функцию от них, ядра заранее фиксируют нелинейное отображение признаков, а деревья делят выборку по столбцам, которые кто-то подготовил. Это работает, когда столбцы уже что-то значат: доход, возраст, число покупок. Для сырых сигналов это не работает. Отдельный пиксель, отдельный отсчёт звука или номер отдельного токена сами по себе обычно говорят мало, и десятилетиями полезные признаки изображений, звука и текста конструировали вручную: гистограммы границ, спектральные коэффициенты, счётчики n-грамм. Нейронная сеть (neural network) выучивает отображение признаков по данным вместе с линейной моделью поверх него — тем же градиентным спуском, что и раньше. В этой заметке сеть строится из линейных моделей, обсуждается, что такие сети способны представить, и задаются их выходы и функции потерь; следующая заметка вычисляет их градиенты.

Обозначения. Вход — x∈Rdx \in \mathbb R^d. Сеть состоит из слоёв l=1,…,Ll = 1, \dots, L ширины d1,…,dLd_1, \dots, d_L, причём d0=dd_0 = d. У слоя ll есть матрица весов Wl∈Rdl×dl−1W_l \in \mathbb R^{d_l \times d_{l-1}} и смещение bl∈Rdlb_l \in \mathbb R^{d_l}; он вычисляет предактивацию (pre-activation) zl=Wlhl−1+blz_l = W_l h_{l-1} + b_l и активацию (activation) hl=φ(zl)h_l = \varphi(z_l), где h0=xh_0 = x, а функция активации φ\varphi применяется к каждой координате. У последнего слоя активации нет: его выход f(x)=zLf(x) = z_L — это предсказание или вектор логитов (logits). σ(t)=1/(1+e−t)\sigma(t) = 1/(1 + e^{-t}) — логистическая сигмоида, θ\theta — совокупность всех весов и смещений.

Почему композиции линейных слоёв недостаточно#

Первая идея — составить композицию линейных отображений. Она ничего не даёт: два аффинных слоя — это один аффинный слой,

W2(W1x+b1)+b2=(W2W1) x+(W2b1+b2),W_2 (W_1 x + b_1) + b_2 = (W_2 W_1)\, x + (W_2 b_1 + b_2),

и по индукции то же верно для любого их числа. Глубина без нелинейности добавляет параметры, но не выразительность: композиция — это линейная модель с матрицей весов WL⋯W1W_L \cdots W_1, ранг которой не больше наименьшей ширины, так что узкий слой может её только ограничить.

Нелинейность между слоями это меняет. С одним скрытым слоем

f(x)=W2 φ(W1x+b1)+b2,f(x) = W_2\, \varphi(W_1 x + b_1) + b_2 ,

выход — линейная модель, применённая к признакам h=φ(W1x+b1)h = \varphi(W_1 x + b_1), и эти признаки сами выучиваются.

Разобранный пример: XOR. Четыре точки (0,0),(0,1),(1,0),(1,1)(0,0), (0,1), (1,0), (1,1) с метками 0,1,1,00, 1, 1, 0 нельзя разделить прямой. Если бы неравенство w1x1+w2x2+c>0w_1 x_1 + w_2 x_2 + c > 0 выполнялось ровно для двух точек с меткой 1, то w2+c>0w_2 + c > 0 и w1+c>0w_1 + c > 0, тогда как c≤0c \le 0 и w1+w2+c≤0w_1 + w_2 + c \le 0. Сложив первые два неравенства, получаем w1+w2+c>−c≥0w_1 + w_2 + c > -c \ge 0 — противоречие. Хватает двух ReLU-нейронов, ReLU⁡(t)=max⁡(0,t)\operatorname{ReLU}(t) = \max(0, t) (Goodfellow, Bengio and Courville, 2016, раздел 6.1). Возьмём h1=ReLU⁡(x1+x2)h_1 = \operatorname{ReLU}(x_1 + x_2), h2=ReLU⁡(x1+x2−1)h_2 = \operatorname{ReLU}(x_1 + x_2 - 1) и f=h1−2h2f = h_1 - 2h_2:

xxx1+x2x_1 + x_2h1h_1h2h_2ff
(0,0)(0, 0)0000
(0,1)(0, 1)1101
(1,0)(1, 0)1101
(1,1)(1, 1)2210

В скрытых координатах (h1,h2)(h_1, h_2) входы переходят в (0,0)(0,0), (1,0)(1,0), (1,0)(1,0) и (2,1)(2,1). Две точки класса 1 попадают в одно место, и классы разделяет прямая, например h1−2h2=12h_1 - 2h_2 = \tfrac12. Скрытый слой сделал задачу линейной.

Многослойный перцептрон#

Композиция таких слоёв даёт многослойный перцептрон (multilayer perceptron, MLP), который также называют полносвязной сетью или сетью прямого распространения (feedforward network):

zl=Wlhl−1+bl,hl=φ(zl),l=1,…,L−1,f(x)=WLhL−1+bL.\begin{aligned} z_l &= W_l h_{l-1} + b_l, \qquad h_l = \varphi(z_l), \qquad l = 1, \dots, L-1, \\ f(x) &= W_L h_{L-1} + b_L . \end{aligned}

Каждый скрытый нейрон (hidden unit) вычисляет φ(w⊤h+b)\varphi(w^\top h + b) для одной строки ww своей матрицы весов: линейную модель от предыдущего слоя, за которой следует фиксированная нелинейность. При φ=σ\varphi = \sigma скрытый нейрон имеет функциональный вид логистической регрессии — сигмоиды от аффинной функции, а сеть — линейная модель на признаках такого вида, вычисленных из признаков такого же вида. Аналогия ограничивается видом: у скрытого нейрона нет собственной наблюдаемой метки 0/1, а его веса обучаются совместно с остальной сетью.

На практике сеть обрабатывает сразу мини-батч из nn объектов, записанных строками матрицы X∈Rn×dX \in \mathbb R^{n \times d}. Тогда каждый слой — одно матричное произведение, Zl=Hl−1Wl⊤+1bl⊤Z_l = H_{l-1} W_l^\top + \mathbf 1 b_l^\top, где H0=XH_0 = X, а смещение прибавляется к каждой строке. nn.Linear(d_in, d_out) в PyTorch хранит вес размера (d_out, d_in) — это размер WlW_l в наших обозначениях — и вычисляет x @ W.T + b.

Разобранный пример: размерности. Классификатор изображений цифр 8×88 \times 8 на 10 классов с ширинами слоёв 64→128→64→1064 \to 128 \to 64 \to 10:

слойвес WlW_lсмещение blb_lпараметрывыход для батча из 32
вход32×6432 \times 64
1128×64128 \times 64128832032×12832 \times 128
264×12864 \times 12864825632×6432 \times 64
3 (логиты)10×6410 \times 641065032×1032 \times 10

У сети 17 22617\,226 параметров, почти все они в матрицах весов; их число растёт как произведение ширин соседних слоёв.

Функции активации#

φ(t)\varphi(t)φ′(t)\varphi'(t)область значенийтипичное применение
сигмоидаσ(t)=11+e−t\sigma(t) = \dfrac{1}{1 + e^{-t}}σ(t) (1−σ(t))≤14\sigma(t)\,(1 - \sigma(t)) \le \tfrac14(0,1)(0, 1)выходные вероятности, гейты
tanhtanh⁡t=2σ(2t)−1\tanh t = 2\sigma(2t) - 11−tanh⁡2t≤11 - \tanh^2 t \le 1(−1,1)(-1, 1)рекуррентные сети
ReLUmax⁡(0,t)\max(0, t)11 при t>0t > 0, 00 при t<0t < 0[0,∞)[0, \infty)по умолчанию в скрытых слоях
GELUt Φ(t)t\, \Phi(t)Φ(t)+t ϕ(t)\Phi(t) + t\, \phi(t)[−0.170,∞)[-0.170, \infty)трансформеры

Здесь Φ\Phi и ϕ\phi — функция распределения и плотность стандартного нормального распределения. Важны три свойства:

  • Насыщение. Сигмоида и tanh становятся плоскими при больших ∣t∣\lvert t \rvert, где их производная близка к нулю. Нейрон, попавший в эту область, почти не пропускает градиент назад, а в стопке сигмоидных слоёв производная сигмоиды вносит множитель не больше 14\tfrac14 на каждом слое; это разобрано в курсе о генеративных моделях.
  • ReLU (Nair and Hinton, 2010; Glorot, Bordes and Bengio, 2011) имеет наклон ровно 1 везде, где она активна, и дёшево вычисляется. Её слабость — другая сторона: нейрон, у которого предактивация отрицательна на всех входах, получает нулевой градиент и уже не восстанавливается — это «мёртвый» нейрон.
  • GELU (Hendrycks and Gimpel, 2016, раздел 2) умножает tt на вероятность Φ(t)\Phi(t) того, что стандартная нормальная величина окажется меньше этого аргумента. Это гладкая ReLU, пропускающая небольшие отрицательные значения: её минимум равен −0.170-0.170 при t=−0.752t = -0.752. BERT и GPT-2 используют её в полносвязной части каждого блока трансформера.

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

Что может представить один скрытый слой#

Дополнительный материал: теоремы о приближении, стоящие за ключевой идеей ниже, и то, чего они не обещают. Остальная часть заметки на него не опирается.

Теорема. Пусть σ\sigma — любая непрерывная сигмоидальная функция, то есть σ(t)→1\sigma(t) \to 1 при t→+∞t \to +\infty и σ(t)→0\sigma(t) \to 0 при t→−∞t \to -\infty. Для любой непрерывной функции gg на единичном кубе [0,1]d[0,1]^d и любого ε>0\varepsilon > 0 найдутся число NN и параметры αj,βj∈R\alpha_j, \beta_j \in \mathbb R, wj∈Rdw_j \in \mathbb R^d, такие что

G(x)=∑j=1Nαj σ(wj⊤x+βj)удовлетворяет∣G(x)−g(x)∣<ε  при всех x∈[0,1]dG(x) = \sum_{j=1}^{N} \alpha_j\, \sigma(w_j^\top x + \beta_j) \quad \text{удовлетворяет} \quad \lvert G(x) - g(x) \rvert < \varepsilon \ \text{ при всех } x \in [0,1]^d

(Cybenko, 1989, теорема 2). GG — это MLP с одним скрытым слоем из NN нейронов и линейным выходом. Hornik (1991), теорема 2, распространяет утверждение на любую непрерывную, ограниченную и непостоянную активацию на любом компакте (замкнутом ограниченном множестве), а Leshno et al. (1993), теорема 1, закрывают вопрос для неограниченных активаций, таких как ReLU: для локально ограниченной кусочно-непрерывной φ\varphi сети с одним скрытым слоем равномерно приближают на компактах любую непрерывную функцию тогда и только тогда, когда φ\varphi — не многочлен. Полиномиальная активация степени kk не годится, потому что тогда сеть — многочлен степени не выше kk от xx, какой бы широкой она ни была.

Разобранный пример: ReLU-нейроны как кусочно-линейное приближение. В одномерном случае построение явное. Расставим узлы xk=k/Nx_k = k/N на [0,1][0, 1] и обозначим через sks_k наклон отрезка от (xk,g(xk))(x_k, g(x_k)) до (xk+1,g(xk+1))(x_{k+1}, g(x_{k+1})). Ломаная, проходящая через эти точки, равна

g^(x)=g(0)+∑k=0N−1ck ReLU⁡(x−xk),c0=s0,  ck=sk−sk−1,\hat g(x) = g(0) + \sum_{k=0}^{N-1} c_k\, \operatorname{ReLU}(x - x_k), \qquad c_0 = s_0,\ \ c_k = s_k - s_{k-1},

это сеть с одним скрытым слоем из NN ReLU-нейронов: нейрон kk включается в узле xkx_k и меняет наклон на ckc_k. Для g(x)=sin⁡2πxg(x) = \sin 2\pi x наибольшая ошибка на [0,1][0, 1] равна 0.21050.2105, 0.07040.0704, 0.01880.0188 и 0.00480.0048 при N=4,8,16,32N = 4, 8, 16, 32 нейронах. Удвоение ширины делит ошибку примерно на 4, как и предсказывает оценка погрешности интерполяции 18h2max⁡∣g′′∣\tfrac18 h^2 \max \lvert g'' \rvert с шагом h=1/Nh = 1/N.

Чего теорема не обещает#

  • Разумного размера. Теорема говорит о существовании. Сам Цыбенко в заключении (раздел 5) ожидает, что для большинства задач аппроксимации понадобится астрономическое число нейронов — из-за проклятия размерности, встреченного в заметке о ближайших соседях. Кусочно-линейное построение разбивает вход на ячейки: при 16 узлах на ось в размерности d=10d = 10 ему нужно 1610≈1.1⋅101216^{10} \approx 1.1 \cdot 10^{12} ячеек. Хватит ли гораздо меньшего числа нейронов, зависит от функции. Barron (1993) показывает, что для функций, у которых преобразование Фурье имеет конечный первый момент CgC_g, NN сигмоидальных нейронов дают интегральную квадратичную ошибку порядка Cg2/NC_g^2/N без зависимости от размерности, тогда как никакой фиксированный набор из NN базисных функций не может быть лучше порядка N−2/dN^{-2/d} на этом классе.
  • Что обучение его найдёт. Веса существуют; функция потерь как функция от них не выпукла, и градиентный спуск может остановиться в другом месте. Эксперимент с XOR ниже показывает, как это происходит.
  • Что хватит выборки. Приблизить истинную функцию и оценить её по nn зашумлённым примерам — разные задачи. Широкая сеть может подогнаться под любую обучающую выборку вместе с шумом, а это переобучение в чистом виде.
  • Ничего вне области. Гарантия действует на компакте. За пределами диапазона обучающих входов ReLU-сеть просто продолжается линейно, а данные там редко ведут себя так.

Глубина против ширины#

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

Если одного скрытого слоя достаточно для универсальности, зачем использовать много слоёв? Потому что для некоторых функций композиция гораздо эффективнее сложения. Одномерный пример делает это утверждение точным.

Разобранный пример: функция-«палатка» (tent map). На [0,1][0, 1] сеть

g(x)=2ReLU⁡(x)−4ReLU⁡(x−12)g(x) = 2\operatorname{ReLU}(x) - 4\operatorname{ReLU}\big(x - \tfrac12\big)

задаёт «палатку»: g(x)=2xg(x) = 2x при x≤12x \le \tfrac12 и g(x)=2−2xg(x) = 2 - 2x при x≥12x \ge \tfrac12, со значениями в [0,1][0, 1]. Композиция с самой собой, g∘gg \circ g, дважды проходит палатку вверх и вниз: 4 линейных куска. Каждая следующая композиция удваивает их число, так что у gk=g∘⋯∘gg^{k} = g \circ \dots \circ g ровно 2k2^k кусков. Как сеть, gkg^k имеет kk скрытых слоёв по 2 ReLU-нейрона (Telgarsky, 2016, лемма 3.10).

Сеть с одним скрытым слоем из mm ReLU-нейронов вычисляет на прямой c+∑j=1majReLU⁡(wjx+βj)c + \sum_{j=1}^m a_j \operatorname{ReLU}(w_j x + \beta_j). Эта функция линейна между mm точками излома −βj/wj-\beta_j / w_j, поэтому у неё не больше m+1m + 1 кусков. Чтобы точно представить gkg^k, ей нужно m≥2k−1m \ge 2^k - 1 нейронов: 31 против 10 у глубокой сети при k=5k = 5 и 1 048 5751\,048\,575 против 40 при k=20k = 20.

Рисунок

Глубина умножает число линейных кусков

Композиции функции-палаткиЧетыре графика на [0, 1]: одна палатка, две, четыре и шестнадцать зубцов пилы; число линейных кусков 2, 4, 8 и 32.1 слой · 2 нейрона012 кускаодин слой: ≥ 12 слоя · 4 нейрона014 кускаодин слой: ≥ 33 слоя · 6 нейронов018 кусководин слой: ≥ 75 слоёв · 10 нейронов0132 кускаодин слой: ≥ 31Композиции функции-палаткиЧетыре графика на [0, 1]: одна палатка, две, четыре и шестнадцать зубцов пилы; число линейных кусков 2, 4, 8 и 32.1 слой · 2 нейрона012 кускаодин слой: ≥ 12 слоя · 4 нейрона014 кускаодин слой: ≥ 33 слоя · 6 нейронов018 кусководин слой: ≥ 75 слоёв · 10 нейронов0132 кускаодин слой: ≥ 31
Функция-«палатка» g(x) = 2 ReLU(x) − 4 ReLU(x − 1/2) на [0, 1] и её композиции g∘g, g∘g∘g и пятикратная: сеть из k скрытых слоёв по 2 ReLU-нейрона. Каждый новый слой удваивает число линейных кусков: 2, 4, 8, 32. Сеть с одним скрытым слоем из m ReLU-нейронов даёт на прямой не больше m + 1 кусков, поэтому для пятой композиции ей нужно не меньше 31 нейронов против 10 у глубокой сети.

Общие утверждения, стоящие за примером: число линейных областей ReLU-сети может расти экспоненциально с глубиной и лишь полиномиально с шириной (Montúfar et al., 2014), и существуют функции, которые вычисляются глубокими узкими ReLU-сетями, но не приближаются никакой сетью с гораздо меньшим числом слоёв, если она не экспоненциально широка (Telgarsky, 2016, теорема 1.1). Глубина позволяет поздним слоям переиспользовать то, что вычислили ранние. В сетях для изображений ранние слои выделяют границы, а поздние собирают из них части и объекты, как описано в разделе о свёртках курса о генеративных моделях.

У глубины есть цена. Каждый слой умножает градиент на ещё один множитель, и глубокие стопки оптимизировать труднее, чем неглубокие; этим занимается заметка «Обучение глубоких сетей». Ширина тоже помогает оптимизации: у более широкой сети гораздо больше способов подогнаться под данные, и градиентный спуск надёжнее находит один из них, как показывает следующий эксперимент.

Сеть сама выучивает признаки#

Построение для XOR выше было придумано вручную. Те же признаки получаются и при обучении. Возьмём 200 точек в четырёх облаках вокруг (±1,±1)(\pm 1, \pm 1), где класс 1 — там, где знаки различаются, и сеть с двумя скрытыми нейронами tanh и сигмоидой на выходе, обученную градиентным спуском (Adam, 300 шагов по всей выборке) на бинарной кросс-энтропии (BCE):

Рисунок

Сеть сама находит признаки для XOR

XOR во входном пространстве и в скрытом слоеСлева четыре облака точек в шахматном порядке и две параллельные пунктирные прямые скрытых нейронов; красные облака лежат между прямыми. Справа те же точки в координатах скрытого слоя: синие облака A и B в углах (−1, −1) и (1, 1), красные C и D вместе в углу (1, −1), их отделяет одна прямая.(а) входы x-2-1012-2-112h₁ = 0h₂ = 0ABCD(б) скрытый слой h-101-11h₁h₂ABC, Dh = tanh(W₁x + b₁)класс 0: x₁x₂ > 0класс 1: x₁x₂ < 0скрытые нейронывыходной нейронXOR во входном пространстве и в скрытом слоеСлева четыре облака точек в шахматном порядке и две параллельные пунктирные прямые скрытых нейронов; красные облака лежат между прямыми. Справа те же точки в координатах скрытого слоя: синие облака A и B в углах (−1, −1) и (1, 1), красные C и D вместе в углу (1, −1), их отделяет одна прямая.(а) входы x-2-1012-2-112h₁ = 0h₂ = 0ABCD(б) скрытый слой h-101-11h₁h₂ABC, Dh = tanh(W₁x + b₁)класс 0: x₁x₂ > 0класс 1: x₁x₂ < 0скрытые нейронывыходной нейрон
200 точек из четырёх облаков A, B, C, D вокруг (±1, ±1); класс 1 (красный) там, где знаки координат различаются. Сеть 2 → 2 → 1 с tanh в скрытом слое после 300 шагов Adam верно классифицирует все точки (кросс-энтропия 0.013). (а) Каждый скрытый нейрон — это прямая во входной плоскости: h₁ = tanh(3.8x₁ + 3.2x₂ + 3.4) отрицателен только при обеих отрицательных координатах («или»), h₂ = tanh(3.4x₁ + 3.4x₂ − 3.1) положителен только при обеих положительных («и»). Сеть относит точку к классу 1 почти ровно тогда, когда та лежит между прямыми (совпадение на 99.6% квадрата). (б) В координатах (h₁, h₂) облака попадают в три угла: A в (−1, −1), B в (1, 1), C и D вместе в (1, −1), и одна прямая выходного нейрона 5.3h₁ − 5.4h₂ − 5.2 = 0 разделяет классы.

Обученная сеть верно классифицирует все 200 точек. Её первый скрытый нейрон, h1=tanh⁡(3.8x1+3.2x2+3.4)h_1 = \tanh(3.8x_1 + 3.2x_2 + 3.4), отрицателен, только когда обе координаты отрицательны: это «или» (OR). Второй, h2=tanh⁡(3.4x1+3.4x2−3.1)h_2 = \tanh(3.4x_1 + 3.4x_2 - 3.1), положителен, только когда обе координаты положительны: это «и» (AND). Выходной нейрон вычисляет σ(5.3h1−5.4h2−5.2)\sigma(5.3h_1 - 5.4h_2 - 5.2), и это значение велико ровно тогда, когда «или» включено, а «и» выключено. С точностью до гладкой активации это два признака ручного решения, найденные подгонкой.

Однако успех оптимизации не гарантирован. Если обучить ту же сеть с двумя нейронами из 10 случайных инициализаций, классы разделяют лишь 4 запуска; остальные 6 останавливаются в плохом решении с долей правильных ответов (accuracy) 65–75%. С четырьмя скрытыми нейронами успешны все 10 запусков. Дополнительная ширина стоит параметров и покупает более простую задачу оптимизации.

Так же можно прочитать любую сеть: последний слой — линейная модель на признаках hL−1(x)h_{L-1}(x), вычисленных слоями под ним. На данных «две луны» из раздела «Код» (1000 точек, шум 0.2, половина отложена) логистическая регрессия на исходных координатах достигает на тесте доли правильных ответов 0.8480.848, а сеть 2→16→16→12 \to 16 \to 16 \to 1 — 0.9580.958. Новая логистическая регрессия, обученная на 16 признаках последнего скрытого слоя сети, тоже даёт 0.9580.958: вся нелинейность сосредоточена в отображении признаков, а классификатор поверх него — логистическая регрессия из прежних заметок.

Выходные слои и функции потерь#

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

задачавыходывыходная активациямодель для yyфункция потерьPyTorch
регрессия1 (или kk)нетN(f(x),σ2)N(f(x), \sigma^2)квадратичная ошибкаMSELoss
два класса1 логитсигмоидаBernoulli(σ(f(x)))\mathrm{Bernoulli}(\sigma(f(x)))бинарная кросс-энтропияBCEWithLogitsLoss
KK классовKK логитовsoftmaxCategorical(softmax⁡f(x))\mathrm{Categorical}(\operatorname{softmax} f(x))кросс-энтропияCrossEntropyLoss
KK метокKK логитовKK сигмоидKK независимых бернуллиевских величинсумма KK бинарных кросс-энтропийBCEWithLogitsLoss

Строка для двух классов превращает последний слой в логистическую регрессию на hL−1h_{L-1}, а две нижние строки — это softmax-модель и модель «один против остальных» из заметки о многоклассовой классификации, теперь на выученных признаках. При softmax pk=ezk/∑jezjp_k = e^{z_k} / \sum_j e^{z_j} и истинном классе yy кросс-энтропия равна

−log⁡py=−zy+log⁡∑j=1Kezj.-\log p_y = -z_y + \log \sum_{j=1}^{K} e^{z_j} .

Разобранный пример. Для логитов z=(2,1,−1)z = (2, 1, -1) имеем ez=(7.389,2.718,0.368)e^z = (7.389, 2.718, 0.368) с суммой 10.47510.475, так что p=(0.705,0.259,0.035)p = (0.705, 0.259, 0.035). Если истинный класс первый, функция потерь равна −log⁡0.705=0.349-\log 0.705 = 0.349; если бы он был третьим, она была бы равна −log⁡0.035=3.35-\log 0.035 = 3.35.

Функции потерь PyTorch из таблицы принимают логиты, а не вероятности, и вычисляют log⁡∑jezj\log \sum_j e^{z_j} как m+log⁡∑jezj−mm + \log \sum_j e^{z_j - m} с m=max⁡jzjm = \max_j z_j. Для логитов (1000,0)(1000, 0) наивное e1000e^{1000} переполняется, а сдвинутая форма даёт значение функции потерь точно. Если применить сигмоиду или softmax в модели, а функцию потерь — к вероятностям, эта защита теряется. Тот же softmax по словарю служит выходным слоем каждой нейросетевой языковой модели в курсе NLP, а курс о генеративных моделях объясняет, почему минимизация кросс-энтропии минимизирует KL-дивергенцию.

Свёртки: линейный слой с ограничениями#

Когда у входа есть сеточная структура, как у пикселей изображения, отсчётов звука или токенов предложения, полносвязный первый слой тратит параметры впустую и игнорирует локальность. У цветного изображения 32×3232 \times 32 есть 30723072 входа; полносвязному слою, выдающему карту 32×3232 \times 32 с 16 каналами, нужно 3072×16 384≈50.33072 \times 16\,384 \approx 50.3 миллиона весов. Свёрточный слой (convolution layer) вместо этого применяет одно и то же маленькое ядро в каждой позиции: каждый выход смотрит только на окрестность (локальность), а ядро общее для всех позиций (разделение весов, weight sharing), поэтому тот же слой с ядрами 3×33 \times 3 имеет 16⋅3⋅9+16=44816 \cdot 3 \cdot 9 + 16 = 448 параметров. Это по-прежнему линейный слой — с матрицей весов, которая обязана быть разреженной и повторяющейся, — и всё сказанное в этой и следующей заметках к нему применимо. Его размерности, параметры и инициализация разобраны в курсе о генеративных моделях, а курс NLP использует одномерные свёртки по токенам текста.

Что изменилось#

Линейные модели, ядра, бустингНейронные сети
Признакиданы, сконструированы вручную или заданы ядромвыучиваются скрытыми слоями
Модельлинейная по фиксированным признакам или сумма деревьевкомпозиции линейных отображений и нелинейностей
Что может представитьограничена признакамилюбую непрерывную функцию на ограниченной области при достаточном числе нейронов
Выход и функция потерьсигмоида или softmax с кросс-энтропией, квадратичная ошибкато же самое в последнем слое
Обучениевыпуклое (линейная, логистическая) или жадное (деревья)невыпуклое, градиентным спуском через все слои

Ограничения#

  • Невыпуклое обучение. Разные инициализации дают разные сети, и некоторые запуски неудачны, как у сети для XOR с двумя нейронами в 6 из 10 запусков. Результаты нужно проверять на нескольких начальных значениях генератора (seed).
  • Данные и вычисления. У сети много параметров, и для их оценки нужно много примеров. На типичных табличных данных умеренного размера ансамбли деревьев по-прежнему не уступают сетям или превосходят их (Grinsztajn, Oyallon and Varoquaux, 2022); сети выигрывают там, где признаки нужно выучивать из сырых сигналов.
  • Существование — не построение. Теорема об универсальной аппроксимации не даёт ни ширины, ни процедуры обучения, ни размера выборки.
  • Много проектных решений. Глубина, ширина, активация, инициализация, шаг обучения и регуляризация — всё это важно; последняя заметка раздела собирает стандартные варианты.
  • Непрозрачные признаки. Выученный признак — это направление в скрытом слое, а не именованный столбец, поэтому модель труднее объяснить, чем дерево или линейную модель.

Что нужно запомнить#

  • Композиция линейных слоёв — линейный слой; глубину делает полезной нелинейность между ними.
  • MLP вычисляет hl=φ(Wlhl−1+bl)h_l = \varphi(W_l h_{l-1} + b_l) с Wl∈Rdl×dl−1W_l \in \mathbb R^{d_l \times d_{l-1}}; последний слой — линейная модель (логиты) на выученных признаках.
  • ReLU — скрытая активация по умолчанию, GELU — её гладкий вариант; сигмоида и tanh насыщаются.
  • Один скрытый слой приближает любую непрерывную функцию на компакте тогда и только тогда, когда активация — не многочлен (Цыбенко, Хорник, Лешно и др.), но без оценки ширины и без гарантий обучения и обобщения.
  • Глубина может быть экспоненциально эффективнее ширины: kk слоёв по 2 ReLU-нейрона дают 2k2^k линейных кусков, одному слою нужно 2k−12^k - 1 нейронов.
  • Выходной слой и функция потерь следуют из правдоподобия: тождественный выход с квадратичной ошибкой, сигмоида с бинарной кросс-энтропией, softmax с кросс-энтропией; в функцию потерь передаются логиты.

Код#

Схлопывание линейных слоёв, XOR вручную, подсчёт параметров, функция потерь softmax, признаки, выученные на двух лунах, ReLU-интерполяция и функция-палатка:

import numpy as np, torch
from torch import nn
from sklearn.datasets import make_moons
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split

rng = np.random.default_rng(0)
relu = lambda z: np.maximum(z, 0)

# два линейных слоя — это один линейный слой: W2 (W1 x + b1) + b2 = (W2 W1) x + (W2 b1 + b2)
W1, b1, W2, b2, x = rng.normal(size=(4, 3)), rng.normal(size=4), rng.normal(size=(2, 4)), rng.normal(size=2), rng.normal(size=3)
print(np.allclose(W2 @ (W1 @ x + b1) + b2, (W2 @ W1) @ x + (W2 @ b1 + b2)))   # True

# XOR на двух ReLU-нейронах: h = ReLU(x1 + x2, x1 + x2 - 1), выход h1 - 2 h2
X = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])
h = relu(X @ np.array([[1, 1], [1, 1]]) + np.array([0, -1]))
print(h.tolist(), (h @ np.array([1, -2])).tolist())   # [[0, 0], [1, 0], [1, 0], [2, 1]] [0, 1, 1, 0]

# параметры сети 64 -> 128 -> 64 -> 10
mlp = nn.Sequential(nn.Linear(64, 128), nn.ReLU(), nn.Linear(128, 64), nn.ReLU(), nn.Linear(64, 10))
print(sum(p.numel() for p in mlp.parameters()))   # 17226

# softmax и кросс-энтропия для логитов (2, 1, -1), когда истинный класс первый
z = torch.tensor([2.0, 1.0, -1.0])
print(torch.softmax(z, 0).numpy().round(3), round(nn.functional.cross_entropy(z[None], torch.tensor([0])).item(), 3))
# [0.705 0.259 0.035] 0.349

# две луны: логистическая регрессия на (x1, x2) против сети 2 -> 16 -> 16 -> 1
Xm, ym = make_moons(1000, noise=0.2, random_state=0)
Xtr, Xte, ytr, yte = train_test_split(Xm, ym, test_size=0.5, random_state=0)
print(round(LogisticRegression().fit(Xtr, ytr).score(Xte, yte), 3))   # 0.848
torch.manual_seed(0)
body = nn.Sequential(nn.Linear(2, 16), nn.ReLU(), nn.Linear(16, 16), nn.ReLU())
head = nn.Linear(16, 1)
opt = torch.optim.Adam([*body.parameters(), *head.parameters()], lr=0.01)
Xt, yt = torch.tensor(Xtr, dtype=torch.float32), torch.tensor(ytr, dtype=torch.float32)
for step in range(1000):
    opt.zero_grad()
    loss = nn.functional.binary_cross_entropy_with_logits(head(body(Xt)).squeeze(1), yt)
    loss.backward()
    opt.step()
with torch.no_grad():
    Ftr, Fte = body(Xt).numpy(), body(torch.tensor(Xte, dtype=torch.float32)).numpy()
    acc = ((head(torch.tensor(Fte)).squeeze(1) > 0).numpy() == yte).mean()
# сеть и новая логистическая регрессия на 16 признаках последнего скрытого слоя сети
print(round(float(acc), 3), round(LogisticRegression(max_iter=1000).fit(Ftr, ytr).score(Fte, yte), 3))   # 0.958 0.958

# один скрытый слой из N ReLU-нейронов интерполирует sin(2 pi x) в N + 1 узлах: ошибка падает как 1/N^2
f = lambda t: np.sin(2 * np.pi * t)
grid = np.linspace(0, 1, 10001)
for N in (4, 8, 16, 32):
    knots = np.linspace(0, 1, N + 1)
    slopes = np.diff(f(knots)) * N
    c = np.diff(slopes, prepend=0.0)
    approx = f(0) + relu(grid[:, None] - knots[:-1]) @ c
    print(N, round(float(np.abs(approx - f(grid)).max()), 4))
# 4 0.2105 | 8 0.0704 | 16 0.0188 | 32 0.0048

# глубина: палатка g(x) = 2 ReLU(x) - 4 ReLU(x - 1/2), применённая k раз, имеет 2^k линейных кусков
g = lambda t: 2 * relu(t) - 4 * relu(t - 0.5)
t = np.linspace(0, 1, 2**12 + 1)
for k in (1, 2, 3, 5):
    y = t.copy()
    for _ in range(k):
        y = g(y)
    slopes = np.round(np.diff(y) / np.diff(t), 6)
    print(k, 1 + int((slopes[1:] != slopes[:-1]).sum()))
# 1 2 | 2 4 | 3 8 | 5 32

Дальше#

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

Литература#