tf.nn.embedding_lookup(params, ids, partition_strategy='mod', name=None)
私はこの機能の義務を理解できません。それはルックアップテーブルのようなものですか?それぞれのIDに対応するパラメータを(IDで)返すことを意味しますか?
例えば、skip-gram
モデルでtf.nn.embedding_lookup(embeddings, train_inputs)
を使うと、それぞれのtrain_input
に対して対応する埋め込みが見つかりますか?
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
をリストにどのように分配するかを制御します。分割は、行列が大きすぎて1つにまとめることができない場合に、大規模問題に役立ちます。
はい、あなたがポイントを得るまで、この関数は理解するのが難しいです。
その最も単純な形式では、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()
paramsの最初の要素(index 0)は[10 20 30 40]
で、paramsの2番目の要素(index 1)は10
であるため、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
引数は、単一のテンソルではなく、listのテンソルにすることができます。
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
で指定されたインデックスは、partition strategyに従ってテンソルの要素に対応します。ここで、デフォルトのpartition strategyは 'mod'です。
'mod'ストラテジーでは、インデックス0はリストの最初のテンソルの最初の要素に対応します。インデックス1は、2番目テンソルの1番目要素に対応します。インデックス2は、番目テンソルの1番目要素に対応します。 paramsがi
テンソルのリストであると仮定すると、すべてのインデックス0..(n-1)
に対して、インデックスn
は(i + 1)番目のテンソルの最初の要素に対応します。
リストn
に含まれるのはparams
テンソルのみであるため、インデックスn
はテンソルn + 1に対応できません。そのため、インデックスn
は最初のテンソルの2番目要素に対応します。同様に、インデックスn+1
は、2番目のテンソルの2番目の要素に対応します。
だから、コードで
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は、2番目のテンソルの最初の要素に対応します。10
添字2は、最初のテンソルの2番目の要素に対応します。2
添字3は2番目のテンソルの2番目の要素に対応します。
したがって、結果は次のようになります。
[ 2 1 2 10 2 20]
はい、 tf.nn.embedding_lookup()
関数の目的は、lookupinを実行することです。埋め込み行列そして単語の埋め込み(または簡単に言えばベクトル表現)を返します。
単純な埋め込み行列(形状:vocabulary_size x embedding_dimension
)は次のようになります。 (つまり、Wordはそれぞれvectorの数字で表されます。したがって、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
にwordsのみをロードしました。これがボキャブラリーとなり、対応するベクトルが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にルックアップを埋め込む
それでは、任意の入力文に対してembedding lookupを実行する方法を見ていきましょう。
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がどのように得られたかを観察します。
通常、そのような埋め込みルックアップは最初のレイヤ(Embedding layerと呼ばれる)によって実行され、次にこれらの埋め込みをさらなる処理のためにRNN/LSTM/GRUレイヤに渡します。
サイドノート:通常、語彙は特別なunk
トークンも持ちます。したがって、入力文のトークンが語彙に含まれていない場合は、unk
に対応するインデックスが埋め込み行列で検索されます。
PSembedding_dimension
はアプリケーションに合わせて調整する必要があるハイパーパラメーターですが、のような一般的なモデルです。 )Word2VecとGloVeは300
次元ベクトルを使います各単語を表すために。
ボーナス読書Word2vecスキップグラムモデル
これは、ルックアップを埋め込むプロセスを表す画像です。
簡潔に言うと、IDのリストで指定された埋め込み層の対応する行を取得し、それをテンソルとして提供します。それは以下のプロセスを通して達成される。
lookup_ids = tf.placeholder([10])
embeddings = tf.Variable([100,10],...)
embed_lookup = tf.embedding_lookup(embeddings, lookup_ids)
lookup = session.run(embed_lookup, feed_dict={lookup_ids:[95,4,14]})
を実行して結果を取得しますParamsテンソルが高次元の場合、IDは最上位次元のみを参照します。たぶんそれは大部分の人々にとって明白である、しかし私はそれを理解するために次のコードを実行しなければならない:
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 1]
[ 2 2]
[ 3 3]
[ 4 4]]
[[21 21]
[22 22]
[23 23]
[24 24]]
[[11 11]
[12 12]
[13 13]
[14 14]]]
もう1つの見方は、テンソルを1次元配列に平坦化してからルックアップを実行していると仮定します。
(例)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]になります。
たとえば、ルックアップ値が7で、3つのテンソル(または3つの行を持つテンソル)がある場合、(i、e)
7/3:(リマインダーは1、商は2)したがってTensor1の2番目の要素が表示されます。これは6です。
私もこの機能に興味をそそられたので、私は2セントを差し上げます。
私が2Dの場合にそれを見る方法はまさに行列の乗算のようなものです(他の次元に一般化するのは簡単です)。
N個の記号を有する語彙を考える。そうすると、シンボルxを次元N×1のベクトルとして表現でき、ワンホットエンコードされます。
しかし、このシンボルをN×1のベクトルとしてではなく、M×1の次元を持ち、yという表現で表現する必要があります。
したがって、xをyに変換するには、埋め込み行列E、次元M×Nで:
y = Ex =。
これは本質的にtf.nn.embedding_lookup(params、ids、...)がしていることです。idsは1の位置を表す1つの数値に過ぎません。ワンホットエンコードされたベクトルx。
Asher Sternの答えに加えて、params
は大きな埋め込みテンソルのpartitioningとして解釈されます。これは完全な埋め込みテンソルを表す単一のテンソル、または最初の次元を除いてすべて同じ形状のXテンソルのリストで、鋭い埋め込みテンソルを表します。
関数tf.nn.embedding_lookup
はembedding(params)が大きくなるという事実を考慮して書かれています。そのためpartition_strategy
が必要です。