推荐系统实战(协同过滤算法与推荐知识补充)

协同过滤算法构建

构建流程

Data
Feature
ML Algorithem
Prediction

DATA:
显示数据:Rating、Comment
隐形数据:Page view、Search log、Click、car event
Feature:
用户–项目交互矩阵或评分矩阵:

用户\项目 项目1 项目2 项目3 项目4 项目5
用户1 0 1 0 0 1
用户2 0 0 0 0 1
用户3 1 0 0 1 1
用户4 0 1 0 0 0
用户5 0 0 0 0 0
用户6 0 1 1 0 1

训练模型:
依据应用场景调整相应算法
预测结果:
得出预测评分进行排序

协同过滤

算法思想:

物以类聚,人以群分
基本的协同过滤推荐算法基于以下假设:
“跟你喜好相似的人喜欢的东西你也很有可能喜欢” :基于用户的协同过滤推荐(User-based CF)
“跟你喜欢的东西相似的东西你也很有可能喜欢 ”:基于物品的协同过滤推荐(Item-based CF)

步骤:

1.找出最相似的人或物品:TOP-N相似的人或物品:
通过计算两两的相似度来进行排序,即可找出TOP-N相似的人或物品
2.根据相似的人或物品产生推荐结果:
利用TOP-N结果生成初始推荐结果,然后过滤掉用户已经有过记录的物品或明确表示不感兴趣的物品

示例:

相似度计算方法:

关于相似度计算这里先用一个简单的思想:如有两个同学X和Y,X同学爱好[足球、篮球、乒乓球],Y同学爱好[网球、足球、篮球、羽毛球],可见他们的共同爱好有2个,那么他们的相似度可以用:2/3 * 2/4 = 1/3 ≈ 0.33 来表示。
欧氏距离:
E ( p , q ) = ∑ i = 1 n ( p i − q i ) 2 E(p,q)=\sqrt{\sum_{i=1}^n{(p_i-q_i)^2}} E(p,q)=i=1n(piqi)2
欧氏距离的值非负, 最大值正无穷, 通常计算相似度的结果希望是[-1,1]或[0,1]之间,一般可以使用如下转化公式: 1 1 + E ( p , q ) \frac{1}{1+E(p,q)} 1+E(p,q)1

余弦相似度:
c o s ( θ ) = A ∗ B ∣ A ∣ ∣ B ∣ cos(\theta)=\frac{A*B}{\mid A\mid\mid B \mid} cos(θ)=ABAB
通过计算两个向量的夹角余弦值来评估他们的相似度,余弦相似度的特点, 与向量长度无关

皮尔逊相关系数:
P e a r s o n = p 与 q 的 协 方 差 p 的 标 准 差 ∗ q 的 标 准 差 = ∑ i = 1 n ( p i − p ‾ ) ∗ ( q i − q ‾ ) ∑ i = 1 n ( p i − p ‾ ) 2 ∑ i = 1 n ( q i − q ‾ ) 2 Pearson=\frac{p与q的协方差}{p的标准差*q的标准差}= \frac {\sum_{i=1}^n{(p_i-\overline{p})*(q_i-\overline{q})}}{\sqrt{\sum_{i=1}^n (p_i-\overline{p})^2}\sqrt{\sum_{i=1}^n (q_i-\overline{q})^2}} Pearson=pqpq=i=1n(pip)2 i=1n(qiq)2 i=1n(pip)(qiq)
实际上也是一种余弦相似度, 不过先对向量做了中心化, 向量a b 各自减去向量的均值后, 再计算余弦相似度;皮尔逊相关系数度量的是两个变量的变化趋势是否一致, 不适合计算布尔值向量之间的相关度;

杰卡德相似度:
J ( A , B ) = ∣ A ∩ B ∣ ∣ A ∪ B ∣ J(A,B)= \frac{\mid A \cap B \mid}{\mid A\cup B \mid} J(A,B)=ABAB
两个集合的交集元素个数在并集中所占的比例, 非常适用于布尔向量表示
推荐系统实战(协同过滤算法与推荐知识补充)_第1张图片

