keras 理解词嵌入层

keras understanding Word Embedding Layer

page 我得到以下代码:

from numpy import array
from keras.preprocessing.text import one_hot
from keras.preprocessing.sequence import pad_sequences
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Flatten
from keras.layers.embeddings import Embedding
# define documents
docs = ['Well done!',
        'Good work',
        'Great effort',
        'nice work',
        'Excellent!',
        'Weak',
        'Poor effort!',
        'not good',
        'poor work',
        'Could have done better.']
# define class labels
labels = array([1,1,1,1,1,0,0,0,0,0])
# integer encode the documents
vocab_size = 50
encoded_docs = [one_hot(d, vocab_size) for d in docs]
print(encoded_docs)
# pad documents to a max length of 4 words
max_length = 4
padded_docs = pad_sequences(encoded_docs, maxlen=max_length, padding='post')
print(padded_docs)
# define the model
model = Sequential()
model.add(Embedding(vocab_size, 8, input_length=max_length))
model.add(Flatten())
model.add(Dense(1, activation='sigmoid'))
# compile the model
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['acc'])
# summarize the model
print(model.summary())
# fit the model
model.fit(padded_docs, labels, epochs=50, verbose=0)
# evaluate the model
loss, accuracy = model.evaluate(padded_docs, labels, verbose=0)
print('Accuracy: %f' % (accuracy*100))
  1. 我查看了 encoded_docs 并注意到词 donework 的 one_hot 编码都是 2,为什么?是因为 unicity of word to index mapping non-guaranteed. 按照这个 page?
  2. 我通过命令 embeddings = model.layers[0].get_weights()[0] 获得了 embeddings。在这种情况下,为什么我们得到 embedding 大小为 50 的对象?即使两个词具有相同的 one_hot 个数,它们是否具有不同的嵌入?
  3. 我怎么能理解哪个嵌入是哪个词,即 done vs work
  4. 我还在 处找到了下面的代码,可以帮助找到每个单词的嵌入。但是我不知道如何创建 word_to_index

    word_to_index 是从单词到索引的映射(即字典),例如love: 69 words_embeddings = {w:embeddings[idx] for w, idx in word_to_index.items()}

  5. 请确保我对para #的理解是正确的。

第一层有 400 个参数,因为总字数是 50,嵌入有 8 个维度,所以 50*8=400。

最后一层有 33 个参数,因为每个句子最多有 4 个单词。所以 4*8 是因为嵌入的维度,1 是因为偏差。共 33 个

_________________________________________________________________
Layer (type)                 Output Shape              Param#   
=================================================================
embedding_3 (Embedding)      (None, 4, 8)              400       
_________________________________________________________________
flatten_3 (Flatten)          (None, 32)                0         
_________________________________________________________________
dense_3 (Dense)              (None, 1)                 33        
=================================================================
  1. 最后,如果上面的 1 是正确的,是否有更好的方法来获得嵌入层 model.add(Embedding(vocab_size, 8, input_length=max_length)) 而无需进行一次热编码 encoded_docs = [one_hot(d, vocab_size) for d in docs]

+++++++++++++++++++++++++++++++ 更新 - 提供更新的代码

from numpy import array
from keras.preprocessing.text import one_hot
from keras.preprocessing.sequence import pad_sequences
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Flatten
from keras.layers.embeddings import Embedding
# define documents
docs = ['Well done!',
        'Good work',
        'Great effort',
        'nice work',
        'Excellent!',
        'Weak',
        'Poor effort!',
        'not good',
        'poor work',
        'Could have done better.']
# define class labels
labels = array([1,1,1,1,1,0,0,0,0,0])


from keras.preprocessing.text import Tokenizer

tokenizer = Tokenizer()

#this creates the dictionary
#IMPORTANT: MUST HAVE ALL DATA - including Test data
#IMPORTANT2: This method should be called only once!!!
tokenizer.fit_on_texts(docs)

#this transforms the texts in to sequences of indices
encoded_docs2 = tokenizer.texts_to_sequences(docs)

encoded_docs2

max_length = 4
padded_docs2 = pad_sequences(encoded_docs2, maxlen=max_length, padding='post')
max_index = array(padded_docs2).reshape((-1,)).max()



