天天看點

深度學習word2vec筆記之基礎篇深度學習word2vec學習筆記.pdf

深度學習word2vec學習筆記.pdf

深度學習word2vec筆記之基礎篇

by 北流浪子

部落格位址:

http://blog.csdn.net/mytestmy/article/details/26969149

基礎篇:

http://blog.csdn.net/mytestmy/article/details/26961315

一.前言

伴随着深度學習的大紅大紫,隻要是在自己的成果裡打上deep learning字樣,總會有人去看。深度學習可以稱為當今機器學習領域的當之無愧的巨星,也特别得到工業界的青睐。

在各種大舉深度學習大旗的公司中,Google公司無疑是旗舉得最高的,口号喊得最響亮的那一個。Google正好也是網際網路界璀璨巨星,與深度學習的聯姻,就像影視巨星劉德華和林志玲的結合那麼光彩奪目。

巨星聯姻産生的成果自然是天生的寵兒。2013年末,Google釋出的word2vec工具引起了一幫人的熱捧,網際網路界大量google公司的粉絲們興奮了,進而google公司的股票開始大漲,如今直逼蘋果公司。

在大量贊歎word2vec的微網誌或者短文中,幾乎都認為它是深度學習在自然語言領域的一項了不起的應用,各種歡呼“深度學習在自然語言領域開始發力了”。

網際網路界很多公司也開始跟進,使用word2vec産出了不少成果。身為一個網際網路民工,有必要對這種炙手可熱的技術進行一定程度的了解。

好在word2vec也算是比較簡單的,隻是一個簡單三層神經網絡。在浏覽了多位大牛的部落格,随筆和筆記後,整理成自己的博文,或者說抄出來自己的博文。

二.背景知識

2.1詞向量

自然語言處理(NLP)相關任務中,要将自然語言交給機器學習中的算法來處理,通常需要首先将語言數學化,因為機器不是人,機器隻認數學符号。向量是人把自然界的東西抽象出來交給機器處理的東西,基本上可以說向量是人對機器輸入的主要方式了。

詞向量就是用來将語言中的詞進行數學化的一種方式,顧名思義,詞向量就是把一個詞表示成一個向量。

主要有兩種表示方式,下面分别介紹,主要參考了@皮果提在知乎上的問答,也就是參考文獻【2】。

2.1.1 O

ne-Hot Representation

一種最簡單的詞向量方式是 one-hot representation,就是用一個很長的向量來表示一個詞,向量的長度為詞典的大小,向量的分量隻有一個 1,其他全為 0, 1 的位置對應該詞在詞典中的位置。舉個例子,

  “話筒”表示為 [0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 ...]

  “麥克”表示為 [0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 ...]

每個詞都是茫茫 0 海中的一個 1。

這種 One-hot Representation 如果采用稀疏方式存儲,會是非常的簡潔:也就是給每個詞配置設定一個數字 ID。比如剛才的例子中,話筒記為 3,麥克記為 8(假設從 0 開始記)。如果要程式設計實作的話,用 Hash 表給每個詞配置設定一個編号就可以了。這麼簡潔的表示方法配合上最大熵、SVM、CRF 等等算法已經很好地完成了 NLP 領域的各種主流任務。

但這種詞表示有兩個缺點:(1)容易受維數災難的困擾,尤其是将其用于 Deep Learning 的一些算法時;(2)不能很好地刻畫詞與詞之間的相似性(術語好像叫做“詞彙鴻溝”):任意兩個詞之間都是孤立的。光從這兩個向量中看不出兩個詞是否有關系,哪怕是話筒和麥克這樣的同義詞也不能幸免于難。

是以會尋求發展,用另外的方式表示,就是下面這種。

2.1.2 Distributed Representation

另一種就是Distributed Representation 這種表示,它最早是 Hinton 于 1986 年提出的,可以克服 one-hot representation 的缺點。其基本想法是直接用一個普通的向量表示一個詞,這種向量一般長成這個樣子:[0.792, −0.177, −0.107, 0.109, −0.542, ...],也就是普通的向量表示形式。次元以 50 維和 100 維比較常見。

當然一個詞怎麼表示成這麼樣的一個向量是要經過一番訓練的,訓練方法較多,word2vec是其中一種,在後面會提到,這裡先說它的意義。還要注意的是每個詞在不同的語料庫和不同的訓練方法下,得到的詞向量可能是不一樣的。

詞向量一般維數不高,很少有人閑着沒事訓練的時候定義一個10000維以上的維數,是以用起來維數災難的機會現對于one-hot representation表示就大大減少了。

由于是用向量表示,而且用較好的訓練算法得到的詞向量的向量一般是有空間上的意義的,也就是說,将所有這些向量放在一起形成一個詞向量空間,而每一向量則為該空間中的一個點,在這個空間上的詞向量之間的距離度量也可以表示對應的兩個詞之間的“距離”。所謂兩個詞之間的“距離”,就是這兩個詞之間的文法,語義之間的相似性。

一個比較爽的應用方法是,得到詞向量後,假如對于某個詞A,想找出這個詞最相似的詞,這個場景對人來說都不輕松,畢竟比較主觀,但是對于建立好詞向量後的情況,對計算機來說,隻要拿這個詞的詞向量跟其他詞的詞向量一一計算歐式距離或者cos距離,得到距離最小的那個詞,就是它最相似的。

這樣的特性使得詞向量很有意義,自然就會吸引比較多的人去研究,前有Bengio發表在JMLR上的論文《A Neural Probabilistic Language Model》,又有Hinton的階層化Log-Bilinear模型,還有google的Tomas Mikolov 團隊搞的word2vec,等等。

詞向量在機器翻譯領域的一個應用,就是google的Tomas Mikolov 團隊開發了一種詞典和術語表的自動生成技術,該技術通過向量空間,把一種語言轉變成另一種語言,實驗中對英語和西班牙語間的翻譯準确率高達90%。

介紹算法工作原理的時候舉了一個例子:考慮英語和西班牙語兩種語言,通過訓練分别得到它們對應的詞向量空間 E 和 S。從英語中取出五個詞 one,two,three,four,five,設其在 E 中對應的詞向量分别為 v1,v2,v3,v4,v5,為友善作圖,利用主成分分析(PCA)降維,得到相應的二維向量 u1,u2,u3,u4,u5,在二維平面上将這五個點描出來,如下圖左圖所示。類似地,在西班牙語中取出(與 one,two,three,four,five 對應的) uno,dos,tres,cuatro,cinco,設其在 S 中對應的詞向量分别為 s1,s2,s3,s4,s5,用 PCA 降維後的二維向量分别為 t1,t2,t3,t4,t5,将它們在二維平面上描出來(可能還需作适當的旋轉),如下圖右圖所示:

觀察左、右兩幅圖,容易發現:五個詞在兩個向量空間中的相對位置差不多,這說明兩種不同語言對應向量空間的結構之間具有相似性,進而進一步說明了在詞向量空間中利用距離刻畫詞之間相似性的合理性。

2.2語言模型

2.2.1基本概念

語言模型其實就是看一句話是不是正常人說出來的。這玩意很有用,比如機器翻譯、語音識别得到若幹候選之後,可以利用語言模型挑一個盡量靠譜的結果。在 NLP 的其它任務裡也都能用到。

語言模型形式化的描述就是給定一個T個詞的字元串s,看它是自然語言的機率 P(w1,w2,…,wt)。w1 到 wT 依次表示這句話中的各個詞。有個很簡單的推論是:

ps=pw1,w2,⋯wT=pw1pw2w1)p(w3|w1,w2)⋯p(wt|w1,w2,⋯wT-1) (1)

上面那個機率表示的意義是:第一個詞确定後,看後面的詞在前面的詞出現的情況下出現的機率。如一句話“大家喜歡吃蘋果”,總共四個詞“大家”,“喜歡”,“吃”,“蘋果”,怎麼分詞現在不讨論,總之詞已經分好,就這四個。那麼這句話是一個自然語言的機率是:

P(大家,喜歡,吃,蘋果)=p(大家)p(喜歡|大家)p(吃|大家,喜歡)p(蘋果|大家,喜歡,吃)

p(大家)表示“大家”這個詞在語料庫裡面出現的機率;

p(喜歡|大家)表示“喜歡”這個詞出現在“大家”後面的機率;

p(吃|大家,喜歡)表示“吃”這個詞出現在“大家喜歡”後面的機率;

p(蘋果|大家,喜歡,吃)表示“蘋果”這個詞出現在“大家喜歡吃”後面的機率。

把這些機率連乘起來,得到的就是這句話平時出現的機率。

如果這個機率特别低,說明這句話不常出現,那麼就不算是一句自然語言,因為在語料庫裡面很少出現。如果出現的機率高,就說明是一句自然語言。

