參考:https://zhuanlan.zhihu.com/p/22477976
http://yobobobo001.github.io/2016/05/26/%E6%88%91%E6%89%80%E7%90%86%E8%A7%A3%E7%9A%84word2vec/
http://x-algo.cn/index.php/2016/03/12/281/#i
https://www.zhihu.com/question/25269336
http://mp.weixin.qq.com/s?__biz=MzA3MDg0MjgxNQ==&mid=208116963&idx=3&sn=2a4c2eb8fbd27cad2293d8acabce52e9&mpshare=1&scene=1&srcid=0327HXI7DLeR7UUMDXHlb7H3#rd
http://www.flyml.net/2016/11/07/word2vec-basic-understanding/
簡介
word2vec就是用一個一層的神經網絡(CBOW的本質)把one-hot形式的詞向量映射爲分佈式形式的詞向量,爲了加快訓練速度,用了Hierarchical softmax,negative sampling 等trick。訓練的主要目標是獲得分佈式詞向量,而不是神經網絡的預測模型。word2vec的訓練過程是有監督的神經網絡學習,但是得到的結果居然是無監督的clustering的效果,就是獲取詞向量。分佈式詞向量隱含了詞語的信息,分佈式詞向量之間的夾角可以表示詞語之間的相關性,因此用作特徵值比直接用詞本身更方便。word2vec還規避了兩大問題:詞語的次序和熱門詞語的降權處理。與潛在語義分析(Latent Semantic Index, LSI)、潛在狄立克雷分配(Latent Dirichlet Allocation,LDA)的經典過程相比,Word2vec利用了詞的上下文,語義信息更加地豐富。
word2vec涉及到很多自然語言處理的名詞。首先是詞向量(word vector),圖像和音頻等信號都可以用一個矩陣或者向量表示,所以我們也希望用一個數學方法來表達單詞,這樣可以方便的用於各種後續計算,這就是詞向量。
1. one-hot方式。從很大的詞庫corpus裏選V個頻率最高的詞(忽略其他的),V一般比較大,比如V=10W,固定這些詞的順序,然後每個詞就可以用一個V維的稀疏向量表示了,這個向量只有一個位置的元素是1,其他位置的元素都是0。One hot方式其實就是簡單的直接映射,所以缺點也很明顯,維數很大,也沒啥計算上的意義。
2. 分佈式詞向量(distributed word representation), 分佈式詞向量是一個固定大小的實數向量,事前確定它的大小比如N=300維或者N=1000維,每個元素都是一個實數,實數的具體值是詞庫裏面每個詞通過不同的貢獻得來的,所以叫分佈式的。而word2vec就是一種學習這個分佈式詞向量的算法。向量的餘弦夾角可以代表詞語之間的相似度。種方法相較於One-hot方式另一個區別是維數下降極多,對於一個10W的詞表,我們可以用10維的實數向量來表示一個詞,而One-hot得要10W維。
分佈式詞向量並不是word2vec的作者發明的,他只是提出了一種更快更好的方式來訓練也就是:連續詞袋模型Continous Bag of Words Model(CBOW)和Skip-Gram Model。這兩種都是訓練詞向量的方法,可以選擇其一,不過據論文說CBOW要更快一些(1天vs.3天的區別)。統計語言模型statistical language model就是給你幾個詞,在這幾個詞出現的前提下來計算某個詞出現的(事後)概率。CBOW也是統計語言模型的一種,顧名思義就是根據某個詞前面的C個詞或者前後C個連續的詞,來計算某個詞出現的概率。Skip-Gram
Model相反,是根據某個詞,然後分別計算它前後出現某幾個詞的各個概率。
以“我愛北京天安門”這句話爲例。假設我們現在關注的詞是“愛”,C=2時它的上下文分別是“我”,“北京天安門”。CBOW模型就是把“我” “北京天安門” 的one hot表示方式作爲輸入,也就是C個1xV的向量,分別跟同一個VxN的大小的係數矩陣W1相乘得到C個1xN的隱藏層hidden layer,然後C個取平均所以只算一個隱藏層。這個過程也被稱爲線性激活函數(這也算激活函數?分明就是沒有激活函數了)。然後再跟另一個NxV大小的係數矩陣W2相乘得到1xV的輸出層,這個輸出層每個元素代表的就是詞庫裏每個詞的事後概率。輸出層需要跟ground
truth也就是“愛”的one hot形式做比較計算loss。這裏需要注意的就是V通常是一個很大的數比如幾百萬,計算起來相當費時間,除了“愛”那個位置的元素肯定要算在loss裏面,word2vec就用基於huffman編碼的Hierarchical softmax篩選掉了一部分不可能的詞,然後又用nagetive samping再去掉了一些負樣本的詞所以時間複雜度就從O(V)變成了O(logV)。Skip gram訓練過程類似,只不過輸入輸出剛好相反。
訓練完成後對於某個詞就可以拿出它的1xN的隱藏層作爲詞向量,就可以w2v(中國)-w2v(北京)=w2v(法國)-w2v(巴黎)了。
如何訓練詞向量
一般而言,一篇文章都會有個主題,比如海賊王的文章裏面可能通篇都出現海賊王相關的詞語,如路飛,索隆等,這些詞的語義肯定比不同文章的更相近,所以作者提出了以下兩種模型,把相近的單詞放進模型裏面訓練。
-
cbow
把中間的詞單獨拎出來,讓周圍的詞去預測這個詞的概率。
- 每個單詞先映射到公用詞表中(大矩陣)一列對應的向量
- 把向量相加
- 把這個向量經過一個softmax得出預測相應單詞的概率。
-
skip-gram
skip-gram則是輸入一個單詞,把詞周圍的其他詞作爲要預測的單詞。
訓練的細節
-
softmax層加速
兩種網絡結構的最後一層都是一個大的softmax,起到對於每個詞的預測概率歸一化作用,可是在實際訓練過程中每次迭代都要通過softmax計算每個詞的概率是相當耗費時間的,能否優化這個呢?這個問題早在03年Bengio提出的語言模型[2]也遇到,後來他們提出了Hierarchical Softmax[3]來加速。
-
爲什麼要歸一化
在每次迭代中把要預測的詞相關權重增加,通過歸一化,同時把其他的詞相關權重減少。這個不難理解,總的預測概率和是1,把其中某一個詞的概率增加就意味着把其他詞的預測概率打壓。能不能只增加其中某個詞的概率呢?可以,但是收斂很慢。
-
Hierarchical Softmax 是如何提速的?
Hierarchical Softmax 相對於原來的softmax是把一個多分類問題轉換成了多個二分類問題。通俗地說,現在倉管人員要去倉庫找一個配件,按照softmax策略是把倉庫裏面的配件一個個的過一遍,最後確定下來是哪個,Hierarchical Softmax則是根據預先記錄的表知道了要找的配件在第二個房間的第一個貨架第二行第一個直接去取的。在上面提到,歸一化的作用是把其他詞的概率打壓,那麼能不能把詞分好類別直接打壓呢?這樣就是每次打壓一個類別(多個單詞),而不用一個個地打壓每個單詞的相關權重了。原來的softmax是這樣的:
現在我們構建一顆二叉樹把所有單詞放在葉子節點上:
現在我們要更新12號節點,沿着二叉樹的路徑可以看到判斷是這樣的:是否在1號節點的左兒子裏,是否在2號節點的左兒子裏,是否在5號節點的左兒子裏,是否在9號兒子的左節點裏。這就變成了多個二分類問題,作者採用哈弗曼樹編碼更是把每個單詞在樹中路徑的長度減少到極致,並且等價於原來的softmax,因爲整棵樹的葉子節點和是歸一化的。所以,最終的更新只需要更新一部分的權重即可。注意:最終的節點已經不是表示單詞了,而是二叉樹上的非葉子節點。
這樣,由原來的O(N)次運算下降到了O(logN)級別,搞過ACM的同學可以意識到這個做法類似線段樹的段更新。
-
-
negative sampling
不做歸一化可是又想降低其他單詞的預測概率,該怎麼做呢?這就是negative sampling的由來。作者對其他的單詞進行負採樣以降低其他單詞的相關權重,作者認爲負採樣中單詞作爲負樣本的概率應該和其詞頻正相關,所以詞頻越高的越大概率被抽到。這種方案我認爲只是一種近似方案,和Hierarchical Softmax 這種等價於原來的softmax是有明顯區別的。不過這種方案在低頻詞上的學習會好一些,主要原因在於負採樣更容易抽到其中的高頻詞。
-
訓練數據的窗口選取策略
從cbow的網絡結構可以看出,模型輸入的單詞數量是不定長的,那在構造訓練樣本的時候應該如何選取預測單詞附近的多少個單詞作爲輸入呢?Mikolov認爲離預測單詞近的詞語比那些遠的單詞更相關,所以採用了隨機窗口大小的方式來選取,每次在區間[1, window]隨機一個數值K,然後在預測單詞的前後各選取k個單詞作爲輸入。
window是可選參數大小,window增大可以使向量學習更充分,但同時也增加了訓練的複雜度。
-
exp運算打表
計算softmax的時候需要求e的x次方,一輪迭代中指數運算至少需要上億級別的指數運算,由於sigmod函數是個長“S”型的函數,可以通過對中間部分的x查表來取代多次指數運算。
-
降採樣
對於語料中的高頻詞,Mikolov選擇對它們進行降採樣(sub-samplig),我認爲高頻詞在語料中的出現次數比較多,而且高頻詞一般而言都不是決定附近詞語的詞,比如“的”這種停用詞。所以對高頻詞進行降採樣既不影響模型效果,又能提升收斂速度,何樂而不爲呢?
-
低頻詞的處理
對於訓練預料中的低頻詞,源碼中是對這部分進行過濾處理,我認爲主要是這部分低頻詞的數據不充足,模型對於低頻詞向量訓練不充分,學習的向量置信度不高,也會影響其他向量的效果。所以,過濾低頻詞是一個比較好的選擇。
-
學習率調整策略
學習率過大導致模型不收斂,學習率過小則會陷入局部最優,Mikolov在實現過程中採取了學習率隨着實際訓練的語料增加而減少。
學習率的動態調整極爲重要,在搭建網絡訓練過程中,跑了15輪的迭代相應的觀察指標都沒有降下來,我反反覆覆檢查了數據生成以及網絡實現都沒有發現問題,斷斷續續debug了兩天之後才意識到根本不是實現上的問題,而是學習率沒有控制好,模型沒有收斂。
-
caffe等框架中的實現方法
這段主要是在框架中實現源碼的細節,如對框架代碼不熟悉者可略過
網絡結構:
在Hierarchical Softmax添加了全連接矩陣,每次需要做二分類(logistics regression)的時候就把相應列中的參數摳出來組成一個小矩陣乘以輸入的x向量即可,相應的bias偏置項也一樣,梯度回傳的時候則根據每個二分類的loss分別回傳,記得對應之前摳出來的列向量。
相關指標以及效果
-
訓練指標
如何評估模型是否符合預期地正在訓練呢?最大似然,如果模型朝着正確的方向收斂,相應的單詞出現時其預測概率會增大。所以,通過累加每個單詞的最大似然預測求均值可以看出模型是否如預期一樣收斂。貼一個自己訓練過程中的最大似然(取反)
-
評估指標
原文中作者主要是模型學到的向量所包含的線性代數關係作爲評估指標的,即判斷類似
這種關係的準確率,我查了不少資料,發現很少單獨針對詞向量的模型,更多的是把訓練出來的詞向量放到實際應用場景中看是否有所提升,如語音識別,機器翻譯等。
-
實際效果。用了wiki的數據進行訓練:
應用推廣
word2vec適合的情況就是對於一個序列的數據,在序列局部數據間存在着很強的關聯。word2vec中兩個詞的相似度可以直接通過餘弦來衡量,巧妙的地方就是如何定義doc和word。
文本序列分析
典型的就是文本的序列了,鄰近的詞之間關聯很強,甚至可以通過一個詞的上下文大概預測出中間那個詞是什麼。學習到的詞向量代表了詞的語義,可以用來做分類、聚類、也可以做詞的相似度計算。此外,Word2vec本身的層次分類器或者採樣方式實際上對熱門item做了很大的懲罰,所以不會像一般的矩陣分解一樣,最後算出來語義接近的都是熱門詞,這也是word2vec很好的一個特性。對於短文本分類,直接把文檔裏面所有的word對應的向量線性相加,作爲文本的特徵訓練分類器,效果也很不錯。這種方式其實在word2vec的訓練過程中也有使用。另外如果換成非線性分類器,比如rbf kernel SVM,分類準確度還能再高,這個也是符合預期的。
- 找相關詞,注意是相關詞而不是同義詞。例如你輸入”雷軍”,計算出來的相關詞就會有:手機,小米,喬布斯等等。
- 根據上下文預測句子中缺失的單詞
- 根據不同語種裏相同單詞的詞向量之間的特定關係做機器翻譯。
app分發平臺
我非常滿意的一個應用是把word2vec應用在用戶app下載序列上,根據用戶下載app的順序,把app看做單詞,也是可以形成這樣的序列數據,進而訓練處每個app對應的向量。利用這個向量計算app之間的相似度,效果非常好,能夠把真正內容相關的app聚合在一起,同事規避熱門app的影響。相關詞挖掘
用在品牌詞和品牌相似詞挖掘中品牌詞的挖掘也特別有意思。背景是淘寶打擊盜版,普通商家不敢在淘寶商品詳情裏寫品牌名。他們會進行品牌詞的變種,比如Levi's會變成李家、L家,tommy 會變成湯米、湯家、T家等等來躲避打擊。 我先進行文本的 統計分詞, 然後用word2vec 進行模型訓練、最終把這些盜版品牌找出來了。在社交網絡中的推薦
有一個個性化推薦的場景,給當前用戶推薦他可能關注的『大V』。對一個新用戶,此題基本無解,如果在已知用戶關注了幾個『大V』之後,相當於知道了當前用戶的一些關注偏好,根據此偏好給他推薦和他關注過大V相似的大V,就是一個很不錯的推薦策略。所以,如果可以求出來任何兩個V用戶的相似度,上面問題就可以基本得到解決。
我們知道word2vec中兩個詞的相似度可以直接通過餘弦來衡量,接下來就是如何將每個V用戶變爲一個詞向量的問題了。巧妙的地方就是如何定義doc和word,針對上面問題,可以將doc和word定義爲:
由於用戶量很大(大約4億),可以將關注word個數少的doc刪掉,因爲本身大V的種類是十萬級別(如果我沒記錯的話), 選擇可以覆蓋絕大多數大V的文章數量就足夠了。
KDD上有一篇DeepWalk的文章,在社交網絡上進行隨機遊走生成一組組節點的序列,然後通過word2vec訓練每個節點對應的向量。但是我用這個方法在qq的社交網絡上面做了一些實驗,發現效果非常不理想,可能和qq社交網絡的複雜性有關。
計算商品的相似度
在商品推薦的場景中,競品推薦和搭配推薦的時候都有可能需要計算任何兩個商品的相似度,根據瀏覽/收藏/下單/App下載等行爲,可以將商品看做詞,將每一個用戶的一類行爲序看做一個文檔,通過word2vec將其訓練爲一個向量。
同樣的,在計算廣告中,根據用戶的點擊廣告的點擊序列,將每一個廣告變爲一個向量。變爲向量後,用此向量可以生成特徵融入到rank模型中。
這種相識性還可以用在,物品的推薦上,根據用戶購買物品的順序,把每個物品當成一個單詞,相當於一門外語了,誰也看不懂而已,但裏面放映了上下文的關係,這個是很重要的,也是我們一開頭那種普通算法無法做到的,同時對一些熱門的物品自然有降權的處理,非常的方便。word2vec自然規避了兩大問題:詞語的次序和熱門詞語的降權處理。
作爲另一個模型的輸入
1. 在nlp的任務中,可以通過將詞聚類後,生成一維新的特徵來使用。在CRF實體識別的任務中,聚類結果類似詞性,可以作爲特徵來使用。
在依存句法分析的任務中,哈工大ltp的nndepparser則是將詞向量直接作爲輸入。
具體論文『A Fast and Accurate Dependency Parser using Neural Networks』
2. 作爲其它如火如荼的cnn rnn rnn-lstm 系列的初始化輸入特徵
向量快速檢索
當我們將一個文檔變成一個向量之後,如何根據餘弦/歐氏距離快速得到其最相似的topk個文章,是工程實現上不得不考慮的問題。例如線上可以允許的時間是5ms以內,如果文章數量往往上萬或者更多,O(n)的方式計算明顯不可接受了。
如果文章更新的速度很慢,可以通過離線的方式一天或者幾天計算一次,導入redis(或者別的)提供線上快速查詢。 但是如果文章實時新增,並且大量流量來自新文章,這個問題就要好好考慮一下。
一般可以通過kd-tree、simhash、聚類等方式解決,選擇不同的方式和具體的推薦場景、數據分佈有關。
序列點擊數據的分析
1、序列數據不能過長,過長會導致偏移現象,訓練的詞向量會變差。其實就是用戶注意力的問題,用時髦的話說就是attention。現在attention在lstm裏混得風生水起。我當時拿到用戶log後,先根據session進行數據切割,如果一個session過長,我會進行限制,只允許最大一個長度。儘量保證一個序列的主題(attention)基本一致。這一塊還有很多可以優化,session的切分好像也有相關算法。數據沒清洗乾淨,後期也很惱火。
2、淘寶的數據特別多,用戶點擊數據量特別大,並且商品數也接近10億,真是讓人又愛又恨。10億的商品如果每個都算200維的話,內存根本撐不住。淘寶的商品id特別長,佔用內存多,最開始我對商品進行了重排序節約內存。但是這點優化遠遠不夠,就開始修改源代碼,只保存整數,後來發現還是不行,就切換到當時有個很初級的ps版本word2vec(我和xlab的一個同事開發),吭哧吭哧的跑了很久完成。不過效果不如原始的word2vec,有點心灰意冷,後面有其他任務,這方面老大也不是特別上心,沒有支持,就停止了這方面的嘗試。感覺很是遺憾。
擴展
word2vec提供了一種計算關聯的新思路,通過轉化爲term向量的方式,可以計算出任何term之間的關聯度,同時這種關聯能夠在一個很快速的時間內被計算出來,這在實際應用中就有很大的價值。上述的term還可以被替換爲其他任何類型的item,比如book2vec、movie2vec、query2vec等等,只要你有足夠的上下文語料去做訓練,而現實中這種語料是非常多的,比如一個用戶看過/買過/評價過的book、movie,用戶搜索過的query。
在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是小牛隊的球員討論區。這個結果看起來是相當激動人心的。
根據這樣的一個結果,就可以在廣告主下單的那個頁面上增加一個條件——經常瀏覽的相似頁面推薦,功能就是——在廣告主過來選條件的時候,可以選擇那些經常瀏覽跟自己主頁相似的頁面的用戶。舉個例子就是,當匹克體育用品公司來下單的時候,頁面上給它推薦了幾個經常瀏覽頁面的粉絲:湖人隊比賽數據頁,熱火隊的灌水討論區,小牛隊的球員討論區。意思是說,目標人羣中包括了經常瀏覽這三個頁面的人。
這個功能上線後是獲得過很多廣告主的好評的。