Co robi funkcja tf.nn.embedding_lookup?


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

Nie rozumiem obowiązku tej funkcji. Czy to jest jak tabela przeglądowa? Co oznacza zwrócenie parametrów odpowiadających każdemu identyfikatorowi (w identyfikatorach)?

Na przykład w skip-grammodelu, jeśli używamy tf.nn.embedding_lookup(embeddings, train_inputs), to dla każdego train_inputznajduje odpowiednie osadzenie?


„Czy to jest jak tabela przeglądowa?” tldr - Tak. Dla każdego x (ids) podaj skojarzony y (params).
David Refaeli

Odpowiedzi:


147

embedding_lookupfunkcja pobiera wiersze paramstensora. Zachowanie jest podobne do indeksowania za pomocą tablic w numpy. Na przykład

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] 

paramsArgumentem może być również lista tensorów, w którym to przypadku idszostanie rozłożony na tensory. Na przykład, biorąc pod uwagę wykaz 3 tensorów [2, 64], domyślnym zachowaniem jest to, że będą reprezentować ids: [0, 3], [1, 4], [2, 5].

partition_strategykontroluje sposób idsdystrybucji na liście. Podział jest przydatny w przypadku problemów o większej skali, gdy macierz może być zbyt duża, aby zachować ją w jednym kawałku.


21
Dlaczego mieliby to tak nazywać, a nie select_rows?
Lenar Hoyt

12
@LenarHoyt, ponieważ pomysł wyszukiwania pochodzi z osadzania w programie Word. a „rzędy” to reprezentacje (osadzenia) słów w przestrzeni wektorowej - i są użyteczne w sobie. Często bardziej niż rzeczywista sieć.
Lyndon White,

2
W jaki sposób tensorflow poznaje strukturę osadzania? Czy ta funkcja również zarządza tym procesem?
vgoklani

19
@vgoklani, nie, embedding_lookuppo prostu zapewnia wygodny (i równoległy) sposób pobierania osadzeń odpowiadających identyfikatorowi w ids. paramsNapinacz jest zwykle zmienna TF, które uczy się jako część procesu szkolenia - zmienna TF, których składniki są stosowane, bezpośrednio lub pośrednio, w funkcji strat (na przykład tf.l2_loss), który jest optymalizowany przez optymalizator (takiego jak tf.train.AdamOptimizer).
Shobhit

5
@ Rafał Józefowicz Dlaczego „domyślnym zachowaniem jest to, że będą reprezentować identyfikatory: [0, 3], [1, 4], [2, 5].”? Czy możesz wytłumaczyć?
Aerin

219

Tak, ta funkcja jest trudna do zrozumienia, dopóki nie zrozumiesz, o co chodzi.

W swojej najprostszej formie jest podobny do tf.gather. Zwraca elementy paramszgodnie z indeksami określonymi przez ids.

Na przykład (zakładając, że jesteś w środku tf.InteractiveSession())

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

zwróci [10 20 30 40], ponieważ pierwszym elementem (indeks 0) params jest 10, drugim elementem params (indeks 1) jest 20itd.

Podobnie,

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

wróci [20 20 40].

Ale embedding_lookupto coś więcej. paramsArgument może być lista tensorów, raczej niż pojedynczy tensor.

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)

W takim przypadku indeksy określone w sekcji idsodpowiadają elementom tensorów zgodnie ze strategią partycji , gdzie domyślną strategią podziału jest „mod”.

W strategii „mod” indeks 0 odpowiada pierwszemu elementowi pierwszego tensora na liście. Indeks 1 odpowiada pierwszemu elementowi drugiego tensora. Indeks 2 odpowiada pierwszemu elementowi trzeciego tensora i tak dalej. Po prostu indeks iodpowiada pierwszemu elementowi tensora (i + 1), dla wszystkich indeksów 0..(n-1), przy założeniu, że params to lista ntensorów.

Teraz indeks nnie może odpowiadać tensorowi n + 1, ponieważ lista paramszawiera tylko ntensory. Tak więc indeks nodpowiada drugiemu elementowi pierwszego tensora. Podobnie indeks n+1odpowiada drugiemu elementowi drugiego tensora itp.

A więc w kodzie

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)

indeks 0 odpowiada pierwszemu elementowi pierwszego tensora: 1

indeks 1 odpowiada pierwszemu elementowi drugiego tensora: 10

indeks 2 odpowiada drugiemu elementowi pierwszego tensora: 2

indeks 3 odpowiada drugiemu elementowi drugiego tensora: 20

Zatem wynik byłby:

[ 2  1  2 10  2 20]

8
uwaga: możesz użyć partition_strategy='div'i dostać [10, 1, 10, 2, 10, 20], czyli id=1jest to drugi element pierwszego parametru. Zasadniczo: partition_strategy=mod(domyślnie) id%len(params): indeks parametru w params id//len(params): indeks elementu w powyższym parametrze partition_strategy=*div*na odwrót
Mario Alemi

3
@ asher-stern czy mógłbyś wyjaśnić, dlaczego strategia "mod" jest domyślna? wydaje się, że strategia „div” jest bardziej podobna do standardowego wycinania tensorów (wybierz wiersze według danych wskaźników). Czy są jakieś problemy z wydajnością w przypadku „div”?
svetlov.vsevolod

