Keras深度學習——從零開始構建單詞向量

語言: CN / TW / HK

theme: hydrogen

持續創作,加速成長!這是我參與「掘金日新計劃 · 6 月更文挑戰」的第28天,點選檢視活動詳情

構建單詞向量

構建單詞向量的核心思想是,在向量空間中,每個單詞周圍都存在著與之相似的單詞。例如:“queen” 和 “princess” 單詞的周圍會出現類似的詞,如 “kingdom”。從某種意義上說,這些詞的上下文同樣是相似的。

使用句子 “I love watching movie” 和 “I like watching movie”,當我們一個句子中的某個單詞作為輸出,而句子中的其餘單詞作為輸入時,可以構造以下資料集:

輸入 輸出
love watching movie I
I watching movie love
I love movie watching
I love watching movie
like watching movie I
I watching movie like
I like movie watching
I like watching movie

當我們將某一個單詞用作輸出,其餘單詞用作輸入,將輸入和輸出進行獨熱編碼後得到以下形式的向量:

輸入向量 輸出向量
0 1 1 1 0 1 0 0 0 0
1 0 1 1 0 0 1 0 0 0
1 1 0 1 0 0 0 1 0 0
1 1 1 0 0 0 0 0 1 0
0 0 1 1 1 1 0 0 0 0
1 0 1 1 0 0 0 0 0 1
1 0 0 1 1 0 0 1 0 0
1 0 1 0 1 0 0 0 1 0

可以看到,輸入向量的第一行為 {0, 1, 1, 1, 0},因為輸入單詞的索引為 {1, 2, 3},輸出為 {1, 0, 0, 0, 0},因為輸出單詞的索引為 {0}。 如果我們使用的神經網路中隱藏層包含三個神經元,則神經網路架構如下所示:

神經網路架構

網路中每層的資訊如下:

|網路層| 尺寸| 描述 |--|--|--| |輸入層| 5 |每個輸入向量尺寸為 5 |輸入層權重 |5x3| 隱藏層中的 3 個神經元各有 5 個連線到輸入層的權重 |隱藏層 |3 |隱藏層包含 3 個神經元 |輸出層權重 |3x5| 由於有 5 個不同單詞,因此 3 個隱藏單元輸出對映到輸出層的 5 個輸出 |輸出層 |5| 輸出向量的尺寸為 5,每一單詞對應一個預測單詞概率

在構建單詞向量時,在隱藏層中並不使用啟用函式。使用 softmax 函式處理輸出層輸出值,以便得到單詞概率,使用交叉熵損失作為損失函式,使用 Adam 優化器優化網路權重值。當向網路中輸入單詞(而非輸入語句)的獨熱編碼時,給定單詞的編碼向量可以使用隱藏層的輸出值表示。

從零開始構建單詞向量

