В чем разница между слоем вложения и плотным слоем?

Документы для слоя вложения в Keras говорят:

Превращает положительные целые числа (индексы) в плотные векторы фиксированного размера. например. [[4], [20]][[0.25, 0.1], [0.6, -0.2]]

Я считаю, что это также может быть достигнуто путем кодирования входных данных в виде одноразовых векторов длины vocabulary_size и подачи их в плотный слой.

Является ли встраиваемый слой просто удобством для этого двухэтапного процесса или что-то странное происходит под капотом?

Ответ 1

Математически разница заключается в следующем:

  • Уровень внедрения выполняет операцию выбора. В keras этот слой эквивалентен:

    K.gather(self.embeddings, inputs)      # just one matrix
    
  • Плотный слой выполняет операцию точечного произведения плюс дополнительную активацию:

    outputs = matmul(inputs, self.kernel)  # a kernel matrix
    outputs = bias_add(outputs, self.bias) # a bias vector
    return self.activation(outputs)        # an activation function
    

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

Ответ 2

Встраиваемый слой быстрее, потому что он по сути эквивалент плотного слоя, который делает упрощающие предположения.

Представьте слой со словом для встраивания с такими весами:

w = [[0.1, 0.2, 0.3, 0.4],
     [0.5, 0.6, 0.7, 0.8],
     [0.9, 0.0, 0.1, 0.2]]

Слой Dense будет обрабатывать их как фактические веса, с которыми нужно выполнять матричное умножение. Слой встраивания будет просто обрабатывать эти веса как список векторов, каждый из которых представляет одно слово; 0-е слово в словаре - w[0], 1-е - w[1] и т.д.


Например, используйте приведенные выше веса и это предложение:

[0, 2, 1, 2]

Наивной сети Dense -based необходимо преобразовать это предложение в горячую кодировку

[[1, 0, 0],
 [0, 0, 1],
 [0, 1, 0],
 [0, 0, 1]]

затем умножьте матрицу

[[1 * 0.1 + 0 * 0.5 + 0 * 0.9, 1 * 0.2 + 0 * 0.6 + 0 * 0.0, 1 * 0.3 + 0 * 0.7 + 0 * 0.1, 1 * 0.4 + 0 * 0.8 + 0 * 0.2],
 [0 * 0.1 + 0 * 0.5 + 1 * 0.9, 0 * 0.2 + 0 * 0.6 + 1 * 0.0, 0 * 0.3 + 0 * 0.7 + 1 * 0.1, 0 * 0.4 + 0 * 0.8 + 1 * 0.2],
 [0 * 0.1 + 1 * 0.5 + 0 * 0.9, 0 * 0.2 + 1 * 0.6 + 0 * 0.0, 0 * 0.3 + 1 * 0.7 + 0 * 0.1, 0 * 0.4 + 1 * 0.8 + 0 * 0.2],
 [0 * 0.1 + 0 * 0.5 + 1 * 0.9, 0 * 0.2 + 0 * 0.6 + 1 * 0.0, 0 * 0.3 + 0 * 0.7 + 1 * 0.1, 0 * 0.4 + 0 * 0.8 + 1 * 0.2]]

=

[[0.1, 0.2, 0.3, 0.4],
 [0.9, 0.0, 0.1, 0.2],
 [0.5, 0.6, 0.7, 0.8],
 [0.9, 0.0, 0.1, 0.2]]

Тем не менее, слой Embedding просто смотрит на [0, 2, 1, 2] и берет веса слоя с индексами ноль, два, один и два, чтобы немедленно получить

[w[0],
 w[2],
 w[1],
 w[2]]

=

[[0.1, 0.2, 0.3, 0.4],
 [0.9, 0.0, 0.1, 0.2],
 [0.5, 0.6, 0.7, 0.8],
 [0.9, 0.0, 0.1, 0.2]]

Так что это тот же результат, только что полученный, надеюсь, быстрее.


Уровень Embedding имеет ограничения:

  • Входные данные должны быть целыми числами в [0, vocab_length).
  • Нет предвзятости.
  • Нет активации.

Однако ни одно из этих ограничений не должно иметь значения, если вы просто хотите преобразовать закодированное в целое число слово во вложение.