TensorFlow 2 GRU Layer с несколькими скрытыми слоями - PullRequest
0 голосов
/ 05 апреля 2020

Я пытаюсь перенести некоторый код TensorFlow 1 в TensorFlow 2. Старый код использовал устаревшую MultiRNNCell для создания слоя GRU с несколькими скрытыми слоями. В TensorFlow 2 я хочу использовать встроенный GRU Layer , но, похоже, не существует опции, которая допускает несколько скрытых слоев с этим классом. У PyTorch-эквивалента есть такая опция, как параметр инициализации, num_layers.

Мой обходной путь - использовать слой RNN TensorFlow и передавать ячейку GRU для каждого скрытого слоя, который я хочу - этот способ рекомендуется в документах:

dim = 1024
num_layers = 4
cells = [tf.keras.layers.GRUCell(dim) for _ in range(num_layers)]
gru_layer = tf.keras.layers.RNN(
  cells,
  return_sequences=True,
  stateful=True
)

Но встроенный слой GRU имеет поддержку CuDNN, которого, как кажется, нет в обычном RNN, чтобы процитировать документы :

Математически RNN (LSTMCell (10)) дает тот же результат, что и LSTM (10). Фактически реализация этого уровня в TF v1.x просто создавала соответствующую ячейку RNN и помещала ее в уровень RNN. Однако использование встроенных слоев GRU и LSTM позволяет использовать CuDNN, и вы можете увидеть лучшую производительность.

Итак, как мне этого добиться? Как получить слой GRU, который поддерживает несколько скрытых слоев и поддерживает CuDNN? Учитывая, что во встроенном слое GRU в TensorFlow такой опции нет, действительно ли это необходимо? Или единственный способ получить глубокую сеть GRU - это сложить несколько слоев GRU в последовательности?

РЕДАКТИРОВАТЬ: Кажется, согласно этот ответ на Аналогичный вопрос, что на самом деле не существует встроенного способа создания слоя GRU с несколькими скрытыми слоями и что их нужно укладывать вручную.

1 Ответ

0 голосов
/ 10 апреля 2020

ОК, так что, кажется, единственный способ достичь этого - определить стек экземпляров GRU Layer. Вот что я придумал (обратите внимание, что мне нужны только слои GRU с сохранением состояния, которые возвращают последовательности, и не нужно состояние возврата последнего слоя):

class RNN(tf.keras.layers.Layer):

    def __init__(self, dim, num_layers=1):
        super(RNN, self).__init__()
        self.dim = dim
        self.num_layers = num_layers
        def layer():
            return tf.keras.layers.GRU(
                self.dim,
                return_sequences=True,
                return_state=True,
                stateful=True)
        self._layer_names = ['layer_' + str(i) for i in range(self.num_layers)]
        for name in self._layer_names:
             self.__setattr__(name, layer())

    def call(self, inputs):
        seqs = inputs
        state = None
        for name in self._layer_names:
            rnn = self.__getattribute__(name)
            (seqs, state) = rnn(seqs, state)
        return seqs

Необходимо вручную добавить внутренние слои rnn на родительский слой, используя __setattr__. Кажется, что добавление rnns в список и установка , что в качестве атрибута слоя, не позволит отслеживать внутренние слои родительским слоем (см. this ответ this проблема).

Я надеялся, что это ускорит мою сеть. Тесты на Colab пока не показали никакой разницы, во всяком случае, на самом деле это немного медленнее , чем при использовании прямой RNN, инициализированной списком ячеек GRU. Я думал, что увеличение размера пакета с 10 до 64 может иметь значение, но нет, похоже, что они по-прежнему работают примерно с той же скоростью.

ОБНОВЛЕНИЕ : На самом деле там действительно ли кажется заметным ускорением, но только если я не украшаю свою функцию шага тренировки с tf.function (у меня есть специальное обучение l oop, я не использую Model.fit). Небольшое увеличение скорости - возможно, примерно на 33% быстрее при размере партии 96. Гораздо меньший размер партии (от 10 до 20) дает еще большую скорость, около 70%.

...