推荐系统学习的历程(一)

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的认识有了进一步的认识。

你可能感兴趣的:(推荐系统的研究历程)