Що робить tf.nn.embedding_lookup функція?


158
tf.nn.embedding_lookup(params, ids, partition_strategy='mod', name=None)

Я не можу зрозуміти обов'язок цієї функції. Це як таблиця пошуку? Що означає повернути параметри, що відповідають кожному id (у id)?

Наприклад, skip-gramякщо ми використовуємо модель tf.nn.embedding_lookup(embeddings, train_inputs), то для кожної train_inputвона знаходить відповідне вбудовування?


"Це як таблиця пошуку?" tldr - Так. Для кожного x (id) дайте мені асоційований y (парами).
Девід

Відповіді:


147

embedding_lookupфункція витягує рядки paramsтензора. Поведінка схожа на використання індексації з масивами в нумері. Напр

matrix = np.random.random([1024, 64])  # 64-dimensional embeddings
ids = np.array([0, 5, 17, 33])
print matrix[ids]  # prints a matrix of shape [4, 64] 

paramsАргументом може бути також перелік тензорів, і в цьому випадку idsрозподіляється між тензорами. Наприклад, якщо список 3 тензорів [2, 64], поведінка за умовчанням є те , що вони будуть представляти ids: [0, 3], [1, 4], [2, 5].

partition_strategyконтролює спосіб idsрозподілу списку серед списку. Розбиття є корисним для проблем з більшими масштабами, коли матриця може бути занадто великою, щоб зберігати її в одному шматку.


21
Чому б вони називали це так, а ні select_rows?
Ленар Хойт

12
@LenarHoyt, оскільки ця ідея пошуку походить від Word Embeddings. а "рядки" - це зображення (вбудовування) слів у векторний простір - і вони корисні в самій собі. Часто більше, ніж фактична мережа.
Ліндон Уайт

2
Як tensorflow вивчає структуру вбудовування? Чи керує ця функція і цим процесом?
вгоклани

19
@vgoklani, ні, embedding_lookupпросто забезпечує зручний (і паралельний) спосіб отримання вбудовок, відповідних id в ids. paramsТензор, як правило , змінна тс , що дізналися як частина процесу навчання - змінна тс , чиї компоненти використовуються, прямо або побічно, в функції втрат (наприклад , як tf.l2_loss) , який оптимізований з допомогою оптимізатора (наприклад , як tf.train.AdamOptimizer).
Shobhit

5
@ Rafał Józefowicz Чому "поведінка за замовчуванням полягає в тому, що вони представлятимуть ідентифікатори: [0, 3], [1, 4], [2, 5]." Чи можете ви пояснити?
Ерін

219

Так, цю функцію важко зрозуміти, поки не отримаєте бал.

У своєму найпростішому вигляді він схожий на tf.gather. Він повертає елементи у paramsвідповідності з індексами, заданимиids .

Наприклад (якщо ви знаходитесь всередині tf.InteractiveSession())

params = tf.constant([10,20,30,40])
ids = tf.constant([0,1,2,3])
print tf.nn.embedding_lookup(params,ids).eval()

повернеться [10 20 30 40], тому що перший елемент (індекс 0) парам є 10, другий елемент парам (індекс 1) є20 і т.д.

Аналогічно

params = tf.constant([10,20,30,40])
ids = tf.constant([1,1,3])
print tf.nn.embedding_lookup(params,ids).eval()

повернеться [20 20 40] .

Але embedding_lookupце більше того. paramsАргумент може бути список тензорів, а не один тензор.

params1 = tf.constant([1,2])
params2 = tf.constant([10,20])
ids = tf.constant([2,0,2,1,2,3])
result = tf.nn.embedding_lookup([params1, params2], ids)

У такому випадку індекси, зазначені в ids, відповідають елементам тензорів відповідно до стратегії розподілу , де стратегія розділення за замовчуванням - «mod».

У стратегії 'mod' індекс 0 відповідає першому елементу першого тензору у списку. Індекс 1 відповідає першому елементу другого тензора. Індекс 2 відповідає першому елементу третього тензору тощо. Просто індекс iвідповідає першому елементу (i + 1) -го тензора для всіх індексів 0..(n-1), якщо припустимо, що парами є спискомn тензорів.

Тепер індекс nне може відповідати тензору n + 1, оскільки список paramsмістить лише nтензори. Отже індекс nвідповідає другому елементу першого тензора. Аналогічно індексуйтеn+1 відповідає другий елемент другого тензора тощо.

Отже, в коді

params1 = tf.constant([1,2])
params2 = tf.constant([10,20])
ids = tf.constant([2,0,2,1,2,3])
result = tf.nn.embedding_lookup([params1, params2], ids)

індекс 0 відповідає першому елементу першого тензора: 1

індекс 1 відповідає першому елементу другого тензору: 10

індекс 2 відповідає другому елементу першого тензора: 2

індекс 3 відповідає другому елементу другого тензору: 20

