推荐系统浅浅的例子
? 對于推薦系統(tǒng),有很多的很強大的算法。這里作為練習(xí),只介紹基本的協(xié)同過濾算法(userbased)和FM(通過梯度下降的角度,還可以通過交替優(yōu)化的角度來看)。
? 這里的例子是在七月算法的視頻中看的,分析的內(nèi)容基于自己的理解并對代碼做了部分的勘誤。
(一)簡單的user-based協(xié)同過濾算法
? 先來看一眼數(shù)據(jù):
userss = {"小明": {"中國合伙人": 5.0, "太平輪": 3.0, "荒野獵人": 4.5,"老炮兒": 5.0, "我的少女時代": 3.0,"肖洛特?zé)?#34;: 4.5, "火星救援": 5.0},"小紅": {"小時代4": 4.0, "荒野獵人": 3.0, "我的少女時代": 5.0, "肖洛特?zé)?#34;: 5.0, "火星救援": 3.0,"后會無期": 3.0},"小陽": {"小時代4": 2.0, "中國合伙人": 5.0, "我的少女時代": 3.0, "老炮兒": 5.0, "肖洛特?zé)?#34;: 4.5,"速度與激情7": 5.0},"小四": {"小時代4": 5.0, "中國合伙人": 3.0, "我的少女時代": 4.0,"匆匆那年": 4.0, "速度與激情7": 3.5, "火星救援": 3.5, "后會無期": 4.5},"六爺": {"小時代4": 2.0, "中國合伙人": 4.0, "荒野獵人": 4.5,"老炮兒": 5.0, "我的少女時代": 2.0},"小李": {"荒野獵人": 5.0, "盜夢空間": 5.0, "我的少女時代": 3.0, "速度與激情7": 5.0, "蟻人": 4.5,"老炮兒": 4.0, "后會無期": 3.5},"隔壁老王": {"荒野獵人": 5.0, "中國合伙人": 4.0, "我的少女時代": 1.0, "Phoenix": 5.0,"甄嬛傳": 4.0, "The Strokes": 5.0},"鄰村小芳": {"小時代4": 4.0, "我的少女時代": 4.5,"匆匆那年": 4.5, "甄嬛傳": 2.5, "The Strokes": 3.0}}? 這里是假想的一份數(shù)據(jù),分別是每個用戶對于自己看過的電影的評分。這里要進行userbased協(xié)同過濾,首先就要解釋一下什么是userbased協(xié)同過濾;所謂userbased協(xié)同過濾,就是把一個用戶看成是一條記錄,然后看到的電影作為屬性,對應(yīng)的評分作為屬性值。然后通過計算兩條記錄(兩個user)之間的距離來衡量兩條記錄(兩個user)的相近度。最后把距離最近的K(自己指定)的users看過而該user未看過的電影推薦給該user。所以最重要的就是計算距離了,下面介紹幾種距離的計算方式:
(1)歐幾里得距離
??
def euclidean_dis(rating1, rating2):"""計算2個打分序列間的歐式距離. 輸入的rating1和rating2都是打分dict格式為{'小時代4': 1.0, '瘋狂動物城': 5.0}"""distance = 0common_ratings = Falsefor key in rating1:if key in rating2:distance += pow(rating1[key] - rating2[key], 2)common_ratings = True# 兩個打分序列之間有公共打分電影if common_ratings:return distance# 無公共打分電影else:return float('inf')?(2)曼哈頓距離
??
def manhattan_dis(rating1, rating2):"""計算2個打分序列間的曼哈頓距離. 輸入的rating1和rating2都是打分dict格式為{'小時代4': 1.0, '瘋狂動物城': 5.0}"""distance = 0common_ratings = Falsefor key in rating1:if key in rating2:distance += abs(rating1[key] - rating2[key])common_ratings = True# 兩個打分序列之間有公共打分電影if common_ratings:return distance# 無公共打分電影else:return float('inf')?(3)COS距離
?
def cos_dis(rating1, rating2):"""計算2個打分序列間的cos距離. 輸入的rating1和rating2都是打分dict格式為{'小時代4': 1.0, '瘋狂動物城': 5.0}"""distance = 0dot_product_1 = 0dot_product_2 = 0common_ratings = Falsefor score in rating1.values():dot_product_1 += pow(score, 2)for score in rating2.values():dot_product_2 += pow(score, 2)for key in rating1:if key in rating2:distance += rating1[key] * rating2[key]common_ratings = True# 兩個打分序列之間有公共打分電影# 注意,上面的distance是相似度,相似度越大,距離就會越小,所以用1來減if common_ratings:return 1 - distance/sqrt(dot_product_1 * dot_product_2)# 無公共打分電影else:return 2?(4)Person距離(相關(guān)系數(shù))
??
?這里使用了,
def pearson_dis(rating1, rating2):"""計算2個打分序列間的pearson距離. 輸入的rating1和rating2都是打分dict格式為{'小時代4': 1.0, '瘋狂動物城': 5.0}"""sum_xy = 0sum_x = 0sum_y = 0sum_x2 = 0sum_y2 = 0n = 0for key in rating1:if key in rating2:n += 1x = rating1[key]y = rating2[key]sum_xy += x * ysum_x += xsum_y += ysum_x2 += pow(x, 2)sum_y2 += pow(y, 2)# now compute denominatordenominator = sqrt(sum_x2 - pow(sum_x, 2) / n) * sqrt(sum_y2 - pow(sum_y, 2) / n)# 相關(guān)系數(shù)的取值在-1到1之間,值越大相關(guān)性越大,距離就越小,所以要用1來減一下if denominator == 0:return 2else:return 1 - (sum_xy - (sum_x * sum_y) / n) / denominator? 有了各種距離的定義,我們就可以根據(jù)定義的距離來計算兩個user之間的想近程度了,計算方法如下:
# 查找最近鄰 def compute_nearest_neighbor(username, users):"""在給定username的情況下,計算其他用戶和它的距離并排序"""distances = []# 查找最近鄰的時候要除去自身,要不然肯定是自己離自己最近了for user in users:if not user == username:distance = pearson_dis(users[user], users[username])# distance = cos_dis(users[user], users[username])# distance = manhattan_dis(users[user], users[username])# distance = euclidean_dis(users[user], users[username])distances.append((distance, user))distances.sort()print(distances)return distances?最終進行推薦:
# 推薦 def recommend(username, users):"""對指定的user推薦電影"""# 找到最鄰近nearest = compute_nearest_neighbor(username, users)[0][1]print("愛好相同的人:", nearest)recommendations = []# 找到最鄰近看過, 但是username本身沒有看過的電影, 計算推薦neighbor_ratings = users[nearest]user_ratings = users[username]for artist in neighbor_ratings:if artist not in user_ratings:recommendations.append((artist, neighbor_ratings[artist]))results = sorted(recommendations, key=lambda artist_tuple: artist_tuple[1], reverse=True)print("推薦電影:")for result in results:print(result[0], result[1])?測試:
recommend('六爺', userss)?測試結(jié)果:
?
(二)簡單的張量分解來實現(xiàn)推薦
? 首先,把數(shù)據(jù)整理成一個矩陣,缺失的用0(根據(jù)實際情況來定)來表示
主要的目標就是填充缺失的評分,這里使用FunkSVD,其代價函數(shù)為:
,
詳情請參見https://www.cnblogs.com/pinard/p/6351319.html
使用SGD來做優(yōu)化,代碼如下:
def matrix_factorization(R, P, Q, K, steps=10000, alpha=0.0002, beta=0.02):Q = Q.Tfor step in range(steps):for i in range(len(R)):for j in range(len(R[i])):if R[i][j] > 0:e_ij = R[i][j] - np.dot(P[i, :], Q[:, j])for k in range(K):P[i][k] = P[i][k] + alpha * (2 * e_ij * Q[k][j] - beta * P[i][k])Q[k][j] = Q[k][j] + alpha * (2 * e_ij * P[i][k] - beta * Q[k][j])e = 0for i in range(len(R)):for j in range(len(R[i])):if R[i][j] > 0:e = e + pow(R[i][j] - np.dot(P[i, :], Q[:, j]), 2)for k in range(K):e = e + (beta / 2) * (pow(P[i][k], 2) + pow(Q[k][j], 2))if e < 0.001:breakreturn P, Q.T使用上述函數(shù)進行缺失值的填充:
R = [[5.0, 0, 0, 3.5, 5.0, 0, 0, 0],[0, 0, 2.5, 0, 0, 4.0, 0, 0],[0, 0, 0, 0, 5.0, 0, 0, 0],[0, 0, 0, 0, 4.5, 0, 0, 0],[5.0, 5.0, 0, 0, 4.0, 0, 0, 5.0],[0, 0, 3.0, 0, 0, 5.0, 0, 0],[4.5, 0, 0, 0, 0, 0, 5.0, 4.5],[3.0, 2.0, 4.5, 4.0, 3.0, 1.0, 5.0, 3.0],[2.0, 2.0, 4.0, 5.0, 0, 0, 4.0, 0],[0, 0, 4.5, 4.0, 0, 0, 0, 0],[0, 0, 0, 3.5, 0, 0, 3.0, 5.0],[0, 0, 0, 0, 0, 5.0, 0, 0],[0, 0, 0, 0, 0, 0, 0, 3.0],[0, 0, 0, 4.5, 3.5, 0, 3.0, 0],[0, 4.5, 0, 0, 5.0, 5.0, 3.0, 4.5],[5.0, 4.0, 0, 3.0, 0, 4.0, 0, 5.0]] R = np.array(R) M, N = np.shape(R) # 設(shè)置隱藏屬性的個數(shù)為2 K = 2 P = np.random.rand(M, K) Q = np.random.rand(N, K) nP, nQ = matrix_factorization(R, P, Q, K) nR = np.dot(nP, nQ.T) nR = np.around(nR, decimals=1) print("更新后的得分矩陣為") print(nR)運行結(jié)果:
?
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎總結(jié)
- 上一篇: js常用API
- 下一篇: Unity资源管理--AssetBund