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