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个分桶当中,然后计算每个分桶里面物品的相似物品。这样性能就提升了很多。

这样,从算法原理上就完成了内容相似推荐。