关键词权重度量:TF-IDF算法原理及其Python实现


算法思想

TF-IDF(term frequency–inverse document frequency,词频-逆向文件频率) 是用于信息检索与文本挖掘的重要算法,其中TF用于度量关键词在文档中的重要性,IDF用于度量关键词在全文档中的重要性, 即文档中某关键词的重要性,与它在当前文档中的频率成正比,而与包含它的文档数成反比。

TF-IDF的主要思想是,若一个关键词在一篇文档中出现的频率高,而在其他文档中很少出现,则该关键词可较好的反应当前文档的特征。


算法原理

度量某文档和查询的相关性,最简单的方法是利用各查询关键词在该文档中出现的总词频(Term Frequency,TF)

具体地,对于包含M个关键词的w1, w2,…wn查询,各关键词在某文档中出现的频率分别为:TF(w1), TF(w2),…,TF(wM)​,则该文档与查询的相关性为:
T F ( w 1 ) + T F ( w 2 ) + ⋯ + T F ( w M ) TF(w_1)+TF(w_2)+\cdots+TF(w_M) TF(w1)+TF(w2)++TF(wM)

某些关键词可能同时出现在多篇文档中,该类关键词的主题预测能力较弱,可见,仅使用TF不能很好的反应文档与查询的相关性。

关键词的主题预测能力越强,在度量与文档的相关性时,其权重应该越大。 也就是说,若某关键词在较少文档中出现,则该关键词的权重应该较高,如关键词原子能的权重大于应用的权重。因此,利用包含某关键词的文档数,修正仅用词频TF度量该关键词的权重。

在信息检索领域,使用逆文本频率(Inverse Document Frequency, IDF) 表示关键词的主题预测能力(权重),表示为
I D F ( w ) = log ⁡ D D F ( w ) IDF(w)=\log\frac{D}{DF(w)} IDF(w)=logDF(w)D

其中D为全部文档数,DF(w)为包含关键词w的文档数。

利用IDF的思想,文档与查询的相关性计算由简单的词频求和,变为以IDF为权重的加权求和,即
T F ( w 1 ) ⋅ I D F ( w 1 ) + T F ( w 2 ) ⋅ I D F ( w 2 ) + ⋯ + T F ( w M ) ⋅ I D F ( w M ) TF(w_1)\cdot IDF(w_1)+ TF(w_2)\cdot IDF(w_2)+\cdots+ TF(w_M)\cdot IDF(w_M) TF(w1)IDF(w1)+TF(w2)IDF(w2)++TF(wM)IDF(wM)


TF-IDF与信息论

一个查询中,每个关键词的权重应该反应其为查询提供的信息量,简单的方法就是,用关键词的信息量,作为它在查询中的权重,即
I ( w ) = − P ( w ) log ⁡ P ( w )   = − T F ( w ) N log ⁡ T F ( w ) N = T F ( w ) N log ⁡ N T F ( w ) \begin{aligned} I(w) & =-P(w)\log P(w) \\\,\\ & = -\frac{{TF}(w)}{N}\log\frac{{TF}(w)}{N}=\frac{{TF}(w)}{N}\log\frac{N}{{TF}(w)} \end{aligned} I(w)=P(w)logP(w)=NTF(w)logNTF(w)=NTF(w)logTF(w)N

其中N​为整个语料库中的总词数,是可忽略的常数,此时
I ( w ) = T F ( w ) log ⁡ N T F ( w ) I(w)={TF}(w)\log\frac{N}{{TF}(w)} I(w)=TF(w)logTF(w)N

若两个关键词在全文档中出现的频率相同,但第一个关键词集中分布在少数文章中,而第二个关键词分布在多篇文章中,显然,第一个关键词具有更好的主题预测能力,应赋予更高的查询权重。

