Lightfm学习记录

推荐参考资料

上面的引用资料都很有用!

一些使用的细节

对于用户和物品的特征设置权重

注意网上的文章大多都是未设置权重的,如(user id, [list of feature names]),但是从官方文档我们可以看到实际上是可以设置权重的(user id, {feature name: feature weight})
这个权重的意思就是某字段在全部字段中的占比,比如
在这里插入图片描述

打印稀疏矩阵

一般的交互矩阵都是稀疏矩阵,貌似没有很好的实现__str__,为了查看矩阵的值,参考下方

(interactions, weights) = dataset1.build_interactions([(x[0], x[1], x[2]) for x in df.values ])
interactions.todense()
'''
matrix([[1, 1, 0, 0],
       [0, 1, 1, 0],
       [1, 0, 1, 1]])
'''

矩阵分解

日常业务中可以得到用户的行为数据(交互),如点赞/评分等,如三元组{userID,itemID,rate},但是复杂业务中矩阵会很大,且矩阵十分稀疏(可能1w个物品,用户A只点赞了5个,我们在lightfm中使用的就是scipycoo_matrixcsr_matrix)。我们的推荐,实际上就是预测这些空白项的值。因此我们引入矩阵分解,将这个大矩阵分解为两个较小的矩阵以实现降维,如M x N分解为M x kk x N,即把他们投射到k维(这个k无法解释,此时就成为隐向量了)。此时两个小矩阵重新一乘,原来有的项会近似相等,原来的空白项此时也有值,那么这些值就是预测值了。
而对于这个分解过程,就有一些算法和目标函数了,我暂时还没搞懂
按照论文的说法,至少比MF模型CB要好

混合模型

由于协同过滤需要历史交互数据,存在冷启动问题;同时由于基于内容的推荐没有使用交互,用户之间是孤立的,所以实际效果不如协同过滤。所以提出了混合模型,结合了基于内容的推荐(CB)和协同过滤的基于模型推荐(CF协同过滤,MF矩阵分解)两种方式,训练者可以传入用户/物品的特征信息(如地理位置/年龄)等,同时也传入交互信息{userID,itemID,rate},那么在数据少时仍可以基于用户物品的特征进行适当的推荐
所以,如果没有传入用户物品的特征信息,那么模型只是一个单纯的MF模型,基于内容是通过embedding实现的
而embedding是通过矩阵分解?得到的,事实上M x k的矩阵元素都是embedding

论文解读(我是科研新人,如有不对欢迎指正)

模型的需求:1. 如果物品A和B经常同时被推荐,那么应该学到A和B非常相似 2. 模型能即时根据新数据进行更新
对于需求一,使用latent representation,根据交互信息确定两个物体的embedding的距离。
对于需求二,将用户和物体表示为内容特征的线性组合

对于predict,就是是对用户/物品的已分解出的特征矩阵进行点积,再加上偏置,得到一个值 r ^ u i \hat r_{ui} r^ui,这个就是MF模型的方法。但是这个 q u q_u qu p i p_i pi还有那两个偏置都是各特征之和(也算线性组合),所以也融入了一点CB的思想。
r ^ u i = f ( q u ⋅ p i + b u + b i ) \hat r_{ui} = f (q_u · p_i + b_u + b_i) r^ui=f(qupi+bu+bi)

目标函数是求最优化的分解出的矩阵,利用了交互信息,即对于交互信息(有正向和负向的交互),每个{user,item}对求出 r ^ u i \hat r_{ui} r^ui然后根据正负属性进行操作,累乘后再两部分相乘。
在这里插入图片描述
论文中提及的模型结构如下
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

我的demo

只是对于官方例子和参考其他人的例子改出来的demo,不知道是不是符合原作者的想法。
使用这个框架时,对于用户/物品的特征和交互量的量化,似乎也是一个关键,而所有例子都没有体现(包括我这个)。不过这本来也该是自己完成的。

import pandas as pd
import numpy as np
from scipy import sparse
from lightfm import LightFM

# 交互信息
interaction = [('u1', 'i1', 1), ('u1', 'i3', 2), ('u2', 'i2', 1), ('u2', 'i3', 3),
               ('u3', 'i1', 4), ('u3', 'i4', 5), ('u3', 'i2', 2)]
