计算文本的权重向量,有个很有效的权重方案:TF-IDF权重策略。TF-IDF含义是词频逆文档频率,指的是,如果某个词或短语在一篇文章中出现的频率高,并且在其他文章中很少出现,则认为此词或短语具有很好的分类区分能力,适合用来分类。简单的说,TF-IDF(词频-逆文档频率),它可以反映出语料库中某篇文档中某个词的重要性。目前所知应用是用来计算文档相似性(TF-IDF与余弦相似性的应用(二):找出相似文章)
TF-IDF权重公式参见这篇博文:TF-IDF与余弦相似性的应用(一):自动提取关键词。由于自己实现代码,在运算时效率很低,所以本文主要讲述sklearn里面的TF-IDF方法。里面主要用到了两个函数:CountVectorizer()和TfidfTransformer()。CountVectorizer是通过fit_transform函数将文本中的词语转换为词频矩阵,矩阵元素weight[i][j] 表示j词在第i个文本下的词频,即各个词语出现的次数;通过get_feature_names()可看到所有文本的关键字,通过toarray()可看到词频矩阵的结果。TfidfTransformer也有个fit_transform函数,它的作用是计算tf-idf值。这里附上一个我初学时用于理解的小例子(python2实现)。
1 # coding:utf-8 2 import jieba 3 import jieba.posseg as pseg 4 import os 5 import sys 6 from sklearn import feature_extraction 7 from sklearn.feature_extraction.text import TfidfTransformer 8 from sklearn.feature_extraction.text import CountVectorizer 9 10 if __name__ == "__main__": 11 corpus=["我 来到 北京 清华大学",#第一类文本切词后的结果,词之间以空格隔开 12 "他 来到 了 网易 杭研 大厦",#第二类文本的切词结果 13 "小明 硕士 毕业 与 中国 科学院",#第三类文本的切词结果 14 "我 爱 北京 天安门"]#第四类文本的切词结果 15 vectorizer=CountVectorizer() 16 #该类会将文本中的词语转换为词频矩阵,矩阵元素a[i][j],表示j词在i类文本下的词频 17 transformer=TfidfTransformer() 18 #该类会统计每个词语的tf-idf权值 19 tfidf=transformer.fit_transform(vectorizer.fit_transform(corpus)) 20 #第一个fit_transform是计算tf-idf,第二个fit_transform是将文本转为词频矩阵 21 word=vectorizer.get_feature_names() 22 #获取词袋模型中的所有词语 23 weight=tfidf.toarray() 24 #将tf-idf矩阵抽取出来,元素a[i][j]表示j词在i类文本中的tf-idf权重 25 for i in range(len(weight)): 26 #打印每类文本的tf-idf词语权重,第一个for遍历所有文本,第二个for便利某一类文本下的词语权重 27 print u"-------这里输出第",i,u"类文本的词语tf-idf权重------" 28 for j in range(len(word)): 29 print word[j],weight[i][j]
若有多个文档统计TF-IDF权重时,则会出现一些计算结果不等值的问题。现取4个小样本txt数据做测试分析,每个txt中取单个句子,句子先做分词,再调用sklearn库做计算。测试案例代码实现如下图1,分词结果和计算结果展示如下图2:
1 import os 2 import jieba 3 import jieba.posseg as pseg 4 import sys 5 import string 6 from sklearn import feature_extraction 7 from sklearn.feature_extraction.text import TfidfTransformer 8 from sklearn.feature_extraction.text import CountVectorizer 9 10 reload(sys) 11 sys.setdefaultencoding(‘utf8‘) 12 13 def savefile(savepath, content): 14 fp = open(savepath, ‘wb‘) 15 fp.write(content) 16 fp.close() 17 18 def readfile(path): 19 fp = open(path, ‘rb‘) 20 content = fp.read() 21 fp.close() 22 return content 23 24 if __name__ == "__main__": 25 start_path = "segfile/" 26 end_path = "resultfile/" 27 corpus = [] 28 tfidfdict = {} 29 for file_path in os.listdir(start_path): 30 fullname = start_path + file_path 31 content = readfile(fullname).strip() 32 #content = content.decode(‘gbk‘,‘ignore‘).encode(‘utf-8‘) 33 content = content.replace("\r\n", "") 34 content = content.decode(‘utf-8‘) 35 content_seg = jieba.cut(content.strip()) 36 savefile(end_path + file_path, " ".join(content_seg)) 37 for file_path in os.listdir(end_path): 38 fullname = end_path + file_path 39 content = readfile(fullname).strip() 40 content = content.decode(‘utf-8‘) 41 content = content.replace("\r\n", "").strip() 42 corpus.append(content) 43 #print corpus 44 for i in range(len(corpus)): 45 print str(corpus[i]) 46 print "--------" 47 vectorizer = CountVectorizer() 48 transformer = TfidfTransformer() 49 tfidf = transformer.fit_transform(vectorizer.fit_transform(corpus)) 50 word = vectorizer.get_feature_names() 51 weight = tfidf.toarray() 52 #print(weight) 53 for i in range(len(weight)): 54 print u"-------这里输出第", i, u"类文本的词语tf-idf权重------" 55 for j in range(len(word)): 56 print word[j], weight[i][j]
1 我 来到 北京 清华大学 。 2 他 来到 了 网易 杭研 大厦 3 小明 硕士 毕业 于 中国科学院 4 我 爱 北京 天安门 5 -------- 6 -------这里输出第 0 类文本的词语tf-idf权重------ 7 中国科学院 0.0 8 北京 0.52640543361 9 大厦 0.0 10 天安门 0.0 11 小明 0.0 12 来到 0.52640543361 13 杭研 0.0 14 毕业 0.0 15 清华大学 0.66767854461 16 硕士 0.0 17 网易 0.0 18 -------这里输出第 1 类文本的词语tf-idf权重------ 19 中国科学院 0.0 20 北京 0.0 21 大厦 0.525472749264 22 天安门 0.0 23 小明 0.0 24 来到 0.414288751166 25 杭研 0.525472749264 26 毕业 0.0 27 清华大学 0.0 28 硕士 0.0 29 网易 0.525472749264 30 -------这里输出第 2 类文本的词语tf-idf权重------ 31 中国科学院 0.5 32 北京 0.0 33 大厦 0.0 34 天安门 0.0 35 小明 0.5 36 来到 0.0 37 杭研 0.0 38 毕业 0.5 39 清华大学 0.0 40 硕士 0.5 41 网易 0.0 42 -------这里输出第 3 类文本的词语tf-idf权重------ 43 中国科学院 0.0 44 北京 0.61913029649 45 大厦 0.0 46 天安门 0.78528827571 47 小明 0.0 48 来到 0.0 49 杭研 0.0 50 毕业 0.0 51 清华大学 0.0 52 硕士 0.0 53 网易 0.0
这时我们会发现问题,如果用传统的TF-IDF与余弦相似性的应用(一):自动提取关键词 中计算方法,笔算之后结果和程序结果完全不同,相差很大,在确定自己笔算无误后,debug调试研究计算的中间过程,我们发现计算过程中加入了tfidf平滑处理:
1 if self.use_idf: 2 n_samples, n_features = X.shape 3 df = _document_frequency(X) 4 5 # perform idf smoothing if required 平滑处理 6 df += int(self.smooth_idf) #df+1 1处 7 n_samples += int(self.smooth_idf) #n_samples+1 2处 8 9 # log+1 instead of log makes sure terms with zero idf don‘t get 10 # suppressed entirely. 11 idf = np.log(float(n_samples) / df) + 1.0 #idf+1 3处 12 self._idf_diag = sp.spdiags(idf, diags=0, m=n_features, 13 n=n_features, format=‘csr‘)
假设t表示某个词,d表示一篇文档,则词频TF(t,d)是某个词t在文档d中出现的次数,而文档DF(t,D)是包含词t的文档数目,于是公式变成了这样:
IDF(t,D)=log(|D|+1)/(DF(t,D)+1) +1
这里|D|表示语料库的文档总数,为了不让分母为了0,在此进行了加1平滑操作。
TFIDF(t,d,D)=TF(t,d)*IDF(t,D)=TF(t,d)*(log(|D|+1)/(DF(t,D)+1) +1)
然后发现结果还是不对,除了做平滑处理以外,sklearn库还进行了归一化处理normalization,将值趋于0到1之间。什么是归一化,比如[1,2],归一化后成为[1/sqrt(5), 2/sqrt(5)]。(归一化问题未做研究,有时间再看看,跳过)
所以得出结论,调用sklearn库最后得出的TF-IDF结果是归一化后的idf值。不同的词在不同文档中有不同的idf,又有不同的tf词频,所以这个求得的值是在单篇文档中对于单个词做的分析,那么对于整个语料来说,如何利用求得的这个tf-idf值做热词分析呢?还是不知啊。
[ 0.52640543 0.52640543 0.66767854 0.41428875 0.52547275 0.52547275 0.52547275 0.5 0.5 0.5 0.5 0.6191303 0.78528828]
参考博文:用python开始机器学习(5:文本特征抽取与向量化)