日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

机器学习算法实现解析——word2vec源码解析

發(fā)布時間:2024/1/1 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 机器学习算法实现解析——word2vec源码解析 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

在wrod2vec工具中,有如下的幾個比較重要的概念:

  • CBOW
  • Skip-Gram
  • Hierarchical Softmax
  • Negative Sampling

其中CBOW和Skip-Gram是word2vec工具中使用到的兩種不同的語言模型,而Hierarchical Softmax和Negative Sampling是對以上的兩種模型的具體的優(yōu)化方法。

在word2vec工具中,主要的工作包括:

  • 預(yù)處理。即變量的聲明,全局變量的定義等;
  • 構(gòu)建詞庫。即包含文本的處理,以及是否需要有指定詞庫等;
  • 初始化網(wǎng)絡(luò)結(jié)構(gòu)。即包含CBOW模型和Skip-Gram模型的參數(shù)初始化,Huffman編碼的生成等;
  • 多線程模型訓(xùn)練。即利用Hierarchical Softmax或者Negative Sampling方法對網(wǎng)絡(luò)中的參數(shù)進(jìn)行求解;
  • 最終結(jié)果的處理。即是否保存和以何種形式保存。

對于以上的過程,可以由下圖表示:

在接下來的內(nèi)容中,將針對以上的五個部分,詳細(xì)分析下在源代碼中的實現(xiàn)技巧,以及簡單介紹我在讀代碼的過程中對部分代碼的一些思考。

1. 預(yù)處理

在預(yù)處理部分,對word2vec需要使用的參數(shù)進(jìn)行初始化,在word2vec中是利用傳入的方式對參數(shù)進(jìn)行初始化的。
在預(yù)處理部分,實現(xiàn)了sigmoid函數(shù)值的近似計算。在利用神經(jīng)網(wǎng)絡(luò)模型對樣本進(jìn)行預(yù)測的過程中,需要對其進(jìn)行預(yù)測,此時,需要使用到sigmoid函數(shù),sigmoid函數(shù)的具體形式為:

σ(x)=11+e?x=ex1+ex\sigma \left ( x \right )=\frac{1}{1+e^{-x}}=\frac{e^x}{1+e^x}σ(x)=1+e?x1?=1+exex?

如果每一次都請求計算sigmoid值,對性能將會有一定的影響,當(dāng)sigmoid的值對精度的要求并不是非常嚴(yán)格時,可以采用近似計算。在word2vec中,將區(qū)間[?6,6]\left [ -6,6 \right ][?6,6](設(shè)置的參數(shù)MAX_EXP為6)等距離劃分成EXP_TABLE_SIZE等份,并將每個區(qū)間中的sigmoid值計算好存入到數(shù)組expTable中,需要使用時,直接從數(shù)組中查找。計算sigmoid值的代碼如下所示:

expTable = (real *)malloc((EXP_TABLE_SIZE + 1) * sizeof(real));// 申請EXP_TABLE_SIZE+1個空間// 計算sigmoid值 for (i = 0; i < EXP_TABLE_SIZE; i++) {expTable[i] = exp((i / (real)EXP_TABLE_SIZE * 2 - 1) * MAX_EXP); // Precompute the exp() tableexpTable[i] = expTable[i] / (expTable[i] + 1); // Precompute f(x) = x / (x + 1) }

注意:在上述代碼中,作者使用的是小于EXP_TABLE_SIZE,實際的區(qū)間是[?6,6)\left [ -6,6 \right )[?6,6)

2. 構(gòu)建詞庫

在word2vec源碼中,提供了兩種構(gòu)建詞庫的方法,分別為:

  • 指定詞庫:ReadVocab()方法
  • 從詞的文本構(gòu)建詞庫:LearnVocabFromTrainFile()方法

2.1. 構(gòu)建詞庫的過程

在這里,我們以從詞的文本構(gòu)建詞庫為例。構(gòu)建詞庫的過程如下所示:

在這部分中,最主要的工作是對文本進(jìn)行處理,包括低頻詞的處理,hash表的處理等等。首先,會在詞庫中增加一個“< /s>”的詞,同時,在讀取文本的過程中,將換行符“\n”也表示成該該詞,如:

if (ch == '\n') {strcpy(word, (char *)"</s>");// 換行符用</s>表示return; }

在循環(huán)的過程中,不斷去讀取文件中的每一個詞,并在詞庫中進(jìn)行查找,若存在該詞,則該詞的詞頻+1,否則,在詞庫中增加該詞。在詞庫中,是通過哈希表的形式存儲的。最終,會過濾掉一些低頻詞。

在得到最終的詞庫之前,還需根據(jù)詞庫中的詞頻對詞庫中的詞進(jìn)行排序。

2.2. 對詞的哈希處理

在存儲詞的過程中,同時保留這兩個數(shù)組:

  • 存儲詞的vocab
  • 存儲詞的hash的vocab_hash

其中,在vocab中,存儲的是詞對應(yīng)的結(jié)構(gòu)體:

// 詞的結(jié)構(gòu)體 struct vocab_word {long long cn; // 出現(xiàn)的次數(shù)int *point; // 從根結(jié)點到葉子節(jié)點的路徑char *word, *code, codelen;// 分別對應(yīng)著詞,Huffman編碼,編碼長度 };

在vocab_hash中存儲的是詞在詞庫中的Index。

在對詞的處理過程中,主要包括:

  • 計算詞的hash值:
// 取詞的hash值 int GetWordHash(char *word) {unsigned long long a, hash = 0;for (a = 0; a < strlen(word); a++) hash = hash * 257 + word[a];hash = hash % vocab_hash_size;return hash; }
  • 檢索詞是否存在。如不存在則返回-1,否則,返回該詞在詞庫中的索引:
while (1) {if (vocab_hash[hash] == -1) return -1;// 不存在該詞if (!strcmp(word, vocab[vocab_hash[hash]].word)) return vocab_hash[hash];// 返回索引值hash = (hash + 1) % vocab_hash_size;// 處理沖突 } return -1;// 不存在該詞

在這個過程中,使用到了線性探測的開放定址法處理沖突,開放定址法就是一旦發(fā)生沖突,就去尋找下一個空的散列地址。

  • 不存在,則插入新詞。

在這個過程中,除了需要將詞增加到詞庫中,好需要計算該詞的hash值,并將vocab_hash數(shù)組中的值標(biāo)記為索引。

2.3. 對低頻詞的處理

在循環(huán)讀取每一個詞的過程中,當(dāng)出現(xiàn)“vocab_size > vocab_hash_size * 0.7”時,需要對低頻詞進(jìn)行處理。其中,vocab_size表示的是目前詞庫中詞的個數(shù),vocab_hash_size表示的是初始設(shè)定的hash表的大小。

在處理低頻詞的過程中,通過參數(shù)“min_reduce”來控制,若詞出現(xiàn)的次數(shù)小于等于該值時,則從詞庫中刪除該詞。

在刪除了低頻詞后,需要重新對詞庫中的詞進(jìn)行hash值的計算。

2.4. 根據(jù)詞頻對詞庫中的詞排序

基于以上的過程,程序已經(jīng)將詞從文件中提取出來,并存入到指定的詞庫中(vocab數(shù)組),接下來,需要根據(jù)每一個詞的詞頻對詞庫中的詞按照詞頻從大到小排序,其基本過程在函數(shù)SortVocab中,排序過程為:

qsort(&vocab[1], vocab_size - 1, sizeof(struct vocab_word), VocabCompare);

保持字符“< \s>”在最開始的位置。排序后,根據(jù)“min_count”對低頻詞進(jìn)行處理,與上述一樣,再對剩下的詞重新計算hash值。

至此,整個對詞的處理過程就已經(jīng)結(jié)束了。加下來,將是對網(wǎng)絡(luò)結(jié)構(gòu)的處理和詞向量的訓(xùn)練。

3. 初始化網(wǎng)絡(luò)結(jié)構(gòu)

有了以上的對詞的處理,就已經(jīng)處理好了所有的訓(xùn)練樣本,此時,便可以開始網(wǎng)絡(luò)結(jié)構(gòu)的初始化和接下來的網(wǎng)絡(luò)訓(xùn)練。網(wǎng)絡(luò)的初始化的過程在InitNet()函數(shù)中完成。

3.1. 初始化網(wǎng)絡(luò)參數(shù)

在初始化的過程中,主要的參數(shù)包括詞向量的初始化和映射層到輸出層的權(quán)重的初始化,如下圖所示:

在初始化的過程中,映射層到輸出層的權(quán)重都初始化為000,而對于每一個詞向量的初始化,作者的初始化方法如下代碼所示:

for (a = 0; a < vocab_size; a++) for (b = 0; b < layer1_size; b++) {next_random = next_random * (unsigned long long)25214903917 + 11;// 1、與:相當(dāng)于將數(shù)控制在一定范圍內(nèi)// 2、0xFFFF:65536// 3、/65536:[0,1]之間syn0[a * layer1_size + b] = (((next_random & 0xFFFF) / (real)65536) - 0.5) / layer1_size;// 初始化詞向量 }

首先,生成一個很大的next_random的數(shù),通過與“0xFFFF”進(jìn)行與運算截斷,再除以65536得到[0,1]\left [ 0,1 \right ][0,1]之間的數(shù),最終,得到的初始化的向量的范圍為:[?0.5m,0.5m]\left [ -\frac{0.5}{m},\frac{0.5}{m}\right ][?m0.5?,m0.5?],其中,mmm為詞向量的長度。

3.2. Huffman樹的構(gòu)建

在層次Softmax中需要使用到Huffman樹以及Huffman編碼,因此,在網(wǎng)絡(luò)結(jié)構(gòu)的初始化過程中,也需要初始化Huffman樹。在生成Huffman樹的過程中,首先定義了333個長度為vocab_size*2+1的數(shù)組:

long long *count = (long long *)calloc(vocab_size * 2 + 1, sizeof(long long)); long long *binary = (long long *)calloc(vocab_size * 2 + 1, sizeof(long long)); long long *parent_node = (long long *)calloc(vocab_size * 2 + 1, sizeof(long long));

其中,count數(shù)組中前vocab_size存儲的是每一個詞的對應(yīng)的詞頻,后面初始化的是很大的數(shù),已知詞庫中的詞是按照降序排列的,因此,構(gòu)建Huffman樹的過程如下所示(對于Huffman樹的原理,可以參見博文“數(shù)據(jù)結(jié)構(gòu)和算法——Huffman樹和Huffman編碼”):

首先,設(shè)置兩個指針pos1和pos2,分別指向最后一個詞和最后一個詞的后一位,從兩個指針?biāo)傅臄?shù)中選擇出最小的值,記為min1i,如pos1所指的值最小,此時,將pos1左移,再比較pos1和pos2所指的數(shù),選擇出最小的值,記為min2i,將他們的和存儲到pos2所指的位置。并將此時pos2所指的位置設(shè)置為min1i和min2i的父節(jié)點,同時,記min2i所指的位置的編碼為1,如下代碼所示:

// 設(shè)置父節(jié)點 parent_node[min1i] = vocab_size + a; parent_node[min2i] = vocab_size + a; binary[min2i] = 1;// 設(shè)置一個子樹的編碼為1

構(gòu)建好Huffman樹后,此時,需要根據(jù)構(gòu)建好的Huffman樹生成對應(yīng)節(jié)點的Huffman編碼。假設(shè),上述的數(shù)據(jù)生成的最終的Huffman樹為:

此時,count數(shù)組,binary數(shù)組和parent_node數(shù)組分別為:

在生成Huffman編碼的過程中,針對每一個詞(詞都在葉子節(jié)點上),從葉子節(jié)點開始,將編碼存入到code數(shù)組中,如對于上圖中的“R”節(jié)點來說,其code數(shù)組為{1,0},再對其反轉(zhuǎn)便是Huffman編碼:

vocab[a].codelen = i;// 詞的編碼長度 vocab[a].point[0] = vocab_size - 2; for (b = 0; b < i; b++) {vocab[a].code[i - b - 1] = code[b];// 編碼的反轉(zhuǎn)vocab[a].point[i - b] = point[b] - vocab_size;// 記錄的是從根結(jié)點到葉子節(jié)點的路徑 }

注意:這里的Huffman樹的構(gòu)建和Huffman編碼的生成過程寫得比較精簡。

3.3. 負(fù)樣本選中表的初始化

如果是采用負(fù)采樣的方法,此時還需要初始化每個詞被選中的概率。在所有的詞構(gòu)成的詞典中,每一個詞出現(xiàn)的頻率有高有低,我們希望,對于那些高頻的詞,被選中成為負(fù)樣本的概率要大點,同時,對于那些出現(xiàn)頻率比較低的詞,我們希望其被選中成為負(fù)樣本的頻率低點。這個原理于“輪盤賭”的策略一致(詳細(xì)可以參見“優(yōu)化算法——遺傳算法”)。在程序中,實現(xiàn)這部分功能的代碼為:

// 生成負(fù)采樣的概率表 void InitUnigramTable() {int a, i;double train_words_pow = 0;double d1, power = 0.75;table = (int *)malloc(table_size * sizeof(int));// int --> intfor (a = 0; a < vocab_size; a++) train_words_pow += pow(vocab[a].cn, power);// 類似輪盤賭生成每個詞的概率i = 0;d1 = pow(vocab[i].cn, power) / train_words_pow;for (a = 0; a < table_size; a++) {table[a] = i;if (a / (double)table_size > d1) {i++;d1 += pow(vocab[i].cn, power) / train_words_pow;}if (i >= vocab_size) i = vocab_size - 1;} }

在實現(xiàn)的過程中,沒有直接使用每一個詞的頻率,而是使用了詞的0.750.750.75次方。

4、多線程模型訓(xùn)練

以上的各個部分是為訓(xùn)練詞向量做準(zhǔn)備,即準(zhǔn)備訓(xùn)練數(shù)據(jù),構(gòu)建訓(xùn)練模型。在上述的初始化完成后,接下來就是根據(jù)不同的方法對模型進(jìn)行訓(xùn)練,在實現(xiàn)的過程中,作者使用了多線程的方法對其進(jìn)行訓(xùn)練。

4.1、多線程的處理

為了能夠?qū)ξ谋具M(jìn)行加速訓(xùn)練,在實現(xiàn)的過程中,作者使用了多線程的方法,并對每一個線程上分配指定大小的文件:

