实验目的
掌握文本处理和分析的内容:
-
- 掌握文本预处理的基本技术
-
- 掌握倒排文档的实现
-
- 掌握向量空间模型的实现
-
- 掌握文本分类模型的实现
链接分析的内容:
-
- 掌握 PageRank 算法的原理和实现
实验步骤
实验三 文本处理与分析
一、 文本预处理
1. 词汇切分
实现的正向减字最大匹配法代码如下:
import os
# 读取所有的字典中的词汇,并排序
dicts = []
with open("./dict_example.txt", "r", encoding="utf-8") as dicts_input:
for word in dicts_input:
dicts.append(word.split("\n")[0].strip())
dicts.sort(key=lambda i: len(i), reverse=True)
print(dicts)
# 输入句子
sentence = input("input sentence: ")
print(sentence)
word_cut_ans = [] # 分析结果
dicts_word_max_len = len(dicts[0])
sentence_len = len(sentence)
i = 0;
while(sentence_len > 0):
s = sentence[0:]
# s = sentence[0:dicts_word_max_len]
s_len = len(s)
while(s_len > 0):
print("s[" + str(i) + "]: " + s)
i += 1
if(s in dicts):
word_cut_ans.append(s)
break
elif(s_len == 1):
word_cut_ans.append(s)
break
else:
s_len -= 1
s = s[0: s_len]
sentence = sentence[s_len:]
sentence_len -= s_len
print(word_cut_ans)
当测试用例为: 今天是中华人民共和国获得奥运会举办权的日子
时的输出为:
PS G:\Backup\CollegeProjectBackup\ExperimentalReport\搜 > python -u "g:\Backup\CollegeProjectBackup\ExperimentalReport\搜索引
擎\实验二\实验三 文本处理与分析\一、 文本预处理\1. 词汇切分\word_cut.py"
['中华人民共和国', '人民共和国', '中华人民', '共和国', '奥运会', '举办权', '今天', '中华', '华人', '人民', '共和', '获得', '奥
运', '举办', '日子', '今', '天', '是', '中', '华', '人', '民',
'共', '和', '国', '获', '得', '奥', '运', '会', '举', '办', '权
', '的', '日', '子']
input sentence: 今天是中华人民共和国获得奥运会举办权的日子
今天是中华人民共和国获得奥运会举办权的日子
s[0]: 今天是中华人民共和国获得奥运会举办权的日子
s[1]: 今天是中华人民共和国获得奥运会举办权的日
s[2]: 今天是中华人民共和国获得奥运会举办权的
s[3]: 今天是中华人民共和国获得奥运会举办权
s[4]: 今天是中华人民共和国获得奥运会举办
s[5]: 今天是中华人民共和国获得奥运会举
s[6]: 今天是中华人民共和国获得奥运会
s[7]: 今天是中华人民共和国获得奥运
s[8]: 今天是中华人民共和国获得奥
s[9]: 今天是中华人民共和国获得
s[10]: 今天是中华人民共和国获
s[11]: 今天是中华人民共和国
s[12]: 今天是中华人民共和
s[13]: 今天是中华人民共
s[14]: 今天是中华人民
s[15]: 今天是中华人
s[16]: 今天是中华
s[17]: 今天是中
s[18]: 今天是
s[19]: 今天
s[20]: 是中华人民共和国获得奥运会举办权的日子
s[21]: 是中华人民共和国获得奥运会举办权的日
s[22]: 是中华人民共和国获得奥运会举办权的
s[23]: 是中华人民共和国获得奥运会举办权
s[24]: 是中华人民共和国获得奥运会举办
s[25]: 是中华人民共和国获得奥运会举
s[26]: 是中华人民共和国获得奥运会
s[27]: 是中华人民共和国获得奥运
s[28]: 是中华人民共和国获得奥
s[29]: 是中华人民共和国获得
s[30]: 是中华人民共和国获
s[31]: 是中华人民共和国
s[32]: 是中华人民共和
s[33]: 是中华人民共
s[34]: 是中华人民
s[35]: 是中华人
s[36]: 是中华
s[37]: 是中
s[38]: 是
s[39]: 中华人民共和国获得奥运会举办权的日子
s[40]: 中华人民共和国获得奥运会举办权的日
s[41]: 中华人民共和国获得奥运会举办权的
s[42]: 中华人民共和国获得奥运会举办权
s[43]: 中华人民共和国获得奥运会举办
s[44]: 中华人民共和国获得奥运会举
s[45]: 中华人民共和国获得奥运会
s[46]: 中华人民共和国获得奥运
s[47]: 中华人民共和国获得奥
s[48]: 中华人民共和国获得
s[49]: 中华人民共和国获
s[50]: 中华人民共和国
s[51]: 获得奥运会举办权的日子
s[52]: 获得奥运会举办权的日
s[53]: 获得奥运会举办权的
s[54]: 获得奥运会举办权
s[55]: 获得奥运会举办
s[56]: 获得奥运会举
s[57]: 获得奥运会
s[58]: 获得奥运
s[59]: 获得奥
s[60]: 获得
s[61]: 奥运会举办权的日子
s[62]: 奥运会举办权的日
s[63]: 奥运会举办权的
s[64]: 奥运会举办权
s[65]: 奥运会举办
s[66]: 奥运会举
s[67]: 奥运会
s[68]: 举办权的日子
s[69]: 举办权的日
s[70]: 举办权的
s[71]: 举办权
s[72]: 的日子
s[73]: 的日
s[74]: 的
s[75]: 日子
['今天', '是', '中华人民共和国', '获得', '奥运会', '举办权', '
的', '日子']
当每一次搜索模式串的长度为词汇中最长值和字符串最长值的最小值时,可以明显减小一些无意义的匹配过程,实现方法是将源代码中的注释解除即可:
PS G:\Backup\CollegeProjectBackup\ExperimentalReport\搜 > python -u "g:\Backup\CollegeProjectBackup\ExperimentalReport\搜索引
擎\实验二\实验三 文本处理与分析\一、 文本预处理\1. 词汇切分\word_cut.py"
['中华人民共和国', '人民共和国', '中华人民', '共和国', '奥运会', '举办权', '今天', '中华', '华人', '人民', '共和', '获得', '奥
运', '举办', '日子', '今', '天', '是', '中', '华', '人', '民',
'共', '和', '国', '获', '得', '奥', '运', '会', '举', '办', '权
', '的', '日', '子']
input sentence: 今天是中华人民共和国获得奥运会举办权的日子
今天是中华人民共和国获得奥运会举办权的日子
s[0]: 今天是中华人民
s[1]: 今天是中华人
s[2]: 今天是中华
s[3]: 今天是中
s[4]: 今天是
s[5]: 今天
s[6]: 是中华人民共和
s[7]: 是中华人民共
s[8]: 是中华人民
s[9]: 是中华人
s[10]: 是中华
s[11]: 是中
s[12]: 是
s[13]: 中华人民共和国
s[14]: 获得奥运会举办
s[15]: 获得奥运会举
s[16]: 获得奥运会
s[17]: 获得奥运
s[18]: 获得奥
s[19]: 获得
s[20]: 奥运会举办权的
s[21]: 奥运会举办权
s[22]: 奥运会举办
s[23]: 奥运会举
s[24]: 奥运会
s[25]: 举办权的日子
s[26]: 举办权的日
s[27]: 举办权的
s[28]: 举办权
s[29]: 的日子
s[30]: 的日
s[31]: 的
s[32]: 日子
['今天', '是', '中华人民共和国', '获得', '奥运会', '举办权', '
的', '日子']
二、 倒排文档的实现
对jieba的倒排文本代码添加停用词的实现,即+号所在行,停用词来源为: 百度停用词
import jieba
def mapper(lineNum, list):
dic = {}
for item in list:
key = ''.join([str(lineNum), ':', item])
if key in dic:
ll = dic.get(key)
ll.append(1)
dic[key] = ll
else:
dic[key] = [1]
return dic
def reducer(dic):
keys = dic.keys()
rdic = {}
for key in keys:
lineNum, kk = key.split(":")
ss = ''.join([lineNum, ':', str(dic.get(key))])
if kk in rdic:
ll = rdic[kk]
ll.append(ss)
rdic[kk] = ll
else:
rdic[kk] = [ss]
return rdic
def combiner(dic):
keys = dic.keys()
tdic = {}
for key in keys:
valuelist = dic.get(key)
count = 0
for i in valuelist:
count += i
tdic[key] = count
return tdic
+ stopwords = []
+ def remove_stopwords(word_list):
+ if(len(stopwords) == 0):
+ with open("./stopwords.txt", "r", encoding="utf-8") as word_input:
+ for word in word_input:
+ stopwords.append(word.split("\n")[0].strip())
+ new_word_list = []
+ for word in word_list:
+ if(word in stopwords):
+ continue
+ new_word_list.append(word)
+ return new_word_list
def get_reverse_index(filepath):
file = open(filepath, 'r', encoding="utf8")
lineNum = 0
rdic_p = {}
while True:
lineNum += 1
line = file.readline()
if line != '':
print(lineNum, ' ', line)
else:
break
# 先分词
word_list = list(jieba.cut(line))
+ word_list = remove_stopwords(word_list)
mdic = mapper(lineNum, word_list)
cdic = combiner(mdic)
print(cdic)
rdic_p.update(cdic)
rdic = reducer(rdic_p)
print(rdic)
return rdic
if __name__ == '__main__':
# data 文档
dic = get_reverse_index('./data.txt')
while(1):
search_word = input('Please input the word you want to search: ')
if (search_word in dic):
print(dic.get(search_word))
else:
print(-1)
三、 向量空间模型的实现
四、 文本分类的实现
SVM:
(research) G:\Backup\CollegeProjectBackup\ExperimentalReport\搜
索引擎\实验二\实验三 文本处理与分析\四、 文本分类的实现>python
train_classifier.py
请输入训练集的的根目录:data\train
Building prefix dict from the default dictionary ...
Loading model from cache C:\Users\314159~1\AppData\Local\Temp\jieba.cache
Loading model cost 0.795 seconds.
Prefix dict has been built successfully.
训练完成!
(research) G:\Backup\CollegeProjectBackup\ExperimentalReport\搜
索引擎\实验二\实验三 文本处理与分析\四、 文本分类的实现>python
classify.py
请输入模型文件的目录:.
请输入包含训练集的根目录:data\train
请输入包含测试集的根目录:data\test
Building prefix dict from the default dictionary ...
Loading model from cache C:\Users\314159~1\AppData\Local\Temp\jieba.cache
Loading model cost 0.792 seconds.
Prefix dict has been built successfully.
[[12 1 7]
[ 0 16 4]
[ 0 1 19]]
precision recall f1-score support
class 0 1.00 0.60 0.75 20
class 1 0.89 0.80 0.84 20
class 2 0.63 0.95 0.76 20
accuracy 0.78 60
macro avg 0.84 0.78 0.78 60
weighted avg 0.84 0.78 0.78 60
正确率=0.7833333333333333
分类完成!
KNN:
训练代码如下,添加knn:
#训练分类器
import jieba,os
from gensim import corpora
from sklearn import svm
from sklearn import neighbors
from sklearn.feature_extraction.text import TfidfVectorizer
import joblib
#读取所有文本信息,生成文档列表
def load_data(trainsdir):
documents=[]
label=[]
#读取每个子目录下的文本文件
subdirs=os.walk(trainsdir)
for d,s,fns in subdirs:
for fn in fns:
if fn[-3:]=='txt':
#print(d+os.sep+fn)
#根据文件编码指定编码方式:utf-8,gbk,ansi等
f=open(d+os.sep+fn, "r",encoding="ansi")
filecontent=f.read()
documents.append(filecontent)
label.append(d[d.rindex("\\")+1:]) #子目录名称作为类别标签
return documents,label
#预处理:分词、停用词过滤、词频过滤、特征选择
def preprocess(documents):
stoplist=open('stopword.txt','r',encoding="utf-8").readlines()
stoplist = set(w.strip() for w in stoplist)
#分词、去停用词
texts=[]
for document in documents:
doc=[]
for w in list(jieba.cut(document,cut_all=True)):
if len(w)>1 and w not in stoplist:
doc.append(w)
texts.append(doc)
#生成词典
dictionary=corpora.Dictionary(texts)
dictionary.filter_extremes(no_below=3, no_above=1.0,keep_n=1000)
return texts,dictionary
#训练svm分类器:构造TFIDF矩阵、SVM参数拟合
def train_svm(train_data, dictionary,train_tags):
traindata=[]
dlist=list(dictionary.values())
for l in train_data:
words=""
for w in l:
if w in dlist:
words = words+w+" "
traindata.append(words)
v = TfidfVectorizer()
tdata = v.fit_transform(traindata)
svc = svm.SVC(kernel='rbf',gamma='auto')
svc.fit(tdata,train_tags)
return svc
#训练knn分类器:构造TFIDF矩阵、knn参数拟合
def train_knn(train_data, dictionary, train_tags):
traindata=[]
dlist=list(dictionary.values())
for l in train_data:
words=""
for w in l:
if w in dlist:
words = words+w+" "
traindata.append(words)
v = TfidfVectorizer()
tdata = v.fit_transform(traindata)
knn = neighbors.KNeighborsClassifier(n_neighbors=3, metric='euclidean')
knn.fit(tdata, train_tags)
return knn
if __name__ == '__main__':
newsdir=input("请输入训练集的的根目录:")
docs,label=load_data(newsdir)
corpus, dictionary=preprocess(docs)
# svm=train_svm(corpus,dictionary,label)
knn = train_knn(corpus, dictionary, label)
dictionary.save("classifier_knn.dict")
# joblib.dump(svm, "svm.model")
joblib.dump(knn, "knn.model")
print("训练完成!")
测试分类代码如下:
#使用SVM进行分类测试
import jieba,os
from gensim import corpora
from sklearn import svm
from sklearn.feature_extraction.text import TfidfVectorizer
import joblib
from sklearn.metrics import confusion_matrix
from sklearn.metrics import classification_report
#训练svm分类器及词典
def loadmodel(modeldir):
svm = joblib.load("svm.model")
dictionary = corpora.Dictionary.load('classifier.dict')
return svm,dictionary
#训练svm分类器及词典
def loadmodel_knn(modeldir):
knn = joblib.load("knn.model")
dictionary = corpora.Dictionary.load('classifier_knn.dict')
return knn,dictionary
'''读取所有文本信息,生成文档列表.
测试样本位于列表的前面,测试样本个数与label大小一致
包含训练集,因IDF的计算与训练集有关
'''
def load_data(trainsdir,testdir):
documents=[]
label=[]
#读取每个testdir子目录下的文本文件
subdirs=os.walk(testdir)
for d,s,fns in subdirs:
for fn in fns:
if fn[-3:]=='txt':
#print(d+os.sep+fn)
#根据文件编码指定编码方式:utf-8,gbk,ansi等
f=open(d+os.sep+fn, "r",encoding="ansi")
filecontent=f.read()
documents.append(filecontent)
label.append(d[d.rindex("\\")+1:]) #子目录名称作为类别标签
#读取每个trainsdir子目录下的文本文件
subdirs=os.walk(trainsdir)
for d,s,fns in subdirs:
for fn in fns:
if fn[-3:]=='txt':
#print(d+os.sep+fn)
#根据文件编码指定编码方式:utf-8,gbk,ansi等
f=open(d+os.sep+fn, "r",encoding="ansi")
filecontent=f.read()
documents.append(filecontent)
return documents,label
#预处理:分词、特征词过滤,生成新的文档列表
def preprocess(documents,dictionary):
stoplist=open('stopword.txt','r',encoding="utf-8").readlines()
stoplist = set(w.strip() for w in stoplist)
dclist=list(dictionary.values())
#分词、去停用词
texts=[]
for document in documents:
doc=[]
for w in list(jieba.cut(document,cut_all=True)):
if w in dclist:
doc.append(w)
texts.append(doc)
return texts
#分类
def svm_classify(svm,dataset, dictionary, test_tags):
data=[]
testresult=[]
dlist=list(dictionary.values())
for l in dataset:
words=""
for w in l:
if w in dlist:
words = words+w+" "
data.append(words)
#把文档集(由空格隔开的词汇序列组成的文档)转换成为tfidf向量
v = TfidfVectorizer()
tdata = v.fit_transform(data)
correct=0
#获取测试样本(待分类的眼本),输出分类结果
for i in range(len(test_tags)):
test_X=tdata[i]
r=svm.predict(test_X) #此处test_X为特征集
testresult.append(r[0])
if r[0]==test_tags[i]:
correct+=1
#性能评估
cm=confusion_matrix(test_tags,testresult)
print(cm)
target_names = ['class 0', 'class 1', 'class 2']
print(classification_report(test_tags,testresult, target_names=target_names))
print("正确率=" + str(correct/len(test_tags)))
return
#knn分类
def knn_classify(knn,dataset, dictionary, test_tags):
data=[]
testresult=[]
dlist=list(dictionary.values())
for l in dataset:
words=""
for w in l:
if w in dlist:
words = words+w+" "
data.append(words)
#把文档集(由空格隔开的词汇序列组成的文档)转换成为tfidf向量
v = TfidfVectorizer()
tdata = v.fit_transform(data)
correct=0
#获取测试样本(待分类的眼本),输出分类结果
for i in range(len(test_tags)):
test_X=tdata[i]
# r=svm.predict(test_X) #此处test_X为特征集
r=knn.predict(test_X) #此处test_X为特征集
testresult.append(r[0])
if r[0]==test_tags[i]:
correct+=1
#性能评估
cm=confusion_matrix(test_tags,testresult)
print(cm)
target_names = ['class 0', 'class 1', 'class 2']
print(classification_report(test_tags,testresult, target_names=target_names))
print("正确率=" + str(correct/len(test_tags)))
return
if __name__ == '__main__':
modeldir=input("请输入模型文件的目录:")
# svm,dictionary=loadmodel(modeldir)
knn,dictionary=loadmodel(modeldir)
trainsdir=input("请输入包含训练集的根目录:")
testdir=input("请输入包含测试集的根目录:")
documents,label=load_data(trainsdir,testdir);
dataset=preprocess(documents,dictionary)
# svm_classify(svm,dataset,dictionary,label)
knn_classify(knn,dataset,dictionary,label)
print("分类完成!")
(research) G:\Backup\CollegeProjectBackup\ExperimentalReport\搜
索引擎\实验二\实验三 文本处理与分析\四、 文本分类的实现>python
classify.py
请输入模型文件的目录:.
请输入包含训练集的根目录:data\train
请输入包含测试集的根目录:data\test
Building prefix dict from the default dictionary ...
Loading model from cache C:\Users\314159~1\AppData\Local\Temp\jieba.cache
Loading model cost 1.171 seconds.
Prefix dict has been built successfully.
[[12 1 7]
[ 0 16 4]
[ 0 1 19]]
precision recall f1-score support
class 0 1.00 0.60 0.75 20
class 1 0.89 0.80 0.84 20
class 2 0.63 0.95 0.76 20
accuracy 0.78 60
macro avg 0.84 0.78 0.78 60
weighted avg 0.84 0.78 0.78 60
正确率=0.7833333333333333
分类完成!
实验四 链接分析
一、 基于 PageRank 的链接分析
根据pagerank的计算公式:
P
R
n
(
A
)
=
(
1
−
d
)
/
N
+
d
×
(
∑
i
=
1
m
P
R
n
−
1
(
T
i
)
C
(
T
i
)
PR_n(A)=(1-d)/N+d\times(\sum_{i=1}^{m}\frac{PR_{n-1}(T_i)}{C(T_i)}
PRn(A)=(1−d)/N+d×(∑i=1mC(Ti)PRn−1(Ti) ,其中,:
P
R
n
(
A
)
PR_n(A)
PRn(A) 是网页 A 的 PageRank 值,
P
R
n
−
1
(
T
i
)
PR_{n-1}(T_i)
PRn−1(Ti)是指网页
T
i
T_i
Ti 存在指向 A 的链接, 并且网页在上一次迭代时的 PageRank 值,
C
(
T
i
)
C(T_i)
C(Ti)是指网页
T
i
T_i
Ti 的外链数量。d 是 平滑因子,N 是页面总数。
如果将网页间的链接引用看作一个有向图,那么上述计算公式可以理解 该次迭代的pagerand值 为一堆的常数和 上一次PR值同图的出边数的相除结果并取有出边的做和运算即可 ,所以只需定义一个 有向图的临界矩阵E[n*n] 、 每个点的出边总数C[1*n] 以及一个 存储结果的PR[1*n] 即可完成迭代运算。每一次迭代的计算过程为: P R = ( 1 − d ) / N + d × ( P R / C ) ⋅ E PR = (1 - d) / N + d \times (PR / C) \cdot E PR=(1−d)/N+d×(PR/C)⋅E 。
代码如下:
import numpy as np
n, m = map(int, input("点数和边数: ").split())
print(n, m)
E = np.zeros((n, n))
for i in range(m):
u, v = map(int, input("u->v: ").split())
E[u - 1][v - 1] = 1
print(E)
C = np.sum(E, axis=1)
print(C)
PR = np.zeros((1, n))
print(PR)
it_num = int(input("迭代次数: "))
d = float(input("平滑因子: "))
const = (1 - d) / n
for i in range(it_num):
PR = np.dot(PR / C, E) * d + const
print(PR)
print("final res: " + str(PR))
20多次的迭代计算结果基本收敛:
PS G:\Backup\CollegeProjectBackup\ExperimentalReport\搜索引擎\
实验二> python -u "g:\Backup\CollegeProjectBackup\ExperimentalReport\搜索引擎\实验二\实验四 链接分析\一、 基于 PageRank 的链接
分析\pagerank.py"
点数和边数: 3 4
3 4
u->v: 1 2
u->v: 1 3
u->v: 2 3
u->v: 3 1
[[0. 1. 1.]
[0. 0. 1.]
[1. 0. 0.]]
[2. 1. 1.]
[[0. 0. 0.]]
迭代次数: 30
平滑因子: 0.5
[[0.16666667 0.16666667 0.16666667]]
[[0.25 0.20833333 0.29166667]]
[[0.3125 0.22916667 0.33333333]]
[[0.33333333 0.24479167 0.359375 ]]
[[0.34635417 0.25 0.37239583]]
[[0.35286458 0.25325521 0.37825521]]
[[0.35579427 0.25488281 0.38151042]]
[[0.35742188 0.25561523 0.38305664]]
[[0.35819499 0.25602214 0.38382975]]
[[0.35858154 0.25621541 0.38422648]]
[[0.35877991 0.25631205 0.38441976]]
[[0.35887655 0.25636164 0.38451767]]
[[0.3589255 0.2563858 0.38456662]]
[[0.35894998 0.25639804 0.38459094]]
[[0.35896214 0.25640416 0.38460318]]
[[0.35896826 0.2564072 0.38460928]]
[[0.35897131 0.25640873 0.38461233]]
[[0.35897283 0.25640949 0.38461386]]
[[0.3589736 0.25640987 0.38461462]]
[[0.35897398 0.25641007 0.384615 ]]
[[0.35897417 0.25641016 0.38461519]]
[[0.35897426 0.25641021 0.38461529]]
[[0.35897431 0.25641023 0.38461534]]
[[0.35897434 0.25641024 0.38461536]]
[[0.35897435 0.25641025 0.38461537]]
[[0.35897435 0.25641025 0.38461538]]
[[0.35897436 0.25641025 0.38461538]]
[[0.35897436 0.25641026 0.38461538]]
[[0.35897436 0.25641026 0.38461538]]
[[0.35897436 0.25641026 0.38461538]]
final res: [[0.35897436 0.25641026 0.38461538]]