为此,提出以下假设(总文档数D,总词数N​,包含关键词w​的文档数DF(w)​):

  • 每个文档含词数基本相同,即
    M = N D = ∑ w T F ( w ) D M=\dfrac{N}{D}=\dfrac{\sum_w{TF}(w)}{D} M=DN=DwTF(w)

  • 每个关键词一旦在文档中出现,不论其出现多少次,权重都相同,即关键词w在文档中未出现,则权重为0;否则,则为
    c ( w ) = T F ( w ) D F ( w ) c(w)=\dfrac{TF(w)}{DF(w)} c(w)=DF(w)TF(w)

因此,关键词w的信息量
I ( w ) = T F ( w ) log ⁡ N T F ( w ) = T F ( w ) log ⁡ M D c ( w ) ⋅ D F ( w ) = T F ( w ) [ log ⁡ D D F ( w ) + log ⁡ M c ( w ) ] \begin{aligned} I(w) & = {TF}(w)\log\frac{N}{{TF}(w)}= {TF}(w)\log\frac{MD}{c(w)\cdot DF(w)} \\ & = {TF}(w)\left[\log\frac{D}{DF(w)}+\log\frac{M}{c(w)}\right] \end{aligned} I(w)=TF(w)logTF(w)N=TF(w)logc(w)DF(w)MD=TF(w)[logDF(w)D+logc(w)M]

=>
T F − I D F ( w ) = I ( w ) − T F ( w ) log ⁡ M c ( w ) {TF-IDF}(w)=I(w)-{TF}(w)\log\frac{M}{c(w)} TFIDF(w)=I(w)TF(w)logc(w)M

易知,关键词w的TF-IDF值,与其信息量成正比;又由于M>c(w),知关键词w的TF-IDF值,与其在文档中出现的平均次数成反比,这些结论完全符合信息论。


平滑处理

经过平滑处理后, IDF的最终计算公式如下:
I D F ( w ) = log ⁡ N + 1 D F ( w ) + 1 + 1 IDF(w) = \log\frac{N+1}{DF(w)+1} + 1 IDF(w)=logDF(w)+1N+1+1

  • log项中分子项和分母项均加1,表示虚拟增加一篇包含任意词的文档,避免分母项为0;
  • IDF的最终值加1,避免某单词在所有文档中出现时,IDF的值为0,即不忽略出现在所有文档中的词;

正则化处理

sklearn中类TfidfTransformer默认对文档的TF-IDF特征向量做l2正则化,即某文档的TF-IDF特征向量为v,则
V n o r m = v ∣ ∣ v ∣ ∣ 2 = v v 1 2 + v 2 2 + ⋯ + v n 2 V_{norm}=\frac{v}{||v||_2}=\frac{v}{\sqrt{v_1^2+v_2^2+\cdots + v_n^2}} Vnorm=v2v=v12+v22++vn2 v

若单词表为{w1, w2, w3},文档A=(w1, w2, w2),B=(w1, w2, w3),且w1, w2, w3IDF值相同,则未正则化时
T F − I D F ( A ) = ( 0.333 , 0.666 , 0 ) ⋅ I D F ( w ) T F − I D F ( B ) = ( 0.333 , 0.333 , 0.333 ) ⋅ I D F ( w ) \begin{aligned} & TF-IDF(A) = (0.333, 0.666, 0)\cdot IDF(w) \\ & TF-IDF(B) = (0.333, 0.333, 0.333)\cdot IDF(w) \end{aligned} TFIDF(A)=(0.333,0.666,0)IDF(w)TFIDF(B)=(0.333,0.333,0.333)IDF(w)

此时,文档A、B中单词w1TF-IDF值相同。

若进行l2正则化,则
T F − I D F ( A ) l 2 = ( 0.447 , 0.894 , 0 ) T F − I D F ( B ) l 2 = ( 0.577 , 0.577 , 0.577 ) \begin{aligned} & TF-IDF(A)_{l2} = (0.447, 0.894, 0) \\ & TF-IDF(B)_{l2} = (0.577, 0.577, 0.577) \end{aligned} TFIDF(A)l2=(0.447,0.894,0)TFIDF(B)l2=(0.577,0.577,0.577)

