机器学习:TF-IDF算法【词频-逆文本频率=TF×IDF】【用以评估一个词对于一个文档集中的其中一份文档的重要】【词频:词或短语在一篇文章中出现的概率】【逆文本频率:总文档数量/该词出现的文档数量】

一、什么是TF-IDF

TF-IDF是一种统计方法,用以评估一个词对于一个文件集或一个语料库中的其中一份文件重要程度。可用于关键词提取。

字词的重要性随着它在文件中出现的次数成正比增加,但同时会随着它在语料库中出现的频率成反比下降。TF-IDF加权的各种形式常被搜索引擎应用,作为文件与用户查询之间相关程度的度量或评级。除了TF-IDF以外,因特网上的搜索引擎还会使用基于链接分析的评级方法,以确定文件在搜寻结果中出现的顺序。

TF-IDF实际上是:TF * IDF。主要思想是:如果某个词或短语在一篇文章中出现的频率高(即TF高),并且在其他文章中很少出现(即IDF高),则认为此词或者短语具有很好的类别区分能力,适合用来分类。

通俗理解TF-IDF就是:

  • TF刻画了词语t对某篇文档的重要性
  • IDF刻画了词语t对整个文档集的重要性,是对TF的惩罚项;

二、名词解释和数学算法

TF是词频(Term Frequency)表示词条在文本中出现的频率公式
机器学习:TF-IDF算法【词频-逆文本频率=TF×IDF】【用以评估一个词对于一个文档集中的其中一份文档的重要】【词频:词或短语在一篇文章中出现的概率】【逆文本频率:总文档数量/该词出现的文档数量】_第1张图片
IDF是逆文档频率(Inverse Document Frequency):某一特定词语的IDF,可以由总文件数目除以包含该词语的文件的数目,再将得到的商取对数得到。如果包含词条t的文档越少, IDF越大,则说明词条具有很好的类别区分能力
机器学习:TF-IDF算法【词频-逆文本频率=TF×IDF】【用以评估一个词对于一个文档集中的其中一份文档的重要】【词频:词或短语在一篇文章中出现的概率】【逆文本频率:总文档数量/该词出现的文档数量】_第2张图片

TF× IDF \text{TF× IDF} TF× IDF:该词在该篇文档中的重要性程度

  • TF-IDF实际上是:TF * IDF某一特定文件内的高词语频率,以及该词语在整个文件集合中的低文件频率,可以产生出高权重的TF-IDF。因此,TF-IDF倾向于过滤掉常见的词语,保留重要的词语。
  • TF× IDF的主要思想是:如果某个词或短语在一篇文章中出现的概率高,并且在其他文章中很少出现,则认为此词或者短语具有很好的类别区分能力,适合用来分类。
  • TF× IDF作用:用以评估一字词对于一个文件集或一个语料库中的其中一份文件的重要程度。
  • TF× IDF是分类机器学习算法的重要依据。

TF× IDF方法比count方法更好一些,但是在自然语言处理过程中会使用比TF× IDF更好的方法【使用单字,使用n-garm,使用BM25,使用word2vec等,让其结果更加准确】。

三、TF-IDF应用举例

有很多不同的数学公式可以用来计算TF-IDF。这边的例子以上述的数学公式来计算。词频 (TF) 是一词语出现的次数除以该文件的总词语数。

  • 假如一篇文件的总词语数是100个,而词语“中国”出现了8次,那么“中国””一词在该文件中的词频就是8/100=0.08。
  • 一个计算文件频率 (IDF) 的方法是文件集里包含的文件总数除以测定有多少份文件出现过“中国””一词。所以,如果“中国””一词在1,000份文件出现过,而文件总数是10,000,000份的话,其逆向文件频率就是 lg(10,000,000 / 1,000)=4。
  • 最后的TF-IDF的分数为0.08 * 4=0.32

四、TF-IDF应用

搜索引擎
关键词提取
文本相似性
文本摘要

五、TF× IDF算法代码实现

import numpy as np
import pandas as pd
import math

### 1. 定义数据和预处理
docA = "The cat sat on my bed"
docB = "The dog sat on my knees"