Таким чином, результатом було б:

[ 2  1  2 10  2 20]

8
Примітка: ви можете використовувати partition_strategy='div', і ви отримаєте [10, 1, 10, 2, 10, 20], тобто id=1є другим елементом першого парамуму. В основному: partition_strategy=mod(за замовчуванням) id%len(params): індекс парамуму в парамах id//len(params): індекс елемента у вищезгаданій partition_strategy=*div*
парамі

3
@ asher-stern ви могли б пояснити, чому стратегія "mod" є типовою? видається, що стратегія "div" більше схожа на стандартну тензорну нарізку (select-рядки за заданими індексами). Чи є якісь проблеми з продуктивністю у випадку "div"?
svetlov.vsevolod

46

Так, мета tf.nn.embedding_lookup()функції - виконати пошук у вбудовувальній матриці та повернути вбудовування (або простіше кажучи, векторне подання) слів.

Проста матриця вбудовування (форма vocabulary_size x embedding_dimension:) виглядатиме нижче. (тобто кожне слово буде представлене вектором чисел; звідси назва word2vec )


Вкладення матриці

the 0.418 0.24968 -0.41242 0.1217 0.34527 -0.044457 -0.49688 -0.17862
like 0.36808 0.20834 -0.22319 0.046283 0.20098 0.27515 -0.77127 -0.76804
between 0.7503 0.71623 -0.27033 0.20059 -0.17008 0.68568 -0.061672 -0.054638
did 0.042523 -0.21172 0.044739 -0.19248 0.26224 0.0043991 -0.88195 0.55184
just 0.17698 0.065221 0.28548 -0.4243 0.7499 -0.14892 -0.66786 0.11788
national -1.1105 0.94945 -0.17078 0.93037 -0.2477 -0.70633 -0.8649 -0.56118
day 0.11626 0.53897 -0.39514 -0.26027 0.57706 -0.79198 -0.88374 0.30119
country -0.13531 0.15485 -0.07309 0.034013 -0.054457 -0.20541 -0.60086 -0.22407
under 0.13721 -0.295 -0.05916 -0.59235 0.02301 0.21884 -0.34254 -0.70213
such 0.61012 0.33512 -0.53499 0.36139 -0.39866 0.70627 -0.18699 -0.77246
second -0.29809 0.28069 0.087102 0.54455 0.70003 0.44778 -0.72565 0.62309 

Я розділив вищевказану матрицю вбудовування та завантажив лише слова, в vocabяких буде наш словниковий запас та відповідні вектори в embмасиві.

vocab = ['the','like','between','did','just','national','day','country','under','such','second']

emb = np.array([[0.418, 0.24968, -0.41242, 0.1217, 0.34527, -0.044457, -0.49688, -0.17862],
   [0.36808, 0.20834, -0.22319, 0.046283, 0.20098, 0.27515, -0.77127, -0.76804],
   [0.7503, 0.71623, -0.27033, 0.20059, -0.17008, 0.68568, -0.061672, -0.054638],
   [0.042523, -0.21172, 0.044739, -0.19248, 0.26224, 0.0043991, -0.88195, 0.55184],
   [0.17698, 0.065221, 0.28548, -0.4243, 0.7499, -0.14892, -0.66786, 0.11788],
   [-1.1105, 0.94945, -0.17078, 0.93037, -0.2477, -0.70633, -0.8649, -0.56118],
   [0.11626, 0.53897, -0.39514, -0.26027, 0.57706, -0.79198, -0.88374, 0.30119],
   [-0.13531, 0.15485, -0.07309, 0.034013, -0.054457, -0.20541, -0.60086, -0.22407],
   [ 0.13721, -0.295, -0.05916, -0.59235, 0.02301, 0.21884, -0.34254, -0.70213],
   [ 0.61012, 0.33512, -0.53499, 0.36139, -0.39866, 0.70627, -0.18699, -0.77246 ],
   [ -0.29809, 0.28069, 0.087102, 0.54455, 0.70003, 0.44778, -0.72565, 0.62309 ]])


emb.shape
# (11, 8)

Вбудований пошук у TensorFlow

Тепер ми побачимо, як можна виконати пошук вбудовування для якогось довільного вхідного речення.

In [54]: from collections import OrderedDict

# embedding as TF tensor (for now constant; could be tf.Variable() during training)
In [55]: tf_embedding = tf.constant(emb, dtype=tf.float32)

# input for which we need the embedding
In [56]: input_str = "like the country"

# build index based on our `vocabulary`
In [57]: word_to_idx = OrderedDict({w:vocab.index(w) for w in input_str.split() if w in vocab})

