Хотели бы вы ускорить выполнение задач языкового моделирования (LM) на 1000% почти без снижения точности? Недавняя статья Исследовательской группы ИИ Facebook (FAIR), написанная Grave et al. (2017) , названный Эффективное приближение softmax для графических процессоров , показывает, как можно добиться значительного ускорения в одном из наиболее трудоемких аспектов языкового моделирования - этапе softmax, требующем больших вычислений, с помощью их адаптивного softmax . Гигантское ускорение от использования адаптивного softmax связано с минимальными затратами на точность, поэтому любой, кто занимается языковым моделированием, обязательно должен рассмотреть возможность его использования. Здесь, в Части 1 этого сообщения в блоге, я полностью объясню адаптивный softmax, а затем в Части 2 я шаг за шагом проведу вас через реализацию Pytorch (с прилагаемым Блокнотом Jupyter), который использует встроенный Pytorch. -в функции AdaptiveLogSoftmaxWithLoss.

Как вы, наверное, знаете, языковое моделирование - одна из самых основных и важных задач в обработке естественного языка (НЛП). Он включает в себя создание модели, которая может предсказывать следующее слово в последовательности, когда вводится последовательность слов (это то, что обеспечивает функцию предсказания текста на клавиатуре вашего телефона). Для этого последний вычислительный шаг в языковой модели включает выбор слова из огромного списка слов в словаре языковой модели. Поскольку словарь обычно содержит от 10 до 100 тысяч слов, этот последний шаг требует значительных вычислений и времени.

Так как же языковая модель выбирает одно слово из словаря в качестве предсказания? После этапов предварительной обработки токенизации / числовой обработки типичным входом в вашу модель может быть тензор размера [seq_length, bs], а целью также будет тензор размера [seq_length, bs ] (на самом деле это просто вход, сдвинутый на одну временную точку в будущее), где bs - количество примеров в данном мини-пакете, а seq_length длина любого данного примера. После доступа к вложениям (данные теперь будут иметь размер [seq_length, bs, emb_sz]), обычно данные проходят через несколько уровней рекуррентной нейронной сети (RNN) с использованием модулей LSTM или GRU для окончательный вывод скрытого состояния размером [seq_length, bs, nh], где nh - размерность вашего последнего слоя. Цель состоит в том, чтобы вычислить p (w | h)… то есть вероятность слова w с учетом окончательного скрытого состояния h (здесь h - окончательный вывод скрытого состояния для данного примера и временной точки, вектор длины nh).

Решение softmax для получения прогноза на основе этого вывода со скрытым состоянием включает в себя сначала преобразование вывода с помощью полностью подключенного линейного слоя с количеством функций вывода, равным размеру словаря (так, начиная с size [seq_length, bs, nh] до размера [seq_length, bs, vs], где vs - размер вашего словаря). Затем формула softmax exp (x [i]) / sum (exp (x)) применяется ко всем словам в словаре, производя значение вероятности (от 0 до 1) для каждого слова. Наконец, вы обычно используете потерю отрицательного логарифма правдоподобия (NLL) для сравнения этих прогнозов с целевым.

Этот шаг преобразования вывода из размера [seq_length, bs, nh] в размер [seq_length, bs, vs] является огромным! Вычислительные затраты на этом последнем шаге линейны с размером вашего словарного запаса, который, как упоминалось ранее, будет довольно большим, если вам нужна достойная языковая модель. Для типичных значений nh и vs матрица весов для этого преобразования может иметь размер [1000 x 200 000], который применяется к каждому временному шагу каждого примера в вашем наборе данных. . Этот шаг преобладает в вычислениях, как во время обучения, так и во время тестирования, и отнимает много времени для любой языковой модели.

Адаптивный softmax от Grave et al. - довольно элегантное решение этой проблемы. Чтобы понять это, вам сначала нужно понять иерархический softmax, который является предыдущей попыткой ускорить softmax (например, Morin & Bengio, 2005). Вы начинаете с присвоения каждому слову w уникального кластера C (w). Затем иерархический softmax разбивает softmax на два этапа: сначала вы прогнозируете кластер C, а затем выполняете softmax по всем словам только для этого кластера. Вы можете факторизовать условную вероятность слова w с учетом окончательного скрытого состояния h как p (w | h) = p(w | C (w), h ) * p(C (w) | h)… т.е. вероятность слова w с учетом скрытого состояния h, - это вероятность кластера C (w) с учетом h, умноженная на вероятность слова с учетом обоих кластеров C (w) и h. Проще говоря, сначала просто предскажите кластер, а затем предскажите, какое слово в кластере. Это должно сократить время вычислений с линейной зависимости от размера словаря до порядка квадратного корня из размера словаря.

Однако, как объясняется в статье Grave et al., Иерархический softmax на самом деле не так хорошо работает на GPU (объяснение этого выходит за рамки этого сообщения в блоге). Их адаптивный softmax - это простой вариант иерархического softmax, адаптированный для графических процессоров. Он использует закон Ципфа ... наблюдение, что в любом корпусе большая часть вероятностной массы распределения слов покрывается лишь небольшой частью словарного запаса. Например, в наборе данных Penn Treebank 87% слов в документе покрываются только 20% словарного запаса. Адаптивный softmax использует эту информацию, распределяя слова в словаре в кластеры в зависимости от того, насколько они распространены.

