目录
实验目的:
加深对汉语文本信息处理基础理论及方法的认识和了解,锻炼和提高分析问题、解决问题的能力。通过对具体项目的任务分析、数据准备、算法设计和编码实现以及测试评价几个环节的练习,基本掌握实现一个自然语言处理系统的基本过程。
实验要求:
1. 基于tfidf的特征权重计算方法;
2.基于余弦距离的文本相似度计算方法
实验内容与原理:
实验6.1:基于tfidf的特征权重计算方法
依据实验五得到文本的表示特征,从文件中读取特征词集,构建向量空间模型,计算每个文章表示向量表示中每一个特征维度的权重。计算方法可采用TFIDF进行。
词频-逆文档频度(Term Frequency - Inverse DocumentFrequency,TF-IDF)字词的重要性随着它在文件中出现的次数成正比增加,但同时会随着它在语料库中出现的频率成反比下降。
TF-IDF计算公式
对于一个带标签的文章sen,遍历特征词表,若sen含有第i个特征词,则计算该词的TFIDF值作为向量对应维度的权重值,否则特征权重为0。遍历完成后得到文章的向量表示。
对给定语料库内的所有带标签的文章,要求输出其文章表示向量,以“文章id向量表示”的形式存在文件当中。
存储格式:一行为一个文本特征,以utf-8进行编码。
实验6.2:基于余弦距离的文本相似度计算方法
利用实验五中构建的特征集,对任意输入的文本序列,能将其转变为以TFIDF为权重的向量表示,并从实验6.2的文本文章中依照余弦相似度给出相似度最高的三篇文章并输出其相似度的值。
余弦相似度的计算公式如下:
实验数据说明:
实验数据采用htl_del_4000宾馆情感分析数据进行处理,所有数据已按照情感极性划分为褒(pos)贬(neg)两类,各2000篇,每个文本文件为一篇文章,实验数据需要先进行分词,分词方法不限。
统计频数时按照篇章级共现进行计算。停用词表采用中文通用停用词表,文件名为
“cn stopwords.txt”,可用于数据预处理中的去除停用词。
参考代码:
任务1:
import csv
import jieba
import os
from collections import defaultdict
import math
import numpy as np
import heapq
path='./中文信息处理技术实验数据/htl_del_4000/'
TFIDFs=[[0.0 for x in range(1000)] for x in range(4000)]
d = {} #记录文档频率
stop=[]#停用词
s0 = open('cn_stopwords.txt', 'r', encoding='utf-8').read()
for l in s0.splitlines():#按照行('\r', '\r\n', \n')分隔.返回停用词表
stop.append(l)
#转化为CSV文件
def ToCSV(file1,file2):
with open(file1, 'w', newline='', encoding='utf-8') as cf:
writer = csv.writer(cf)
writer.writerow(["id", "标签","向量"])
with open(file2, 'r', encoding='utf-8') as f:
l = f.readlines()
for s in l:
l1 = s.split('\t') # l1[0]为文档id,l1[1]为标签,l1[2]为向量
writer.writerow([l1[0], l1[1],l1[2]])
def add_file( file_name):#加载文件
if (os.path.exists(path + file_name)):
content = open(file_name, 'rb').read() if file_name[0] == '/' or file_name[0] == 'C' else open(path + file_name, 'rb').read()
words = jieba.cut(content)
else:
return
set1 = set()
for w in words:
if len(w.strip()) < 2 or w.lower() in stop: # 如果词语长度小于2或者读到停止词
continue
set1.add(w)
for i in set1:
d[i] = d.get(i, 0.0) + 1.0
class VectorSpaceModel:
def __init__(self, docs,cn=5000):
self.tf=[0.0 for i in range(1000)]
self.idf=[0.0 for i in range(1000)]
self.bag_of_words = [] # 数据中的关键词
self.token2id = {} #词编号
self.tfidf=[0.0 for i in range(1000)]
self.docx=[] #关键词
self.docs = docs # 读取数据,列表
self.num_docs =4000
with open('5-1.txt','r+',encoding='utf-8') as f:
words=f.readlines()
for word in words:
word=word.replace('\n',"")
self.docx.append(word)
self.similarity = [[0.0 for i in range(1000)]] # 初始化相似度矩阵
self.initialize_bow() # 初试化词袋
self.Gettfs() # 计算tf
self.Getidfs() # 计算idf
self.Gettfidfs() # 计算tf-idf
def initialize_bow(self):
for doc in self.docx: #使用关键词建立词袋
counter = defaultdict(int)
counter_res=defaultdict(int)
if doc not in self.token2id: #如果该词还未被编号
self.token2id[doc] = len(self.token2id) # 为词进行编号
for doc in self.docs: #遍历该文档的所有词
if doc in self.docx: #如果该词汇是关键词
counter[self.token2id[doc]] += 1 #关键词出现次数+1
list1=sorted(counter.items(),key=lambda x:x[0]) #按关键词编号顺序排序
for i in list1:
counter_res[i[0]]=i[1]
self.bag_of_words.append(counter_res)
def Gettfs(self):#计算tf
total_fs=len(self.docs)
if self.bag_of_words[0]=={}:
return
for bow in self.bag_of_words:
for termid,fs in bow.items():
self.tf[termid]=float(fs)/float(total_fs)
def Getidfs(self):#计算给定数据的idf
if self.bag_of_words[0]=={}:
return
for bow in self.bag_of_words:
for termid,fs in bow.items():
count=d[self.docx[termid]]
self.idf[termid]=math.log2(float(4000)/count)
def Gettfidfs(self):#计算tf-idf
for i in range(1000):
self.tfidf[i]=self.tf[i]*self.idf[i]
if cn <4000:
TFIDFs[cn][i]= self.tf[i] * self.idf[i]
def pr5():
print('*****************我是分割线**********************')
if __name__ == "__main__":
print("任务一:")
cn = 0
docs = []
fname1 = 'neg'
fname2 = 'pos'
# 当前文件的路径
dir = os.path.dirname(__file__)
# 拼接文件路径
f1 = os.path.join(dir, path + fname1)
f2 = os.path.join(dir, path, fname2)
numf1 = len([name for name in os.listdir(f1) if os.path.isfile(os.path.join(f1, name))]) + 1
numf2 = len([name for name in os.listdir(f2) if os.path.isfile(os.path.join(f2, name))]) + 1
# 遍历所有文件
l1 = os.listdir(f1)# 返回当前文件夹的文件列表
l2 = os.listdir(f2)# 返回当前文件夹的文件列表
#计算每一个词的文档频率
for j in l1:
file_name= fname1 + '/' + j
add_file(file_name)
for j in l2:
file_name = fname2 + '/' + j
add_file(file_name)
for j in l1: #文件挨个计算
file_name= fname1 + '/' + j
if (os.path.exists(path+ file_name)):
s0 = open(file_name, 'rb').read() if file_name[0] == '/' or file_name[0] == 'C' else open(path + file_name, 'rb').read() #读出来的数据
words = jieba.cut(s0)
for i in words:
docs.append(i)
VectorSpaceModel(docs,cn)
cn+=1
docs=[]
for j in l2:
file_name = fname2 + '/' + j
if (os.path.exists(path+ file_name)):
s0 = open(file_name, 'rb').read() if file_name[0] == '/' or file_name[0] == 'C' else open(path + file_name, 'rb').read()
words = jieba.cut(s0)
for i in words:
docs.append(i)
VectorSpaceModel(docs, cn)
docs = []
cn+=1
with open("6-1.txt",'w+',encoding='utf-8')as f:
for i in range(4000):
if i <2000:
f.write(l1[i].replace('neg', '').replace('.', '').replace('txt', '') + '\t0\t'+str(TFIDFs[i])+'\n')
else:
f.write(l2[i-2000].replace('pos', '').replace('.', '').replace('txt', '') + '\t1\t'+str(TFIDFs[i])+'\n')
ToCSV("tfidf.csv","6-1.txt")
print("任务1完成!数据已存至6-1.txt")
任务2:
import csv
import jieba
import os
from collections import defaultdict
import math
import numpy as np
import heapq
path='./中文信息处理技术实验数据/htl_del_4000/'
TFIDFs=[[0.0 for x in range(1000)] for x in range(4000)]
d = {} #记录文档频率
stop=[]#停用词
s0 = open('cn_stopwords.txt', 'r', encoding='utf-8').read()
for l in s0.splitlines():#按照行('\r', '\r\n', \n')分隔.返回停用词表
stop.append(l)
#转化为CSV文件
def ToCSV(file1,file2):
with open(file1, 'w', newline='', encoding='utf-8') as cf:
writer = csv.writer(cf)
writer.writerow(["id", "标签","向量"])
with open(file2, 'r', encoding='utf-8') as f:
l = f.readlines()
for s in l:
l1 = s.split('\t') # l1[0]为文档id,l1[1]为标签,l1[2]为向量
writer.writerow([l1[0], l1[1],l1[2]])
def add_file( file_name):#加载文件
if (os.path.exists(path + file_name)):
content = open(file_name, 'rb').read() if file_name[0] == '/' or file_name[0] == 'C' else open(path + file_name, 'rb').read()
words = jieba.cut(content)
else:
return
set1 = set()
for w in words:
if len(w.strip()) < 2 or w.lower() in stop: # 如果词语长度小于2或者读到停止词
continue
set1.add(w)
for i in set1:
d[i] = d.get(i, 0.0) + 1.0
class VectorSpaceModel:
def __init__(self, docs,cn=5000):
self.tf=[0.0 for i in range(1000)]
self.idf=[0.0 for i in range(1000)]
self.bag_of_words = [] # 数据中的关键词
self.token2id = {} #词编号
self.tfidf=[0.0 for i in range(1000)]
self.docx=[] #关键词
self.docs = docs # 读取数据,列表
self.num_docs =4000
with open('5-1.txt','r+',encoding='utf-8') as f:
words=f.readlines()
for word in words:
word=word.replace('\n',"")
self.docx.append(word)
self.similarity = [[0.0 for i in range(1000)]] # 初始化相似度矩阵
self.initialize_bow() # 初试化词袋
self.Gettfs() # 计算tf
self.Getidfs() # 计算idf
self.Gettfidfs() # 计算tf-idf
def initialize_bow(self):
for doc in self.docx: #使用关键词建立词袋
counter = defaultdict(int)
counter_res=defaultdict(int)
if doc not in self.token2id: #如果该词还未被编号
self.token2id[doc] = len(self.token2id) # 为词进行编号
for doc in self.docs: #遍历该文档的所有词
if doc in self.docx: #如果该词汇是关键词
counter[self.token2id[doc]] += 1 #关键词出现次数+1
list1=sorted(counter.items(),key=lambda x:x[0]) #按关键词编号顺序排序
for i in list1:
counter_res[i[0]]=i[1]
self.bag_of_words.append(counter_res)
def Gettfs(self):#计算tf
total_fs=len(self.docs)
if self.bag_of_words[0]=={}:
return
for bow in self.bag_of_words:
for termid,fs in bow.items():
self.tf[termid]=float(fs)/float(total_fs)
def Getidfs(self):#计算给定数据的idf
if self.bag_of_words[0]=={}:
return
for bow in self.bag_of_words:
for termid,fs in bow.items():
count=d[self.docx[termid]]
self.idf[termid]=math.log2(float(4000)/count)
def Gettfidfs(self):#计算tf-idf
for i in range(1000):
self.tfidf[i]=self.tf[i]*self.idf[i]
if cn <4000:
TFIDFs[cn][i]= self.tf[i] * self.idf[i]
if __name__ == "__main__":
cn = 0
docs = []
fname1 = 'neg'
fname2 = 'pos'
# 当前文件的路径
dir = os.path.dirname(__file__)
# 拼接文件路径
f1 = os.path.join(dir, path + fname1)
f2 = os.path.join(dir, path, fname2)
numf1 = len([name for name in os.listdir(f1) if os.path.isfile(os.path.join(f1, name))]) + 1
numf2 = len([name for name in os.listdir(f2) if os.path.isfile(os.path.join(f2, name))]) + 1
# 遍历所有文件
l1 = os.listdir(f1)# 返回当前文件夹的文件列表
l2 = os.listdir(f2)# 返回当前文件夹的文件列表
#计算每一个词的文档频率
for j in l1:
file_name= fname1 + '/' + j
add_file(file_name)
for j in l2:
file_name = fname2 + '/' + j
add_file(file_name)
for j in l1: #文件挨个计算
file_name= fname1 + '/' + j
if (os.path.exists(path+ file_name)):
s0 = open(file_name, 'rb').read() if file_name[0] == '/' or file_name[0] == 'C' else open(path + file_name, 'rb').read() #读出来的数据
words = jieba.cut(s0)
for i in words:
docs.append(i)
VectorSpaceModel(docs,cn)
cn+=1
docs=[]
for j in l2:
file_name = fname2 + '/' + j
if (os.path.exists(path+ file_name)):
s0 = open(file_name, 'rb').read() if file_name[0] == '/' or file_name[0] == 'C' else open(path + file_name, 'rb').read()
words = jieba.cut(s0)
for i in words:
docs.append(i)
VectorSpaceModel(docs, cn)
docs = []
cn+=1
with open("6-1.txt",'w+',encoding='utf-8')as f:
for i in range(4000):
if i <2000:
f.write(l1[i].replace('neg', '').replace('.', '').replace('txt', '') + '\t0\t'+str(TFIDFs[i])+'\n')
else:
f.write(l2[i-2000].replace('pos', '').replace('.', '').replace('txt', '') + '\t1\t'+str(TFIDFs[i])+'\n')
ToCSV("tfidf.csv","6-1.txt")
print("任务2:")
tt = input("请输入文本:")
words = jieba.cut(tt)
for i in words:
docs.append(i)
TFIDF = VectorSpaceModel(docs, cn).tfidf
sim=[0.0 for i in range(4000)]
np.array(TFIDFs)
np.array(TFIDF)
for i in range(4000):
a=np.array(TFIDFs[i])
b=np.array(TFIDF)
if a.dot(b) !=0:
sim[i]= a.dot(b) / np.linalg.norm(a) * np.linalg.norm(b)
else:
sim[i]=0
r1 = map(sim.index, heapq.nlargest(3, sim)) # 求最大的三个索引 nsmallest与nlargest相反,求最小
r2 = heapq.nlargest(3, sim) # 求最大的三个元素
with open("6-2.txt",'w+',encoding='utf-8')as f:
l3 = list(r1) # 前三个最大的索引
f.write("相似文章 相似度\n")
for i in l3:
if i < 2000:
f.write(l1[i]+"\t"+str(sim[i])+'\n')
if i >= 2000:
i = i - 2000
f.write(l2[i]+"\t"+str(sim[i + 2000])+'\n')
print("任务2成!数据已存至6-2.txt")
实验结果:
1. 基于tfidf的特征权重
2.相似度