可见文档B中w1TF-IDF值(权重)更大,正则化后的意义为:考虑文档的TF-IDF特征分布,增加不同权重之间的差异。

不失一般性,文档A、B中正则化后w1的TF-IDF分别为
T F − I D F ( A w 1 ) l 2 = T F ( A w 1 ) T F ( A w 1 ) 2 + T F ( A w 2 ) 2   T F − I D F ( B w 1 ) l 2 = T F ( B w 1 ) T F ( B w 1 ) 2 + T F ( B w 2 ) 2 + + T F ( B w 3 ) 2 TF-IDF(A_{w_1})_{l2}=\frac{TF(A_{w_1})}{\sqrt{TF(A_{w_1})^2+TF(A_{w_2})^2}}\\\,\\ TF-IDF(B_{w_1})_{l2}=\frac{TF(B_{w_1})}{\sqrt{TF(B_{w_1})^2+TF(B_{w_2})^2++TF(B_{w_3})^2}} TFIDF(Aw1)l2=TF(Aw1)2+TF(Aw2)2 TF(Aw1)TFIDF(Bw1)l2=TF(Bw1)2+TF(Bw2)2++TF(Bw3)2 TF(Bw1)

如TF(A_w1) = TF(B_w1),且TF之和为1,知
T F ( A w 2 ) = T F ( B w 2 ) + T F ( B w 3 ) TF(A_{w_2})=TF(B_{w_2})+TF(B_{w_3}) TF(Aw2)=TF(Bw2)+TF(Bw3)

推导出
T F ( A w 2 ) 2 = T F ( B w 2 ) 2 + T F ( B w 3 ) 2 + 2 T F ( B w 2 ) ⋅ T F ( B w 3 ) ≥ T F ( B w 2 ) 2 + T F ( B w 3 ) 2 \begin{aligned} TF(A_{w_2})^2 & =TF(B_{w_2})^2+TF(B_{w_3})^2+2TF(B_{w_2}) \cdot TF(B_{w_3})\\ & \geq TF(B_{w_2})^2+TF(B_{w_3})^2 \end{aligned} TF(Aw2)2=TF(Bw2)2+TF(Bw3)2+2TF(Bw2)TF(Bw3)TF(Bw2)2+TF(Bw3)2

进而,推导出
T F − I D F ( A w 1 ) l 2 ≤ T F − I D F ( B w 1 ) l 2 TF-IDF(A_{w_1})_{l2} \leq TF-IDF(B_{w_1})_{l2} TFIDF(Aw1)l2TFIDF(Bw1)l2

当前仅当TF(B_w2) = 0或TF(B_w3) = 0,即B中w2或w3的频率为0时,等式成立。

算法实现

算法的实现参考了sklearn.feature_extraction.text中的CountVectorizerTfidfVectorizer类,如下:

import re
from collections import defaultdict

from sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer
import numpy as np
from scipy.sparse import csr_matrix, spdiags
from scipy.sparse.linalg import norm

PTN_SYMBOL = re.compile(r'[.!?\'",]')


def tokenize(doc):
    """
    英文分词,小写输出
    """
    for word in PTN_SYMBOL.sub(' ', doc).split(' '):
        if word and word != ' ':
            yield word.lower()


def count_vocab(raw_documents):
    """
    返回文档词频的稀疏矩阵
    参考sklearn.feature_extraction.text.CountVectorizer._count_vocab

    矩阵大小:M*N, M个文档, 共计N个单词

    :param raw_documents: ['Hello world.', 'Hello word', ...]
    :return: csc_matrix, vocabulary
    """
    vocab = {}
    data, indices, indptr = [], [], [0]

    for doc in raw_documents:
        doc_feature = defaultdict(int)
        for term in tokenize(doc):
            # 词在词表中的位置
            index = vocab.setdefault(term, len(vocab))
            # 统计当前文档的词频
            doc_feature[index] += 1
        # 存储当前文档的词及词频
        indices.extend(doc_feature.keys())
        data.extend(doc_feature.values())
        # 累加词数
        indptr.append(len(indices))

    # 构造稀疏矩阵
    X = csr_matrix((data, indices, indptr), shape=(len(indptr) - 1, len(vocab)), dtype=np.int64)

    # 将单词表排序,同时更新压缩矩阵数据的位置
    map_index = np.empty(len(vocab), dtype=np.int32)
    for new_num, (term, old_num) in enumerate(sorted(vocab.items())):
        vocab[term] = new_num
        map_index[old_num] = new_num
    X.indices = map_index.take(X.indices, mode='clip')

    X.sort_indices()

    return X, vocab


