一. 什么是TF-IDF
TF-IDF(Term Frequency-Inverse Document Frequency, 词频-逆文件频率).
是一种用于资讯检索与资讯探勘的常用加权技术。TF-IDF是一种统计方法,用以评估一字词对于一个文件集或一个语料库中的其中一份文件的重要程度。字词的重要性随着它在文件中出现的次数成正比增加,但同时会随着它在语料库中出现的频率成反比下降。
上述引用总结就是, 一个词语在一篇文章中出现次数越多, 同时在所有文档中出现次数越少, 越能够代表该文章.
这也就是TF-IDF的含义.
词频 (term frequency, TF) 指的是某一个给定的词语在该文件中出现的次数。这个数字通常会被归一化(一般是词频除以文章总词数), 以防止它偏向长的文件。(同一个词语在长文件里可能会比短文件有更高的词频,而不管该词语重要与否。)
但是, 需要注意, 一些通用的词语对于主题并没有太大的作用, 反倒是一些出现频率较少的词才能够表达文章的主题, 所以单纯使用是TF不合适的。权重的设计必须满足:一个词预测主题的能力越强,权重越大,反之,权重越小。所有统计的文章中,一些词只是在其中很少几篇文章中出现,那么这样的词对文章的主题的作用很大,这些词的权重应该设计的较大。IDF就是在完成这样的工作.
公式:
TFw=在某一类中词条w出现的次数该类中所有的词条数目
逆向文件频率 (inverse document frequency, IDF) IDF的主要思想是:如果包含词条t的文档越少, IDF越大,则说明词条具有很好的类别区分能力。某一特定词语的IDF,可以由总文件数目除以包含该词语之文件的数目,再将得到的商取对数得到。
公式:
IDF=log(语料库的文档总数包含词条w的文档数+1),分母之所以要加1,是为了避免分母为0
某一特定文件内的高词语频率,以及该词语在整个文件集合中的低文件频率,可以产生出高权重的TF-IDF。因此,TF-IDF倾向于过滤掉常见的词语,保留重要的词语。
一个语料库有N篇文档,从中随机抽取一篇文档,切词后该文档长度为T(个词),词“data”在该文中出现K次,同时词“data”在整个语料库的1/3的文档中出现过,则词“data”对抽出的这篇文档而言的tfidf为()
该文中出现K次/总次数T * log ( N篇文档/整个语料库的1/3的文档中出现过N/3 )= K * Log(3) / T
正确答案: K * Log(3) / T
二、实践
案例一: 基于TF-IDF的关键词提取
TF-IDF:衡量某个词对文章的重要性由TF和IDF组成
TF:词频(因素:某词在同一文章中出现次数)
IDF:反文档频率(因素:某词是否在不同文章中出现)
TF-IDF = TF*IDF
TF :一个单词在一篇文章出现次数越多越重要
IDF: 每篇文章都出现的单词(如的,你,我,他) ,越不重要
案例二: 相似度计算公式
(1)余弦相似度cosine
举例:A(1,2,3) B(2,3,4)
cosine(A, B) = 分子 / 分母 = 20 / 20.12 = 分子:A*B = 1*2 + 2*3 + 3*4 = 20 分母: ||A||* ||B|| = 3.74 * 5.38 = 20.12 ||A|| = sqrt(A*A) = sqrt(1*1 + 2*2 + 3* 3) = 3.74 ||B|| = sqrt(B*B) = sqrt(4*4 + 2*2 + 3* 3) = 5.38
A表示一个句子,B表示另外一个句子
cosine(A, B) = 表示两个句子的相似程度
(二)Jaccard 相似度:
A 用户(香蕉、苹果、鸭梨)
B 用户(苹果、橘子、桃子)
A∩B=苹果 1
A∪B=香蕉、苹果、鸭梨、橘子、桃子 5
相似度为1/5=0.2
# Step 1 文件整合 把不同文本整合到一起 import os import sys import math file_path_dir = './data' raw_path = './raw.data' idf_path = './idf.data' def read_file_handler(f): fd = open(f, 'r', encoding='utf-8') return fd file_raw_out = open(raw_path, 'w', encoding='utf-8') # 遍历整个原始数据目录,将零散的文章整合到一个文件中,便于后续数据处理 file_name = 0 for fd in os.listdir(file_path_dir): file_path = file_path_dir + '/' + fd content_list = [] file_fd = read_file_handler(file_path) for line in file_fd: content_list.append(line.strip()) content = '\t'.join([str(file_name), ' '.join(content_list)]) + '\n' file_raw_out.writelines(content) file_name += 1 file_raw_out.close() # Step 2 输出IDF docs_cnt = file_name wc_tulist = [] with open(raw_path, 'r', encoding='utf-8') as fd: for line in fd: # 遍历每一篇文章,文章=line ss = line.strip().split('\t') if len(ss) != 2: continue # 对文章的解析,区分出文章的名字和文章的内容 file_name, file_content = ss # 对文章的内容进行切词,因为内容已经按“ ”空格区分好了,所以直接按空格做split就好 word_list = file_content.strip().split(' ') # 去重:对于idf,只关心词有没有出现在文章中,至于出现多少次,并不关心 word_set = set(word_list) for word in word_set: # 对于每个关键词,打一个标记“1”,来标识该次出现过 wc_tulist.append((word, '1')) # 将内容输出到指定目标文件中去 file_idf_out = open(idf_path, 'w', encoding='utf-8') # 按照词的字典序,进行排序 wc_sort_tulist = sorted(wc_tulist, key=lambda x: x[0]) current_word = None sum = 0 for tu in wc_sort_tulist: word, val = tu if current_word == None: current_word = word if current_word != word: # 通过idf计算公式,得到每个关键词的idf score idf = math.log(float(docs_cnt) / (float(sum) + 1.0)) idf公式 content = '\t'.join([current_word, str(idf)]) + '\n' file_idf_out.write(content) current_word = word sum = 0 sum += int(val) idf = math.log(float(docs_cnt) / (float(sum) + 1.0)) content = '\t'.join([current_word, str(idf)]) + '\n' file_idf_out.write(content) file_idf_out.close() # Step 3 input_str = '我们 带来 阿里巴巴 希望 差 我们 我们 我们' token_idf_dict = {} # 将idf字典加载到内存 with open(idf_path, 'r', encoding='utf-8') as fd: for line in fd: ss = line.strip().split('\t') if len(ss) != 2: continue token, idf_score = ss token_idf_dict[token] = idf_score def get_tfidf(input_str): token_dict = {} # 对输入字符串的每一个词,计算tf for t in input_str.strip().split(' '): if t not in token_dict: token_dict[t] = 1 else: token_dict[t] += 1 # res_tu_list = [] for k, v in token_dict.items(): tf_score = token_dict[k] if k not in token_idf_dict: continue idf_score = token_idf_dict[k] tf_idf = tf_score * float(idf_score) yield (k, tf_idf) for k, v in get_tfidf(input_str): print(k, v) 求cos # Step 1: Cosine input1_str = '我们 带来 阿里巴巴 希望 差 差 差' # input2_str = '我们 带来 阿里巴巴 好 好 好' # input2_str = '我们 带来 搜狐 好 好 好' input2_str = '你们 带来 搜狐 希望 好 好 好' def cosine(input1_str, input2_str): t1_dict = {} sum = 0. for k, v in get_tfidf(input1_str): sum += pow(v, 2) sum = math.sqrt(sum) for k, v in get_tfidf(input1_str): t1_dict[k] = float(v / sum) sum = 0. for k, v in get_tfidf(input2_str): sum += pow(v, 2) sum = math.sqrt(sum) final_score = 0. for k, v in get_tfidf(input2_str): if k not in t1_dict: continue s1 = t1_dict[k] s2 = float(v / sum) final_score += s1 * s2 return final_score print(cosine(input1_str, input2_str)) #用Python手动实现TF-IDF算法 import math corpus = [ "what is the weather like today", "what is for dinner tonight", "this is a question worth pondering", "it is a beautiful day today" ] words = [] # 对corpus分词 for i in corpus: words.append(i.split()) # 如果有自定义的停用词典,我们可以用下列方法来分词并去掉停用词 # f = ["is", "the"] # for i in corpus: # all_words = i.split() # new_words = [] # for j in all_words: # if j not in f: # new_words.append(j) # words.append(new_words) # print(words) # 进行词频统计 def Counter(word_list): wordcount = [] for i in word_list: count = {} for j in i: if not count.get(j): count.update({j: 1}) elif count.get(j): count[j] += 1 wordcount.append(count) return wordcount wordcount = Counter(words) # 计算TF(word代表被计算的单词,word_list是被计算单词所在文档分词后的字典) def tf(word, word_list): return word_list.get(word) / sum(word_list.values()) # 统计含有该单词的句子数 def count_sentence(word, wordcount): return sum(1 for i in wordcount if i.get(word)) # 计算IDF def idf(word, wordcount): return math.log(len(wordcount) / (count_sentence(word, wordcount) + 1)) # 计算TF-IDF def tfidf(word, word_list, wordcount): return tf(word, word_list) * idf(word, wordcount) p = 1 for i in wordcount: print("part:{}".format(p)) p = p+1 for j, k in i.items(): print("word: {} ---- TF-IDF:{}".format(j, tfidf(j, i, wordcount)))