Ex_treme's blog.

关键词检索模型————TF-IDF的简单应用

2018/03/29 Share

TF-IDF算法

  • TF-IDF算法的定义
  • TF-IDF算法应用范围
  • TF-IDF算法的原理

TF-IDF算法(一)

  • TF-IDF(term frequency-inverse document frequency)是一种用于信息检索文本挖掘的常用技术。用以评估一字词对于一个文件集或一个语料库中的其中一份文件的重要程度
  • TF-IDF的主要思想:如果某个词或短语在一篇文章中出现的平率很高,并且在其它文章中很少出现,则认为此词或者短语具有很好的类别区分能力,适合用来分类。

TF-IDF算法应用范围

  • 权重计算
  • 搜索引擎
  • 论文检索
  • 文章比对
  • 关键词抽取

TF-IDF算法的原理

TF-IDF是计算某个词在文件集的其中一份文件的权重(重要程度)。
TF-IDF = TF×IDF
TF:词频(Term Frequency)
IDF:逆向文件频率(Inverse Document Frequency)


TF(词频)=某个词在文章中出现的次数
考虑文章有长短之分
TF(词频)=某个词在文章中出现的次数/文章中总词数
IDF(逆文档频率)=文档总数n与词w所出现文件数比值的对数=log(文件集中文档的总数/包含该词的文档数+1)


