欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

协同过滤算法

程序员文章站 2022-07-14 22:04:50
...

说明:本博客是学习《python机器学习算法》赵志勇著的学习笔记,其图片截取也来源本书。

协同过滤(ccollaborative Filtering,CF)算法是最基本的推荐算法,CF算法是从用户的历史行为数据中挖掘出用户的兴趣,为用户推荐其感兴趣的项。
根据挖掘方法的不同,协同过滤算法可以分为基于用户的(User-based)协同过滤算法和基于项的(Item-based)协同过滤算法。

1、推荐系统的概述

1.1、推荐系统

在信息过载的时代,信息呈现出爆炸式增长,大量的信息给用户不断带来新的信息的同时,也增加了用户筛选信息的难度,当用户有明确的需求时,比如需要
查找信息,可以利用搜索引擎查找到相关的信息。

但是并不是任何情况下用户都是有明确要求的,纯粹是为了打发时间,为了能够帮助用户筛选出一批他们可能喜欢的信息,就需要分析出该用户的兴趣,
从海量的信息中选择与用户相似的信息,并将这些信息推荐给用户。推荐系统(Recommendation System,RS)正是在这样的背景下被提出的,推荐算法根据用户的历史行为,挖掘出用户的喜好,并为用户推荐与其喜好相符的商品或者信息。推荐系统的任务就是能够连接信息与用户,帮助用户找到其感兴趣的信息,
同时让一些有价值的信息能够推荐给潜在的用户。

1.2、推荐问题的描述

推荐系统的核心问题是为用户推荐与其兴趣相似度比较高的商品。此时,需要一个函数f(x),并用来计算商品与用户之间的相似度,并向用户推荐相似度比较高的的商品。为了能够预测出函数f(x),可以利用到的历史数据主要有:用户的历史行为数据,与该用户相关的其他用户信息,商品之间的相似度、文本的描述等。

假设集合C表示所有的用户,集合S表示所有需要推荐的商品。函数f表示商品s到用户c之间的有效性的效用函数,例如:
f:C*S -> R , 其中,R是一个全体的排序集合。对于每一个用户c属于C,希望从商品的集合中选出商品,即s属于S,以使得函数f的值最大。

1.3、推荐的常用方法

在推荐系统中,常用的推荐算法主要有:

协同过滤的推荐(Collaborative Filtering Recommendation)

基于内容的推荐(Content-based Recommendation)

基于关联规则的推荐(Association Rule-based Recommendation)

基于效用的推荐(Utility-based Recommendation)

基于知识的推荐(knowledge-based Recommendation)

组合推荐(Hybrid Recommendation)

2、基于协同过滤的推荐

2.1、协同过滤算法的分类

上面讲过基于协同过滤算法有两种方法:

1、通过相似用户进行推荐,通过比较用户之间的相似性,越相似表明两者之间的品味越相近,这就是基于用户的协同过滤算法

2、通过相似项进行推荐。通过比较项与项之间的相似性,为用户推荐与其历史行为相似的项。

在基于用户的协同过滤算法中,利用用户访问行为的相似性向目标用户推荐其感兴趣的项,如下图所示。

协同过滤算法

在上图中,假设用户分别为u1、u2、u3,其中用户u1互动过的商品有i1和i3,用户u2互动过的商品为i2,用户u3互动过的商品有i1、i3和i4。通过计算知道u1和u3较为相似,对于u1来说,u3互动过的商品i4是u1没有互动过的,因此会将商品i4推荐给u1。

在基于项的协同过滤算法中,根据所有用户对物品的评价,发现物品的相似度,然后根据用户的历史偏好将类似的物品推荐给该用户,如下图所示。
协同过滤算法

在上图中,u1对应i1和i3;u2对应i1、i2和i3;u3对应i1。通过计算知道i1和i3较为相似,对于用户i3来说,u1互动过的商品i3是u3没有互动过的,因此会将i3推荐给u3。

2.2、相似度的度量方法

相似性的度量方法有很多种,不同的度量方法的应用范围也不一样。相似度的度量方法在不同机器学习算法中都有应用。

相似性的度量方法必须满足拓扑学中的度量空间的基本条件:假设d是度量空间M上的度量:d:M*M -> R,其中度量d满足:

非负性:d(x,y) >= 0,当且仅当x=y时取等号;

对称性:d(x,y) = d(y,x)

三角不等式性:d(x,z) <= d(x,y) + d(y,z)

这里主要介绍三种相似性的度量方法,分别为:欧式距离、皮尔逊相关系数和余弦相似度。

欧式距离

皮尔逊相关系数
在欧式距离中,不同特征之间的量级对欧氏距离的影响比较大,因此计算欧式距离一般都要将特征值进行归一化。而皮尔逊相关系数的度量方法对量级不敏感,其具体形式为:
协同过滤算法
其中< X,Y >表示向量X和向量Y的内积,||X||表示向量X的范数。利用皮尔逊相关系数计算。

余弦相似度
余弦相似度是文本相似度度量中使用比较多的一种方法,对于两个向量X和Y,其对应的形式如下:
协同过滤算法
其对应的余弦相似度为:
协同过滤算法

3、基于用户的推荐系统

import numpy as np
from math import sqrt