看到了上面的計算,看有多麻煩:隻有四個詞的一句話,需要計算的是p(大家),p(喜歡|大家),p(吃|大家,喜歡),p(蘋果|大家,喜歡,吃)這四個機率,這四個機率還要預先計算好,考慮詞的數量,成千上萬個,再考慮組合數,p(吃|大家,喜歡)這個有“大家”、“喜歡”和“吃”的組合,總共會上億種情況吧;再考慮p(蘋果|大家,喜歡,吃)這個機率,總共也會超過萬億種。

從上面的情況看來,計算起來是非常麻煩的,一般都用偷懶的方式。

為了表示簡單,上面的公式(1)用下面的方式表示

ps=pw1,w2,⋯wT=i=1Tp(wi|Contexti)

其中,如果Contexti是空的話,就是它自己p(w),另外如“吃”的Context就是“大家”、“喜歡”,其餘的對号入座。

符号搞清楚了,就看怎麼偷懶了。

2.2.2 N-gram模型

接下來說怎麼計算p(wi|Contexti),上面看的是跟據這句話前面的所有詞來計算,那麼p(wi|Contexti)就得計算很多了,比如就得把語料庫裡面p(蘋果|大家,喜歡,吃)這種情況全部統計一遍,那麼為了計算這句話的機率,就上面那個例子,都得掃描四次語料庫。這樣一句話有多少個詞就得掃描多少趟,語料庫一般都比較大,越大的語料庫越能提供準确的判斷。這樣的計算速度在真正使用的時候是萬萬不可接受的,線上掃描一篇文章是不是一推亂七八糟的沒有序列的文字都得掃描很久,這樣的應用根本沒人考慮。

最好的辦法就是直接把所有的p(wi|Contexti)提前算好了,那麼根據排列組上面的來算,對于一個隻有四個詞的語料庫,總共就有4!+3!+2!+1!個情況要計算,那就是24個情況要計算;換成1000個詞的語料庫,就是i=11000i!個情況需要統計,對于計算機來說,計算這些東西簡直是開玩笑。

這就誕生了很多偷懶的方法,N-gram模型是其中之一了。N-gram什麼情況呢?上面的context都是這句話中這個詞前面的所有詞作為條件的機率,N-gram就是隻管這個詞前面的n-1個詞,加上它自己,總共n個詞,計算p(wi|Contexti)隻考慮用這n個詞來算,換成數學的公式來表示,就是

pwiContexti=p(wi|wi-n+1,wi-n+2,⋯,wi-1)

這裡如果n取得比較小的話,就比較省事了,當然也要看到n取得太小,會特别影響效果的,有可能計算出來的那個機率很不準。怎麼平衡這個效果和計算就是大牛們的事情了,據大牛們的核算,n取2效果都還湊合,n取3就相當不錯了,n取4就頂不住了。看下面的一些資料,假設詞表中詞的個數 |V| = 20,000 詞,那麼有下面的一些資料。

照圖中的資料看去,取n=3是目前計算能力的上限了。在實踐中用的最多的就是bigram和trigram了,而且效果也基本夠了。

N-gram模型也會有寫問題,總結如下:

1、n不能取太大,取大了語料庫經常不足,是以基本是用降級的方法

2、無法模組化出詞之間的相似度,就是有兩個詞經常出現在同一個context後面,但是模型是沒法展現這個相似性的。

3、有些n元組(n個詞的組合,跟順序有關的)在語料庫裡面沒有出現過,對應出來的條件機率就是0,這樣一整句話的機率都是0了,這是不對的,解決的方法主要是兩種:平滑法(基本上是分子分母都加一個數)和回退法(利用n-1的元組的機率去代替n元組的機率)

2.2.3N-pos模型

當然學術是無止境的,有些大牛覺得這還不行,因為第i個詞很多情況下是條件依賴于它前面的詞的文法功能的,是以又弄出來一個n-pos模型,n-pos模型也是用來計算p(wi|Contexti)的,但是有所改變,先對詞按照詞性(Part-of-Speech,POS)進行了分類,具體的數學表達是

pwiContexti=pwi|c(wi-n+1),c(wi-n+2),⋯,c(wi-1)

