ฟังก์ชัน tf.nn.embedding_lookup ทำอะไร


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

ฉันไม่เข้าใจหน้าที่ของฟังก์ชั่นนี้ มันเหมือนโต๊ะค้นหาหรือไม่? ซึ่งหมายถึงการส่งคืนพารามิเตอร์ที่สอดคล้องกับแต่ละ id (ในรหัส)?

ตัวอย่างเช่นในskip-gramโมเดลถ้าเราใช้tf.nn.embedding_lookup(embeddings, train_inputs)แล้วแต่ละอันtrain_inputจะพบการฝังที่สอดคล้องกันหรือไม่


"มันเหมือนกับโต๊ะค้นหาใช่ไหม" tldr - ใช่ สำหรับแต่ละ x (รหัส) ให้ฉันเชื่อมโยง y (params)
David Refaeli

คำตอบ:


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?
Lenar Hoyt

12
@ LenarHoyt เพราะแนวคิดการค้นหานี้มาจาก Word Embeddings และ "แถว" คือการนำเสนอของ (embeddings) ของคำในพื้นที่เวกเตอร์ - และมีประโยชน์ในตัวของพวกเขาเอง บ่อยกว่าเครือข่ายจริง
Lyndon White

2
tensorflow เรียนรู้โครงสร้างการฝังอย่างไร ฟังก์ชั่นนี้จัดการกระบวนการนั้นด้วยหรือไม่
vgoklani

19
@vgoklani ไม่มีembedding_lookupก็ให้ความสะดวกสบาย (และขนาน) วิธีที่จะดึง embeddings idsสอดคล้องกับรหัสใน paramsเมตริกซ์มักจะเป็นตัวแปร TF ที่จะเรียนรู้เป็นส่วนหนึ่งของกระบวนการฝึกอบรม - ตัวแปร TF ที่มีส่วนประกอบที่ใช้โดยตรงหรือโดยอ้อมในฟังก์ชั่นการสูญเสีย (เช่นtf.l2_loss) ซึ่งจะเพิ่มประสิทธิภาพโดยการเพิ่มประสิทธิภาพ (เช่นtf.train.AdamOptimizer)
Shobhit

5
@ RafałJózefowiczทำไม "พฤติกรรมเริ่มต้นคือพวกเขาจะเป็นตัวแทนรหัส: [0, 3], [1, 4], [2, 5]."? คุณช่วยอธิบายได้ไหม
Aerin

219

ใช่ฟังก์ชั่นนี้ยากที่จะเข้าใจจนกว่าคุณจะได้รับจุด

tf.gatherในรูปแบบที่ง่ายของมันก็จะคล้ายกับ ก็จะส่งกลับองค์ประกอบของตามดัชนีที่กำหนดโดยparamsids

ตัวอย่าง (สมมติว่าคุณอยู่ข้างใน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) th เทนเซอร์สำหรับดัชนีทั้งหมด0..(n-1)สมมติว่า params เป็นรายการของ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*ในลักษณะอื่น ๆ
Mario Alemi

3
@ asher-stern คุณสามารถอธิบายได้ว่าทำไมกลยุทธ์ "mod" จึงเป็นค่าเริ่มต้น ดูเหมือนว่ากลยุทธ์ "div" คล้ายกับการแบ่งส่วนเทนเซอร์มาตรฐาน (เลือกแถวตามดัชนีที่กำหนด) มีปัญหาเรื่องประสิทธิภาพในกรณีของ "div" หรือไม่?
svetlov.vsevolod

46

ใช่จุดประสงค์ของtf.nn.embedding_lookup()ฟังก์ชั่นคือทำการค้นหาในเมทริกซ์การฝังและส่งคืน embeddings (หรือในแง่ง่าย ๆ คือการแสดงเวกเตอร์) ของคำ

เมทริกซ์การฝังแบบง่าย (ของรูปร่าง:) 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)

สังเกตว่าเราได้งานแต่งงานจากเมทริกซ์การฝังแบบดั้งเดิมของเรา (ด้วยคำ) โดยใช้ดัชนีของคำในคำศัพท์ของเราอย่างไร

