基于TF-IDF算法、余弦相似度算法实现相似文本推荐——文本相似度算法,主要应用于文本聚类、相似文本推荐等场景。
设计说明
使用jieba切词,设置自定义字典
使用TF-IDF算法,找出文章的关键词;
每篇文章各取出若干个关键词(比如20个),合并成一个集合,计算每篇文章对于这个集合中的词的词频(待优化:为了避免文章长度的差异,可以使用相对词频,);
生成两篇文章各自的词频向量;
计算两个向量的余弦相似度,值越大就表示越相似。
实现说明
1)初始化
需要对原始文档做些简单的处理,在预处理结果文件中每一行记录一个文档,文档ID与文档内容通过一定分隔符分割(比如以:::间隔),写入txt文档,当然并非一定如此处理,此处可以根据实际使用,调整代码实现。
我选取了五则新闻,其中1,2,3为房地产板块新闻,4,5为NBA板块新闻。
原始文本预处理
其余初始化内容详见代码注释。
class DocumentSimilarity:
def __init__(self):
#停词字典路径
self.stopword = 'Cstopword.dic'
#原始文档路径,每一行一个文档,文档ID与文档内容以:::间隔
self.DocumentFilePath = 'Document.txt'
#切词结果存放路径
self.CutWordFilePath = 'CutWordNews.txt'
#每个文档选取关键词个数
self.KeyWordsNum = 10
#推荐相似文档个数
self.DocumentSimilarityNum = 6
#切词结果,格式为{文档ID:切词结果,……}
self.CutWordDict = {}
#文档关键词字典,格式为{文档ID:{关键词:TF-IDF值,……},……}
self.DocumentKeywords = {}
#文档词频字典,格式为{文档ID:{切词:词频,……},……}
self.WordsFrequency = {}
# 导入自定义字典
jieba.load_userdict("Custom_dictionary.dic")
2)使用jieba切词
之前在
Python 中文分词——jieba
文章中已详细介绍过,这里不做赘述。
def CutWord(self):
"""
切词
:return:
"""
stopwords = [line.strip().decode('utf-8') for line in open(self.stopword).readlines()] # 读取中文停用词表
with open(self.DocumentFilePath, 'r+') as readfile:
content = readfile.readlines() #读取文本内容
with open(self.CutWordFilePath, 'w+') as writerfile:
for line in content:
cut_words = jieba.cut(line.split(':::')[1]) #分词,默认是精确分词
tmp = []
for word in cut_words:
word = ''.join(re.findall(u'[\\u4e00-\\u9fa5]|[0-9]+', word)).strip() # 过滤不是中文、数字的内容
if (len(word) != 0 and not stopwords.__contains__(word)): # 去掉在停用词表中出现的内容
tmp.append(word)
writerfile.write('\t'.join(tmp).encode('utf-8') + "\n")
self.CutWordDict[line.split(':::')[0]] = '\t'.join(tmp).encode('utf-8')
结巴分词结果
3)使用TF-IDF算法,找出文章的关键词
TF-IDF(term frequency–inverse document frequency)是一种用于信息检索与数据挖掘的常用加权技术。用以评估一字词对于一个文件集或一个语料库中的其中一份文件的重要程度。字、词语的重要性随着它在文件中出现的次数成正比增加,但同时会随着它在语料库中出现的频率成反比下降。即如果某个词或短语在一篇文章中出现的频率(TF)高,并且在整个语料库章中很少出现,则认为此词或者短语具有很好的类别区分能力,适合提取为文档关键字。
计算词频
词频 = 某个词在文章中出现的总次数/文章的总词数
计算逆文档频率
在此,首先需要一个语料库来模拟语言的使用环境。
逆文档频率(IDF) = log(词料库的文档总数/包含该词的文档数+1)
计算TF-IDF值
TF-IDF值 = TF * IDF(TF-IDF值与该词的出现频率成正比,与在整个语料库中的出现次数成反比)
排序取关键字
计算出文章中每个词的TF-IDF值之后,进行排序,选取其中值最高的几个作为关键字。
def GetKeyWords(self):
"""
获取文档关键词
:return:
"""
vectorizer = CountVectorizer() # 将文本中的词语转换为词频矩阵 矩阵元素a[i][j] 表示j词在i类文本下的词频 http://scikit-learn.org/stable/modules/feature_extraction.html
FrequencyMatrix = vectorizer.fit_transform(self.CutWordDict.values()) #返回词频矩阵
transformer = TfidfTransformer()
TFIDF = transformer.fit_transform(FrequencyMatrix) # 第一个fit_transform是计算tf-idf 第二个fit_transform是将文本转为词频矩阵,
AllWord = vectorizer.get_feature_names()
Weight = TFIDF.toarray() # tf-idf矩阵,元素w[i][j]表示j词在i类文本中的tf-idf权重
Title = self.CutWordDict.keys()
self.WordsFrequency = {}
for i in range(len(Weight)): #打印每类文本的tf-idf词语权重,第一个for遍历所有文本,第二个for便利某一类文本下的词语权重
tmp ={}
for j in range(len(AllWord)):
if Weight[i][j] != 0.0:
tmp[AllWord[j]] = Weight[i][j]
sortdict = dict(sorted(tmp.items(), key=lambda d: d[1], reverse=True)[:self.KeyWordsNum])
self.DocumentKeywords[Title[i]] = sortdict #获取每篇文章的关键词
wordsFrequencyTmp = {}
for word in AllWord:
y = vectorizer.vocabulary_.get(word)
WordFrequency = FrequencyMatrix.toarray()[i][y]
if WordFrequency != 0:
wordsFrequencyTmp[word] = WordFrequency
self.WordsFrequency[Title[i]] = wordsFrequencyTmp
4)生成两篇文章各自的词频向量,计算两个向量的余弦相似度,值越大就表示越相似。
@staticmethod
def Cos(cipin):
"""
余弦计算,返回余弦相似度
:param cipin:词频,格式[[1,2],[1,2]]
:return:
"""
dot_product = 0.0
normA = 0.0
normB = 0.0
for x,y in cipin:
dot_product += float(x) * float(y)
normA += float(x) * float(x)
normB += float(y) * float(y)
if normA == 0.0 or normB == 0.0:
return 0
else:
return float(dot_product)/ float(sqrt(normA * normB))
def Cosinesimilarity(self,OneselfTextId):
"""
获取相似文档
:param OneselfTextId:文档ID
:return:
"""
SimilarText ={}
for TextId,Keywords in self.DocumentKeywords.iteritems():
if TextId != OneselfTextId:
Bothtextfrequency = []
K = self.DocumentKeywords[OneselfTextId].keys() + self.DocumentKeywords[TextId].keys()#获取双方关键词列表
for keyword in K :#获取关键词词频
if keyword in self.WordsFrequency[OneselfTextId].keys():
FrequencyOneself =self.WordsFrequency[OneselfTextId][keyword]
else:
FrequencyOneself = 0
if keyword in self.WordsFrequency[TextId].keys():
FrequencyOther =self.WordsFrequency[TextId][keyword]
else:
FrequencyOther = 0
Bothtextfrequency.append([FrequencyOneself,FrequencyOther])
Cosinesimilarity = DocumentSimilarity.Cos(Bothtextfrequency)#计算余弦
SimilarText[TextId] = Cosinesimilarity
SortDict = dict(sorted(SimilarText.items(), key=lambda d: d[1], reverse=True)[:self.DocumentSimilarityNum])
for i,n in sorted(SimilarText.items(), key=lambda d: d[1], reverse=True)[:self.DocumentSimilarityNum]:
print u'【文本ID】:{0},【文本相似度】:{1} 【文本关键词】:{2}|{3}'.format(i ,n,','.join(self.DocumentKeywords[i]),','.join(self.DocumentKeywords[OneselfTextId]))
return SortDict
使用说明
需安装jieba、sklearn、numpy第三方库。
import re
import jieba
import jieba.analyse
from sklearn.feature_extraction.text import TfidfTransformer
from sklearn.feature_extraction.text import CountVectorizer
from numpy import *
寻找与第一则新闻相似的文本内容结果如下:
if __name__ == "__main__":
DS = DocumentSimilarity()
DS.CutWord()
DS.GetKeyWords()
DS.Cosinesimilarity('1')
转载请说明,若对你有帮助,关注支持一下哦。