Алгоритм обратного распространения ошибки на примере Word2Vec

Поскольку я столкнулся с существенными затруднениями в поисках объяснения механизма обратного распространения ошибки, которое мне понравилось бы, я решил написать собственный пост об обратном распространении ошибки реализовав алгоритм Word2Vec. Моя цель, — объяснить сущность алгоритма, используя простую, но нетривиальную нейросеть. Кроме того, word2vec стал настолько популярным в NLP сообществе, что будет полезно сосредоточиться на нем.

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

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

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

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

1. Что такое алгоритм обратного распространения

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

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

Алгоритм обратного распространения ошибки на примере Word2Vec — IT-МИР. ПОМОЩЬ В IT-МИРЕ 2020
Рисунок 1. Визуальное объяснение алгоритма градиентного спуска.

Вначале мы не знаем оптимальные значения, то есть не знаем какие значения w1 и w2 минимизируют функцию потерь.

Допустим, мы начинаем с красной точки. Если мы знаем как изменяется функция потерь при изменении весов, то есть если мы знаем производные $partialmathcal{L}/partial w_1$ и $partialmathcal{L}/partial w_2$, то мы можем сдвинуть красную точку ближе к минимуму функции потерь, которая представлена на графике синей точкой. Шаг сдвига определяется параметром $eta$, который обычно называется параметром обучения.

2. Word2Vec

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

Например, модель word2vec обученная со скрытым слоем размерности [N, 3], где N -количество слов в словаре, даст трехмерные эмбеддинги слов. Это значит что, например, слово ‘квартира’, будет представлено трехмерным вектором действительных чисел, который будет близок (воспринимайте это как Евклидову метрику), к представлению аналогичного слова, такого как ‘дом’. Другими словами, word2vec это техника отображения слов в числа.

В контексте word2vec используются две основные модели: мешок слов (CBOW) и скипграммы (skip-gram). Сначала мы рассмотрим простейшую модель, CBOW, с окном из одного слова, затем перейдем к окну из нескольких слов и наконец рассмотрим модель skip-gram.

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

3. Простая модель CBOW

В CBOW модели задача состоит в поиске слова по его контексту. В простейшем случае когда контекст слова представлен одним словом, нейронная сеть будет выглядеть так:
Алгоритм обратного распространения ошибки на примере Word2Vec — IT-МИР. ПОМОЩЬ В IT-МИРЕ 2020
Рисунок 2. Топология модели Continuous Bag-of-Words модели с контекстом из одного слова

Один входной слой, один скрытый слой и выходной слой, функция активации скрытого слоя
a = 1 (identity function, или функция линейной активации, хотя последнее неправильно).
В качестве функции активации выходного слоя используется Softmax.

Входной слой представлен one hot encoding вектором, длина которого совпадает с размером массива слов, все элементы данного вектора равны нулю, кроме элементов, индексы которых совпадают с индексами слов из контекста, по данным индексам значения вектора равны 1.

Пример: словарь [‘мама’, ‘мыла’, ‘раму’, ‘маша’, ‘ела’, ‘кашу’]
OneHot(‘маша’) = [0, 0, 0, 1, 0, 0]
OneHot([‘мама’, ‘маша’]) = [1, 0, 0, 1, 0, 0]
OneHot([‘мама’, ‘ела’, ‘кашу’]) = [1, 0, 0, 0, 1, 1]

Перейдем к весам, весовые коэффициенты между входным и скрытым слоем представлены матрицей W размера $Vtimes N$, матрица между скрытым и выходным слоем $W’$ размером $Ntimes V$, где V — размер словаря, N — размер эмбеддинг вектора (то есть, те вектора которые и пытается найти word2vec)

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

Если на текущем этапе что то звучит непонятно, то пример ниже должен прояснить ситуацию.

Пример

Предположим, мы хотим обучить word2vec следующим текстом:
"I like playing football"
Мы решаем обучить модель CBOW с одним контекстным словом как на рисунке (2) выше.
Учитывая текст который у нас есть, наш словарь будет состоять из 4 слов, соответственно V=4, также установим что скрытый слой будет иметь два нейрона, то есть N=2, наша нейросеть будет выглядеть так:

Алгоритм обратного распространения ошибки на примере Word2Vec — IT-МИР. ПОМОЩЬ В IT-МИРЕ 2020

Со словарём:

$textrm{Vocabulary}=[textrm{“I”}, textrm{“like”}, textrm{“playing”}, textrm{“football”}]$

Далее мы определяем как выбирать ‘целевое’ и ‘контекстное’ слово, и мы можем построить наши обучающие примеры двигаясь окном по тексту. Например:

Алгоритм обратного распространения ошибки на примере Word2Vec — IT-МИР. ПОМОЩЬ В IT-МИРЕ 2020

Тогда обучающие примеры будут выглядеть так:

Алгоритм обратного распространения ошибки на примере Word2Vec — IT-МИР. ПОМОЩЬ В IT-МИРЕ 2020

Чтобы передать эти данные в алгоритм понадобится преобразовать их в числа, для чего используем one-hot encoding.

Алгоритм обратного распространения ошибки на примере Word2Vec — IT-МИР. ПОМОЩЬ В IT-МИРЕ 2020

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

3.1 Функция потерь (Loss function)

Исходя из топологии на рисунке 1, давайте запишем как посчитать значения скрытого и выходного слоев, при входе x:

$begin{eqnarray*} textbf{h} = & W^Ttextbf{x} hspace{7.0cm} \ textbf{u}= & W'^Ttextbf{h}=W'^TW^Ttextbf{x} hspace{4.6cm} \ textbf{y}= &   mathbb{S}textrm{oftmax}(textbf{u})= mathbb{S}textrm{oftmax}(W'^TW^Ttextbf{x}) hspace{2cm} end{eqnarray*}$

где, h — сумма на скрытом слое, u — сумма на выходном слое, y — выход сети.

Теперь, предположим, мы обучаем модель по паре целевое слово, контекстное слово (wt, wc). Целевое слово представляет собой идеальное предсказание нейронной сети, в виде onehot encoding вектора.

Функция потерь должна будет оценить выходной слой, относительно onehot вектора wt (целевого слова).

Поскольку значения softmax можно интерпретировать как условные вероятности целевого слова, то учитывая контекстное слово функцию потерь можно записать в следующем виде:

$$display$$begin{equation*} mathcal{L} = -log mathbb{P}(w_t|w_c)=-log y_{j^*} =-log[mathbb{S}textrm{oftmax}(u_{j^*})]=-logleft(frac{exp{u_{j^*}}}{sum_i exp{u_i}}right), end{equation*}$$display$$

, где j* — ожидаемая позиция правильно предсказанного слова.
Добавление функции логарифма является стандартным подходом. Из данного выражения мы получаем уравнение (1):

$$display$$begin{equation} bbox[lightblue,5px,border:2px solid red]{ mathcal{L} = -u_{j^*} + log sum_i exp{(u_i)}. } label{eq:loss} end{equation} >(1)$$display$$

Функция потерь максимизирует вероятность предсказания правильного слова исходя из заданного контекста.

Пример

Вернемся к предыдущему примеру предложения "I like play football", предположим мы обучаем модель по первой записи обучающих данных, с контекстным словом "I" и целевым словом "like", в идеале веса сети должны быть такими, чтобы при вводе $textbf{x}=(1, 0, 0, 0)$ — что соответствует слову "I", результат работы был близок к $hat{textbf{y}}=(0, 1, 0, 0)$, что соответствует слову "like".

Стандартный подход к инициализации весов word2vec, использование нормального распределения. Примем начальное состояние весов матрицы W размером $4times 2$

$W = begin{pmatrix} -1.38118728 & 0.54849373 \ 0.39389902 & -1.1501331 \ -1.16967628 & 0.36078022 \ 0.06676289 & -0.14292845 end{pmatrix}$

И начальное состояние весов матрицы $W’$ размером $2times 4$

$W' = begin{pmatrix} 1.39420129 & -0.89441757 & 0.99869667 & 0.44447037 \ 0.69671796 & -0.23364341 & 0.21975196 & -0.0022673 end{pmatrix}$

Для обучающих данных "I like" мы получаем:

$textbf{h} = W^Ttextbf{x}= begin{pmatrix} -1.38118728 \ 0.54849373 end{pmatrix}$

Затем

$textbf{u} = W'^Ttextbf{h}= begin{pmatrix} -1.54350765 \ 1.10720623 \ -1.25885456 \ -0.61514042 end{pmatrix}$

И наконец

$textbf{y} = mathbb{S}textrm{oftmax}(textbf{u})= begin{pmatrix} 0.05256567 \ 0.7445479 \ 0.06987559 \ 0.13301083 end{pmatrix}$

На этом этапе функция потерь будет отрицательным логарифмом второго элемента $textbf{y}$, или

$mathcal{L}=-logmathbb{P}(textrm{“like”}|textrm{“I”})=-log y_3 = -log(0.7445479)= 0.2949781.$

Также, мы могли бы рассчитать ее при помощи уравнения (1):

$begin{eqnarray*} mathcal{L}=-u_2+logsum_{i=1}^4 u_i=-1.10720623 + log[exp(-1.54350765)+exp(1.10720623) \ +exp(-1.25885456)+exp(-0.61514042)]=0.2949781. end{eqnarray*}$

Теперь, прежде чем перейти к следующему обучающему примеру "like play", мы должны изменить веса нейросети, как это сделать расскажет следующий пункт про обратное распространение.

3.2 Алгоритм обратного распространения для модели CBOW

Теперь, имея в наличии функцию потерь, мы хотим найти веса W и W` которые ее минимизируют. В терминах машинного обучения, мы хотим чтобы модель обучилась.

В первом разделе мы уже обсудили что в мире нейронный сетей эта проблема решается использованием градиентного спуска. На рисунке (1) показано как применить этот метод и обновить матрицы весов W и W`. Нам нужно найти производные $partial mathcal{L}/partial{W}$ и $partial mathcal{L}/partial{W’}$

Я считаю что самый простой способ понять как это сделать, это записать соотношения между функцией потерь и матрицами весов. Глядя на уравнение (1) ясно что функция потерь зависит от весов W и W`, через переменную u=[u1, …., uV], или

$$display$$begin{equation*} mathcal{L} = mathcal{L}(mathbf{u}(W,W’))=mathcal{L}(u_1(W,W’), u_2(W,W’),dots, u_V(W,W’)) . end{equation*}$$display$$

Получить производные можно из правила цепочки для функций многих переменных:

$$display$$begin{equation} frac{partialmathcal{L}}{partial W’_{ij}} = sum_{k=1}^Vfrac{partialmathcal{L}}{partial u_k}frac{partial u_k}{partial W’_{ij}} label{eq:dLdWp} end{equation} >(2)$$display$$

и

$$display$$begin{equation} frac{partialmathcal{L}}{partial W_{ij}} = sum_{k=1}^Vfrac{partialmathcal{L}}{partial u_k}frac{partial u_k}{partial W_{ij}} . label{eq:dLdW} end{equation} >(3)$$display$$

И это большая часть алгоритма обратного распространения, на данный момент нам просто нужно определить уравнения (2) и (3) для нашего случая.

Начнем с уравнения (2), обратите внимание что вес Wij, относится к матрице W, и соединяет нейрон i в скрытом слое с нейроном j в выходном слое, и соответственно оказывает влияние только на выход uj (соответственно и на yj).

Алгоритм обратного распространения ошибки на примере Word2Vec — IT-МИР. ПОМОЩЬ В IT-МИРЕ 2020
Рисунок 3. (a) Выходной узел $y_j$ зависит от общего узла $h_i$ скрытого слоя только через элемент $W'_{ij}$ матрицы $W'$. (b) Но кроме того, этот же выходной узел зависит от общего входного узла $x_k$ через N элементов $W_{k1}dots W_{kN}$ матрицы W.

Следовательно, среди всех производных $partial u_k/partial W'_{ij}$, только одна, где k=j, будет отличаться от 0.

Уравнение (4):

$$display$$begin{equation} frac{partialmathcal{L}}{partial W’_{ij}} = frac{partialmathcal{L}}{partial u_j}frac{partial u_j} {partial W’_{ij}} label{eq:derivative#1} end{equation} >(4)$$display$$

Давайте посчитаем $partial mathcal{L}/partial u_j$, уравнение (5):

$$display$$begin{equation} frac{partialmathcal{L}}{partial u_j} = -delta_{jj^*} + y_j := e_j label{eq:term#1} end{equation} >(5)$$display$$

, где $delta_{jj^*}$дельта Кронекера, функция двух целых переменных, которая равна 1, если они равны, и 0 в противном случае.

В уравнении (5) мы ввели вектор e размерности N (размер словаря), который мы используем чтобы снизить сложность обозначений, этот вектор представляет собой разницу между полученным и ожидаемым результатом, то есть это вектор ошибок предсказания.

Для второго члена правой части уравнения (4) мы получим уравнения (6):

$$display$$begin{equation} frac{partial u_j}{partial W’_{ij}} = sum_{k=1}^V W_{ik}x_k label{eq:term#2} end{equation} >(6)$$display$$

После подставновки уравнений (5) и (6) в уравнение (4) мы получим уравнение (7):

$$display$$begin{equation} bbox[white,5px,border:2px dotted red]{ frac{partialmathcal{L}}{partial W’_{ij}} = (-delta_{jj^*} + y_j) left(sum_{k=1}^V W_{ki}x_kright) } label{eq:backprop1} end{equation} >(7)$$display$$

Мы можем найти выполнить аналогичное упражнение для поиска производной $partialmathcal{L}/partial W_{ij}$, однако на этот раз отметим что после задания входа Xk, выход yj нейрона j зависит от всех элементов матрицы W соединенных со входом, как видно на рисунке 3(b). Поэтому мы должны оценивать все элементы в сумме. Прежде чем перейти к оценке $partial u_k/partial W_{ij}$, полезно перезаписать выражение для элемента uk из вектора u как:

$$display$$begin{equation*} u_k = sum_{m=1}^Nsum_{l=1}^VW’_{mk}W_{lm}x_l . end{equation*}$$display$$

Из этого уравнения легко вывести $partial u_k/partial W_{ij}$, поскольку единственный выживший член производной будет с индексами l=i и m=j, или же в виде уравнения (8):

$$display$$begin{equation} frac{partial u_k}{partial W_{ij}} = W’_{jk}x_i . label{eq:term#3} end{equation} >(8)$$display$$

Наконец применив уравнения (5) и (8) мы получим результат, уравнение (9):

$$display$$begin{equation} bbox[white,5px,border:2px dotted red]{ frac{partial mathcal{L}}{partial W_{ij}} = sum_{k=1}^V (-delta_{kk^*}+y_k)W’_{jk}x_i } label{eq:backprop2} end{equation} >(9)$$display$$

Векторизация

Мы можем упростить запись уравнений. (7) и (9) используя векторную нотацию. Сделав это мы получим для уравнения (7)

$$display$$begin{equation} bbox[white,5px,border:2px dotted red]{ frac{partialmathcal{L}}{partial W’} = (W^Ttextbf{x}) otimes textbf{e} } end{equation} >(10)$$display$$

Где символ ⊗ обозначает векторное произведение.

Для уравнения (9) мы получим:

$$display$$begin{equation} bbox[white,5px,border:2px dotted red]{ frac{partial mathcal{L}}{partial W} = textbf{x}otimes(W’textbf{e}) } end{equation} >(11)$$display$$

3.3 Градиентный спуск на основе полученных выше результатов

Теперь, когда у нас есть уравнения (7) и (9), у нас есть все необходимое для реализации одной итерации обучения нейросети на базе алгоритма обратного распространения ошибки, применяя градиентный спуск. Каждая итерация должна немного приближать к минимуму функцию потерь. После задания скорости обучения $eta>0$, мы можем обновить наши веса следующим образом:

$begin{eqnarray} W_{textrm{new}} & = W_{textrm{old}} - eta frac{partial mathcal{L}}{partial W} nonumber \ W'_{textrm{new}} & = W'_{textrm{old}} - eta frac{partial mathcal{L}}{partial W'} nonumber \ end{eqnarray}$

3.4 Итерация алгоритма

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

4. Алгоритм обратного распространения для контекста из нескольких слов в модели CBOW

Мы уже знаем как работает алгоритм обратного распространения для модели CBOW с одним словом на входе. Теперь увеличим сложность и добавим в контекст несколько слов. Рисунок (4) показывает как выглядит нейросеть теперь. Вход теперь представляет собой серию OneHot Encoded векторов слов входящих в контекст. Количество слов в контексте является параметром который мы можем задавать при инициализации word2vec. Скрытый слой получает усреднение из всех контекстных слов.

Алгоритм обратного распространения ошибки на примере Word2Vec — IT-МИР. ПОМОЩЬ В IT-МИРЕ 2020
Рисунок 4. Топология модели CBOW с контекстом из нескольких слов

Уравнения модели CBOW с несколькими контекстными словами являются обобщением уравнений модели CBOW с одним контекстным словом.

$begin{eqnarray} textbf{h} = & frac{1}{C} W^T sum_{c=1}^Ctextbf{x}^{(c)} = W^Toverline{textbf{x}}hspace{5.8cm} nonumber \ textbf{u}= & W'^Ttextbf{h}= frac{1}{C}sum_{c=1}^CW'^T W^Ttextbf{x}^{(c)}=W'^T W^Toverline{textbf{x}} hspace{2.8cm} nonumber \ textbf{y}= &   mathbb{S}textrm{oftmax}(textbf{u})= mathbb{S}textrm{oftmax}left( W'^T W^Toverline{textbf{x}}right) hspace{3.6cm} nonumber end{eqnarray}$

Обратите внимание, что для удобства мы ввели определение ‘усредненный’ входной вектор $overline{textbf{x}}=sum_{c=1}^Ctextbf{x}^{(c)}/C$

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

$$display$$begin{equation} mathcal{L} = -logmathbb{P}(w_o|w_{c,1},w_{c,2},dots,w_{c,C})=-u_{j^*} + log sum_i exp{(u_i)}. end{equation} >(12)$$display$$

Снова выпишем уравнения по правилу цепочки, аналогично предыдущим:

$$display$$begin{equation} frac{partialmathcal{L}}{partial W’_{ij}} = sum_{k=1}^Vfrac{partialmathcal{L}}{partial u_k}frac{partial u_k}{partial W’_{ij}} end{equation} >(13)$$display$$

и

$$display$$begin{equation} frac{partialmathcal{L}}{partial W_{ij}} = sum_{k=1}^Vfrac{partialmathcal{L}}{partial u_k}frac{partial u_k}{partial W_{ij}} . end{equation} >(14)$$display$$

Производные функции потерь по весам такие же как для модели CBOW с одним словом на входе, с единственным отличием, что мы заменим входной вектор из одного слова на усредненный вектор из слов контекста. Выведем эти уравнения начиная с производной по $W’_{ij}$

$inline$begin{equation} frac{partialmathcal{L}}{partial W’_{ij}} = sum_{k=1}^Vfrac{partialmathcal{L}}{partial u_k}frac{partial u_k}{partial W’_{ij}} = frac{partialmathcal{L}}{partial u_j}frac{partial u_j}{partial W’_{ij}} = (-delta_{jj^*} + y_j) left(sum_{k=1}^V W_{ki}overline{x}_kright) end{equation} > (15)$inline$

Теперь запишем производную по $W_{ij}$:

$$display$$begin{equation} frac{partialmathcal{L}}{partial W_{ij}} = sum_{k=1}^Vfrac{partialmathcal{L}}{partial u_k}frac{partial}{partial W_{ij}}left(frac{1}{C}sum_{m=1}^Nsum_{l=1}^V W’_{mk}sum_{c=1}^C W_{lm}x_l^{(c)}right)=frac{1}{C}sum_{k=1}^Vsum_{c=1}^C(-delta_{kk^*} + y_k)W’_{jk}x_i^{(c)} . end{equation} >(16)$$display$$

Подведя итог имеем следующее:

$$display$$begin{equation} bbox[white,5px,border:2px dotted red]{ frac{partialmathcal{L}}{partial W’_{ij}} = (-delta_{jj^*} + y_j) left(sum_{k=1}^V W_{ki}overline{x}_kright) } label{eq:backprop1_multi} end{equation} >(17)$$display$$

и

$$display$$begin{equation} bbox[white,5px,border:2px dotted red]{ frac{partialmathcal{L}}{partial W_{ij}} = sum_{k=1}^V(-delta_{kk^*} + y_k)W’_{jk}overline{x}_i . } label{eq:backprop2_multi} end{equation} > (18)$$display$$

Векторизация

Перепишем уравнения (17) и (18) в записи для векторов.
Уравнение (17) примет вид:

$$display$$begin{equation} bbox[white,5px,border:2px dotted red]{ frac{partialmathcal{L}}{partial W’} = (W^Toverline{textbf{x}}) otimes textbf{e} } end{equation} >(19)$$display$$

Для уравнения (18):

$$display$$begin{equation} bbox[white,5px,border:2px dotted red]{ frac{partial mathcal{L}}{partial W} =overline{textbf{x}}otimes(W’textbf{e}) } end{equation} >(20)$$display$$

Еще раз, обратите внимание что уравнения идентичны уравнениям в модели CBOW для контекста из одного слова.
Оператор ⊗ обозначает векторное произведение.

5. Алгоритм обратного распространения для модели Skip-gram

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

Алгоритм обратного распространения ошибки на примере Word2Vec — IT-МИР. ПОМОЩЬ В IT-МИРЕ 2020
Рисунок 5.Топология Skip-gram модели.

Уравнения skip-gram модели следующие:

$begin{eqnarray*} textbf{h} = & W^Ttextbf{x} hspace{9.4cm} \ textbf{u}_c= & W'^Ttextbf{h}=W'^TW^Ttextbf{x} hspace{4cm} c=1, dots, C hspace{0.7cm}\ textbf{y}_c = &   mathbb{S}textrm{oftmax}(textbf{u})= mathbb{S}textrm{oftmax}(W'^TW^Ttextbf{x}) hspace{2cm} c=1, dots, C end{eqnarray*}$

Обратите внимание что выходные вектора (как и вектор $textbf{u}_c$) идентичны, $mathbf{y}_1=mathbf{y}_2dots= mathbf{y}_C$. Функция потерь выглядит следующим образом:

$begin{eqnarray*} mathcal{L} = -log mathbb{P}(w_{c,1}, w_{c,2}, dots, w_{c,C}|w_o)=-log prod_{c=1}^C mathbb{P}(w_{c,i}|w_o) \ = -log prod_{c=1}^C frac{exp(u_{c,j^*})}{sum_{j=1}^V exp(u_{c,j})} =-sum_{c=1}^C u_{c,j^*} + sum_{c=1}^C log sum_{j=1}^V exp(u_{c,j}) end{eqnarray*}$

Для модели skip-gram функция потерь зависит от $Ctimes V$ переменных
через:

$$display$$begin{equation*} mathcal{L} = mathcal{L}(mathbf{u_1}(W,W’), mathbf{u_2}(W,W’), dots, mathbf{u_C}(W,W’))\=mathcal{L}(u_{1,1}(W,W’), u_{1,2}(W,W’), dots, u_{C,V}(W,W’)) end{equation*}$$display$$

Соответственно правило цепочки выглядит так:

$$display$$begin{equation*} frac{partialmathcal{L}}{partial W’_{ij}} = sum_{k=1}^Vsum_{c=1}^Cfrac{partialmathcal{L}}{partial u_{c,k}}frac{partial u_{c,k}}{partial W’_{ij}} end{equation*}$$display$$

и

$$display$$begin{equation*} frac{partialmathcal{L}}{partial W_{ij}} = sum_{k=1}^Vsum_{c=1}^Cfrac{partialmathcal{L}}{partial u_{c,k}}frac{partial u_{c,k}}{partial W_{ij}} . end{equation*}$$display$$

Посчитаем $partial mathcal{L}/partial u_{c,j}$, получим:

$$display$$begin{equation*} frac{partialmathcal{L}}{partial u_{c,j}} = -delta_{jj_c^*} + y_{c,j} := e_{c,j} end{equation*}$$display$$

Аналогично модели CBOW получаем:

$$display$$begin{equation*} frac{partialmathcal{L}}{partial W’_{ij}} = sum_{k=1}^Vsum_{c=1}^Cfrac{partialmathcal{L}}{partial u_{c,k}}frac{partial u_{c,k}}{partial W’_{ij}} = sum_{c=1}^Cfrac{partialmathcal{L}}{partial u_{c,j}}frac{partial u_{c,j}}{partial W’_{ij}} = sum_{c=1}^C(-delta_{jj_c^*} + y_{c,j}) left(sum_{k=1}^V W_{ki}x_kright) end{equation*}$$display$$

Производная по $W_{ij}$ самая сложная, но выполнимая:

$$display$$begin{equation*} frac{partialmathcal{L}}{partial W_{ij}} = sum_{k=1}^Vsum_{c=1}^Cfrac{partialmathcal{L}}{partial u_{c,k}}frac{partial}{partial W_{ij}}left(sum_{m=1}^Nsum_{l=1}^V W’_{mk} W_{lm}x_lright)=sum_{k=1}^Vsum_{c=1}^C (-delta_{kk_c^*} + y_{c,k})W’_{jk}x_i . end{equation*}$$display$$

Подведя итог, для модели skip-gram мы имеем:

$$display$$begin{equation} bbox[white,5px,border:2px dotted red]{ frac{partialmathcal{L}}{partial W’_{ij}} = sum_{c=1}^C(-delta_{jj_c^*} + y_{c,j}) left(sum_{k=1}^V W_{ki}x_kright) } label{eq:backprop1_skip} end{equation} >(21)$$display$$

и

$$display$$begin{equation} bbox[white,5px,border:2px dotted red]{ frac{partialmathcal{L}}{partial W_{ij}} = sum_{k=1}^Vsum_{c=1}^C (-delta_{kk_c^*} + y_{c,k})W’_{jk}x_i . } label{eq:backprop2_skip} end{equation} >(22)$$display$$

Векторизация

Векторизованная версия уравнения (21):

$$display$$begin{equation} bbox[white,5px,border:2px dotted red]{ frac{partialmathcal{L}}{partial W’} = (W^Ttextbf{x}) otimes sum_{c=1}^Ctextbf{e}_c } end{equation} >(23)$$display$$

Уравнение (22):

$$display$$begin{equation} bbox[white,5px,border:2px dotted red]{ frac{partial mathcal{L}}{partial W} = textbf{x}otimesleft(W’sum_{c=1}^Ctextbf{e}_cright) } end{equation} >(24)$$display$$

6. Что дальше

Мы подробно рассмотрели как работает алгоритм обратного распространения в случае word2vec. Однако рассмотренная реализация неэффективна для больших текстовых корпусов. В оригинальной статье [2] применяются некоторые трюки для преодоления этих проблем (иерархический softmax, negative sampling), однако я не буду на них останавливаться подробно. Вы можете найти подробное описание по ссылке [1].

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

Следующим шагом будет реализация этих уравнений на вашем любимом языке программирования. Если вам нравится Python, я уже реализовал эти уравнения в моем следующем посте.

Надеюсь увидеть вас там!

Дополнительные ссылки

[1] X. Rong, word2vec Parameter Learning Explained, arXiv:1411.2738 (2014).
[2] T. Mikolov, K. Chen, G. Corrado, J. Dean, Efficient Estimation of Word Representations in Vector Space, arXiv:1301.3781 (2013).

Специально для сайта ITWORLD.UZ. Новость взята с сайта Хабр