一個簡單的推薦系統

      咱以電影電視的推薦系統爲例,一步一步的來實現一個簡單的推薦系統吧, 由於比較簡單,整個推薦系統源碼不到100行,大概70-80行吧,應該很容易掌握。 爲了快速開發原型,咱採用Python代碼來演示

1. 推薦系統的第一步,需要想辦法收集信息

不同的業務,不同的推薦系統需要收集的信息不一樣 針對咱要做的電影推薦,自然是每個用戶對自己看過的電影的評價了,如下圖所示:


RecommandationSystem_Estimate_Data


     Kai Zhou對Friends打分是4分, 對Bedtime Stories打分是3分,沒有對RoboCop打分 Shuai Ge沒有對Friends打分,對Bedtime Stories打分是3.5分 …… 爲簡單,咱將此數據存成csv文件,形成一個二維的矩陣,假設存在D:\train.csv, 數據如下:
Name,Friends,Bedtime Stories,Dawn of the Planet of the Apes,RoboCop,Fargo,Cougar Town
Kai Zhou,4,3,5,,1,2
Shuai Ge,,3.5,3,4,2.5,4.5
Mei Nv,3,4,2,3,2,3
xiaoxianrou,2.5,3.5,3,3.5,2.5,3
fengzhi,3,4,,5,3.5,3
meinv,,4.5,,4,1,
mincat,3,3.5,1.5,5,3.5,3
alex,2.5,3,,3.5,,4
先從csv文件中加載二維矩陣,代碼如下:  
def load_matrix():
 matrix = {}
 f = open("d:\\train.csv")
 columns = f.readline().split(',')
 
 for line in f:
 scores = line.split(',')
 for i in range(len(scores))[1:]:
 matrix[(scores[0], columns[i])] = scores[i].strip("\n")
 
 return matrix
 
matrix = load_matrix()
print "matrix:", matrix
load_matrix()解析csv文件,返回一個dictionary, 該dictionary以(行名,列名)爲索引

數據有了,下面咱就正式開始幹活了 ,推薦系統要幹些什麼呢?
咱以電影推薦來說,推薦系統需要解決的幾個主要問題:

1. 判斷兩個電影,兩個觀影人之間的相似度
2. 找到和某影片最相似的影片, 或找到和某觀影人有同樣興趣的人
3. 找到某觀影人可能喜歡的電影,或找到對某影片感興趣的人

2. 推薦系統的基礎,判斷相似度
     針對咱的電影推薦來說,就是判斷兩個電影,兩個觀影人之間的相似度。 2.1 歐幾里德距離計算相似度 最簡單的,最容易理解的就是歐幾里德距離. 那麼,什麼是歐幾里德距離,怎麼用呢? 請對比評價數據,看下圖:

咱用兩個電影Fargo和Cougr Town來取例 圖中X軸代表電影Fargo,  Y軸代表電影Cougr Town,  Kai Zhou給電影Fargo 打1分,Cougr Town打2分,畫到圖上
同理,咱可以將Shuai Ge和Mei Nv的數據點都畫到圖上 很明顯,咱可以看出Kai Zhou與Mei Nv 離得近,與Shuai Ge離得遠,所以說Kai Zhou與Mei Nv的興趣更相近. 用數學式子表達出來就是:
Kai Zhou與Mei Nv的距離的平方:    (2 – 1)^2 + (3 – 2)^2 = 2
Kai Zhou 與Shuai Ge的距離的平方: (2.5 – 1)^2 + (4.5 – 2)^2 = 8.5
2 < 8.5, 所以Kai Zhou與Mei Nv比Shuai Ge興趣更近. 這就是利用歐幾里得距離來判斷相似度   兩個用戶對所有電影的評價相似度的和,就是兩用戶的相似度
2.2 歸一化處理
爲了方便比較處理後的數據,一般還需要對計算出來的結果進行歸一化處理。
數據標準化(歸一化)處理是數據挖掘的一項基礎工作,不同評價指標往往具有不同的量綱和量綱單位,這樣的情況會影響到數據分析的結果,爲了消除指標之間的量綱影響,需要進行數據標準化處理,以解決數據指標之間的可比性。
原始數據經過數據標準化處理後,各指標處於同一數量級,適合進行綜合對比評價。
上面的介紹太學術化了吧,不容易懂,我的理解:歸一化化就是要把你需要處理的數據經過處理後(通過某種算法)限制在你需要的一 定範圍內。
簡單的說,我們希望,處理後的數據取值範圍在0-1之間. 在數學上有很多歸一化處理的方法 常用的有

