datacastle上的一道推薦算法競賽(這裏是地址和數據),由於最近想整理和彙總最常用的推薦算法。因此乾脆就把這個競賽拿出來實戰一下。
1. 賽題 & 數據解析
本次比賽是一個名副其實的推薦算法大賽,在本次比賽中,我們提供了一個商品網站中大約16萬名用戶在四年內對商品的評分數據,每條評分記錄都有時間戳(隱匿了具體時間,只保證順序不變)。評分分爲5級,1分最低,5分最高。
這是訓練集,其中四列分別代表用戶編號,物品編號,評分,時間。
這是測試集,只有兩列用戶編號,物品編號,需要預測分數:
2. 基於人工規則模型
基於人工規則的模型可以設計出很多,其特點是簡單,魯棒:
# -*- coding: utf-8 -*-
""" 基於各種指標對用戶, 物品分類, 然後取均值進行預測
1. 基於用戶對物品評分的均值來分類, 然後預測;
1.1 根據用戶對train物品的評分來分類train, test中的用戶;
1.2 由於對用戶進行了分類, 那麼對於同一種物品, 在不同的簇下得分就不同;
1.3 從而就可以確定[物品, 簇]的得分;
"""
import numpy as np
import pandas as pd
import data
train = data.load_data("train")
test = data.load_data("test")
# 求平均分
score_mean = train.groupby(["uid"], as_index=False)["score"].agg({"score_mean": "mean"})
score_mean["cluster"] = np.array(score_mean["score_mean"] * 2, np.int)
score_mean = score_mean[["uid", "cluster"]]
# 基於用戶, 對train, test添加cluster列
train = pd.merge(train, score_mean, on="uid", how="left")
test = pd.merge(test, score_mean, on="uid", how="left")
# 基於cluster對物品打分
item_mean_score = train.groupby(["iid", "cluster"], as_index=False).mean()
result = pd.merge(test, item_mean_score[["iid", "cluster", "score"]], on=["iid", "cluster"], how="left")
result = result.fillna(3)
# 保存結果
data.save_data(result, "result_rule.csv")
3. 基於協同過濾模型
主要是基於物品的協同過濾算法,這個算法複雜度比較高,需要計算的東西很多。因此一般實際應用都是離線計算。主要注意兩點:
1. 多使用字典,利於後期查詢;
2. 對於兩件不同的物品,可能對某個人而言,只評價過一種物品,另一種物品爲空。此時計算距離,認爲他們之間的距離爲0,而不是兩者之差。
這裏給出一個之前實現的協同過濾推薦算法:http://blog.csdn.net/zk_j1994/article/details/77062091
# -*- coding: utf-8 -*-
""" 基於物品的協同過濾算法, 使用SVD分解對人進行降維
1. 有一個細節, 計算相似度時, 一個位置爲空, 另一個位置有評價分數, 此時相似度視爲0;
2. 算法複雜度很高, 需要離線計算;
3. 這裏使用一個小數據集來進行示例;
4. 之所以使用SVD來對樣本降維, 是由於推薦系統矩陣的稀疏性導致的, 事實上使用SVD不但可以減少
計算量, 還能提高推薦性能;
"""
import numpy as np
def load_data():
data = [[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]]
return np.array(data)
if __name__ == "__main__":
data = load_data()
u, sigma, v = np.linalg.svd(data)
sigma_4 = np.mat(np.eye(6) * sigma[0:6])
# 約簡後的矩陣
simpler_data = np.dot(u[0:6, :], data)
"""
由於行被壓縮, 因此計算物品相似度的時候, 計算複雜度大大降低了;
同時, 矩陣越稀疏, 這種先SVD降維, 再計算, 效果就越好
"""
4. 基於矩陣分解的模型
使用矩陣分解來對原矩陣進行恢復,但是這個數據量稍微大了點(用戶 - 物品矩陣就有9G),我的本本跑不動。這裏是一個例子:
# -*- coding: utf-8 -*-
"""
基於矩陣分解的推薦算法
1. 使用梯度下降進行迭代更新;
"""
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(1)
def load_data():
data = [[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]]
return np.array(data)
def gradAscent(data, K, max_iter, alpha, beta):
""" 梯度下降更新P, Q矩陣的元素, 使均方誤差最小 """
if not isinstance(data, np.matrix):
data = np.mat(data)
# 初始化P, Q矩陣
n, m = data.shape
P = np.mat(np.random.random((n, K)))
Q = np.mat(np.random.random((K, m)))
print("\nP = \n{0}".format(P))
print("\nQ = \n{0}".format(Q))
loss_list = []
_iter = 0
while _iter < max_iter:
# 更新P, Q中的每一個元素
for i in range(n):
for j in range(m):
if data[i, j] > 0:
error = (data[i, j] - P[i, :] * Q[:, j])[0, 0] # (i, j)處的誤差
for k in range(K):
P[i, k] = P[i, k] + alpha * (2 * error * Q[k, j] - beta * P[i, k])
Q[k, j] = Q[k, j] + alpha * (2 * error * P[i, k] - beta * Q[k, j])
# 計算原矩陣和恢復矩陣之間的誤差
loss = 0
for i in range(n):
for j in range(m):
if data[i, j]> 0:
for k in range(K):
loss += P[i, k] * Q[k, j]
loss = np.sum(abs(data[i, j] - loss))
loss_list.append(loss)
if loss <= 1e-3:
break
_iter += 1
return P, Q, loss_list
def draw_loss(loss):
plt.plot(range(len(loss)), loss)
plt.show()
if __name__ == "__main__":
data = load_data()
P, Q, loss = gradAscent(data, 5, 20000, 0.0002, 0.02)
print("\n恢復矩陣 = \n{0}".format(P * Q))
draw_loss(loss)
參考文獻
http://blog.csdn.net/nihaoxiaocui/article/details/51974194
http://blog.csdn.net/datacastle/article/details/52190423