// 利用多線程對訓(xùn)練文件劃分,每個線程訓(xùn)練一部分的數(shù)據(jù) fseek(fi, file_size / (long long)num_threads * (long long)id, SEEK_SET);

注意:這邊的多線程分割方式并不能保證每一個線程分到的文件是互斥的。對于其中的原因,可以參見“Linux C 編程——多線程”。

這個過程可以通過下圖簡單的描述:

在實現(xiàn)多線程的過程中,作者并沒有加鎖的操作,而是對模型參數(shù)和詞向量的修改可以任意執(zhí)行,這一點類似于基于隨機(jī)梯度的方法,訓(xùn)練的過程與訓(xùn)練樣本的訓(xùn)練是沒有關(guān)系的,這樣可以大大加快對詞向量的訓(xùn)練。拋開多線程的部分,在每一個線程內(nèi)執(zhí)行的是對模型和詞向量的訓(xùn)練。

作者在實現(xiàn)的過程中,主要實現(xiàn)了兩個模型,即CBOW模型和Skip-gram模型,在每個模型中,又分別使用到了兩種不同的訓(xùn)練方法,即層次Softmax和Negative Sampling方法。

對于CBOW模型和Skip-gram模型的理解,首先必須知道統(tǒng)計語言模型(Statistic Language Model)