词频(TF

  • 一篇文件的总词语数是100个
  • 词语“极客”出现了3次
  • 那么“极客”一词在该文件中的词频(TF)就是3/100=0.03

逆文档频率(IDF)

  • “极客”一词在1000份文件出现过
  • 文件总数是10000000份
  • 逆向文件频率就是lg(10000000/1000)=4

最后的TF-IDF的分数为0.03×4=0.12

TF-IDF(二)

实现输入一个关键词,而后从数据库中调出文章关键词最相似的文档。

配置MySQL

1
2
3
4
5
6
7
8
9
10
11
$ sudo apt-get install mysql-server
$ sudo apt-get isntall mysql-client
$ sudo apt-get install libmysqlclient-dev
$ sudo netstat -tap | grep mysql
$ mysql -u root -p
mysql> show databases;
mysql> use mysql;
mysql> show tables;
mysql> exit
$ pip install MySQL_python
Get navicate from https://download.csdn.net/download/jiangchb1/4522129

ModuleNotFoundError: No module named ‘ConfigParser’:
$ cp ./anaconda3/lib/python3.6/configparser.py ./anaconda3/lib/python3.6/ConfigParser.py

TF-IDF(三)

这一部分主要通过数据库MySQL和TF-IDF算法原理实现一个基于关键词的的文章检索模型。

数据预处理

这一部分可以沿用ACC文档摘要器中文档读取(word,pdf,txt)和文章预处理(主要是分词,因为TF-IDF是基于term也就是词语的)的代码。

文档切分:TF-IDF是基于文档和词语的,所以这就要求我们一篇文章一个文档。

1
2
3
4
5
6
7
8
9
10
11
with open('test.txt', 'r', encoding='utf-8') as r:
count = 0
for line in r.readlines():
with open(str(count) + '.txt', 'w', encoding='utf-8') as w:

count += 1
print(count)
print(line)
w.write(line)
# if count == 100:
# break

文档读取:要求能读取txt,doc,docx,pdf的文档,这里拿部分做示范。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import re
import docx2txt
import useHanLP as HanLP
# filepath = '/home/pzs741/PycharmProjects/TFIDF/data/Untitled 1.docx'
# text = docx2txt.process(filepath)
# print(text)

def WtoT(filepath):
if filepath.split('.')[-1] == 'docx' or filepath.split('.')[-1] == 'doc':
text = docx2txt.process(filepath)
elif filepath.split('.')[-1] == 'txt':
file = open(filepath,'r',encoding='utf-8')
text = file.read()
else:
print('err file type')
fenlist = list()
res = str(HanLP.GetFenword(text))
for fen in res.split(','):
if re.findall('[\u4e00-\u9fa5]',fen):
fenlist.append(fen.replace(' ',''))
return fenlist


if __name__ == '__main__':
print(WtoT('data/test.txt'))
print(WtoT('data/Untitled 1.docx'))

数据持久化

数据持久化实际上就是提取关键字段信息建表入库,主要包括下面两部分。

数据库实现:数据库连接,数据插入,查询。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import pymysql

def connectMySQL():
return pymysql.connect(host='127.0.0.1',
port=3306,
user='root',
password='root',
db='FileDB',
charset='utf8')

def queryMySQL1(conn,table,filemd5):
#通过数据库链接对象获得游标
cur = conn.cursor()
sql = 'SELECT id FROM '+ table +' WHERE filemd5=%s'
cur.execute(sql,filemd5)
#返回一个结果
return cur.fetchone()

def insertMySQL(conn,table,filename,filepath,filemd5,filefen):
cur = conn.cursor()
sql = 'insert into '+table+'(filename,filepath,filemd5,filefen) values (%s,%s,%s,%s)'
cur.execute(sql,(filename,filepath,filemd5,filefen))
conn.commit()
cur.close()
print('insert successfully!!!')

数据初始化:读取文章,读取个字段信息,存入数据库,分词转换成json(方便交互)格式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import math
import json
import hashlib
import os
import useMySQL
from DoxcToStr import WtoT

conn = useMySQL.connectMySQL()

TABLE = 'filetable'

#初始化文件
def initFile():
path = './data/'
for filename in os.listdir(path):
filepath = path + filename
name = filename.split('.')[0]
filemd5 = ''
with open(filepath,'rb') as f:
m = hashlib.md5()
m.update(f.read())
#文件MD5
filemd5 = m.hexdigest()
if filemd5 is not '':
res = useMySQL.queryMySQL1(conn,TABLE,filemd5)
if res is None:
fen = WtoT(filepath)

fenJson = json.dumps(fen,ensure_ascii=False)
print(fenJson)
useMySQL.insertMySQL(conn,TABLE,filename,filepath,filemd5,fenJson)

查询和匹配:由于要计算TF-IDF,这就要求我们要能查询出匹配关键字的文档,计算出数据库中文档总数,以及查询单个文档的分词结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

#模糊匹配的导关键词出现过的文章
def queryMySQLnum(conn,table,keyword):
cur = conn.cursor()
sql = 'select id from '+ table+" where filefen like '%"+keyword+"%'"
cur.execute(sql)
restuple = cur.fetchall()
reslist = list()
for rest in restuple:
reslist.append(int(rest[0]))
return reslist
#查询数据库中文档的总数
def queryALLFileNum(conn,table):
cur = conn.cursor()
sql = 'select count(id) from '+ table
cur.execute(sql)
return cur.fetchall()
#查询相应id对应份当的分词结果
def queryMySQLFen(conn,table,id):
cur = conn.cursor()
sql = "select filefen from "+ table + " where id =%d"%(id)
cur.execute(sql)
return cur.fetchall()

if __name__ == '__main__':
initFile()

计算TF-IDF:这是最关键的部分,主要思路如下。

  • 输入关键词

输入的关键词就是我们需要查询的文章主题,比如我想要一篇和智能手机相关的科技类文章,我就可以输入[智能,手机,科技]

  • 模糊匹配

通过数据库的模糊匹配拿到的实际上就是包含有关键词的文章,也就是我们的粗选文章(IDF中的包含该词的文档数)。

  • 计算文档总数

这个通过数据库的count非常容易实现,得到了文档总数之后,IDF也就计算出来了,IDF=log(文件集中文档的总数/包含该词的文档数+1)

  • 计算单个文档的单个关键词的tf

这个就要通过双重循环实现,第一重显然是针对关键词的,这里的话一共3词,第二重是针对包含该关键词的文档的,次数就等于文档数。

  • 计算TF-IDF

上面已经计算了IDF和单个关键词的tf,也就是说能计算出每个关键词的TF-IDF,把三个关键词的TF-IDF累加起来就是这个文档的得分了,输出最大的,就是我们要得结果了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
def tfidf():
keylist = ['科技','头条','男人']
#关键词出现的文章
reslist = []
for key in keylist:
l = useMySQL.queryMySQLnum(conn,TABLE,key)
reslist.append(l)
print(reslist)
#tfidf
keydict = dict()
for l in reslist:
for ll in l:
keydict[ll] = 0
#获得文档库中文档总数
allFileNum = int(useMySQL.queryALLFileNum(conn,TABLE)[0][0])

i = 0
for idL in reslist:
fileNum = len(idL)+1
idf = math.log(allFileNum/fileNum)

tf = 0
for id in idL:
fenres = useMySQL.queryMySQLFen(conn,TABLE,id)
# print(fenres)
djson = json.loads(fenres[0][0])
allwordNum = len(djson)
wordNum = djson.count(keylist[i])
tf = wordNum/allwordNum

keydict[id] += tf*idf
i +=1

sort_id_list = sorted(keydict, key=lambda x: keydict[x],reverse=True)
# print(keydict)
# print(sort_id_list)
# print(useMySQL.queryMySQLFen(conn,TABLE,sort_id_list[0]))

if __name__ == '__main__':
# initFile()
tfidf()

基于TF-IDF的关键词检索模型效果测试

可以看到最后给出的文档描述的是一部三星的手机~还是很符合预期的嘛

image

CATALOG
  1. 1. TF-IDF算法
    1. 1.1. TF-IDF算法(一)
      1. 1.1.1. TF-IDF算法应用范围
      2. 1.1.2. TF-IDF算法的原理
    2. 1.2. TF-IDF(二)
      1. 1.2.1. 配置MySQL
    3. 1.3. TF-IDF(三)
      1. 1.3.1. 数据预处理
      2. 1.3.2. 数据持久化
  2. 2. 基于TF-IDF的关键词检索模型效果测试