'''导入用户商品数据
input:  file_path(string):用户商品数据存放的文件
output: data(mat):用户商品矩阵
'''
users = {"小明": {"中国合伙人": 5.0, "太平轮": 3.0, "荒野猎人": 4.5, "老炮儿": 5.0, "我的少女时代": 3.0, "肖洛特烦恼": 4.5, "火星救援": 5.0},
         "小红":{"小时代4": 4.0, "荒野猎人": 3.0, "我的少女时代": 5.0, "肖洛特烦恼": 5.0, "火星救援": 3.0, "后会无期": 3.0},
         "小阳": {"小时代4": 2.0, "中国合伙人": 5.0, "我的少女时代": 3.0, "老炮儿": 5.0, "肖洛特烦恼": 4.5, "速度与激情7": 5.0},
         "小四": {"小时代4": 5.0, "中国合伙人": 3.0, "我的少女时代": 4.0, "匆匆那年": 4.0, "速度与激情7": 3.5, "火星救援": 3.5, "后会无期": 4.5},
         "六爷": {"小时代4": 2.0, "中国合伙人": 4.0, "荒野猎人": 4.5, "老炮儿": 5.0, "我的少女时代": 2.0},
         "小李":  {"荒野猎人": 5.0, "盗梦空间": 5.0, "我的少女时代": 3.0, "速度与激情7": 5.0, "蚁人": 4.5, "老炮儿": 4.0, "后会无期": 3.5},
         "隔壁老王": {"荒野猎人": 5.0, "中国合伙人": 4.0, "我的少女时代": 1.0, "Phoenix": 5.0, "甄嬛传": 4.0, "The Strokes": 5.0},
         "邻村小芳": {"小时代4": 4.0, "我的少女时代": 4.5, "匆匆那年": 4.5, "甄嬛传": 2.5, "The Strokes": 3.0}
        }

# 定义几种距离计算函数
# 更高效的方式为得把分向量化之后使用scipy中定义的distance
def euclidean_dis(rating1,rating2):
    """计算2个打分序列间的欧式距离,输入的rating1和rating2都是打分dict
        格式为{"小时代4":1.0,"疯狂动物城":5.0}
    """
    distance = 0
    commonRatings = False
    for key in rating1:
        if key in rating2:
            distance += (rating1[key] - rating2[key])^2
            commonRatings = True
    # 两个打分序列之间公共打分电影
    if commonRatings:
        return distance
    else:
        return -1

def manhattan_dis(rating1,rating2):
    """计算2个打分序列间的曼哈顿距离. 输入的rating1和rating2都是打分dict
       格式为{'小时代4': 1.0, '疯狂动物城': 5.0}"""
    distance = 0
    commonRatings = False
    for key in rating1:
        if key in rating2:
            distance += abs(rating1[key] - rating2[key])
            commonRatings = True
    #两个打分序列之间有公共打分电影
    if commonRatings:
        return distance
    #无公共打分电影
    else:
        return -1

def cos_dis(rating1, rating2):
    """计算2个打分序列间的cos距离. 输入的rating1和rating2都是打分dict
       格式为{'小时代4': 1.0, '疯狂动物城': 5.0}"""
    distance = 0
    dot_product_1 = 0
    dot_product_2 = 0
    commonRatings = False

    for score in rating1.values():
        dot_product_1 += score^2
    for score in rating2.values():
        dot_product_2 += score^2

    for key in rating1:
        if key in rating2:
            distance += rating1[key]*rating2[key]
            commonRatings = True
    #两个打分序列之间有公共打分电影
    if commonRatings:
        return 1-distance/sqrt(dot_product_1*dot_product_2)
    #无公共打分电影
    else:
        return -1

def pearson_dis(rating1,rating2):
    """计算2个打分序列间的pearson距离. 输入的rating1和rating2都是打分dict
       格式为{'小时代4': 1.0, '疯狂动物城': 5.0}"""
    sum_xy = 0
    sum_x = 0
    sum_y = 0
    sum_x2 = 0
    sum_y2 = 0
    n=0

    for key in rating1:
        if key in rating2:
            n += 1
            x = rating1[key]
            y = rating2[key]
            sum_xy += x*y
            sum_x += x
            sum_y += y
            sum_x2 += pow(x,2)
            sum_y2 += pow(y,2)
    # 计算分母
    denominator = sqrt(sum_x2-pow(sum_x,2)/n)*sqrt(sum_y2-pow(sum_y,2)/n)
    if denominator == 0:
        return 0
    else:
        return(sum_xy-(sum_x*sum_y)/n)/denominator

#查找最近邻
def computerNearestNeighor(username,users):
    """在给定username的情况下,计算其他用户和它的距离并排序"""
    distances = []
    for user in users:
        if user != username:
            distance = pearson_dis(users[user],users[username])
            distances.append((distance,user))
    # 根据距离排序,距离越近,排的越靠前
    distances.sort()
    return distances

# 对指定的user推荐电影
def recommend(username,users):
    nearest = computerNearestNeighor(username,users)[0][1]
    print("nearest : ",nearest)

    recommendations = []
    # 找到最近邻看过,但是我们没有看过的电影,计算推荐
    neighorRatings = users[nearest]
    # print("neighorRatings : ",neighorRatings)
    userRatings = users[username]
    # print("userRatings : ",userRatings)
    for artist in neighorRatings:
        if not artist in userRatings:
            recommendations.append((artist,neighorRatings[artist]))
    results = sorted(recommendations,key=lambda artistTuple:artistTuple[1],reverse=True)
    for result in results:
        print(result[0],result[1])
recommend('六爷', users)
nearest :  小红
肖洛特烦恼 5.0
火星救援 3.0
后会无期 3.0
# 简单的张量分解进行打分和推荐
import numpy as np

#手写矩阵分解
#现在有很多很方便对高维矩阵做分解的package,比如libmf, svdfeature等
def matrix_factorization(R,P,Q,K,steps=5000,alpha=0.0002,beta=0.02):
    Q = Q.T
    for