在統(tǒng)計語言模型中的核心內(nèi)容是:計算一組詞語能夠成為一個句子的概率。

為了能夠求解其中的參數(shù),一大批參數(shù)求解的方法被提出,在其中,就有word2vec中要使用的神經(jīng)概率語言模型。具體的神經(jīng)概率語言模型可以參見“”。

4.2. CBOW模型

CBOW模型和Skip-gram模型是神經(jīng)概率語言模型的兩種變形形式,其中,在CBOW模型中包含三層,即輸入層,映射層和輸出層。對于CBOW模型,如下圖所示:

在CBOW模型中,通過詞wtw_twt?的前后詞wt?2w_{t-2}wt?2?wt?1w_{t-1}wt?1?wt+1w_{t+1}wt+1?wt+2w_{t+2}wt+2?來預(yù)測當(dāng)前詞wtw_twt?。此處的窗口的大小window為2。

4.3.1. 從輸入層到映射層

首先找到每個詞對應(yīng)的詞向量,并將這些詞的詞向量相加,程序代碼如下所示:

// in -> hidden // 輸入層到映射層 cw = 0; for (a = b; a < window * 2 + 1 - b; a++) if (a != window) {c = sentence_position - window + a;// sentence_position表示的是當(dāng)前的位置// 判斷c是否越界if (c < 0) continue;if (c >= sentence_length) continue;last_word = sen[c];// 找到c對應(yīng)的索引if (last_word == -1) continue;for (c = 0; c < layer1_size; c++) neu1[c] += syn0[c + last_word * layer1_size];// 累加cw++; }