一、min-max標準化(Min-Max Normalization)
也稱爲離差標準化,是對原始數據的線性變換,使結果值映射到[0 – 1]之間。

二、Z-score標準化方法
這種方法給予原始數據的均值(mean)和標準差(standard deviation)進行數據的標準化。經過處理的數據符合標準正態分佈,即均值爲0,
標準差爲1

咱可以根據需要選擇,不過,針對咱這系統採用的是歐幾里德距離,咱可以用下面的更簡單的公式:
假設計算出來的歐幾里德距離爲:n
1 / (1 + n)
當距離爲0,歸一化後的值爲:1
距離越大,歸一化後的值越接近0
有了上面的基礎知識之後,下面的代碼就水到渠成了
def sim_distance(matrix, row1, row2):
    columns = set(map(lambda l: l[1], matrix.keys()))
    si = filter(lambda l: matrix.has_key((row1, l)) and matrix[(row1, l)] != "" and matrix.has_key((row2, l)) and matrix[(row2, l)] != "", columns)
    if len(si) == 0: return 0
    sum_of_distance = sum([pow(float(matrix[(row1, column)]) - float(matrix[(row2, column)]), 2) for column in si])
    return 1 / (1 + sqrt(sum_of_distance))
 
print sim_distance(matrix, "Kai Zhou", "Shuai Ge")
3. 找到和和某觀影人有同樣興趣的人,某影片最相似的影片
a.有了上面的代碼,找到和某用戶有同樣興趣的人,就非常簡單了。只要將某用戶和其它所有用戶的相似度計算出來,排下序就行了。
def top_matches(matrix, row, similarity=sim_distance):
    rows = set(map(lambda l: l[0], matrix.keys()))
    scores = [(similarity(matrix, row, r), r) for r in rows if r != row]
    scores.sort()
    scores.reverse()
    return scores
 
person = "Kai Zhou"
print "top match for:", person
print top_matches(matrix, person)
 b. 找到和某影片相似的影片,這個需要稍微變化下。咱的輸入數據是以用戶爲行數據,影片爲列數據, 只要改成以影片爲行數據,用戶爲列數據,一樣的調用。 所以需要一個函數,將矩陣轉置
def transform(matrix):
    rows = set(map(lambda l: l[0], matrix.keys()))
    columns = set(map(lambda l: l[1], matrix.keys()))
 
    transform_matrix = {}
    for row in rows:
        for column in columns:
            transform_matrix[(column, row)] = matrix[(row, column)]
    return transform_matrix
找到和Friends 相似的影片:
Source code           
trans_matrix = transform(matrix)
print "trans:", trans_matrix
 
film = "Friends"
print "top match for:", film
print top_matches(trans_matrix, film)
4. 找到某觀影人可能喜歡的電影,找到對某影片感興趣的人
最理想的是找到兩個相似度一樣的人,可以認爲某個人喜歡的電影,另外那個也喜歡。 但是這樣有它的缺點,比較好的辦法是把所有人的數據都用上,方法如下:
1. 先計算所有人和Kai Zhou的相似度
2. 對於Kai Zhou沒有看過,沒有評分,而其它人有評分的的影片,  將其評分與相似度相乘,得到的值再除以相似度之和 3. 排序   咱先以給Kai Zhou推薦影片爲例來說明, Dawn of the Planet of the Apes 和 RoboCop 這兩部影片Kai Zhou都沒有看,我們該推薦他看哪部呢? 假設我們計算出來Kai Zhou與其它人的相似度如下:

