[关闭]
@lianjizhe 2018-07-13T22:17:30.000000Z 字数 5664 阅读 9287

使用不同的方法计算TF-IDF值

tfidf


欢迎大家访问我的博客以及简书
本博客所有内容以学习、研究和分享为主,如需转载,请联系本人,标明作者和出处,并且是非商业用途,谢谢!

一. 摘要

这篇文章主要介绍了计算TF-IDF的不同方法实现,主要有三种方法:

  • 用gensim库来计算tfidf值
  • 用sklearn库来计算tfidf值
  • 用python手动实现tfidf的计算

关于TFIDF的算法原理我就不过多介绍了,看这篇博客即可——TF-IDF原理。阮一峰大佬写的,浅显易懂,看了这么多篇就这篇最好懂。


二. 正文

1.使用gensim提取文本的tfidf特征

首先来看我们的语料库

  1. corpus = [
  2. 'this is the first document',
  3. 'this is the second second document',
  4. 'and the third one',
  5. 'is this the first document'
  6. ]

接下来看我们的处理过程
1)把语料库做一个分词的处理

  1. [输入]:
  2. word_list = []
  3. for i in range(len(corpus)):
  4. word_list.append(corpus[i].split(' '))
  5. print(word_list)
  6. [输出]:
  7. [['this', 'is', 'the', 'first', 'document'],
  8. ['this', 'is', 'the', 'second', 'second', 'document'],
  9. ['and', 'the', 'third', 'one'],
  10. ['is', 'this', 'the', 'first', 'document']]

2) 得到每个词的id值及词频

  1. [输入]:
  2. from gensim import corpora
  3. # 赋给语料库中每个词(不重复的词)一个整数id
  4. dictionary = corpora.Dictionary(word_list)
  5. new_corpus = [dictionary.doc2bow(text) for text in word_list]
  6. print(new_corpus)
  7. # 元组中第一个元素是词语在词典中对应的id,第二个元素是词语在文档中出现的次数
  8. [输出]:
  9. [[(0, 1), (1, 1), (2, 1), (3, 1), (4, 1)],
  10. [(0, 1), (2, 1), (3, 1), (4, 1), (5, 2)],
  11. [(3, 1), (6, 1), (7, 1), (8, 1)],
  12. [(0, 1), (1, 1), (2, 1), (3, 1), (4, 1)]]
  13. [输入]:
  14. # 通过下面的方法可以看到语料库中每个词对应的id
  15. print(dictionary.token2id)
  16. [输出]:
  17. {'document': 0, 'first': 1, 'is': 2, 'the': 3, 'this': 4, 'second': 5, 'and': 6,
  18. 'one': 7, 'third': 8}

3)训练gensim模型并且保存它以便后面的使用

  1. [输入]:
  2. # 训练模型并保存
  3. from gensim import models
  4. tfidf = models.TfidfModel(new_corpus)
  5. tfidf.save("my_model.tfidf")
  6. # 载入模型
  7. tfidf = models.TfidfModel.load("my_model.tfidf")
  8. # 使用这个训练好的模型得到单词的tfidf值
  9. tfidf_vec = []
  10. for i in range(len(corpus)):
  11. string = corpus[i]
  12. string_bow = dictionary.doc2bow(string.lower().split())
  13. string_tfidf = tfidf[string_bow]
  14. tfidf_vec.append(string_tfidf)
  15. print(tfidf_vec)
  16. [输出]:
  17. [[(0, 0.33699829595119235),
  18. (1, 0.8119707171924228),
  19. (2, 0.33699829595119235),
  20. (4, 0.33699829595119235)],
  21. [(0, 0.10212329019650272),
  22. (2, 0.10212329019650272),
  23. (4, 0.10212329019650272),
  24. (5, 0.9842319344536239)],
  25. [(6, 0.5773502691896258), (7, 0.5773502691896258), (8, 0.5773502691896258)],
  26. [(0, 0.33699829595119235),
  27. (1, 0.8119707171924228),
  28. (2, 0.33699829595119235),
  29. (4, 0.33699829595119235)]]

通过上面的计算我们发现这向量的维数和我们语料单词的个数不一致呀,我们要得到的是每个词的tfidf值,为了一探究竟我们再做个小测试

4) 小测试现出gensim计算的原形

  1. [输入]:
  2. # 我们随便拿几个单词来测试
  3. string = 'the i first second name'
  4. string_bow = dictionary.doc2bow(string.lower().split())
  5. string_tfidf = tfidf[string_bow]
  6. print(string_tfidf)
  7. [输出]:
  8. [(1, 0.4472135954999579), (5, 0.8944271909999159)]

结论

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

2.使用sklearn提取文本tfidf特征

我们的语料库不变,还是上面那个

  1. corpus = [
  2. 'this is the first document',
  3. 'this is the second second document',
  4. 'and the third one',
  5. 'is this the first document'
  6. ]