余弦相似度适合用户评分数据(实数值), 杰卡德相似度适用于隐式反馈数据(0,1布尔值)(是否收藏,是否点击,是否加购物车)
推荐系统实战(协同过滤算法与推荐知识补充)_第2张图片

代码:

UsersCF.py


# 步骤如下:
#1.数据集
#2.用户之间相似度   -->datafarme
#3.用户topN相似用户 -->字典
#4.生成推荐结果(1.每个用户的相似用户 2.相似用户的消费项目 3.过滤用户已消费项目)
import pandas as pd
import numpy as np
from pprint import pprint
#Perpare dataset
users = ['User1','User2','User3','User4','User5']
items = ['Item A','Item B','Item C','Item D','Item E']
datasets = [
    [1,0,1,1,0],
    [1,0,0,1,1],
    [1,0,1,0,0],
    [0,1,0,1,1],
    [1,1,1,0,1],
]
#DataFrame 是一个表格型的数据结构
df = pd.DataFrame(datasets,				
                  columns=items,
                  index=users)
print(df)

# Caculate user similarity
from sklearn.metrics.pairwise import pairwise_distances
user_similar = 1 - pairwise_distances(df.values,metric="jaccard")
user_similar = pd.DataFrame(user_similar,columns=users,index=users)
print("用户两两之间相似度")
print(user_similar)

#Top_N similar users
topN_users = {}
for i in user_similar.index:
    _df = user_similar.loc[i].drop([i])     #loc[columns:index] drop是去除
    _df_sorted = _df.sort_values(ascending=False)   #排列
    top2 = list(_df_sorted.index[:2])
    topN_users[i] = top2
print("Top2相似用户:")
pprint(topN_users)

#Generate recommendation results
rs_results = {}
for user,sim_users in topN_users.items(): #1.每个user的相似用户
    rs_result = set()                     #set()创建无序不重复元素集
    for sim_user in sim_users:            #2.相似用户消费过的项目
        # 构建初始的推荐结果
        dfsimuser_row=df.loc[sim_user]                                 #找到sim_users中的其中一个sim_user的一行消费记录, df是消费记录
        dfsimuser_row[dfsimuser_row==0] = np.nan                       #把他的记录中的0替换成nan缺失值
        rs_result = rs_result.union(set(dfsimuser_row.dropna().index)) #再把nan删除,返回对应的索引  union合并,无重复
    # 过滤掉已经购买过的物品
    dfuser_row = df.loc[user]
    dfuser_row[dfuser_row==0] = np.nan
    rs_result -= set(dfuser_row.dropna().index)
    rs_results[user] = rs_result
print("最终推荐结果:")
pprint(rs_results)

运行结果:
用户两两之间相似度:
          User1  User2     User3  User4  User5
User1  1.000000   0.50  0.666667    0.2    0.4
User2  0.500000   1.00  0.250000    0.5    0.4
User3  0.666667   0.25  1.000000    0.0    0.5
User4  0.200000   0.50  0.000000    1.0    0.4
User5  0.400000   0.40  0.500000    0.4    1.0
Top2相似用户:
{'User1': ['User3', 'User2'],
 'User2': ['User1', 'User4'],
 'User3': ['User1', 'User5'],
 'User4': ['User2', 'User5'],
 'User5': ['User3', 'User1']}
最终推荐结果:
{'User1': {'Item E'},
 'User2': {'Item C', 'Item B'},
 'User3': {'Item D', 'Item E', 'Item B'},
 'User4': {'Item A', 'Item C'},
 'User5': {'Item D'}}

ItemsCF.py

