文章目录
- 基于人口统计学的推荐算法
- 用户画像
- 基于内容的推荐算法
- 相似度计算
- 基于内容推荐系统的高层次结构
- 特征工程
- 数值型特征处理
- 类别特征处理
- 时间型特征处理
- 统计型特征处理
- 推荐系统常见反馈数据
- 基于UGC的推荐
- TF-IDF
- TF-IDF算法示例
- 1. 引入依赖
- 2. 定义数据和预处理
- 3. 进行词数统计
- 4. 计算词频TF
- 5. 计算逆文档频率idf
- 6. 计算TF-IDF
- 基于协同过滤的推荐算法
- 基于近邻的推荐
- 基于协同过滤的推荐优缺点
- 基于模型的协同过滤思想
- 隐语义模型(LFM)
- LFM降维方法——矩阵因子分解
- LFM进一步理解
- 矩阵因子分解
- 模型求解——损失函数
- 模型求解算法——ALS
- 梯度下降算法
- 代码实现
- 1. 引入依赖
- 2. 定义数据
- 3. 算法实现
- 4. 测试
常用推荐算法分类:
- 基于人口统计学的推荐与用户画像
- 基于内容的推荐和特征工程
- 基于协同过滤的推荐
基于人口统计学的推荐算法
- 基于人口统计学的推荐机制(Demographic-based Recommendation)是一种最易实现的推荐算法,它只是简单的根据系统用户的基本信息发现用户的相关程度,然后将相似用户喜爱的其他物品推荐给当前用户
- 对于没有明确含义的用户信息(比如登录时间、地域等上下文信息),也可以通过聚类等手段,给用户打上分类标签。
- 对于特定标签的用户,又可以根据预设的规则(知识)或者模型,推荐出对应的物品
- 用户信息化的过程一般又称为用户画像(User Profile)
用户画像
- 用户画像(User Profile)就是企业通过收集与分析消费者社会属性、生活习惯、消费行为等主要信息的数据之后,完美地抽象出一个用户的商业全貌作为企业应用大数据技术的基本方式
- 用户画像为企业提供了足够的信息基础,能够帮助企业快速找到精准用户群体以及用户需求等更为广泛的反馈信息
- 作为大数据的根基,它完美地抽象出一个用户的信息全貌,为进一步精准、快速地分析用户行为习惯、消费习惯等重要信息,提供了足够的数据基础
基于内容的推荐算法
- Content-bashed Recommendation(CB)根据推荐物品或内容的元数据,发现物品的相关性,再基于用户过去的喜好记录,为用户推荐相似的物品。
- 通过抽取物品内在或者外在的特征值,实现相似度计算。
- 比如一个电影,有导演、演员、用户标签UGC、用户评论、时长、风格等等,都可以算是特征。、
- 将用户(user)个人信息的特征(基于喜好记录或是预设兴趣标签),和物品(item)的特征相匹配,就能得到用户对物品感兴趣的程度
- 在一些电影、音乐、图书的社交网络有很成功的应用,有些网站还请专业人员对物品进行基因编码/打标签(PGC)
- 对于物品的特征提取——打标签(tag)
- 专家标签(PGC)
- 用户自定义标签(UGC)
- 降维分析数据,提取隐语义标签(LFM)
- 对于文本信息的特征提取——关键字
- 分词、语义处理和情感分析(NLP)
- 潜在语义分析(LSA)
相似度计算
基于内容推荐系统的高层次结构
特征工程
- 特征(feature):数据中抽取出来的对结果预测有用的信息。
- 特征的个数就是数据的观测维度
- 特征工程是使用专业背景知识和技巧处理数据,使得特征能在机器学习算法上发挥更好的作用的过程
- 特征工程一般包括特征清洗(采样、清洗异常样本),特征处理,特征选择。
- 特征按照不同的数据类型分类,有不同的特征处理方法:
- 数值型
- 类别型
- 时间型
- 统计型
数值型特征处理
- 归一化:
- 离散化:
类别特征处理
- 类别型特征本身没有大小关系,需要将它们编码为数字,它但们之间不能有预先设定的大小关系,因此既要做到公平,又要区分它们,那么直接开辟多个空间
- One-Hot编码/哑变量
- One-Hot编码/哑变量所做的就是将类别型数据平行地展开,也就是说,经过One-Hot编码/哑变量后,这个特征的空间会膨胀
时间型特征处理
时间特征既可以看做连续值,又可以看作离散值
- 连续值
- 持续时间(网页浏览时长)
- 间隔时间(上次购买/点击离现在的时间间隔)
- 离散值
- 一天中哪个时段
- 一周中的星期几
- 一年中的哪个月/星期
- 工作日/周末
统计型特征处理
- 加减平均:商品价格高于平均价格多少,用户在某个品类下消费超过多少。
- 分位线:商品属于售出商品价格的分位线处。
- 次序性:商品处于热门商品第几位。
- 比例类:电商中商品的好/中/差评比例。
推荐系统常见反馈数据
基于UGC的推荐
基于UGC简单推荐的问题:
TF-IDF
- TF-IDF对基于UGC推荐的改进:
TF-IDF算法示例
1. 引入依赖
import numpy as np
import pandas as pd
2. 定义数据和预处理
docA = "The cat sat on my bed"
docB = "The dog sat on my knees"
bowA = docA.split(" ")
bowB = docB.split(" ")
bowA
# 构建词库
wordSet = set(bowA).union(set(bowB))
wordSet
{'The', 'bed', 'cat', 'dog', 'knees', 'my', 'on', 'sat'}
3. 进行词数统计
# 用统计字典来保存词出现的次数
wordDictA = dict.fromkeys( wordSet, 0 )
wordDictB = dict.fromkeys( wordSet, 0 )
# 遍历文档,统计词数
for word in bowA:
wordDictA[word] += 1
for word in bowB:
wordDictB[word] += 1
pd.DataFrame([wordDictA, wordDictB])
4. 计算词频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( wordDictA, bowA )
tfB = computeTF( wordDictB, bowB )
tfA
{'my': 0.16666666666666666,
'on': 0.16666666666666666,
'knees': 0.0,
'dog': 0.0,
'The': 0.16666666666666666,
'sat': 0.16666666666666666,
'cat': 0.16666666666666666,
'bed': 0.16666666666666666}
5. 计算逆文档频率idf
def computeIDF( wordDictList ):
# 用一个字典对象保存idf结果,每个词作为key,初始值为0
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
# 已经得到所有词汇i对应的Ni,现在根据公式把它替换成为idf值
for word, ni in idfDict.items():
idfDict[word] = math.log10( (N+1)/(ni+1) )
return idfDict
idfs = computeIDF( [wordDictA, wordDictB] )
idfs
{'cat': 0.17609125905568124,
'on': 0.0,
'sat': 0.0,
'knees': 0.17609125905568124,
'bed': 0.17609125905568124,
'The': 0.0,
'my': 0.0,
'dog': 0.17609125905568124}
6. 计算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 )
pd.DataFrame( [tfidfA, tfidfB] )
基于协同过滤的推荐算法
-
基于内容(Content based,CB)主要利用的是用户评价过的物品的内容特征,而CF方法还可以利用其他用户评分过得物品内容
-
CF可以解决CB的一些局限:
- 物品内容不完全或者难以获得时,依然可以通过其他用户的反馈给出推荐
- CF基于用户之间物品的评价质量,避免了CB仅依赖内容可能造成的对物品质量的判断干扰
- CF推荐不受内容限制,只要其他类型用户给出了对不同的物品的兴趣,CF就可以给用户推荐出内容差异很大的物品(但有某种内在联系)
-
分为两类:基于近邻和基于模型
基于近邻的推荐
- 基于用户:
- 基于用户的协同过滤(User-CF):
- 基于物品:
- 基于物品的协同过滤(Item-CF):
- User-CF与Item-CF比较:
基于协同过滤的推荐优缺点
- 基于协同过滤的推荐机制的优点:
- 它不需要对物品或者用户进行严格的建模,而且不要求对物品特征的描述是机器可理解的,所以这种方式也是领域无关的
- 这种方法算出来的推荐是开放的,可以共用他人的经验,很好的支持用户发现潜在的兴趣偏好
- 存在的问题:
- 方法的核心是基于历史数据,所以对新物品和新用户都有“冷启动”的问题
- 推荐的效果依赖于用户历史偏好数据的多少和准确性
- 在大部分的实现中,用户历史偏好使用稀释矩阵进行存储的,而稀疏矩阵上的计算有些明显的问题,包括可能少部分人的错误偏好会对推荐的准确度有很大的影响等等
- 对一些特殊品味的用户不能给予很好的推荐
基于模型的协同过滤思想
-
基本思想:
- 用户具有一定的特征,决定着他的偏好选择;
- 物品具有一定的特征,影响着用户是否选择它;
- 用户之所以选某一个商品,是因为用户特征与物品特征相匹配;
-
基于这种思想,模型的建立相当于从行为数据总提取特征,给用户和物品同时打上“标签”;这和基于人口统计学的用户标签、基于内容方法的物品标签本质是一样的,都是特征提取和匹配
-
有显性特征时(比如用户标签,物品分类标签)我们可以直接匹配做出推荐;没有时,可以根据已有的偏好数据,去发掘出隐藏的特征,这需要用到隐语义模型(LFM)
-
基于模型的协同过滤推荐,就是基于样本的用户偏好信息,训练一个推荐模型,然后根据实时的用户喜好的信息进行预测新物品的得分,计算推荐
-
基于近邻的推荐和基于模型的推荐
- 基于近邻的推荐是在预测时直接使用已有的用户偏好数据,通过近邻数据来预测对新物品的偏好(类似分类)
- 而基于模型的方法,是要使用这些皮那好数据来训练模型,找到内在规律,再用模型来做预测(类似回归)
-
训练模型时,可以基于标签内容来提取物品特征,也可以让模型去发掘物品的潜在特征;这样的模型被称为隐语义模型(Latent Factor Model, LFM)
隐语义模型(LFM)
- 用隐语义模型来进行协同过滤的目标
- 揭示隐藏的特征,这些特征能够解释为什么给出对应的预测评分
- 这类特征可能是无法直接用语言解释描述的,事实上我们并不需要知道,类似“玄学“
- 通过矩阵分解进行降维分析
- 协同过滤算法非常依赖历史数据,而一般的推荐系统中,偏好数据又往往是稀疏的;这就需要对原始数据做降维处理
- 分解之后的矩阵,就代表了用户和物品的隐藏特征
- 隐语义模型的实例
- 基于概率的隐语义分析(pLSA )
- 隐式迪利克雷分布模型(LDA )
- 矩阵因子分解模型(基于奇异值分解的模型,SVD )
LFM降维方法——矩阵因子分解
LFM进一步理解
- 我们可以认为,用户之所以给电影打出这样的分数,是有内在原因的,我们可以挖掘出影响用户打分的隐藏因素,进而根据未评分电影与这些隐藏因素的关联度,决定此未评分电影的预测评分
- 应该有一些隐藏的因素,影响用户的打分,比如电影:演员、题材、年代…甚至不一定是人直接可以理解的隐藏因子
- 找到隐藏因子,可以对user和item进行关联(找到是由于什么使得user喜欢/不喜欢此item,什么会决定user喜欢/不喜欢此item),就可以推测用户是否会喜欢某一部未看完的电影
矩阵因子分解
模型求解——损失函数
模型求解算法——ALS
梯度下降算法
代码实现
1. 引入依赖
import numpy as np
import pandas as pd
2. 定义数据
# 评分矩阵R,6个用户对5个物品的评分
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],])
3. 算法实现
"""
@输入参数:
R:M*N 的评分矩阵
K:隐特征向量维度
max_iter: 最大迭代次数
alpha:步长
lamda:正则化系数
@输出:
分解之后的 P,Q
P:初始化用户特征矩阵M*K
Q:初始化物品特征矩阵N*K
"""
# 给定超参数
K = 2 # 隐特征向量维度
max_iter = 5000 # 最大迭代次数
alpha = 0.0002 # 迭代步长
lamda = 0.004 # 正则化系数
# 核心算法
def LFM_grad_desc( R, K=2, max_iter=1000, alpha=0.0001, lamda=0.002 ):
# 基本维度参数定义
M = len(R) # 行数
N = len(R[0]) # 列数
# P,Q初始值,随机生成 -> 随机梯度下降
P = np.random.rand(M, K) # 随机生成一个M*K的矩阵
Q = np.random.rand(N, K) # 随机生成一个N*K的矩阵
Q = Q.T # 将Q矩阵转置
# 开始迭代
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.0001:
break
return P, Q.T, cost
4. 测试
P, Q, cost = LFM_grad_desc(R, K, max_iter, alpha, lamda)
print(P)
print(Q)
print(cost)
predR = P.dot(Q.T)
print(R)
predR
[[ 1.42191162 1.03351644]
[ 1.72249312 0.62970942]
[ 1.65325684 -0.13902681]
[ 0.40523191 1.72699454]
[ 1.073343 1.77336266]
[ 1.46799251 0.7221439 ]]
[[ 0.89568365 2.63697244]
[ 1.24396992 0.68943122]
[ 1.41289734 -0.069854 ]
[ 2.3704516 1.24042426]
[ 0.34949845 0.46040278]]
2.305292572214812
[[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]]
array([[3.99893737, 2.4813538 , 1.9368199 , 4.65257155, 0.97278975],
[3.20333531, 2.57687096, 2.38971822, 4.8641934 , 0.89192864],
[1.11418525, 1.96075235, 2.34559377, 3.74651308, 0.51380237],
[4.91699659, 1.69474026, 0.4519136 , 3.10278854, 0.93674101],
[5.63768424, 2.55781799, 1.39264699, 4.74402969, 1.19159281],
[3.21913046, 2.32400708, 2.02367808, 4.37557001, 0.84553816]])