Embedding理解與程式碼實現
Embedding 字面理解是 “嵌入”,實質是一種對映,從語義空間到向量空間的對映,同時儘可能在向量空間保持原樣本在語義空間的關係,如語義接近的兩個詞彙在向量空間中的位置也比較接近。
下面以一個基於Keras的簡單的文字情感分類問題為例解釋Embedding的訓練過程:
首先,匯入Keras的相關庫
from keras.layers import Dense, Flatten, Input
from keras.layers.embeddings import Embedding
from keras.models import Model
from keras.preprocessing. sequence import pad_sequences
from keras.preprocessing.text import one_hot
import numpy as np
給出文字內容和label
# 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 = [1, 1, 1, 1, 1, 0, 0, 0, 0, 0]
然後將文字編碼成數字格式並padding到相同長度
# 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)
上面兩個print輸出如下,每次執行得到的數字可能會不一樣,但同一個單詞對應相同的數字。上述one_hot編碼對映到[1,n],不包括0,n為上述的vocab_size,為估計的詞彙表大小。然後padding到最大的詞彙長度,用0向後填充,這也是為什麼前面one-hot不會對映到0的原因。
[[41, 13], [14, 5], [11, 19], [30, 5], [47], [16], [37, 19], [26, 14], [37, 5], [38, 40, 13, 19], [37], [14]]
[[41 13 0 0]
[14 5 0 0]
[11 19 0 0]
[30 5 0 0]
[47 0 0 0]
[16 0 0 0]
[37 19 0 0]
[26 14 0 0]
[37 5 0 0]
[38 40 13 19]
[37 0 0 0]
[14 0 0 0]]
模型的定義
# define the model
input = Input(shape=(4, ))
x = Embedding(vocab_size, 8, input_length=max_length)(input) #這一步對應的引數量為50*8
x = Flatten()(x)
x = Dense(1, activation='sigmoid')(x)
model = Model(inputs=input, outputs=x)
# compile the model
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['acc'])
# summarize the model
print(model.summary()) #輸出模型結構
輸出的模型結構如下所示:
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
input_1 (InputLayer) (None, 4) 0
_________________________________________________________________
embedding_1 (Embedding) (None, 4, 8) 400
_________________________________________________________________
flatten_1 (Flatten) (None, 32) 0
_________________________________________________________________
dense_1 (Dense) (None, 1) 33
=================================================================
這裡先介紹一下Keras中的Embedding函式,詳細見z官方文件:Embedding
keras.layers.Embedding(input_dim, output_dim, input_length)
- input_dim:這是文字資料中詞彙的取值可能數。例如,如果您的資料是整數編碼為0-9之間的值,那麼詞彙的大小就是10個單詞;
- output_dim:這是嵌入單詞的向量空間的大小。它為每個單詞定義了這個層的輸出向量的大小。例如,它可能是32或100甚至更大,可以視為具體問題的超引數;
- input_length:這是輸入序列的長度,就像您為Keras模型的任何輸入層所定義的一樣,也就是一次輸入帶有的詞彙個數。例如,如果您的所有輸入文件都由1000個字組成,那麼input_length就是1000。
再看一下keras中embeddings的原始碼,其構建函式如下:
def build(self, input_shape):
self.embeddings = self.add_weight(
shape=(self.input_dim, self.output_dim),
initializer=self.embeddings_initializer,
name='embeddings',
regularizer=self.embeddings_regularizer,
constraint=self.embeddings_constraint,
dtype=self.dtype)
self.built = True
可以看到它的引數shape為(self.input_dim, self.output_dim),在上述例子中即(50,8)
上面輸出的模型結構中Embedding層的引數為400,它實質就是50*8得到的,這是一個關鍵要理解的資訊。
我們知道one-hot 是無法考慮語義間的相互關係的,但embedding向量的訓練是要藉助one-hot的。上面的one-hot把每一個單詞對映成一個整數,但實際上這個整數就表示了50維向量中 1 所在的索引位置,用整數顯示是為了更好理解和表示,而實際在網路中,它的形式可以理解為如下圖(下面相當於one-hot向量為5維,輸出embedding向量為3維)
右邊的神經元為one-hot輸入,左邊為得到的embedding表示,圖中1所對應的紅線權重就是該單詞對應的詞向量,這一層神經元只能作為第一層嵌入,是沒有偏置和啟用函式的,它也可以被理解為如下的一個矩陣相乘,輸出就是該單詞的詞向量。然後詞向量再輸入到下一層。這一層總的引數量就是這些權重,也是下面中間的矩陣。
一個片語中有多個單詞,如上面例子中有四個,那就分別經過這一層得到四個詞向量,然後Flatten 到一層,再後接全連線層進行分類。因此,在這個模型中,embedding向量和分類模型是同時訓練的,當然也可以用他人在大預料庫中已經訓練好的向量來直接訓練分類。
上述例子的完整程式碼如下:
from keras.layers import Dense, Flatten, Input
from keras.layers.embeddings import Embedding
from keras.models import Model
from keras.preprocessing.sequence import pad_sequences
from keras.preprocessing.text import one_hot
import numpy as np
# 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 = [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] #one_hot編碼到[1,n],不包括0
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
input = Input(shape=(4, ))
x = Embedding(vocab_size, 8, input_length=max_length)(input) #這一步對應的引數量為50*8
x = Flatten()(x)
x = Dense(1, activation='sigmoid')(x)
model = Model(inputs=input, outputs=x)
# 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=100, verbose=0)
# evaluate the model
loss, accuracy = model.evaluate(padded_docs, labels, verbose=0)
loss_test, accuracy_test = model.evaluate(padded_docs, labels, verbose=0)
print('Accuracy: %f' % (accuracy * 100))
# test the model
test = one_hot('good',50)
padded_test = pad_sequences([test], maxlen=max_length, padding='post')
print(model.predict(padded_test))
圖片參考自:https://blog.csdn.net/laolu1573/article/details/77170407