#1.数据集
#2.项目之间相似度   -->datafarme
#3.项目topN相似项目 -->字典
#4.生成推荐结果(1.每个用户的消费项目 2.消费项目的相似项目 3.过滤用户已消费项目)
from operator import index
import pandas as pd
import numpy as np
from pprint import pprint

#构造数据集
users = ['User1','User2','User3','User4','User5']
items = ['Item A','Item B','Item C','Item D','Item E']
#用户购买记录数据集
datasets = [
    [1,0,1,1,0],
    [1,0,0,1,1],
    [1,0,1,0,0],
    [0,1,0,1,1],
    [1,1,1,0,1],
]
df = pd.DataFrame(datasets,
                  columns=items,
                  index=users)

#计算项目之间相似度
from sklearn.metrics.pairwise import pairwise_distances
item_similar = 1 - pairwise_distances(df.T.values,metric="jaccard")
item_similar = pd.DataFrame(item_similar,columns=items,index=items)
print("物品两两之间相似度")
print(item_similar)

#生成topN相似的项目
topN_items = {}
for i in item_similar.index:
    #取每一列去除自己,并排序
    _df = item_similar.loc[i].drop([i])
    _df_sorted = _df.sort_values(ascending=False)
    top2 = list(_df_sorted.index[:2])
    topN_items[i] = top2
print("Top2相似物品:")
pprint(topN_items)

#生成推荐结果
rs_results = {}
for user in df.index:   #1.每个user消费过的项目 
    rs_result = set()
    items_result = set()
    dfitem = df.loc[user]
    dfitem[dfitem == 0] = np.nan
    items_result = items_result.union(set(dfitem.dropna().index))
    for item in items_result:#2.每个项目的相似项目之和
        rs_result = rs_result.union(topN_items[item])
    #3.过滤掉用户消费过的项目
    dfuser_row = df.loc[user]
    dfuser_row[dfuser_row==0] = np.nan
    rs_result -= set(dfuser_row.dropna().index)
    rs_results[user] = rs_result
print("最终推荐结果:")
pprint(rs_results)

运行结果:
物品两两之间相似度
        Item A    Item B  Item C  Item D    Item E
Item A    1.00  0.200000    0.75    0.40  0.400000
Item B    0.20  1.000000    0.25    0.25  0.666667
Item C    0.75  0.250000    1.00    0.20  0.200000
Item D    0.40  0.250000    0.20    1.00  0.500000
Item E    0.40  0.666667    0.20    0.50  1.000000
Top2相似物品:
{'Item A': ['Item C', 'Item D'],
 'Item B': ['Item E', 'Item C'],
 'Item C': ['Item A', 'Item B'],
 'Item D': ['Item E', 'Item A'],
 'Item E': ['Item B', 'Item D']}
最终推荐结果:
{'User1': {'Item E', 'Item B'},
 'User2': {'Item C', 'Item B'},
 'User3': {'Item B', 'Item D'},
 'User4': {'Item C', 'Item A'},
 'User5': {'Item D'}}

ScoreCF.py
电影评分预测案例

from logging import exception
import os
import pandas as pd
import numpy as np
#1.加载数据集
#2.计算用户相似度
#3.根据相似度评分预测
#4.推荐结果
DATA_PATH = 'D:/p/ml-latest-small/ratings.csv'
CACHE_DIR = 'D:/p/cache/'