其中c是類别映射函數,功能是把V個詞映射到K個類别(1=

其他的模型還很多,不一一介紹了。

2.2.4模型的問題與目标

如果是原始的直接統計語料庫的語言模型,那是沒有參數的,所有的機率直接統計就得到了。但現實往往會帶一些參數,所有語言模型也能使用極大似然作為目标函數來建立模型。下面就讨論這個。

假設語料庫是一個由T個詞組成的詞序列s(這裡可以保留疑問的,因為從很多資料看來是不管什麼多少篇文檔,也不管句子什麼的,整個語料庫就是一長串詞連起來的,或許可以根據情況拆成句子什麼的,這裡就往簡單裡說),其中有V個詞,則可以建構下面的極大似然函數

L=i=1TpwiContexti

另外,做一下對數似然

l=logL=1Vi=1TlogpwiContexti

對數似然還有些人稱為交叉熵,這裡不糾結也不介紹。

上面的問題跟正常的情況不太符合,來看看下一種表達。假設語料庫是有S個句子組成的一個句子序列(順序不重要),同樣是有V個詞,似然函數就會建構成下面的樣子

L=jSij=1TjpwijContextij

對數似然就會是下面的樣子

l=logL=1Vj=1Sij=1TjlogpwijContextij

有意向的同學可以擴充到有文檔的樣子,這裡就不介紹了。

為啥要注意這個問題呢?原因有多種,計算pwiContexti這個東西的參數是主要的原因。

為啥會有參數呢?在計算pwiContexti這個東西的過程中,有非常多的方法被開發出來了,如上面的平滑法,回退法上面的,但這些都是硬統計一下基本就完了;這就帶來一些需要求的參數,如平滑法中使用的分子分母分别加上的常數是什麼?

這還不夠,假如用的是trigram,還得存儲一個巨大的元組與機率的映射(如果不存儲,就得再進行使用的時候實際統計,那太慢了),存這個東西可需要很大的記憶體,對計算機是個大難題。

這都難不倒大牛們,他們考慮的工作是利用函數來拟合計算pwiContexti,換句話說,pwiContexti不是根據語料庫統計出來的,而是直接把context和wi代到一個函數裡面計算出來的,這樣在使用的時候就不用去查那個巨大的映射集了(或者取語料庫裡面統計這個機率)。用數學的方法描述就是

pwiContexti=f(wi,Contexti;θ)

這樣的工作也展現了科學家們的價值——這幫人終于有點東西可以忙了。

那麼探索這個函數的具體形式就是主要的工作了,也是後面word2vec的工作的主要内容。函數的形式實在太多了,線性的還好,非線性真叫一個多,高維非線性的就更多了。

探索一個函數的具體形式的術語叫做拟合。

然後就有人提出了用神經網絡來拟合這個函數,就有了各種方法,word2vec是其中的一種。

緻謝

多位Google公司的研究員無私公開的資料。

多位部落客的部落格資料。

參考文獻

[1]

http://techblog.youdao.com/?p=915

Deep Learning實戰之word2vec,網易有道的pdf

[2]

http://www.zhihu.com/question/21714667/answer/19433618

@皮果提在知乎上的問答

[3]

http://www.zhihu.com/question/21661274/answer/19331979

@楊超在知乎上的問答《Word2Vec的一些了解》

[4] 第五章 n-gram語言模型 百度文庫上的一個資料

[5] 主題:統計自然語言處理的數學基礎 百度文庫上的一個資料

深度學習word2vec筆記之算法篇

在看word2vec的資料的時候,經常會被叫去看那幾篇論文,而那幾篇論文也沒有系統地說明word2vec的具體原理和算法,這樣看資料就沒有得到應有的效果。

為了節省看無用資料的時間,就整理了一個筆記,希望能幫助各位盡快了解word2vec的基本原理,避免浪費時間。

當然如果已經了解了,就随便看看得了。

一. CBOW加層次的網絡結構與使用說明

Word2vec總共有兩種類型,每種類型有兩個政策,總共4種。這裡先說最常用的一種。這種的網絡結構如下圖。

其中第一層,也就是最上面的那一層可以稱為輸入層。輸入的是若幹個詞的詞向量(詞向量的意思就是把一個詞表示成一個向量的形式表達,後面會介紹)。中間那個層可以成為隐層,是輸入的若幹個詞向量的累加和,注意是向量的累加和,結果是一個向量。

第三層是方框裡面的那個二叉樹,可以稱之為輸出層,隐層的那個節點要跟輸出層的那個二叉樹的所有非葉節點連結的,線太多畫不過來了。第三層的這個二叉樹是一個霍夫曼樹,每個非葉節點也是一個向量,但是這個向量不代表某個詞,代表某一類别的詞;每個葉子節點代表一個詞向量,為了簡單隻用一個w表示,沒有下标。另外要注意的是,輸入的幾個詞向量其實跟這個霍夫曼樹中的某幾個葉子節點是一樣的,當然輸入的那幾個詞跟它們最終輸出的到的那個詞未必是同一個詞,而且基本不會是同一個詞,隻是這幾個詞跟輸出的那個詞往往有語義上的關系。

還有要注意的是,這個霍夫曼樹的所有葉子節點就代表了語料庫裡面的所有詞,而且是每個葉子節點對應一個詞,不重複。

這個網絡結構的功能是為了完成一個的事情——判斷一句話是否是自然語言。怎麼判斷呢?使用的是機率,就是計算一下這句話的“一列詞的組合”的機率的連乘(聯合機率)是多少,如果比較低,那麼就可以認為不是一句自然語言,如果機率高,就是一句正常的話。這個其實也是語言模型的目标。前面說的“一列詞的組合”其實包括了一個詞跟它的上下文的聯合起來的機率,一種普通的情況就是每一個詞跟它前面所有的詞的組合的機率的連乘,這個後面介紹。

對于上面的那個網絡結構來說,網絡訓練完成後,假如給定一句話s,這句話由詞w1,w2,w3,…,wT組成,就可以利用計算這句話是自然語言的機率了,計算的公式是下面的公式

其中的Context表示的是該詞的上下文,也就是這個詞的前面和後面各若幹個詞,這個“若幹”(後面簡稱c)一般是随機的,也就是一般會從1到5之間的一個随機數;每個p(wi|Contexti)代表的意義是前後的c個詞分别是那幾個的情況下,出現該詞的機率。舉個例子就是:“大家 喜歡 吃 好吃 的 蘋果”這句話總共6個詞,假設對“吃”這個詞來說c随機抽到2,則“吃”這個詞的context是“大家”、“喜歡”、“好吃”和“的”,總共四個詞,這四個詞的順序可以亂,這是word2vec的一個特點。

計算p(wi|Contexti)的時候都要用到上面的那個網絡,具體計算的方法用例子說明,假設就是計算“吃”這個詞的在“大家”、“喜歡”、“好吃”和“的”這四個詞作為上下文的條件機率,又假設“吃”這個詞在霍夫曼樹中是的最右邊那一個葉子節點(在下面的圖中的節點R),那麼從根節點到到達它就有兩個非葉節點,根節點對應的詞向量命名為A,根節點的右孩子節點對應的詞向量命名為B,另外再假設“大家”、“喜歡”、“好吃”和“的”這四個詞的詞向量的和為C,則

p吃Context吃=(1-σA∙C)∙(1-σB∙C)

其中σx=1/(1+e-x),是sigmoid公式。

要注意的是,如果“吃”這個詞在非葉節點B的左孩子節點(假設稱為E)的右邊的那個葉子節點(在下面的圖中的節點S),也就是在圖中右邊的三個葉子的中間那個,則有

p吃Context吃=(1-σA∙C)∙σB∙C∙(1-σE∙C)

上面的那句話的每個詞都計算p(wi|Contexti)後連乘起來得到聯合機率,這個機率如果大于某個門檻值,就認為是正常的話;否則就認為不是自然語言,要排除掉。

對于這個神經網絡的描述索然無味,因為主角也不是這個機率,這個神經網絡最重要的是輸出層的那個霍夫曼樹的葉子節點上的那些向量,那些向量被稱為詞向量,詞向量就是另外一篇博文裡面介紹的,是個好東西。

怎麼得到這些詞向量更加是一個重要的過程,也是word2vec這整個算法最重要的東西,後面會認真介紹。

至于霍夫曼樹,其實是一個優化的解法,後面再提。

二.優化目标與解問題

2.1從霍夫曼樹到條件機率的計算

前面已經提過語言模型的目标就是判斷一句話是否是正常的,至于怎麼判斷則需要計算很多條件機率如p(wi|Contexti),然後還要把這些條件機率連乘起來得到聯合機率。這樣就帶來了問題了——怎麼去計算p(wi|Contexti),有很多辦法的,後面的章節會介紹。這裡的word2vec的計算這個條件機率的方法是利用神經網絡的能量函數,因為在能量模型中,能量函數的功能是把神經網絡的狀态轉化為機率表示,這在另外一篇博文RBM裡面有提到,具體要看hinton的論文來了解了。能量模型有個特别大的好處,就是能拟合所有的指數族的分布。那麼,如果認為這些條件機率是符合某個指數族的分布的話,是可以用能量模型去拟合的。總之word2vec就認為p(wi|Contexti)這個條件機率可以用能量模型來表示了。

既然是能量模型,那麼就需要能量函數,word2vec定義了一個非常簡單的能量函數

EA,C=-(A∙C)

其中A可以認為是某個詞的詞向量,C是這個詞的上下文的詞向量的和(向量的和),基本上就可以認為C代表Context;中間的點号表示兩個向量的内積。

然後根據能量模型(這個模型假設了溫度一直是1,是以能量函數沒有分母了),就可以表示出詞A的在上下文詞向量C下的機率來了

pAC=e-E(A,C)v=1Ve-E(wv,C) (2.1.2)

其中V表示語料庫裡面的的詞的個數,這個定義的意思是在上下文C出現的情況下,中間這個詞是A的機率,為了計算這個機率,肯定得把語料庫裡面所有的詞的能量都算一次,然後再根據詞A的能量,那個比值就是出現A的機率。這種計算機率的方式倒是能量模型裡面特有的,這個定義在論文《Hierarchical Probabilistic Neural Network Language Model》裡面,這裡拿來改了個形式。

這個機率其實并不好統計,為了算一個詞的的機率,得算上這種上下文的情況下所有詞的能量,然後還計算指數值再加和。注意那個分母,對語料庫裡面的每個詞,分母都要算上能量函數,而且再加和,假如有V個詞彙,整個語料庫有W個詞,那麼一輪疊代中光計算分母就有WVD個乘法,如果詞向量次元是D的話。比如,語料庫有100000000個詞,詞彙量是10000,計算100維的詞向量,一輪疊代要1014次乘法,計算機計算能力一般是109每秒,然後一輪疊代就要跑100000秒,大約27小時,一天多吧。1000輪疊代就三年了。

這時候科學家們的作用又展現了,假如把語料庫的所有詞分成兩類,分别稱為G類和H類,每類一半,其中詞A屬于G類,那麼下面的式子就可以成立了

pAC=p(A|G,C)p(G|C) (2.1.3)

這個式子的的含義算明确的了,詞A在上下文C的條件下出現的機率,與後面的這個機率相等——在上下文C的條件下出現了G類詞,同時在上下文為C,并且應該出現的詞是G類詞的條件下,詞A出現的機率。

列出這麼一個式子在論文《Hierarchical Probabilistic Neural Network Language Model》裡面也有個證明的,看原始的情況

PY=yX=x=P(Y=y|D=dy,X)P(D=d(y)|X=x)

其中d是一個映射函數,把Y裡面的元素映射到詞的類别D裡面的元素。還有個證明

PYX=iPY,D=iX=iPYD=i,XPD=iX =P(Y|D=dY,X)P(D=d(Y)|X)

式子(2.1.3)說明了一個問題,計算一個詞A在上下文C的情況下出現的機率,可以先對語料庫中的詞分成兩簇,然後能節省計算。現在來展示一下怎麼節省計算,假設G,H這兩類的簇就用G和H表示(G和H也是一個詞向量,意思就是G表示了其中一簇的詞,H表示了另外一簇的詞,G和H隻是一個代表,也不是什麼簇中心的說法。其實如果情況極端點,隻有兩個詞,看下面的式子就完全沒問題了。在多個詞的情況下,就可以認為詞被分成了兩團,G和H個表示一團的詞,計算機率什麼的都以一整團為機關),那麼式子(2.3)中的p(G|C)可以用下面的式子計算

pGC=e-E(G,C)e-E(G,C)+e-E(H,C)=11+e-(-(H-G)∙C)=11+e-E(H-G,C)

也就是說,可以不用關心這兩個簇用什麼表示,隻要利用一個F=H-G的類詞向量的一個向量就可以計算P(G|C)了,是以這一步是很節省時間的。再看另外一步

pAG,C=e-E(A,C)W∈Ge-E(W,C)

由于在G内的詞數量隻有V/2個,也就是說計算分母的時候隻要計算V/2個詞的能量就可以了。這已經省了一半的計算量了,可惜科學家們是貪得無厭的,是以還要繼續省,怎麼來呢?把G類詞再分成兩個簇GG,GH,A在GH裡面,然後

pAG,C=p(A|GH,G,C)p(GH|G,C)

同樣有

pGHG,C=11+e-E(GG-GH,C)

pAGH,G,C=e-E(A,C)W∈GHe-E(W,C)

同樣可以把GG-GH用一個類詞向量表達,這時候

pAC=p(A|GH,G,C)p(GH|G,C)p(G|C)

繼續下去假設繼續分到GHG簇的時候隻剩兩個詞了,再分兩簇為GHGG和GHGH,其中的簇GHGG就隻有一個詞A,那麼pAC可以用下面的式子算

pAC=pAGHGG,GHG,GH,G,Cp(GHGG|GHG,GH,G,C)p(GHG|GH,G,C)p(GH|G,C)p(G|C)

其中p(A|GHGG,GHG,GH,G)是1,因為隻有一個單詞,代到公式(2.2)就可以得到,那麼就有

pAC=p(GHGG|GHG,GH,G,C)p(GHG|GH,G,C)p(GH|G,C)p(G|C)

也就是

pAC=11+e-E(GHH-GHG,C)∙11+e-E(GG-GH,C)∙11+e-E(H-G,C)

假設再令FFF=GHH-GHG,FF=GG-GH,F=H-G,那麼p(A|C)隻要算這三個詞與上下文C的能量函數了,确實比原來的要節省很多計算的。

對于上面的霍夫曼樹來說假設G表示向右,H表示向左,那麼A就是從右邊開始數的第二個葉子節點,就是圖中右邊的三個W的中間那個。那麼F,FF,FFF就是這個葉子節點路徑上的三個非葉節點。

但是一個詞總是會一會向左,一會向右的,也就是在根節點那裡,一會是p(G|C)那麼F=H-G,一會又是p(H|C)那麼F=G-H,如果F在每個節點都是唯一一個值,就可以直接用一次詞向量表示這個非葉節點了。這下難不倒科學家的,令F一直是等于H-G,那麼一直有

pHC=11+e-E(F,C)

并且有p(G|C)=1-p(H|C)。

這樣每個非葉節點就可以用唯一一個詞向量表示了。

看到這裡,總該明白為啥p(A|C)要這麼算了吧。再換種情況,上面的機率p吃Context吃這個機率的計算方法是不是也是同樣的道理?

總結下來,p(wi|Contexti)可以用下面的公式計算了

pwContext=k=1Kp(dk|qk,C)=k=1Kσ(qk∙C)1-dk∙(1-σ(qk∙C))dk

其中C表示上下文的詞向量累加後的向量,qk表示從根節點下來到葉子節點的路徑上的那些非葉節點,dk就是編碼了,也可以說是分類,因為在霍夫曼樹的每個非葉節點都隻有兩個孩子節點,那可以認為當wi在這個節點的左子樹的葉子節點上時dk=0,否則dk=1。這樣的話每個詞都可以用一組霍夫曼編碼來表示,就有了上面的那個式子中間的那個dk,整個pwContext就可以用霍夫曼樹上的若幹個非葉節點和詞w的霍夫曼編碼來計算了。

看到這務必想明白,因為開始要讨論怎麼訓練了。

2.1.1霍夫曼樹

上面輸出層的二叉樹是霍夫曼樹,其實并沒有要求是霍夫曼樹,随便一個不太離譜的二叉樹都可以的,但是用霍夫曼樹能達到最優的計算效果。

根據之前的讨論,已經知道了語料庫裡面每個詞都要從根節點下來,一直走到葉子節點,每經過一個非葉節點,就要計算一個sigmoid函數。

随便亂分也能達到效果,但是資訊熵理論給出了最優的方案——霍夫曼樹。具體可以檢視其它資料。

2.2目标函數

假設語料庫是有S個句子組成的一個句子序列(順序不重要),整個語料庫有V個詞,似然函數就會建構成下面的樣子

L(θ)=jSij=1TjpwijContextij

(2.2.1)

其中Tj表示第j個句子的詞個數,極大似然要對整個語料庫去做的。對數似然就會是下面的樣子

l(θ)=logL(θ)=1Vj=1Sij=1TjlogpwijContextij

(2.2.2)

如果前面有個1/V,對數似然還有些人稱為交叉熵,這個具體也不了解,就不介紹了;不用1/V的話,就是正常的極大似然的樣子。

但是對于word2vec來說,上面的似然函數得改改,變成下面的樣子

Lθ=jSij=1TjpwijContextij=jSij=1Tjkij=1Kijσ(qkij∙Cij)1-dkij∙1-σ(qkij∙Cij)dkij

其中的Cij表示上下文相加的那個詞向量。對數似然就是下面的

lθ=logLθ=j=1Sij=1Tjkij=1Kijlogσqkij∙Cij1-dkij∙1-σqkij∙Cijdkij=j=1Sij=1Tjkij=1Kij1-dkijlogσqkij∙Cij+dkijlog1-σqkij∙Cij

這裡就不要1/V了。

這個看起來應該比較熟悉了,很像二分類的機率輸出的邏輯回歸——logistic regression模型。沒錯了,word2vec就是這麼考慮的,把在霍夫曼樹向左的情況,也就是dk=0的情況認為是正類,向右就認為是負類(這裡的正負類隻表示兩種類别之一)。這樣每當出現了一個上下文C和一個詞在左子樹的情況,就認為得到了一個正類樣本,否則就是一個負類樣本,每個樣本的屬于正類的機率都可以用上面的參數算出來,就是σ(qijk∙Contextij),如果是向右的話,就用1-σ(qijk∙Contextij)計算其機率。注意每個詞可以産生多個樣本,因為從霍夫曼樹的根節點開始,每個葉子節點都産生一個樣本,這個樣本的label(也就是屬于正類或者負類标志)可以用霍夫曼編碼來産生,前面說過了,向左的霍夫曼編碼dk=0,是以很自然地可以用1-dk表示每個樣本label。

在這裡,霍夫曼編碼也變成了一個重要的東西了。

這樣就好多了,問題到這也該清楚了,上面那個lθ就是對數似然,然後負對數似然f=-lθ就是需要最小化的目标函數了。

2.3解法

解法選用的是SGD,博文《線上學習算法FTRL》中說過SGD算法的一些情況。具體說來就是對每一個樣本都進行疊代,但是每個樣本隻影響其相關的參數,跟它無關的參數不影響。對于上面來說,第j個樣本的第ij個詞的負對數似然是

fij=pwijCij=-kij=1Kij1-dkijlogσqkij∙Cij+dkijlog1-σqkij∙Cij

第j個樣本的第ij個詞的在遇到第kij個非葉節點時的負對數似然是

fkij=-1-dkijlogσqkij∙Cij-dkijlog1-σqkij∙Cij

計算fkij的梯度,注意參數包括qkij和Cij,其中Cij的梯度是用來計算wij的時候用到。另外需要注意的是logσ(x)的梯度是1-σ(x),log1-σ(x)的梯度是-σ(x),

Fqqkij=∂fkij∂qkij=-1-dkij∙1-σqkij∙Cij∙Cij-dkij∙-σqkij∙Cij∙Cij=-1-dkij-σqkij∙Cij∙Cij

Fcqkij=∂fkij∂Cij=-1-dkij∙1-σqkij∙Cij∙qkij-dkij∙-σqkij∙Cij∙qkij=-1-dkij-σqkij∙Cij∙qkij

上面的Fq和Fc隻是簡寫,有了梯度就可以對每個參數進行疊代了

qkijn+1=qkijn-ηFqqkijn

同時,每個詞的詞向量也可以進行疊代了

wIn+1=wIn-ηkij=1KijFcqkijn

注意第二個疊代的wI是代表所有的輸入詞的,也就是假如輸入了4個詞,這四個詞都要根據這個方式進行疊代(注意是上下文的詞才是輸入詞,才根據梯度更新了,但是wij這個詞本身不更新的,就是輪到它自己在中間的時候就不更新了)。第二個疊代式确實不好了解,因為這裡的意思是所有非葉節點上的對上下文的梯度全部加和就得到了這個詞的上下文的梯度,看起來這個就是BP神經網絡的誤差反向傳播。

論文《Hierarchical Probabilistic Neural Network Language Model》和《Three New Graphical Models for Statistical Language Modelling》中看起來也是這麼樣的解釋,人家都是Context的幾個詞首尾連接配接得到的一個向量,對這個長向量有一個梯度,或者一個超大的V*m矩陣(m是詞向量的次元),對這個矩陣每個元素有一個梯度,這些梯度自然也包括了輸入詞的梯度。

對于這個疊代式,csdn有一位網名為@LJZLJZ1994的同學給出了他的解釋,說得很好,直接原話引用:就是用對C的偏導疊代我覺得可以這麼了解,這個關于C的偏導既是關于hidden layer的偏導(就是2c個向量相加平均),同時也是關于input layer裡各個詞向量的偏導(不太準确,還要再乘上一個常數),這是因為hidden上的activation function是線性的,而且input到hidden的線性變換的各個權重都是(1/2c),考慮一下求偏導的鍊式法則就行了。

非常感謝這位熱心的同學,就以此作為該疑問的答案,如有其它解釋,請告知,本人再綜合。

2.4代碼中的trick

如前文,文字的c表示左右各取多少個詞(注意跟代碼中的不同),代碼中b是一個從0到window-1的一個數,是對每個詞都随機生成的,而這個window就是使用者自己輸入的一個變量,預設值是5(這裡得換換概念了,代碼中的c代表目前處理到了那個詞的下标,實際的随機變量是b)。代碼實際實作的時候是換了一種方法首先生成一個0到window-1的一個數b,然後訓練的詞(假設是第i個詞,代碼中的變量是sentence_position)的視窗是從第i-(window-b)個詞開始到第i+(window-b)個詞結束,中間用a != window這個判斷跳過了這個詞他自己,也就是更新詞向量的時候隻更新上下文相關的幾個輸入詞,這個詞本身是不更新的。要注意的是每個詞的b都不一樣的,都是随機生成的,這就意味着連視窗大小都是随機的。

如果有人看過代碼,就會發現,其中的qkij在代碼中用矩陣syn1表示,Cij在代碼中用neu1表示。葉子節點裡面的每個詞向量在代碼中用syn0表示,利用下标移動去讀取。

核心代碼如下,其中的vocab[word].code[d]就表示dkij,其他就是疊代過程,代碼是寫得相當簡潔啊。

代碼中的419行就是計算Cij,425-428行就是計算f,也就是σqkij∙Cij的值,432行就是累積Cij的誤差,434就是更新qkijn+1。

注意上面,對每個輸入詞都進行了更新,更新的幅度就是432行中誤差累計的結果。

三. CBOW加抽樣的網絡結構與使用說明

3.1網絡結構與使用說明

網絡結構如下

如(二)中,中間的那個隐層是把上下文累加起來的一個詞向量,然後有一個矩陣R,是在訓練過程中用到的臨時矩陣,這個矩陣連接配接隐層與所有輸出節點,但是這個矩陣在使用這個網絡的時候不怎麼用得到,這裡也是沒弄清楚的一個地方。每個輸出節點代表一個詞向量。

同樣如(二)中的例子,計算p吃Context吃這麼一個機率,這裡的計算方法就簡單多了,就是随機從語料庫裡面抽取c個詞,這裡假設c=3,抽中了D,E,F這三個詞,又假設“吃”這個詞的詞向量是A,那麼就計算“吃”這個詞的機率就用下面的公式

p吃Context吃=σ(A∙C)∙1-σ(D∙C)∙1-σ(E∙C)∙1-σ(F∙C)

同樣如(二)中,那句話的每個詞都計算p(wi|Contexti)後連乘起來得到聯合機率,這個機率如果大于某個門檻值,就認為是正常的話;否則就認為不是自然語言,要排除掉。

這裡隻是說明這個網絡是怎麼樣的例子,真正重要的始終是那些詞向量。

四.CBOW加抽樣的優化目标與解問題

4.1抽樣方法的意義與目标函數

為啥要抽樣呢?目的跟(二)中的霍夫曼樹其實是一樣的,都是為了節省計算量,這個計算量就是式(2.1.2)中計算 p(A|C)的機率,因為這個機率實在不好算。論文《Distributed Representations of Words and Phrases and their Compositionality》中提到有一個叫NCE的方法可以來代替上面的那個hierarchical softmax方法(就是使用霍夫曼樹的方法),但是由于word2vec隻關心怎麼學到高品質的詞向量,是以就用了一種簡單的NCE方法,稱為NEG,方法的本質就是在第j個句子的第ij個詞wij處使用下面的式子代替logpwijContextij

logσwij∙Cij+k=1KEwk~pV(w)log1-σwk∙Cij

其中E下面的那個下标的意思是wk是符合某個分布的,在這裡pV(w)表示詞頻的分布。

這個式子的第二項是求K個期望,這K個期望中的每一個期望,都是在該上下文的情況下不出現這個詞的期望。這裡出現一個特别大的偷懶,就是認為這個期望隻要抽取一個樣本就能計算出來,當然,如果說周遊完整個語料庫,其實還可以認為是抽取了多次實驗的,因為每次抽取詞的時候,是按照詞頻的分布來抽樣的,如果抽樣的次數足夠多,在總體的結果上,這裡計算的期望還是接近這個期望的真實值的,這個可以參考博文中RBM中計算梯度的時候的那個蒙特卡洛抽樣來了解。

在這裡,從代碼上展現來看,就隻用一個樣本來估算這個期望的,所有式子被簡化成了下面的形式

logσwij∙Cij+k=1Klog1-σwk∙Cij

用這個式子取代(2.2.2)中的logpwijContextij,就能得到CBOW加抽樣的目标函數(去掉1/V的),這個目标函數也是極其像logistic regression的目标函數,其中wij是正類樣本,wk是負類樣本。

為了統一表示,正類樣本設定一個label為1,負類樣本設定label為0,每個樣本的負對數似然都變成下面的方式

fw=-label∙logσw∙Cij-(1-label)log1-σw∙Cij

4.2CBOW加抽樣方法的解法

解法還是用SGD,是以對一個詞wij來說,這個詞本身是一個正類樣本,同時對這個詞,還随機抽取了k個負類樣本,那麼每個詞在訓練的時候都有k+1個樣本,是以要做k+1次SGD。

對于每個樣本求兩個梯度

Fw(w)=∂fw∂w=-label∙1-σw∙Cij∙Cij+1-label∙σw∙Cij∙Cij=-label-σw∙Cij∙Cij

Fc(w)=∂fw∂Cij=-label∙1-σw∙Cij∙w+1-label∙σw∙Cij∙w=-label-σw∙Cij∙w

兩個梯度都有這麼相似的形式實在太好了,然後開始疊代,代碼裡面有個奇怪的地方,就是一開的網絡結構中的那個V*m的矩陣,用來儲存的是每次抽中的負類樣本的詞向量,每一行代表一個詞向量,剛好是所有詞的詞向量。每次抽樣抽中一個詞後,拿去進行疊代的(就是計算梯度什麼的),就是這個矩陣中對應的那個詞向量,而且這個矩陣還參與更新,就是第一個梯度實際更新的是這個矩陣裡面的詞向量。但總的看來,這個矩陣在疊代計算完了就丢棄了,因為最終更新的詞向量,每次隻有一個,就是公式一直出現的那個詞wij,最終輸出的,也是輸入裡面的詞向量(在圖中是最下面的輸出層的那些詞向量),當然這個矩陣可以認為是隐藏層跟輸出層的連接配接矩陣。

Rwn+1=Rwn-ηFw(Rwn)

其中w代表每個樣本對應的詞向量,包括wij和抽中的詞向量,注意更新的是R這個連接配接矩陣。詞向量的更新公式如下

wIn+1=wIn-ηFcRwIn+k=1KFcRwkn

注意每個梯度的值的計算都是拿連接配接矩陣R中對應的那一行來算的,這樣看起來可以避免每次都更新輸出層的詞向量,可能是怕搞亂了吧。

4.3CBOW加抽樣方法代碼中的trick

随機數是自己産生的,代碼裡面自己寫了随機數程式。

每個詞都要執行negative次抽樣的,如果遇到了目前詞(就是label為1的詞)就提前退出抽樣,這個步驟就提前完成了,這個也是一個比較費解的trick。

其中前面說的R矩陣在代碼中用syn1neg表示,Cij在代碼中用neu1表示,同樣的,葉子節點裡面的每個詞向量在代碼中用syn0表示,利用下标移動去讀取。

其中442-446就是抽樣的代碼了,是作者自己寫的子產品,然後label這個變量跟上文的label意思是一樣的,f就表示σw∙Cij的值,syn1neg就儲存了矩陣R每一行的值,neu1e還是累積誤差,直到一輪抽樣完了後再更新輸入層的詞向量。。

對輸入層的更新子產品和上面的(二)中一樣的,都是更新所有的輸入詞。

五.Skip-gram加層次的優化目标與解問題

5.1網絡結構與使用說明

網絡結構如下圖

其中的Wi是相應的詞,詞Wi與huffman樹直接連接配接,沒有隐藏層的。使用方法依然與cbow加層次的相似。

在判斷“大家 喜歡 吃 好吃 的 蘋果”這句話是否自然語言的時候,是這麼來的,同樣比如計算到了“吃”這個詞,同樣随機抽到的c=2,對吃這個詞需要計算的東西比較多,總共要計算的機率是p(大家|吃),p(喜歡|吃),p(好吃|吃)和p(的|吃)總共四個,在計算p(大家|吃)這個機率的時候,要用到上面圖中的二叉樹,假設“大家”這個詞在huffman樹根節點的右孩子的最左邊的那個節點(途中的節點T),就是圖中右數第三個葉子節點;再假設“吃”這個詞在huffman樹的節點S。再假設從根節點開始到這個葉子節點的路徑上的三個非葉節點分别為A,B,C(從高到低排列的),“吃”這個詞的詞向量設為D,那麼p(大家|吃)這個機率可以用下面的公式算機率

p大家吃=(1-σA∙D)∙σB∙D∙σC∙D

同樣的方法計算p(喜歡|吃),p(好吃|吃)和p(的|吃),再把這四個機率連乘,得到了“吃”這個詞的上下文機率,注意這隻是一個詞的機率。

把一整句話的所有詞的機率都計算出來後進行連乘,得到的就是這句話是自然語言的機率。這個機率如果大于某個門檻值,就認為是正常的話;否則就認為不是自然語言,要排除掉。

再聲明一下,這裡隻是說明這個網絡是怎麼樣的例子,真正重要的始終是那些詞向量。

5.2目标函數

L(θ)=jSij=1Tj-cij

其中Tj表示第j個句子的詞個數,wuij+ij表示詞wij左右的各cij個詞的其中一個,注意cij對每個wij都不一樣的。極大似然要對整個語料庫去做的。

但是,Google這公司,為了代碼風格的統一,用了一個trick,我舉例說明吧。

對于一開始的那句話:大家 喜歡 吃 好吃 的 蘋果,總共6個詞,假設每次的cij都抽到了2,按照上面的公式中的ij=1Tj-cij

大家:P(喜歡|大家)*p(吃|大家)

喜歡:p(大家|喜歡)p(吃|喜歡)p(好吃|喜歡)

吃: p(大家|吃) p(喜歡|吃)p(好吃|吃)*p(的|吃)

好吃:p(喜歡|好吃)p(吃|好吃)p(的|好吃)*p(蘋果|好吃)

的: p(吃|的) p(好吃|的)p(蘋果|的)

蘋果:p(好吃|蘋果)*p(的|蘋果)

把結果重新組合一下,得到下面的組合方式。

大家:p(大家|喜歡)*p(大家|吃)

喜歡:P(喜歡|大家)p(喜歡|吃)p(喜歡|好吃)

吃: p(吃|大家) p(吃|喜歡)p(吃|好吃)*p(吃|的)

好吃:p(好吃|喜歡)p(好吃|吃)p(好吃|的)*p(好吃|蘋果)

的: p(的|吃) p(的|好吃)p(的|蘋果)

蘋果:p(蘋果|好吃)* p(蘋果|的)

不證明,不推導,直接得到下面的結論:

ij=1Tj-cij

這個是網易有道的那個資料給出的結論,這裡是變成了本文的方式來描述,具體參考網易有道的原文。

這個變化倒是莫名其妙的,不過也能了解,Google公司的代碼是要求很優美的,這樣做能把代碼極大地統一起來。

總對數似然就會是下面的樣子

l(θ)=logL(θ)=1Vj=1Sij=1Tj-cij (5.2.2)

其中的V表示整個語料庫的詞沒有去重的總個數,整個目标函數也可以叫交叉熵,但是這裡對這也不感興趣,一般去掉。

這就涉及到計算某個詞的機率了,如p(wij|wuij+ij),這個機率變了,條件變成上下文的詞,計算條件機率的詞變成了輸入中要考察的那個詞。當然,計算方法沒有變,前面介紹的huffman樹的計算方法到這裡是一摸一樣的。

pwI=k=1Kp(dk|qk,I)=k=1Kσ(qk∙I)1-dk∙(1-σ(qk∙I))dk

其中I表示上下文的詞,也就是(5.1)的例子中的那個詞“大家”,也就是節點T就表示例子中的“大家”;目前詞假設是“吃”,在huffman樹上的節點是S,qk表示從根節點下來到“吃”這個詞所在的葉子節點的路徑上的非葉節點,dk就是編碼了,也可以說是分類,當w在某個節點如qk的左子樹的葉子節點上時dk=0,否則dk=1。

用這個式子代替掉上面的(5.2.1)中的似然函數中的p(wij|wuij+ij),當然每個變量都對号入座,就能得到總的似然函數了。

再對這個式子求個對數,得到

logpwI=k=1K1-dklogσqk∙I+dk∙1-σ(qk∙I)

再利用這個式子替換掉(5.2.2)中的logp(wij|wuij+ij)就能得到總的對數似然函數,也就是目标函數,剩下的就是怎麼解了。

可以注意的是,計算每個詞(例中的“吃”)上下文機率的時候都要計算好幾個條件機率的(例子中p(吃|大家),p(吃|喜歡),p(吃|好吃)和p(吃|的)),這每個條件機率又是需要在huffman樹上走好幾個非葉節點的,每走到一個非葉節點,都需要計算一個1-dklogσqk∙I+dk∙1-σ(qk∙I)的。可以看到,走到每一個非葉節點,在總的對數似然函數中,都類似logistic regression的一個樣本,為了友善描述,就用樣本和label的方式在稱呼這些東西。

跟一般的logistic regression一樣,每走到一個非葉節點,如果是向左走的,就定義label為1,為正樣本;否則label就是0,是負樣本。這樣label=1-dk,每一個非葉節點都為整個問題産生了一個樣本。

5.3解法

解法選用的是SGD,在處理每個樣本時,對總目标函數的貢獻是

lf=pdkqk,I=-1-dklogσqk∙I-dk∙1-σ(qk∙I)

計算梯度

Fqqk=∂lf∂qk=-1-dk∙1-σqk∙I∙I-dk∙-σqk∙I∙I=-1-dk-σqk∙I∙I

Fiqk=∂lf∂I=-1-dk∙1-σqk∙I∙qk-dk∙-σqk∙I∙qk=-1-dk-σqk∙I∙qk

更新

qkn+1=qkn-ηFq(qkn)

In+1=In-ηFi(qkn)

5.4代碼中的trick

對輸入詞I的更新是走完整個huffman樹後對整個誤差一起計算的,這個誤差儲存在neu1e這個數組裡面。

其中480-483行是計算σqk∙I的值,儲存在f中,vocab[word].code[d]表示的就是dk的值,word = sen[sentence_position],表示的是目前詞,neu1e就儲存了從根節點到葉子節點的路徑上的所有非葉節點的累積誤差。

這個誤差反向傳播就簡單多了,每次都是對一個詞進行更新的,就是p(w|I)中的那個I。

六.Skip-gram加抽樣的優化目标與解問題

這個就簡單說說吧,不清楚的看上面的。

6.1網絡結構與使用說明

使用說明就不說的,同樣是抽樣的。

如(四)中,有一個矩陣R,是在訓練過程中用到的臨時矩陣,這個矩陣連接配接隐層與所有輸出節點,但是這個矩陣在使用這個網絡的時候不怎麼用得到,這裡也是沒弄清楚的一個地方。每個輸出節點代表一個詞向量。

同樣如(五)中的例子,計算p大家吃這麼一個機率,這裡的計算方法就簡單多了,就是随機從語料庫裡面抽取c個詞,這裡假設c=3,抽中了D,E,F這三個詞,又假設“吃”這個詞的詞向量是A,那麼就計算“吃”這個詞的機率就用下面的公式

同樣如(五)中,那句話的每個詞都計算與上下文幾個詞的機率(p(大家|吃),p(喜歡|吃),p(好吃|吃)和p(的|吃))後連乘起來得到詞“吃”的機率,所有詞的機率計算出來再連乘就是整句話是自然語言的機率了。剩下的同上。

6.2目标函數與解法

似然函數跟(5.2.1)一樣的,對數似然函數跟(5.2.2)是一樣的,但是計算logp(wuij+ij|wij)也就是logp(w|I)的的時候不一樣,論文《Distributed Representations of Words and Phrases and their Compositionality》中認為logp(w|I)可以用下面的式子

logσw∙I+k=1KEw~pV(w)log1-σwk∙Cij

代替。

同樣可以和(四)中一樣,設定正負樣本的概念。為了統一表示,正類樣本設定一個label為1,負類樣本設定label為0,每個樣本的負對數似然都變成下面的方式

fw=-label∙logσw∙I-(1-label)log1-σw∙I

梯度

Fw(w)=∂fw∂w=-label∙1-σw∙I∙Cij+1-label∙σw∙I∙I=-label-σw∙I∙I

Fc(w)=∂fw∂I=-label∙1-σw∙I∙w+1-label∙σw∙I∙w=-label-σw∙I∙w

In+1=In-ηFcRInn+k=1KFcRwkn

6.3代碼

還是每個詞都要執行negative次抽樣的,如果遇到了目前詞(就是label為1的詞)就提前退出抽樣。

其中493-502就是抽樣的代碼,505-508是計算σw∙I的值,儲存在f中,syn1neg就是儲存了矩陣R中的每一行的值。而neu1e還是累積這誤差,直到一輪抽樣完了後再更新輸入層的詞向量。

更新輸入層還是一樣。

七.一些總結

從代碼看來,word2vec的作者Mikolov是個比較實在的人,那種方法效果好就用哪種,也不糾結非常嚴格的理論證明,代碼中的trick也是很實用的,可以參考到其他地方使用。

http://xiaoquanzi.net/?p=156

hisen部落格的博文

[4] Hierarchical probabilistic neural network language model. Frederic Morin and Yoshua Bengio.

[5] Distributed Representations of Words and Phrases and their Compositionality T. Mikolov, I. Sutskever, K. Chen, G. Corrado, and J. Dean.

[6] A neural probabilistic language model Y. Bengio, R. Ducharme, P. Vincent.

[7] Linguistic Regularities in Continuous Space Word Representations. Tomas Mikolov,Wen-tau Yih,Geoffrey Zweig

[8] Efficient Estimation of Word Representations in Vector Space. Tomas Mikolov,Kai Chen,Greg Corrado,Jeffrey Dean.

深度學習word2vec筆記之應用篇

好不容易學了一個深度學習的算法,大家是否比較爽了?但是回頭想想,學這個是為了什麼?吹牛皮嗎?寫論文嗎?參加競賽拿獎嗎?

不管哪個原因,都顯得有點校園思維了。

站在企業的層面,這樣的方式顯然是不符合要求的,如果隻是學會了,公式推通了,但是沒有在工作中應用上,那會被老大認為這是沒有産出的。沒有産出就相當于沒有幹活,沒有幹活的話就……呃……不說了。

下面就給大家弄些例子,說說在網際網路廣告這一塊的應用吧。

一. 對廣告主的輔助

1.1基本概念

網際網路廣告的廣告主其實往往有他們的困惑,他們不知道自己的目标人群在哪裡。所謂目标人群,就是廣告主想向他們投廣告的那幫人。就像網際網路廣告的一個大牛的一句名言——我知道網際網路廣告有一半是浪費的,問題是我不知道是哪一半。

這個困惑就給媒體帶來一個義務——要幫助廣告主定向他們的目标人群。

對于普通的廣告主來說,比如說一個化妝品廣告的廣告主,它的目标人群很明顯就是年輕的女性。注意關鍵詞“年輕”和“女性”,這是決定媒體這邊能否賺到錢的關鍵詞。要知道對于媒體來說,廣告主是它們的客戶,滿足客戶的要求,客戶就給它們錢,不滿足客戶的要求,就沒有人為媒體買單;沒有人為媒體買單,媒體就沒有錢養它們的員工和機器,也弄不來新聞和網際網路的其他内容,那樣媒體公司就垮了……

那麼在媒體這邊,需要做的的工作就很明确了——滿足它們的客戶(也就是廣告主)的需求。怎麼滿足呢?這工作說容易也容易,說簡單也簡單,就是把喜歡這個廣告主的廣告的人找出來,然後幫這個廣告主把他們的廣告投放給這些人,讓這些人看到這個廣告主的廣告。

這個工作帶來的問題就真多了,媒體又不是什麼神人,比如說一個新聞網站,浏覽這個網站的每天有100萬人,這個新聞網站的員工不可能一個個去通路他們的使用者(浏覽這個網站的人),整天問他們你喜不喜歡化妝品啊,喜不喜歡體育啊之類的問題。

那怎麼辦呢?媒體的員工隻好猜了,但是哪怕是猜都很費勁,想想都頭疼,一百萬人啊,一個個猜也得吃力不讨好啊。這時候計算機的作用就來了,用計算機猜嘛,而且不一定需要全部瞎猜的,因為使用者如果注冊了的話,還有一些使用者的個人資訊可以參考的。一般的網站注冊的時候都要求提供年齡性别之類的個人資訊,有時候要要求寫一些個人的興趣什麼的标簽。這個時候這些資料就用上大用處了。

網站可以把注冊使用者的個人資訊儲存下來,然後提供廣告主選擇。如上面的那個化妝品的廣告主,它就可以跟媒體提它的要求——我要向年輕的女性投放廣告。媒體這個時候就可以提供一些條件給這個廣告主選擇,如媒體說我有很多使用者,18到80歲的都有,然後男性女性使用者都有。廣告主就可以根據這些條件選擇自己的目标使用者,如選擇了18到30歲的女性使用者作為目标人群。選中了目标人群後,廣告主和媒體就可以談價錢了,談好了價錢廣告主就下單,然後媒體就幫廣告主投廣告,然後媒體的錢就賺到了。

1.2興趣挖掘的必要性

上面多次提到的“目标人群”,就是廣告主最關心的事情。客戶最關心的事情自然也是媒體最關心的事情。是以媒體會盡力幫助它們的客戶去定向它們的目标人群。

一般所謂的定向也不是媒體親自有一個人來跟廣告主談的,是媒體建立好一個頁面,這個頁面上有一些選項,比如年齡,性别,地域什麼的,都是條件。廣告主在上面把自己的目标人群符合的條件輸入,然後下單購買向這些人投放廣告的機會。

媒體為了更好地賺錢,肯定是願意把這個頁面上的條件做得更加豐富一點,讓更多的廣告主覺得這個網站的使用者裡面有它們的目标人群,進而讓更多的廣告主願意過來下單。

廣告主的定向其實有粗細之分的,有些廣告主粗放點,它們有錢,選的定向條件比較寬,就說女性的使用者,全部都投放;有些就定向得比較窄,比如說,北京的20到25歲的女性,并且要喜歡羽毛球的使用者。對于定向寬的廣告主好處理,問題就是這些定向窄的廣告主,它們還希望知道使用者的興趣所在,這就麻煩了。

為啥麻煩呢?一個使用者的興趣鬼才知道呢。就算當面問,人家也不樂意回答,何況就憑借一點點東西瞎猜。但是為了賺錢,瞎猜也得上的了,工業界為了賺這個錢,誕生了整整一個行業——資料挖掘,甚至在學術界還有一個更加生猛的名字——機器學習。學術界的那個名字和解釋都是相當大氣的:讓機器學會像人一樣思考。工業界就務實一點,隻是對資料内容本身做一個挖掘,擷取到啥呢?一般就是使用者的興趣啊,愛好啊什麼的。這些東西供誰使用呢?暫時看來隻有廣告主願意為這些掏錢,其他的就有些媒體做來讓自己推薦的内容不至于讓使用者那麼反感而已。

上面有個名詞“資料”,沒錯了,這個詞是網際網路廣告業,甚至是資料挖掘行業的核心的東西。所謂資料,這裡簡單點說就可以認為是使用者的年齡、性别、地域等使用者的基本屬性;複雜點說可以說是使用者興趣、愛好,浏覽記錄等;更進階的有使用者的交易資料(當然這個進階的資料很少媒體能搞得到)等。

解釋完“資料”這個詞,結合一下廣告這個場景,就可以得到活在媒體公司裡面的網際網路廣告行業資料挖掘工程師的工作是什麼了。他們的工作就是:根據使用者自身的基本屬性和使用者流量的網頁記錄以及内容,想方設法讓計算機猜出使用者的興趣愛好。使用者的興趣愛好“挖掘”出來後,就可以作為定向條件放到上面說的那個網頁上面供廣告主選擇了。這事情整好了,廣告投了有人點選,公司的錢就賺到了;沒整好,廣告沒人點選,廣告主不樂意下單了,公司就賺不到錢……怎麼着?炒這些工程師的鱿魚去。

上面可以看到了,輔助廣告主定位它們的目标人群是很重要的。

經過一番的探索,word2vec在網際網路廣告上面也是可以輔助廣告主定向他們的目标人群的,下面就講講這個算法在網際網路廣告的應用吧。

1.3利用word2vec給廣告主推薦使用者

為了用上word2vec,把場景轉換到一個新聞媒體如A公司。

在A公司的多個頁面中,電商公司B有他們的一個首頁,專門介紹他們公司一些産品促銷,搶購和釋出會什麼的。

公司A目前有很多使用者的浏覽資料,如使用者u浏覽了公司A的頁面a1,a2,a3等。

把這些資料處理一下,整合成word2vec能處理的資料,如下

U1 a1,a2,a3……

U2 a2,a3,a5,……

U3 a1,a3,a6,……

其中u1,u2,u3表示不同的使用者,後面的一串表示這些使用者的浏覽記錄,如U1 a1,a2,a3表示使用者u1先浏覽了頁面a1,再浏覽a2,然後浏覽了a3,……

這些資料還不符合word2vec的輸入資料格式,把第一列去掉,變成下面的樣子

a1,a2,a3……

a2,a3,a5,……

a1,a3,a6,……

這些資料就可以作為word2vec的輸入資料了。

就把這些資料作為word2vec的訓練資料,詞向量次元為3,進行訓練,完成後得到下面的輸出

A1 (0.3,-0.5,0.1)

A2 (0.1,0.4,0.2)

A3 (-0.3,0.7,0.8)

……

An (0.7,-0.1,0.3)

就得到了每個頁面的向量。

這些向量有啥意義呢?其實單個向量的意義不大,隻是用這些向量可以計算一個東西——距離,這個距離是頁面之間的距離,如頁面a1和a2可以用歐式距離或者cos距離計算公式來計算一個距離,這個距離是有意義的,表示的是兩個網頁在使用者浏覽的過程中的相似程度(也可以認為是這兩個頁面的距離越近,被同一個人浏覽的機率越大)。注意這個距離的絕對值本身也是沒有意義的,但是這個距離的相對大小是有意義的,意思就是說,假設頁面a1跟a2、a3、a4的距離分别是0.3、0.4、0.5,這0.3、0.4、0.5沒啥意義,但是相對來說,頁面a2與a1的相似程度就要比a3和a4要大。

那麼這裡就有玄機了,如果頁面a1是電商公司B的首頁,頁面a2、a3、a4與a1的距離在所有頁面裡面是最小的,其他都比這三個距離要大,那麼就可以認為同一個使用者u浏覽a1的同時,浏覽a2、a3、a4的機率也比較大,那麼反過來,一個使用者經常浏覽a2、a3、a4,那麼浏覽a1的機率是不是也比較大呢?從實驗看來可以這麼認為的。同時還可以得到一個推論,就是使用者可能會喜歡a1這個頁面對應的廣告主的廣告。

這個在實驗中實際上也出現過的。這裡模拟一個例子吧,如a1是匹克體育用品公司在媒體公司A上的官網,a2是湖人隊比賽資料頁,a3是熱火隊的灌水讨論區,a4是小牛隊的球員讨論區。這個結果看起來是相當激動人心的。

根據這樣的一個結果,就可以在廣告主下單的那個頁面上增加一個條件——經常浏覽的相似頁面推薦,功能就是——在廣告主過來選條件的時候,可以選擇那些經常浏覽跟自己首頁相似的頁面的使用者。舉個例子就是,當匹克體育用品公司來下單的時候,頁面上給它推薦了幾個經常浏覽頁面的粉絲:湖人隊比賽資料頁,熱火隊的灌水讨論區,小牛隊的球員讨論區。意思是說,目标人群中包括了經常浏覽這三個頁面的人。

這個功能上線後是獲得過很多廣告主的好評的。

這樣word2vec這個算法在這裡就有了第一種用途。

二. 對ctr預估模型的幫助

根據另一篇博文《網際網路廣告綜述之點選率系統》,裡面需要計算的使用者對某廣告的ctr。在實際操作的時候,這個事情也是困難重重的,其中有一個冷啟動問題很難解決。冷啟動問題就是一個廣告是新上線的,之前沒有任何的曆史投放資料,這樣的廣告由于資料不足,點選率模型經常不怎麼湊效。

但是這個問題可以使用同類型廣告點選率來緩解,意思就是拿一個同行的廣告的各種特征作為這個廣告的特征,對這個新廣告的點選率進行預估。

同行往往太粗糙,那麼怎麼辦呢?可以就利用跟這個廣告主比較相似的廣告的點選率來預估一下這個廣告的點選率。

上面說過,可以得到每個頁面的詞向量。這裡的方法比較簡單,如在媒體公司A上面有1000個廣告主,它們的首頁分别是a1、a2、……、a1000。

根據上面的方法,得到了這1000個詞向量,然後運作kmean或者其他聚類算法,把這1000個廣告主聚成100個簇,然後每個簇裡面的廣告主看成是一個。

這裡可以模拟一個例子,聚類完成後,某個簇c裡面包含了幾個廣告主的首頁,分别是京東商城,天貓,唯品會,當當,聚美優品,1号店,蘑菇街,卓越,亞馬遜,淘寶這10個,這10個的目标人群看起來基本是一緻的。

這裡的看成是一個簇是有意義的,比如說第一個簇c1,c1這個簇裡面的所有曆史投放資料和實時資料可以做特征,來預估這個流量對這個簇的ctr。得到這個ctr後,就很有用了,如果某廣告投放資料比較充分,就直接預估這個廣告的ctr;如果某廣告的曆史投放資料很少,就用這個廣告主所在的簇的ctr來代替這個廣告,認為對簇的ctr就是這個廣告的ctr,這樣能讓一個新廣告也能得到相對靠譜的預估ctr,保證不至于亂投一番。

三.一些總結

如何應用好一個算法,确實是很多算法工程師的一個重大課題。

資料挖掘算法工程師經常要面對的一個難題就是:這個算法怎麼用到我們的資料上面來?有不少同學會認為是:我到了公司,就發明一個很牛逼的算法,把公司的原來的問題解決掉,然後大大增加了效果,獲得了上司的好評。這個天真爛漫的想法就不評價了,免得被說打擊人。網際網路企業裡面的真實情況是算法工程師面對那一團亂遭的資料,得想盡辦法去把資料整合成能用的格式。

拿上面的(1.3)中的例子,那個把資料組合成a1,a2,a3……這樣一行行的,然後進入word2vec去進行訓練是最難想到的而且是最核心的東西,雖然明着說是word2vec這個算法厲害,實際上面是“把資料整合成合适的方式交給word2vec進行訓練”這個想法重要,因為嘗試了很多想法,做了很多實驗才能想到這樣的一招的。

還有資料的整合其實也費了很多功夫的,比如說媒體有些使用者是一些機器的賬号,人家亂搞的,要想辦法排除掉的,而“想辦法排除”這麼簡單一句話,真正要做的工作真是多多的有。

哪怕結果都訓練出來了,怎麼解釋這個結果是好的?這個問題也是得想了一段時間的,後來是實驗發現了利用詞向量的距離來評價相似性這個東西最靠譜,然後才用上的。

一個資料挖掘的過程其實不簡單,這個部落格也沒辦法一一展現做的過程裡面的那些各種折騰,各種不順暢。

資料挖掘工程師經常要面對的另一個難題就是:明明理論上推得杠杠的,算法性能也是杠杠的,但是對于網際網路廣告的效果,怎麼就那麼不鹹不淡的呢?

這個問題真沒有什麼統一的答案,這種現象多了去了。經常遇到的原因有:資料本身處理的方式不對和算法不合适。

所謂資料本身處理的方式,可以參看博文《網際網路廣告綜述之點選率特征工程》,裡面說的那些方法不是從哪本書上面看到的,是經過比較長時間實踐,然後各種折騰,各種特征取舍,各種胡思亂想,各種坑踩出來的。可能志在學術的人看起來都簡單,實際上課本那些東西,學生們吹起牛皮來不眨眼的那些東西,一跟真實應用場景結合起來就各種坑要踩的了。

拿上面的(二)中的例子來看。方法簡單得不得了,但是可以想象一下,word2vec牛逼啊,kmeans牛逼啊,第一次聚類出來的結果也不過如此。後來又加入了每個廣告主的行業和地域作為特征,而且這個加特征,就是直接把行業和地域處理一下,連接配接到廣告主的詞向量後面的。如a1的詞向量是(0.3,-0.5,0.1),然後假設隻有兩個行業,體育和化妝品,處理成二值特征,占據第4和5兩個index,第4個特征為1,第5個特征為0表示體育類廣告主,反過來,第4個特征為0,第5個特征為1表示化妝品;再對地域的下标做了一下處理,成為二值特征,比如說占據了6到10這5個位置(假設第6個位置為1,其餘7到10為0表示北京;第7個位置為1,其餘為0表示廣東,以此類推)。

經過了上面的處理,再用kmeans進行聚類,從聚類後一個個簇去看,結果看起來才順眼了很多。上面的行業和地域特征的加入,也是用了比較多的經驗的,不是憑空亂整出來的一個吹牛皮的東西,當然誰有更好的方法,也可以提出來試試看。另外還希望大家注意關鍵字“一個個簇去看”,這個工作真是費時費力,比較辛苦的。

以上舉了一些例子,也把網際網路廣告的資料挖掘算法工程師的一些工作中的成功和不成功的地方都說出來了,基本上算是實話實說,希望對大家有點幫助吧。有過類似經曆的人能看懂,沒啥興趣的就呵呵吧。

多位同僚提供的建議與指導。

多位google研究員有關word2vec的資料。