协同过滤算法可以分为基于用户的协同过滤算法和基于项的协同过滤算法。基于用户的协同过滤算法计算用户之间的相似度,并根据用户之间的相似度给目标用户未评分项进行预测,找出目标用户对未评分项的兴趣;基于项的协同过滤算法计算项之间的相似度,并根据项之间的相似度给目标用户未评分项进行预测,找出目标用户对未评分项的兴趣。
相似度的度量必须要满足三个条件:非负性、对称性和三角不等性(参考资料2)。本博文采用余弦相似度来度量两个向量(X,Y)之间的相似度。
C o s S i m ( X , Y ) = ⟨ X , Y ⟩ ∥ X ∥ ∥ Y ∥ {\mathop{\rm Cos}\nolimits} {\mathop{\rm Si}\nolimits} m\left( {X,Y} \right) = \frac{{\left\langle {X,Y} \right\rangle }}{{\left\| X \right\|\left\| Y \right\|}} CosSim(X,Y)=∥X∥∥Y∥⟨X,Y⟩
其中 ⟨ X , Y ⟩ {\left\langle {X,Y} \right\rangle } ⟨X,Y⟩为向量 X X X与向量 Y Y Y的内积, ∥ X ∥ {\left\| X \right\|} ∥X∥和 ∥ Y ∥ {\left\| Y\right\|} ∥Y∥分别是向量 X X X与向量 Y Y Y的范数。
基于用户的协同过滤算法中对目标用户 u u u未评分项 i i i的预测公式为:
p ( u , i ) = ∑ v ∈ N ( i ) w u , v r v , i p\left( {u,i} \right) = \sum\limits_{v \in N(i)} {{w_{u,v}}{r_{v,i}}} p(u,i)=v∈N(i)∑wu,vrv,i
其中 N ( i ) {N(i)} N(i)表示对项 i i i评过分的用户集合, w u , v {{w_{u,v}}} wu,v表示用户 u u u和用户 v v v之间的相似度, r v , i {{r_{v,i}}} rv,i表示用户 v v v对项 i i i的评分。
基于项的协同过滤算法中对目标用户 u u u未评分项 i i i的预测公式为:
p ( u , i ) = ∑ j ∈ I ( u ) w i , j r j , u p\left( {u,i} \right) = \sum\limits_{j \in I(u)} {{w_{i,j}}{r_{j,u}}} p(u,i)=j∈I(u)∑wi,jrj,u
其中 I ( u ) {I(u)} I(u)表示目标用户 u u u所用评过分的项的集合, w i , j {{w_{i,j}}} wi,j表示项 i i i和项 j j j之间的相似度, r j , u {{r_{j,u}}} rj,u表示目标用户 u u u对项 j j j的评分。
协同过滤算法需要对完整的用户商品矩阵 D a t a Data Data进行处理,但是这个矩阵一般是稀疏的,导致协同过滤算法对大量无用数据进行处理,效率低下。
可以通过利用奇异值分解(SVD)对原始的用户商品数据进行降维处理,将那些对最终结果预测意义不大的用户或项删除掉。SVD的具体内容可以参考(资料3)。假设截取后的奇异值分解的对角矩阵为 Δ \Delta Δ,则将维后的用户商品矩阵 N e w D a t a NewData NewData为:
N e w D a t a = ( D a t a T × U × Δ − 1 ) T NewData = {({Dat{a^T} \times U \times {\Delta ^{ - 1}}})^T} NewData=(DataT×U×Δ−1)T
其中 U U U是奇异值分解中的左奇异矩阵。
完整的python代码地址:https://github.com/shiluqiang/svd_item_based_recommend
本博文python代码中协同过滤推荐算法采用基于项的协同推荐算法,原始用户商品信息矩阵中行代表用户,列代表项。
首先,导入用户商品信息矩阵:
import numpy as np
def load_data():
'''导入用户商品数据
output:data(mat):用户商品信息矩阵
'''
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.mat(data)
然后,对原始用户商品信息矩阵进行降维处理。
def svd_dim(data):
'''对原始用户商品矩阵进行降维
input:data(mat):原始的用户商品矩阵
output:newdata(mat):降维后的用户商品矩阵
'''
n = np.shape(data)[1]
U,Sigma,VT = np.linalg.svd(data)
sig2 = Sigma ** 2
cut = 0
for i in range(n):
if sum(sig2[:i]) / sum(sig2) > 0.9:
cut = i
break
Sig4 = np.mat(np.eye(cut)*Sigma[:cut]) #arrange Sig4 into a diagonal matrix
xformedItems = data.T * U[:,:cut] * Sig4.I #create transformed items
newdata = xformedItems.T
return newdata
其次,计算相似度矩阵:
def cos_sim(x,y):
'''计算余弦相似度
input:x(mat):行向量矩阵,项目信息
y(mat):行向量矩阵,项目信息
output:x和y的余弦相似度
'''
nemerator = x * y.T #x与y的内积
denominator = np.sqrt(x * x.T) * np.sqrt(y * y.T) #x与y范数的乘积
return (nemerator / denominator)[0,0]
def similarity(data):
'''计算矩阵中任意两行之间的相似度
input:data(mat):任意矩阵
output:w(mat):矩阵中任意两行之间的相似度
'''
m = np.shape(data)[0] #用户数量
#初始化相似度矩阵
w = np.mat(np.zeros((m,m)))
for i in range(m):
for j in range(i,m):
if j != i:
#计算任意两行之间的相似度
w[i,j] = cos_sim(data[i,:],data[j,:])
w[j,i] = w[i,j]
else:
w[i,j] = 0
return w
基于项的协同过滤推荐算法:
def item_based_recommend(data, w, user):
'''基于商品相似度为用户user推荐商品
input: data(mat):商品用户矩阵
w(mat):商品与商品之间的相似性
user(int):用户的编号
output: predict(list):推荐列表
'''
m, n = np.shape(data) # m:商品数量 n:用户数量
interaction = data[:,user].T # 用户user的互动商品信息
# 1、找到用户user没有互动的商品
not_inter = []
for i in range(n):
if interaction[0, i] == 0: # 用户user未打分项
not_inter.append(i)
# 2、对没有互动过的商品进行预测
predict = {}
for x in not_inter:
item = np.copy(interaction) # 获取用户user对商品的互动信息
for j in range(m): # 对每一个商品
if item[0, j] != 0: # 利用互动过的商品预测
if x not in predict:
predict[x] = w[x, j] * item[0, j]
else:
predict[x] = predict[x] + w[x, j] * item[0, j]
# 按照预测的大小从大到小排序
return sorted(predict.items(), key=lambda d:d[1], reverse=True)
最后,找出目标用户最感兴趣的前k个未评分项:
def top_k(predict, k):
'''为用户推荐前k个商品
input: predict(list):排好序的商品列表
k(int):推荐的商品个数
output: top_recom(list):top_k个商品
'''
top_recom = []
len_result = len(predict)
if k >= len_result:
top_recom = predict
else:
for i in range(k):
top_recom.append(predict[i])
return top_recom
1、《Python机器学习算法》
2、https://blog.csdn.net/google19890102/article/details/28112091
3、https://blog.csdn.net/Luqiang_Shi/article/details/84787224