def load_data(data_path):
    
    """
        加载数据
        Param: data_path:数据集路径
            cache_path:数据集缓存路径
        Return:用户——物品评分矩阵
    """
    #数据集缓存地址
    cache_path = os.path.join(CACHE_DIR,'rating_matrix.cache')  #os.path.join()函数用于路径拼接文件路径

    print('开始加载数据集.')
    if os.path.exists(cache_path):                              
        print('加载缓存中...')
        ratings_matrix = pd.read_pickle(cache_path)             #pd.read_pickle()函数用于从文件中加载 pickle的 Pandas对象(或任何对象)
        print('从缓存加载数据集完毕.')#什么是pickle? pickle可以将对象以文件的形式存放在磁盘上,python中几乎所有的数据类型(列表,字典,集合,类等)都可以用pickle来序列化。
    else:
        print('加载新数据中...')
        #设置要加载的数据字段的类型
        dtype = {'userId':np.int32,'movieId':np.int32,'rating':np.float32}
        #加载数据,我们只利用前三列数据
        ratings = pd.read_csv(data_path,dtype=dtype,usecols=range(3))
        #透视表,将其转换成User-movie评分矩阵
        ratings_matrix = ratings.pivot_table(index=['userId'],columns=['movieId'],values=['rating'])
        #存入缓存文件
        ratings_matrix.to_pickle(cache_path)
        # print(ratings_matrix)
        print('数据集加载完毕')
    return ratings_matrix

def computer_pearson_similarity(ratings_matrix,based='user'):
    """
    计算相似度
    Param:rating_matrix:评分矩阵
          based:user or item
    Return:相似度矩阵
    """
    user_similarity_cache_path = os.path.join(CACHE_DIR,'user_similarity.cache')
    item_similarity_cache_path = os.path.join(CACHE_DIR,'item_similarity.cache')
    #基于皮尔逊相关系数计算相似度
    #用户相似度
    if based == 'user':
        if os.path.exists(user_similarity_cache_path):
            print('正从缓存加载用户相似度矩阵')
            similarity = pd.read_pickle(user_similarity_cache_path)
        else:
            print('开始计算用户相似度矩阵')
            similarity = ratings_matrix.T.corr()
            similarity.to_pickle(user_similarity_cache_path)
    elif based == 'item':
        if os.path.exists(item_similarity_cache_path):
            print('正从缓存加载物品相似度矩阵')
            similarity = pd.read_pickle(item_similarity_cache_path)
        else:
            print('开始计算物品相似度矩阵')
            similarity = ratings_matrix.corr()
            similarity.to_pickle(item_similarity_cache_path)
    else:
        raise Exception("Unhandled 'based' value: %s" %based)
    print('相似度矩阵加载完毕')
    return similarity

def predict(uid,iid,ratings_matrix,user_similar):
    """
    预测给定用户规定物品的评分
    Param:uid:用户id
          iid:项目id
          rating_matrix:评分矩阵
          user_similar:用户相似度矩阵
    """
    print("开始预测用户<%s>对电影<%s>的评分"%(uid,iid))
    #1.找出uid的相似用户
    similar_users = user_similar[uid].drop([uid]).dropna() #去掉自己并且去掉缺失值
    #相似用户筛选规则:正相关的用户
    similar_users = similar_users.where(similar_users>0).dropna()
    if similar_users.empty is True:
        raise Exception("用户<%d>没有相似的用户" % uid)
    # print(similar_users)
    #2.从uid用户的正相关的用户中筛选出对iid物品有评分记录的近邻用户
    ids = set(ratings_matrix.iloc[:,iid-1].dropna().index)&set(similar_users.index) #ratings_matrix[iid]拿的是列
    # print(ids)
    finally_similar_users = similar_users.loc[list(ids)]
    # print(finally_similar_users)

    #3.结合uid用户的相似用户的相似度预测uid对iid物品的评分
    sum_up = 0          #分子
    sum_down = 0        #分母
    for sim_uid,similarity in finally_similar_users.iteritems():
        sim_user_rated_movies = ratings_matrix.loc[sim_uid].dropna()
        sim_user_rating_for_item = sim_user_rated_movies[uid]
        sum_up += similarity*sim_user_rating_for_item
        sum_down += similarity
    predict_rating = sum_up/(sum_down+0.01)
    return predict_rating

def predict_all(uid,ratings_matrix,user_similar):
    """
    预测全部评分
    """
    item_ids = ratings_matrix.loc[uid].dropna().index
    for iid in item_ids:
            rating = predict(uid,iid,ratings_matrix,user_similar)
            print(rating)