# lookup in embedding matrix & return the vectors for the input words
In [58]: tf.nn.embedding_lookup(tf_embedding, list(word_to_idx.values())).eval()
Out[58]: 
array([[ 0.36807999,  0.20834   , -0.22318999,  0.046283  ,  0.20097999,
         0.27515   , -0.77126998, -0.76804   ],
       [ 0.41800001,  0.24968   , -0.41242   ,  0.1217    ,  0.34527001,
        -0.044457  , -0.49687999, -0.17862   ],
       [-0.13530999,  0.15485001, -0.07309   ,  0.034013  , -0.054457  ,
        -0.20541   , -0.60086   , -0.22407   ]], dtype=float32)

Поспостерігайте, як ми отримали вбудовування з нашої оригінальної матриці вбудовування (зі словами), використовуючи індекси слів у нашому словнику.

Зазвичай такий пошук вбудовування виконується першим шаром (званим шаром вбудовування ), який потім передає ці вкладення шарам RNN / LSTM / GRU для подальшої обробки.


Бічна примітка : Зазвичай словниковий запас також матиме спеціальний unkзнак. Отже, якщо маркер з нашого вхідного речення відсутній у нашому словнику, то індекс, що відповідає, unkбуде шукати в матриці вбудовування.


PS Зауважте, що embedding_dimensionце гіперпараметр, який потрібно налаштувати для їх застосування, але популярні моделі, такі як Word2Vec та GloVe, використовують 300розмірний вектор для представлення кожного слова.

Бонусна модель читання word2vec, пропущена грам модель


17

Ось зображення із зображенням процесу вбудовування.

Зображення: вбудовування процесу пошуку

Точно, він отримує відповідні рядки вкладеного шару, визначені списком ідентифікаторів, і надають це як тензор. Він досягається за допомогою наступного процесу.

  1. Визначте заповнювач lookup_ids = tf.placeholder([10])
  2. Визначте шар вбудовування embeddings = tf.Variable([100,10],...)
  3. Визначте операцію тензоорф embed_lookup = tf.embedding_lookup(embeddings, lookup_ids)
  4. Отримайте результати, запустивши lookup = session.run(embed_lookup, feed_dict={lookup_ids:[95,4,14]})

6

Коли тензор парам у великих розмірах, ідентифікатор відноситься лише до верхнього виміру. Можливо, для більшості людей це очевидно, але мені потрібно запустити наступний код, щоб зрозуміти це:

embeddings = tf.constant([[[1,1],[2,2],[3,3],[4,4]],[[11,11],[12,12],[13,13],[14,14]],
                          [[21,21],[22,22],[23,23],[24,24]]])
ids=tf.constant([0,2,1])
embed = tf.nn.embedding_lookup(embeddings, ids, partition_strategy='div')

with tf.Session() as session:
    result = session.run(embed)
    print (result)

Пробуючи стратегію "дів" і для одного тензора, це не має ніякого значення.

Ось вихід:

[[[ 1  1]
  [ 2  2]
  [ 3  3]
  [ 4  4]]

 [[21 21]
  [22 22]
  [23 23]
  [24 24]]

 [[11 11]
  [12 12]
  [13 13]
  [14 14]]]

3

Ще один спосіб поглянути на це: припустимо, що ви розгладжуєте тензори до одновимірного масиву, а потім здійснюєте пошук

(наприклад) Tensor0 = [1,2,3], Tensor1 = [4,5,6], Tensor2 = [7,8,9]

Згладжений тензор буде таким [1,4,7,2,5,8,3,6,9]

Тепер, коли ви зробите пошук [0,3,4,1,7], він вийде [1,2,5,4,6]

(i, e) якщо, наприклад, значення пошуку становить 7, і у нас є 3 тензори (або тензор з 3 рядками),

7/3: (Нагадування - 1, Коефіцієнт - 2) Отже, буде показаний 2-й елемент Tensor1, який становить 6


2

Оскільки мене також заінтригувала ця функція, я віддам свої два центи.

Те, як я бачу це у двовимірному випадку, - це як матричне множення (його легко узагальнити до інших вимірів).

Розглянемо словник з N символами. Потім ви можете зобразити символ x як вектор розмірів Nx1, кодируемый одним гарячим.

Але ви хочете подати цей символ не як вектор Nx1, а як такий, який має розміри Mx1, званий y .

Отже, для перетворення x в y можна використовувати і вбудовування матриці E з розмірами MxN:

y = E x .

Це, по суті, те, що робить tf.nn.embedding_lookup (парами, ідентифікатори, ...), з нюансом, що ідентифікатори - це лише одне число, яке представляє позицію 1 в однокольоровому кодованому векторі x .


0

Додаючи до відповіді Ашера Стерна, paramsтрактується як розділення великого тензору вбудовування. Це може бути одиночний тензор, що представляє повний тензор вбудовування, або список тензорів X, що мають однакову форму, за винятком першого виміру, що представляє собою осколкові тензори вбудовування.

Функція tf.nn.embedding_lookupпишеться враховуючи той факт, що вбудовування (парами) буде великим. Тому нам потрібно partition_strategy.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.