然后来看我们的处理过程

  1. [输入]:
  2. from sklearn.feature_extraction.text import TfidfVectorizer
  3. tfidf_vec = TfidfVectorizer()
  4. tfidf_matrix = tfidf_vec.fit_transform(corpus)
  5. # 得到语料库所有不重复的词
  6. print(tfidf_vec.get_feature_names())
  7. # 得到每个单词对应的id值
  8. print(tfidf_vec.vocabulary_)
  9. # 得到每个句子所对应的向量
  10. # 向量里数字的顺序是按照词语的id顺序来的
  11. print(tfidf_matrix.toarray())
  12. [输出]:
  13. ['and', 'document', 'first', 'is', 'one', 'second', 'the', 'third', 'this']
  14. {'this': 8, 'is': 3, 'the': 6, 'first': 2, 'document': 1, 'second': 5, 'and': 0, 'third': 7, 'one': 4}
  15. [[0. 0.43877674 0.54197657 0.43877674 0. 0.
  16. 0.35872874 0. 0.43877674]
  17. [0. 0.27230147 0. 0.27230147 0. 0.85322574
  18. 0.22262429 0. 0.27230147]
  19. [0.55280532 0. 0. 0. 0.55280532 0.
  20. 0.28847675 0.55280532 0. ]
  21. [0. 0.43877674 0.54197657 0.43877674 0. 0.
  22. 0.35872874 0. 0.43877674]]

3.python提取文本的tfidf特征

我们的语料库依旧不变

  1. corpus = [
  2. 'this is the first document',
  3. 'this is the second second document',
  4. 'and the third one',
  5. 'is this the first document'
  6. ]

1) 对语料进行分词

  1. [输入]:
  2. word_list = []
  3. for i in range(len(corpus)):
  4. word_list.append(corpus[i].split(' '))
  5. print(word_list)
  6. [输出]:
  7. [['this', 'is', 'the', 'first', 'document'],
  8. ['this', 'is', 'the', 'second', 'second', 'document'],
  9. ['and', 'the', 'third', 'one'],
  10. ['is', 'this', 'the', 'first', 'document']]

2) 统计词频

  1. [输入]:
  2. countlist = []
  3. for i in range(len(word_list)):
  4. count = Counter(word_list[i])
  5. countlist.append(count)
  6. countlist
  7. [输出]:
  8. [Counter({'document': 1, 'first': 1, 'is': 1, 'the': 1, 'this': 1}),
  9. Counter({'document': 1, 'is': 1, 'second': 2, 'the': 1, 'this': 1}),
  10. Counter({'and': 1, 'one': 1, 'the': 1, 'third': 1}),
  11. Counter({'document': 1, 'first': 1, 'is': 1, 'the': 1, 'this': 1})]

3) 定义计算tfidf公式的函数

  1. # word可以通过count得到,count可以通过countlist得到
  2. # count[word]可以得到每个单词的词频, sum(count.values())得到整个句子的单词总数
  3. def tf(word, count):
  4. return count[word] / sum(count.values())
  5. # 统计的是含有该单词的句子数
  6. def n_containing(word, count_list):
  7. return sum(1 for count in count_list if word in count)
  8. # len(count_list)是指句子的总数,n_containing(word, count_list)是指含有该单词的句子的总数,加1是为了防止分母为0
  9. def idf(word, count_list):
  10. return math.log(len(count_list) / (1 + n_containing(word, count_list)))
  11. # 将tf和idf相乘
  12. def tfidf(word, count, count_list):
  13. return tf(word, count) * idf(word, count_list)

4) 计算每个单词的tfidf值

  1. [输入]:
  2. import math
  3. for i, count in enumerate(countlist):
  4. print("Top words in document {}".format(i + 1))
  5. scores = {word: tfidf(word, count, countlist) for word in count}
  6. sorted_words = sorted(scores.items(), key=lambda x: x[1], reverse=True)
  7. for word, score in sorted_words[:]:
  8. print("\tWord: {}, TF-IDF: {}".format(word, round(score, 5)))
  9. [输出]:
  10. Top words in document 1
  11. Word: first, TF-IDF: 0.05754
  12. Word: this, TF-IDF: 0.0
  13. Word: is, TF-IDF: 0.0
  14. Word: document, TF-IDF: 0.0
  15. Word: the, TF-IDF: -0.04463
  16. Top words in document 2
  17. Word: second, TF-IDF: 0.23105
  18. Word: this, TF-IDF: 0.0
  19. Word: is, TF-IDF: 0.0
  20. Word: document, TF-IDF: 0.0
  21. Word: the, TF-IDF: -0.03719
  22. Top words in document 3
  23. Word: and, TF-IDF: 0.17329
  24. Word: third, TF-IDF: 0.17329
  25. Word: one, TF-IDF: 0.17329
  26. Word: the, TF-IDF: -0.05579
  27. Top words in document 4
  28. Word: first, TF-IDF: 0.05754
  29. Word: is, TF-IDF: 0.0
  30. Word: this, TF-IDF: 0.0
  31. Word: document, TF-IDF: 0.0
  32. Word: the, TF-IDF: -0.04463

三. 总结

之所以做了这方面的总结是因为最近在研究word2vec,然后涉及到了基于word2vec的文本表示方法。你用word2vec训练好的模型可以得到词的向量,然后我们可以利用这些词向量表示句子向量。

  1. 一般处理方法是把句子里涉及到的单词用word2vec模型训练得到词向量,然后把这些向量加起来再除以单词数,就可以得到句子向量。这样处理之后可以拿去给分类算法(比如LogisticRegression)训练,从而对文本进行分类。
  2. 还有一种是把句子里的每个单词的向量拼接起来,比如每个单词的维度是1*100
    一句话有30个单词,那么如何表示这句话的向量呢?
    把单词拼接来,最终得到这句话的向量的维度就是30*100维
  3. 我想做的是把句子里所有的单词用word2vec模型训练得到词向量,然后把这些向量乘以我们之前得到的tfidf值,再把它们加起来除以单词数,就可以得到句子向量。也就是结合tfidf给单词加上一个权重,评判一个单词的重要程度。
  4. 最后发现gensim和sklearn都不能满足我的需求,用python的方法做了一个。
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注