if __name__ == '__main__' :
    Ratings=[]
    ratings_matrix = load_data(DATA_PATH)
    # print(ratings_matrix)
    user_similar = computer_pearson_similarity(ratings_matrix,based='user')
    # print(user_similar)
    item_similar = computer_pearson_similarity(ratings_matrix,based='item')
    # print(item_similar)
    # pre_score = predict(1,2,ratings_matrix,user_similar)
    # print(pre_score)
    # item_ids = ratings_matrix.loc[1].dropna().index
    # print(item_ids)
    # predict_all(1,ratings_matrix,user_similar)
    # print('top-n')
    item_ids = ratings_matrix.loc[1].dropna().index
    # print(item_ids)
    for iid in item_ids:
            rating = predict(1,iid[1],ratings_matrix,user_similar)
            print(rating)
            Ratings.append(rating)
    print(Ratings)
    top_N = Ratings.sort()
    top_10 = top_N
    print(top_N)

推荐知识补充

冷启动问题

推荐系统冷启动概念

⽤户冷启动:如何为新⽤户做个性化推荐
物品冷启动:如何将新物品推荐给⽤户(协同过滤)
系统冷启动:⽤户冷启动+物品冷启动
本质是推荐系统依赖历史数据,没有历史数据⽆法预测⽤户偏好

用户冷启动

1.收集⽤户特征
⽤户注册信息:性别、年龄、地域
设备信息:定位、⼿机型号、app列表
社交信息、推⼴素材、安装来源
推荐系统实战(协同过滤算法与推荐知识补充)_第3张图片

2.引导用户填写兴趣
推荐系统实战(协同过滤算法与推荐知识补充)_第4张图片
3.使用其它站点的行为数据
4. 新老用户推荐策略的差异
新⽤户在冷启动阶段更倾向于热门排⾏榜,⽼⽤户会更加需要长尾推荐
Explore Exploit⼒度
使⽤单独的特征和模型预估

物品冷启动

给物品打标签
利用物品的内容信息,将新物品先投放给曾经喜欢过和它内容相似的其他物品的用户。
推荐系统实战(协同过滤算法与推荐知识补充)_第5张图片

系统冷启动

推荐系统评估

常用指标:
• 准确性 • 信任度
• 满意度 • 实时性
• 覆盖率 • 鲁棒性
• 多样性 • 可扩展性
• 新颖性 • 商业⽬标
• 惊喜度 • ⽤户留存
准确性:
评分预测:RMSE、MAE
TopN推荐:召回率、精准率
覆盖度:
信息熵 对于推荐越大越好
覆盖率
多样性&新颖性&惊喜性:
多样性:推荐列表中两两物品的不相似性。(相似性如何度量?
新颖性:未曾关注的类别、作者;推荐结果的平均流⾏度
惊喜性:历史不相似(惊)但很满意(喜)
往往需要牺牲准确性
使⽤历史⾏为预测⽤户对某个物品的喜爱程度
Exploitation & Exploration 问题:
Exploitation(开发 利用):选择现在可能最佳的⽅案
Exploration(探测 搜索):选择现在不确定的⼀些⽅案,但未来可能会有⾼收益的⽅案
在做两类决策的过程中,不断更新对所有决策的不确定性的认知,优化长期的⽬标
EE问题实践
兴趣扩展: 相似话题, 搭配推荐
人群算法: userCF 用户聚类
平衡个性化推荐和热门推荐比例
随机丢弃用户行为历史
随机扰动模型参数
EE可能带来的问题
探索伤害用户体验, 可能导致用户流失
探索带来的长期收益(留存率)评估周期长, KPI压力大
如何平衡实时兴趣和长期兴趣
如何平衡短期产品体验和长期系统生态
如何平衡大众口味和小众需求

你可能感兴趣的:(推荐系统实战,算法,人工智能,推荐算法)