TF-IDF的理论与实践

1 前言

TF-IDF称为“词频-逆向文件频率”,是英文“term frequency–inverse document frequency”的缩写,它是NLP领域在做文本处理,信息检索等任务时常用到的一种经典算法,该算法的目的是为了衡量一个词的重要性。今天从算法的原理和实践进行介绍。

2 原理

在阐述原理前,可以先想想一个任务:给你几篇经济领域文章,让你找出文章中一些关键词。从统计学的角度考虑:你会觉得出现频率高的词更可能是关键词,这个统计词频的想法就是TF的思想;然而在统计词频后排序输出时,你可能发现有“的”、“一直”等这样跟经济没多大关联的词;在解决这个问题时,你可能想如果一个词在每篇文章都出现,就可以视该词像“的”这类助词一样,没什么代表性,不重要,而这个想法就是IDF的思想。

总的来说,TF-IDF的主要思想是:如果某个单词在一篇文章中出现的频率(TF值)很高,而在其他文章中很少出现(IDF值很大),就认为该词具有很高的代表性,对该篇文章更重要。

下面来介绍TF-IDF的数学表达公式:
(1) TF词频(Term Frequency)

其中,表示词w出现的频率(次数),表示整个语料库中所有词出现的次数,也可认为是语料库文本的长度。TF用来统计一个词出现的频率,出现的频率越高,一定程度上可认为越重要。

(2) IDF逆向文件频率(Inverse Document Frequency)

其中,表示文档的总数量,表示词出现的文档数量,加1是为了防止分母为0,一种平滑处理。简单来理解就是:总文件数目除以包含该词的文件的数目,再取对数即为该词的idf值。若包含词w的文档越少, IDF越大,则说明该词更具有代表性。

(3) TF-IDF

将词w计算出的tf值和idf值相乘,即为tf_idf值。在NLP任务中,可用tf_idf值将词向量化,也可用tf_idf值作为一个权重参与计算。TF-IDF用一句话来概括就是:过滤掉常见的词语,保留重要的词语。

但TF-IDF也存在缺陷:当一个关键词每篇文章都出现,那TF-IDF会把它当着不重要的词来处理。它是基于统计学的思想,是具备一定说服力和意义的,但不能解决所有问题,所以在使用时,还的结合具体任务来进行调整。

3 实践

3.1 任务说明

利用TF-IDF方法,找出医疗领域文本中tf_idf最大的top词。
file_corpus是我从网上收集的医疗领域语料库,比较小,约50M。
在统计词过程中,先过滤掉标点符号,长度为1的词,这样可以提高一下要找词的质量。在分词中,使用的是开源分词器jieba。

3.2 代码

import codecs
import jieba
import re
import math

def compute_tf_idf(file_dir,tf_idf_file):
    D=0 #文档的数量
    N=0 #词出现的总频率,也是文档的长度累积
    re_han = re.compile(u"([\u4E00-\u9FD5a-zA-Z0-9]+)")  #过滤掉标点符号之类的符号
    with codecs.open(file_dir ,'r' ,encoding='utf-8') as f:
        #统计每个词的tf,idf值,以字典的形式存储
        dataset=dict()
        for line in f:
            N+=len(line)
            D+=1
            tmp = []  #统计idf时,一条文本中词只统计一次,为了出重设置的变量
            blocks = re_han.split(line)
            for blk in blocks:
                if re_han.match(blk):
                    for w in jieba.cut(blk):
                        if len(w)>= 2:   #过滤掉长度为1的词
                            if w in dataset:
                                dataset[w]['tf'] += 1
                                if w not in tmp:
                                    dataset[w]['idf'] += 1
                                    tmp.append(w)  
                            else:
                                dataset.setdefault(w, {})
                                dataset[w]['tf'] = 1
                                dataset[w]['idf'] = 1
                                tmp.append(w)
#     print(D,N)
    
    #计算tf-idf值
    res=[]
    for key in dataset:
        tf=dataset[key]['tf']/N
        idf=math.log(D/(dataset[key]['idf']+1))
        tf_idf=tf*idf
        res.append([key,tf_idf])
    
    #排序输出
    res=sorted(res,key=lambda x:x[1],reverse=True)
    with codecs.open(tf_idf_file, 'w', encoding='utf-8') as f:
        for item in res:
            f.write(item[0]+'\t'+str(item[1])+'\n')

if __name__=="__main__":

    corpus_dir=r'F:\open_data\file_corpus.txt'
    tf_idf_dir=r'F:\tf_idf.txt'
    compute_tf_idf(corpus_dir,tf_idf_dir)

3.3 结果分析

运行上述的代码,输出的文件就是按tf_idf大小降序排列的词,下面是前20词的截图:

通过上面的显示,我们可以看出都是跟医疗相关的,其中“肿瘤”的tf-idf值最大。这前20一个词基本都是医学领域专有词,整体来说,tf-idf找领域关键词是有效果的,但也存在缺陷:因为它的最终结果是由tf与idf共同决定的,当tf特别大,idf即使很小,可能最终的结果也是靠前的。这就能解释为何“出现”、“发生”这类词的tf-idf值很高(排在20~40范围内)的原因,而这些可能并不是我们想要的。所以,面对具体任务下,我们可以在tf-idf的基础上,再加一些限定条件,比如该词是不是领域专有词,或者其他统计指标,这样得到的效果会更好。

你可能感兴趣的:(TF-IDF的理论与实践)