# define the model
model = Sequential()
model.add(Embedding(max_index+1, 8, input_length=max_length))# you cannot use just max_index 
model.add(Flatten())
model.add(Dense(1, activation='sigmoid'))
# compile the model
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['acc'])
# summarize the model
print(model.summary())
# fit the model
model.fit(padded_docs2, labels, epochs=50, verbose=0)
# evaluate the model
loss, accuracy = model.evaluate(padded_docs2, labels, verbose=0)
print('Accuracy: %f' % (accuracy*100))

embeddings = model.layers[0].get_weights()[0]

embeding_for_word_7 = embeddings[14]
index = tokenizer.texts_to_sequences([['well']])[0][0]
tokenizer.document_count
tokenizer.word_index

1 - 是的,不保证单词唯一性,请参阅 docs:

  • 来自 one_hot:这是 hashing_trick 函数的包装...
  • 来自 hashing_trick:"Two or more words may be assigned to the same index, due to possible collisions by the hashing function. The probability of a collision is in relation to the dimension of the hashing space and the number of distinct objects."

为此使用 Tokenizer 会更好。 (见问题 4)

记住创建索引时应该一次包含所有单词是非常重要的。你不能使用一个函数来创建一个包含 2 个单词的字典,然后再用 2 个单词,然后再......这将创建非常错误的字典。


2 - 嵌入的大小为 50 x 8,因为它是在嵌入层中定义的:

Embedding(vocab_size, 8, input_length=max_length)
  • vocab_size = 50 - 这意味着字典中有 50 个单词
  • embedding_size= 8 - 这是嵌入的真实大小:每个单词由 8 个数字的向量表示。

3 - 你不知道。他们使用相同的嵌入。

系统将使用相同的嵌入(索引 = 2 的嵌入)。这对您的模型根本不健康。您应该使用另一种方法来创建问题 1 中的索引。


4 - 您可以手动创建单词词典,或使用 Tokenizer class.

手动:

确保删除标点符号,将所有单词小写。

只需为您拥有的每个单词创建一个字典:

dictionary = dict()
current_key = 1

for doc in docs:
    for word in doc.split(' '):
        #make sure you remove punctuation (this might be boring)
        word = word.lower()

        if not (word in dictionary):
            dictionary[word] = current_key
            current_key += 1

分词器:

from keras.preprocessing.text import Tokenizer

tokenizer = Tokenizer()

#this creates the dictionary
#IMPORTANT: MUST HAVE ALL DATA - including Test data
#IMPORTANT2: This method should be called only once!!!
tokenizer.fit_on_texts(docs)

#this transforms the texts in to sequences of indices
encoded_docs2 = tokenizer.texts_to_sequences(docs)

查看encoded_docs2的输出:

[[6, 2], [3, 1], [7, 4], [8, 1], [9], [10], [5, 4], [11, 3], [5, 1], [12, 13, 2, 14]]

查看最大索引:

padded_docs2 = pad_sequences(encoded_docs2, maxlen=max_length, padding='post')
max_index = array(padded_docs2).reshape((-1,)).max()

所以,您的 vocab_size 应该是 15(否则您会有很多无用且无害的嵌入行)。请注意,0 未用作索引。它将出现在 padding!!!

Do not "fit" the tokenizer again! Only use texts_to_sequences() or other methods here that are not related to "fitting".

提示:有时在您的文本中包含 end_of_sentence 个词可能会有用。

提示 2: 保存你的 Tokenizer 以备后用是个好主意(因为它有一个特定的数据字典,用 fit_on_texts).

#save:
text_to_save = tokenizer.to_json()

#load:
from keras.preprocessing.text import tokenizer_from_json
tokenizer = tokenizer_from_json(loaded_text)

5 - 嵌入参数正确。

密集:

Dense 的参数始终基于前一层(在本例中为 Flatten)。

公式为:previous_output * units + units

这导致 32 (from the Flatten) * 1 (Dense units) + 1 (Dense bias=units) = 33

展平:

它将之前的所有维度相乘 = 8 * 4.
Embedding 输出 lenght = 4embedding_size = 8.


6 - Embedding 层不依赖于您的数据及其预处理方式。

Embedding 图层的大小仅为 50 x 8,因为您是这么说的。 (见问题 2)

当然,还有更好的数据预处理方法 - 请参阅问题 4。

这将使您 select 比 vocab_size(字典大小)更好。

看到一个词的嵌入:

获取嵌入矩阵:

embeddings = model.layers[0].get_weights()[0]

选择任意词索引:

embeding_for_word_7 = embeddings[7]

就是这样。

如果您使用分词器,获取单词索引:

index = tokenizer.texts_to_sequences([['word']])[0][0]