1. 程式人生 > >機器學習 | 簡介推薦場景中的協同過濾演算法,以及SVD的使用

機器學習 | 簡介推薦場景中的協同過濾演算法,以及SVD的使用

本文始發於個人公眾號:**TechFlow**,原創不易,求個關注

今天是機器學習專題的第29篇文章,我們來聊聊SVD在上古時期的推薦場景當中的應用。

推薦的背後邏輯

有沒有思考過一個問題,當我們在淘寶或者是某東這類電商網站購物的時候。我們一進首頁,就會看到首頁展出了很多商品。這些商品往往質量很高,很吸引人,一旦逛起來可能就沒個結束。那麼問題來了,電商平臺擁有那麼多商品,它是怎麼知道我們可能會喜歡什麼樣的商品的呢?這背後的邏輯是什麼?

簡單來說在這背後,平臺端的演算法做了兩件事情,第一件事情是召回,第二件事情是排序。本質上來說和搜尋引擎做的事情是類似的,只是不同的是搜尋的時候我們有搜尋詞作為輸入,而首頁的推薦是沒有任何顯性的輸入資訊的。所以召回的時候只能根據使用者畫像的一些特徵和使用者之前在平臺上的行為來作為特徵召回商品,召回了商品之後再用一個模型預估使用者點選的概率,根據這個概率進行排序。

雖然召回-排序的框架沒有變,但是召回的演算法、邏輯以及排序的演算法和邏輯一直在迭代。尤其是召回模型,從一開始的協同過濾再到後來的向量召回、雙塔模型以及樹模型等等,有了巨大的進步,模型的效果自然也有了一個質的飛躍。

今天我們來著重聊聊協同過濾,雖然這個模型非常簡單,目前也幾乎已經退出歷史舞臺了,但是這不妨礙它仍然是一個經典的演算法,值得我們學習。

協同過濾的原理

協同過濾的原理非常簡單,一句話概括,就是尋找相似的商品以及相似的人。

因為在平臺當中的商品和人可能數量都非常大,當我們要進行推薦的時候,我們不可能窮舉所有的商品來進行預測點選率,這顯然是機器無法抗住的。所以我們希望把使用者在平臺上的行為使用起來,讓使用者的行為給平臺作為指引。根據使用者的行為尋找出行為相似的使用者以及相似的商品。

img

所以協同過濾有兩套邏輯,也可以認為是兩種做法。第一種做法是user-based也就是尋找偏好相似的使用者,這個不難理解,比如說經常買文具、買書的大概率是學生。假設我們知道了A和B行為相似,也就是說他們可能有相似的喜好。那麼假設A購買過商品1並且給出了好評,而B沒有購買過,那麼很有可能B也會喜歡這個商品,所以我們就可以推薦給B。

第二種做法自然就是item-based,比如你搜索點選了一個商品A,平臺會將和這個商品類似的商品BCD推薦給你,會放在商品詳情頁的下方的猜你喜歡當中。比如你看的是襯衫,它可能會給你推薦別家的襯衫,也可能給你推薦西褲或者是領帶。本質上邏輯是一樣的,因為這些商品和這件襯衫的相關度比較高。

下一個問題是使用者和使用者,商品和商品之間的相關度是怎麼來的呢?

答案很簡單,是通過這個矩陣來的:

我們觀察一下這個矩陣,這是一個使用者和商品的相關行為矩陣,每一行表示一個使用者的行為,每一列表示每一個商品的銷售情況。也就是說我們可以用這個矩陣當中的行向量表示使用者,列向量表示商品。既然我們把使用者和商品用向量表示出來了,接下來的事情就很簡單了,我們只需要計算向量之間的相似度就可以找到相似的使用者以及商品了。

我們要計算向量的相似度有很多種辦法,我們可以計算兩個向量的餘弦值,可以計算歐式距離、皮爾遜值等等。

SVD的作用

其實到這裡關於協同過濾就介紹完了,但問題是這和SVD看起來好像沒什麼關係呀?

我們仔細琢磨一下就能發現它們之間的關係,對於規模比較小的公司或者場景來說,這當然是沒問題的。比如說電影評分網站,因為電影的數量往往不會很大,充其量也在萬這個量級,所以這個矩陣可能還是存的下的。如果是電商公司,商品和使用者都是億這個維度的,這個矩陣顯然是非常巨大的,根本不可能在記憶體當中儲存得下,更別提相似度計算了。並且這樣的矩陣必然存在大量稀疏和空缺,我們將它使用SVD壓縮也是非常合理的做法。

首先我們開發出一個輔助函式,根據我們設定的百分比計算出最少需要的奇異值的數量:

def select_K(sigma, percentage):
    square = sigma**2 
    base = sum(square) 
    s = 0 
    k = 0
    for i in sigma:
        s += i**2
        k += 1
        if s >= base * percentage:
            return k

其次我們對原矩陣進行svd分解,並且設定閾值對原矩陣進行壓縮:

data = np.mat([[0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 5],
           [0, 0, 0, 3, 0, 4, 0, 0, 0, 0, 3],
           [0, 0, 0, 0, 4, 0, 0, 1, 0, 4, 0],
           [3, 3, 4, 0, 0, 0, 0, 2, 2, 0, 0],
           [5, 4, 5, 0, 0, 0, 0, 5, 5, 0, 0],
           [0, 0, 0, 0, 5, 0, 1, 0, 0, 5, 0],
           [4, 3, 4, 0, 0, 0, 0, 5, 5, 0, 1],
           [0, 0, 0, 4, 0, 4, 0, 0, 0, 0, 4],
           [0, 0, 0, 2, 0, 2, 5, 0, 0, 1, 2],
           [0, 0, 0, 0, 5, 0, 0, 0, 0, 4, 0],
           [1, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0]])

u, sigma, v = np.linalg.svd(data)
k = select_K(sigma, 0.95) 
sigmaK = np.mat(np.eye(k) * sigma[:k]) 
itemMat = data.T.dot(u[:,:k]).dot(sigmaK.I)

最後壓縮之後得到的是item的矩陣,其中的每一個行向量對應一個item。

這只是一個模擬,如果是在實際上的應用,我們可以將幾億甚至是更多的維度壓縮到幾百甚至更少,極大的縮減了儲存所需要的開銷。而且svd的計算是可以分散式併發進行的,所以即使原始資料非常龐大,也是可以支撐的。

總結

到這裡關於協同過濾演算法以及SVD的應用就結束了,雖然演算法非常簡單,實現起來也容易,但是這其中還有很多問題沒有解決。比如說這個使用者和商品的矩陣並不是一成不變的,因為我們隨時都會有新商品上架以及新使用者註冊,對於這些沒有行為的新商品和新使用者應該怎麼辦?

另外一個問題是,這個演算法沒有改進的空間,一旦實現完成了上線之後,我們做不了太多的改進。如果是其他的模型或者是演算法,我們可以通過迭代演算法以及模型的方法來獲取更好的效果,但是協同過濾不行。這也是為什麼逐漸被淘汰的原因。

今天的文章到這裡就結束了,如果喜歡本文的話,請來一波素質三連,給我一點支援吧(關注、轉發、點贊)。

本文使用 mdnice 排版

![](https://user-gold-cdn.xitu.io/2020/7/28/1739369fb0f0b2d2?w=258&h=258&f=png&