def tfidf_transform(X, smooth_idf=True, normalize=True):
    """
    将词袋矩阵转换为TF-IDF矩阵

    :param X: 压缩的词袋矩阵 M*N, 文本数M, 词袋容量N
    :param smooth_idf: 是否对DF平滑处理
    :param normalize: 是否对TF-IDF执行l2标准化
    :return: TF-IDF压缩矩阵(csc_matrix)
    """
    n_samples, n_features = X.shape

    df = np.bincount(X.indices, minlength=X.shape[1])
    df += int(smooth_idf)
    new_n_samples = n_samples + int(smooth_idf)
    idf = np.log(float(new_n_samples) / df) + 1.0

    # 对角稀疏矩阵N*N,元素值对应单词的IDF
    idf_diag = spdiags(idf, diags=0, m=n_features, n=n_features, format='csr')

    # 等价于 DF * IDF
    X = X * idf_diag

    # 执行l2正则化
    if normalize:
        norm_l2 = 1. / norm(X, axis=1)
        tmp = spdiags(norm_l2, diags=0, m=n_samples, n=n_samples, format='csr')
        X = tmp * X

    return X


if __name__ == '__main__':
    # 源文档
    raw_documents = [
        'This is the first document.',
        'This is the second second document.',
        'And the third one.',
        'Is this the first document?',
    ]
    # 转换为词袋模型
    X, vocab = count_vocab(raw_documents)
    # X = CountVectorizer().fit_transform(raw_documents)
    """
    >> vocab
    {'this': 8, 'is': 3, 'the': 6, 'first': 2, 'document': 1, 'second': 5, 'and': 0, 'third': 7, 
    'one': 4}
    
    >> X.toarray()
    [[0 1 1 1 0 0 1 0 1]
     [0 1 0 1 0 2 1 0 1]
     [1 0 0 0 1 0 1 1 0]
     [0 1 1 1 0 0 1 0 1]]
    """

    # 计算TF-IDF
    tfidf_x = tfidf_transform(X)
    # tfidf_x = TfidfVectorizer().fit_transform(raw_documents)
    """
    >> tfidf_x.toarray()
    [   [0.       0.439       0.542       0.439       0.          0.      0.359   0.         0.439]
        [0.       0.272       0.          0.272       0.          0.853   0.223   0.         0.272]
        [0.553    0.          0.          0.          0.553       0.      0.288   0.553      0.   ]
        [0.       0.439       0.542       0.439       0.          0.      0.359   0.         0.439]	]
    """
  • 4
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: TF-IDF算法是一种常用的文本处理算法,可以用于计算文本中每个单词的要程度。在Python中,可以使用scikit-learn库来实现TF-IDF算法。为了改进TF-IDF算法的效果,可以考虑以下几点: 1. 去除停用词:停用词是指在文本中频繁出现但没有实际意义的词语,如“的”、“是”等。在TF-IDF算法中,去除停用词可以减少噪声,提高算法的准确性。 2. 调整TF-IDF算法中,词频和逆文档频率的默认是相等的,但实际上不同的文本可能需要不同的。可以通过调整来提高算法的效果。 3. 使用n-gram模型:n-gram模型是指将文本中的词语按照一定的顺序组合成n个词语的模型。使用n-gram模型可以更好地捕捉文本中的语义信息,提高算法的准确性。 4. 使用词根还原:词根还原是指将单词还原为其原始形式,如将“running”还原为“run”。使用词根还原可以减少单词形态的差异,提高算法的准确性。 以上是TF-IDF算法改进的一些方法,可以根据具体情况选择适合自己的方法来实现算法。 ### 回答2: TF-IDF算法是信息检索中常用的一种技术,它能够帮助我们对文本数据进行快速、准确的搜索。它的核心思想是通过计算每个单词在文档集合中出现的频率和逆文档频率,来衡单词的要程度,从而得出每份文档的关键词。这样,我们就能用这些关键词来快速地判断一份文档与搜索实例的相关性。 Python作为一种广泛使用的编程语言,在实现TF-IDF算法方面具有一定优势。下面就来介绍一下如何改进Python实现TF-IDF算法。 1. 加载数据 首先,需要将文本数据加载到Python中。常用的方法是使用pandas库中的read_csv函数。 2. 预处理 在计算TF-IDF之前,需要进行一些预处理。首先要将所有文本都转换成小写字母,以避免大小写带来的误差。同时,还需要去除一些停用词,例如“the”、“a”、“an”等等。这些词并不会对文本的相关性产生太大的影响,反而会干扰计算。 3. 分词 将文本进行分词,是TF-IDF算法的一个要步骤。在Python中,可以使用NLTK(自然语言工具包)来进行分词操作。NLTK提供了许多分词方法,例如最简单的word_tokenize函数。此外,还可以使用正则表达式的方法进行分词,更加灵活。 4. 计算词频 计算每个单词在文档集合中的频率,是TF-IDF算法的第一部分。在Python中,可以使用collections库的Counter函数来计算单词出现的次数。 5. 计算逆文档频率 计算每个单词的逆文档频率,是TF-IDF算法的第二部分。在Python中,可以使用math库的log函数来计算自然对数。然后,将所有文档中的单词频率除以单词的逆文档频率,即可得到TF-IDF值。 6. 排序 对计算出的TF-IDF值进行排序,并筛选出一定数量的关键词。在Python中,可以使用pandas库的sort_values函数进行排序。此外,也可以使用Python自带的sorted函数,更加灵活。 总之,TF-IDF算法Python中的实现,需要依次进行数据加载、预处理、分词、计算词频、计算逆文档频率、排序等一系列步骤。通过适当的改进,可以使这些步骤更加高效、精确。这样,就能够为我们提供更加可靠、快速的检索服务。 ### 回答3: tf-idf算法是一种常用的文本挖掘算法,用于计算文档中每个词语的要性,它基于两个统计量:词频(term frequency)和逆文档频率(inverse document frequency)。在实际应用中,tf-idf算法往往需要与其他算法一起使用,以提高算法的准确性和效率。 为了改进tf-idf算法python实现,我们可以从以下几个方面入手: 1. 数据预处理:在使用tf-idf算法之前,需要对文本数据进行预处理,包括分词、去停用词、词干提取等。可以使用已有的分词库,如jieba分词库,来对文本进行分词,并使用NLTK库来对文本进行预处理。 2. 选择合适的计算方法:如果使用普通的TF-IDF算法,容易忽略一些要的词语。因此,我们可以使用改进的TF-IDF算法,如Okapi BM25、Full-tF、Bidirectional TF-IDF、Sensitive TF-IDF等,来计算每个词语的。 3. 使用稀疏矩阵压缩数据:当文本数据量非常大时,使用稀疏矩阵压缩数据可以减少内存占用,提高算法的运行效率。可以使用Python的SciPy库来实现稀疏矩阵。 4. 优化算法实现tf-idf算法实现可以使用多线程技术,以提高算法的运行速度。可以使用Python的线程库,如threading和multiprocessing,来实现多线程计算。 总之,改进tf-idf算法python实现可以通过优化数据预处理、选择合适的计算方法、使用稀疏矩阵压缩数据和优化算法实现这几个方面来实现。这些改进可以提高算法的准确性和效率,使得tf-idf算法更加适用于实际应用场景。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值