推荐系统
推荐系统
1、概要
推荐系统时使用广泛的技术之一,尤其在电商领域中,使用非常频繁。推荐系统涉及多种专业术语和算法。
2、数据说明
2.1 用户列表
所有用户构成的集合,主要是用户id。 例如电影推荐中的所有观影人users.dat数据:
uid::性别::年龄::职业::邮编
----------------------
1::F::1::10::48067
2::M::56::16::70072
3::M::25::15::55117
4::M::45::7::02460
5::M::25::20::55455
6::F::50::9::55117
7::M::35::1::06810
8::M::25::12::11413
9::M::25::17::61614
10::F::35::1::95370
11::F::25::1::04093
12::M::25::12::32793
13::M::45::1::93304
2.2 商品列表
所有商品构成的集合,主要是商品id。例如电影推荐中的所有影片movies.dat数据:
id::片名(上映年度)::题材
-------------------------------------------------
1::Toy Story (1995)::Animation|Children's|Comedy
2::Jumanji (1995)::Adventure|Children's|Fantasy
3::Grumpier Old Men (1995)::Comedy|Romance
4::Waiting to Exhale (1995)::Comedy|Drama
5::Father of the Bride Part II (1995)::Comedy
6::Heat (1995)::Action|Crime|Thriller
7::Sabrina (1995)::Comedy|Romance
8::Tom and Huck (1995)::Adventure|Children's
9::Sudden Death (1995)::Action
10::GoldenEye (1995)::Action|Adventure|Thriller
11::American President, The (1995)::Comedy|Drama|Romance
12::Dracula: Dead and Loving It (1995)::Comedy|Horror
13::Balto (1995)::Animation|Children's
14::Nixon (1995)::Drama
15::Cutthroat Island (1995)::Action|Adventure|Romance
16::Casino (1995)::Drama|Thriller
17::Sense and Sensibility (1995)::Drama|Romance
18::Four Rooms (1995)::Thriller
2.3 训练数据
用户和商品的交互关系列表。例如电影推荐中的所有评分ratings.dat数据:
userid::moveid::评分::时间戳
--------------------------
1::1193::5::978300760
1::661::3::978302109
1::914::3::978301968
1::3408::4::978300275
1::2355::5::978824291
1::1197::3::978302268
1::1287::5::978302039
1::2804::5::978300719
1::594::4::978302268
1::919::4::978301368
1::595::5::978824268
1::938::4::978301752
1::2398::4::978302281
1::2918::4::978302124
1::1035::5::978301753
3、指标
3.1 用户满意度
用户对推荐系统的满意程度,通过用户调查得到/在线实验。
3.2 均方根误差
实际评分与推荐计算评分差的平方和根的平均值
3.3 平均绝对误差
3.4 召回率
召回即找回,命中的记录数除以测试列表中本有的记录数,即找回来多少条。
3.5 精确率
准确率,是在推荐列表中的命中的记录数和推荐了商品总数的比率。
3.6 覆盖率
对所有用户进行推荐后,推荐的商品数的总和与总商品数的比率。
3.61多样性
推荐列表中物品间的不相似性。多样性的计算是推荐列表中两两商品的不相似性的累加和除以对用户u推荐列表商品排列组合。
推荐系统的整体多样性是所有用户的多样性总和的均值。
3.62 新颖性
越不流行的商品,新颖性越高些,通过用户调查得到。意料之中的满意推荐。
3.63 惊喜度
意料之外的满意度有很高的推荐。
3.64 信任度
可信程度,通过调查获得。
3.65 实时性
3.66 健壮性
3.67 评测维度
针对不同的维度进行评测,可以对多个算法融合。时间维度、地域维度、物品维度、用户维度。
3.7 信息熵
衡量数据分布的指标。
p(i)是商品i的流行度,流行度是指交互该商品的用户数。用户数越多,商品越流行。与商品流行度类似,用户的话就是活跃度,该用户交互商品的个数。
3.8 基尼系数
3.9 jaccard相似度
计算用户u和v之间的jaccard相似度,交集除以并集。N(u)是用户u交互的商品数量。
3.10 余弦相似
3.11 John breese相似度
该相似度对流行商品进行惩罚,越流行,对相似度的影响就越小。
4、UserCF
基于用户的协同过滤找出和用户最相似的K个用户,从这K个用户中找出他们交互的商品,过滤掉用户已经交互的商品,剩余的商品就是要推荐给user的商品,每个推荐的商品按照用户和相似用户之间的相似度累加求和。就可以得到推荐的排序列表:
4.1 计算user-items集合
'''
计算用户商品列表集合
'''
def getUserItems():
user_items = {}
global trainData
for t in trainData:
if(t[0] not in user_items.keys()):
user_items[t[0]] = []
user_items[t[0]].append(t[1])
return user_items
4.2 计算用相似度矩阵
'''
计算相似度矩阵,不要重复计算,
每用户都和比自己大的用户进行计算,每次计算,更新两个相似度。
'''
def userSimMatrxi(userItems):
matrix = {}
for u in userItems.items():
if(u[0] not in matrix):
matrix[u[0]] = {}
for v in userItems.items():
if(u[0] < v[0]):
sim = sim_cos(u[1] , v[1])
matrix[u[0]][v[0]] = sim
if(v[0] not in matrix.keys()):
matrix[v[0]] = {}
matrix[v[0]][u[0]] = sim
return matrix
4.3 对用户相似度矩阵按相似度倒排序
'''
对相似度排序
'''
def sortSimMatrix():
sortedmatrix = {}
#得到相似度矩阵
m = userSimMatrxi(getUserItems())
for d in m.items():
#对集合进行排序 , 指定排序表达式 , 是否倒序
sv = sorted(d[1].items(), key=lambda e: e[1], reverse=True)
sortedmatrix[d[0]] = sv
return sortedmatrix
4.4 使用UserCF计算用户u和商品i的偏好
用户协同过滤,计算用户u对商品i的可能评分(偏好-preference)是多少。
'''
计算用户u商品i的偏好值
'''
def pref(uid, iid , K , M):
#找到最相似的K个用户
kusers = M[uid][:K]
#获得用户商品列表
userItems = getUserItems()
#预测偏好值
pref = 0
for t in kusers:
vid = t[0]
vsim = t[1]
if (iid in userItems[vid]):
pref = pref + vsim
return pref
4.5 通过UserCF实现推荐列表
基于用户的协同过滤可解释性差。
'''
基于用户的协同过滤推荐算法实现
'''
def recByUserCF(uid ,K , M):
#找出和u最相似的K个用户
kusers = M[uid][:K]
#用户商品集合
userItems = getUserItems()
uItems = userItems[uid]
#推荐列表
recDict = {}
for t in kusers:
vid = t[0]
vsim = t[1]
for vi in userItems[vid]:
if vi not in uItems :
if vi not in recDict.keys():
recDict[vi] = 0
recDict[vi] = recDict[vi] + vsim
return sorted(recDict.items() , key = lambda e:e[1] , reverse=True)
5、基于商品的协同过滤
5.1 计算商品相似度矩阵
5.2 基于itemCF计算用户u对商品j的偏好
6、哈利波特问题
哈利波特问题是过于热门的物品导致商品相似度影响过大,为了惩罚过于过于热门物品,修改相似度公式:
7、算法与指标实现
7.1 用户列表数据
'''
用户列表
'''
users = [1,2,3,4]
7.2 商品列表
'''
商品列表
'''
items = [1,2,3,4,5,6,7,8]
7.3 训练数据
trainData = [
(1,1,1),
(1,2,1),
(1,3,1),
(1,4,1),
(1,5,1),
(2,3,1),
(2,4,1),
(3,3,1),
(3,4,1),
(3,5,1),
(3,7,1),
(3,8,1),
(4,1,1),
(4,3,1)
]
'''
训练数据
'''
testData = [
(1,5,1),
(1,6,1),
(2,1,1),
(2,6,1)
]
7.4 用户满意度
问卷调查实现。
7.5 均方根误差
'''
均方根误差,衡量推荐算法准确率的
'''
def rmse(test):
sum = 0
for t in test:
sum = sum + math.pow(t[2] - t[3],2)
return math.sqrt(sum) / test.__len__()
7.6 绝对误差
'''
平均绝对误差
SUM(|Rui - R'ui|)
mae = -----------------
|Test|
'''
def mae(test):
sum = 0
for t in test:
sum = sum + math.fabs(t[2] - t[3])
return sum / test.__len__()
7.7 覆盖率
'''
覆盖率
推荐的商品占商品总数的比例
衡量算法发掘长尾的能力。
R(u)推荐商品数
cov = -------------
Items
recommData = [(1,2,0.5)]
'''
def coverage(train ,recommData):
allItems = getAllItems(train)
recommItems = set()
for t in recommData:
recommItems.add(t[1])
return float(recommItems.__len__()) / allItems.__len__()
7.8 从训练集提取所有用户
'''
获取所有用户
'''
def getAllUsers(train):
#set集合不重复
allusers = set()
for t in train :
allusers.add(t[0])
return allusers
7.9 提取所有商品
'''
得到所有商品
'''
def getAllItems(train):
allitems = set()
for t in train:
allitems.add(t[1])
return allitems
7.10 信息熵
'''
信息熵
衡量数据分布的指标。
p(i) : 是商品i的流行度 / 所有商品流行度之和.
infosahng = -SUM(P(i) * logP(i))
'''
def infoshang(train):
itemPops0= itemPops(train)
sum = 0
for k,v in itemPops0.items():
sum += math.log10(v) * v
return -sum
7.11 基尼系数
'''
基尼系数
衡量数据分布的指标。gini系数如果为0是绝对平局
对流行度进行排序.
SUM((2j -n- 1) * p(ij))
gini = --------------------------
n - 1
'''
def gini(train ,recommData):
#得到商品流行度
itemPops0 = itemPops(train)
#得到推荐商品的流行度
recommPop = []
for t in recommData:
recommPop.append(itemPops0[t[1]])
#对流行度排序
sortedPop = sorted(recommPop())
#长度
len = sortedPop.__len__()
sum = 0
index = 1
for p in sortedPop:
sum += (2 *index - len - 1) * p
return float(sum) / (len - 1)
7.12 马太效应
'''
马太效应
强者越强,弱者越弱。
'''
7.13 多样性
'''
多样性
针对一个用户的推荐商品之间的不相似性。
SUM(S(ij))
diversity = 1- ---------------------
1/2 * R(u) * (Ru - 1)
'''
def diersity(train , recommData):
1
7.14 整体多样性
'''
整体多样性
所有用户的多样性总和的平均值
SUM(diversity(u))
diversity2 = ----------------------
|U|
'''
7.15 新颖性
'''
新颖性
从商品流行度进行衡量,如果流行度很低,新颖性会较高
'''
7.16 惊喜度
'''
惊喜度
完全想不到的推荐商品!
'''
7.17 实时性
'''
实时性
实时更新推荐列表满足用户的行为变化。
通过列表变化速率进行衡量。
'''
7.18 冷启动
'''
冷启动
新的商品或新用户。
处理冷启动,使用流行度推荐。
'''
7.19 健壮性
'''
健壮性
robus,鲁棒性
'''
7.20 评测维度
'''
评测维度
用户维度
商品维度
时间维度
'''
7.21 行为数据
'''
行为数据
1.显式反馈
用户有评分。
2.隐式反馈
用户行为
行为统一表示
userid
itemid
behavior type 行为的种类(比如是购买还是浏览)
context 产生行为的上下文,包括时间和地点等
behavior weight 行为的权重(如果是观看视频的行为,那么这个权重可以是观看时长;如果是打分行为,
这个权重可以是分数)
behavior content行为的内容(如果是评论行为,那么就是评论的文本;如果是打标签的行为,就是标签)
协同过滤
仅仅通过用户行为数据设计的推荐算法。
实现方法有:
1.基于邻域的方法
1.1)基于用户的协同过滤
1.2)基于商品的协同过滤
2.隐语义模型
3.基于图的随机游走算法
'''
7.22 召回
'''
召回(找回)
对测试数据集中的命中的数量 / 测试数据的数量
针对所有用的计算。
SUM( R(u) && T(u)) :u推荐物品 && u在测试集中物品
recall = --------------------------
SUM(|T(u)|) :u在测试集上的喜欢商品数
'''
def recall():
8
7.23 准确率
'''
准确率
针对所有用户进行计算得出的结果。
用户推荐列表中命中的商品数 / 推荐的商品总数。
SUM( R(u) && T(u)) :u推荐物品 && u在测试集中物品
precistion = ----------------------
SUM(|R(u)|) :u在推荐集上的喜欢商品数
'''
def presicion():
1
7.24 jaccard相似度
'''
jaccard相似度
N(u) && N(v) //两个用户共同交互商品的个数
jaccard = -----------------------
N(u) || N(v) //两个用户交互商品总数
'''
def jaccard(itemsA ,itemsB):
seta = set(itemsA)
setb = set(itemsB)
common = seta & setb
if common.__len__() == 0:
return 0
all = seta | setb
return float(common.__len__()) / all.__len__()
7.25 余弦相似
'''
余弦相似 N(u) & N(v)
cos = ------------------------
sqrt(N(u) x N(v))
'''
def cos(itemsA , itemsB):
seta = set(itemsA)
setb = set(itemsB)
#取出交集
common = seta & setb
if common.__len__() == 0:
return 0
import math
return float(common.__len__()) / math.sqrt(seta.__len__() * setb.__len__())
7.26 john breese相似
'''
john breese相似度算法
目的对用户间共同商品的流行度进行惩罚,降低流行商品对用户相似度的权重.
'''
def breese(itemsA, itemsB):
seta = set(itemsA)
setb = set(itemsB)
# 取出交集
common = seta & setb
if common.__len__() == 0:
return 0
import math
#得到所有商品相对流行读列表(暂时使用流行度)
relItemPops0 = relItemPops(trainData)
sum = 0
#
for i in common:
pop = relItemPops0[i]
sum = sum + (float(1) / math.log10(pop + 1))
return sum / math.sqrt(seta.__len__() * setb.__len__())
7.27 基于ItemCF的协同过滤
'''
john breese算法计算商品相似度,对活跃用户进行惩罚
'''
def breeseItemCF(train , usersA , usersB):
#A商品的所有购买用户
seta = set(usersA)
#B商品的所有购买用户
setb = set(usersB)
# 取出交集
common = seta & setb
if common.__len__() == 0:
return 0
#所有用户活跃度
useracts = getUserActs(train)
#
sum = 0
for userid in common:
x = float(1) / (math.log10(1 + useracts[userid]))
sum = sum + x
return sum / math.sqrt(seta.__len__() * setb.__len__())
7.28 归一化
'''
归一化商品相似度矩阵
每个商品的相似商品集合,对集合集合内的每个相似度/该集合中相似度的最大值。
'''
def normalizeItemSimMatrix(train):
#得到商品相似度矩阵
itemsimmatrix = itemSim(train)
for iid , values in itemsimmatrix.items():
max = descDictByValue(values)[0][1]
for iid2,sim in values.items():
values[iid2] = sim / max
return itemsimmatrix
7.29 商品流行度
'''
商品流行度
计算训练集中每个商品的用户交互次数,可能远大于1.
'''
def itemPops(train):
#得到每个商品的交互人次
itemsPop = {}
allitems=[]
userItems = getUserItems(train)
for u,items in userItems.items():
for i in items:
if i not in allitems:
allitems.append(i)
if i not in itemsPop:
itemsPop[i] = 0
itemsPop[i] = itemsPop[i] + 1
itemSum = allitems.__len__()
for i,p in itemsPop.items():
itemsPop[i] = float(p) / itemSum
return itemsPop
7.30 用户活跃度
'''
用户活跃度
用户购买的商品数 / 商品总数
N(u)
useract = -------------
|Items|
'''
def getUserActs(train):
#用户活跃度
useract= {}
#用户商品列表
userItems = getUserItems(train)
#商品总数
count = getAllItems(train).__len__()
for uid,items0 in userItems.items():
useract[uid] = float(items0.__len__()) / count
return useract
7.31 相对商品流行度
'''
相对商品流行度
每个商品的流行度 / 流行度总和
'''
def relItemPops(train):
#计算商品流行度
itemPops0 = itemPops(train)
#计算流行度总和
sum = 0
for p in itemPops0.values():
sum += p
#计算相对值
for i,p0 in itemPops0.items():
itemPops0[i] = p0 / sum
return itemPops0
7.32 商品用户列表
'''
商品用户列表
'''
def geItemUsers(train):
# 用户商品列表字典
itemUsers = {}
for t in train:
userid = t[0]
itemid = t[1]
if itemid not in itemUsers.keys():
itemUsers[itemid] = [userid]
else:
itemUsers[itemid].append(userid)
return itemUsers
7.33 用户相似度矩阵
'''
用户相似度矩阵
通过训练数据集计算两两用户间的相似度,形成矩阵。
得到数据结构是字典:
usersim = {
1 -> {2->0.6,3->0.5}
}
'''
def usersim(train):
#用户相似度字典
userSim = {}
userItems = getUserItems(train)
for k,v in userItems.items():
d = {}
if k not in userSim.keys():
userSim[k] = d
else:
d = userSim[k]
for i,j in userItems.items():
if (k != i) :
if i not in d.keys():
kiSim = breese(v, j)
d[i] = kiSim
#
if i not in userSim.keys():
userSim[i] = {}
userSim[i][k] = kiSim
else:
userSim[i][k] = kiSim
return userSim
7.34 用户商品列表
'''
从训练数据集中抽取user_items列表
'''
def getUserItems(train):
#用户商品列表字典
userItems = {}
for t in train :
userid = t[0]
itemid = t[1]
if userid not in userItems.keys():
userItems[userid] = [itemid]
else:
userItems[userid].append(itemid)
return userItems
7.35 按照相似度倒排序
userSim = usersim(trainData)
sim1 = userSim[3]
sim11 = sorted(sim1.items() , key = lambda e:e[1] , reverse=True)
print 1
7.36 对字典进行value的倒排
'''
对字典进行倒排序,按照value的值进行倒排
'''
def descDictByValue(d):
return sorted(d.items(), key=lambda e: e[1], reverse=True)
7.37 计算u对i的偏好
'''
计算用户u对商品i的偏好值
找出和用户u最相似的K个用户,从这K个用户中找出和商品i有过交互行为
用户v,将用户u和v集合中的相似度累加求和。
'''
def prefer(train , u , i , K):
pre = 0
#用户商品列表
userItems = getUserItems(train)
#计算用户相似度
userSim = usersim(train)
#按照相似度倒排序,提取前K个用户
usims = dict(descDictByValue(userSim[u])[:K])
for v,sim in usims.items():
#找出v交互的所有商品
vitems = userItems[v]
#判断用户v是否交互过商品i
if i in vitems:
pre += sim
return pre
7.38 实现userCF的推荐
'''
使用基于用户的协同过滤实现推荐算法
找出K个和用户u最相似的用户集合v,从集合v中出每个用户交互的商品,
如果用户u对商品没有交互行为,就可以推荐,推荐时需要计算用户u对商品
i的偏好。
user cf: user collaborative filter,基于用户的协同过滤.
'''
def recommUserCF(train , u , K , n):
#向用户推荐的商品和评分
urecomms = {}
#找出最相似度列表
userSim = usersim(train)
#找出最K最相似的
usims = dict(descDictByValue(userSim[u])[:K])
#得到用户商品列表
userItems = getUserItems(train)
#
for (v,sim) in usims.items():
for i in userItems[v]:
if i not in userItems[u]:
if i not in urecomms:
urecomms[i] = sim
else:
urecomms[i] = urecomms[i] +sim
#倒排序
return descDictByValue(urecomms)[:n]
7.39 非个性化推荐(随机)
'''
非个性化推荐
随机算法和最流行算法
'''
def random():
pass
7.40 流行推荐
'''
最流行算法
'''
def mostPopluar():
pass
7.41 商品相似度
'''
商品相似度计算
'''
def itemSim(train):
itemSim0 = {}
#找出用户商品列表
useritems = getUserItems(train)
for items in useritems.values():
for i in items :
for j in items :
if i != j:
if i not in itemSim0:
itemSim0[i] = {}
if j not in itemSim0[i]:
itemSim0[i][j] = 1
else:
itemSim0[i][j] += 1
#得到商品和用户列表
itemusers = getItemUsers(train)
for i,values in itemSim0.items():
for j,sim in values.items():
itemSim0[i][j] = float(sim) / math.sqrt(itemusers[i].__len__() * itemusers[j].__len__())
return itemSim0
7.42 得到商用用户列表
'''
获得商品用户列表
'''
def getItemUsers(train):
itemusers = {}
for t in train:
userid = t[0]
itemid = t[1]
if itemid not in itemusers:
itemusers[itemid] = []
itemusers[itemid].append(userid)
return itemusers
7.43 基于itemCF的偏好计算
'''
基于商品的协同过滤,用户对商品偏好值的计算
结算用户u对上i的偏好值,使用K个最相似的商品
'''
def preferByItemCF(train , u , i , K):
prefer = 0
#用户u的商品列表
items = getUserItems(train)[u]
#商品相似度矩阵
itemSimMatrix = itemSim(train)
#找出K个和i最相似的商品
ksim = descDictByValue(itemSimMatrix[i])[:K]
for t in ksim:
if t[0] in items :
prefer += t[1]
return prefer
7.44 基于itemCF的推荐
'''
使用基于商品的协同过滤算法实现推荐
'''
def recommendByItemCF(train , u , K):
#排名字典
rank = {}
#找到用户u的商品列表
items = getUserItems(train)[u]
#计算商品相似度矩阵
itemSimMatrix = itemSim(train)
for i in items:
#取得每个商品的相似列表前K个
isims = descDictByValue(itemSimMatrix[i])[:K]
for t in isims :
if t[0] not in items:
if t[0] not in rank :
rank[t[0]] = 0
rank[t[0]] += t[1]
return rank
7.46 算法比较
方面 | userCF | itemCF |
---|---|---|
性能 | 用户少,多成本高 | 物品少于用户 |
领域 | 个性化不明显 | 长尾丰富,个性强烈 |
实时性 | 实时差 | 实时好 |
冷启动 | 新用户无法立即推荐(离线计算矩阵) 新产品无法立推(需要用户产生行为) |
新用户只要有商品就能立推 新商品需要离线更新相似度表 |
推荐理由 | 很难解释 | 可以解释 |
8、隐语义模型
隐语义模型,使用矩阵分解方式计算相似度,没有可解释性。实际使用并不常见。具体可以参考spark的推荐算法实现。