一、TF-IDF算法
TF(Term Frequency)指词频,IDF(Inverse Document Frequency)指逆向文件频率。它的主要思想是:如果某个词或短语在一篇文章中出现的频率高,并且在其他文章中很少出现,则认为此词或者短语具有很好的类别区分能力,适合用来分类。简单来说,就是计算字符在文章中的重要性,并给它赋予相应的权,权重大越重要越能作为区分文章的工具。举个例子:“的”这个字在文章中往往出现最多,但是在其他文章中也很多出现,因此认为“的”重要性小,而比如这篇文章中大量出现“数学”,而其他文章中“数学”出现很少,说明对这篇文章来说,“数学”很重要,这篇文章也大概是讲数学的。
二、文章的向量化与分类方式
那么,知道了每个字符的重要性后如何给文章分类呢?向量就登场了。我们生活在一个三维的空间之中,我们绝大多数人都能够理解的只有直线向量、平面向量和空间向量,它们分别像是这样a=(-3)(直线向量),b=(5,7)(平面向量),c=(1,6,-4)(空间向量)。而向量在更多维度下的表现对我们来说是不可见的,但是是可算的,比如五维的向量e1=(2,5,1,7,13)、e2=(14,6,4,9,10),我们没法把它们画出来,但是我们可以利用向量外积计算它们之间的夹角的余弦值,在每条维度上的数值都大于零时,夹角的范围便被限定为[0, ],所以余弦值越大,夹角越小,相反余弦值越小,夹角越大。而当我们将每一个字符都作为一个维度,文章中它们的权重作为它们的坐标,那么文章就可以用一条高维向量来表示,不同的文章会有不同的向量,当两篇文章所对应的向量夹角小的时候,说明具有区分度词或短语在这两篇文章中重合率越高,也就说明两篇文章越相似,相反向量夹角大,就说明两篇文章差别越大。将向量夹角小的文章分为一类,也就完成了将相似的文章分为一类这一任务,正确率不亚于人工。
三、python实现
首先我们需要一个字典,利用字典建立向量,这里我找了一个两千五百字的常用字字典命名为dictionary.txt
read_dictionary.py
def txttermnumber(files):
"""计算txt文件中每个字出现的个数"""
# 打开文件
fr = open(files, 'r', encoding='UTF-8')
# 读取文件所有行
content = fr.readlines()
contentLines = ''
characers = []
stat = {}
# 依次迭代所有行
for line in content:
# 去除空格
line = line.strip()
if len(line) == 0:
continue
contentLines = contentLines + line
# 统计每一字出现的个数
for x in range(0, len(line)):
# 如果字符第一次出现 加入到字符数组中
if not line[x] in characers:
characers.append(line[x])
# 如果是字符第一次出现 加入到字典中
if line[x] not in stat:
stat[line[x]] = 0
# 出现次数加一
stat[line[x]] += 0
# 对字典进行倒数排序 从高到低 其中e表示dict.items()中的一个元素,
# e[1]则表示按 值排序如果把e[1]改成e[0],那么则是按键排序,
# reverse=False可以省略,默认为升序排列
return stat
其次需要将文章转化为向量形式,注意在IDF中作为语料库的文案我是找的论文,地址为dll/
txt_turnto_vector.py
from read_dictionary import txttermnumber
from math import log
def tf(file):
"""计算词频"""
dictionary = txttermnumber("dictionary.txt")
# 打开文件
fr = open(file, 'r', encoding='UTF-8')
# 读取文件所有行
content = fr.readlines()
contentLines = ''
characers = []
stat = {}
# 依次迭代所有行
for line in content:
# 去除空格
line = line.strip()
if len(line) == 0:
continue
contentLines = contentLines + line
# 统计每一字出现的个数
for x in range(0, len(line)):
# 如果字符第一次出现 加入到字符数组中
if not line[x] in characers:
characers.append(line[x])
# 如果是字符第一次出现 加入到字典中
if line[x] not in stat:
stat[line[x]] = 0
# 出现次数加一
stat[line[x]] += 1
# 对字典进行倒数排序 从高到低 其中e表示dict.items()中的一个元素,
# e[1]则表示按 值排序如果把e[1]改成e[0],那么则是按键排序,
# reverse=False可以省略,默认为升序排列
stat = sorted(stat.items(), key=lambda e: e[1], reverse=True)
# 打印stat 每个字和其出现的次数 stat经过排序后变成二元组
for i in stat:
# 将stat里面所有在字典中的字输出加入到字典中
if i[0] in dictionary.keys():
dictionary[i[0]] += i[1]
else:
continue
fr.close()
for a in dictionary.keys():
dictionary[a] = dictionary[a]/len(contentLines)
return dictionary
def idf():
"""计算逆向文件频率"""
n = 1
dictionary = txttermnumber("dictionary.txt")
while n < 67:
n = str(n)
file = 'dll/' + n + '.txt'
fr1 = open(file, 'r', encoding='UTF-8')
# 读取文件所有行
content = fr1.readlines()
contentLines = ''
characers = []
stat = {}
# 同上
for line in content:
line = line.strip()
if len(line) == 0:
continue
contentLines = contentLines + line
for x in range(0, len(line)):
if not line[x] in characers:
characers.append(line[x])
if line[x] not in stat:
stat[line[x]] = 0
stat[line[x]] += 1
fr1.close()
# 将文字出现的文章数附到字典中
for i in stat:
if i[0] in dictionary.keys():
dictionary[i[0]] += 1
else:
continue
n = int(n)
n += 1
# 计算字典的idf值
for a in dictionary.keys():
dictionary[a] = log(66/(dictionary[a]+1), 10)
return dictionary
print(tf("TF-IDF.txt"))
print(tf("search.txt"))
print(idf())
最后进行向量运算
tfidf.py
from txt_turnto_vector import tf, idf
def tf_idf(file):
"""计算tf-idf"""
tfidf = {}
for i in tf(file).keys():
tfidf[i] = tf(file)[i]*(idf()[i])
return tfidf
def vector_inner_product(vector1, vector2):
"""用于计算两向量的内积"""
n = 0
num = 0
while n < 2500:
num += vector1[n]*vector2[n]
n += 1
num1 = 0
num2 = 0
for i in vector1:
num1 += i**2
for a in vector2:
num2 += a**2
num1 = num1**0.5
num2 = num2**0.5
cos12 = num/(num1 * num2)
return cos12
最后选择两篇文章可以计算出他们之间的cos值