bowA = docA.split(" ")
bowB = docB.split(" ")
print('bowA = {0}'.format(bowA))
print('bowB = {0}'.format(bowB))

# 构建词库
wordSet = set(bowA).union(set(bowB))
print('wordSet = {0}'.format(wordSet))

### 2. 进行词数统计
# 用统计字典来保存词出现的次数
wordDictA = dict.fromkeys(wordSet, 0)
wordDictB = dict.fromkeys(wordSet, 0)

# 遍历文档,统计词数
for word in bowA:
    wordDictA[word] += 1
for word in bowB:
    wordDictB[word] += 1

print('\nwordDataFrame = \n{0}'.format(pd.DataFrame([wordDictA, wordDictB])))


### 3. 计算词频tf=该词在文章A中的词频
def computeTF(wordDict, bow):
    # 用一个字典对象记录tf,把所有的词对应在bow文档里的tf都算出来
    tfDict = {}
    nbowCount = len(bow)
    for word, count in wordDict.items():
        tfDict[word] = count / nbowCount
    return tfDict


### 4. 计算逆文档频率idf=log(总文档数量/该词出现的文档数量)
def computeIDF(wordDictList):
    # 用一个字典对象保存idf结果,每个词作为key,初始值为0
    idfDict = dict.fromkeys(wordDictList[0], 0)
    N = len(wordDictList)
    for wordDict in wordDictList:
        # 遍历字典中的每个词汇,统计Ni
        for word, count in wordDict.items():
            if count > 0:
                # 先把Ni增加1,存入到idfDict
                idfDict[word] += 1
    # 已经得到所有词汇i对应的Ni,现在根据公式把它替换成为idf值
    for word, ni in idfDict.items():
        idfDict[word] = math.log10((N + 1) / (ni + 1))

    return idfDict


### 5. 计算TF× IDF
def computeTFIDF(tf, idfs):
    tfidf = {}
    for word, tfval in tf.items():
        tfidf[word] = tfval * idfs[word]
    return tfidf


if __name__ == '__main__':
    tfA = computeTF(wordDictA, bowA)
    tfB = computeTF(wordDictB, bowB)
    print('\ntfA = {0}'.format(tfA))
    print('tfB = {0}'.format(tfB))

    idfs = computeIDF([wordDictA, wordDictB])
    print('\nidfs = {0}'.format(idfs))

    tfidfA = computeTFIDF(tfA, idfs)
    tfidfB = computeTFIDF(tfB, idfs)
    print('\ntfidfDataFrame = \n{0}'.format(pd.DataFrame([tfidfA, tfidfB])))

打印结果:

bowA = ['The', 'cat', 'sat', 'on', 'my', 'bed']
bowB = ['The', 'dog', 'sat', 'on', 'my', 'knees']

wordSet = {'The', 'my', 'sat', 'knees', 'cat', 'on', 'bed', 'dog'}

wordDataFrame = 
   The  bed  cat  dog  knees  my  on  sat
0    1    1    1    0      0   1   1    1
1    1    0    0    1      1   1   1    1

tfA = {'The': 0.16666666666666666, 'my': 0.16666666666666666, 'sat': 0.16666666666666666, 'knees': 0.0, 'cat': 0.16666666666666666, 'on': 0.16666666666666666, 'bed': 0.16666666666666666, 'dog': 0.0}
tfB = {'The': 0.16666666666666666, 'my': 0.16666666666666666, 'sat': 0.16666666666666666, 'knees': 0.16666666666666666, 'cat': 0.0, 'on': 0.16666666666666666, 'bed': 0.0, 'dog': 0.16666666666666666}

idfs = {'The': 0.0, 'my': 0.0, 'sat': 0.0, 'knees': 0.17609125905568124, 'cat': 0.17609125905568124, 'on': 0.0, 'bed': 0.17609125905568124, 'dog': 0.17609125905568124}

tfidfDataFrame = 
   The       bed       cat       dog     knees   my   on  sat
0  0.0  0.029349  0.029349  0.000000  0.000000  0.0  0.0  0.0
1  0.0  0.000000  0.000000  0.029349  0.029349  0.0  0.0  0.0

六、TF× IDF算法案例

