推荐系统基础

一.推荐系统概论

推荐系统是为了解决信息过载。

  1. 推荐系统的目的

  • 让用户更快更好的获取到自己 需要的内容

  • 让内容更快更好的推送到喜欢他的用户手中

  • 让网站更有效的保留客户资源

  1. 推荐系统的基本思想

  • 利用用户和物品的特征信息,给用户推荐那些具有用户喜欢的特征物品

  • 利用用户喜欢过的物品,给用户推荐与他虚幻过的物品相似的物品

  • 利用和用户相似的其他用户,给用户推荐那些和他们兴趣爱好相似的其他用户喜欢的物品

  1. 推荐系统的数据分析

  • 要推荐物品和内容的元数据,例如关键字,分类标签,基因描述等

  • 系统用户的基本信息,例如年龄,性别,兴趣爱好

  • 用户的行为数据,可以转化为对物品或者信息的偏好,根据应用本身的不同,可能包括用户对物品的评分,用户查看物品的记录,用户的购买记录等,这些用户的偏好信息可以分为两类:

1.显式的用户反馈:这类是用户在网站上自然浏览或者使用网站以外,显示提供反馈消息,例如用户对物品的评分,或者对物品的评论

2.隐式的用户反馈:这类事用户在使用网站时产生的数据,隐式的反应了用户对物品的喜好,例如购买了某物品,用户查看了某物品的信息等。

  1. 推荐系统的分类

  1. 根据实时性分类:离线推荐和实时推荐

  1. 根据推荐是否个性化分类:基于统计的推荐和个性化推荐

  1. 根据推荐原则分类:基于相似度的推荐,基于知识的推荐和基于模型的推荐

  1. 根据数据源分类:基于人口统计学的推荐,基于内容的推荐(CB)和基于协同过滤(CF)的推荐(重点介绍)

  • 基于内容与基于协同过滤的区别:

基于内容主要利用的是用户评价过的物品的内容特征,而CF方法还可以利用其他用户评分过的物品内容。CF可以解决CB的一些局限:

1.物品内容不完全或者难以获得时,依然可以通过其他用户的反馈给出意见

2.CF基于用户之间对物品的评价质量,避免了CB仅依赖内容可能造成的对物品质量判断的干扰 3.CF推荐不受内容限制,只要其他类似用户给出了对不同物品的兴趣,CF就可以给用户推荐出内容差异很大的物品

  • 协同过滤的分类:

基于近邻的协同过滤:基于用户和基于物品

基于模型的协同过滤:奇异值分解,潜在语义分析,支撑向量机

  • 混合推荐

实际网站的推荐系统往往都不是单纯只采用了某一种推荐的机制和策略,往往时将多个方法混合在一起,从而达到更好的推荐效果。比较流行的组合方法是:

1.加权混合:用线性将不同的推荐按照一定权重组合起来,具体权重的值需要在测试数据集上反复实验,从而达到最好的推荐效果

2.切换混合:允许在不同的情况(数据量,系统运行状况,用户和物品的数目等)下,选择最为合适的推荐机制

3.分区混合:采用多种推荐机制,并将不同的推荐结果分给不同的区显示给用户

4.分层混合:采用多种推荐机制,并将一个推荐机制的结果作为另一个输入,从而综合各个推荐机制的优缺点,得到更加准确的推荐

  1. 推荐系统实验方法

  1. 离线实验:

  • 通过体制系统获得用户行为数据,并按照一定格式生成一个标准的数据集

  • 将数据集按照一定的规则分成训练集和测试集

  • 在训练集上训练用户兴趣模型,在测试集上进行预测

  • 通过实现定义的离线指标评测算法在测试集上的预测结果

  1. 用户调查

  • 用户调查需要有一些真实用户,让他们在需要测试的推荐系统上完成一些任务;我们需要记录他们的行为,并让他们回答一些问题,最后进行分析。

  1. 在线实验

  • AB测试(把用户分为两份,把他们分别引到不同的推荐系统中去)

  1. 推荐系统评测指标

用户满意度(问卷调查),覆盖率(不要只推荐热门的商品,而忽略小众的商品),多样性(避免搜了一个商品,推荐的全是此商品),惊喜度(推荐出用户没有想到但是喜欢的商品),信任度,实时性,健壮性,商业目标(销售指标),预测准确度(用户对物品的历史评分,从中学习一个兴趣模型,预测用户对新物品的评分;均方根误差或平均绝对误差)

Top-N推荐:网站提供推荐服务时,一般是给用户一个个性化的推荐列表。这种推荐的预测准确率一般用精确率和召回率来度量。

