Ответ 1
Вы упомянули, что хотите производительность GPU-класса:
но теперь держит все на процессоре и немного замедляет работу
и хотите использовать скрытый размер 300 единиц и словари на 10 М слов.
Это означает, что (при условии float32
) вам понадобится 4 * 300 * 10M * 2 байта = 24 ГБ, чтобы сохранить параметры и градиент для выходного слоя.
Иерархический Softmax (HSM) не уменьшает требования к памяти - он просто ускоряет обучение.
Реально, вам понадобится намного больше памяти GPU, потому что вам также нужно будет хранить:
-
другие параметры и их градиенты
-
данные оптимизатора, например. скорости в обучении импульсам
-
активация и backpropagated временных данных
-
служебные данные, связанные с инфраструктурой
Поэтому, если вы хотите сделать все вычисления на графических процессорах, у вас не будет выбора, кроме как распределить этот слой на нескольких графических процессорах с высокой памятью.
Однако у вас теперь есть другая проблема:
Чтобы сделать это конкретным, предположим, что у вас есть двухуровневый HSM с 3K классами, с 3K словами на класс (всего 9M слов). Вы распределяете классы 3K по 8 графическим процессорам, так что каждый хост 384 класса.
Что делать, если все целевые слова в пакете из одних и тех же классов 384, т.е. принадлежат одному и тому же GPU? Один GPU будет выполнять всю работу, в то время как остальные 7 ждут его.
Проблема в том, что даже если целевые слова в пакете принадлежат к различным графическим процессорам, вы все равно будете иметь такую же производительность, как в худшем случае, если вы хотите сделать это вычисление в TensorFlow (это потому, что TensorFlow представляет собой структуру "указать-и-запустить" - вычислительный граф является одинаковым для наилучшего случая и наихудшего случая)
Каков наилучший способ сделать это для того, чтобы быть масштабируемым до большого класса и эффективным?
Вышеприведенная неэффективность модели parallelism (каждый GPU должен обрабатывать всю партию) предполагает, что нужно стараться держать все в одном месте.
Предположим, что вы либо реализуете все на хосте, либо на одном огромном графическом процессоре.
-
Если вы не моделируете последовательности или если вы есть, но для всей последовательности есть только один вывод, тогда накладные расходы памяти от копирования параметров, на которые вы ссылались, ничтожно по сравнению с требованиями к памяти описанных выше:
400 == размер партии < количество классов == 3K
В этом случае вы можете просто использовать
gather
илиembedding_lookup
(хотя копирование неэффективно) -
Однако, если вы моделируете последовательности длины, скажем, 100, с выходом на каждом временном шаге, то копирование параметров становится большой проблемой.
В этом случае, я думаю, вам нужно будет спуститься до С++/CUDA C и реализовать весь этот слой и его градиент как пользовательский op.