TF-IDF介绍及相关代码实现

一、TF-IDF介绍

TF-IDF(词频-逆向文件频率)是一种用于信息检索与文本挖掘的常用加权技术。

  • TF-IDF是一种统计方法,用以评估一字词对于一个文件集或一个语料库中的其中一份文件的重要程度。
  • 字词的重要性随着它在文件中出现的次数成正比增加,但同时会随着它在语料库中出现的频率成反比下降。

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

1.1 TF(Term Frequency,词频)
  • 词频(TF)表示词条(关键字)在文本中出现的频率。
1.2 IDF(Inverse Document Frequency,逆向文件频率)
  • 某一特定词语的IDF,可以由总文件数目除以包含该词语的文件的数目,再将得到的商取对数得到。
  • 如果包含词条t的文档越少, IDF越大,则说明词条具有很好的类别区分能力。
1.3 TF-IDF(词频-逆向文件频率)
  • 某一特定文件内的高词语频率,以及该词语在整个文件集合中的低文件频率,可以产生出高权重的TF-IDF。因此,TF-IDF倾向于过滤掉常见的词语,保留重要的词语。

TF-IDF(w) = \frac{在某一类词条w出现的次数}{该词条所有词数} log{ \frac{语料库中文档总数}{包含词语w的文档数目+1}}

二、基于sklearn的TF-IDF用于词向量获取

2.1、库引用
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfTransformer
2.2、TF-IDF训练
x_train = ['TF-IDF 主要 思想 是','算法 一个 重要 特点 可以 脱离 语料库 背景',
           '如果 一个 网页 被 很多 其他 网页 链接 说明 网页 重要']
x_test=['原始 文本 进行 标记','主要 思想']

# max_features为维度词信息
#该类会将文本中的词语转换为词频矩阵,矩阵元素a[i][j] 表示j词在i类文本下的词频
vectorizer = CountVectorizer(max_features=10)

#该类会统计每个词语的tf-idf权值
tf_idf_transformer = TfidfTransformer()

#将文本转为词频矩阵并计算tf-idf
tf_idf = tf_idf_transformer.fit_transform(vectorizer.fit_transform(x_train))
#将tf-idf矩阵抽取出来,元素a[i][j]表示j词在i类文本中的tf-idf权重

x_train_weight = tf_idf.toarray()
 
#对测试集进行tf-idf权重计算
tf_idf = tf_idf_transformer.transform(vectorizer.transform(x_test))

x_test_weight = tf_idf.toarray()  # 测试集TF-IDF权重矩阵
 
print('输出x_train文本向量:')
print(x_train_weight)
print('输出x_test文本向量:')
print(x_test_weight)

2.3、向量结果
输出x_train文本向量:
[[0.70710678 0.         0.70710678 0.         0.         0.
  0.         0.         0.         0.        ]
 [0.         0.3349067  0.         0.44036207 0.         0.44036207
  0.44036207 0.44036207 0.         0.3349067 ]
 [0.         0.22769009 0.         0.         0.89815533 0.
  0.         0.         0.29938511 0.22769009]]
输出x_test文本向量:
[[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 1. 0. 0. 0. 0. 0. 0. 0.]]

三、基于原始代码的TF-IDF[全局词频]获得权重

# -*- coding: utf-8 -*-
from collections import defaultdict
import math
import operator
 
"""
函数说明:创建数据样本
Returns:
    dataset - 实验样本切分的词条
    classVec - 类别标签向量
"""
def loadDataSet():
    dataset = [ ['my', 'dog', 'has', 'flea', 'problems', 'help', 'please'],    # 切分的词条
                   ['maybe', 'not', 'take', 'him', 'to', 'dog', 'park', 'stupid'],
                   ['my', 'dalmation', 'is', 'so', 'cute', 'I', 'love', 'him'],
                   ['stop', 'posting', 'stupid', 'worthless', 'garbage'],
                   ['mr', 'licks', 'ate', 'my', 'steak', 'how', 'to', 'stop', 'him'],
                   ['quit', 'buying', 'worthless', 'dog', 'food', 'stupid'] ]
    classVec = [0, 1, 0, 1, 0, 1]  # 类别标签向量,1代表好,0代表不好
    return dataset, classVec
 
 
"""
函数说明:特征选择TF-IDF算法
Parameters:
     list_words:词列表
Returns:
     dict_feature_select:特征选择词字典
"""
def feature_select(list_words):
    #总词频统计
    doc_frequency=defaultdict(int)
    for word_list in list_words:
        for i in word_list:
            doc_frequency[i]+=1
 
    #计算每个词的TF值
    word_tf={}  #存储没个词的tf值
    for i in doc_frequency:
        word_tf[i]=doc_frequency[i]/sum(doc_frequency.values())
 
    #计算每个词的IDF值
    doc_num=len(list_words)
    word_idf={} #存储每个词的idf值
    word_doc=defaultdict(int) #存储包含该词的文档数
    for i in doc_frequency:
        for j in list_words:
            if i in j:
                word_doc[i]+=1
    for i in doc_frequency:
        word_idf[i]=math.log(doc_num/(word_doc[i]+1))
 
    #计算每个词的TF*IDF的值
    word_tf_idf={}
    for i in doc_frequency:
        word_tf_idf[i]=word_tf[i]*word_idf[i]
 
    # 对字典按值由大到小排序
    dict_feature_select=sorted(word_tf_idf.items(),key=operator.itemgetter(1),reverse=True)
    return dict_feature_select
 
if __name__=='__main__':
    data_list,label_list=loadDataSet() #加载数据
    features=feature_select(data_list) #所有词的TF-IDF值
    print(features)
    print(len(features))

改进版:速度从上述代码3小时变成1分钟

def feature_select(list_words):
    
    print("1、总词频统计:")
    #总词频统计
    doc_frequency=defaultdict(int)
    for word_list in tqdm(list_words):
        for i in word_list:
            doc_frequency[i]+=1
    
    print("2、TF计算:")
    #计算每个词的TF值
    n = sum(doc_frequency.values())
    word_tf={}  #存储没个词的tf值
    for i in tqdm(doc_frequency):
        word_tf[i]=doc_frequency[i]/n
   
    print("3、IDF计算:")
    #计算每个词的IDF值
    doc_num=len(list_words)
    word_idf={} #存储每个词的idf值
    word_doc=defaultdict(int) #存储包含该词的文档数
    for i in tqdm(range(len(list_words))):
        for word in list_words[i]:
            word_doc[word]+=1
    
    for i in tqdm(doc_frequency):
        word_idf[i]=math.log(doc_num/(word_doc[i]+1))

    print("4、TF计算:")
    #计算每个词的TF*IDF的值
    word_tf_idf={}
    for i in tqdm(doc_frequency):
        word_tf_idf[i]=word_tf[i]*word_idf[i]
 
    # 对字典按值由大到小排序
    dict_feature_select=sorted(word_tf_idf.items(),key=operator.itemgetter(1),reverse=True)
    return dict_feature_select

四、基于sklearn的TF-IDF用于权值获取

4.1、库引用
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfTransformer
4.2、数据集
corpus = [
  "帮我 查下 明天 北京 天气 怎么样",
  "帮我 查下 今天 北京 天气 好不好",
  "帮我 查询 去 北京 的 火车",
  "帮我 查看 到 上海 的 火车",
  "帮我 查看 特朗普 的 新闻",
  "帮我 看看 有没有 北京 的 新闻",
  "帮我 搜索 上海 有 什么 好玩的",
  "帮我 找找 上海 东方明珠 在哪"
]
4.3、字典构造
# 步骤1
vectoerizer = CountVectorizer(min_df=1, max_df=1.0, token_pattern='\\b\\w+\\b')

# 步骤2
vectoerizer.fit(corpus)

# 步骤3
bag_of_words = vectoerizer.get_feature_names()

print("Bag of words(词袋):")
print(bag_of_words)
# 字典大小
print(len(bag_of_words))

# 步骤4
X = vectoerizer.transform(corpus)
# 每句话种每个词出现的次数
print("Vectorized corpus:")
print(X.toarray())

# 步骤5:字典
print("index of `的` is : {}".format(vectoerizer.vocabulary_.get('上海')))
4.4、TF-IDF权值获取
from sklearn.feature_extraction.text import TfidfTransformer


# 步骤1
tfidf_transformer = TfidfTransformer()
# 步骤2
tfidf_transformer.fit(X.toarray())

# 步骤3,tf-idf权值
word_list = {}
for idx, word in enumerate(vectoerizer.get_feature_names()):
#       print("{}\t{}".format(word, tfidf_transformer.idf_[idx]))
    word_list[word] = tfidf_transformer.idf_[idx]
    

dic = sorted(word_list.items(),key=lambda x:-x[1])
print(dic)

# 步骤4 # 句子向量
# tfidf = tfidf_transformer.transform(X)
# print(tfidf.toarray())
4.5、结果
[('东方明珠', 2.504077396776274), ('什么', 2.504077396776274), ('今天', 2.504077396776274), ('到', 2.504077396776274), ('去', 2.504077396776274), ('在哪', 2.504077396776274), ('好不好', 2.504077396776274), ('好玩的', 2.504077396776274), ('怎么样', 2.504077396776274), ('找找', 2.504077396776274), ('搜索', 2.504077396776274), ('明天', 2.504077396776274), ('有', 2.504077396776274), ('有没有', 2.504077396776274), ('查询', 2.504077396776274), ('特朗普', 2.504077396776274), ('看看', 2.504077396776274), ('天气', 2.09861228866811), ('新闻', 2.09861228866811), ('查下', 2.09861228866811), ('查看', 2.09861228866811), ('火车', 2.09861228866811), ('上海', 1.8109302162163288), ('北京', 1.587786664902119), ('的', 1.587786664902119), ('帮我', 1.0)]

五、基于gensim的TF-IDF用于权值获取

gensim原始地址:models.tfidfmodel – TF-IDF model — gensim (radimrehurek.com)

5.1 语料库
corpus = [
    'this is the first document',
    'this is the second second document',
    'and the third one',
    'is this the first document'
]
5.2 分词
[输入]:
word_list = []
for i in range(len(corpus)):
    word_list.append(corpus[i].split(' '))
print(word_list)
    
[输出]:
[['this', 'is', 'the', 'first', 'document'],
 ['this', 'is', 'the', 'second', 'second', 'document'],
 ['and', 'the', 'third', 'one'],
 ['is', 'this', 'the', 'first', 'document']]
5.3 字典和词袋模型构建
from gensim import corpora
# 赋给语料库中每个词(不重复的词)一个整数id
dictionary = corpora.Dictionary(word_list)
new_corpus = [dictionary.doc2bow(text) for text in word_list]
print(new_corpus)

# 元组中第一个元素是词语在词典中对应的id,第二个元素是词语在文档中出现的次数
[输出]:
[[(0, 1), (1, 1), (2, 1), (3, 1), (4, 1)], 
 [(0, 1), (2, 1), (3, 1), (4, 1), (5, 2)], 
 [(3, 1), (6, 1), (7, 1), (8, 1)], 
 [(0, 1), (1, 1), (2, 1), (3, 1), (4, 1)]]

通过下面的方法可以看到语料库中每个词对应的id

 [输入]:
 print(dictionary.token2id)

 [输出]:
 {'document': 0, 'first': 1, 'is': 2, 'the': 3, 'this': 4, 'second': 5, 'and': 6,
 'one': 7,   'third': 8}
5.4 TF-IDF模型训练
[输入]:
# 训练模型并保存
from gensim import models
tfidf = models.TfidfModel(new_corpus)
tfidf.save("my_model.tfidf")

# 载入模型
tfidf = models.TfidfModel.load("my_model.tfidf")

# 使用这个训练好的模型得到单词的tfidf值
print(list(tfidf[new_corpus]))

[输出]:
[[(0, 0.33699829595119235),
  (1, 0.8119707171924228),
  (2, 0.33699829595119235),
  (4, 0.33699829595119235)],
 [(0, 0.10212329019650272),
  (2, 0.10212329019650272),
  (4, 0.10212329019650272),
  (5, 0.9842319344536239)],
 [(6, 0.5773502691896258), (7, 0.5773502691896258), (8, 0.5773502691896258)],
 [(0, 0.33699829595119235),
  (1, 0.8119707171924228),
  (2, 0.33699829595119235),
  (4, 0.33699829595119235)]]
核心代码
# TF-IDF
dictionary = corpora.Dictionary(Descartes_doc)
new_corpus = [dictionary.doc2bow(text) for text in Descartes_doc]
tfidf = models.TfidfModel(new_corpus)
corpus_tfidf = tfidf[new_corpus]

结论:

  • gensim训练出来的tf-idf值左边是词的id,右边是词的tfidf值
  • gensim有自动去除停用词的功能,比如the
  • gensim会自动去除单个字母,比如i
  • gensim会去除没有被训练到的词,比如name
  • 所以通过gensim并不能计算每个单词的tfidf值

参考文献

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容