[关闭]
@ArrowLLL 2017-04-20T13:49:20.000000Z 字数 4096 阅读 4244

使用python做tf-ifd算法实践

python 机器学习 爬虫


主页地址 : 月光森林


引入

信息内容安全课杨老师讲了一个tf-idf算法,用于提取一篇文档的关键词。觉得蛮好玩又正好练习python,所以就试着写了一下,特此记录,各位看官请轻拍板砖。

tf-idf 算法及其原理

偷个懒,直接贴维基百科的解释:

这里写图片描述

至于原理,同样贴图(没错,我就这么懒了怎么样吧。。→_→) :

tf-idf原理

如果看维基还是了解不清楚的话,推荐阮一峰大神的博客,这个也是我们的老师上课讲的东西。

至于其中的数学原理,从维基的原理就很容易看出来,基础还是“贝叶斯定理”,另外一点就是idf的值这里使用到了熵的概念。推荐这篇博客的介绍 : TF-IDF模型的概率解释

(少壮不努力, 老大学数学。。。囧rz)

实践准备

操作系统 : ubuntu 16.04
python版本 :python3.5.2
引入的python组件 :

语料库准备, 努努书坊-鲁迅-伪自由书 。当然是用爬虫爬喽~

爬虫爬取语料库

如果对爬虫理解有问题可参考本人之前的博客 —— 第一个爬虫:爬去古诗中带‘月’的诗句 。爬取的方法并没有很大的改进 (太弱只能写基础的。。。orz)

三步走 :

  1. 分析之前给出的鲁迅-伪自由书, 获得《伪自由书》中所有文章的链接,并存入一个list ;
  2. for循环遍历链接list, 从新的url获得《伪自由书》中的文章 ;
  3. 将获得的文章存入一个文件夹。

附上代码

  1. #!usr/bin/python3
  2. #coding=utf-8
  3. import re
  4. import os
  5. from urllib.request import urlopen
  6. from urllib.error import HTTPError
  7. from bs4 import BeautifulSoup
  8. def geturl(url):
  9. try :
  10. html = urlopen(url)
  11. except HTTPError as e :
  12. return None
  13. try :
  14. bsObj = BeautifulSoup(html, 'lxml')
  15. except AttributeError as e :
  16. return None
  17. return bsObj
  18. def getArticle(url, f) :
  19. articlePage = geturl(url)
  20. article = articlePage.findAll('p')
  21. for paragraph in article :
  22. print (paragraph.get_text(), file = f)
  23. start = "http://www.kanunu8.com/book/4433/"
  24. page = geturl(start)
  25. tr = page.findAll('tr', {'bgcolor' : '#ffffff'})
  26. links = (re.findall(r'<td><a href="(.*?)">(.*?)</a></td>', str(tr)))
  27. if not os.path.isdir('article') :
  28. os.mkdir('article')
  29. for link in links :
  30. with open('./article/' + link[1] + '.txt', 'wt+') as f :
  31. f.write(link[1])
  32. getArticle(start + link[0], f)
  33. print ('---< ' , link[1], ' > get ---')

这个爬虫只是在之前的基础上加了一点新功能,将爬取结果都放在了一个文件夹下,使用到了 os 模块的 os.path.isdir(str) 函数和 os.mkdir(str) 函数。 前者用于检查 str 这个文件夹是否存在,后者用于创建一个名为 str 的文件夹 。

爬取的结果如下:
这里写图片描述

然后是爬取结果的一部分 :
爬取结果2

tf-idf算法实现

仍然是三步走 :

  1. 使用jieba给所有的文章分词;
  2. 使用sklearn(即 scikit-learn模块) 中的CountVectorizer()类TfidfTransformer()类 完成对词语的词频分析并获得每篇文章对应的词语的idf值, 可参考[python] 使用scikit-learn工具计算文本TF-IDF值这篇文章的第三部分
  3. 设定关键字个数,取出每篇文章中tf-idf值最大的值作为对应文章的关键词

先附上代码,然后作说明 :

  1. #coding: utf-8
  2. import os
  3. import jieba
  4. import jieba.posseg as pseg
  5. from sklearn.feature_extraction.text import CountVectorizer
  6. from sklearn.feature_extraction.text import TfidfTransformer
  7. import numpt as np
  8. path = './article/'
  9. titlelist, wordslist = [], []
  10. for fileName in os.listdir(path) :
  11. titlelist.append(''.join(fileName[:-4].split()))
  12. with open(path + fileName) as f :
  13. text = f.read()
  14. text = ''.join(text.split())
  15. seg_list = jieba.cut(text)
  16. wordslist.append(' '.join(seg_list))
  17. vec = CountVectorizer()
  18. wordFrequence = vec.fit_transform(wordslist)
  19. words = vec.get_feature_names()
  20. trans = TfidfTransformer()
  21. tfidf = trans.fit_transform(wordFrequence)
  22. wordsWeight = tfidf.toarray()
  23. n = int(input('输入关键字的个数 : '))
  24. while(n > 5 or n < 0) :
  25. n = int(input('输入数字应大于零并且小于等于5 : '))
  26. for (title, weight) in zip(titlelist, wordsWeight) :
  27. print (title, ' : ')
  28. loc = np.argsort(-weight)
  29. for i in range(n) :
  30. print('\t#' + str(i + 1) + ':', words[loc[i]])
  31. print()
  32. input('any key to continue...')

对于初学者(比如我)其中可能会不太懂点:

最终结果展示

tfres1.png-53.9kB

以上です~


一个更新

在结巴分词的文档里看到一个提取关键词的函数:

jieba.analyse.extract_tags(sentence,topK)

函数有3点要求:

  • 需要先import jieba.analyse
  • setence为待提取的文本
  • topK为返回几个TF/IDF权重最大的关键词,默认值为20

由此可以直接对中文文档做关键字提取,附上代码:

  1. #!usr/bin/python3
  2. # -*- coding: utf-8 -*-
  3. import os
  4. import jieba.analyse as jiebanys
  5. import os
  6. import jieba.analyse as jiebays
  7. def getTag(fileLocation, k) :
  8. with open(fileLocation) as f :
  9. txt = f.read()
  10. # 重点 ----- 在 ----- 这里
  11. tags = jiebays.extract_tags(txt, topK = k)
  12. return (" ".join(tags)).split()
  13. if __name__ == '__main__' :
  14. n = int(input('输入关键字的个数 : '))
  15. while(n > 5 or n < 0) :
  16. n = int(input('输入数字应大于零并且小于等于5 : '))
  17. path = './article/'
  18. for fileName in os.listdir(path) :
  19. tagList = getTag(path + fileName, n)
  20. print (fileName + ' : ')
  21. for i in range(n) :
  22. print ('\t#' + str(i + 1) + ':', tagList[i])
  23. print()
  24. input('any key to continue...')

附上结果 :

结果展示2

二者比较

显然前后两份的结果区别还是很大的,具体原因应该是语料库的原因。不知道 jieba 的语料库如何,但是本人之前写的代码,因为语料库只有40个文本,且均选自同一本书籍,显然文本本身的区别应该就不是很大。对于一些应该作为关键词的词语,可能会因为在这40个文本组成的语料库相似度的问题而导致计算得出的 idf值很大,从而导致算法结果不是很理想。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注