當(dāng)累加完窗口內(nèi)的所有的詞向量的之后,存儲在映射層neu1中,并取平均,程序代碼如下所示:

for (c = 0; c < layer1_size; c++) neu1[c] /= cw;// 計算均值

當(dāng)取得了映射層的結(jié)果后,此時就需要使用Hierarchical Softmax或者Negative Sampling對模型進(jìn)行訓(xùn)練。

4.3.2. Hierarchical Softmax

Hierarchical Softmax是word2vec中用于提高性能的一項關(guān)鍵的技術(shù)。由Hierarchical Softmax的原理可知,對于詞w,其對數(shù)似然函數(shù)為:

L(w)=∑j∈point{(1?djw)?log[σ(XwTθjw)]+djw?log[1?σ(XwTθjw)]}L\left ( w \right )=\sum _{j\in point}\left \{ \left ( 1-d_j^w \right )\cdot log\left [ \sigma \left ( X_w^T\theta _j^w \right ) \right ]+d_j^w\cdot log\left [ 1-\sigma \left ( X_w^T\theta _j^w \right ) \right ] \right \}L(w)=jpoint?{(1?djw?)?log[σ(XwT?θjw?)]+djw??log[1?σ(XwT?θjw?)]}

其中,jjj表示的詞w對應(yīng)的Huffman編碼中的每一個編碼的下標(biāo),djwd_j^wdjw?表示的是第jjj個Huffman編碼,且djw∈{0,1}d_j^w\in \left \{ 0,1 \right \}djw?{0,1},這是一個求和的過程,因此,對于Huffman編碼中的每一個編碼,上述的對數(shù)似然函數(shù)為:

L(w,j)=(1?djw)?log[σ(XwTθjw)]+djw?log[1?σ(XwTθjw)]L\left ( w,j \right )=\left ( 1-d_j^w \right )\cdot log\left [ \sigma \left ( X_w^T\theta _j^w \right ) \right ]+d_j^w\cdot log\left [ 1-\sigma \left ( X_w^T\theta _j^w \right ) \right ]L(w,j)=(1?djw?)?log[σ(XwT?θjw?)]+djw??log[1?σ(XwT?θjw?)]

在此,變量為XwX_wXw?θjw\theta _j^wθjw?,此時,分別對XwX_wXw?θjw\theta _j^wθjw?求偏導(dǎo)數(shù):

?L(w,j)?θjw=[1?djw?σ(XwTθjw)]Xw\frac{\partial L\left ( w,j \right )}{\partial \theta _j^w}=\left [ 1-d_j^w-\sigma \left ( X_w^T\theta _j^w \right ) \right ]X_w?θjw??L(w,j)?=[1?djw??σ(XwT?θjw?)]Xw?

?L(w,j)?Xw=[1?djw?σ(XwTθjw)]θjw\frac{\partial L\left ( w,j \right )}{\partial X_w}=\left [ 1-d_j^w-\sigma \left ( X_w^T\theta _j^w \right ) \right ]\theta _j^w?Xw??L(w,j)?=[1?djw??σ(XwT?θjw?)]θjw?

因此,對于θjw\theta _j^wθjw?的更新公式為:

θjw=θjw+η[1?djw?σ(XwTθjw)]Xw\theta _j^w=\theta _j^w+\eta \left [ 1-d_j^w-\sigma \left ( X_w^T\theta _j^w \right ) \right ]X_wθjw?=θjw?+η[1?djw??σ(XwT?θjw?)]Xw?

在word2vec源碼中,為了能夠加快計算,作者在開始的時候存儲了一份Sigmoid的值,因此,對于$\sigma \left ( X_w^T\theta _j^w \right ) $需要從expTable中查詢到對應(yīng)的值。