46

Tak, celem tf.nn.embedding_lookup()funkcji jest wykonanie wyszukiwania w matrycy osadzania i zwrócenie osadzeń (lub w uproszczeniu reprezentacji wektorowej) słów.

Prosta matryca osadzania (shape vocabulary_size x embedding_dimension:) wyglądałaby jak poniżej. (tj. każde słowo będzie reprezentowane przez wektor liczb; stąd nazwa word2vec )


Matryca osadzania

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 

Podzieliłem powyższą macierz osadzania i załadowałem tylko słowa, w vocabktórych będzie nasze słownictwo i odpowiadające im wektory w embtablicy.

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)

Osadzanie wyszukiwania w TensorFlow

Teraz zobaczymy, jak możemy przeprowadzić wyszukiwanie osadzające dla dowolnego dowolnego zdania wejściowego.

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)

Obserwować, jak dostaliśmy zanurzeń z naszej oryginalnej matrycy osadzania (ze słowami) z wykorzystaniem indeksów słów w naszym słowniku.

Zazwyczaj takie wyszukiwanie osadzania jest wykonywane przez pierwszą warstwę (nazywaną warstwą osadzania ), która następnie przekazuje te osadzenia do warstw RNN / LSTM / GRU w celu dalszego przetwarzania.


Uwaga boczna : Zazwyczaj słownictwo będzie miało również specjalny unktoken. Tak więc, jeśli token z naszego zdania wejściowego nie jest obecny w naszym słowniku, wówczas indeks odpowiadający unkzostanie wyszukany w matrycy osadzania.


PS Zauważ, że embedding_dimensionjest to hiperparametr, który trzeba dostroić do ich aplikacji, ale popularne modele, takie jak Word2Vec i GloVe, używają 300wektora wymiaru do reprezentowania każdego słowa.

Bonusowe czytanie modelu pomijania gramów word2vec


17

Oto obraz przedstawiający proces wyszukiwania osadzania.

Obraz: osadzanie procesu wyszukiwania

Dokładniej, pobiera odpowiednie wiersze warstwy osadzania, określone przez listę identyfikatorów i dostarcza je jako tensor. Osiąga się to poprzez następujący proces.

  1. Zdefiniuj symbol zastępczy lookup_ids = tf.placeholder([10])
  2. Zdefiniuj osadzaną warstwę embeddings = tf.Variable([100,10],...)
  3. Zdefiniuj operację tensorflow embed_lookup = tf.embedding_lookup(embeddings, lookup_ids)
  4. Uzyskaj wyniki, biegając lookup = session.run(embed_lookup, feed_dict={lookup_ids:[95,4,14]})

6

Gdy tensor parametrów ma duże wymiary, identyfikatory odnoszą się tylko do wymiaru górnego. Może jest to oczywiste dla większości ludzi, ale muszę uruchomić następujący kod, aby to zrozumieć:

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)

Samo wypróbowanie strategii „div” i dla jednego tensora nie ma znaczenia.

Oto wynik:

[[[ 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

Innym sposobem spojrzenia na to jest założenie, że spłaszczasz tensory do jednowymiarowej tablicy, a następnie przeprowadzasz wyszukiwanie

(np.) Tensor0 = [1,2,3], Tensor1 = [4,5,6], Tensor2 = [7,8,9]

Spłaszczony tensor będzie następujący [1,4,7,2,5,8,3,6,9]

Teraz, gdy wyszukasz [0,3,4,1,7], będzie to [1,2,5,4,6]

(i, e) jeśli wartość wyszukiwania wynosi na przykład 7 i mamy 3 tensory (lub tensor z 3 wierszami), to

7/3: (Przypomnienie to 1, iloraz to 2) Więc pokazany zostanie drugi element Tensor1, czyli 6


2

Ponieważ również mnie zaintrygowała ta funkcja, dam dwa grosze.

Sposób, w jaki widzę to w przypadku 2D, to po prostu mnożenie macierzy (łatwo jest uogólnić na inne wymiary).

Rozważ słownictwo z N symbolami. Następnie możesz przedstawić symbol x jako wektor o wymiarach Nx1, zakodowany na gorąco.

Ale chcesz przedstawić ten symbol nie jako wektor Nx1, ale jako jeden o wymiarach Mx1, zwany y .

Tak więc, aby przekształcić x w y , możesz użyć i osadzić macierz E o wymiarach MxN:

y = E x .

To właśnie robi tf.nn.embedding_lookup (params, ids, ...) z niuansem, że ids to tylko jedna liczba, która reprezentuje pozycję 1 w zakodowanym na gorąco wektorze x .


0

Dodanie do odpowiedzi Ashera Sterna paramsjest interpretowane jako podział dużego tensora osadzającego. Może to być pojedynczy tensor reprezentujący cały tensor osadzania lub lista tensorów X o tym samym kształcie z wyjątkiem pierwszego wymiaru, reprezentujących tensory osadzania fragmentarycznego.

Funkcja tf.nn.embedding_lookupzostała napisana z uwzględnieniem faktu, że osadzanie (parametry) będzie duże. Dlatego potrzebujemy partition_strategy.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.