根據我們在上一節中介紹的單詞向量的生成方式,我們使用 Keras 實現單詞編碼向量神經網路。 首先,定義輸入句子: python docs = ["I love watching movie", "I like watching movie"] 在以上語句中,我們期望 lovelike 的詞向量是相似的,因為 lovelike 的上下文是完全相同的。 然後,我們為每個句子建立一個獨熱編碼: python from sklearn.feature_extraction.text import CountVectorizer vectorizer = CountVectorizer(min_df=0, token_pattern=r"\b\w+\b") vectorizer.fit(docs) vectorizer 定義了將文件轉換為向量格式的引數。此外,通過傳遞引數 min_df 確保在 CountVectorizer 中不會過濾掉諸如 I 之類的詞,使用定義的輸入句子擬合 vectorizer 得到合適的單詞向量化模型。 將文件 docs 轉換為向量格式: python vector = vectorizer.transform(docs) 驗證執行轉換後的語句向量: python print(vectorizer.vocabulary_) print(vector.shape) print(vector.toarray()) vocabulary_ 返回各種單詞的索引,而 vector.toarray 將返回句子的獨熱編碼,輸出結果如下: shell {'i': 0, 'love': 2, 'watching': 4, 'movie': 3, 'like': 1} (2, 5) [[1 0 1 1 1] [1 1 0 1 1]] 建立輸入和輸出資料集: ```python x = [] y = [] for i in range(len(docs)): for j in range(len(docs[i].split())): t_x = [] t_y = [] for k in range(4): if(j==k): t_y.append(docs[i].split()[k]) continue else: t_x.append(docs[i].split()[k]) x.append(t_x) y.append(t_y)

x2 = [] y2 = [] for i in range(len(x)): x2.append(' '.join(x[i])) y2.append(' '.join(y[i])) 從前面的程式碼中,我們建立了輸入和輸出資料集,我們可以列印資料集,檢視其內容:python print(x2) print(y2) 列印粗的輸入和輸出資料如下:python ['love watching movie', 'I watching movie', 'I love movie', 'I love watching', 'like watching movie', 'I watching movie', 'I like movie', 'I like watching'] ['I', 'love', 'watching', 'movie', 'I', 'like', 'watching', 'movie'] 將前面的輸入和輸出單詞轉換為向量:python vector_x = vectorizer.transform(x2) vector_y = vectorizer.transform(y2)

vector_x = vector_x.toarray() vector_y = vector_y.toarray()

列印輸入與輸出陣列

print('Input: ', vector_x) print('Output: ' vector_y) 打印出的輸入和輸出陣列如下:python Input: [[0 0 1 1 1] [1 0 0 1 1] [1 0 1 1 0] [1 0 1 0 1] [0 1 0 1 1] [1 0 0 1 1] [1 1 0 1 0] [1 1 0 0 1]] Output: [[1 0 0 0 0] [0 0 1 0 0] [0 0 0 0 1] [0 0 0 1 0] [1 0 0 0 0] [0 1 0 0 0] [0 0 0 0 1] [0 0 0 1 0]] 根據定義的神經網路,構建模型:python from keras.layers import Dense from keras.models import Sequential model = Sequential() model.add(Dense(3, input_shape=(5,))) model.add(Dense(5,activation='sigmoid')) model.summary() 模型簡要架構資訊輸入如下:shell Model: "sequential"


Layer (type) Output Shape Param #

dense (Dense) (None, 3) 18


dense_1 (Dense) (None, 5) 20

Total params: 38 Trainable params: 38 Non-trainable params: 0


編譯並擬合模型:python model.compile(loss='categorical_crossentropy',optimizer='adam') model.fit(vector_x, vector_y, epochs=1000, batch_size=2,verbose=1) 通過獲取中間層值來提取詞向量,其中輸入是每個單個詞的編碼向量:python from keras.models import Model layer_name = 'dense' intermediate_layer_model = Model(inputs=model.input,outputs=model.get_layer(layer_name).output) 在以上程式碼中,我們從目標層中提取輸出——通過模型中的名為 `dense` 的層獲取單詞編碼向量。 接下來,向網路中傳遞單詞的獨熱編碼向量作為輸入,提取中間層的輸出:python for i in range(len(vectorizer.vocabulary_)): word = list(vectorizer.vocabulary_.keys())[i] word_vec = vectorizer.transform([list(vectorizer.vocabulary_.keys())[i]]).toarray() print(word, intermediate_layer_model.predict(word_vec)) 各個單詞的編碼向量如下:shell i [[-1.41066 0.02432728 -1.0654368 ]] love [[-1.1692711 1.7719828 0.54331756]] watching [[ 1.163808 1.908086 -1.5191256]] movie [[0.01165223 2.0688105 1.532387 ]] like [[-1.197992 1.662775 0.5817174]] `` 可以看出,在以上單詞編碼向量中,“love” 和 “like`” 這兩個單詞之間的相關性更高,因此可以更好地表示單詞向量。