传统的协同过滤推荐系统一般基于是用户对商品的评分(如5星好评),这些评分通常可以明确表达用户对商品的喜好,通过分析用户评分,协同过滤推荐算法可以为用户推荐他们可能喜好的其他商品。但是要收集用户评分有时候会非常的困难,大多数情况是我们无法收集到用户的评分,那如果没有用户评分我们还能开发推荐系统吗?其实也是可以的,除了“显示”的用户评分,还有一种“隐式”的用户对物品喜好的表达方式,如用户的点击,点赞,浏览,收藏,评论等行为,隐式的表达方式虽然没有显示的表达方式那样直接表达用户的喜好,但它也可以间接反映出用户对物品的喜好程度。今天我们就来尝试利用用户隐式反馈数据,如点击,点赞,浏览,收藏,评论等行为数据,以及使用python和implicit库来开发一个个性化的推荐系统。
我们的数据来自于kaggle,你可以在这里下载,这里包含了两个数据文件(shared_articles.csv,users_interactions.csv),请大家将它们下载下来,这些数据来自于Deskdrop是 CI&T开发的内部通信平台,该平台还允许公司员工与同行分享相关文章,并围绕他们进行协作。数据包中含大约73,000个用户在平台上共享的3k多篇公共文章上的交互信息,更重要的是,它包含丰富的隐式反馈信息,记录了不同的交互类型,我们可以利用这些信息来推断用户对文章的兴趣程度。
同时我们将会使用implicit库,它是基于隐式偏好数据的python快速协同过滤推荐算法,它主要通过矩阵分解的方法来预测用户对物品的评分,从而进行个性化的推荐。
我们首先加载数据集:
import pandas as pd
import scipy.sparse as sparse
import numpy as np
import random
import implicit
from sklearn.preprocessing import MinMaxScaler
from sklearn import metrics
articles_df = pd.read_csv('./data/shared_articles.csv')
interactions_df = pd.read_csv('./data/users_interactions.csv')
articles_df.drop(['authorUserAgent', 'authorRegion', 'authorCountry'], axis=1, inplace=True)
interactions_df.drop(['userAgent', 'userRegion', 'userCountry'], axis=1, inplace=True)
articles_df.head()
接下来我们要对shared_articles.csv和users_interactions.csv中的数据进行整理,并删除一些不需要的列,并将两个表进行合并
articles_df = articles_df[articles_df['eventType'] == 'CONTENT SHARED']
articles_df.drop('eventType', axis=1, inplace=True)
df = pd.merge(interactions_df[['contentId','personId', 'eventType']], articles_df[['contentId', 'title']], how = 'inner', on = 'contentId')
df.head()
"eventType"字段记录了用户行为的类别,因此我们需要查看一下用户行为的分类情况:
我们看到存在以下5种用户交互行为分类:
VIEW: 查看,表示用户点击过该篇文章LIKE: 点赞,表示用户喜好该篇文章。BOOKMARK: 加入书签,用户已将该文章加入书签,以便将来查看。这是用户对该文章表现出感兴趣的迹象COMMENT CREATED: 创建评论,说明用户被这篇文章所吸引,同时希望表达自己的观点。FOLLOW: 关注,用户关注了文章的作者,这应该说明用户对这位作者的文章非常感兴趣我们可以认为上述的5种用户行为可以表达出用户对文章的不同的喜好程度,为此我们可以为这5种用户行为各打上一个分值,以便量化用户的喜好程度:
event_type_strength = {
'VIEW': 1.0,
'LIKE': 2.0,
'BOOKMARK': 3.0,
'FOLLOW': 4.0,
'COMMENT CREATED': 5.0,
}
df['eventStrength'] = df['eventType'].apply(lambda x: event_type_strength[x])
df.sample(10)
我们给数据增加了一列(eventStrength),用以表示用户行为所对应的分值。
接下来我们删除重复数据,并对数据进行分组合计
df = df.drop_duplicates()
grouped_df = df.groupby(['personId', 'contentId', 'title']).sum().reset_index()
grouped_df.sample(10)
接下来我们查看分组数据的数据类型:
personId和contentId都是int64的长整型字段,为了避免出现负整数警告信息以及为了节省内存空间,我们可以将他们转换成int16的短整型字段:
grouped_df['title'] = grouped_df['title'].astype("category")
grouped_df['personId'] = grouped_df['personId'].astype("category")
grouped_df['contentId'] = grouped_df['contentId'].astype("category")
grouped_df['person_id'] = grouped_df['personId'].cat.codes
grouped_df['content_id'] = grouped_df['contentId'].cat.codes
grouped_df.sample(10)
这样我们就可以不再需要personId和contentId这两个字段了。
在矩阵分解(matrix factorization)中使用的一种算法。有一个稀疏矩阵,假设这个矩阵是低阶的,可以分解成两个小矩阵相乘。然后交替对两个小矩阵使用最小二乘法,算出这两个小矩阵,就可以估算出稀疏矩阵缺失的值:
假设用户评分矩阵可以分解成两个矩阵相乘的形式,这两个矩阵相乘的结果就可以近似等于原来的矩阵,为了寻找出最优的两个矩阵以便它们相乘以后的结果与原来的矩阵误差最小,我们需要使用交替最小二乘法,算法在迭代的过程中交替固定两个矩阵中的一个矩阵然后使用最小二乘法求出另外一个矩阵的最优矩阵,最终使两个矩阵都是最优矩阵并且它们乘积的结果与原来的矩阵误差最小。
在这里我们将使用implicit库中的als.AlternatingLeastSquares方法来实现交替最小二乘法。不过,这里我们首先要创建两个稀疏矩阵:内容-人-评分矩阵,人-内容-评分矩阵。
sparse_content_person = sparse.csr_matrix((grouped_df['eventStrength'].astype(float), (grouped_df['content_id'], grouped_df['person_id'])))
sparse_person_content = sparse.csr_matrix((grouped_df['eventStrength'].astype(float), (grouped_df['person_id'], grouped_df['content_id'])))
print(sparse_content_person.shape)
print(sparse_person_content.shape)
我们看到文章总数为2979,用户总数为1895
内容-人-评分矩阵(sparse_content_person)的每一行代表一篇文章,每一列代表一个用户。
人-内容-评分矩阵(sparse_person_content)的每一行代表一个用户,每一列代表一篇文章。
下面我们开始训练模型,并设置20个特征因子。
alpha = 15
data = (sparse_content_person * alpha).astype('double')
model = implicit.als.AlternatingLeastSquares(factors=20, regularization=0.1, iterations=50)
model.fit(data)
模型训练完成以后,我们可以计算文章之间的相似度,并根据相似度来进行推荐:我们以content_id = 235的文章为例,这篇文章的标题是"Artificial intelligence is hard to see",这是一篇有关人工智能的文章,我们的目标是在所有的文章的标题中找出和它最相似的10篇文章标题。这里我们主要通过计算文章之间的相似度,在计算相似度的过程中有以下几个步骤:
从模型中获取人和内容的向量计算内容的向量的范数计算相似度得分获取相似度得分最大的10篇文章生成这10篇文章的content_id和title的元组person_vecs = model.user_factors
content_vecs = model.item_factors
content_norms = np.sqrt((content_vecs * content_vecs).sum(axis=1))
scores = content_vecs.dot(content_vecs[content_id]) / content_norm
top_idx = np.argpartition(scores, -n_similar)[-n_similar:]
similar = sorted(zip(top_idx, scores[top_idx] / content_norms[content_id]), key=lambda x: -x[1])
print(person_vecs.shape)
print(content_vecs.shape)
我们注意到我们的用户矩阵和我们的内容矩阵都只有20列,这是因为我们在定义模型时设置了factors=20。这里的用户矩阵和内容矩阵就类似与前面交替最小二乘法示意图中介绍的User Feature Matrix和Movie Feature Matrix.
下面我们展示这10篇最相似的文章title:
for content in similar:
idx, score = content
print(grouped_df.title.loc[grouped_df.content_id == idx].iloc[0],"|",score)
这些文章似乎都和人工智能有关。其中第一篇文章就是content_id = 235的文章本身,正如我们前面说content_id = 235要和所有文章计算相似度,这当然也包含它自己本身,所以content_id = 235的文章和自己的相似度应该是最大的,因此应该排第一位,其余9篇文章标题则是按照相似度由高到低排列的。
接下来我们要为用户推荐他们没有看过的(即没有发生过交互行为),但是他们可能会敢兴趣的文章。我们首先要定义一个推荐函数,在推荐函数中我们主要要做以下3件事:
将指定用户的用户向量乘以内容矩阵,得到该用户对所有文章的评分向量从评分向量中过滤掉用户已经评分过的文章(将其评分值置为0),因为用户已经发生过交互行为的文章不应该被推荐将余下的评分分数排序,并输出分数最大的10篇文章。def recommend(person_id, sparse_person_content, person_vecs, content_vecs, num_contents=10):
rec_vector = person_vecs[person_id,:].dot(content_vecs.T).toarray()
person_interactions = sparse_person_content[person_id,:].toarray()
person_interactions = person_interactions.reshape(-1) + 1
person_interactions[person_interactions > 1] = 0
min_max = MinMaxScaler()
rec_vector_scaled = min_max.fit_transform(rec_vector.reshape(-1,1))[:,0]
recommend_vector = person_interactions * rec_vector_scaled
content_idx = np.argsort(recommend_vector)[::-1][:num_contents]
titles = []
scores = []
for idx in content_idx:
titles.append(grouped_df.title.loc[grouped_df.content_id == idx].iloc[0])
scores.append(recommend_vector[idx])
recommendations = pd.DataFrame({'title': titles, 'score': scores})
return recommendations
'在上面的函数中,我们首先从稀疏矩阵sparse_person_content中获取指定用户对所有文章的评分,这里需要注意一点的是,如果某个用户对某篇文章没有发生过交互行为(VIEW,LIKE,BOOKMARK,COMMENT CREATED,FOLLOW),那么在原始的数据集中是不存在这条交互记录的,但是当我们使用了稀疏矩阵的.toarray()方法后,那些用户没有发生过交互的所有文章都会被展示出来,只不过对那些文章的评分值都会被置为0,因此toarray()方法展现的是所有用户对所有文章的评分结果。
下面我们要为特定的用户推荐他们没有看过的,但可能会感兴趣的10篇文章:
person_vecs = sparse.csr_matrix(model.user_factors)
content_vecs = sparse.csr_matrix(model.item_factors)
person_id = 50
recommendations = recommend(person_id, sparse_person_content, person_vecs, content_vecs)
print(recommendations)
在实际应用中要检验推荐系统的表现,最重要的还是要看推荐系统是否为企业带来了更多的利润,或者是否为网站带来了更多的流量等商业价值。
在这里我们只能通过理论的方式来检验推荐系统的有效性,大家可以参考这篇文章,也可以参考我之前写的一篇关于评估推荐系统表现的博客,还有这篇关于ROC和AUC的博客。
大体上来说评估推荐系统的表现主要是通过计算推荐结果的“命中率”来考察推荐算法表现,主要思想是这样的,我们从现有的评分矩阵中分离出少部分评分数据(如20%左右),将剩余的80%的推荐数据用来训练推荐算法模型,然后让推荐模型对用户未评分过的文章进行推荐,在推荐结果中我们考察其中是否包含了之前被分离出来的那20%的文章,同时我们计算“命中率”或AUC作为评价指标。
现在我们要做的是从评分数据中创建训练集和测试集,在测试集中我们删除了20%的有过交互行为的评分数据,在测试集中我们将所有的有过交互行为评分置为1,这样就测试集变成了一个二分类数据的集合。
import random
def make_train(ratings, pct_test = 0.2):
test_set = ratings.copy()
test_set[test_set != 0] = 1
training_set = ratings.copy()
nonzero_inds = training_set.nonzero()
nonzero_pairs = list(zip(nonzero_inds[0], nonzero_inds[1]))
random.seed(0)
num_samples = int(np.ceil(pct_test*len(nonzero_pairs)))
samples = random.sample(nonzero_pairs, num_samples)
content_inds = [index[0] for index in samples]
person_inds = [index[1] for index in samples]
training_set[content_inds, person_inds] = 0
training_set.eliminate_zeros()
return training_set, test_set, list(set(person_inds))
content_train, content_test, content_persons_altered = make_train(sparse_content_person, pct_test = 0.2)
接下来我们定义一个计算ACU分数的函数,不过我们首先需要计算fpr和tpr:
def auc_score(predictions, actual):
fpr, tpr, thresholds = metrics.roc_curve(actual, predictions)
return metrics.auc(fpr, tpr)
'接下来我们要计算用户预测评分的平均AUC,和最受欢迎文章的平均AUC :
def calc_mean_auc(training_set, altered_persons, predictions, test_set):
store_auc = []
popularity_auc = []
pop_contents = np.array(test_set.sum(axis = 1)).reshape(-1)
content_vecs = predictions[1]
for person in altered_persons:
training_column = training_set[:,person].toarray().reshape(-1)
zero_inds = np.where(training_column == 0)
person_vec = predictions[0][person,:]
pred = person_vec.dot(content_vecs).toarray()[0,zero_inds].reshape(-1)
actual = test_set[:,person].toarray()[zero_inds,0].reshape(-1)
pop = pop_contents[zero_inds]
store_auc.append(auc_score(pred, actual))
popularity_auc.append(auc_score(pop, actual))
return float('%.3f'%np.mean(store_auc)), float('%.3f'%np.mean(popularity_auc))
calc_mean_auc(content_train, content_persons_altered,
[person_vecs, content_vecs.T], content_test)
今天我们学习了如何利用隐式反馈数据(如点击,点赞,浏览,收藏,评论)来实现一个个性化的协同过滤推荐系统,同时我们也学会了如何评估推荐系统,虽然评估推荐系统的方法有点复杂,但是如果你多读几篇代码,我觉得还是能够理解其中的思想。
https://github.com/tongzm/ml-python/blob/master/%E5%9F%BA%E4%BA%8E%E9%9A%90%E5%BC%8F%E5%81%8F%E5%A5%BD%E7%9A%84%E6%8E%A8%E8%8D%90%E7%B3%BB%E7%BB%9F.ipynb
如想交流数据科学请加本人微信:相关知识
使用python实战开发基于隐式反馈(点击,点赞,浏览,收藏,评论)的协同过滤的推荐系统(implicit库)
基于JAVA协同过滤算法网上宠物用品推荐购物商城系统设计与实现(Springboot框架)可行性分析
个性化宠物护理推荐系统
Python基于大数据技术的宠物商品信息比价及推荐系统
基于微信小程序的宠物小程序系统(源码+文档+部署+讲解)
基于协同过滤的宠物用品在线购物系统的设计与实现
计算机毕业设计ssm基于协同过滤算法的竞赛管理系统ht5jj系统+程序+源码+lw+远程部署
【2024】基于springboot的宠物领养管理系统设计与实现研究思路
python计算机毕设【附源码】宠物寄养系统(django+mysql+论文)
【2024】基于springboot的宠物领养管理系统设计与实现
网址: 使用python实战开发基于隐式反馈(点击,点赞,浏览,收藏,评论)的协同过滤的推荐系统(implicit库) https://m.mcbbbk.com/newsview429576.html
上一篇: Jitter click tes |
下一篇: 如何点击者训练你的狗 |