目录
前言
本项目以学术论文、维基百科等数据集为基础,通过应用TextRank和Seq2Seq算法对模型进行优化和改进,致力于构建一体化的文章摘要、标题和关键词辅助生成系统。为了提供用户友好的体验,我们设计了可视化界面,并将整个程序封装为可执行文件,可以在PC端直接运行。
首先,我们使用学术论文和维基百科等丰富的数据集作为训练样本。通过训练和优化TextRank算法,我们可以提取文章中的关键句子和关键词,形成精炼的摘要内容。
其次,我们利用Seq2Seq算法进行模型改进,以进一步提高文章相关内容的生成质量。Seq2Seq模型是一种基于循环神经网络(RNN)的序列到序列模型,能够将输入序列转化为输出序列。通过使用Seq2Seq算法,我们可以根据文章内容生成具有概括性和吸引力的标题。
同时,我们还为用户提供了可视化界面,使其能够轻松使用这一一体化的辅助生成系统。用户可以输入文章内容,系统将自动生成文章摘要、标题和关键词,并将结果直观地展示在界面上。
为了方便用户使用,我们将整个项目封装为可执行文件,使其可以在PC端直接运行。这样,用户可以直接打开程序,使用系统提供的功能,而无需进行复杂的配置和设置。
通过本项目的实施,用户可以快速生成具有准确、简洁且吸引人的文章摘要、标题和关键词,大大提高工作效率和创作质量。同时,可视化界面和可执行文件的设计使得系统的使用变得更加便捷和直观,满足用户对于简单易用的需求。
总体设计
本部分包括系统整体结构图和系统流程图。
系统整体结构图
系统整体结构如图所示。
系统流程图
系统流程如图所示。
运行环境
本部分包括Python环境、TextRank环境、TensorFlow环境、PyQt5及Qt Designer运行环境。
Python 环境
版本: Python 3. 5。
TextRank环境
从清华仓库镜像中下载numpy-1.9.3.tar.gz
、networkx-2.4.tar.gz
、math-0.5.tar.gz
文件,在本地解压后,使用cmd命令行进入控制台,切换到对应目录中,执行下面命令,完成安装。
python setup.py install
TensorFlow环境
在终端中,使用pip命令进行安装,使用pip命令安装tensorflow、tarfile、matplotlib、jieba依赖包,实现TensorFlow平台相关模型的准备。
pip3 install <package name>
PyQt5及Qt Designer运行环境
使用pip命令安装与Python语言对应版本的PyQt5工具包,同时在环境中配置PyUIC5和PyQt5-tools,用于图形化界面的快速开发及转换。将上述工具添加至PyCharm编辑器的ExternalTools中。
从PyCharm编辑器的Tools- External Tools中打开Qt Designer,如下图所示,表明Qt Designer安装成功。
模块实现
本项目包括6个模块:数据预处理、抽取摘要、 模型搭建与编译、模型训练与保存、图形化界面的开发和应用封装。下面分别给出各模块的功能介绍及相关代码。
1. 数据预处理
数据预处理下载地址为http://www.sogou.com/labs/resource/cs.php,未经处理的原始数据图如下图所示。
对于其编码形式,由于文件过大,无法通过打开文件的方式获取编码,采用GBK18030可对其进行编码。处理过程如下。
1)数据提取及划分
使用正则表达式提取数据的内容,按照比例进行训练集和验证集的划分,去除文本内容长度不符合要求的数据,对划分后的标题和内容进行存储,生成的文件使用软件改变编码格式为utf-8。相关代码如下:
a)正则表达式匹配:
for_title='<contenttitle>(.*)</contenttitle>'#筛选标题,去除标签
for_content='<content>(.*)</content>'#筛选内容,去除标签
p1=re.compile(for_title)
p2=re.compile(for_content)
b)将数据筛选进行写入过程:
for i in range(4,len(data.values)+1,6):#针对位置选择相应的数据
n=p2.findall(str(data.values[i]))
text=n[0]
word=text
result=''
for w in word:
result=result+str(w.word)+' '
#对意外的情况进行替换
result=result.replace(u'\ u3000','').replace(u'\ ue40c','')
#检查数据长度是否复合需求,太长或者太短,都要舍弃
if len(result)>=1024 or len(result)==0:
id.append(i)
continue
if i<for_train:
f_content_train.write(result+'\n')
else:
f_content_test.write(result+'\n')
print((i/6)/len(range(3,len(data.values)+1,6)))
2)替换和分词
对获得的文本进行标签替换,完成分词操作。相关代码如下:
def token(self, sentence):
words = self.segmentor.segment(sentence) #分词
words = list(words)
postags = self.postagger.postag(words) #词性标注
postags = list(postags)
netags = self.recognizer.recognize(words, postags)#命名实体识别
netags = list(netags)
result = []
for i, j in zip(words, netags):
if j in ['S-Nh', 'S-Ni', 'S-Ns']:
result.append(j)
continue
result.append(i)
return result
使用上述代码后,得到4个文件——2 个训练集和2个测试集,对应文件的同一行分别为标题和内容。对所有的文字都进行标签替换,完成分词。
3)数据读取
根据得到的文件进行数据读取,相关代码如下:
data_set = [[] for _ in buckets]
with tf.gfile.GFile(source_path, mode="r") as source_file:
with tf.gfile.GFile(target_path, mode="r") as target_file:
source, target = source_file.readline(), target_file.readline()
counter = 0 #源文件和目标文件
while source and target and (not max_size or counter < max_size):
counter += 1
if counter % 10000 == 0:
print("reading data line%d"% counter)#输出信息
sys.stdout.flush()
source_ids = [int(x) for x in source.split()]
target_ids = [int(x) for x in target.split()]
target_ids.append(data_utils.EOS_ID) #添加标识
for bucket_id, (source_size, target_size) in enumerate(buckets):
if len(source_ids)<source_size and len(target_ids)<target_size:
data_set[bucket_id].append([source_ids, target_ids])
break
source, target = source_file.readline(), target_file.readline()
return data_set
该代码会自动读取信息,并将读取的数据存入bucket内,深度学习模型通过学习内容与标题生成,数据预处理在准备数据时进行,使用Python的codecs模块读取即可。
2. 抽取摘要
大多数论文的篇幅都是数万字,直接使用模型对数据进行训练与测试会耗费计算资源。因此,通过文本排序对数据进行重要性提取,算法如下。
1)排序迭代算法
首先,获得二位列表,句子为子列表,元素是单词;其次,通过判断两个单词是否同时出现在同一个时间窗口内确定链接。将所有词添加到图的链接后,使用PageRank算法进行迭代,获得平稳的单词PR值;最后,获取重要的单词列表。
def sort_words(vertex_source, edge_source, window=2, pagerank_config={'alpha': 0.85, }):#对单词的关键程度进行排序
"""
vertex_source:二维列表,子列表代表句子,其元素是单词,用来构造PageRank中的节点
edge_source:二维列表,子列表代表句子,其元素为单词,根据单词位置关系构造PageRank中的边窗口,一个句子中相邻的window个单词,两两之间认为有边
pagerank_config:PageRank的设置
"""
sorted_words = []
word_index = {}
index_word = {}
_vertex_source = vertex_source
_edge_source = edge_source
words_number = 0
for word_list in _vertex_source:#对每个句子进行处理,提取包含单词的列表
for word in word_list:
if not word in word_index:
#更新word_index 假如字典中没有单词,将这个单词与索引添加到字典中
word_index[word] = words_number
index_word[words_number] = word #对word进行反向映射
words_number += 1
graph = np.zeros((words_number, words_number))
#构建word_number*word_number的矩阵,实现图计算
for word_list in _edge_source:
for w1, w2 in combine(word_list, window):
if w1 in word_index and w2 in word_index:
index1 = word_index[w1]
index2 = word_index[w2]
graph[index1][index2] = 1.0
graph[index2][index1] = 1.0
#根据窗口判断其连接
nx_graph = nx.from_numpy_matrix(graph)
#构成邻接矩阵
scores = nx.pagerank(nx_graph, **pagerank_config)
#使用PageRank算法进行迭代
sorted_scores = sorted(scores.items(), key=lambda item: item[1], reverse=True)
for index, score in sorted_scores:
item = AttrDict(word=index_word[index], weight=score)
sorted_words.append(item)
return sorted_words
2)句子相似度算法
在用TextRank算法对句子进行输出时,使用的默认节点是句子,两个节点相互连接的权重使用句子的相似度。相关代码如下:
def get_similarity(word_list1, word_list2):#计算两个句子的相似程度
"""默认用于计算两个句子相似度的函数
word_list1, word_list2分别代表两个句子,都是由单词组成的列表
"""
words = list(set(word_list1 + word_list2))
vector1 = [float(word_list1.count(word)) for word in words]
#统计某个单词在句子中的频率
vector2 = [float(word_list2.count(word)) for word in words]
vector3 = [vector1[x] * vector2[x] for x in range(len(vector1))]
vector4 = [1 for num in vector3 if num > 0.]
co_occur_num = sum(vector4)#分子
if abs(co_occur_num) <= 1e-12:
return 0.
denominator = math.log(float(len(word_list1))) + math.log(float(len(word_list2))) #分母
if abs(denominator) < 1e-12:
return 0.
return co_occur_num / denominator #返回句子的相似度
3. 模型搭建与编译
完成数据集制作后,进行模型搭建、定义模型输入、确定损失函数。
1)模型搭建
以TensorFlow提供的模型为基础,参数使用类进行传递:
class LargeConfig(object): #定义网络结构
learning_rate = 1.0 #学习率
init_scale = 0.04
learning_rate_decay_factor = 0.99 #学习率下降
max_gradient_norm = 5.0
num_samples = 4096 #采样的Softmax
batch_size = 64
size = 256 #每层节点数
num_layers = 4 #层数
vocab_size = 50000
#模型构建
def seq2seq_f(encoder_inputs, decoder_inputs, do_decode):
return tf.contrib.legacy_seq2seq.embedding_attention_seq2seq(
encoder_inputs,#输入的句子
decoder_inputs,#输出的句子
cell,#使用的cell、LSTM或者GRU
num_encoder_symbols=source_vocab_size,#源字典的大小
num_decoder_symbols=target_vocab_size,#转换后字典的大小
embedding_size=size,#embedding的大小
output_projection=output_projection,#看字典大小
feed_previous=do_decode,#进行训练还是测试
dtype=tf.float32)
2)定义模型输入
在模型中,bucket
承接输入的字符,所以须为bucket的每个元素构建一个占位符。
#输入
self.encoder_inputs = []
self.decoder_inputs = []
self.target_weights = []
for i in xrange(buckets[-1][0]):
self.encoder_inputs.append(tf.placeholder(tf.int32, shape=[None],
name="encoder{0}".format(i)))
#为列表对象中的每个元素表示一个占位符,名称分别为encoder0、encoder1...
for i in xrange(buckets[-1][1] + 1):
self.decoder_inputs.append(tf.placeholder(tf.int32, shape=[None],
name="decoder{0}".format(i)))
self.target_weights.append(tf.placeholder(tf.float32, shape=[None],
name="weight{0}".format(i)))
#target_weights 是一个与 decoder_outputs 大小一样的矩阵
#该矩阵将目标序列长度以外的其他位置填充为标量值0
#目标是将解码器输入移位1
targets = [self.decoder_inputs[i + 1]
for i in xrange(len(self.decoder_inputs) - 1)]
#将 decoder input向右平移一个单位
3)确定损失函数
在损失函数上,使用TensorFlow
中的sampled_softmax_loss()
函数。
def sampled_loss(labels, inputs):#使用候选采样损失函数
labels = tf.reshape(labels, [-1, 1])
#需要使用32位浮点数计算sampled_softmax_loss,以避免数值不稳定性
local_b = tf.cast(b, tf.float32)
local_inputs = tf.cast(inputs, tf.float32)
return tf.cast(
tf.nn.sampled_softmax_loss( #损失函数
weights=local_w_t,
biases=local_b,
labels=labels,
inputs=local_inputs,
num_sampled=num_samples,
num_classes=self.target_vocab_size),tf.float32)
4. 模型训练与保存
设定模型结构后,定义模型训练函,以导入及调用模型。
1)定义模型训练函数
定义模型训练函数及相关操作。
def train():
#准备标题数据
print("Preparing Headline data in %s" % FLAGS.data_dir)
src_train,,dest_train,src_dev,dest_dev,_,_=data_utils.prepare_headline_data(FLAGS.data_dir, FLAGS.vocab_size)
#将获得的数据进行处理,包括:构建词典、根据词典单词ID的转换,返回路径
config = tf.ConfigProto(device_count={"CPU": 4},
inter_op_parallelism_threads=1,
intra_op_parallelism_threads=2)
with tf.Session(config = config) as sess:
print("Creating %d layers of %d units."%(FLAGS.num_layers, FLAGS.size))
model = create_model(sess, False)
#创建模型
print ("Reading development and training data (limit: %d)."
% FLAGS.max_train_data_size)
dev_set = read_data(src_dev, dest_dev)
train_set = read_data(src_train, dest_train, FLAGS.max_train_data_size)
train_bucket_sizes = [len(train_set[b]) for b in xrange(len(buckets))]
train_total_size = float(sum(train_bucket_sizes))
trainbuckets_scale=[sum(train_bucket_sizes[:i + 1]) / train_total_size
for i in xrange(len(train_bucket_sizes))]
#进行循环训练
step_time, loss = 0.0, 0.0
current_step = 0
previous_losses = []
while True:
random_number_01 = np.random.random_sample()
bucket_id = min([i for i in xrange(len(trainbuckets_scale))
if trainbuckets_scale[i] > random_number_01])
#随机选择一个bucket进行训练
start_time = time.time()
encoder_inputs,decoder_inputs,target_weights=model.get_batch(
train_set, bucket_id)
_,step_loss,_=model.step(sess, encoder_inputs, decoder_inputs,
target_weights, bucket_id, False)
step_time+=(time.time()-start_time)/FLAGS.steps_per_checkp oint
loss += step_loss / FLAGS.steps_per_checkpoint
current_step += 1
if current_step % FLAGS.steps_per_checkpoint == 0:
perplexity=math.exp(float(loss))
if loss<300
else
float("inf")
print ("global step %d learning rate %.4f step-time %.2f perplexity ""%.2f" % (model.global_step.eval(),
model.learning_rate.eval(),
step_time, perplexity)) #输出参数
if len(previous_losses)>2 and loss > max(previous_losses[-3:]):
sess.run(model.learning_rate_decay_op)
previous_losses.append(loss)
checkpoint_path=os.path.join(FLAGS.train_dir, "headline_large.ckpt")
model.saver.save(sess,checkpoint_path,
global_step=model.global_step) #检查点输出路径
step_time, loss = 0.0, 0.0
for bucket_id in xrange(len(buckets)):
if len(dev_set[bucket_id]) == 0:
print(" eval: empty bucket %d" % (bucket_id))
continue
encoder_inputs,decoder_inputs,target_weights=
model.get_batch(dev_set, bucket_id) #编解码及目标加权
_,eval_loss,_=model.step(sess,encoder_inputs,
decoder_inputs, target_weights, bucket_id, True)
eval_ppx = math.exp(float(eval_loss))#计算损失
if eval_loss < 300
else float("inf")
print("eval:bucket%dperplexity%.2f"%(bucket_id, eval_ppx))#输出困惑度
sys.stdout.flush()
2)模型导入及调用
将生成的模型放在/ckpt
文件夹内部,运行的过程中加载该模型。当程序获取句子之后,进行以下处理:
while sentence:
sen=tf.compat.as_bytes(sentence)
sen=sen.decode('utf-8')
token_ids = data_utils.sentence_to_token_ids(sen, vocab,
normalize_digits=False)
print (token_ids) # 打印ID
#选择合适的bucket
bucket_id = min([b for b in xrange(len(buckets)) if buckets[b][0] > len(token_ids)])
print ("current bucket id" + str(bucket_id))
encoder_inputs, decoder_inputs, target_weights = model.get_batch(
{bucket_id: [(token_ids, [])]}, bucket_id)
#获得模型的输出
_, _, output_logits_batch = model.step(sess, encoder_inputs,
decoder_inputs, target_weights,
bucket_id, True)
#贪婪解码器
output_logits = []
for item in output_logits_batch:
output_logits.append(item[0])
print (output_logits)
print (len(output_logits))
print (output_logits[0])
outputs = [int(np.argmax(logit)) for logit in output_logits]
print(output_logits)
#剔除程序对文本进行的标记
if data_utils.EOS_ID in outputs:
outputs = outputs[:outputs.index(data_utils.EOS_ID)]
print(" ".join([tf.compat.as_str(rev_vocab[output]) for output in outputs]))
5. 图形化界面的开发
为提高可用性,将面向代码进行操作的环境转变为面向界面的操作,通过Python提供的QtDesigner及PyQt5环境完成项目的图形化界面。
1)界面设计
从PyCharm的External Tools 中打开配置好的Qt Designer, 创建主窗口,并使用WidgetBox进行组件的布局,如下图所示。
原生组件的美观性不足,需对各组件进行样式自定义。在监控窗口中选择修改对应组件的样式表,通过添加CSS( Cascading Style Sheets,层叠样式表)完成各组件和界面的美化。如下图所示的美化图展示了“进入程序”按钮的CSS代码,此处分别设置了基础样式、点按样式及鼠标悬浮样式,使按钮的逻辑更贴近真实的使用场景,提升用户体验。
对主窗口和各组件的样式表修改后进行预览,如下图所示。
2)代码转换
将.上述界面设计保存为.ui文件,使用配置好的PyUIC5工具进行处理,得到转换后的.py代码。
对代码中无法通过程序运行渲染出的组件进行调整,例如,通过.qrc文件指定关联引入的icon修改为引用相对地址。相关代码如下:
from PyQt5 import QtCore, QtGui, QtWidgets #引入所需的库
from PyQt5 import QtCore, QtGui, QtWidgets, Qt
from PyQt5.QtWidgets import *
import PreEdit
class Ui_MainWindow_home(QtWidgets.QMainWindow): #界面类定义
def __init__(self):
super(Ui_MainWindow_home,self).__init__()
self.setupUi(self)
self.retranslateUi(self)
def setupUi(self, MainWindow_home): #设置界面
MainWindow_home.setObjectName("MainWindow_home")
MainWindow_home.resize(900, 650)
MainWindow_home.setMinimumSize(QtCore.QSize(900, 650))
MainWindow_home.setMaximumSize(QtCore.QSize(900, 650))
MainWindow_home.setBaseSize(QtCore.QSize(900, 650))
font = QtGui.QFont()
font.setFamily("黑体")
font.setPointSize(12)
MainWindow_home.setFont(font) #设置字体
MainWindow_home.setStyleSheet("QMainWindow#MainWindow_home{\n"
"background:#FFFEF8\n}")
self.centralwidget = QtWidgets.QWidget(MainWindow_home)
self.centralwidget.setStyleSheet("") #设置表单风格
self.centralwidget.setObjectName("centralwidget")
self.pushButton_openfile=QtWidgets.QPushButton(self.centralwidget)
self.pushButton_openfile.setGeometry(QtCore.QRect(320,328,258,51))
font = QtGui.QFont()
font.setFamily("等线")
font.setPointSize(11)
font.setBold(True)
font.setWeight(75)
self.pushButton_openfile.setFont(font) #单击按钮,自动浏览文件设置
self.pushButton_openfile.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
self.pushButton_openfile.setStyleSheet("QPushButton#pushButton_openfile{ \n"
"border: 1px solid #9a8878; \n"
"background-color:#ffffff;\n"
"border-style: solid; \n"
"border-radius:0px; \n"
"width: 40px; \n"
"height:20px; \n"
"padding:0 0px; \n"
"margin:0 0px; \n"
"} \n"
"\n"
"QPushButton#pushButton_openfile:pressed{\n"
"background-color:#FBF7F6;\n"
"border:0.5px solid #DDCFC2;\n"
"}\n"
"\n"
"QPushButton#pushButton_openfile:hover{\n"
"border:0.5px solid #DDCFC2;\n"
"}")
icon = QtGui.QIcon() #图标设置
icon.addPixmap(QtGui.QPixmap(r".\icon\enter2.png"),
QtGui.QIcon.Normal, QtGui.QIcon.Off)
icon.addPixmap(QtGui.QPixmap(r".\icon\enter2.png"),
QtGui.QIcon.Normal, QtGui.QIcon.On)
self.pushButton_openfile.setIcon(icon)
self.pushButton_openfile.setCheckable(False)
self.pushButton_openfile.setObjectName("pushButton_openfile")
self.label_maintitle_shadow=QtWidgets.QLabel(self.centralwidget)
self.label_maintitle_shadow.setGeometry(QtCore.QRect(331,188,241, 61))
font = QtGui.QFont() #图形界面的字体设置
font.setFamily("微软雅黑")
font.setPointSize(36)
font.setBold(True)
font.setWeight(75)
self.label_maintitle_shadow.setFont(font)
self.label_maintitle_shadow.setStyleSheet("QLabel#label_maintitle_shadow{\n"
" color:#847c74\n"
"}") #设置表单的风格
self.label_maintitle_shadow.setAlignment(QtCore.Qt.AlignCenter)
self.label_maintitle_shadow.setObjectName("label_shadow")
self.label_format = QtWidgets.QLabel(self.centralwidget)
self.label_format.setGeometry(QtCore.QRect(325, 395, 251, 20))
font = QtGui.QFont()
font.setFamily("黑体")
font.setPointSize(10)
self.label_format.setFont(font) #设置表单的格式字体
self.label_format.setStyleSheet("QLabel#label_format{\n"
"color:#3A332A\n"
"}")
self.label_format.setObjectName("label_format")
self.label_maintitle = QtWidgets.QLabel(self.centralwidget)
self.label_maintitle.setGeometry(QtCore.QRect(331, 189, 241, 61))
font = QtGui.QFont()
font.setFamily("微软雅黑")
font.setPointSize(35)
font.setBold(True)
font.setWeight(75)
self.label_maintitle.setFont(font)
self.label_maintitle.setStyleSheet("QLabel#label_maintitle{\n"
"color:#3A332A\n"
"}") #主题标签的风格设置
self.label_maintitle.setAlignment(QtCore.Qt.AlignCenter)
self.label_maintitle.setObjectName("label_maintitle")
self.label_author = QtWidgets.QLabel(self.centralwidget)
self.label_author.setGeometry(QtCore.QRect(328, 600, 251, 20))
font = QtGui.QFont()
font.setFamily("等线")
font.setPointSize(8)
self.label_author.setFont(font)
self.label_author.setStyleSheet("QLabel#label_author{\n"
"color:#97846c\n" #设置表单风格
"}")
self.label_author.setAlignment(QtCore.Qt.AlignCenter)
self.label_author.setObjectName("label_author")
MainWindow_home.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow_home)
self.menubar.setGeometry(QtCore.QRect(0, 0, 900, 23))
self.menubar.setObjectName("menubar")
MainWindow_home.setMenuBar(self.menubar) #主窗口设置菜单栏
self.statusbar = QtWidgets.QStatusBar(MainWindow_home)
self.statusbar.setObjectName("statusbar")
MainWindow_home.setStatusBar(self.statusbar) #主窗口设置状态栏
self.retranslateUi(MainWindow_home)
QtCore.QMetaObject.connectSlotsByName(MainWindow_home)
def retranslateUi(self, MainWindow_home):
_translate = QtCore.QCoreApplication.translate
MainWindow_home.setWindowTitle(_translate("MainWindow_home",
"MainWindow"))#主窗口设置窗口标题
self.pushButton_openfile.setText(_translate("MainWindow_home",
"进入程序"))
self.label_maintitle_shadow.setText(_translate("MainWindow_home",
"论文助手"))
self.label_format.setText(_translate("MainWindow_home",
"支持扩展名: .pdf .doc .docx .txt"))
self.label_maintitle.setText(_translate("MainWindow_home",
"论文助手"))
self.label_author.setText(_translate("MainWindow_home",
"Designed by Hu Tong & Li Shuolin"))
def openfile(self):
openfile_name=QFileDialog.getOpenFileName(self,'选择文件',
'','files(*.doc,*.docx,*.pdf,*.txt)')
3)界面交互
完成设计后,在界面之间建立交互关系。此处尝试两种方式: 一是定义跳转函数;二是绑定按钮的槽函数,以完成跳转。
a) 定义跳转函数
定义跳转函数及相关操作。
#Jumpmain2pre.py
from PyQt5 import QtCore, QtGui, QtWidgets
from home import Ui_MainWindow_home #跳转按钮所在界面
from PreEdit import Ui_Form #跳转到的界面
class Ui_PreEdit(Ui_Form): #定义跳转函数的名字
def __init__(self):
super(Ui_PreEdit,self).__init__() #跳转函数类名
self.setupUi(self)
#主界面
class Mainshow(Ui_MainWindow_home):
def __init__(self):
super(Mainshow,self).__init__()
self.setupUi(self)
#定义按钮功能
def loginEvent(self):
self.hide()
self.dia = Ui_PreEdit() #跳转到的界面类名
self.dia.show()
def homeshow(): #调用这个函数来执行
import sys
app=QtWidgets.QApplication(sys.argv)
first=Mainshow()
first.show()
first.pushButton_openfile.clicked.connect(first.loginEvent)
#绑定跳转功能的按钮
sys.exit(app.exec_())
b) 绑定按钮的槽函数
给需要完成跳转功能的按钮定单击事件,并使用槽函数绑定事件,此处以绑定showwaiting()函数为例:
self.pushButton_create.clicked.connect(self.showwaiting)
将按钮要绑定的事件单独定义为函数:
def showwaiting(self):
import sys
self.MainWindow = QtWidgets.QMainWindow()
self.newshow = Ui_MainWindow_sumcreating() #图形界面创建
self.newshow.setupUi(self.MainWindow) #界面设置
self.hide()
self.MainWindow.show()
print('生成中…')
c) 示例:在图形化界面中读取本地文件
将打开动作、保存动作、保存内容分别定义为三个函数,便于单击按钮时通过槽进行调用,相关代码如下:
def open_event(self): #打开文件事件
_translate = QtCore.QCoreApplication.translate
directory1 = QFileDialog.getOpenFileName(None, "选择文件", "C:/","Wo
rd文档 (*.docx;*.doc);;文本文件(*.txt);;pdf(*.pdf);;")
print(directory1) #输出路径
path = directory1[0]
self.open_path_text.setText(_translate("Form", path))
if path is not None:
with open(file=path, mode='r+', encoding='utf-8') as file:
self.text_value.setPlainText(file.read())
def save_event(self): #保存事件
global save_path
_translate = QtCore.QCoreApplication.translate
fileName2, ok2 = QFileDialog.getSaveFileName(None, "文件保存", "C:/",
"Text Files (*.txt)")
print(fileName2) #打印保存文件的全部路径(包括文件名和后缀名)
save_path = fileName2
self.save_path_text.setText(_translate("Form", save_path))
def save_text(self): #保存文本
global save_path
if save_path is not None:
with open(file=save_path, mode='a+', encoding='utf-8') as file:
file.write(self.text_value.toPlainText())
print('已保存!')
给打开、保存按钮绑定单击动作,并通过槽调用上面定义的相应函数。同时,使用相同的方式将open_event()和 save_event( )函数中获取的路径与已定义的两个路径显示框进行关联,相关代码如下:
def retranslateUi(self, Form):
_translate = QtCore.QCoreApplication.translate
Form.setWindowTitle(_translate("Form", "Form"))
self.label_preview.setText(_translate("MainWindow_preview","预览"))
self.open_path_text.setPlaceholderText(_translate("Form","打开"))
self.open_path_but.setText(_translate("Form", "浏览"))
self.save_path_but.setText(_translate("Form", "浏览"))
self.save_path_text.setPlaceholderText(_translate("Form","保存"))
self.save_but.setText(_translate("Form", "保存"))
self.open_path_but.clicked.connect(self.open_event)
self.save_path_but.clicked.connect(self.save_event)
self.save_but.clicked.connect(self.save_text)
self.pushButton_create.clicked.connect(self.showwaiting)
self.pushButton_create.setText(_translate("Main_preview","生成"))
程序运行效果图分别如下两图所示。
4)程序对接
为完成模型与图形化界面的联合,需要在代码中留出相应的对接接口。在本项目中,界面与模型主体的重点对接部分共有四处:调用模型、模型处理结束、结果展示和输出保存。
a) 调用模型
为完成单击生成按钮后开始调用模型进行处理的功能,在PreEdit.py最后的showWaiting()函数中写入调用接口,在摘要生成页弹出的同时调用模型。相关代码如下:
def showwaiting(self):
import sys
self.MainWindow = QtWidgets.QMainWindow()
self.newshow = Ui_MainWindow_sumcreating() #创建
self.newshow.setupUi(self.MainWindow) #设置
self.hide()
#待对接程序,读取前面保存的文件(文件的路径在save_event函数里)
#调用模型进行输出并保存
self.MainWindow.show()
print('生成中…')
b)模型处理结束
在模型处理结束后需继续运行结果展示页,故在PaperMain.py的主函数里加一个判断,在判断模型处理完毕后,调用resultShow()函数,继续运行后续的结果展示。相关代码如下:
def main():
homeshow()
#待对接程序在PreEdit.py最后的showwaiting()函数里调用模型
#待对接程序判断处理完成后继续运行结果展示页
resultshow()
c)结果展示
模型处理完毕后,需要在结果展示页显示得到摘要、标题、关键词。结果展示页对应result.py
文件,对接前,界面显示的结果为固定的字符串;对接时仅需将模型运行的结果存为字符串形式,替换之前固定的内容即可。相关代码如下:
#待对接程序模型运行的结果存几个字符串后替换下面的文字即可
#替换摘要
self.plainTextEdit_summary.setPlainText(_translate("MainWindow_result", "生成的摘要"))
#替换标题1
self.lineEdit_title1.setText(_translate("MainWindow_result","标题1"))
#替换标题2
self.lineEdit_title2.setText(_translate("MainWindow_result","标题2"))
#替换标题3
self.lineEdit_title3.setText(_translate("MainWindow_result","标题3"))
#替换关键词
self.lineEdit_keywords.setText(_translate("MainWindow_result","关键词"))
d)输出保存
单击“保存"按钮时,将模型的输出直接保存到本地。该功能由result. py
中save_text()
函数的f.write()
完成,在对接之前f.write()的输出为固定字符串,对接时替换为模型输出的内容即可。相关代码如下:
def save_text(self):
global save_path
if save_path is not None:
with open(file=save_path, mode='a+', encoding='utf-8') as file:
#对接file.write这里直接把程序里的字符串加起来写入保存的结果即可
file.write("hello,Tibbarr")
print('已保存!')
6. 应用封装
为提高使用的便捷性,降低用户的使用门槛,本项目需要进行一体化封装。考虑到论文的撰写大多是在PC端完成的,故使用PyInstaller将项目封装为.exe
应用程序。
1)安装PyInstaller
从清华仓库镜像中下载PyInstaller-3.6.tar.gz
,在本地解压后,使用cmd进入控制台,切换到解压后的对应目录中,执行命令,即可完成安装。:
python setup.py install
2)将程序打包为.exe文件
打开命令窗口,将目录切换到papermain.py
路径下,输入命令
pyinstaller -F -w papermain.py
具体如下图所示。
使用PyInstaller 命令打包成功,如下图所示。
3)查看.exe 文件
成功打包程序后,在papermain.py文件目录下生成dist文件夹,里面有生成的.exe文件,双击即可运行,程序封装完成。
系统测试
本部分包括训练困惑度、测试效果和模型应用。
1. 训练困惑度
在Seq2Seq模型中,使用困惑度评估最终效果,值越小则代表语言模型效果越好。本项目使用大网络进行训练,共计48000步,在训练过程进行一段时间后,损失不再减少。开始执行训练与构建网络,如下图所示。
在训练过程中,模型困惑度的值呈下降趋势,即语言的歧义性随着训练的进行而逐渐减小,模型效果逐渐变好。当模型运行到30 000步时,其下降趋势已趋于平缓,困惑度基本不再减小;到47000步时,模型的perplexity值在小范围波动,最终,其困惑度最低点降至232.62,如下图所示。
2. 测试效果
载入训练好的模型,输入相关文本进行测试。使用Seq2Seq
进行标题部分的输出,如下图所示。
从输出结果可以看到,模型的标题生成能力仍有所欠缺,仅能对简单的内容进行效果较好的标题实现,在处理难度较大的文本时,准确性还有待提高。
摘要提取与关键字提取部分,使用TextRank
算法训练模型,用训练好的模型对给定的文本进行输出,经过多次测试,均得到了较好的结果,如下图所示。
3. 模型应用
由于程序已打包为可执行文件,故将.exe
文件下载到计算机中,双击即可运行,应用初始界面如下图所示。
首页为项目名称、“进入程序”按钮及对支持处理文档格式的说明,单击“进入程序”按钮即可进入文件读取页。
在文件读取页中,分别通过单击打开地址、保存地址对应的浏览和需要处理的文档,并设置修改结果的保存路径及文件名,如下图所示。
读入文件后,预览及编辑页如下图-1所示。在此处对读入的内容进行预览及修改,单击“保存"按钮暂存修改无误的文档后,单击“生成”按钮,模型开始处理文本内容,进入等待页,如下图-2所示。
处理模型后,关闭当前窗口,程序会自动跳转至结果展示页。页面由上到下依次展示了三个不同的标题方案、论文摘要及关键词,用户可以在界面中直接复制,或在页面下方选择路径,将全部结果保存到本地机,如下图所示。
选择保存路径并单击“保存”按钮后,程序会将所有结果保存至指定路径下,并跳转至下载成功页,如下图所示。
处理完毕,用户直接关闭程序或单击“返回主页”按钮,跳转回首页处理其他文件。在PC端对程序进行测试,输入文件内容、输出内容图及输出结果文件分别如下图3~图5所示。
工程源代码下载
其它资料下载
如果大家想继续了解人工智能相关学习路线和知识体系,欢迎大家翻阅我的另外一篇博客《重磅 | 完备的人工智能AI 学习——基础知识学习路线,所有资料免关注免套路直接网盘下载》
这篇博客参考了Github知名开源平台,AI技术平台以及相关领域专家:Datawhale,ApacheCN,AI有道和黄海广博士等约有近100G相关资料,希望能帮助到所有小伙伴们。