Чтобы понять этот подход, лучше всего начать с простейшей формы адаптивного softmax, в которой словарь разделен всего на два кластера, Vᴴᴱᴬᴰ и Vᵀᴬᴵᴸ. Меньшее количество наиболее часто встречающихся слов в вашем корпусе должно входить в Vᴴᴱᴬᴰ, а Vᵀᴬᴵᴸ должен содержать оставшиеся нечастые слова. Таким образом, в вашем корпусе гораздо более вероятно, что слово будет происходить из Vᴴᴱᴬᴰ, чем из Vᵀᴬᴵᴸ (то есть p (Vᴴᴱᴬᴰ) ›p (Vᵀᴬᴵᴸ)), но в Vᵀᴬᴵᴸ должно быть намного больше слов, чем в Vᴴᴱᴬᴰ.

Первым шагом в этом примере с двумя кластерами является вычисление softmax для всех слов в заголовке плюс 1 дополнительное «слово», которое соответствует выбору хвостового кластера. То есть сначала выполните softmax для Vᴴᴱᴬᴰ + 1 слов, где дополнительное «слово» - это вероятность того, что оно принадлежит хвостовому кластеру. Для слов в Vᴴᴱᴬᴰ, p (w | h) просто pᴴᴱᴬᴰ (w | h)… то есть просто вычислите вероятность слова в Vᴴᴱᴬᴰ с учетом h . Если выбрано «слово», соответствующее категории хвоста, только тогда вам нужно сделать дополнительный softmax (по всем словам в Vᵀᴬᴵᴸ). Для этих слов p (w | h) равно pᴴᴱᴬᴰ (tail | h) * pᵀᴬᴵᴸ (w | h)… то есть если pᴴᴱᴬᴰ (w | h) указывает 'хвост -cluster ', затем возьмем вероятность' хвостового кластера 'в голове, умноженную на вероятность выбранного слова в хвосте. Для того, чтобы просто использовать этот двухкластерный подход, Grave et al. наблюдайте 5-кратное ускорение по сравнению с полным softmax!

Тем не менее, вы можете получить еще больший выигрыш, используя больше кластеров ... кажется, что лучше всего 2–5 кластеров. Подход такой же, как и в примере с двумя кластерами, за исключением того, что первый softmax будет поверх всех слов в заголовке, плюс несколько дополнительных «слов» для представления хвостовых кластеров. Например, для реализации с 5 кластерами начальный softmax будет более Vᴴᴱᴬᴰ + 4 слов. Если одно из «слов» хвостового кластера было выбрано в начальном softmax, то дополнительный softmax будет вычисляться только для слов в выбранном хвосте. Используя тот же принцип, что и выше, наиболее часто встречающиеся слова назначаются наименьшим кластерам ... Голова и первые хвосты будут содержать более частые слова, но имеют меньший словарный запас, а последние хвосты будут содержать менее частые слова и больший словарный запас.

Есть еще одна проблема, которая увеличивает скорость с минимальными затратами на точность, а именно то, что каждому кластеру предоставляется разная емкость. Выше nh - это мера емкости окончательного вывода со скрытым состоянием. Вам нужна высокая способность правильно предсказывать наиболее часто встречающиеся слова. Однако редкие слова по определению встречаются всего несколько раз и поэтому не могут быть хорошо выучены вашей моделью ... Было бы расточительно использовать большую емкость для этих слов. Таким образом, Grave et al. уменьшить размерность конечного входа скрытого состояния для softmax хвостовых кластеров, применяя матрицу проекции, которая уменьшает размерность для каждого дополнительного хвостового кластера в 4 раза (это станет яснее, когда мы рассмотрим пример реализации в Часть 2 этого сообщения в блоге).

Вот и все! Вы можете поэкспериментировать с количеством кластеров и распределением слов между кластерами, чтобы выяснить, какие слова лучше всего подходят для вашего набора данных, используя принципы, изложенные выше (или используя динамическое программирование). В целом, адаптивный softmax обеспечивает значительное ускорение (до 10 раз!) По сравнению с полным softmax с минимальными затратами на точность. [Посмотрите мою запись в блоге на CNN, которая также может ускорить обучение НЛП]. Адаптивный softmax будет полезен в любое время, когда вам нужно выбрать из огромного количества возможных категорий, некоторые из которых более распространены, чем другие. Это верно не только для языкового моделирования, но и для многих других задач НЛП (нейронный машинный перевод (NMT), преобразование речи в текст и т. Д.) И, вероятно, для многих неязыковых приложений. Прочтите Часть 2 этого сообщения в блоге, чтобы увидеть пошаговую реализацию Pytorch адаптивного softmax, который использует встроенную функцию AdaptiveLogSoftmaxWithLoss Pytorch.

Использованная литература:

Могила, Эдуард и др. «Эффективное приближение softmax для графических процессоров. Препринт arXiv arXiv: 1609.04309v3 (2017). »

Морин, Фредерик и Йошуа Бенжио. «Иерархическая вероятностная языковая модель нейронной сети. Аистатс. Vol. 5. 2005. »