推荐算法学习实践:基于物品相似度

Posted python与人工智能学习交流

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了推荐算法学习实践:基于物品相似度相关的知识,希望对你有一定的参考价值。

推荐算法学习实践(一):基于物品相似度

明觉


最近在和推荐系统打交道,学习实践过程中有一些细节、这里记录一下,与大家共同学习和分享交流,不足之处,欢迎大家指正交流。

在物品数量远小于用户数量的推荐场景中,会选择基于物品相似度的推荐算法。比如用户在亿级,物品在万级,符合基于物品相似度的场景。如果用户在亿级,在计算物品相似度时依然计算量巨大,这里采用奇异值分解来降维提取特征。


首先,导入数据,程序输入的数据格式为,用户ID、物品ID、一段时间内的购买频次三列。用户ID、物品ID提前在数据库中处理为0~m-1,0~n-1,m为用户数,n为商品数。

import pandas as pd

df = pd.read_table("data.txt", header=-1, sep=",")


这三列数据如果转成矩阵,是一个巨大的稀疏矩阵,在sklearn中稀疏矩阵和奇异值分解,使用 sklearn.decomposition.TruncatedSVD(),而具体文件其中的参数说明如下:

        algorithm : string, default = "randomized"

        SVD solver to use. Either "arpack" for the ARPACK wrapper in SciPy

        (scipy.sparse.linalg.svds), or "randomized" for the randomized

        algorithm due to Halko (2009).

这里直接用 scipy.sparse.linalg.svds,可以分解为有限个奇异值和对应的奇异向量.这里有一个问题,既然没有完全分解,怎么判断前K个奇异值的贡献占所有奇异值贡献的比例?其实所有奇异值的总贡献,也就是平方和,等于矩阵的转置乘以矩阵得到的矩阵对角线元素之和,其实就是矩阵本身所有元素的平方和。对应到三列数据,就是第三列数据的平方和。

接下来按照稀疏矩阵的 scipy.sparse.csr_matrix 的格式要求处理一下数据,并进行奇异值分解。

from scipy.sparse.linalg import svds

from scipy.sparse import csr_matrix

X = csr_matrix((df[2]+0.0001, (df[0], df[1])))  # 这里加上0.0001是因为,svds分解时只接受float和double型数字

U,Sigma,VT = svds(X, SigmaN) # SigmaN 为奇异值个数

VT 为 K*N的矩阵,K为奇异值个数,N为物品个数,接下来计算物品相似度矩阵,对角线为1的对称矩阵。由于物品个数有限,这里直接利用numpy矩阵计算方法来计算物品两两之间的相似度。在内存够用的情况下,矩阵计算比我自己写的循环快的多多多。。。

def cosSim(VT):

    r, c = VT.shape

    VTnorm = np.array(np.sqrt(np.mat(VT*VT).T*np.mat([1]*r).T))

    VTm = np.mat(VT)

    VTV = VTm.T*VTm

    cosSim = 0.5 + 0.5*np.array(VTV)/VTnorm/VTnorm.T

    return np.mat(cosSim)


最后,使用相似度矩阵计算用户对每个商品之间可能购买频次,返回每个用户的推荐商品topN。

def recommend(X,sim,u,N):

    return np.argsort(np.array(X[u,:]*Sim)/np.sum(Sim, 1).T)[:, -1:-(1+N):-1]


由于用户数量巨大,可以分批计算结果并输出

output = open('user_result.txt', 'w')

l = step(X.shape[1], len(user_valid))


for i in range(len(l)-1):

    print(l[i], l[i+1])

    Y = recommend(X, Sim, user_valid[l[i]:l[i+1]], itemN).tolist()

    for u, y in zip(user_valid[l[i]:l[i+1]], Y):

        output.write(str(u))

        output.write(":")

        line = [str(s) for s in y]

        output.write(",".join(line)+ "\n")

        

output.close()


其中step函数如下:

def step(item_num, user_num):

    step = int(1e8/item_num)

    l = [k for k in range(0, user_num, step)]

    return l + [user_num]

#########################################################################

完整代码,如下:

import numpy as np

import pandas as pd

from scipy.sparse.linalg import svds

from scipy.sparse import csr_matrix

import datetime

import time


SigmaN = 10

itemN = 5

testSize = 1e8


def timer(func):

    def wrapper(*arg, **kw):

        t0 = time.time()

        print(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), func.__name__, 'start......')

        result = func(*arg, **kw)

        t1 = time.time()

        print(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), func.__name__, 'finished, spent time:', round(t1-t0, 2), 's.')    

        return result

    return wrapper



@timer

def cosSim(VT):

    r, c = VT.shape

    VTnorm = np.array(np.sqrt(np.mat(VT*VT).T*np.mat([1]*r).T))

    VTm = np.mat(VT)

    VTV = VTm.T*VTm

    cosSim = 0.5 + 0.5*np.array(VTV)/VTnorm/VTnorm.T

    return np.mat(cosSim)

    

@timer

def recommend(X,sim,u,N):

    return np.argsort(np.array(X[u,:]*Sim)/np.sum(Sim, 1).T)[:, -1:-(1+N):-1]


@timer

def step(item_num, user_num):

    step = int(testSize/item_num)

    l = [k for k in range(0, user_num, step)]

    return l + [user_num]


df = pd.read_table("data.txt", header=-1, sep=",")

df_valid = pd.read_table("user.txt", header=-1, sep=",")

output = open('user_result.txt', 'w')

user_valid = df_valid[0]


X = csr_matrix((df[2]+0.0001, (df[0], df[1])))

del df

U,Sigma,VT = svds(X, SigmaN)

del U, Sigma

Sim = cosSim(VT)


l = step(X.shape[1], len(user_valid))


for i in range(len(l)-1):

    print(l[i], l[i+1])

    Y = recommend(X, Sim, user_valid[l[i]:l[i+1]], itemN).tolist()

    for u, y in zip(user_valid[l[i]:l[i+1]], Y):

        output.write(str(u))

        output.write(":")

        line = [str(s) for s in y]

        output.write(",".join(line)+ "\n")

        

output.close()




以上是关于推荐算法学习实践:基于物品相似度的主要内容,如果未能解决你的问题,请参考以下文章

推荐系统(一):基于物品的协同过滤算法

推荐系统中物品相似度计算

专栏1-推荐系统基于物品的协同过滤算法

基于物品的协同过滤算法ItemCF算法实现

基于协同过滤推荐算法的图书推荐研究

协同过滤推荐算法简述