Overview
内容相似推荐算法,是推荐系统当中的一个常用的算法。假设我们做的是文章推荐,那么,我们该怎样求两篇文章的相似度呢?本文将记录几个常用工具的使用方法。
1. jieba
分词工具
可以说是当前最好用的中文分词组件。我们拿到文章的标题、摘要、全文之后,可以用jieba
提取关键词,然后去另一篇文章当中做Doc2Vec
,把另一篇文章转化为一个向量,可以用平均或者加权平均的方式得到一个文章的向量。
首先安装这个组件:
pip3 install jieba
之后就可以直接导入使用了:
import jieba
seg_list = jieba.cut("我正在做推荐系统当中的内容相似推荐部分。", cut_all=False)
print("Default Mode: " + "/ ".join(seg_list)) # 精确模式
输出如下:
Default Mode: 我/ 正在/ 做/ 推荐/ 系统/ 当中/ 的/ 内容/ 相似/ 推荐/ 部分/ 。
直接分词会把所有的词都得到,这样显然是不符合要求的。我们可以提取关键词功能来获得最重要的K
个关键词。
import jieba.analyse
key_words = jieba.analyse.extract_tags("我正在做推荐系统当中的内容相似推荐部分。", topK=3, withWeight=True, allowPOS=())
print(key_words)
显示如下:
[('推荐', 1.386500847515), ('当中', 0.82161213919375), ('相似', 0.80830253373)]
我们会得到一个带权重的关键词列表。
这种只能对文章进行精确匹配,鲁棒性稍微差点。因此我们更需要下面这种更强大的工具Word2Vec
。
2. Word2Vec
Word2Vec
其实是一个神经网络模型。我们不用自己实现,可以使用开源届的现有工具,Spark Word2Vec
或腾讯Word2Vec
工具NLP ChineseEmbedding
。
2.1 Spark Word2Vec
具体用法如下:
from pyspark.sql import SparkSession
from pyspark.sql import functions as F
from pyspark.ml.feature import Word2Vec
spark = SparkSession.builder.appName("Spark Word2Vec") \
.master("spark://***.***.***.***:7077") \
.config("spark.cores.max", "10") \
.config("spark.driver.memory", "2G") \
.config("spark.executor.memory", "2G") \
.config("spark.executor.cores", "2")\
.getOrCreate()
# 输入数据可以是一个词袋、一个句子或者一个文档
documentDF = spark.createDataFrame([
("我 正在 做 推荐 系统 当中 的 内容 相似 推荐 部分".split(" "), ),
("我 喜欢 做 推荐 系统".split(" "), ),
("做 机器 学习 我 是 专业 的".split(" "), )
], ["text"])
# 学习一个词到向量的映射模型
word2Vec = Word2Vec(vectorSize=3, minCount=0, inputCol="text", outputCol="result")
model = word2Vec.fit(documentDF)
result = model.transform(documentDF)
for row in result.collect():
text, vector = row
print("Text: [%s] => \nVector: %s\n" % (", ".join(text), str(vector)))
最后我们得到如下答案:
Text: [我, 正在, 做, 推荐, 系统, 当中, 的, 内容, 相似, 推荐, 部分] =>
Vector: [0.02447097257456996,-0.05411485582590103,0.02040436087091538]
Text: [我, 喜欢, 做, 推荐, 系统] =>
Vector: [0.04216975644230843,-0.0064331904053688055,-0.011528981802985073]
Text: [做, 机器, 学习, 我, 是, 专业, 的] =>
Vector: [0.019645389967731065,0.010864340301070894,-0.0007330668491444417]
更多内容可以参考Spark
官方文档Extracting, transforming and selecting features。
这个虽然强大,但是受制于我们手头的语料,即训练数据不够充分,所以我们不建议自己训练一个Word2Vec
模型,我们更推荐用更强大的腾讯Word2Vec
——NLP ChineseEmbedding
。
2.2 NLP ChineseEmbedding
这个工具非常有名,具体介绍可以看官网Tencent AI Lab Embedding Corpus for Chinese Words and Phrases。我们这里只记录它是怎么用的。
首先,我们下载到本地,然后解压缩:
wget https://ai.tencent.com/ailab/nlp/zh/data/Tencent_AILab_ChineseEmbedding
.tar.gz
tar -zxvf Tencent_AILab_ChineseEmbedding.tar.gz
得到Tencent_AILab_ChineseEmbedding.txt
这个文件。
然后我们需要安装gensim
包来辅助我们使用腾讯的word2vec
模型。
pip3 install gensim
from gensim.models import KeyedVectors
wv_from_text = KeyedVectors.load_word2vec_format('Tencent_AILab_ChineseEmbedding.txt', binary=False)
model=wv_from_text.wv
这样,我们就得到了这个Word2Vec
模型。
可以通过model.word_vec
方法将文字转成向量。
3. Top N
相似近邻搜索
在我们将文字转成向量之后,可以比较两个向量的相似度,进而选取最相似的N
个向量。而相似近邻搜索主要有两种方式:scipy余弦相似度
和LSH局部敏感哈希
。
3.1 scipy余弦相似度
scipy.spatial.distance.cosine
这个方法在scipy
这个包里,这个包对于做机器学习的人来说已经很熟悉了,不再赘述。
from scipy.spatial import distance
distance.cosine([1, 0, 0], [0, 1, 0])
这个方式会有问题,每两个物品都对比余弦相似度,那么时间复杂度为$O(N^{2})$,这在推荐系统中是不允许的.
3.2 LSH局部敏感哈希
Locality Sensitive Hashing
我们可以从Spark
文档当中找到介绍 Locality Sensitive Hashing。
原理是将备选物品哈希到M
个分桶当中,然后计算每个分桶里面物品的相似物品。这样性能就提升了很多。
这样,从算法原理上就完成了内容相似推荐。