基于随机梯度下降的矩阵分解推荐算法(python)

SVD是矩阵分解常用的方法,其原理为:矩阵M可以写成矩阵A、B与C相乘得到,而B可以与A或者C合并,就变成了两个元素M1与M2的矩阵相乘可以得到M。

矩阵分解推荐的思想就是基于此,将每个user和item的内在feature构成的矩阵分别表示为M1与M2,则内在feature的乘积得到M;因此我们可以利用已有数据(user对item的打分)通过随机梯度下降的方法计算出现有user和item最可能的feature对应到的M1与M2(相当于得到每个user和每个item的内在属性),这样就可以得到通过feature之间的内积得到user没有打过分的item的分数。

本文所采用的数据是movielens中的数据,且自行切割成了train和test,但是由于数据量较大,没有用到全部数据。

代码如下:

# -*- coding: utf-8 -*-
"""
Created on Mon Oct 9 19:33:00 2017
@author: wjw
"""
import pandas as pd
import numpy as np
import os

def difference(left,right,on): #求两个dataframe的差集
  df = pd.merge(left,right,how='left',on=on) #参数on指的是用于连接的列索引名称
  left_columns = left.columns
  col_y = df.columns[-1] # 得到最后一列
  df = df[df[col_y].isnull()]#得到boolean的list
  df = df.iloc[:,0:left_columns.size]#得到的数据里面还有其他同列名的column
  df.columns = left_columns # 重新定义columns
  return df

def readfile(filepath): #读取文件,同时得到训练集和测试集

  pwd = os.getcwd()#返回当前工程的工作目录
  os.chdir(os.path.dirname(filepath))
  #os.path.dirname()获得filepath文件的目录;chdir()切换到filepath目录下
  initialData = pd.read_csv(os.path.basename(filepath))
  #basename()获取指定目录的相对路径
  os.chdir(pwd)#回到先前工作目录下
  predData = initialData.iloc[:,0:3] #将最后一列数据去掉
  newIndexData = predData.drop_duplicates()
  trainData = newIndexData.sample(axis=0,frac = 0.1) #90%的数据作为训练集
  testData = difference(newIndexData,trainData,['userId','movieId']).sample(axis=0,frac=0.1)
  return trainData,testData

def getmodel(train):
  slowRate = 0.99
  preRmse = 10000000.0
  max_iter = 100
  features = 3
  lamda = 0.2
  gama = 0.01 #随机梯度下降中加入,防止更新过度
  user = pd.DataFrame(train.userId.drop_duplicates(),columns=['userId']).reset_index(drop=True) #把在原来dataFrame中的索引重新设置,drop=True并抛弃

  movie = pd.DataFrame(train.movieId.drop_duplicates(),columns=['movieId']).reset_index(drop=True)
  userNum = user.count().loc['userId'] #671
  movieNum = movie.count().loc['movieId']
  userFeatures = np.random.rand(userNum,features) #构造user和movie的特征向量集合
  movieFeatures = np.random.rand(movieNum,features)
  #假设每个user和每个movie有3个feature
  userFeaturesFrame =user.join(pd.DataFrame(userFeatures,columns = ['f1','f2','f3']))
  movieFeaturesFrame =movie.join(pd.DataFrame(movieFeatures,columns= ['f1','f2','f3']))
  userFeaturesFrame = userFeaturesFrame.set_index('userId')
  movieFeaturesFrame = movieFeaturesFrame.set_index('movieId') #重新设置index

  for i in range(max_iter):
    rmse = 0
    n = 0
    for index,row in user.iterrows():
      uId = row.userId
      userFeature = userFeaturesFrame.loc[uId] #得到userFeatureFrame中对应uId的feature

      u_m = train[train['userId'] == uId] #找到在train中userId点评过的movieId的data
      for index,row in u_m.iterrows():
        u_mId = int(row.movieId)
        realRating = row.rating
        movieFeature = movieFeaturesFrame.loc[u_mId] 

        eui = realRating-np.dot(userFeature,movieFeature)
        rmse += pow(eui,2)
        n += 1
        userFeaturesFrame.loc[uId] += gama * (eui*movieFeature-lamda*userFeature)
        movieFeaturesFrame.loc[u_mId] += gama*(eui*userFeature-lamda*movieFeature)
    nowRmse = np.sqrt(rmse*1.0/n)
    print('step:%f,rmse:%f'%((i+1),nowRmse))
    if nowRmse<preRmse:
      preRmse = nowRmse
    elif nowRmse<0.5:
      break
    elif nowRmse-preRmse<=0.001:
      break
    gama*=slowRate
  return userFeaturesFrame,movieFeaturesFrame

def evaluate(userFeaturesFrame,movieFeaturesFrame,test):
  test['predictRating']='NAN' # 新增一列

  for index,row in test.iterrows(): 

    print(index)
    userId = row.userId
    movieId = row.movieId
    if userId not in userFeaturesFrame.index or movieId not in movieFeaturesFrame.index:
      continue
    userFeature = userFeaturesFrame.loc[userId]
    movieFeature = movieFeaturesFrame.loc[movieId]
    test.loc[index,'predictRating'] = np.dot(userFeature,movieFeature) #不定位到不能修改值

  return test 

if __name__ == "__main__":
  filepath = r"E:\学习\研究生\推荐系统\ml-latest-small\ratings.csv"
  train,test = readfile(filepath)
  userFeaturesFrame,movieFeaturesFrame = getmodel(train)
  result = evaluate(userFeaturesFrame,movieFeaturesFrame,test)

在test中得到的结果为:

