(一) tensorflow筆記:流程,概念和簡單代碼注釋
(二) tensorflow筆記:多層CNN代碼分析
(三) tensorflow筆記:多層LSTM代碼分析
(四) tensorflow筆記:常用函數說明
(五) tensorflow筆記:模型的保存與訓練過程可視化
(六)tensorflow筆記:使用tf來實現word2vec
時隔若干個月,又繞到了word2vec。關於word2vec的原理我就不敘述了,具體可見word2vec中的數學,寫的非常好。
我后來自己用Python實現了一遍word2vec,過程寫在自己動手寫word2vec (一):主要概念和流程以及后續的若干文章中
我當時使用的是Hierarchical Softmax+CBOW的模型。給我的感覺是比較累,既要費力去寫huffman樹,還要自己寫計算梯度的代碼,完了按層softmax速度還慢。這次我決定用tensorflow來寫,除了極大的精簡了代碼以外,可以使用gpu對運算進行加速。此外,這次使用了負采樣(negative sampling)+skip-gram模型,從而避免了使用Huffman樹導致訓練速度變慢的情況,適合大規模的文本。
一些相關的資料:
word2vec 中的數學原理詳解-基於 Negative Sampling 的模型
自己動手寫word2vec (四):CBOW和skip-gram模型
tensorflow筆記:流程,概念和簡單代碼注釋
tensorflow筆記 :常用函數說明
其實google已經實現過一遍word2vec了(點這里),我看了一下代碼,感覺核心代碼非常簡介干練,我自己寫的許多運算和函數調用也是參照它來的,但是關於外圍的代碼,包括數據集的生成等方面,我不是很喜歡,而且也與我的要求不符,所以我重新寫了一下,並且進行了封裝,增加了模型的存/取,訓練過程可視化等功能,並且簡化了流程。
我的模型主要分成兩部分:由輸入單詞生成訓練集的外圍代碼,以及用於描述模型,訓練的核心代碼。在訓練的時候,外圍代碼收到一個分好詞的句子,例如[‘我’,’中午’,’吃飯’],然后根據skip-gram模型,將其轉化成輸入集和標簽集。例如
[我,中午,中午,吃飯]
[中午,我,吃飯,中午]
當然了,實際過程中輸入集和標簽集都是用id來表示的。生成輸入集和標簽集以后,將其輸入核心代碼進行訓練。那就先從核心代碼講起吧。這篇文章中的代碼是不完全的,想看完整版的可以移步https://github.com/multiangle/tfword2vec
核心代碼
核心代碼主要就是描述模型,計算loss,根據loss優化參數等步驟。這里計算loss直接使用了tf封裝好的tf.nn.nce_loss方法,比較方便。優化方法這里也是選的最簡單的梯度下降法。具體的描述就放在代碼里說好了
self.graph = tf.Graph() self.graph = tf.Graph() with self.graph.as_default(): # 首先定義兩個用作輸入的占位符,分別輸入輸入集(train_inputs)和標簽集(train_labels) self.train_inputs = tf.placeholder(tf.int32, shape=[self.batch_size]) self.train_labels = tf.placeholder(tf.int32, shape=[self.batch_size, 1]) # 詞向量矩陣,初始時為均勻隨機正態分布 self.embedding_dict = tf.Variable( tf.random_uniform([self.vocab_size,self.embedding_size],-1.0,1.0) ) # 模型內部參數矩陣,初始為截斷正太分布 self.nce_weight = tf.Variable(tf.truncated_normal([self.vocab_size, self.embedding_size], stddev=1.0/math.sqrt(self.embedding_size))) self.nce_biases = tf.Variable(tf.zeros([self.vocab_size])) # 將輸入序列向量化,具體可見我的【常用函數說明】那一篇 embed = tf.nn.embedding_lookup(self.embedding_dict, self.train_inputs) # batch_size # 得到NCE損失(負采樣得到的損失) self.loss = tf.reduce_mean( tf.nn.nce_loss( weights = self.nce_weight, # 權重 biases = self.nce_biases, # 偏差 labels = self.train_labels, # 輸入的標簽 inputs = embed, # 輸入向量 num_sampled = self.num_sampled, # 負采樣的個數 num_classes = self.vocab_size # 類別數目 ) ) # tensorboard 相關 tf.scalar_summary('loss',self.loss) # 讓tensorflow記錄參數 # 根據 nce loss 來更新梯度和embedding,使用梯度下降法(gradient descent)來實現 self.train_op = tf.train.GradientDescentOptimizer(learning_rate=0.1).minimize(self.loss) # 訓練操作 # 計算與指定若干單詞的相似度 self.test_word_id = tf.placeholder(tf.int32,shape=[None]) vec_l2_model = tf.sqrt( # 求各詞向量的L2模 tf.reduce_sum(tf.square(self.embedding_dict),1,keep_dims=True) ) avg_l2_model = tf.reduce_mean(vec_l2_model) tf.scalar_summary('avg_vec_model',avg_l2_model) self.normed_embedding = self.embedding_dict / vec_l2_model # self.embedding_dict = norm_vec # 對embedding向量正則化 test_embed = tf.nn.embedding_lookup(self.normed_embedding, self.test_word_id) self.similarity = tf.matmul(test_embed, self.normed_embedding, transpose_b=True) # 變量初始化操作 self.init = tf.global_variables_initializer() # 匯總所有的變量記錄 self.merged_summary_op = tf.merge_all_summaries() # 保存模型的操作 self.saver = tf.train.Saver()
外圍代碼
外圍代碼其實有很多,例如訓練過程中變量的記錄,模型的保存與讀取等等,不過這與訓練本身沒什么關系,這里還是貼如何將句子轉化成輸入集和標簽集的代碼。對其他方面感興趣的看官可以到github上看完整的代碼。
def train_by_sentence(self, input_sentence=[]):
# input_sentence: [sub_sent1, sub_sent2, ...] # 每個sub_sent是一個單詞序列,例如['這次','大選','讓'] sent_num = input_sentence.__len__() batch_inputs = [] batch_labels = [] for sent in input_sentence: # 輸入有可能是多個句子,這里每個循環處理一個句子 for i in range(sent.__len__()): # 處理單個句子中的每個單詞 start = max(0,i-self.win_len) # 窗口為 [-win_len,+win_len],總計長2*win_len+1 end = min(sent.__len__(),i+self.win_len+1) # 將某個單詞對應窗口中的其他單詞轉化為id計入label,該單詞本身計入input for index in range(start,end): if index == i: continue else: input_id = self.word2id.get(sent[i]) label_id = self.word2id.get(sent[index]) if not (input_id and label_id): # 如果單詞不在詞典中,則跳過 continue batch_inputs.append(input_id) batch_labels.append(label_id) if len(batch_inputs)==0: # 如果標簽集為空,則跳過 return batch_inputs = np.array(batch_inputs,dtype=np.int32) batch_labels = np.array(batch_labels,dtype=np.int32) batch_labels = np.reshape(batch_labels,[batch_labels.__len__(),1]) # 生成供tensorflow訓練用的數據 feed_dict = { self.train_inputs: batch_inputs, self.train_labels: batch_labels } # 這句操控tf進行各項操作。數組中的選項,train_op等,是讓tf運行的操作,feed_dict選項用來輸入數據 _, loss_val, summary_str = self.sess.run([self.train_op,self.loss,self.merged_summary_op], feed_dict=feed_dict) # train loss,記錄這次訓練的loss值 self.train_loss_records.append(loss_val) # self.train_loss_k10 = sum(self.train_loss_records)/self.train_loss_records.__len__() self.train_loss_k10 = np.mean(self.train_loss_records) # 求loss均值 if self.train_sents_num % 1000 == 0 : self.summary_writer.add_summary(summary_str,self.train_sents_num) print("{a} sentences dealed, loss: {b}" .format(a=self.train_sents_num,b=self.train_loss_k10)) # train times self.train_words_num += batch_inputs.__len__() self.train_sents_num += input_sentence.__len__() self.train_times_num += 1
轉自:http://blog.csdn.net/u014595019/article/details/54093161