# 文本特征抽取(TF× IDF方式)
from sklearn.feature_extraction.text import TfidfVectorizer
import jieba

def cutword():
    """中文分词"""
    con1 = jieba.cut("今天很残酷,明天更残酷,后天很美好,但绝对大部分是死在明天晚上,所以每个人不要放弃今天。")
    con2 = jieba.cut("我们看到的从很远星系来的光是在几百万年之前发出的,这样当我们看到宇宙时,我们是在看它的过去。")
    con3 = jieba.cut("如果只用一种方式了解某样事物,你就不会真正了解它。了解事物真正含义的秘密取决于如何将其与我们所了解的事物相联系。")
    # 转换成列表
    content1 = list(con1)
    content2 = list(con2)
    content3 = list(con3)
    # 把列表转换成字符串
    c1 = ' '.join(content1)
    c2 = ' '.join(content2)
    c3 = ' '.join(content3)
    return c1, c2, c3


def tfidfvec():
    """中文特征值化"""
    c1, c2, c3 = cutword()
    print('c1 = \n', c1)
    print('c2 = \n', c2)
    print('c3 = \n', c3)
    tfidf = TfidfVectorizer()
    data = tfidf.fit_transform([c1, c2, c3])
    print('tfidf.get_feature_names() = \n', tfidf.get_feature_names())
    print('data.toarray() = \n', data.toarray())

    return None


if __name__ == "__main__":
    tfidfvec()

打印结果:

c1 = 
 今天 很 残酷 , 明天 更 残酷 , 后天 很 美好 , 但 绝对 大部分 是 死 在 明天 晚上 , 所以 每个 人 不要 放弃 今天 。
c2 = 
 我们 看到 的 从 很 远 星系 来 的 光是在 几百万年 之前 发出 的 , 这样 当 我们 看到 宇宙 时 , 我们 是 在 看 它 的 过去 。
c3 = 
 如果 只用 一种 方式 了解 某样 事物 , 你 就 不会 真正 了解 它 。 了解 事物 真正 含义 的 秘密 取决于 如何 将 其 与 我们 所 了解 的 事物 相 联系 。
tfidf.get_feature_names() = 
 ['一种', '不会', '不要', '之前', '了解', '事物', '今天', '光是在', '几百万年', '发出', '取决于', '只用', '后天', '含义', '大部分', '如何', '如果', '宇宙', '我们', '所以', '放弃', '方式', '明天', '星系', '晚上', '某样', '残酷', '每个', '看到', '真正', '秘密', '绝对', '美好', '联系', '过去', '这样']
data.toarray() = 
[
 	[0.         0.         0.21821789 0.         0.         0.      0.43643578 0.         0.         0.         0.      0.    0.21821789 0.         0.21821789 0.         0.         0.     0.         0.21821789 0.21821789 0.         0.43643578 0.     0.21821789 0.         0.43643578 0.21821789 0.         0.     0.         0.21821789 0.21821789 0.         0.         0.        ]
 	[0.         0.         0.         0.2410822  0.         0.     0.         0.2410822  0.2410822  0.2410822  0.       0.     0.         0.         0.         0.         0.         0.2410822     0.55004769 0.         0.         0.         0.         0.2410822      0.         0.         0.         0.         0.48216441 0.      0.         0.         0.         0.         0.2410822  0.2410822 ]
 	[0.15698297 0.15698297 0.         0.         0.62793188 0.47094891     0.         0.         0.         0.         0.15698297 0.15698297     0.         0.15698297 0.         0.15698297 0.15698297 0.      0.1193896  0.         0.         0.15698297 0.         0.      0.         0.15698297 0.         0.         0.         0.31396594     0.15698297 0.         0.         0.15698297 0.         0.        ]
]

七、TF-IDF的理论依据及不足

TF-IDF算法是建立在这样一个假设之上的:对区别文档最有意义的词语应该是那些在文档中出现频率高,而在整个文档集合的其他文档中出现频率少的词语,所以如果特征空间坐标系取TF词频作为测度,就可以体现同类文本的特点。另外考虑到单词区别不同类别的能力,TF-IDF法认为一个单词出现的文本频数越小,它区别不同类别文本的能力就越大。因此引入了逆文本频度IDF的概念,以TF和IDF的乘积作为特征空间坐标系的取值测度,并用它完成对权值TF的调整,调整权值的目的在于突出重要单词,抑制次要单词。

