真實資訊語料下的Word2Vec的遷移實踐:Tag2Vec
前言
互聯網中,對一個內容實體的建模,如新聞,商品,通常有兩個方向:1,content-based,如該文章屬於哪個類別、文章標題、關鍵字、作者、新聞字數等等信息,這些屬於從內容上描述文章信息;2,另一塊是action-based,即從用戶與內容之間的各種不同行為來建模用戶的關係。今天我們就來重點關注下基於用戶行為的內容表示的一些有意思的東西。
協同過濾
協同過濾相信很多做推薦的人經常接觸的一個演算法,是一種經典的集體智慧的演算法:在大量的人群行為數據中收集信息,得到大部分人群的統計結論來表示人群中某種趨勢,或者我們稱為共性的部分。
為幫助理解,這裡簡單舉幾個栗子:比如我等屌絲程序猿,大部分會是下面這種情況:除了程序猿的裝扮,程序猿的timeline很多時候也有下面這個特點:
特點人群,會更大幾率對他相關或者有興趣的內容產生行為,同理,轉換下用戶和內容的彼此身份,如果某個內容,被相似的人群產生行為是否能夠書名這些內容有一些實體會引起這一類人群的興趣,這就是action-based的假設前提,action-based和content-based屬於一個硬幣的兩面,應該綜合考慮才能算是比較合理的某個內容實體的表徵方式。
很顯然一些公共的信息如一個出生湖北武漢,年紀29歲的未婚女演員並不能表徵她就是劉亦菲,但是如果加上她的action信息,她參演過功夫之王、銅雀台、四大名鋪等等那就八九不離十了。
是的,content-based的數據就是那麼重要,而協同過濾就是使用的比較多的方法基於用戶的協同過濾
一句話描述基於用戶的協同過濾,就是找到和目標用戶最相思的用戶,然後把該用戶產生過的物品,收集為候選列表,濾除掉已產生行為物品,並考慮用戶相似度為權重,進行加權排序,大體如下:
對用戶A進行推薦,因為與用戶行為相似度較大[1,1,0,0]與[1,1,0,1],其相似度為sim(A,D),B,C用戶[0,0,1,0],其相似度為sim(B,c),其值為0,所以最終對用戶A的推薦為商品D。
基於Item的協同過濾
一句話描述基於Item的協同過濾就是計算Item的相似度,然後推薦用戶已購買的商品相似度比較大的物品。
很明顯商品D與商品A的和商品B的相似度都比較高,濾除已產生行為商品,對用戶A推商品D。
基於模型的協同過濾
基於模型的協同過濾的方法,大體是用模型來替代比較粗糙的相似度計演算法方式,這裡描述下比較經典的Matrix Factorization方法, 前面基於用戶和Item的方法在實際場景中會出現數據稀疏、計算複雜的問題,Matrix Factorization是一個比較好的方法,採用了矩陣分解的思路,將原始的用戶對Item的行為矩陣轉換為兩個dense矩陣用來表示用戶、Item的隱向量表示,然後在隱向量空間來度量用戶或者Item的相似度
等號左邊的矩陣記錄不同用戶對不同商品的行為分布,通常在實際系統中,矩陣很大,而且通常十分稀疏,Matrix Factorization方法就是將這個矩陣分解為兩個比較小的矩陣,分別為用戶和Item的隱向量矩陣,然後利用這些隱向量矩陣計算用戶對Item的偏好得分,或者計算Item與Item或者用戶與用於之間的相似性,在不同場景下來進行各種需求的計算。
利用Word2Vec建模共現關係
前面提到了使用協同過濾來建模,得到action_based的方式,那麼是否有其他的方法呢? 回歸到數據來源,用戶對各種不同的行為如果組成一個有一個的序列,如果我能建模序列內,元素之間的相似度,是不是就能很好的表徵這些元素。好吧,大家可能發現了,這tm不就是Word2Vec嗎?每個序列不就是Word2Vec的語料語句嗎?是的,就是這樣, 其實說了前面許多,什麼協同過濾,Matrix Factorization,就是想引出這個,使用Word2Vec來建模Action數據,下面我將詳細描述,我是怎麼在實際數據中做這些嘗試的。
Word2Vec原理
Word2Vec的原理,有很多文章都講過了,這裡就不詳細描述了,想進一步了解的可以去Google一下, 這裡一句話解釋下:利用cbow或skip-gram收集窗口上下文信息,來建模詞與詞之間的共現關係。
Tag2Vec嘗試
用於閱讀資訊相關內容,通常在一個有效時間內,如一個session,所有文章會形成一個文章序列,通常文章與Tag詞的映射,(何為Tag?如黃忠垃圾?這盤《王者榮耀》竟打了195分鐘,手機充三次電!這篇文章可能就會提取到王者榮耀這個Tag詞),形成Tag詞的序列,收集到有效用戶的所有行為,即可拿到所Tag詞的序列,這個序列中包含了用戶在閱讀比如Tag詞為王者榮耀後,更可能去閱讀王者榮耀英雄的數據如李白、諸葛亮的文章內容,也就是說在一個序列內,其共現概率應該比較高,那麼其向量化後的相似度也應該更大,這個是content-based可能需要話很大力氣才能建模的。Ok,話不多說,我們用實際數據實驗一下,說明一下,以下所有數據均來自於team內部,無法共享,老鐵們不要私信來求數據啦,給你們我就會被開除的。
數據收集
一個Session數據的收集理論上應該包括Tag詞序列,還有先後關係,才能比較合理的建模一個可用的Tag2Vec模型,但是,數據收集難度問題,
我們這裡使用的是用戶每天在Tag詞上的行為序列,也沒考慮Tag詞的先後,所以這裡其實有一個風險,可能達不到我們預先想要的類似Word2Vec的結果,因為Word2Vec理論上是有一個window size限制,然後窗口了上下文來和中間詞做一個推斷,其實質是一個分類問題(正樣本為中間詞,負樣本為窗口外詞), 所以其實這裡如果沒有時間先後,還有窗口太大為天,本身在模型上是有風險的,但是,又稍微一想,其實語義的依賴關係本身就也有可能不在windows size內,隨機性的順序,丟失的信息應該不多,考慮了很多,和小夥伴也討論,感覺問題不大,直接先上看看,最後的數據如下:需要說明的是這裡的數據做過基本的去臟處理,包括設置閾值,排除行為過少的用戶,大概能拿到500w+的數據。
模型訓練
Tag2Vec on Gensim
在Gensim上實現Word2Vec很容易,只需要幾行就可以完成:
#-*-coding:utf-8-*-from gensim.models import word2vecimport logginglogging.basicConfig(format="%(asctime)s : %(levelname)s : %(message)s", level=logging.INFO)sentence = word2vec.LineSentence( "../data/tag_day_ok.csv")model = word2vec.Word2Vec(sentences=sentence, size=50, workers=4, min_count=5)news_w2v = "../data/tag_word2vec.model"model.save(news_w2v)
Tag2Vec on TensorFlow
現在在TensorFlow實現的WordVec,效果不是很好,這裡先佔個坑,後面等搞好了再來填。
Tag2Vec結果
這裡我們對Tag2Vec做一些展示:
#-*-coding:utf-8-*-import gensimimport matplotlib.pyplot as pltfrom sklearn.manifold import TSNEplt.rcParams["font.sans-serif"] = ["SimHei"]plt.rcParams["axes.unicode_minus"] = Falsemodel = gensim.models.Word2Vec.load("../data/tag_word2vec.model")tag_id_name = {"UNK": "UNk"}# tag_id=>tag_namewith open("../data/t_tag_infos.csv", "r") as fread: for line in fread.readlines(): tag_id_name_list = line.split(" ") tag_id = tag_id_name_list[0] tag_name = tag_id_name_list[1].strip() tag_id_name[tag_id] = tag_nametsne = TSNE( perplexity=30, n_components=2, init="pca", random_state=1, n_iter=5000, method="exact")def plot_with_labels(low_dim_embs, labels, filename="tsne.png"): assert low_dim_embs.shape[0] >= len(labels), "More labels than embeddings" plt.figure(figsize=(18, 18)) # in inches for i, label in enumerate(labels): x, y = low_dim_embs[i, :] plt.scatter(x, y) plt.annotate( label, xy=(x, y), xytext=(5, 2), textcoords="offset points", ha="right", va="bottom") plt.savefig(filename)X = model[model.wv.vocab]X_tsne = tsne.fit_transform(X[:500])labels = model.wv.vocab.keys()[:500]labels = [tag_id_name[i].decode("utf-8") for i in labels]plot_with_labels(X_tsne, labels)tag_name_id = dict(zip(tag_id_name.values(), tag_id_name.keys()))def get_topk(tag_word, model, topk=50): nearest_list = model.wv.similar_by_word(tag_name_id[tag_word], topn=topk) nearest_words = [tag_id_name[i[0]] for i in nearest_list] # nearest_words_score = [tag_id_name[i] for i in nearest_list] print "near the {0}, the top {1} words are {2}".format( tag_word, topk, " ".join(nearest_words))get_topk("知乎", model)
以下是一些例子, 感覺還是蠻有意思的:
如王者榮耀,相似度最高的是天美工作室, 然後就是很多相關的英雄以及一些主播信息,還有一些有意思的如宋喆、車曉、張靜初、鄭則仕,這類看起來相關性不大的實體,應該是因為看王者榮耀的小夥伴們更偏向於娛樂化的新聞,因此更優可能露出車曉、張靜初、宋喆這些娛樂圈人物。
500個tag的t-sne分布:
Tag2Vec如何使用?
Tag2Vec產生的向量表示,原則上可以用來表徵Tag詞,由於數據來源於Action,可以加上Content-based的數據,然後放在諸如CTR模型,在文章點擊率預估,又或者模型的部分Feature,提升模型準確性;還可以在一些相關文章推薦時,通過Tag2Vec來露出其他相關的Tag,推薦這些Tag的文章;甚至可以和word2vec相同的用法,作為embedding的一種初始化表示,在任務中retrain這些參數。
所有代碼都在tensorflow-101/Tag2Vec,有興趣想在一些數據上測試的可以重現下。推薦閱讀: