TF-IDF算法及实现

最近在看莫烦的NLP的课程,其中关于TF-IDF算法实际编程的时候还是遇到一些小问题,主要是计算方法问题,整理后放上来,加深记忆。

TF-IDF的计算方法有很多种,这里主要用的是SKlearn中的计算方式,根示例代码不太一样,费了点劲儿才搞明白。

目录

一、 TF-IDF算法简介

1. TF:Term Frequency,词频

2. IDF:Inverse Document Frequency,逆向文本频率

3. TF-IDF = TF * IDF

二、代码示例

1. 自编函数

2. 使用sklearn库



一、 TF-IDF算法简介

TF-IDF(term frequency–inverse document frequency,词频-逆向文件频率)算法是一种用于信息检索与文本数据挖掘的常用加权技术。它用统计学方法评估一个词对某篇文章的重要程度,常用来提取文章的关键词,算法简单高效,因此常用于信息检索的粗排阶段。

TF-IDF算法的核心思想是通过统计的方法,评估一个词对一个文件集或者语料库的重要程度。一个词的重要程度跟它在文章中出现的次数成正比,跟它在语料库出现的次数成反比。这种计算方式能有效避免常用词对关键词的影响,提高了关键词与文章之间的相关性。

1. TF:Term Frequency,词频

指的是某个词在某篇文章中出现的次数, 计算公式为:TF = 某词在某文档中出现的次数

也就是说,就一篇文章局部来看,一个单词出现的次数越多就越重要,但这并不是绝对的。比如,a、the、of等单词出现的次数一定不会少,很显然它们并没有什么重要信息。所以,我们接下来要引入IDF。

注意:也有 “TF = 某词在某文档中出现的次数 / 该文档的总词量” 这种计算,但SKLEARN中是采用直接计次。

2. IDF:Inverse Document Frequency,逆向文本频率

指的是某个词在一个文件集或者语料库中区分力指标。计算公式为:

IDF = \log \frac{Nd + 1}{df(d, t)) + 1}+1

其中,Nd是训练集文档总数量,df(d,t)是包含某个单词的文档数量,+1的原因是避免分母为0。

也就是说,对一个文件集或者语料库而言,包含某个单词的文档越少,IDF的值越大,这个词的区分力越强,就越重要。

特别需要注意的是,IDF是针对文件集或者语料库而言的。计算机领域的IDF用在医学领域往往是不合适的。

3. TF-IDF = TF * IDF

综合考虑以某篇文章为中心的局部信息TF,和以某个语料库全局信息为基础的IDF,得到以下公式:

TF-IDF = TF * IDF

特别注意:

在sklearn中,上述计算的TF-IDF会经过一个欧几里得范数归一化:

二、代码示例

以下代码改编自莫烦的NLP课程中的源码。

输入15篇文章,形成一个44个单词的词汇表(去掉两个高频词,a 和 i),计算这15篇文章的tf-idf矩阵。再输入查询语句,计算该语句的tf-idf向量,然后求该语句的tf-idf向量和每一篇文章tf-idf向量的cosin距离,找出距离最近的三篇文章即是搜索结果。

核心思想--向量化。将文章向量化,将待查询语句也向量化,就可以通过计算余弦距离来比较相近程度。注意这里使用两个向量的夹角的余弦值来衡量两个文本间的相似度,而不是常用的欧氏距离,余弦相似度更加注重两个向量在方向上的差异,而不是实际距离差异。

1. 自编函数

import numpy as np
from collections import Counter
import itertools
from sklearn import preprocessing
from plot import show_tfidf
# from sklearn.metrics.pairwise import cosine_similarity


#15 docs
docs = [
    "it is a good day, I like to stay here",
    "I am happy to be here",
    "I am bob",
    "it is sunny today",
    "I have a party today",
    "it is a dog and that is a cat",
    "there are dog and cat on the tree",
    "I study hard this morning",
    "today is a good day",
    "tomorrow will be a good day",
    "I like coffee, I like book and I like apple",
    "I do not like it",
    "I am kitty, I like bob",
    "I do not care who like bob, but I like kitty",
    "It is coffee time, bring your cup",
]

#vocablist包括44 words 去掉两个超高频单词
docs_words = [d.lower().replace(",", "").split(" ") for d in docs]
wordlist = list(itertools.chain(*docs_words))       #遍历对象,去除内嵌,为什么需要加*没细看
vocablist = list(set(wordlist))
vocablist.sort(key=wordlist.index)        #转set去重,保持原序 => 全部单词表
vocablist.remove('a')           #为了根sklearn保持一致,去掉两个超高频单词
vocablist.remove('i')
#print(vocablist)

v2i = {v: i for i, v in enumerate(vocablist)}    #给单词编索引,eg: 'tree': 0    #enumerate函数 index, value
i2v = {i: v for v, i in v2i.items()}             #逆索引,eg: 0: 'tree'         #items函数,value, index
#print(v2i)
#print(i2v)