二.基于人口统计学的推荐算法

基于人口统计学的推荐机制是一种最易于实现的推荐方法,它只是简单的根据系统用户的基本信息发现用户的相关程度,然后将相似用户喜爱的其他物品推荐给当前用户。

对于没有明确含义的用户信息(比如登陆时间、地域等上下文信息),可以通过聚类等给用户打上分类标签。对于特定标签的用户,又可以根据预设的规则或者模型,推荐出对应的物品。

用户信息标签化的过程一般称为用户画像(就是将用户信息标签化(Tag或者Label),以用户为中心,将各种各样的标签对应到其身上)

三.基于内容的推荐算法

根据推荐物品或内容的元数据,发现物品的相关性,再基于用户过去的喜好记录,为用户推荐相似的物品。

通过抽取物品内在或者外在的特征值,实现相似度计算。将用户个人信息的特征(基于喜好记录或者是预设兴趣标签),和物品的特征相匹配,就能得到用户对物品感兴趣的程度。

  • 相似度的评判可以用距离来表示,一般常用的是余弦相似度(两向量的夹角)

  • 对物品的特征提取--打标签(tag):专家标签(PGC)、用户自定义标签(UGC)、降维分析数据提取隐语义标签(LFM)

  • 对于文本信息的特征提取--关键词:分词、语义处理和情感分析(NLP),潜在语义分析(LSA)

三.特征工程

  • 特征:数据中抽取出来的对结果预测有用的信息

  • 特征的个数就是数据的观测维度

  • 特征工程是使用专业背景知识和技巧处理数据,使得特征能在机器学习算法上发挥更好的作用的过程

  • 特征工程一般包括特征清洗(采样、清洗异场样本),特征处理和特征选择

四.基于用户自定义标签的推荐

用户用标签来描述对物品的看法,所以用户生成标签(UGC)是联系用户与物品的纽带,也是反应用户兴趣的重要数据源。一个用户标签行为的数据集一般由一个三元组(用户、物品、标签)的集合表示,其中一条记录(u,i,b)表示用户u给物品i打上了标签b。

  • 一个简单的算法:

-统计每个用户最常用的标签

-对于每个标签,统计被打过这个标签次数最多的物品

-对于一个用户,首先找到他常用的标签,然后找到具有这些标签的最热门的物品,推荐给他

-所以用户u对物品i的兴趣公式为:p(u,i)=

是用户u打过标签b的次数,物品i被打过标签b的次数

  • 缺点:这种方法倾向于给热门标签(谁都会给的标签)、热门物品(打标签人数最多)比较大的权重,如果一个热门物品对应着热门标签,那他就会”霸榜“,推荐的个性化、新颖性就会降低。

  • 为了避免热门标签和热门物品获得更多的权重,我们需要对”热门“进行惩罚。

由此引出了TF-IDF

TF-IDF

词频-逆文档频率是一种用于资讯检索与文本挖掘的常用加权技术。

是一种统计方法,用以评估一个字词对于一个文件集或一个语料库中的其中一份文件的重要程度。字词的重要性随着它在文件中出现的次数成正比增加,但同时会随着它在语料库中出现的频率会反比下降。

简单来说就是,一个字词在所有文章中(或者标签中)出现频率高,就是热门,相应的让他成反比下降。

TFIDF=TF*IDF

主要思想是如果某个词或短语在一篇文章中出现的频率TF高,并且在其他文章中很少出现,则认为此词或者短语具有很好的类别区分能力,适合用来分类。

  • TF--词频:指的是某一个给定的词语在该文件中出现的频率(这个数字是对词数的归一化)

TF表示词语i在文章j中出现的频率,分子表示i在j中出现的次数,分母表示文章j的总词数

  • IDF--逆向文件频率:是一个词语普遍重要性的度量 IDF表示词语i在文档集中的逆文档频率,N表示文档集中的文档总数,Ni表示文档集中包含了词语i的文档数。(N+1为了防止等于0)

实现代码

import numpy as np
import pandas as pd

docA = "The cat sat on my bed"  # 文档A
docB = "The dog sat on my knees"  # 文档B

bowA = docA.split(" ")
bowB = docB.split(" ")

# 构建词库
wordSet = set(bowA).union(set(bowB))  # 两个集合取并集
# print(wordSet)

# 进行词数统计
# 用统计字典来保存词出现的次数
wordDirtA = dict.fromkeys(wordSet, 0)
wordDirtB = dict.fromkeys(wordSet, 0)
# 遍历文档,统计次数
for word in bowA:
    wordDirtA[word] += 1
