一、基本定义及公式
TF-IDF用来评估一个字词对于一个文件集或一个语料库中的其中一份文件的重要程度。字词的重要性随着它在文件中出现的次数成正比增加,但同时会随着它在语料库中出现的频率反比下降。如果某个单词在一篇文章中出现的频率TF高,并且在其他文章中很少出现(即代表它有明显的区分度),则认为此词或者短语具有很好的类别区分能力,适合分类
一些基础概念:文章画像是描述每篇文章以给定一些词。主要是由主题词与关键词组成,两者最大的区别就是主题词经过了规范化处理。
关键词:文章中一些词权重高的词语。主题词:是进行了规范化处理,文章中出现同义词,计算结果出现次数高的词。
TF(term frequency)词频:表示关键词在文本中出现的频率(通常会被归一化,以防止其对长文件的偏向),它并不能作为文本相似度的衡量标准,因为如若在给定的一篇中文文档中出现的频率非常高的话,但这些词在每篇文章中都具有非常高的词频(在语料库中反复出现),那么会使得其在每篇文章中都会被命中。
表示关键词在文件中出现的次数,分母则代表中所有词汇出现的次数。
IDF(inverse document frequency)逆文档频率:对某一特定词语的IDF,可以由总文件数目除以包含该词语的文件数,再将得到的商取对数即可得到。
其中是语料库中所有文档总数,分母是包含词语的所有文档数。
TF-IDF以TF和IDF的乘积作为取值测度,并用它完成对权值TF的调整,调整的目的在于突出重要单词,抑制次要单词。其单纯地认为文本频率小的单词越重要,文本频率大的单词就越无用,显然这并不是完全正确的。
二、计算方法
关键词与主题词的计算方法:
关键词:TEXTRANK计算出的结果TOP-K个词及权重
主题词:TEXTRANK的TOP-K词语TF-IDF计算的TOP-K个词的交集
利用TF-IDF从文章中抽取相应的画像
步骤:
①读取N篇文章数据,然后对文章数据进行分词处理
②使用Spark.ml(Spark MLlib)的count与IDF进行计算,先计算分词之后每篇文章的词频,然后根据词频计算IDF以及词,得到IDF模型
利用②得到的模型计算N篇文章数据的TF-IDF值。
三、Python部分代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | def segmentation(partition): import os import re import jieba import jieba.analyse import jieba.posseg as pseg import codecs abspath = '/home/admin01/project/words' # jieba加载用户词典 userDict_path = os.path.join(abspath, 'ITKeywords.txt') jieba.load_userdictad(userDict_path) # 停用词文本 stopwords_path = os.path.join(abspath, 'stopwords.txt') def get_stopwords_list(): """ 返回stopwords列表 """ stopwords_list = [i.strip() for i in codecs.open(stopwords_path).readlines()] return stopwords_list # 获得所有的停用词列表 stopwords_list = get_stopwords_list() # 分词 def cur_sentenct(sentence): """ 对切割之后的词语进行过滤,去掉停用词,保留名词,英文和自定义词库中的词,长度大于2的词 """ seg_list = pseg.lcut(sentence) seg_list = [i for i in seg_list if i.flag not in stopwords_list] filterd_words_list = [] for seg in seg_list: if len(seg.word) <= 1: continue elif seg.flag == 'eng': if len(seg.word) <= 2: continue else: filtered_words_list.append(seg.word) elif seg.flag.startswith('n'): filtered_words_list.append(seg.word) elif seg.flag in ['x', 'eng']: filtered_words_list.append(seg.word) return filtered_words_list for row in partition: sentence = re.sub('<.*?>', '', row_sentence) #替换标签数据 words = cut_sentence(sentence) yield row.article_id, row.channel_id, words |
1 2 3 4 5 6 7 8 | # 词语和词频统计 from pyspark.ml.feature import CountVectorizer # 总词汇的大小,文本中必须出现的次数 cv = CountVectorizer(inputCol = 'words', outputCol = 'countFeatures', vocabSize = 200 * 10000, minDF = 1.0) # 训练词频统计模型 cv_model = cv.fit(words_df) cv_model.write().overwrite().save('hdfs://Linux02:8020/headlines/models/CV.model') |
1 2 3 4 5 6 7 8 9 10 | # 词语与词频统计 from pyspark.ml.feature import CountVectorizerModel cv_model = CountVectorizerModel.load('hdfs://LInux02:8020/headlines/model/CV.model') # 得到词频向量结果 cv_result = cv_model.transform(words_df) # 训练IDF模型 from pyspark.ml.feature import IDF idf = IDF(inputCol = 'countFeatures', outputCol = 'idfsFeatures') idf_model = idf.fit(cv_result) idf_model.write().overwrite().save('hdfs://Linux02:8020/headlines/models/IDF.model') |
1 2 3 4 5 6 | from pyspark.ml.feature import CountVectorizerModel cv_model = CountVectorizerModel.load('hdfs://Linux02:8020/headlines/models/countVectorizerOfArticleWords.model') from pyspark.ml.feature import IDFModel idf_model = IDFModel.load('hdfs://Linux02:8020//headlines/models/IDFOfArticleWords.model') |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | keywords_list_with_idf = list(zip(cv_model.vocabulay, idf_model.toArray())) def append_index(data): for index in range(len(data)): data[index] = list(data[index]) # 将元组转化为list data[index].append(index) # 加入索引 data[index][1] = float(data[index][1]) append_index(keywords_list_with_idf) sc = spark.sparkContext() rdd = sc.parallelize(keywords_list_with_idft) # 创建RDD idf_keywords = rdd.toDF(['keywords', 'idf', 'index']) idf_keywords.write.insertInto('idf_keywords_values') |