0%

推荐系统笔记

在最深的黑暗中,有野兽戴着从你的记忆中偷来的脸孔,拨动着你的心弦。了解自己,保持坚强。

师兄的毕设,到时候扔给我维护了。大概是一个医院的项目,给病人微信公众号推送一些医学文章啥的 上网十几年最讨厌的东西大概就是微信和推荐算法了 我能怎么办啊,接吧,这骗钱项目应该也结题就不用管了吧

主要算法

协同过滤算法

最老最经典的一个算法了,大概是给用户推荐兴趣和关注点相似用户的感兴趣内容。

协同过滤有基于领域的方法(neighborhood methods)和隐语义模型(latent factor models),基于图的随机游走算法(random walk on graph) 下面两个是基于邻域的方法:

基于物品的协同过滤

(item-based collaborative filtering,ItemCF) 找到物品之间的属性关联,向用户推荐属性相似的物品 设N(i)是喜欢i物品的用户数 相似度:

避免热门物品干扰,改为

基于用户的协同过滤

(User-based collaborative filtering,UserCF) 找到和用户相似的其他用户,将其他用户感兴趣的产品推荐给用户。还有一种是“购买过XXX等用户也XXX”,算是这个方法的一个变种

计算相似度,N(u)是用户正反馈的集合,N(v)是用户负反馈物品的集合 Jaccard相似度:

余弦相似度:

冷启动问题

等待填坑,利用用户注册信息,人工标注等

大众行为/个性化推荐

等待填坑

imdb电影评论推荐例子

这里使用movielens数据集, 数据集的rating.csv包括用户id,物品id,评分,评论时间。(这个csv文件第一行是userid,movieid,·····,把第一行直接删掉或者从第二行开始读取,不然会报TypeError: unsupported operand type(s) for -: 'str' and 'int'的错) 使用的算法是SVD矩阵分解。构建一个用户-物品矩阵,每一项的值是评分,这是一个极其稀疏的矩阵 我们导入数据集并分割:

1
2
3
4
5
6
7
8
header = ['user_id', 'item_id', 'rating', 'timestamp']  # 用户id,物品id,评分,评论时间

df = pd.read_csv('../data/u.data', sep='\t', names=header)
n_users = df.user_id.unique().shape[0] #用户数量
n_items = df.item_id.unique().shape[0] #物品数量
print('Number of users = ' + str(n_users) + ' | Number of movies = ' + str(n_items))
# 数据集分割——训练集:测试集 = 3:1
train_data,test_data = train_test_split(df, test_size = 0.25)

建立评分矩阵

1
2
3
4
5
6
7
8
9
train_data_matrix = np.zeros((n_users, n_items))
test_data_matrix = np.zeros((n_users, n_items))
#使用 pandas 遍历行数据
for line in train_data.itertuples():
#训练集评分矩阵
train_data_matrix[line[1]-1, line[2]-1] = line[3]
for line in test_data.itertuples():
#测试集评分矩阵
test_data_matrix[line[1]-1, line[2]-1] = line[3]

计算余弦相似度:

1
2
user_similarity = pairwise_distances(train_data_matrix, metric = "cosine")  # 计算余弦距离
item_similarity = pairwise_distances(train_data_matrix.T, metric = "cosine")

基于用户和基于物品的协同过滤

1
2
3
4
5
6
7
8
9
10
11
12
13
def predict(rating, similarity, type = 'user'):
if type == 'user':
#
mean_user_rating = rating.mean(axis = 1) #mean函数:压缩列,对各行求均值,返回 m *1 矩阵
# print(mean_user_rating)
rating_diff = (rating - mean_user_rating[:,np.newaxis])
pred = mean_user_rating[:,np.newaxis] + similarity.dot(rating_diff) / np.array([np.abs(similarity).sum(axis=1)]).T
#dot函数:矩阵相乘;np.abs():矩阵元素的绝对值 .T:转置
# print('test',pred.min())
elif type == 'item':
pred = rating.dot(similarity) / np.array([np.abs(similarity).sum(axis=1)])
# print('test2',pred.max())
return pred
1
2
3
4
5
item_prediction = predict(train_data_matrix, item_similarity, type = 'item')
user_prediction = predict(train_data_matrix, user_similarity, type = 'user')
print(len(item_prediction))
print(len(item_prediction[0]))
print(np.argsort(item_prediction[0]))

计算均方误差

1
2
3
4
5
6
7
8
9
def rmse(prediction, ground_truth):
# 计算均方误差
#nonzero(a)返回数组a中值不为零的元素的下标
#flatten()创建矩阵
prediction = prediction[ground_truth.nonzero()].flatten()
ground_truth = ground_truth[ground_truth.nonzero()].flatten()
return sqrt(mean_squared_error(prediction, ground_truth))
print('User based CF RMSE: ' + str(rmse(user_prediction, test_data_matrix)))
print('Item based CF RMSE: ' + str(rmse(item_prediction, test_data_matrix)))

SVD分解的方法,其中k是特征值个数,指定将分解为,中间的对角矩阵有k个特征值 >k: int, optional,Number of singular values and vectors to compute. Must be 1 <= k < min(A.shape)

1
2
3
4
5
u, s, vt = svds(train_data_matrix, k=500)
s_diag_matrix = np.diag(s)
X_pred = np.dot(np.dot(u, s_diag_matrix), vt)

print('User-based CF MSE: ' + str(rmse(X_pred, test_data_matrix)))

一点题外话

推荐系统这个东西,除了早年间的网易云的歌曲推荐,我还真没见过多少看起来不错的(这里先不提各大软件侵犯用户隐私的事,只说效果)。市面上大多数推荐系统都免不了按照偏好推荐,有的还故意引导,经常收敛到一个点出不去了;大多数时候还是很有效的,当然,是对商家而言。 尽力不在推荐算法面前暴露自己的喜好,才有微弱的可能不被推荐算法牵着鼻子走。(实际上市面上大多数投入使用的推荐算法时间长了都会陷入死区出不来,哪怕你随机乱点,只有意识的往外跳才会跳出来)。这好像也不太怪这些网站,要是推荐不准,怕是要被用户骂智障系统(我的好友某君前一阵刚在空间骂了一顿b站的智障推荐系统)

我小时候上网看门户网站、导航、天涯、博客,能够自由地访问谷哥和度娘(那会百度还没有今天这么不堪,谷歌也没今天这么evil),赶上了当时昙花一现的黄金时期。 中立的搜索引擎、内容合格的百科和问答、论坛、独立的个人网站······当年的环境比现在遍地百家号小编的知识荒漠好太多了 我记忆里的中文世界互联网环境的第一次崩塌差不多就是微博兴起的那个时期,对,就是那个公知果粉嘴里最好的时代。新浪搜狐腾讯网易纷纷开始折腾微博,甚至自家博客都不管了。(这里没提饭否,我感觉饭否更像是一个大的网络聊天室,而不是微缩版博客)这无疑是一次大倒退,微博限制一百多个字、搜索内容按时间和热度排序等措施,极大拉低了内容质量。为什么微博杠精和鸡汤营销号多,因为微博天生就适合这些人,种种举措都在极力打击长文和精品内容,跟水贴惩罚措施极严、精品贴有奖励和置顶的论坛贴吧走向了相反的道路(哪怕营销号想好好写点东西当个看起来认真点的营销号,都只能发长图,长图里面文字配图片写个几千字)。这就搞得微博上的内容质量,比整天作死换吧主等各种恶心操作的百度贴吧差了十条街,就算跟自家之前的博客产品比也差了两条街。微博兴起和论坛博客式微直接导致了网上冲浪看到的内容成了八卦报纸读者意林(新浪本来也搞新闻的)。公知果粉小粉红鸡汤营销号追星粉圈先后登场,止增笑尔。 第二次就差不多是微信兴起,不用多说了,这个很多人骂过了。极度封闭,禁止外链,不对别的搜索引擎开放,评论由公众号筛选。一开始还是有不少人在用心运营公众号的,后来咪蒙应运而生,呼风唤雨,迅速抢占了市场。 第三次,没有第三次了。短视频和越来越急功近利的推荐算法(就比如知乎那个快手系的推荐算法和热榜),除了更加封闭,并不会变得再糟糕了。能跳出来的人自然会跳出来。很多人活得很累,看不下长文也写不动长文了,活在搞笑视频里也是一件好事 现在看来,这样才是常态。当年的情景不过是一个不稳定的乌托邦幻梦罢了。

这种项目啊,我是觉得赶紧凉了的好,太离谱了这个。微信公众号推送疾病科普文章,文章还是从网上爬下来的(牢饭警告) 为了获取个人信息做出有用推荐,还要微信关注公众号授权获取病人信息(侵犯隐私警告) 推送的文章质量呢,符合微信公众号平均水平吧。推送对象都是某肿瘤医院病人,还要分成不同的疾病部位之类的推送文章。效果?不给医患关系添把火都算好了,这患者基本都是重症,有啥问题直接问医生或者直接搜不好么,推送的内容质量大概比不过直接搜。虽然还没上线,我猜实际效果远不如直接建设一个导航网站让病人自己点,或者组织医生写点阅读指南,就像维基百科那种。但是呢,这种东西费力不讨好,医院要的是评职称发论文立项目。服务病人的事情呢,上面不给经费;基本评职称和考核只看论文,医生实际做的怎么样救了多少病人并不能评职称发奖金,甚至这种正事做多了花空了医院的医保,年终考核还要被骂的。

参考

基于矩阵分解的推荐算法
项亮的《推荐系统实践》的代码实现