2017下半年就开始沉淀机器学习的知识。刚刚开始的时候接触到这个知识因为觉得很有意思,另一方面大家都在学习这也是大势所趋。接触到之后,我把线性代数深入的理解了一遍,这也就为我后来对推荐系统里面的矩阵理解奠定了基础。十分感谢youtube上的3B1B(3Blue1Brown,听说这个小哥的瞳孔3/4是蓝色的1/4是棕色的因此得名)对我的启发,同时他还激发了我对数学的兴趣,不仅仅是应用数学还有原理数学。
开始的路是十分艰难的,本来我还有两个队友一起学习,但是他们都放弃了。他们对这个知识不感兴趣,也许是被这个难度劝退了。的确,对于我这种二本院校的普通学生来说,没有人指导的情况下是很难学习的。现在队友走了,我连交流的可能性也没有了,我的路变得越来越艰难。不过这都是半年前的事情了。我也是太忙了,连记录学习历程的时间都没有,最近才找到时间总结一下。
刚开始我查了一些机器学习方面的研究进展和知识框架,我选择了一个最为基础的算法-协同过滤算法。协同过滤算法就是用来做推荐系统的,这是我当时的想法,那我干脆就做一个商品的推荐系统吧。虽说打着系统的名号,只不过是把核心算法用python写好然后找来数据来实验而已。
这个时候问题就来了,我一个大二的学生,大一学个c语言,在ACM实验室刷水题,哪里会python。我又得自学python,倒是c语言的基础让我学习python只花了半个月不到吧。只是会了其中的语法,并没有深究。随后就是最最烦恼的论文阅读。在老师的催促下,我找到了网上一些协同过滤算法的python代码,这个时候我才发现,搜集情报和知识的能力也是一项很重要的技能。如果说ACM教会了我怎么看博客,这次的学习就教会了我怎么去获取信息。包括所有研究协同过滤算法的人都会有的movielens的数据集,我也去官网下载到。
推荐系统的源代码实验成功了,当时的心情无法形容,自己种下的种子好像在一夜之间长出了瓜果。
#coding:UTF-8
from math import sqrt
def loadData():
trainSet = {}
testSet = {}
movieUser = {}
u2u = {}
TrainFile = 'ml-100k/u1.base' #指定训练集
TestFile = 'ml-100k/u1.test' #指定测试集
#加载训练集
for line in open(TrainFile):
(userId, itemId, rating, timestamp) = line.strip().split('\t') #strip()用于去除字符串前后的指定字符串,默认为空格
trainSet.setdefault(userId,{})
trainSet[userId].setdefault(itemId,float(rating))
movieUser.setdefault(itemId,[])
movieUser[itemId].append(userId.strip())
#加载测试集
for line in open(TestFile):
(userId, itemId, rating, timestamp) = line.strip().split('\t')
testSet.setdefault(userId,{})
testSet[userId].setdefault(itemId,float(rating))
#生成用户用户共有电影矩阵
for m in movieUser.keys(): #物品的ID m
for u in movieUser[m]: #用户的集合 u
u2u.setdefault(u,{}) #已用户为键,建立字典
for n in movieUser[m]: # 用户的集合
if u!=n:
u2u[u].setdefault(n,[])
u2u[u][n].append(m)
return trainSet,testSet,u2u
#计算一个用户的平均评分
def getAverageRating(user):
average = (sum(trainSet[user].values())*1.0) / len(trainSet[user].keys())
return average
#计算用户的总分
def getSumRating(user):
sumrate=(sum(trainSet[user].values())*1.0)
return sumrate
#计算用户相似度
def getUserSim(u2u,trainSet):
userSim = {}
# 计算用户的用户相似度
for u in u2u.keys(): #对每个用户u
userSim.setdefault(u,{}) #将用户u加入userSim中设为key,该用户对应一个字典
average_u_rate = getAverageRating(u) #获取用户u对电影的平均评分
for n in u2u[u].keys(): #对与用户u相关的每个用户n
userSim[u].setdefault(n,0) #将用户n加入用户u的字典中
average_n_rate = getAverageRating(n) #获取用户n对电影的平均评分
R_un=0 #共有电影数量与用户最大数量
sum_un_m=0
sum_u_m=0 #u用户对共有电影评分的总和
sum_n_m=0 #n是用户对共有电影评分的总和
part1 = 0 #皮尔逊相关系数的分子部分
part2 = 0 #皮尔逊相关系数的分母的一部分
part3 = 0 #皮尔逊相关系数的分母的一部分
for m in u2u[u][n]: #对用户u和用户n的共有的每个电影
part1 += (trainSet[u][m]-average_u_rate)*(trainSet[n][m]-average_n_rate)*1.0
part2 += pow(trainSet[u][m]-average_u_rate, 2)*1.0 #pow返回x的y次方
part3 += pow(trainSet[n][m]-average_n_rate, 2)*1.0
sum_u_m += trainSet[u][m]*1.0
sum_n_m += trainSet[n][m]*1.0
R_un=R_un+1
sum_un_m=sum_un_m+abs(trainSet[u][m]-trainSet[n][m])*1.0
d_un=sum_un_m/R_un
R_un=R_un/max(len(trainSet[u].keys()),len(trainSet[n].keys()))*1.0
part2 = sqrt(part2)
part3 = sqrt(part3)
sumrate_u=getSumRating(u)
sumrate_n=getSumRating(n)
sumrate_u=sqrt(sumrate_u)
sumrate_n=sqrt(sumrate_n)
sum_u_m=sqrt(sum_u_m)
sum_n_m=sqrt(sum_n_m)
if part2 == 0 or part3 == 0: #若分母为0,相似度为0
userSim[u][n] = 0
else:
userSim[u][n] = part1 / (part2 * part3)
userSim[u][n]=userSim[u][n]*sum_u_m*sum_n_m
userSim[u][n]=userSim[u][n]/sumrate_u/sumrate_n
userSim[u][n]=userSim[u][n]*d_un*R_un
return userSim
#寻找用户最近邻并生成推荐结果
def getRecommendations(N,trainSet,userSim):
pred = {}
for user in trainSet.keys(): #对每个用户
pred.setdefault(user,{}) #生成预测空列表
interacted_items = trainSet[user].keys() #获取该用户评过分的电影
average_u_rate = getAverageRating(user) #获取该用户的评分平均分
userSimSum = 0
simUser = sorted(userSim[user].items(),key = lambda x : x[1],reverse = True)[0:N] #reverse True为大到小 False相反
for n, sim in simUser:
average_n_rate = getAverageRating(n)
userSimSum += sim #对该用户近邻用户相似度求和
for m, nrating in trainSet[n].items():
if m in interacted_items:
continue
else:
pred[user].setdefault(m,0)
pred[user][m] += (sim * (nrating - average_n_rate))
for m in pred[user].keys(): #计算预测评分
pred[user][m] = average_u_rate + (pred[user][m]*1.0) / userSimSum
return pred
#计算预测分析准确度
def getMAE(testSet,pred):
MAE = 0
rSum = 0
setSum = 0
for user in pred.keys(): #对每一个用户
for movie, rating in pred[user].items(): #对该用户预测的每一个电影
if user in testSet.keys() and movie in testSet[user].keys() : #如果用户为该电影评过分
setSum = setSum + 1 #预测准确数量+1
rSum = rSum + abs(testSet[user][movie]-rating) #累计预测评分误差
MAE = rSum / setSum
return MAE
def getRecall(testSet,pred):
setSum=0
Sum_test=0
for user in testSet.keys():
for movie in testSet[user].keys():
Sum_test = Sum_test + 1
if movie in pred[user].keys():
setSum = setSum + 1
recall = setSum * 1.0 / Sum_test * 1.0
return recall
def getPrec(testSet,pred):
setSum=0
Sum_test=0
for user in pred.keys():
for movie in pred[user].keys():
Sum_test = Sum_test + 1
if user in testSet.keys() and movie in testSet[user].keys():
setSum = setSum + 1
prec = setSum * 1.0 / Sum_test * 1.0
return prec
def getCouver(pred): #获得覆盖率Couverage
Sum=[]
for user in pred.keys():
for movie in pred[user].keys():
if movie not in Sum:
Sum.append(movie)
Sum_num=len(Sum)
all_s=1682
couver=Sum_num*1.0/all_s*1.0
return couver
if __name__ == '__main__':
print(u'正在加载数据...')
trainSet,testSet,u2u = loadData()
print(u'正在计算用户间相似度...')
userSim = getUserSim(u2u,trainSet)
print(u'正在寻找最近邻...')
for N in (5,10,20,30,40,50,60,70,80,90,100): #对不同的近邻数
pred = getRecommendations(N,trainSet,userSim) #获得推荐
mae = getMAE(testSet,pred) #计算MAE
recall=getRecall(testSet,pred)
prec=getPrec(testSet,pred)
couver=getCouver(pred)
print (u'邻居数为:N= %d 时 预测评分准确度为:'%(N))
print ('MAE=%f Recall=%f Precision=%f Couverage=%f'%(mae,recall,prec,couver))
input('按任意键继续...')
这一串代码从此把我拉进了这个大坑之中。对着段代码研究了几天,终于对这个算法有的肤浅的认识,同时进行了一些修改,因为原来的代码有很多问题。这也让我duipython的认识有了进一步的认识。