Масштабируемый, эффективный иерархический Softmax в Tensorflow?

Я заинтересован в реализации иерархической модели softmax, которая может обрабатывать большие словари, скажем, порядка 10 миллионов классов. Каков наилучший способ сделать это, чтобы быть масштабируемым до большого количества классов и эффективным? Например, по крайней мере одна статья показала, что HS может достичь примерно 25-кратного ускорения для больших словарей при использовании Двухуровневое дерево, в котором каждый узел sqrt(N) классов. Меня также интересует более общая версия для произвольного дерева глубины с произвольным коэффициентом ветвления.

Здесь я вижу несколько вариантов:

1) Запускаем tf.gather для каждой партии, где мы собираем индексы и расщепления. Это создает проблемы с большими размерами пакетов и толстыми деревьями, где теперь коэффициенты часто дублируются, что приводит к ошибкам OOM.

2) Подобно #1, мы могли бы использовать tf.embedding_lookup, который сохранил бы помощь с ошибками OOM, но теперь держит все на ЦП и немного замедляет работу.

3) Используйте tf.map_fn с parallel_iterations=1 для обработки каждой выборки отдельно и вернитесь к сбору. Это гораздо более масштабируемо, но на самом деле не приближается к 25-кратному ускорению из-за сериализации.

Есть ли лучший способ реализовать HS? Существуют ли разные способы для глубоких и узких деревьев по сравнению с короткими и широкими деревьями?


person Wesley Tansey    schedule 23.05.2017    source источник
comment
Они различаются в зависимости от задачи. Языковые модели имеют большие пакеты около 400 со скрытыми размерами около 300; другие задачи могут иметь меньшие размеры пакетов и большие скрытые размеры, например, классификация imagenet. Видеопамять и оперативная память довольно велики по сравнению с проблемой (хотя ОЗУ графического процессора - нет).   -  person Wesley Tansey    schedule 02.06.2017
comment
Могу я взглянуть на вашу реализацию HS в Tensorflow? Мне он сейчас тоже нужен.   -  person Viet Phan    schedule 01.12.2017
comment
Это немного запутанно, но посмотрите здесь: github.com/tansey /sdp/blob/ - оглядываясь назад, я бы предложил использовать pytorch или другую структуру динамического графа.   -  person Wesley Tansey    schedule 02.12.2017


Ответы (1)


Вы упомянули, что вам нужна производительность класса GPU:

но теперь держит все на процессоре и немного замедляет работу

и хотите использовать 300-элементный скрытый размер и словари на 10 миллионов слов.

Это означает, что (при условии float32) вам потребуется 4 * 300 * 10M * 2 байта = 24 ГБ только для хранения параметров и градиента для выходного слоя.

Иерархический Softmax (HSM) не снижает требования к памяти — он просто ускоряет обучение.

На самом деле вам понадобится намного больше памяти графического процессора, потому что вам также нужно будет хранить:

  • другие параметры и их градиенты

  • данные оптимизатора, например, скорости при тренировке импульса

  • активации и обратное распространение временных данных

  • специфичные для фреймворка накладные расходы

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

Однако теперь у вас есть другая проблема:

Для конкретики предположим, что у вас есть двухуровневый HSM с 3 тыс. классов, по 3 тыс. слов на класс (всего 9 млн слов). Вы распределяете классы 3K по 8 графическим процессорам, чтобы каждый из них содержал 384 класса.

Что, если все целевые слова в пакете относятся к одним и тем же 384 классам, т. е. они относятся к одному и тому же графическому процессору? Один GPU будет выполнять всю работу, а остальные 7 ждут ее.

Проблема в том, что даже если целевые слова в пакете принадлежат разным графическим процессорам, вы все равно будете иметь ту же производительность, что и в худшем случае, если вы хотите выполнить это вычисление в TensorFlow (это потому, что TensorFlow — это « "укажи и запусти" - вычислительный граф одинаков для лучшего и худшего случая)

Каков наилучший способ сделать это, чтобы быть масштабируемым до большого количества классов и эффективным?

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

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

  1. Если вы не моделируете последовательности, или моделируете, но есть только один выход на всю последовательность, то накладные расходы памяти от копирования параметров, на которые вы ссылались, ничтожны по сравнению с описанными выше требованиями к памяти:

    400 == размер партии ‹‹ количество классов == 3K

    В этом случае вы можете просто использовать gather или embedding_lookup (хотя копирование неэффективно)

  2. Однако, если вы моделируете последовательности длиной, скажем, 100, с выводом на каждом временном шаге, то копирование параметров становится большой проблемой.

    В этом случае, я думаю, вам нужно перейти к C++/CUDA C и реализовать весь этот слой и его градиент как пользовательскую операцию.

person bobcat    schedule 05.06.2017
comment
Итак, вы говорите, что единственный эффективный способ реализовать это — использовать стандартный embedding_lookup, который я предложил в № 2? Это кажется разумным, но мне интересно, в какой степени вы действительно увидите остановку графического процессора, которую вы описываете, в реальных наборах данных, что я и ищу. Кроме того, выборка softmax сравнивается с документом, на который я ссылаюсь, и тщательно сравнивалась с рядом других документов. - person Wesley Tansey; 06.06.2017
comment
Кроме того, что, если бы можно было обрабатывать все на одном графическом процессоре? Скажем, в будущем у меня будет, например, графический процессор на 32 ГБ. - person Wesley Tansey; 06.06.2017
comment
@WesleyTansey сравнивает выборку softmax с - теперь я это вижу. Смотрите это и другие обновления. - person bobcat; 06.06.2017
comment
Спасибо. Так что это похоже на то, что он просто соглашается со мной. Я ищу здесь некоторые точные цифры, показывающие, что есть (или нет) лучший способ сделать это, чем gather. Как предотвратить копирование через CUDA? Каков будет прирост производительности на самом деле? - person Wesley Tansey; 06.06.2017
comment
@WesleyTansey Так что, похоже, это просто согласие со мной. Я указал, что ваша проблема с памятью начинается еще до того, как вы тренируете сеть (это не было в вашем вопросе). Я также указал на внутреннюю неэффективность попыток сделать это на нескольких графических процессорах в TF (то же самое). Хотя я знаю C++ и CUDA C, и для меня очевидно, что это можно сделать, реализовать это для вас слишком много работы, извините. - person bobcat; 06.06.2017