# 3user 4item 5种rate
interaction
'''
[('u1', 'i1', 1),
 ('u1', 'i3', 2),
 ('u2', 'i2', 1),
 ('u2', 'i3', 3),
 ('u3', 'i1', 4),
 ('u3', 'i4', 5),
 ('u3', 'i2', 2)]
'''
user_data = [('u1', {'f1': 5, 'f2': 2, 'f3': 1}),
 ('u2', {'f1': 0, 'f2': 1, 'f3': 3}),
 ('u3', {'f1': 4, 'f2': 3, 'f3': 3})]

users = set(map(lambda i:i[0],interaction))
items = set(map(lambda i:i[1],interaction))
user_features = ['f1','f2','f3']
print(users,items,user_features)
'''
{'u3', 'u2', 'u1'} {'i1', 'i2', 'i4', 'i3'} ['f1', 'f2', 'f3']
'''

from lightfm.data import Dataset
# we call fit to supply user id, item id and user/item features
dataset1 = Dataset()
# 把数据先丢进去,生成{值:内部id}的映射,方便后面提取出稀疏矩阵
dataset1.fit_partial(users=users,items=items,user_features=user_features)
dataset1.mapping()
'''
第一行是用户的编号(模型内部生成)
第二行是用户的特征及其编号,可以看到用户编号也作为特征
第三行第四行就是物品的编号和特征和特征编号
搞这么多编号也对应了这个模型适合分析离散的数据,虽然我没怎么懂
({'u3': 0, 'u2': 1, 'u1': 2},
 {'u3': 0, 'u2': 1, 'u1': 2, 'f1': 3, 'f2': 4, 'f3': 5},
 {'i1': 0, 'i2': 1, 'i4': 2, 'i3': 3},
 {'i1': 0, 'i2': 1, 'i4': 2, 'i3': 3})
'''

# 构造用户特征矩阵
uf = dataset1.build_user_features(user_data)
uf.todense()
'''
6列三行,对应3个用户的6个特征属性,
计算过程我们来看第二行,u2是('u2', {'f1': 0, 'f2': 1, 'f3': 3}),他的特征就是
{u1:0,u2:1,u3:0,f1:0,f2:1,f3:3},其他为0的就不看了,只剩{u2:1,f2:1,f3:3},
所以分母为5,分子为各值,就出来了0,2,   0,2,   0.6,这就是权重的作用,可以分配比例

matrix([[0.09090909, 0.        , 0.        , 0.36363637, 0.27272728,
         0.27272728],
        [0.        , 0.2       , 0.        , 0.        , 0.2       ,
         0.6       ],
        [0.        , 0.        , 0.11111111, 0.5555556 , 0.22222222,
         0.11111111]], dtype=float32)
'''

# 构造交互矩阵
(interactions, weights) = dataset1.build_interactions(interaction)
print(interactions.todense())
print(weights.todense())
# 注意这里的行数是用户数,列数是物品数,所以是交互
'''
[[1 1 1 0]
 [0 1 0 1]
 [1 0 0 1]]
[[4. 2. 5. 0.]
 [0. 1. 0. 3.]
 [1. 0. 0. 2.]]
'''

# windows只能用这个
model = LightFM(loss='logistic')
# 下面的参数在ubuntu上可运行,效果更好
# 参考 https://github.com/lyst/lightfm/issues/690
# model = LightFM(loss='warp')

model.fit(interactions,
      user_features= uf, 
      sample_weight= weights,
      epochs=10)
      
from lightfm.evaluation import auc_score
# 好像是测试一下
train_auc = auc_score(model,
                      interactions,
                      user_features=uf
                     ).mean()
print('Hybrid training set AUC: %s' % train_auc)
'''
好像效果不是很好
Hybrid training set AUC: 0.5
'''

user_id_map, user_feature_map, item_id_map, item_feature_map = dataset1.mapping()
# 预测现有的用户
# 注意输入的id必须要转换为lightfm内部的id
user_x = user_id_map['u3']
# 即对于该用户,看所有物品和他的匹配度
items_range = np.arange(len(items))
res = model.predict(user_x, items_range)
res = list(zip(items_range,res))
res = sorted(res, key=lambda x: x[1],reverse=True)
res
'''
注意下方拿到的是内部重新编号后的id及对应的分数,需要重新映射回原来的物品id
[(3, 0.5297617), (0, 0.51895887), (2, 0.4935133), (1, 0.46990803)]
'''
  • 12
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值