基于协同过滤(collaborative filtering)的推荐引擎,是通过将用户和其他用户的数据进行对比来实现推荐
不是利用属性来描述物品从而计算它们之间的相似度,而是利用用户对它们的意见来计算相似度,这就是协同过滤中所使用的方法,它并不关心物品的描述属性,而是严格地按照许多用户的观点来计算相似度
相似度计算
欧氏距离
相似度的范围是 0~1
皮尔逊相关系数 (Pearson correlation)
该方法相对于欧氏距离的一个优势在于,它对用户评级的量级并不敏感
比如某个狂躁者对所有物品的评分都是5分
而另一个忧郁者对所有物品的评分都是1分
皮尔逊相关系数会认为这两个向量是相等的
在 NumPy 中,皮尔逊相关系数的计算是由函数 corrcoef() 进行的
通过 0.5 + 0.5*corrcoef() 计算,把取值范围归一化到 0~1
余弦相似度 (cosine similarity)
计算的是两个向量夹角的余弦值
如果夹角为 90 度,则相似度为 0,如果两个向量的方向相同,则相似度为 1
计算两个餐馆菜肴之间的距离,这称为基于物品的相似度
另一种计算用户距离的方法则称为基于用户的相似度
到底使用哪一种相似度取决于用户或物品的数目
基于物品相似度计算的时间会随物品数量的增加而增加
基于用户的相似度计算的时间则会随用户数量的增加而增加
协同过滤是基于用户的相似度计算
代码(使用 SVD 和没使用 SVD 两种)
# coding=utf-8
import numpy as np
def ecludSim(inA, inB):
"""
欧氏距离
"""
return 1.0 / (1.0 + np.linalg.norm(inA - inB))
def pearsSim(inA, inB):
"""
皮尔逊相关系数
"""
if len(inA) < 3:
return 1.0
# corrcoef 返回的是矩阵,[0][1] 代表 inA 和 inB 间的相关系数
return 0.5 + 0.5 * np.corrcoef(inA, inB, rowvar=False)[0][1]
def cosSim(inA, inB):
"""
余弦相似度
"""
num = float(inA.T * inB)
denom = np.linalg.norm(inA) * np.linalg.norm(inB)
return 0.5 + 0.5 * (num / denom)
def standEst(dataMat, user, simMeas, item):
"""
预测 user 对 item 的评分 (没有使用 SVD)
dataMat - 每行是一个用户,每列是该用户对某物品的评分
user - 要预测的用户
item - 要预测的物品
simMeas - 用于计算相似度的函数,ecludSim、pearsSim、cosSim 中的一个
"""
n = np.shape(dataMat)[1]
simTotal = 0.0
ratSimTotal = 0.0
# 遍历所有物品
for j in range(n):
# 取 user 对 j 的评分
userRating = dataMat[user, j]
# 没评分则跳过
if userRating == 0:
continue
# 找出所有既对 item 有评分,又对 j 有评分的用户
overLap = np.nonzero(np.logical_and(dataMat[:, item].A > 0, dataMat[:, j].A > 0))[0]
if len(overLap) == 0:
similarity = 0
else:
# 计算找出来的用户对 item 评分和对 j 评分的相似度
similarity = simMeas(dataMat[overLap, item], dataMat[overLap, j])
# 累加相似度
simTotal += similarity
# 累加 user 对 j 的评分与相似度的乘积
ratSimTotal += similarity * userRating
if simTotal == 0:
return 0
else:
# 预测 user 对 item 的评分,计算方法类似 (a1*x1+a2*x2+...+an*xn)/(x1+x2+...+xn) 其中 xi 是 i 和 item 的相似度,ai 是 user 对 i 的评分
return ratSimTotal / simTotal
def svdEst(dataMat, user, simMeas, item):
"""
另一种预测评分的方法:基于 SVD 的评分估计
dataMat - 每行是一个用户,每列是该用户对某物品的评分
user - 要预测的用户
item - 要预测的物品
simMeas - 用于计算相似度的函数,ecludSim、pearsSim、cosSim 中的一个
"""
n = np.shape(dataMat)[1]
simTotal = 0.0
ratSimTotal = 0.0
# np.linalg.svd(dataMat) 求解 dataMat 的奇异值矩阵
U, Sigma, VT = np.linalg.svd(dataMat)
# 将 Sigma 转换为只有对角线有值的 r 阶矩阵
# 这里直接取 4 个
# Sigma 是有排好序的,正常应该取前 r 个,使其平方和大于90%,或固定一个比较大的数值
# 然后 dataMat(m,n) ≈ U(m,r) * Sig(r,r) * VT(r,n)
Sig4 = np.mat(np.eye(4) * Sigma[:4])
# xformedItems 维度是 (n*r),通过转换只保留 r 个用户的所有评分,实现了降维,减少了数据量
# 是不是也可以保留所有用户的 r 个评分?是不是可以用 VT 映射到另一个空间
xformedItems = dataMat.T * U[:, :4] * Sig4.I
# 遍历所有物品
for j in range(n):
# 取 user 对 j 的评分
userRating = dataMat[user, j]
# 没评分则跳过
if userRating == 0 or j == item:
continue
# 计算 r 个用户对 j 和 item 的评分的相似度
similarity = simMeas(xformedItems[item, :].T, xformedItems[j, :].T)
# 累加相似度
simTotal += similarity
# 累加 user 对 j 的评分与相似度的乘积
ratSimTotal += similarity * userRating
if simTotal == 0:
return 0
else:
# 预测 user 对 item 的评分,计算方法类似 (a1*x1+a2*x2+...+an*xn)/(x1+x2+...+xn) 其中 xi 是 i 和 item 的相似度,ai 是 user 对 i 的评分
return ratSimTotal / simTotal
网友评论