[(0.3333333333333333, ‘Mei Nv’),
(0.29429805508554946, ‘xiaoxianrou’),
(0.2857142857142857, ‘alex’),
(0.2553967929896867, ‘mincat’),
(0.252650308587072, ‘Shuai Ge’),
即Kai Zhou與Mei Nv 相似度爲0.3333333333333333,  與xiaoxiaorou相似度爲0.29429805508554946,  其它類似… 那麼計算Dawn of the Planet of the Apes對Kai Zhou的推薦值過程如下:
1. 找到Shuai Ge對Dawn of the Planet of the Apes的評價值  乘以Shuai Ge與Kai Zhou的相似度:   3 *  0.252650308587072
2. 找到Mei Nv對Dawn of the Planet of the Apes的評價值  乘以其與Kai Zhou的相似度: 2 * 0.3333333333333333
3. 找到xiaoxianrou 對Dawn of the Planet of the Apes的評價值  乘以其與Kai Zhou的相似度: 3 * 0.29429805508554946
4. fengzhi 沒有對Dawn of the Planet of the Apes評價,不用計算
5. 找到mincat對Dawn of the Planet of the Apes的評價值  乘以其與Kai Zhou的相似度: 1.5 * 0.2553967929896867
6. alex 沒有對Dawn of the Planet of the Apes評價,不用計算
7. 將 1, 2, 3, 5 步的計算結果相加 得到: 3 *  0.252650308587072 + 2 * 0.3333333333333333 +  3 * 0.29429805508554946 +  1.5 * 0.2553967929896867 = 2.6906069471690612
8.  將1,2,3,5步的參與計算的人的相似度相加: 0.252650308587072 +  0.3333333333333333 + 0.29429805508554946 +  0.2553967929896867 = 1.1356784899956416
9.  將第7步結果除以第8步的結果,就是Dawn of the Planet對Kai Zhou的推薦值: 2.6906069471690612 / 1.1356784899956416 = 2.369162549851047

同樣的方法,計算出來RoboCop 對Kai Zhou的推薦值爲:3.9277923180363326 所以RoboCop應該對Kai Zhou的吸引力比Dawn of the Planet of the Apes更大. 代碼如下:
def get_recommendations(matrix, row, similarity=sim_distance):
    rows = set(map(lambda l: l[0], matrix.keys()))
    columns = set(map(lambda l: l[1], matrix.keys()))
 
    sum_of_column_sim = {}
    sum_of_column = {}
 
    for r in rows:
        if r == row: continue
        sim = similarity(matrix, row, r)
        if sim <= 0:  continue
 
        for c in columns:
            if matrix[(r, c)] == "": continue
 
            sum_of_column_sim.setdefault(c, 0)
            sum_of_column_sim[c] += sim
            sum_of_column.setdefault(c, 0)
            sum_of_column[c] += float(matrix[(r, c)]) * sim
 
    scores = [(sum_of_column[c] / sum_of_column_sim[c], c) for c in sum_of_column]
    scores.sort()
    scores.reverse()
    return scores
 
print get_recommendations(matrix, person)
找到對某影片感興趣的人和之前類似,需要將矩陣轉置就行了,代碼如下:
Source code           
trans_matrix = transform(matrix)
print get_recommendations(trans_matrix,  "Friends")
這就是一個簡單的推薦系統的雛型,當然,要實現一個可用的推薦系統,還有很多工作要做。比如推薦的精確度,用戶喜歡打鬥片,咱不可能給他推薦愛情片吧?比如數據量大了之後,性能問題,擴展性?是基於用戶推薦還是物品推薦?……



發佈了47 篇原創文章 · 獲贊 95 · 訪問量 16萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章