【機器學習】使用Python中的區域性敏感雜湊(LSH)構建推薦引擎
學習如何使用LSH在Python中構建推薦引擎; 一種可以處理數十億行的演算法
你會學到:
在本教程結束時,讀者可以學習如何:
- 通過建立帶狀皰疹來檢查和準備LSH的資料
- 選擇LSH的引數
- 為LSH建立Minhash
- 使用LSH Query推薦會議論文
- 使用LSH構建各種型別的推薦引擎
目錄
區域性敏感雜湊(LSH)建議簡介
本教程將提供構建建議引擎的分步指南。我們將根據標題和摘要推薦會議論文。推薦引擎有兩種主要型別:基於內容和協同過濾引擎。
- 基於內容建議僅使用有關所推薦專案的資訊。沒有關於使用者的資訊。
- 協作過濾利用使用者資訊。一般而言,資料包含每個使用者使用的每個專案的喜歡或不喜歡。喜歡和不喜歡可能是隱含的,例如使用者觀看整部電影或明顯像使用者給電影豎起大拇指或良好的星級評級。
通常,推薦引擎基本上是尋找具有相似性的專案。我們可以將相似性視為具有相對較大交叉點的發現集。我們可以收集文件,電影,網頁等專案的任何集合,並收集一組稱為“帶狀皰疹”的屬性。
帶狀皰疹
帶狀皰疹是一個非常基本的廣泛概念。對於文字,它們可以是字元,unigrams或bigrams。您可以使用一組類別作為帶狀皰疹。帶狀皰疹只是將我們的文件簡化為元素集,以便我們可以計算集合之間的相似性。
對於我們的會議論文,我們將使用從論文標題和摘要中提取的unigrams。如果我們有關於一組使用者喜歡哪些論文的資料,我們甚至可以將使用者用作帶狀皰疹。通過這種方式,您可以執行基於專案的協作過濾,其中您搜尋已由相同使用者評價為肯定的專案的MinHash。
同樣,您可以將這個想法放在頭上,並使專案成為基於使用者的協作過濾的帶狀皰疹。在這種情況下,您將找到與其他使用者具有相似正評級的使用者。
讓我們拿兩個紙質標題並將它們轉換成一組unigram帶狀皰疹:
- 標題1 =“使用增強神經網路的強化學習”
帶狀皰疹=['reinforcement', 'learning', 'using', 'augmented', 'neural', 'networks']
- 標題2 =“使用深度強化學習玩Atari”
帶狀皰疹=['playing', 'atari', 'with', 'deep', 'reinforcement', 'learning']
現在,我們可以通過觀察兩組之間的帶狀皰疹交叉的視覺表示來找到這些標題之間的相似性。在這個例子中,帶狀皰疹的總數(聯合)是10,而2是交叉的一部分。我們將相似度測量為2/10 = 1/5。
集合的交集,其中元素是學術論文標題中的單詞
為何選擇LSH?
LSH可以被認為是降維的演算法。當我們推薦大型資料集中的專案時出現的問題是,可能有太多專案來計算每對專案的相似性。此外,我們可能會為所有專案提供少量重疊資料。
為了解這種情況,我們可以考慮在儲存所有會議論文時建立的詞彙表矩陣。
傳統上,為了提出建議,我們將為每個文件新增一列,並為每個遇到的單詞新增一行。由於論文的文字內容差異很大,我們每列都會得到很多空行,因此稀疏性。為了提出建議,我們將通過檢視哪些詞是共同的來計算每一行之間的相似性。
這些問題激發了LSH技術。通過將行壓縮為“簽名”或整數序列,可以將此技術用作更復雜的建議引擎的基礎,這樣我們就可以比較論文而無需比較整個單詞集。
與更傳統的推薦引擎相比,LSH主要加快推薦流程。這些模型也可以更好地擴充套件。因此,定期在大型資料集上重新訓練推薦引擎的計算密集程度遠低於傳統推薦引擎。在本教程中,我們將介紹推薦類似NIPS會議論文的示例。
商業用途
推薦引擎非常受歡迎。當您使用計算機,手機或平板電腦時,您很可能經常與推薦引擎進行互動。我們都在Netflix,亞馬遜,Facebook,谷歌等網路應用程式上向我們推薦過推薦。以下是可能的用例的幾個示例:
- 向客戶推薦產品
- 預測客戶對產品的評級
- 根據調查資料建立消費者群體
- 推薦工作流程中的後續步驟
- 推薦最佳實踐作為工作流程步驟的一部分
- 檢測抄襲
- 找到接近重複的網頁和文章
LSH技術概述
LSH用於基於“相似性”的簡單概念執行最近鄰搜尋。
我們說如果它們的集合的交集足夠大,則兩個專案是相似的。
這與集合的Jaccard相似性完全相同。回想一下上面相似的圖片。我們對1/5相似性的最終衡量標準是Jaccard Similarity。
注: Jaccard相似度定義為兩組的交集除以兩組的並集。
注意,可以使用其他相似度量,但我們將嚴格使用Jaccard Similarity來完成本教程。LSH是一種基於鄰域的方法,如k近鄰(KNN)。正如您在下表中所看到的,與LSH相比,KNN等方法的規模較差。
n :專案數量:東西的個數
p :排列數
LSH的強大之處在於,隨著物品數量的增長,它甚至可以使用Forest技術進行亞線性擴充套件。如前所述,目標是找到類似於查詢集的集合。
一般方法是:
- 雜湊專案使得類似專案很有可能進入同一個桶。
- 將相似性搜尋限制為與查詢項關聯的儲存桶。
將文字轉換為一組帶狀皰疹
請記住,木瓦是一個單獨的元素,可以像字元,單字元或雙字母組合一樣成為集合的一部分。在標準文獻中,存在木瓦大小k的概念ķ,帶狀皰疹的數量等於20ķ20ķ。當你選擇你的帶狀皰疹時,你會隱含地選擇你的木瓦大小。
讓我們使用字母表中的字母作為我們的帶狀皰疹的例子。你會有26個帶狀皰疹。對於我們的字母帶狀皰疹,k = 1,其中大約是經常使用的字母表中不同字元的數量。
當將檔案整理成unigrams時,我們會發現有很多可能的單詞,即我們將使用大的k值k。下表給出了相對於木瓦大小k的帶狀皰疹數量的快速檢視k。
在選擇帶狀皰疹時,必須有足夠的獨特帶狀皰疹,以便在給定檔案中出現木瓦的可能性很低。如果在所有集合中過於頻繁地出現木瓦,則不會在集合之間提供顯著區別。自然語言處理(NLP)中的經典示例是“the”。這太常見了。大多數NLP工作會刪除正在考慮的所有檔案中的“the”一詞,因為它沒有提供任何有用的資訊。同樣,您不希望使用將在每組中顯示的帶狀皰疹。它降低了該方法的有效性。你也可以看到這個想法與TF-IDF一起使用。出現在太多文件中的術語會打折扣。
由於您希望任何單個木瓦出現在任何給定文件中的概率較低,我們甚至可能會看到包含雙字母組,三元組或更大組的帶狀皰疹。如果兩個文件共有大量的雙字母組或三元組,則表明這些文件非常相似。
在尋找適當數量的帶狀皰疹時,請檢視文件中出現的平均字元數或令牌數。確保選擇足夠的帶狀皰疹,使得帶狀皰疹明顯多於檔案中平均帶狀皰疹的數量。
帶狀皰疹的例子
對於本文,我們將推薦會議論文。因此,我們將通過一個會議論文標題的快速示例:
“關聯資料庫的自組織及其應用”
如果我們使用unigrams作為帶狀皰疹,我們可以刪除所有標點,停用詞和小寫所有字元以獲得以下內容:
['self', 'organization', 'associative', 'database', 'applications']
請注意,我們刪除了常見的停用詞,例如“of”,“and”和“its”,因為它們提供的價值很小。
MinHash簽名
MinHash的目標是用一個較小的“簽名”替換一個仍然保留底層相似性度量的大型集合。
為每個集建立一個MinHash簽名:
- 隨機地置換木瓦矩陣的行。例如,行12345 => 35421,所以如果“強化”在第1行,它現在將在第5行。
- 對於每一組(在我們的例子中為紙質標題),從頂部開始,找到出現在集合中的第一個木瓦的位置,即第一個木瓦在其單元格中有1。使用此木瓦編號表示集合。這是“簽名”。
- 根據需要重複多次,每次將結果附加到集合的簽名。
讓我們通過一個具體的例子,我們已經提到了三個論文題目。我們已經把標題拼成了一組unigrams,所以我們可以把它們放到一個矩陣中並執行上面提到的所有步驟:
幻燈片1
在左側,我們定義矩陣,列為列,行定義為三個標題中遇到的所有單詞。如果標題中出現一個單詞,我們會在該單詞旁邊放置一個單詞。這將是我們的雜湊函式的輸入矩陣。
請注意,我們在右邊的簽名矩陣中的第一條記錄就是我們首先找到1的當前行號。有關更詳細的說明,請參閱下一張幻燈片。
幻燈片2
我們執行第一個實行排列。請注意,單字組已經重新排序。同樣,我們在右側也有簽名矩陣,我們將記錄排列的結果。
在每個排列中,我們記錄標題中出現的第一個單詞的行號,即具有第一個單詞的行。在此排列中,標題1和標題2的第一個單字組都在第1行中,因此記錄1標題1和2下的簽名矩陣的第1行。對於標題3,第一個單字組在第5行,因此在標題3的列下的簽名矩陣的第一行中記錄5。
幻燈片3-4
我們基本上執行與幻燈片2相同的操作,但我們在簽名矩陣中為每個排列建立一個新行並將結果放在那裡。
到目前為止,我們已經完成了三種排列,我們實際上可以使用簽名矩陣來計算紙質標題之間的相似性。請注意我們如何將行列表中的15行壓縮到簽名矩陣中的3行。
在MinHash過程之後,每個會議論文將由MinHash簽名表示,其中行數現在遠小於原始木瓦矩陣中的行數。這是因為我們通過執行行排列來捕獲簽名。你應該比實際的帶狀皰疹需要更少的排列。
現在,如果我們考慮可能出現在所有紙質標題和摘要中的所有單詞,我們可能有數百萬行。通過行排列生成簽名,我們可以有效地將行從數百萬減少到數百,而不會喪失計算相似性得分的能力。
顯然,您置換行的次數越多,簽名就越長。這為我們提供了類似會議論文具有類似簽名的最終目標。實際上,您可以觀察以下內容:
為了更有效,在實踐中使用隨機雜湊函式而不是行的隨機排列。
關於MinHash簽名的LSH
上面的簽名矩陣現在被分成bbr的樂隊[R每個行,每個帶分開雜湊。對於此示例,我們設定band b=2b=2,這意味著我們會考慮前兩行相同的任何標題是相似的。我們製作的b越大,另一篇論文就越不可能匹配所有相同的排列。
例如,注意上面矩陣中的第一個頻段,會議論文題目1和會議論文題目2將最終在同一個桶中,因為它們都具有相同的頻段,而標題3完全不同。即使標題1和標誌2的簽名不同,它們仍然被拼湊在一起,並且被認為與我們的樂隊大小相似。
最終,頻帶的大小控制具有給定Jaccard相似性的兩個專案在同一桶中結束的概率。如果波段數量較大,則最終會得到更小的波段。例如,b=pb=p,其中p是排列的數量(即簽名矩陣中的行)幾乎肯定會導致只有一個專案的N個桶,因為在每個排列中只有一個專案完全相似。當我們選擇頻寬時,我們真正想要的是我們對誤報的容忍度(沒有類似的文件在同一個桶中結束)和漏報(類似的文件不會在同一個桶中結束)。
查詢建議書
在進行新查詢時,我們使用以下過程:
- 將查詢文字轉換為帶狀皰疹(標記)。
- 將MinHash和LSH應用於木瓦集,將其對映到特定儲存桶。
- 在查詢項和儲存桶中的其他項之間進行相似性搜尋。
LSH清單
作為執行LSH的最終清單,請確保完成以下步驟:
- 從您的可用資料集建立帶狀皰疹(例如,unigrams,bigrams,等級等)
- 建立一個m×n米×ñ 木瓦矩陣,其中出現在集合中的每個木瓦都標記為1,否則為0。
- 置換步驟2中的木瓦矩陣的行並構建新的p×np×ñ 簽名矩陣,其中為集合出現的第一個瓦片的行的編號被記錄用於簽名矩陣的置換。
- 重複置換輸入矩陣的行[ 數學處理錯誤]p時間和完全填充p×np×ñ 簽名矩陣。
- 選擇一個樂隊大小bb 對於您將在LSH矩陣中的集合之間進行比較的行數。
在PYTHON中實現LSH
第1步:載入Python包
import numpy as np
import pandas as pd
import re
import time
from datasketch import MinHash,MinHashLSHForest
第2步:探索您的資料
我們在本程中的目標是通過使用LSH快速查詢所有已知的會議論文來對會議論文提出建議。作為一般規則,您應該始終檢查您的資料。您需要徹底瞭解資料集,以便正確預處理資料並確定最佳引數。我們已經給出了一些選擇引數的基本指南,它們都需要如上所述探索資料集。
出於本教程的目的,我們將使用簡單的資料集。Kaggle有“神經資訊處理系統(NIPS)會議論文。你可以在這裡找到它們。
這些論文的初步資料探索可以在這裡找到。
第3步:預處理您的資料
出於本文的目的,我們將僅使用粗略版本的unigrams作為我們的帶狀皰疹。我們使用以下步驟:
- 刪除所有標點符號。
- 小寫所有文字。
- 通過分隔任何空白區域來建立unigram帶狀皰疹(標記)。
為了獲得更好的效果,您可以嘗試使用自然語言處理庫(如NLTK或spaCy)來生成unigrams和bigrams,刪除停用詞以及執行詞形還原。
#預處理程式將根據空格將一串文字分割成單獨的tokens/帶狀皰疹。
def preprocess(text):
text = re.sub(r'[^\w\s]','',text)
tokens = text.lower()
tokens = tokens.split()
return tokens
您可以在下面看到預處理步驟的快速示例。
text = 'The devil went down to Georgia'
print('The shingles (tokens) are:', preprocess(text))
The shingles (tokens) are: ['the', 'devil', 'went', 'down', 'to', 'georgia']
第4步:選擇引數
為了開始我們的例子,我們將使用128的標準排列數。我們也將首先提出一個建議。
#排列
permutations = 128
#要返回的建議書數量
num_recommendations = 1
第5步:為查詢建立Minhash Forest
為了建立Minhash Forest,我們將執行以下步驟:
- 使用要查詢的每個字串傳入資料框。
- 使用上面的預處理步驟預處理一串文字。
- 設定MinHash中的排列數。
- MinHash字串中所有帶狀皰疹的字串。
- 儲存字串的MinHash。
- 對資料框中的所有字串重複2-5。
- 構建所有MinHashed字串的林。
- 索引您的森林以使其可搜尋。
def get_forest(data, perms):
start_time = time.time()
minhash = []
for text in data['text']:
tokens = preprocess(text)
m = MinHash(num_perm=perms)
for s in tokens:
m.update(s.encode('utf8'))
minhash.append(m)
forest = MinHashLSHForest(num_perm=perms)
for i,m in enumerate(minhash):
forest.add(i,m)
forest.index()
print('It took %s seconds to build forest.' %(time.time()-start_time))
return forest
第6步:評估查詢
為了查詢構建的林,我們將按照以下步驟操作:
- 將文字預處理為帶狀皰疹。
- 為您的MinHash設定與用於構建林的相同數量的排列。
- 使用所有帶狀皰疹在文字上建立MinHash。
- 使用MinHash查詢林並返回請求的推薦數。
- 提供推薦的每個會議論文的標題。
def predict(text, database, perms, num_results, forest):
start_time = time.time()
tokens = preprocess(text)
m = MinHash(num_perm=perms)
for s in tokens:
m.update(s.encode('utf8'))
idx_array = np.array(forest.query(m, num_results))
if len(idx_array) == 0:
return None # if your query is empty, return none
result = database.iloc[idx_array]['title']
print('It took %s seconds to query forest.' %(time.time()-start_time))
return result
在NIPS會議論文上測試我們的推薦引擎
我們將首先載入包含所有會議論文的CSV並建立一個將標題和摘要合併到一個欄位中的新欄位,這樣我們就可以使用title和abstract來構建帶狀皰疹。
最後,我們可以查詢任何文字字串,例如標題或一般主題,以返回推薦列表。請注意,對於下面的示例,我們實際上選擇了會議論文的標題。當然,我們將確切的論文作為我們的建議之一。
db = pd.read_csv('papers.csv')
db['text'] = db['title'] + ' ' + db['abstract']
forest = get_forest(db, permutations)
It took 12.728999853134155 seconds to build forest.
num_recommendations = 5
title = 'Using a neural net to instantiate a deformable model'
result = predict(title, db, permutations, num_recommendations, forest)
print('\n Top Recommendation(s) is(are) \n', result)
It took 0.006001949310302734 seconds to query forest.
Top Recommendation(s) is(are)
995 Neural Network Weight Matrix Synthesis Using O...
5 Using a neural net to instantiate a deformable...
5191 A Self-Organizing Integrated Segmentation and ...
2069 Analytic Solutions to the Formation of Feature...
2457 Inferring Neural Firing Rates from Spike Train...
Name: title, dtype: object
結論
正如快速的最後一點,您可以使用此方法構建大量推薦引擎。您不必僅限於我們演示的基於內容的過濾。您還可以將這些技術用於Collaborative Filtered Recommendation Engines。
總而言之,本教程中概述的過程代表了Locality-Sensitive Hashing的介紹。這裡的材料可以作為一般指導。如果您正在處理大量專案,並且您的相似度量是Jaccard相似度,則LSH提供了一種非常強大且可擴充套件的方式來提出建議。
原文: