过去的两周里,我一直忙于为 『玩点什么』 设计一个推荐系统(即,recommend system)。在这个过程中,参考了之前的 几本书籍,查找了一系列的资料。想着这些资料上,大部分都是大同小民的,实现了几个简单的推荐功能,改进了标签推荐算法,便想着写篇文章记录一下。
『玩点什么』,是一个基于 Django、Python 的 CMS 系统(Mezzanine)。是的,和我的博客使用的是同一个 CMS 系统。由于使用的是 Python 语言,因此对于机器学习具有天生的优势。
推荐系统
推荐系统是一种信息过滤系统,用于预测用户对物品的“评分”或“偏好”。
对于推荐系统系统来说,目前采用的主要方式是:
- 基于内容推荐:内容之间的相似度,如文章的标签、电影的属性、书籍的分类。
- 协同过滤(待实现):用户之间的相似度,如喜欢看科幻片的 A、B 用户,A 喜欢看的 c 电影,B 也可能喜欢。
要实现这两种方式有一个前提是,拥有的数据。特别是协同过滤,需要有大量的用户行为数据。
而对于一个 CMS 系统来说,还有两种简单的方式:
- 基于统计学推荐,诸如文章的阅读量、分享量,又或者文章的评分数。
- 基于标签推荐,对于专业领域的文章来说,作者提交的标签往往比机器生成更加可靠。
因此,让我们先从这两种简单的方式讲起。
在很多内容网站上,一般都会把流量比较大的
- 博客的访问量
- 用户的点评数据
https://www.biaodianfu.com/imdb-rank.html
贝叶斯统计的算法得出的加权分(Weighted Rank-WR),公式如下:
WR, 加权得分(weighted rating)。 R,该电影的用户投票的平均得分(Rating)。 v,该电影的投票人数(votes)。 m,排名前 250 名的电影的最低投票数(现在为 3000)。 C, 所有电影的平均得分(现在为6.9)。
from math import sqrt
def confidence(ups, downs):
n = ups + downs
if n == 0:
return 0
z = 1.0 #1.44 = 85%, 1.96 = 95%
phat = float(ups) / n
return ((phat + z*z/(2*n) - z * sqrt((phat*(1-phat)+z*z/(4*n))/n))/(1+z*z/n))
id | xx | keywords | votes | rating_sum | rating_average | published | title |
---|---|---|---|---|---|---|---|
"323 " | "0 " | "tech tools " | "45 " | "165 " | "3.66666666666667 " | "1 " | "每个程序员必知之:程序员差别的本质" |
"63 " | "0 " | "programmer resume latex " | "24 " | "66 " | "2.75 " | "1 " | "程序员该如何去写自己的简历(草稿)" |
"207 " | "0 " | "beageek geek anywherehtml " | "20 " | "79 " | "3.95 " | "1 " | "be a geek 1:无处不在的html" |
"38 " | "0 " | "iot osiot laravel ajax RESTful " | "19 " | "84 " | "4.42105263157895 " | "1 " | "一个最小的物联网系统设计方案及源码" |
"361 " | "0 " | "write type writer programmer " | "17 " | "65 " | "3.82352941176471 " | "1 " | "编程同写作,写代码只是在码字" |
"259 " | "0 " | "gitbook " | "12 " | "51 " | "4.25 " | "1 " | "gitbook 制作书籍" |
"37 " | "0 " | "iot ajax laravel RESTful serial " | "11 " | "40 " | "3.63636363636364 " | "1 " | "最小物联网系统(一)——系统组成" |
"391 " | "0 " | "javascript anonymous encapsulation " | "11 " | "50 " | "4.54545454545455 " | "1 " | "Javascript 匿名函数与封装" |
"512 " | "0 " | " " | "9 " | "36 " | "4.0 " | "1 " | "如何通过github提升自己" |
"508 " | "0 " | "emacs vim github " | "8 " | "40 " | "5.0 " | "1 " | "努力只是因为想去做想做的事" |
"548 " | "0 " | "full stack mustache django rework " | "8 " | "35 " | "4.375 " | "1 " | "全栈工程师的思考" |
- 获取文章的所有标签
- 对所有文章的标签进行统计,计数
- 获取文章标签中计数最多的 tag,查找相同标签的博客
- 在剩余的博客中,选择第二多 tag,再过滤剩余的博客
keywords_name = model.get_keywordsfield_name()
assigned = getattr(model, keywords_name).all()
all_keywords = Keyword.objects.filter(assignments__in=assigned)
keywords = all_keywords.annotate(item_count=Count("assignments")).order_by('-item_count')
# TODO: filter most popular tag
first_keyword = keywords.first()
if first_keyword:
first_filtered_blogposts = BlogPost.objects.published().filter(keywords__keyword__title__contains=first_keyword.title)
first_filtered_blogposts = first_filtered_blogposts.filter(~Q(id=post_id))
second_keyword = keywords[1]
if second_keyword:
blog_posts = first_filtered_blogposts.filter(keywords__keyword__title__contains=second_keyword.title)
return blog_posts[:3]
else:
return []
https://stackoverflow.com/questions/10029588/python-implementation-of-the-wilson-score-interval
单一的关键词,只对于网站本身是有价值的,对于用户来说,则不是如此。
基于 Google Search Console 的关键词权重,比如
Queries | Clicks | Impressions | CTR | Position |
---|---|---|---|---|
homebridge-miio | 7 | 28 | 25% | 8.2 |
home assistant broadlink | 4 | 10 | 40% | 15 |
amazon echo raspberry pi | 3 | 10 | 30% | 5.0 |
raspberry pi homebridge | 2 | 6 | 33.33% | 7.7 |
raspberry pi alexa gpio | 2 | 4 | 50% | 10 |
nodemcu homekit | 2 | 3 | 66.67% | 13 |
arduino homekit | 1 | 3 | 33.33% | 9.7 |
用户搜索 Raspberry Pi,那么它可能还会结合 Arduino ??
这也就是上面算法中的问题,假如我们的文章中,出现一系列的 home assistant、raspberry pi 相关的文章,那么它对于网站来说,表明它是没有价值的。
并且如果同一系列的文章太多,如网上各类的 Vue 高仿站点,那么用户可能已经掌握了,或者没有价值。因此,它并不能在搜索结果上体现,
在站点内,它有重要的意义,标签数量多。但是它并不能真正地解决用户的问题?也不能真正地体现出网站的价值。
假如用户搜索了一篇 raspberry pi + homebridge 的文章,那么它确实可以阅读一些相关的文章,而诸如 raspberry pi alexa gpio 从上图来看似乎是一个用户更加喜欢的选择。
示例:http://localhost:8000/play/howto-install-home-assistant-in-raspberry-pi/
这个时候由编辑推荐出来的,反而比较准确。
- 权重
- 加权计算法
- Item Representation:为每个item抽取出一些特征(也就是item的content了)来表示此item;
- Profile Learning:利用一个用户过去喜欢(及不喜欢)的item的特征数据,来学习出此用户的喜好特征(profile);
- Recommendation Generation:通过比较上一步得到的用户profile与候选item的特征,为此用户推荐一组相关性最大的item。
步骤一:文章的标签
步骤二:用户行为
步骤三:分析用户可能性
分词库:jieba 功能:
- 基于前缀词典实现高效的词图扫描,生成句子中汉字所有可能成词情况所构成的有向无环图 (DAG)
- 采用了动态规划查找最大概率路径, 找出基于词频的最大切分组合
- 对于未登录词,采用了基于汉字成词能力的 HMM 模型,使用了 Viterbi 算法
分词功能
- 基于 TF-IDF 算法的关键词抽取
- 基于 TextRank 算法的关键词抽取
TextRank
TextRank算法可以用来从文本中提取关键词和摘要(重要的句子)
- 关键词提取
- 关键短语提取
- 摘要生成
中文库:https://github.com/letiantian/TextRank4ZH
Content Engine: https://github.com/groveco/content-engine
User Filtering: https://github.com/fcurella/django-recommends
LDA
隐含狄利克雷分布简称LDA(Latent Dirichlet allocation),是一种主题模型,它可以将文档集中每篇文档的主题按照概率分布的形式给出。
最近邻方法(k-Nearest Neighbor,简称kNN)
Rocchio算法
决策树算法(Decision Tree,简称DT)
线性分类算法(Linear Classifer,简称LC)
朴素贝叶斯算法(Naive Bayes,简称NB)
http://blog.untrod.com/2016/06/simple-similar-products-recommendation-engine-in-python.html
- hitcounts
- rating
django + celery + redis 调度/定期/afterpost ?
ajax 生成
大多数情况下,对于我而言,又或者大多数程序员而言,我们只需要了解相关领域的算法(无论是机器学习、深度学习),有一个总体的认识:了解有哪些相似的算法、以及每个算法的用途、能读懂原理。除非自己去造轮子,要不仍然只是使用 API 而已。即便你熟悉某一个算法的原理,只要是不能深入理解,在未来也中会是使用 API。
参考内容:
- 《驾驭文本》
- 《推荐系统》
- 《推荐系统:技术、评估及高效算法》
- 《机器学习实战》
文章:http://www.cnblogs.com/breezedeus/archive/2012/04/10/2440488.html