但是在本质上IDF是一种试图抑制噪音的加权 ,并且单纯地认为文本频数小的单词就越重要,文本频数大的单词就越无用,显然这并不是完全正确的。

IDF的简单结构并不能有效地反映单词的重要程度和特征词的分布情况,使其无法很好地完成对权值调整的功能,所以TF-IDF法的精度并不是很高。

此外,在TF-IDF算法中并没有体现出单词的位置信息,对于Web文档而言,权重的计算方法应该体现出HTML的结构特征。特征词在不同的标记符中对文章内容的反映程度不同,其权重的计算方法也应不同。因此应该对于处于网页不同位置的特征词分别赋予不同的系数,然后乘以特征词的词频,以提高文本表示的效果。

八、TF-IDF变体

机器学习:TF-IDF算法【词频-逆文本频率=TF×IDF】【用以评估一个词对于一个文档集中的其中一份文档的重要】【词频:词或短语在一篇文章中出现的概率】【逆文本频率:总文档数量/该词出现的文档数量】_第3张图片

变种1:通过对数函数避免 TF 线性增长

很多人注意到 TF 的值在原始的定义中没有任何上限。虽然我们一般认为一个文档包含查询关键词多次相对来说表达了某种相关度,但这样的关系很难说是线性的。拿我们刚才举过的关于 “Car Insurance” 的例子来说,文档 A 可能包含 “Car” 这个词 100 次,而文档 B 可能包含 200 次,是不是说文档 B 的相关度就是文档 A 的 2 倍呢?其实,很多人意识到,超过了某个阈值之后,这个 TF 也就没那么有区分度了。

用 Log,也就是对数函数,对 TF 进行变换,就是一个不让 TF 线性增长的技巧。具体来说,人们常常用 1+Log(TF) 这个值来代替原来的 TF 取值。在这样新的计算下,假设 “Car” 出现一次,新的值是 1,出现 100 次,新的值是 5.6,而出现 200 次,新的值是 6.3。很明显,这样的计算保持了一个平衡,既有区分度,但也不至于完全线性增长。

变种2:标准化解决长文档、短文档问题

经典的计算并没有考虑 “长文档” 和“短文档”的区别。一个文档 A 有 3,000 个单词,一个文档 B 有 250 个单词,很明显,即便 “Car” 在这两个文档中都同样出现过 20 次,也不能说这两个文档都同等相关。对 TF 进行 “标准化”(Normalization),特别是根据文档的最大 TF 值进行的标准化,成了另外一个比较常用的技巧。

变种3:对数函数处理 IDF

第三个常用的技巧,也是利用了对数函数进行变换的,是对 IDF 进行处理。相对于直接使用 IDF 来作为 “惩罚因素”,我们可以使用 N+1 然后除以 DF 作为一个新的 DF 的倒数,并且再在这个基础上通过一个对数变化。这里的 N 是所有文档的总数。这样做的好处就是,第一,使用了文档总数来做标准化,很类似上面提到的标准化的思路;第二,利用对数来达到非线性增长的目的。

变种4:查询词及文档向量标准化

还有一个重要的 TF-IDF 变种,则是对查询关键字向量,以及文档向量进行标准化,使得这些向量能够不受向量里有效元素多少的影响,也就是不同的文档可能有不同的长度。在线性代数里,可以把向量都标准化为一个单位向量的长度。这个时候再进行点积运算,就相当于在原来的向量上进行余弦相似度的运算。所以,另外一个角度利用这个规则就是直接在多数时候进行余弦相似度运算,以代替点积运算。




参考资料:
TF-IDF算法介绍及实现
机器学习:生动理解TF-IDF算法
文本挖掘预处理之TF-IDF
Tf-Idf详解及应用
TF-IDF
TFIDF介绍
文本挖掘预处理之TF-IDF
机器学习之TF-IDF统计算法介绍与代码实现(篇五)

你可能感兴趣的:(机器学习/ML,机器学习,算法,人工智能,TF-IDF)