1. 程式人生 > >Embedding理解與程式碼實現

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