Lightfm学习记录

urlyy

推荐参考资料

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

一些使用的细节

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

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

打印稀疏矩阵

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

1
2
3
4
5
6
7
(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,就是是对用户/物品的已分解出的特征矩阵进行点积,再加上偏置,得到一个值$\hat r_{ui}$,这个就是MF模型的方法。但是这个$q_u$和$p_i$还有那两个偏置都是各特征之和(也算线性组合),所以也融入了一点CB的思想。
$$
\hat r_{ui} = f (q_u · p_i + b_u + b_i)
$$

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

我的demo

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
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)]
'''
  • 标题: Lightfm学习记录
  • 作者: urlyy
  • 创建于 : 2024-04-05 16:00:38
  • 更新于 : 2024-10-16 14:43:06
  • 链接: https://urlyy.github.io/2024/04/05/Lightfm学习记录/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论