В чем разница между встраиваемым слоем и плотным слоем?
Документы для встраиваемого слоя в Keras говорят:
Превращает натуральные числа (индексы) в плотные векторы фиксированного размера. например.
[[4], [20]]
->[[0.25, 0.1], [0.6, -0.2]]
Я считаю, что это также может быть достигнуто путем кодирования входных данных в виде горячих векторов длины vocabulary_size
и подавать их в плотный слой.
Является ли Embedded Layer просто удобством для этого двухэтапного процесса, или что-то более необычное происходит под капотом?
3 ответа
Математически разница заключается в следующем:
Слой встраивания выполняет операцию выбора. В керасе этот слой эквивалентен:
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
Сама операция быстрее, чем матричный точечный продукт, как при прямом, так и при обратном проходе.
Слой внедрения выполняется быстрее, потому что он, по сути, эквивалентен плотному слою, который делает упрощающие предположения.
Представьте себе слой для встраивания слов с такими весами:
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 net необходимо преобразовать это предложение в 1-горячую кодировку
[[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).
- Без предвзятости.
- Без активации.
Однако ни одно из этих ограничений не имеет значения, если вы просто хотите преобразовать слово в целочисленной кодировке во вложение.
Здесь я хочу улучшить проголосовавший ответ, предоставив более подробную информацию:
Когда мы используем слой встраивания, обычно это делается для того, чтобы уменьшить горячие входные векторы (разреженные) до более плотных представлений.
Слой встраивания очень похож на просмотр таблицы. Когда стол маленький, он быстрый.
Когда таблица большая, поиск в таблице выполняется намного медленнее. На практике мы будем использовать плотный слой в качестве средства уменьшения размера, чтобы уменьшить одноразовый ввод вместо встраивания слоя в этом случае.