for (d = 0; d < vocab[word].codelen; d++) {// word為當(dāng)前詞// 計算輸出層的輸出f = 0;l2 = vocab[word].point[d] * layer1_size;// 找到第d個詞對應(yīng)的權(quán)重// Propagate hidden -> outputfor (c = 0; c < layer1_size; c++) f += neu1[c] * syn1[c + l2];// 映射層到輸出層if (f <= -MAX_EXP) continue;else if (f >= MAX_EXP) continue;else f = expTable[(int)((f + MAX_EXP) * (EXP_TABLE_SIZE / MAX_EXP / 2))];// Sigmoid結(jié)果// 'g' is the gradient multiplied by the learning rateg = (1 - vocab[word].code[d] - f) * alpha;// Propagate errors output -> hiddenfor (c = 0; c < layer1_size; c++) neu1e[c] += g * syn1[c + l2];// 修改映射后的結(jié)果// Learn weights hidden -> outputfor (c = 0; c < layer1_size; c++) syn1[c + l2] += g * neu1[c];// 修改映射層到輸出層之間的權(quán)重 }

對于窗口內(nèi)的詞的向量的更新,則是利用窗口內(nèi)的所有詞的梯度之和∑?L(w,j)?Xw\sum \frac{\partial L\left ( w,j \right )}{\partial X_w}?Xw??L(w,j)?來更新,如程序代碼所示:

// hidden -> in // 以上是從映射層到輸出層的修改,現(xiàn)在返回修改每一個詞向量 for (a = b; a < window * 2 + 1 - b; a++) if (a != window) {c = sentence_position - window + a;if (c < 0) continue;if (c >= sentence_length) continue;last_word = sen[c];if (last_word == -1) continue;// 利用窗口內(nèi)的所有詞向量的梯度之和來更新for (c = 0; c < layer1_size; c++) syn0[c + last_word * layer1_size] += neu1e[c]; }

4.3.3. Negative Sampling

與Hierarchical Softmax一致,Negative Sampling也是一種加速計算的方法,在Negative Sampling方法中使用的是隨機(jī)的負(fù)采樣,在CBOW模型中,已知詞www的上下文,需要預(yù)測詞www,對于給定的上下文,詞www即為正樣本,其他的樣本為負(fù)樣本,此時我們需要根據(jù)詞頻從剩下的詞中挑選出最合適的負(fù)樣本,實現(xiàn)的代碼如下所示:

// 標(biāo)記target和label if (d == 0) {// 正樣本target = word;label = 1; } else {// 選擇出負(fù)樣本next_random = next_random * (unsigned long long)25214903917 + 11;target = table[(next_random >> 16) % table_size];// 從table表中選擇出負(fù)樣本// 重新選擇if (target == 0) target = next_random % (vocab_size - 1) + 1;if (target == word) continue;label = 0; }

當(dāng)選擇出了正負(fù)樣本,此時的損失函數(shù)為:

l(w)=σ(XwTθw)?∏u∈NEG(w)[1?σ(XwTθu)]l\left ( w \right )=\sigma \left ( X_w^T\theta ^w \right )\cdot \prod _{u\in {NEG\left ( w \right )}}\left [ 1- \sigma \left ( X_w^T\theta ^u \right )\right ]l(w)=σ(XwT?θw)?uNEG(w)?[1?σ(XwT?θu)]

其對數(shù)似然函數(shù)為:

L=logl(w)=log∏u∈w∪NEG(w){[σ(XwTθu)]Lw(u)?[1?σ(XwTθu)]1?Lw(u)}L=log\; l\left ( w \right )=log\prod _{u\in {{w}\cup NEG\left ( w \right )}}\left \{ \left [ \sigma \left ( X_w^T\theta ^u \right ) \right ]^{L^w\left ( u \right )}\cdot \left [ 1- \sigma \left ( X_w^T\theta ^u \right )\right ]^{1-L^w\left ( u \right )} \right \}L=logl(w)=loguwNEG(w)?{[σ(XwT?θu)]Lw(u)?[1?σ(XwT?θu)]1?Lw(u)}

即為:

∑u∈w∪NEG(w){Lw(u)?log[σ(XwTθu)]+[1?Lw(u)]?log[1?σ(XwTθu)]}\sum _{u\in {{w}\cup NEG\left ( w \right )}}\left \{ {L^w\left ( u \right )}\cdot log\left [ \sigma \left ( X_w^T\theta ^u \right ) \right ]+\left [ 1-L^w\left ( u \right ) \right ]\cdot log \left [ 1- \sigma \left ( X_w^T\theta ^u \right )\right ] \right \}uwNEG(w)?{Lw(u)?log[σ(XwT?θu)]+[1?Lw(u)]?log[1?σ(XwT?θu)]}

取:

L(w,u)=Lw(u)?log[σ(XwTθu)]+[1?Lw(u)]?log[1?σ(XwTθu)]L\left ( w,u \right )={L^w\left ( u \right )}\cdot log\left [ \sigma \left ( X_w^T\theta ^u \right ) \right ]+\left [ 1-L^w\left ( u \right ) \right ]\cdot log \left [ 1- \sigma \left ( X_w^T\theta ^u \right )\right ]L(w,u)=Lw(u)?log[σ(XwT?θu)]+[1?Lw(u)]?log[1?σ(XwT?θu)]

在此,變量為XwX_wXw?θu\theta ^uθu,此時,分別對XwX_wXw?θjw\theta _j^wθjw?求偏導(dǎo)數(shù):

?L(w,u)?θu=[Lw(u)?σ(XwTθu)]Xw\frac{\partial L\left ( w,u \right )}{\partial \theta ^u}=\left [ L^w\left ( u \right )- \sigma \left ( X_w^T\theta ^u \right )\right ]X_w?θu?L(w,u)?=[Lw(u)?σ(XwT?θu)]Xw?

?L(w,u)?Xw=[Lw(u)?σ(XwTθu)]θu\frac{\partial L\left ( w,u \right )}{\partial X_w}=\left [ L^w\left ( u \right )- \sigma \left ( X_w^T\theta ^u \right )\right ]\theta ^u?Xw??L(w,u)?=[Lw(u)?σ(XwT?θu)]θu

因此,更新的代碼為:

if (f > MAX_EXP) g = (label - 1) * alpha; else if (f < -MAX_EXP) g = (label - 0) * alpha; else g = (label - expTable[(int)((f + MAX_EXP) * (EXP_TABLE_SIZE / MAX_EXP / 2))]) * alpha;for (c = 0; c < layer1_size; c++) neu1e[c] += g * syn1neg[c + l2]; for (c = 0; c < layer1_size; c++) syn1neg[c + l2] += g * neu1[c];

對詞向量的更新與Hierarchical Softmax中一致。

4.3. Skip-gram模型

而Skip-gram模型與CBOW正好相反,在Skip-gram模型中,則是通過當(dāng)前詞wtw_twt?來預(yù)測其前后詞wt?2w_{t-2}wt?2?wt?1w_{t-1}wt?1?wt+1w_{t+1}wt+1?wt+2w_{t+2}wt+2?,Skip-gram模型如下圖所示:

4.3.1. Hierarchical Softmax

由上述的分析,我們發(fā)現(xiàn),在Skip-gram模型中,其計算方法與CBOW模型很相似,不同的是,在Skip-gram模型中,需要使用當(dāng)前詞分別預(yù)測窗口中的詞,因此,這是一個循環(huán)的過程:

for (a = b; a < window * 2 + 1 - b; a++) if (a != window)

對于向量的更新過程與CBOW模型中的Hierarchical Softmax一致:

c = sentence_position - window + a; if (c < 0) continue; if (c >= sentence_length) continue; last_word = sen[c]; if (last_word == -1) continue; l1 = last_word * layer1_size; for (c = 0; c < layer1_size; c++) neu1e[c] = 0; // HIERARCHICAL SOFTMAX if (hs) for (d = 0; d < vocab[word].codelen; d++) {f = 0;l2 = vocab[word].point[d] * layer1_size;// Propagate hidden -> output// 映射層即為輸入層for (c = 0; c < layer1_size; c++) f += syn0[c + l1] * syn1[c + l2];if (f <= -MAX_EXP) continue;else if (f >= MAX_EXP) continue;else f = expTable[(int)((f + MAX_EXP) * (EXP_TABLE_SIZE / MAX_EXP / 2))];// 'g' is the gradient multiplied by the learning rateg = (1 - vocab[word].code[d] - f) * alpha;// Propagate errors output -> hiddenfor (c = 0; c < layer1_size; c++) neu1e[c] += g * syn1[c + l2];// Learn weights hidden -> outputfor (c = 0; c < layer1_size; c++) syn1[c + l2] += g * syn0[c + l1]; } // Learn weights input -> hidden for (c = 0; c < layer1_size; c++) syn0[c + l1] += neu1e[c];

4.3.2. Negative Sampling

與上述一致,在Skip-gram中與CBOW中的唯一不同是在Skip-gram中是循環(huán)的過程。代碼的實現(xiàn)類似與上面的Hierarchical Softmax。

注釋版的word2vec源碼已經(jīng)上傳到Github中:Github:word2vec.c

參考文獻(xiàn)

[1] word2vec 中的數(shù)學(xué)原理詳解(一)目錄和前言

[2] word2vec 中的數(shù)學(xué)原理詳解(二)預(yù)備知識

[3] word2vec 中的數(shù)學(xué)原理詳解(三)背景知識

[4] word2vec 中的數(shù)學(xué)原理詳解(四)基于 Hierarchical Softmax 的模型

[5] word2vec 中的數(shù)學(xué)原理詳解(五)基于 Negative Sampling 的模型

[6] word2vec 中的數(shù)學(xué)原理詳解(六)若干源碼細(xì)節(jié)

[7] 深度學(xué)習(xí)與自然語言處理(1)_斯坦福cs224d Lecture 1

[8] Google Word2vec 學(xué)習(xí)手札

[9] Deep Learning in NLP (一)詞向量和語言模型

[10] Neural Probabilistic Language Model, word2vec來龍去脈

[11] word2vec原理概述

[12] 自己動手寫word2vec (一):主要概念和流程

[13] The amazing power of word vectors

[14] word2vec前世今生

總結(jié)

以上是生活随笔為你收集整理的机器学习算法实现解析——word2vec源码解析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 免费成人黄色网 | 美痴女~美人上司北岛玲 | √8天堂资源地址中文在线 欧美精品在线一区二区 | 欧美日韩视频在线观看一区 | 成人福利在线播放 | av香蕉网 | 热99这里只有精品 | 国产无遮挡又黄又爽又色 | 怡春院一区二区 | а 天堂 在线 | 国产亚洲成av人在线观看导航 | 国产区精品 | 含羞草一区二区三区 | 18禁免费观看网站 | 日韩人妻精品一区二区三区 | av网页在线| 污视频在线免费观看 | 日批视频免费 | 天天操妹子 | 第一福利丝瓜av导航 | 亚洲欧美黄色片 | 婷婷色激情 | 一边摸上面一边摸下面 | 少妇喷潮明星 | 亚洲吧| 精品久久久久久久久久久久久久久 | 亚洲专区免费 | 日韩黄色影视 | av激情在线观看 | 在线中文字幕第一页 | 亚洲午夜一区二区 | 国产午夜精品免费一区二区三区视频 | 在线一区二区三区四区五区 | 狠狠操中文字幕 | 日本中文字幕影院 | 97视频一区 | 捆绑调教sm束缚网站 | 免费久久视频 | 精品黑人 | 少妇高潮网站 | 国产精品日韩欧美 | 精品国产一区二区三区四区阿崩 | 亚洲码欧美码一区二区三区 | 免费国产视频在线观看 | 亚洲视频成人 | 中文字幕一区二区三区乱码人妻 | 奇米影视四色777 | 99人妻少妇精品视频一区 | 日本午夜一区二区 | 亚洲一二三视频 | 亚洲欧美网站 | 精品国产乱码久久久久久浪潮 | 国产日本精品 | 在线观看h网站 | 老司机免费在线视频 | 99热在线观看精品 | 国产喷水福利在线视频 | 激情播播网| 久久久无码18禁高潮喷水 | 亚洲天堂资源 | 亚洲视频中文字幕在线观看 | 秋霞一级全黄大片 | 欧美做受69| 国产日韩网站 | 亚洲精品国产日韩 | 伊人婷婷色| 青青青国产在线 | 影音先锋久久 | 亚洲综合色av | 青青草五月天 | 美女久久 | 亚洲 欧美 自拍偷拍 | 欧美激情一级 | 超碰免费在 | 日本少妇videos高潮 | 欧美在线一二三区 | 中国一级片在线观看 | 华人色 | 朋友的姐姐2在线观看 | 亚洲成人aaa | 日韩天堂在线 | 久久调教视频 | 男女免费毛片 | 久久瑟瑟 | 欧美性视频在线播放 | 国内自拍偷拍网 | 黄网站免费大全入口 | 色爽 av| 中文字幕免费在线看线人动作大片 | 99久久99久久精品国产片桃花 | 精品成人一区二区三区久久精品 | 中国黄色网页 | 日本美女性爱视频 | 玖玖色资源 | 国产精品午夜久久 | 欧美日韩影院 | 国产三级aaa | 午夜成人在线视频 | 日韩激情电影在线 |