for word in bowB:
    wordDirtB[word] += 1
print(pd.DataFrame([wordDirtA, wordDirtB]))  # 输出列表


# 计算TF
def computeTF(wordDict, bow):
    # 用一个字典对象记录TF,把所有词对应在bow文档中的tf都算出来
    tfDict = {}
    nbowCount = len(bow)
    for word, count in wordDict.items():
        tfDict[word] = count / nbowCount
    return tfDict


# 测试
tfA = computeTF(wordDirtA, bowA)
tfB = computeTF(wordDirtB, bowB)


# print(tfA)
# print(tfB)

# 计算逆文档频率
def computeIDF(wordDictList):
    # 用字典对象保存IDF结果,每个词作为key
    idfDict = dict.fromkeys(wordDictList[0], 0)
    N = len(wordDictList)
    import math
    # 遍历每个文档对应的字典
    for wordDict in wordDictList:
        # 遍历字典中的每个词汇,统计Ni
        for word, count in wordDict.items():
            if count > 0:
                # 先把Ni增加1,存入到idfDict
                idfDict[word] += 1
    for word, ni in idfDict.items():
        idfDict[word] = math.log10((N + 1) / (ni + 1))
    return idfDict


# 测试
idfs = computeIDF([wordDirtA, wordDirtB])
# print(idfs)


# 计算TF-IDF
def computeTFIDF(tf, idfs):
    tfidf = {}
    for word, tfval in tf.items():
        tfidf[word] = tfval * idfs[word]
    return tfidf


# 测试
tfidfA = computeTFIDF(tfA, idfs)
tfidfB = computeTFIDF(tfB, idfs)
print(pd.DataFrame([tfidfA, tfidfB]))

五.基于协同过滤的推荐算法

  1. 基于近邻的的协同过滤

基于近邻的协同过滤分为基于用户和基于物品。

  1. 基于用户的协同过滤

根据所有用户对物品的偏好,发现与当前用户口味和偏好相似的”邻居”群,并推荐近邻所偏好的物品

  • 与人口统计学推荐算法的不同:基于人口统计学的机制只考虑用户本身的特征,而基于用户的协同过滤机制可以在用户的历史偏好的数据上计算用户的相似度,它的基本假设是喜欢类似物品的用户可能有相同或者相似的口味和偏好。

  1. 基于物品的协同过滤

使用所有用户对物品的偏好,发现物品和物品之间的相似度,然后根据用户的历史偏好信息,将类似的物品推荐给用户

  • 与基于内容的推荐的不同:基于物品的协同过滤是从用户的历史偏好推断,而基于内容是基于物品本身的属性特征信息

  1. 基于用户和基于物品的比较

基于物品:物品的个数远远小于用户的数量,而且物品的个数和相似度比较稳定,同时基于物品的机制比基于用户的实时性更好一些

基于用户:物品大于用户的个数,比如新闻推荐系统中

  1. 基于用户和物品的优缺点

优点:可以共用他人的经验,很好的支持用户发现潜在的兴趣偏好;不需要对物品或者用户进行严格的建模,而且不要求对物品特征的描述机器可理解的。

缺点:核心是基于历史数据,所以对新用户和新物品有“冷启动”的问题;推荐效果依赖于用户历史偏好数据的多少和准确性;对于一些特殊品味的用户不能给予很好的推荐。

  1. 基于模型的协同过滤

基本思想

  • 用户有一定的特征,决定着他的偏好选择

  • 物品有一定的特征,影响着用户需要是否选择它

  • 用户之所以选择某一个商品,是因为用户特征与物品特征相互匹配

基于模型的协同过滤就是基于样本的用户偏好信息,训练一个推荐模型,然后根据实时的用户喜好的信息进行预测新物品的得分,计算推荐。基于近邻的推荐是在预测时直接使用已有的用户偏好数据,通过近邻数据来预测对新物品的偏好(类似分类);而基于模型的方法是要用这些偏好数据来训练模型,找到内在规律,再用模型来做预测(类似回归)。

模型的建立相当于从行为数据中提取特征,给用户和物品同时打上标签。训练模型时,可以基于标签内容来提取物品特征,也可以让模型去发掘物品的潜在特征,这样的模型被称为隐语义模型

隐语义模型

  • 揭示隐藏特征,这些特征能够解释为什么给出对应的预测评分,这类特征可能是无法用语言解释描述的,事实上我们并不需要知道。

  • 协同过滤算法非常依赖历史数据,而一般的推荐系统中,偏好数据又往往是稀疏的,这就需要对原始数据进行降维处理,分解之后的矩阵就代表了用户和物品的隐藏特征

  • 隐语义模型的实例:基于概率的隐语义分析(pLSA);隐式迪利克雷分布模型(LDA);矩阵因子分解模型(基于奇异值分解的模型,SVD)