NAN则是训练集中没有的数据

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Python编程给numpy矩阵添加一列方法示例

    首先我们有一个数据是一个mn的numpy矩阵现在我们希望能够进行给他加上一列变成一个m(n+1)的矩阵 import numpy as np a = np.array([[1,2,3],[4,5,6],[7,8,9]]) b = np.ones(3) c = np.array([[1,2,3,1],[4,5,6,1],[7,8,9,1]]) PRint(a) print(b) print(c) [[1 2 3] [4 5 6] [7 8 9]] [ 1. 1. 1.] [[1 2 3 1] [4

  • Python3 中把txt数据文件读入到矩阵中的方法

    1.实例程序: ''' 数据文件:2.txt内容:(以空格分开每个数据) 1 2 2.5 3 4 4 7 8 7 ''' from numpy import * A = zeros((3,3),dtype=float) #先创建一个 3x3的全零方阵A,并且数据的类型设置为float浮点型 f = open('2.txt') #打开数据文件文件 lines = f.readlines() #把全部数据文件读到一个列表lines中 A_row = 0 #表示矩阵的行,从0行开始 for line

  • Python3实现的旋转矩阵图像算法示例

    本文实例讲述了Python3实现的旋转矩阵图像算法.分享给大家供大家参考,具体如下: 问题: 给定一个 n × n 的二维矩阵表示一个图像. 将图像顺时针旋转 90 度. 方案一:先按X轴对称旋转, 再用zip()解压,最后用list重组. # -*- coding:utf-8 -*- #! python3 class Solution: def rotate(self, matrix): """ :type matrix: List[List[int]] :rtype: v

  • Python实现螺旋矩阵的填充算法示例

    本文实例讲述了Python实现螺旋矩阵的填充算法.分享给大家供大家参考,具体如下: afanty的分析: 关于矩阵(二维数组)填充问题自己动手推推,分析下两个下表的移动规律就很容易咯. 对于螺旋矩阵,不管它是什么鬼,反正就是依次向右.向下.向右.向上移动. 向右移动:横坐标不变,纵坐标加1 向下移动:纵坐标不变,横坐标加1 向右移动:横坐标不变,纵坐标减1 向上移动:纵坐标不变,横坐标减1 代码实现: #coding=utf-8 import numpy ''''' Author: afanty

  • python中numpy的矩阵、多维数组的用法

    1. 引言 最近在将一个算法由matlab转成python,初学python,很多地方还不熟悉,总体感觉就是上手容易,实际上很优雅地用python还是蛮难的.目前为止,觉得就算法仿真研究而言,还是matlab用得特别舒服,可能是比较熟悉的缘故吧.matlab直接集成了很多算法工具箱,函数查询.调用.变量查询等非常方便,或许以后用久了python也会感觉很好用.与python相比,最喜欢的莫过于可以直接选中某段代码执行了,操作方便,python也可以实现,就是感觉不是很方便. 言归正传,做算法要用

  • Python矩阵常见运算操作实例总结

    本文实例讲述了Python矩阵常见运算操作.分享给大家供大家参考,具体如下: python的numpy库提供矩阵运算的功能,因此我们在需要矩阵运算的时候,需要导入numpy的包. 一.numpy的导入和使用 from numpy import *;#导入numpy的库函数 import numpy as np; #这个方式使用numpy的函数时,需要以np.开头. 二.矩阵的创建 由一维或二维数据创建矩阵 from numpy import *; a1=array([1,2,3]); a1=ma

  • Python表示矩阵的方法分析

    本文实例讲述了Python表示矩阵的方法.分享给大家供大家参考,具体如下: 在c语言中,表示个"整型3行4列"的矩阵,可以这样声明:int  a[3][4];在python中一不能声明变量int,二不能列出维数.可以利用列表中夹带列表形式表示.例如: 表示矩阵 ,可以这样: count = 1 a = [] for i in range(0, 3): tmp = [] for j in range(0, 3): tmp.append(count) count += 1 a.append

  • python矩阵/字典实现最短路径算法

    前言:好像感觉各种博客的最短路径python实现都花里胡哨的?输出不明显,唉,可能是因为不想读别人的代码吧(明明自己学过离散).然后可能有些人是用字典实现的?的确字典的话,比较省空间.改天,也用字典试下.先贴个图吧. 然后再贴代码: _=inf=999999#inf def Dijkstra_all_minpath(start,matrix): length=len(matrix)#该图的节点数 path_array=[] temp_array=[] path_array.extend(matr

  • Python获取二维矩阵每列最大值的方法

    因为做项目中间有一个很小的环节需要这个功能,所以就写了一个简单的小函数,下面是具体实现: #!usr/bin/env python #encoding:utf-8 ''' __Author__:沂水寒城 ''' def get_max_value(martix): ''' 得到矩阵中每一列最大的值 ''' res_list=[] for j in range(len(martix[0])): one_list=[] for i in range(len(martix)): one_list.ap

  • python实现矩阵乘法的方法

    本文实例讲述了python实现矩阵乘法的方法.分享给大家供大家参考.具体实现方法如下: def matrixMul(A, B): res = [[0] * len(B[0]) for i in range(len(A))] for i in range(len(A)): for j in range(len(B[0])): for k in range(len(B)): res[i][j] += A[i][k] * B[k][j] return res def matrixMul2(A, B):

  • Python使用稀疏矩阵节省内存实例

    推荐系统中经常需要处理类似user_id, item_id, rating这样的数据,其实就是数学里面的稀疏矩阵,scipy中提供了sparse模块来解决这个问题,但scipy.sparse有很多问题不太合用: 1.不能很好的同时支持data[i, ...].data[..., j].data[i, j]快速切片: 2.由于数据保存在内存中,不能很好的支持海量数据处理. 要支持data[i, ...].data[..., j]的快速切片,需要i或者j的数据集中存储:同时,为了保存海量的数据,也需

随机推荐