# tf = 每个单词出现频率
def get_tf():
    # term frequency: how frequent a word appears in a doc
    _tf = np.zeros((len(vocablist), len(docs)), dtype=np.float64)    # [n_vocab, n_doc] =》 [44 * 15]矩阵
    for i, d in enumerate(docs_words):      #循环每篇文章
        counter = Counter(d)
        for v in counter.keys():            #统计每篇文章单词计数
            if v in v2i:
                _tf[v2i[v], i] = counter[v]    #每个单词出现频率
    return _tf


# idf = 1 + np.log((len(docs) + 1) / (该单词在几篇文章中出现 + 1))
def get_idf(method="sklearn"):
    # inverse document frequency: low idf for a word appears in more docs, mean less important
    df = np.zeros((len(i2v), 1))
    for i in range(len(i2v)):        #循环词汇表每一个单词
        d_count = 0
        for d in docs_words:
            d_count += 1 if i2v[i] in d else 0     #该单词在几篇文章中出现
        df[i, 0] = d_count

    idf_fn = lambda x: 1 + np.log((len(docs) + 1) / (x+1))
    if idf_fn is None:
        raise ValueError
    return idf_fn(df)


def cosine_similarity(_tf_idf, q):
    unit_ds = _tf_idf / np.sqrt(np.sum(np.square(_tf_idf)))
    unit_q = q / np.sqrt(np.sum(np.square(q)))
    similarity = unit_ds.T.dot(unit_q).ravel()
    return similarity


#根据输入,比较每篇文章的相似度,不考虑输入句子中新加入的单词
def docs_score(q):
    q_words = q.replace(",", "").split(" ")
    counter = Counter(q_words)
    q_tf = np.zeros((len(idf), 1), dtype=float)
    for v in counter.keys():
        if v in v2i:
            q_tf[v2i[v], 0] = counter[v]  # 每个单词出现频率

    q_vec = q_tf * idf
    q_tf_idf = preprocessing.normalize(q_vec, norm='l2', axis=0)  # 欧几里得范数归一化

    #q_scores = cosine_similarity(tf_idf.transpose(), q_tf_idf.transpose())  #如果用库中函数,就用归一化后的
    q_scores = cosine_similarity(origin_tf_idf, q_vec)   #如果自已写,就用未归一化的

    return q_scores


#获得tf_idf最高的n个词
def get_keywords(n=2):
    for c in range(15):
        col = tf_idf[:, c]
        idx = np.argsort(col)[-n:][::-1]    #从小到大排列,提取其对应的index, 从后向前反向取
        print("doc{}, top{} keywords {}".format(c, n, [i2v[i] for i in idx]))  #TOP2,TOP1




#----------TEST
tf = get_tf()           # [n_vocab, n_doc] 44*15
idf = get_idf()         # [n_vocab, 1]     44*1
origin_tf_idf = tf * idf       # [n_vocab, n_doc]   44*15
tf_idf = preprocessing.normalize(origin_tf_idf, norm='l2', axis=0)    #欧几里得范数归一化

print("\ntf samples:\n", tf[:2])
print("\nidf sample:\n", idf[:2])
print("\ntf_idf sample:\n", tf_idf[:2])

#--- 提取关键词
get_keywords()

#--- 搜索最相似的句子
q = "I get a coffee cup"
scores = docs_score(q)
d_ids = scores.ravel().argsort()[-3:][::-1]
print("\ntop 3 docs for '{}':\n{}".format(q, [docs[i] for i in d_ids]))

show_tfidf(tf_idf.T, [i2v[i] for i in range(tf_idf.shape[0])], "tfidf_matrix")

2. 使用sklearn库

from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
from plot import show_tfidf


docs = [
    "it is a good day, I like to stay here",
    "I am happy to be here",
    "I am bob",
    "it is sunny today",
    "I have a party today",
    "it is a dog and that is a cat",
    "there are dog and cat on the tree",
    "I study hard this morning",
    "today is a good day",
    "tomorrow will be a good day",
    "I like coffee, I like book and I like apple",
    "I do not like it",
    "I am kitty, I like bob",
    "I do not care who like bob, but I like kitty",
    "It is coffee time, bring your cup",
]

vectorizer = TfidfVectorizer()
tf_idf = vectorizer.fit_transform(docs)
# print("idf: ", [(n, idf) for idf, n in zip(vectorizer.idf_, vectorizer.get_feature_names())])
# print("v2i: ", vectorizer.vocabulary_)
# print(tf_idf)


q = "I get a coffee cup"
qtf_idf = vectorizer.transform([q])

res = cosine_similarity(tf_idf, qtf_idf)
res = res.ravel().argsort()[-3:]
print("\ntop 3 docs for '{}':\n{}".format(q, [docs[i] for i in res[::-1]]))

i2v = {i: v for v, i in vectorizer.vocabulary_.items()}
dense_tfidf = tf_idf.todense()  #tf_idf为稀疏矩阵
show_tfidf(dense_tfidf, [i2v[i] for i in range(dense_tfidf.shape[1])], "tfidf_sklearn_matrix")

  • 9
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 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、付费专栏及课程。

余额充值