โดยปกติแล้วการค้นหาฝังดังกล่าวจะดำเนินการโดยเลเยอร์แรก (เรียกว่าเลเยอร์ฝัง ) ซึ่งส่งผ่าน embeddings เหล่านี้ไปยังเลเยอร์ RNN / LSTM / GRU สำหรับการประมวลผลเพิ่มเติม


ด้านหมายเหตุ : โดยปกติแล้วคำศัพท์จะมีunkโทเค็นพิเศษด้วย ดังนั้นหากโทเค็นจากประโยคอินพุตของเราไม่ปรากฏในคำศัพท์ของเราดัชนีที่เกี่ยวข้องunkจะถูกค้นหาในเมทริกซ์การฝัง


PS Note นั้นembedding_dimensionเป็นพารามิเตอร์ที่มีการปรับแต่งสำหรับแอพพลิเคชั่นของพวกเขา แต่รุ่นยอดนิยมเช่นWord2VecและGloVeใช้300เวกเตอร์ขนาดสำหรับแสดงแต่ละคำ

การอ่านโบนัส word2vec แบบข้ามแกรม


17

นี่คือภาพที่แสดงกระบวนการฝังการค้นหา

ภาพ: กระบวนการค้นหาแบบฝัง

โดยสรุปจะได้รับแถวที่สอดคล้องกันของเลเยอร์การฝังที่ระบุโดยรายการ ID และระบุว่าเป็นเมตริกซ์ มันสามารถทำได้ผ่านกระบวนการต่อไปนี้

  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)

เพียงแค่ลองใช้กลยุทธ์ 'div' และสำหรับเทนเซอร์เดียวก็ไม่ได้สร้างความแตกต่าง

นี่คือผลลัพธ์:

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

เนื่องจากฉันรู้สึกทึ่งกับฟังก์ชั่นนี้ฉันจะให้สองเซ็นต์ของฉัน

วิธีที่ฉันเห็นในกรณี 2D นั้นเหมือนกับการคูณเมทริกซ์ (มันง่ายที่จะพูดถึงมิติอื่น ๆ )

พิจารณาคำศัพท์ที่มีสัญลักษณ์ N จากนั้นคุณสามารถแทนสัญลักษณ์xเป็นเวกเตอร์ของขนาด Nx1 ซึ่งมีการเข้ารหัสแบบร้อน

แต่คุณต้องการเป็นตัวแทนของสัญลักษณ์นี้ไม่เป็นเวกเตอร์ของ Nx1 แต่เป็นหนึ่งที่มีขนาด MX1 เรียกว่าY

ดังนั้นในการแปลงxเป็นyคุณสามารถใช้และฝังเมทริกซ์Eด้วยขนาด MxN:

Y = E x

นี้เป็นหลักสิ่ง tf.nn.embedding_lookup (params, รหัส, ... ) จะทำด้วยความแตกต่างกันนิดหน่อยที่รหัสเป็นเพียงจำนวนหนึ่งที่แสดงให้เห็นถึงตำแหน่งของ 1 ในหนึ่งร้อนเข้ารหัสเวกเตอร์x


0

การเพิ่มคำตอบของแอชเชอร์สเติร์น paramsถูกตีความเป็นการแบ่งพาร์ติชันของเทนเซอร์ฝังขนาดใหญ่ มันอาจเป็นเทนเซอร์เดียวที่แทนเมตริกซ์การฝังที่สมบูรณ์หรือรายการของเทนเซอร์ X ที่มีรูปร่างเดียวกันทั้งหมดยกเว้นมิติแรกซึ่งแทนเทนเซอร์ฝังที่ฝังอยู่

ฟังก์ชันtf.nn.embedding_lookupนี้เขียนขึ้นโดยพิจารณาจากความจริงที่ว่าการฝัง (params) จะมีขนาดใหญ่ partition_strategyดังนั้นเราจึงจำเป็นต้อง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.