LFM降维方法--矩阵因子分解

假设用户物品评分矩阵为R,现在有m个用户,n个物品,我们想要发现个隐类,我们的任务是找到两个矩阵P和Q,使这两个矩阵的乘积近似等于R,即将用户物品评分矩阵R分解为两个低维矩阵相乘

我们可以认为用户之所以给物品打出这样的评分是有内在原因的,我们可以挖掘出影响用户打分的隐藏因素,进而根据未评分物品与这些隐藏因素的关联度决定此未评分物品的预测评分。

LFM进一步理解:一个用户不可能对所有物品都有评分,所以用户评分矩阵大部分项时空的,是一个稀疏矩阵,所以我们根据用户已有的评分预测出用户对其他物品的评分来填补矩阵。

模型的求解--损失函数

现在的问题是怎么得到分解方式

矩阵分解得到的预测评分矩阵R',与原评分矩阵R在已知的评分项上可能会有误差,我们的目标是找到一个最好的分解方式,让分解之后的预测评分矩阵误差最小。

我们选择平方损失函数,并且加入正则化项,以防过拟合

其中

是正则化项,λ 一般通过交叉验证得到的

使损失函数最小化的过程,一般采用随机梯度下降算法或者交替最小二乘法来实现。

  • 交替最小二乘法(ALS)

思想:由于两个矩阵P和Q都未知,且通过矩阵乘法耦合在一起,为了使他们解耦,可以先固定Q,把P当作变量,通过损失函数最小化求出P,这就是经典的最小而成问题,再反过来固定求得的P,把Q当作变量,求解Q,如此交替执行,知道误差满足阈值条件,或者到达迭代上限。

更精确但计算复杂

  • 梯度下降算法

import numpy as np
import pandas as pd

# 评分矩阵
R = np.array([[4, 0, 2, 0, 1],
              [0, 2, 3, 0, 0],
              [1, 0, 2, 4, 0],
              [5, 0, 0, 3, 1],
              [0, 0, 1, 5, 1],
              [0, 3, 2, 4, 1]])
'''
@输入参数
R:M*N的评分矩阵
K:隐特征向量维度
max_iter:最大迭代次数
alpha:步长
lamda:正则化系数

@输出
分解之后的P、Q
P:初始化用户特征矩阵M*k
Q:初始化物品特征矩阵N*K
'''

# 给定超参数

K = 5
max_iter = 5000
alpha = 0.0002
lamda = 0.004


# 核心算法
def LFM_grad_desc(R, K, max_iter, alpha=0.0002, lamda=0.002):
    # 基本维度参数定义
    M = len(R)
    N = len(R[0])

    # P、Q初始值,随机生成
    P = np.random.rand(M, K)
    Q = np.random.rand(N, K)
    Q = Q.T

    # 开始迭代
    for step in range(max_iter):
        # 对所有的用户u、物品i做遍历,对应的特征向量Pu,Qi梯度下降
        for u in range(M):
            for i in range(N):
                # 对于每一个大于0的评分,求出预测的评分误差
                if R[u][i] > 0:
                    eui = np.dot(P[u, :], Q[:, i]) - R[u][i]

                    # 带入公式,按照梯度下降算法更新当前的Pu与Qi
                    for k in range(K):
                        P[u][k] = P[u][k] - alpha * (2 * eui * Q[k][i] + 2 * lamda * P[u][k])
                        Q[k][i] = Q[k][i] - alpha * (2 * eui * P[u][k] + 2 * lamda * Q[k][i])

        # u、i遍历完成,所有的特征向量更新完成,可以得到P、Q,可以计算预测评分矩阵
        predR = np.dot(P, Q)

        # 计算当前损失函数
        cost = 0
        for u in range(M):
            for i in range(N):
                if R[u][i] > 0:
                    cost += (np.dot(P[u, :], Q[:, i]) - R[u][i]) ** 2
                    # 加上正则化项
                    for k in range(K):
                        cost += lamda * (P[u][k] ** 2 + Q[k][i] ** 2)

        if cost < 0.001:
            break

    return P, Q.T, cost


P, Q, cost = LFM_grad_desc(R, K, max_iter, alpha, lamda)

print(P)
print(Q)
print(cost)
print(R)

predR = P.dot(Q.T)
# 预测矩阵
print(predR)

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值