摘要:那么為什么要表示成向量呢這個問題我們后續討論。所以,完整的模型是現在,我們可以訓練這個模型在訓練的過程中,你在控制臺可以得到如下結果隨著損失值的不斷下降,最終會達到一個穩定值。為了得到這些表示,神經網絡使用了上下文信息。
作者:chen_h
微信號 & QQ:862251340
微信公眾號:coderpai
簡書地址:https://www.jianshu.com/p/4e1...
我認為學習算法的最好方法就是嘗試去實現它,因此這個教程我們就來學習如何利用 TensorFlow 來實現詞嵌入。
這篇文章我們不會去過多的介紹一些詞向量的內容,所以很多 king - man - woman - queue 的例子會被省去,直接進入編碼實踐過程。
我們如何設計這些詞嵌入?對于如何設計詞嵌入有很多的技術,這里我們討論一種非常有名的技術。與我們往常的認知不同,word2vec 并不是一個深層的網絡,它只是一個三層的淺層網絡。
注意:word2vec 有很多的技術細節,但是我們會跳過這些細節,來使得更加容易理解。
word2vec 如何工作?word2vec 算法的設計如下:
它是一個三層的網絡(一個輸入層 + 一個隱藏層 + 一個輸出層)。
模型輸入一個詞,然后去預測它周圍的詞。
移除最后一層(輸出層),保留輸入層和隱藏層。
現在,輸入一個詞庫中的詞,然后隱藏層的輸出就是輸入詞的詞向量。
就是這么簡單,這個三層網絡就可以得到一個還不錯的詞向量。
接下來就讓我們來實現這個模型。完整的代碼可以點擊 Github,但我建議你先不要看完整的代碼,先一步一步學習。
接下來,我們先定義我們要處理的原始文本:
import numpy as np import tensorflow as tf corpus_raw = "He is the king . The king is royal . She is the royal queen " # convert to lower case corpus_raw = corpus_raw.lower()
現在,我們需要將輸入的原始文本數據轉換成一個輸入輸出對,以便我們對輸入的詞,可以去預測它附近的詞。比如,我們確定一個中心詞, 窗口大小 window_size 設置為 n ,那么我們就是去預測中心詞前面 n 個詞和后面 n 個詞。Chris McCormick 的這篇博客給出了比較詳細的解釋。
注意:如果中心詞是在句子的開頭或者末尾,那么我們就忽略窗口無法獲得的詞。
在做這個之前,我們需要創建一個字典,用來確定每個單詞的索引,具體如下:
words = [] for word in corpus_raw.split(): if word != ".": # because we don"t want to treat . as a word words.append(word) words = set(words) # so that all duplicate words are removed word2int = {} int2word = {} vocab_size = len(words) # gives the total number of unique words for i,word in enumerate(words): word2int[word] = i int2word[i] = word
這個字典的運行結果如下:
print(word2int["queen"]) -> 42 (say) print(int2word[42]) -> "queen"
接下來,我們將我們的句子向量轉換成單詞列表,如下:
# raw sentences is a list of sentences. raw_sentences = corpus_raw.split(".") sentences = [] for sentence in raw_sentences: sentences.append(sentence.split())
上面代碼將幫助我們得到一個句子的列表,列表中的每一個元素是句子的單詞列表,如下:
print(sentences) -> [["he", "is", "the", "king"], ["the", "king", "is", "royal"], ["she", "is", "the", "royal", "queen"]]
接下來,我們要產生我們的訓練數據:
data = [] WINDOW_SIZE = 2 for sentence in sentences: for word_index, word in enumerate(sentence): for nb_word in sentence[max(word_index - WINDOW_SIZE, 0) : min(word_index + WINDOW_SIZE, len(sentence)) + 1] : if nb_word != word: data.append([word, nb_word])
這個程序給出了單詞輸入輸出對,我們將窗口的大小設置為 2。
print(data) [["he", "is"], ["he", "the"], ["is", "he"], ["is", "the"], ["is", "king"], ["the", "he"], ["the", "is"], . . . ]
至此,我們有了我們的訓練數據,但是我們需要將它轉換成計算機可以理解的表示,即數字。也就是我們之前設計的 word2int 字典。
我們再進一步表示,將這些數字轉換成 0-1 向量。
i.e., say we have a vocabulary of 3 words : pen, pineapple, apple where word2int["pen"] -> 0 -> [1 0 0] word2int["pineapple"] -> 1 -> [0 1 0] word2int["apple"] -> 2 -> [0 0 1]
那么為什么要表示成 0-1 向量呢?這個問題我們后續討論。
# function to convert numbers to one hot vectors def to_one_hot(data_point_index, vocab_size): temp = np.zeros(vocab_size) temp[data_point_index] = 1 return temp x_train = [] # input word y_train = [] # output word for data_word in data: x_train.append(to_one_hot(word2int[ data_word[0] ], vocab_size)) y_train.append(to_one_hot(word2int[ data_word[1] ], vocab_size)) # convert them to numpy arrays x_train = np.asarray(x_train) y_train = np.asarray(y_train)
現在,我們有了 x_train 和 y_train 數據:
print(x_train) -> [[ 0. 0. 0. 0. 0. 0. 1.] [ 0. 0. 0. 0. 0. 0. 1.] [ 0. 0. 0. 0. 0. 1. 0.] [ 0. 0. 0. 0. 0. 1. 0.] [ 0. 0. 0. 0. 0. 1. 0.] [ 0. 0. 0. 0. 1. 0. 0.] [ 0. 0. 0. 0. 1. 0. 0.] [ 0. 0. 0. 0. 1. 0. 0.] [ 0. 0. 0. 1. 0. 0. 0.] [ 0. 0. 0. 1. 0. 0. 0.] [ 0. 0. 0. 0. 1. 0. 0.] [ 0. 0. 0. 0. 1. 0. 0.] [ 0. 0. 0. 1. 0. 0. 0.] [ 0. 0. 0. 1. 0. 0. 0.] [ 0. 0. 0. 1. 0. 0. 0.] [ 0. 0. 0. 0. 0. 1. 0.] [ 0. 0. 0. 0. 0. 1. 0.] [ 0. 0. 0. 0. 0. 1. 0.] [ 0. 1. 0. 0. 0. 0. 0.] [ 0. 1. 0. 0. 0. 0. 0.] [ 0. 0. 1. 0. 0. 0. 0.] [ 0. 0. 1. 0. 0. 0. 0.] [ 0. 0. 0. 0. 0. 1. 0.] [ 0. 0. 0. 0. 0. 1. 0.] [ 0. 0. 0. 0. 0. 1. 0.] [ 0. 0. 0. 0. 1. 0. 0.] [ 0. 0. 0. 0. 1. 0. 0.] [ 0. 0. 0. 0. 1. 0. 0.] [ 0. 0. 0. 0. 1. 0. 0.] [ 0. 1. 0. 0. 0. 0. 0.] [ 0. 1. 0. 0. 0. 0. 0.] [ 0. 1. 0. 0. 0. 0. 0.] [ 1. 0. 0. 0. 0. 0. 0.] [ 1. 0. 0. 0. 0. 0. 0.]]
這兩個數據的維度如下:
print(x_train.shape, y_train.shape) -> (34, 7) (34, 7) # meaning 34 training points, where each point has 7 dimensions構造 TensorFlow 模型
# making placeholders for x_train and y_train x = tf.placeholder(tf.float32, shape=(None, vocab_size)) y_label = tf.placeholder(tf.float32, shape=(None, vocab_size))
從上圖中可以看出,我們將訓練數據轉換成了另一種向量表示。
EMBEDDING_DIM = 5 # you can choose your own number W1 = tf.Variable(tf.random_normal([vocab_size, EMBEDDING_DIM])) b1 = tf.Variable(tf.random_normal([EMBEDDING_DIM])) #bias hidden_representation = tf.add(tf.matmul(x,W1), b1)
接下來,我們對隱藏層的數據進行處理,并且對其附近的詞進行預測。預測詞的方法我們采用 softmax 方法。
W2 = tf.Variable(tf.random_normal([EMBEDDING_DIM, vocab_size])) b2 = tf.Variable(tf.random_normal([vocab_size])) prediction = tf.nn.softmax(tf.add( tf.matmul(hidden_representation, W2), b2))
所以,完整的模型是:
input_one_hot ---> embedded repr. ---> predicted_neighbour_prob predicted_prob will be compared against a one hot vector to correct it.
現在,我們可以訓練這個模型:
sess = tf.Session() init = tf.global_variables_initializer() sess.run(init) #make sure you do this! # define the loss function: cross_entropy_loss = tf.reduce_mean(-tf.reduce_sum(y_label * tf.log(prediction), reduction_indices=[1])) # define the training step: train_step = tf.train.GradientDescentOptimizer(0.1).minimize(cross_entropy_loss) n_iters = 10000 # train for n_iter iterations for _ in range(n_iters): sess.run(train_step, feed_dict={x: x_train, y_label: y_train}) print("loss is : ", sess.run(cross_entropy_loss, feed_dict={x: x_train, y_label: y_train}))
在訓練的過程中,你在控制臺可以得到如下結果:
loss is : 2.73213 loss is : 2.30519 loss is : 2.11106 loss is : 1.9916 loss is : 1.90923 loss is : 1.84837 loss is : 1.80133 loss is : 1.76381 loss is : 1.73312 loss is : 1.70745 loss is : 1.68556 loss is : 1.66654 loss is : 1.64975 loss is : 1.63472 loss is : 1.62112 loss is : 1.6087 loss is : 1.59725 loss is : 1.58664 loss is : 1.57676 loss is : 1.56751 loss is : 1.55882 loss is : 1.55064 loss is : 1.54291 loss is : 1.53559 loss is : 1.52865 loss is : 1.52206 loss is : 1.51578 loss is : 1.50979 loss is : 1.50408 loss is : 1.49861 . . .
隨著損失值的不斷下降,最終會達到一個穩定值。即使我們無法獲得很精確的結果,但是我們也不在乎,因為我們感興趣的是 W1 和 b1 的值,即隱藏層的權重。
讓我們來看看這些權重,如下:
print(sess.run(W1)) print("----------") print(sess.run(b1)) print("----------") -> [[-0.85421133 1.70487809 0.481848 -0.40843448 -0.02236851] [-0.47163373 0.34260952 -2.06743765 -1.43854153 -0.14699034] [-1.06858993 -1.10739779 0.52600187 0.24079895 -0.46390489] [ 0.84426647 0.16476244 -0.72731972 -0.31994426 -0.33553854] [ 0.21508843 -1.21030915 -0.13006891 -0.24056002 -0.30445012] [ 0.17842589 2.08979321 -0.34172744 -1.8842833 -1.14538431] [ 1.61166084 -1.17404735 -0.26805425 0.74437028 -0.81183684]] ---------- [ 0.57727528 -0.83760375 0.19156453 -0.42394346 1.45631313] ----------為什么采用 0-1 向量?
當我們將一個 0-1 向量與 W1 相乘時,我們基本上可以將 W1 與 0-1 向量對應的那個 1 相乘的結果就是詞向量。也就是說, W1 就是一個數據查詢表。
在我們的程序中,我們也添加了一個偏置項 b1 ,所以我們也需要將它加上。
vectors = sess.run(W1 + b1) # if you work it out, you will see that it has the same effect as running the node hidden representation print(vectors) -> [[-0.74829113 -0.48964909 0.54267412 2.34831429 -2.03110814] [-0.92472583 -1.50792813 -1.61014366 -0.88273793 -2.12359881] [-0.69424796 -1.67628145 3.07313657 -1.14802659 -1.2207377 ] [-1.7077738 -0.60641652 2.25586247 1.34536338 -0.83848488] [-0.10080346 -0.90931684 2.8825531 -0.58769202 -1.19922316] [ 1.49428082 -2.55578995 2.01545811 0.31536022 1.52662396] [-1.02735448 0.72176981 -0.03772151 -0.60208392 1.53156447]]
如果我們想得到 queen 的向量,我們可以用如下表示:
print(vectors[ word2int["queen"] ]) # say here word2int["queen"] is 2 -> [-0.69424796 -1.67628145 3.07313657 -1.14802659 -1.2207377 ]那么這些漂亮的向量有什么用呢?
我們寫一個如何去查找最相近向量的函數,當然這個寫法是非常簡單粗糙的。
def euclidean_dist(vec1, vec2): return np.sqrt(np.sum((vec1-vec2)**2)) def find_closest(word_index, vectors): min_dist = 10000 # to act like positive infinity min_index = -1 query_vector = vectors[word_index] for index, vector in enumerate(vectors): if euclidean_dist(vector, query_vector) < min_dist and not np.array_equal(vector, query_vector): min_dist = euclidean_dist(vector, query_vector) min_index = index return min_index
接下來,讓我們來測試一下單詞 king ,queen 和 royal 這些詞。
print(int2word[find_closest(word2int["king"], vectors)]) print(int2word[find_closest(word2int["queen"], vectors)]) print(int2word[find_closest(word2int["royal"], vectors)]) -> queen king he
我們可以得到如下有趣的結果。
king is closest to queen queen is closest to king royal is closest to he
第三個數據是我們根據大型語料庫得出來的(看起來還不錯)。語料庫的數據更大,我們得到的結果會更好。(注意:由于權重是隨機初始化的,所以我們可能會得到不同的結果,如果有需要,我們可以多運行幾次。)
讓我們來畫出這個向量相關圖。
首先,我們需要利用將為技術將維度從 5 減小到 2,所用的技術是:tSNE(teesnee!)
from sklearn.manifold import TSNE model = TSNE(n_components=2, random_state=0) np.set_printoptions(suppress=True) vectors = model.fit_transform(vectors)
然后,我們需要對結果進行規范化,以便我們可以在 matplotlib 中更好的對它進行查看。
from sklearn import preprocessing normalizer = preprocessing.Normalizer() vectors = normalizer.fit_transform(vectors, "l2")
最后,我們將繪制出圖。
import matplotlib.pyplot as plt fig, ax = plt.subplots() for word in words: print(word, vectors[word2int[word]][1]) ax.annotate(word, (vectors[word2int[word]][0],vectors[word2int[word]][1] )) plt.show()
從圖中,我們可以看出。she 跟 queen 的距離非常接近,king 與 royal 的距離和 king 與 queen 的距離相同。如果我們有一個更大的語料庫,我們可以得到更加復雜的關系圖。
為什么會發生這些?我們給神經網絡的任務是預測單詞的相鄰詞。但是我們還沒有具體的分析神經網絡是如何預測的。因此,神經網絡找出單詞的向量表示,用來幫助它預測相鄰詞這個任務。預測相鄰詞這本身不是一個有趣的任務,我們關心的是隱藏層的向量表示。
為了得到這些表示,神經網絡使用了上下文信息。在我們的語料庫中,king 和 royal 是作為相鄰詞出現的,queen 和 royal 也是作為相鄰詞出現的。
為什么把預測相鄰詞作為一個任務?其他的任務也可以用來訓練這個詞向量任務,比如利用 n-gram 就可以訓練出很好的詞向量!這里有一篇博客有詳細解釋。
那么,我們為什么還要使用相鄰詞預測作為任務呢?因為有一個比較著名的模型稱為 skip gram 模型。我們可以使用中間詞的相鄰單詞作為輸入,并要求神經網絡去預測中間詞。這被稱為連續詞袋模型。
總結詞向量是非常酷的一個工具。
不要在實際生產環境中使用這個 TensorFlow 代碼,我們這里只是為了理解才這樣寫。生產環境建議使用一些成熟的工具包,比如 gensim
我希望這個簡單教程可以幫助到一些人,可以更加深刻的理解什么是詞向量。
作者:chen_h
微信號 & QQ:862251340
簡書地址:https://www.jianshu.com/p/4e1...
CoderPai 是一個專注于算法實戰的平臺,從基礎的算法到人工智能算法都有設計。如果你對算法實戰感興趣,請快快關注我們吧。加入AI實戰微信群,AI實戰QQ群,ACM算法微信群,ACM算法QQ群。長按或者掃描如下二維碼,關注 “CoderPai” 微信號(coderpai)
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/41390.html
摘要:經過第一步的處理已經把古詩詞詞語轉換為可以機器學習建模的數字形式,因為我們采用算法進行古詩詞生成,所以還需要構建輸入到輸出的映射處理。 LSTM 介紹 序列化數據即每個樣本和它之前的樣本存在關聯,前一數據和后一個數據有順序關系。深度學習中有一個重要的分支是專門用來處理這樣的數據的——循環神經網絡。循環神經網絡廣泛應用在自然語言處理領域(NLP),今天我們帶你從一個實際的例子出發,介紹循...
摘要:采用作為特征提取器,并采用雙向語言模型。此外,預訓練的數據規模非常龐大。輸入部分處理輸入是一個線性序列,兩個句子通過分隔符分割,前后兩端分別增加標識符號。輸出處理評價從模型或者方法的角度來看,借鑒了以及,主要提出了語言模型和。 Word2Vec模型 showImg(https://segmentfault.com/img/bVbphHw?w=1282&h=726); Word2Vec有...
閱讀 2815·2021-10-13 09:48
閱讀 3776·2021-10-13 09:39
閱讀 3586·2021-09-22 16:04
閱讀 1816·2021-09-03 10:48
閱讀 837·2021-08-03 14:04
閱讀 2358·2019-08-29 15:18
閱讀 3400·2019-08-26 12:19
閱讀 2869·2019-08-26 12:08