基於神經網絡的embeddding來構建推薦系統
在之前的博客中,我主要介紹了embedding用於處理類別特征的應用,其實,在學術界和工業界上,embedding的應用還有很多,比如在推薦系統中的應用。本篇博客就介紹了如何利用embedding來構建一個圖書的推薦系統。
本文主要譯自《Building a Recommendation System Using Neural Network Embeddings》,完整詳細的代碼見官方GitHub。
目錄
一、背景&數據集讀取
1.1 神經網絡嵌入(Neural Network Embeddings)
1.2 數據集:來自維基百科
1.3 數據集清洗
二、監督學習
2.1 定義機器學習的任務
2.2 關於訓練集及測試集的劃分
2.3 嵌入模型
2.4 生成訓練示例
2.5 訓練模型
三、構建推薦系統
四、嵌入可視化
五、總結
參考文獻
一、背景&數據集讀取
深度學習應用甚廣,在諸多方面的表現,如圖像分割、時序預測和自然語言處理,都優於其他機器學習方法。以前,你只能在學術論文或者大型商業公司中看到它的身影,但如今,我們已能利用自己的電腦進行深度學習計算。本文將利用深度學習和維基百科構建圖書推薦系統。
該推薦系統基於假設:鏈接到相似的維基百科頁面的書籍彼此相似。(註:必須要理解本文所用的數據集才能這句話的深層含義。稍後會講解數據集的含義)
1.1 神經網絡嵌入(Neural Network Embeddings)
嵌入(embedding),即用連續向量表示離散變量的方法。與獨熱編碼不同的是,神經網絡嵌入維度較低,並能令相似實體在嵌入空間中相鄰。
神經網絡嵌入的主要用途有三種:
在嵌入空間中找到最近鄰。
作為有監督的機器學習模型的輸入。
挖掘變量間的關系。
1.2 數據集:來自維基百科
與以往的數據科學項目一樣,我們需要從數據集入手。點擊此處,查看如何下載和處理維基百科上的每一篇文章,以及搜索書籍頁面。在本文的數據集中,我們保存了圖書標題、基本信息、圖書頁面上指向其它維基百科頁面的鏈接(wikilinks)和外部網站的鏈接。為了創建一個推薦系統,我們僅僅使用圖書標題和wikilinks。
(註:我們在這裏仔細解釋下wikilinks:所謂的wikilinks就是在圖書的維基百科頁面上的介紹該書的一些詞組,比如說《戰爭與和平》,那麽它的wikilinks可能就是列夫托爾斯泰,俄國,俄語等。因為這些詞語在介紹《戰爭與和平》時肯定會出現,而有的讀者可能對這些詞語感興趣,因此,在《戰爭與和平》的頁面上會有跳往這些詞語的鏈接,這就是所謂的wikilinks。
因此,該推薦系統基於假設:鏈接到相似的維基百科頁面的書籍彼此相似。這句話的意義就很明顯了,如果2本書籍的wikilinks都指向了列夫托爾斯泰,俄國,俄語等,那麽這2本書可能就是很相似。比如說《戰爭與和平》《安娜·卡列尼娜》。)
這是一本書和它的wikilinks:
1.3 數據集清洗
數據下載完成後,我們需要對其進行探索和清洗,此時你可能會發現一些原始數據之間的關系。如下圖,展示了與維基百科圖書中的頁面關聯性最強的wikilinks:
從上圖可看出,排名前四的都是常用link,對構建推薦系統沒有任何幫助。就像書籍的裝訂版本,是平裝(paperback)還是精裝(hardcover)對我們了解圖書的內容沒有任何作用,並且神經網絡無法根據這個特征判別書籍是否相似。因此,可以選擇過濾掉這些無用的特征。
(註:我們指的相似性是內容的相似性)
仔細思考哪些數據對構建推薦系統是有幫助的,哪些是無用的,有用的保留,無用的過濾,這樣的數據清洗工作才算到位。
完成數據清洗後,我們的數據集中剩余41758條wikilinks以及37020本圖書。接下來,我們需要引入有監督的機器學習方法。
二、監督學習
2.1 定義機器學習的任務
監督學習就是最常見的分類問題,即通過已有的訓練樣本去訓練得到一個最優模型,再利用這個模型將所有的輸入映射為相應的輸出,對輸出進行簡單的判斷從而實現分類的目的。基於我們預先給定的假設:類似的書籍會鏈接到類似的維基百科頁面,我們可將監督學習的任務定義為:給定(book title,wikilink)對,確定wikilink是否出現在書籍的維基百科頁面中。
我們將提供數十萬個由書籍名稱,wikilink以及標簽組成的訓練示例,同時給神經網絡提供一些正確的訓練示例,即數據集中包含的,以及一些錯誤的示例,以促使神經網絡學會區分wikilink是否出現在書籍的維基百科頁面中。
嵌入是為特定的任務而學習的,並且只與該問題有關。如果我們的任務是想要確定哪些書籍由Jane Austen撰寫,嵌入會根據該任務將Austen所寫的書映射在嵌入空間中更相鄰的地方。或者我們希望通過訓練來判斷書籍的頁面中是否有指定的wikilink頁面,此時神經網絡會根據內容使相似書籍在嵌入空間中相鄰。
一旦我們定義了學習任務,接下來便可開始編寫代碼進行實現。由於神經網絡只能接受整數輸入,我們會將書籍分別映射為整數:
# Mapping of books to index and index to books
book_index = {book[0]: idx for idx, book in enumerate(books)}
book_index[‘Anna Karenina‘]
22494
(註:本文的代碼是Git中的部分代碼,完整代碼的獲取請參看GitHub)
對鏈接我們也進行同樣的映射,並創建一個訓練集。對所有書籍進行遍歷,並記錄頁面上記錄出現的wikilink,列出所有的(book,wikilink)對:
pairs = []
# Iterate through each book
for book in books:
title = book[0]
book_links = book[2]
# Iterate through wikilinks in book article
for link in book_links:
# Add index of book and index of link to pairs
pairs.extend((book_index[title],
link_index[link]))
最終有772798個示例用於模型訓練。接下來,隨機選擇鏈接索引和book索引,如果它們不在(book,wikilink)對中,那麽它們就是能用於增強模型的學習能力false examples。
2.2 關於訓練集及測試集的劃分
雖然在有監督的機器學習任務中需要劃分驗證集(validation set)以及測試集,但本文的目的不在於得到精確的模型,只是想訓練神經網絡模型完成預測任務。訓練結束後,我們也不需要在新的數據集中測試我們的模型,所以並不需要評估模型的性能或者使用驗證集以防止過擬合。為了更好的學習嵌入,我們將所有的示例都用於訓練。
2.3 嵌入模型
神經網絡嵌入雖然聽上去十分復雜,但使用Keras深度學習框架實現它們卻相對容易。
嵌入模型分為5層:
Input:並行輸入書籍和鏈接
Embedding:設置代表book和link兩個類別的向量長度為50
Dot:進行點積運算
Reshape:把點積reshape成一個一維向量
Dense:一個帶sigmod激活函數的輸出神經元
在嵌入神經網絡中,能夠通過訓練權重最小化損失函數。神經網絡將一本書和一個鏈接作為輸入,輸出一個0到1之間的預測值,並與真實值進行比較,模型采用Adam優化器。
模型代碼如下:
from keras.layers import Input, Embedding, Dot, Reshape, Dense
from keras.models import Model
def book_embedding_model(embedding_size = 50, classification = False):
"""Model to embed books and wikilinks using the Keras functional API.
Trained to discern if a link is present in on a book‘s page"""
# Both inputs are 1-dimensional
book = Input(name = ‘book‘, shape = [1])
link = Input(name = ‘link‘, shape = [1])
# Embedding the book (shape will be (None, 1, 50))
book_embedding = Embedding(name = ‘book_embedding‘,
input_dim = len(book_index),
output_dim = embedding_www.leyouzaixian2.com size)(book)
# Embedding the link (shape will be (None, 1, 50))
link_embedding www.furong157.com= Embedding(name = ‘link_embedding‘,
input_dim = len(link_index),
output_dim = embedding_size)(link)
# Merge the layers with a dot product along the second axis
# (shape will be (None, 1, www.michenggw.com 1))
merged = Dot(name = ‘dot_product‘, normalize = True,
axes = 2)([book_embedding, link_embedding])
# Reshape to be a single number (shape will be (None, 1))
merged = Reshape(target_shape = [1])(merged)
# Squash outputs for classification
out = Dense(1, activation = ‘sigmoid‘)(merged)
model = Model(inputs =www.gcyl159.com [book, link], outputs = out)
# Compile using specified optimizer and loss
model.compile(optimizer www.gcyl152.com/= ‘Adam‘, loss = ‘binary_crossentropy‘,
metrics = [‘accuracy‘])
return model
這個框架可以擴展至各類嵌入模型。並且,我們並不關心模型是否精準,只想獲取嵌入。在嵌入模型中,權重才是目標,預測只是學習嵌入的手段。
(註:這句話不明白的話,可以參看我之前介紹embedding的博客)
本模型約含400萬個權重,如下所示:
__________________________________________________________________________________________________
Layer (type) Output Shape Param # Connected to
==================================================================================================
book (InputLayer) (None, 1) 0
__________________________________________________________________________________________________
link (InputLayer) (None, 1) 0
__________________________________________________________________________________________________
book_embedding (Embedding) (None, 1, 50) 1851000 book[0][0]
__________________________________________________________________________________________________
link_embedding (Embedding) (None, 1, 50) 2087900 link[0][0]
__________________________________________________________________________________________________
dot_product (Dot) (None, 1, 1) 0 book_embedding[0][0]
link_embedding[0][0]
__________________________________________________________________________________________________
reshape_1 (Reshape) (None, 1) 0 dot_product[0][0]
==================================================================================================
Total params: 3,938,900
Trainable params: 3,938,900
Non-trainable params: 0
利用上述方法,我們不僅可以得到書籍的嵌入,還可以得到鏈接的嵌入。
2.4 生成訓練示例
神經網絡是batch learners,因為它們是基於一小批樣本進行訓練的,對所有的數據批次都進行了一次叠代稱為epochs。常用的神經網絡訓練方法是使用生成器,它能產生批量樣本函數,優點是不需要將所有的訓練集都加載到內存中。
下面的代碼完整地顯示了生成器:
import numpy as np
import random
random.seed(100)
def generate_batch(pairs, n_positive = 50, negative_ratio = 1.0):
"""Generate batches of samples for training.
Random select positive samples
from pairs and randomly select negatives."""
# Create empty array to hold batch
batch_size = n_positive * (1 + negative_ratio)
batch = np.zeros((batch_size, 3))
# Continue to yield samples
while True:
# Randomly choose positive examples
for idx, (book_id, link_id) in enumerate(random.sample(pairs, n_positive)):
batch[idx, :] = (book_id, link_id, 1)
idx += 1
# Add negative examples until reach batch size
while idx < batch_size:
# Random selection
random_book = random.randrange(len(books))
random_link = random.randrange(len(links))
# Check to make sure this is not a positive example
if (random_book, random_link) not in pairs_set:
# Add to batch and increment index
batch[idx, :] = (random_book, random_link, neg_label)
idx += 1
# Make sure to shuffle order
np.random.shuffle(batch)
yield {‘book‘: batch[:, 0], ‘link‘: batch[:, 1]}, batch[:, 2]
其中n_positive表示每個batch中正例樣本的數量,negative_ration表示每個batch中負例樣本與正例樣本的比率。
在有監督的學習任務、生成器、嵌入模型都準備完畢的情況下,我們正式進入圖書推薦系統的構建。
2.5 訓練模型
有一些訓練參數是可以調節的,如每個批次中正例樣本的數量。通常,我會從一小批量開始嘗試,直到性能開始下降。同樣,我們需要通過嘗試調整負例樣本與正例樣本的比率。
n_positive = 1024
gen = generate_batch(pairs, n_positive, negative_ratio = 2)
# Train
h = model.fit_generator(gen, epochs = 15,
steps_per_epoch = len(pairs) // n_positive)
一旦神經網絡開始訓練,我們就能獲取權重:
# Extract embeddings
book_layer = model.get_layer(‘book_embedding‘)
book_weights = book_layer.get_weights()[0]
三、構建推薦系統
嵌入本身不那麽有趣,無非是50維向量。
然而我們可以利用這些向量做些有趣的事,例如構建圖書推薦系統。為了在嵌入空間中找到與所查詢書籍最接近的書,我們取那本書的向量,並計算它與所有其他書的向量的點積。如果我們的嵌入是標準化的,那麽向量之間的範圍會從-1,最不相似,到+1,最相似。
(註:在代碼裏先進行了l2範數的標準化,因此,2個向量點積之後就是余弦相似度,範圍從-1到1)
以查詢《戰爭與和平》為例,相似書籍如下:
除了對書籍進行嵌入,我們也對wikilink也做了嵌入,以此查詢與wikilink最為相似的鏈接:
再比如,目前,我正在閱讀Stephen Jay Gould的經典著作《Bully for Brontosaurus》,將其輸入構建的推薦系統便可以知道接下來應該讀什麽:
(註:通過上述的介紹,我們已經看到了如何利用神經網絡的embeddding來構建推薦系統。本質上,這仍然是一個協調過濾的思想,即根據相似性來尋找某本書籍的最近鄰居,然後把最近鄰居推薦給喜歡某本書籍的人。但是,這裏與傳統的協調過濾方法明顯的不同的地方有:
①基於embedding的推薦方法並不要求書籍向量的”同一化“,即某本書不需要再像以前那樣,必須由長度完全相等的向量來表示,在現在世界中,構造這樣的數據集很困難,即便構造出來,數據也很稀疏。
②基於embedding的推薦方法可以通過”關系“來捕捉相似性,以此來保證embedding後的向量仍然可以保證這種相似性。我們在這裏理解維基百科的數據,可以認為書名到wikilinks存在著某種關系。那麽給我們的啟發是什麽呢?比如,我們想求得成人奶粉這個類目下所有sku的相似性,我們就可以類似文中構造訓練集的方法那樣來構造關於成人奶粉sku的數據集。)
四、嵌入可視化
嵌入的優點是可以將所學到的嵌入進行可視化處理,以顯示哪些類別是相似的。首先需要將這些權重的維度降低為2-D或3-D。然後,在散點圖上可視化這些點,以查看它們在空間中的分離情況。目前最流行的降維方法是——t-Distributed Stochastic Neighbor Embedding (TSNE)。
我們將37000多維的圖書通過神經網絡嵌入映射為50維,接著使用TSNE將維數將至為2。
似乎有一些明顯的團塊。然而,由於我們沒有以任何方式區分書籍,因此很難從上圖中得出有任何意義的東西。
我們需要其它的一些信息讓我們看到我們對圖書做的embedding是有效的。
在數據集裏,有個字段是圖書的類別genre。
讓我們用genre畫出embeddings,它包含在每本書的Infobox模板數據中。我們將其限制為10種最流行的genres。
我們可以看到,經過embedding後的圖書,原來類別很相似的,降維後仍然相似。
更多可視化的探索可以參看源代碼。
五、總結
神經網絡嵌入能夠將離散的數據表示為連續的低維向量,克服了傳統編碼方法的局限性,能查找最近鄰,作為另一個模型的輸入以及進行可視化,是處理離散變量的有效工具,也是深度學習的有效應用。在本文中,我們基於鏈接到相似頁面間彼此相似的假設,利用神經網絡嵌入構建了圖書推薦系統。
構建基於神經網絡嵌入的推薦系統的步驟總結如下:
收集數據
制定一個有監督的學習任務
訓練嵌入神經網絡模型
進行推薦實戰及可視化
參考文獻
【1】基於神經網絡嵌入的推薦系統:利用深度學習和維基百科構建圖書推薦系統
【2】Wikipedia Data Science: Working with the World’s Largest Encyclopedia
【3】 Neural Network Embeddings Explained
【4】How to Use t-SNE Effectively
【5】詮釋數據降維算法:一文講盡t-分布鄰域嵌入算法(t-SNE)如何有效利用
基於神經網絡的embeddding來構建推薦系統