基于TF-IDF+Tensorflow+PyQt+孪生神经网络的智能聊天机器人(深度学习)含全部Python工程源码及模型+训练数据集

目录

  • 前言
  • 总体设计
    • 系统整体结构图
    • 系统流程图
    • 孪生神经网络结构图
  • 运行环境
    • Python 环境
    • TensorFlow 环境
  • 模块实现
    • 1. 数据预处理
    • 2. 创建模型并编译
    • 3. 模型训练及保存
    • 4. 模型应用
  • 系统测试
    • 1. 训练准确率
    • 2. 测试效果
    • 3. 模型生成
  • 工程源代码下载
  • 其它资料下载

在这里插入图片描述

前言

本项目利用TF-IDF(Term Frequency-Inverse Document Frequency 词频-逆文档频率)检索模型和CNN(卷积神经网络)精排模型构建了一个聊天机器人,旨在实现一个能够进行日常对话和情感陪伴的聊天机器人。

首先,我们使用TF-IDF技术构建了一个检索模型。TF-IDF可以衡量一个词语在文档中的重要性,通过计算词频和逆文档频率来为每个词分配一个权重。这个模型可以根据用户输入的问题,从预定义的问题-回答对中找到最相关的答案。

其次,我们利用CNN构建了一个精排模型。CNN是一种深度学习模型,可以从大量的训练数据中学习问题和回答之间的语义关系。通过对问题和回答进行特征提取和匹配,这个模型可以进一步提高回答的准确性和质量。

通过结合TF-IDF检索模型和CNN精排模型,我们的聊天机器人能够根据用户的提问,首先通过检索模型找到一组相关的答案候选,然后通过精排模型从中选择最合适的回答。

这个项目的目标是实现一个能够进行日常对话和情感陪伴的聊天机器人。用户可以向机器人提问各种问题,包括娱乐、学习、生活等方面的内容。机器人将根据其训练的知识和模型的学习能力,给出相关的回答,并尽可能地理解用户的情感和需求。

聊天机器人在日常生活中具有广泛的应用潜力,可以为用户提供便捷的信息查询、娱乐互动和情感支持。通过不断改进和训练,我们的目标是打造一个智能、友好和能够真正陪伴用户的聊天机器人。

总体设计

本部分包括系统整体结构图、系统流程图和孪生神经网络结构图。

系统整体结构图

系统整体结构如图所示。
在这里插入图片描述

系统流程图

系统流程如图所示。
在这里插入图片描述

孪生神经网络结构图

孪生神经网络结构如图所示。
在这里插入图片描述

运行环境

本部分包括 Python环境、TensorFlow 环境和Python包依赖关系。

Python 环境

需要Python 3.6及以上配置,在Windows环境 下推荐下载Anaconda完成Python所需配置。

Anaconda是开源的Python发行版本,包含conda、Python等180多个科学包及其依赖项。下载文件比较大,如果只需要某些包,或者需要节省带宽、存储空间,也可以使用Miniconda发行版(仅包含conda和Python)。下载地址为: https://www.anaconda.com/。也可以下载虚拟机在Linux环境下运行代码。添加环境变量:单击鼠标右键,依次选择属性、高级系统设置、环境变量、新建系统变量,添加安装Anaconda的路径即可。

用CMD命令行测试,输入:

conda list

显示Anaconda所存的路径以及文件。Anaconda 自带Anaconda Prompt,也可以用于其他类似安装包。

TensorFlow 环境

在Anaconda中配置TensorFlow环境的步骤(针对Windows系统)如下:
打开Anaconda Prompt,使用语句查询Python版本,输入清华仓库镜像,命令为

conda config--add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
conda config-set show_channel_urls yes

创建Python 3.x环境,名称为TensorFlow,此时Python版本和后面TensorFlow的版本有匹配问题,此步选择Python3.10,输入命令:

conda create-n tensorflow python=3.10

有需要确认的地方,都输入y。
在Anaconda Prompt中激活TensorFlow环境,输入命令:

activate tensorflow

安装CPU版本的TensorFlow,输入命令:

pip install --upgrade --ignore-installed tensorflow

安装完毕。
在Anaconda Prompt 中激活TensorFlow环境,输入命令:

activate tensorflow

NumPy是存储和处理大型矩阵的科学计算包,比Python自身的嵌套列表结构高效。安装命令:

pip install numpy

Matplotlib是Python最著名的绘图表,提供了一整套和MATLAB相似的命令API,适合交互式制图。安装命令:

pip install matplotlib

jieba是优秀的第三方中文分词库,中文文本需要通过分词获得单个词语。安装命令:

pip install jieba

Pandas是基于NumPy的一种工具,该工具是为了解决数据分析任务而创建的。Pandas纳入了大量库和一些标准的数据模型,提供了高效操作大型数据集所需的工具。安装命令:

pip install pandas

tqdm是快速、可扩展的Python进度条,在Python长循环中添加一个进度提示信息,用户只需封装任意的迭代器tqdm (iterator) 。安装命令:

pip install tqdm

nltk模块中包含大量的语料库,方便完成自然语言处理的任务,包括分词、词性标注、命名实体识别及句法分析。安装命令:

pip install nltk

gensim是开源的第三方Python工具包,用于从原始、非结构化的文本中无监督学习到文本隐层的主题向量表达。安装命令:

pip install gensim 

PyQt是创建GUI应用程序的工具包,它是Python编程语言和Qt库的成功融合。安装命令:

pip install pyqt5

模块实现

本项目包括4个模块:数据预处理、模型创建与编译、模型训练及保存、模型生成。下面分别给出各模块的功能介绍及相关代码。

1. 数据预处理

本部分包括基础数据、数据增强和数据预处理。

1)基础数据

数据来源于GitHub开源语料集,下 载地址为: https://github.com/codemayq。

该库对目前市面上已有的开源中文聊天语料进行搜集和系统化整理,包括chatterbot、 豆瓣多轮、PTT八卦、青云、电视剧对白、贴吧论坛回帖、小黄鸡、微博语料共8个公开闲聊常用语料和短信,并对其进行统一化规整和处理,以便于使用。

将解压后的raw_chat_corpus 文件夹放到当前目录下,执行python main.py,每个来源的语料分别生成一个独立的.tsv文件,放在新生成的clean_chat_corpus文件夹下。

2)数据增强

数据增强一方面可以增加训练数据,提升模型的泛化能力;另一方面可增加噪声数据,增强模型的健壮性。本项目使用同义词替换、随机插入、随机交换、随机删除等数据增强操作。

def synonym_replacement(words, n):  #同义词替换new_words = words.copy()random_word_list = list(set([word for word in words if word not in stop_words]))random.shuffle(random_word_list)num_replaced = 0for random_word in random_word_list:synonyms = get_synonyms(random_word)  #从词林中选择同义词进行替换if len(synonyms) >= 1:  synonym = random.choice(list(synonyms))new_words = [synonym if word == random_word else word for word in new_words]num_replaced += 1if num_replaced >= n:  #最多替换n个词breaksentence = ' '.join(new_words)new_words = sentence.split(' ')
return new_words
def random_insertion(words, n):  #随机插入new_words = words.copy()for _ in range(n):add_word(new_words)return new_words
def random_swap(words, n): #随机交换new_words = words.copy()for _ in range(n):new_words = swap_word(new_words)return new_words
def swap_word(new_words):  #随机把句子里的两个单词交换n次random_idx_1 = random.randint(0, len(new_words)-1)random_idx_2 = random_idx_1counter = 0while random_idx_2 == random_idx_1:random_idx_2 = random.randint(0, len(new_words)-1)counter += 1if counter > 3:return new_wordsnew_words[random_idx_1], new_words[random_idx_2] = new_words[random_idx_2], new_words[random_idx_1] 
return new_words
def random_deletion(words, p):  #随机删除#如果只有一个词,不用删除if len(words) == 1:return words#以概率p删除词new_words = []for word in words:r = random.uniform(0, 1)if r > p:new_words.append(word)#i如果全部删除,返回随机词if len(new_words) == 0:rand_int = random.randint(0, len(words)-1)return [words[rand_int]]
return new_words

3)数据预处理

将文档中原始字符文本转换成Gensim模型所能理解的稀疏向量,进行分词处理,采用Python中文分词最常用的jieba工具,支持多种切分模式。

#采用非全模式
#采用非全模式def cut(self, sentence, stopword= True, cut_all = False):seg_list = jieba.cut(sentence, cut_all)#对原始语料进行分词处理results = []for seg in seg_list:if stopword and seg in self.stopwords: #去除停用词continueresults.append(seg)return results
#另一种粒度较细的jieba分词模式def cut_for_search(self,sentence, stopword=True):seg_list = jieba.cut_for_search(sentence)#对原始语料进行分词处理results = []for seg in seg_list:if stopword and seg in self.stopwords:#去除停用词continueresults.append(seg)return results

对于一些特定的语境和特殊的词语,需要载入自定义词典,从而提高分词的准确率。

#载入自定义词典def load_userdict(self, file_name):jieba.load_userdict(file_name)

停用词是指在信息检索中,为节省存储空间和提高搜索效率,在处理自然语言数据之前或之后自动过滤掉某些字或词,这些字或词被称为停用词。它们没有明确意义,但出现的频率却很高,进行过滤和省略能够节省存储空间、提高搜索效率。

#读入停用词表def read_in_stopword(self):file_obj = codecs.open(self.stopword_filepath, 'r', 'utf-8')while True:line = file_obj.readline()line=line.strip('\r\n') #去掉换行符if not line:breakself.stopwords.add(line)file_obj.close()

使用doc2bow()方法对每个不同单词的词频进行统计,将单词转换为编号,以稀疏向量的形式返回结果。

    #对句子分词def cut(self, seg):return seg.cut_for_search(self.origin_sentence)#获取切词后的词列表def get_cuted_sentence(self):return self.cuted_sentence#获取原句子def get_origin_sentence(self):return self.origin_sentence#设置该句子得分def set_score(self, score):self.score = score
#词袋表示方法
def sentence2vec(self, sentence):sentence = Sentence(sentence, self.seg)vec_bow = self.dictionary.doc2bow(sentence.get_cuted_sentence())return self.model[vec_bow] #返回稀疏向量形式
self.corpus_simple = [self.dictionary.doc2bow(text) for text in self.texts] #生成语料

2. 创建模型并编译

数据加载进模型之后,需要定义模型结构并优化损失函数。

1)定义模型结构

在TF-IDF模型中定义的架构为:计算TF-IDF向量,通过倒排表的方式找到与当前输入类似的问题描述,针对候选问题进行余弦相似度计算。

#初始化模型,将整个语料库转为TF-IDF表示方法,创建余弦相似度索引
#构建其他复杂模型前需要的简单模型def simple_model(self, min_frequency = 0):self.texts = self.get_cuted_sentences()# 删除低频词frequency = defaultdict(int)  #创建频率对象for text in self.texts:for token in text:frequency[token] += 1self.texts = [[token for token in text if frequency[token] > min_frequency] for text in self.texts]self.dictionary = corpora.Dictionary(self.texts) #创建字典self.corpus_simple = [self.dictionary.doc2bow(text) for text in self.texts] #生成语料#TF-IDF模型def TfidfModel(self):self.simple_model()#转换模型self.model = models.TfidfModel(self.corpus_simple)self.corpus = self.model[self.corpus_simple]#创建相似度矩阵self.index = similarities.MatrixSimilarity(self.corpus)
#对新输入的句子(比较的句子)进行预处理def sentence2vec(self, sentence):sentence = Sentence(sentence, self.seg)vec_bow = self.dictionary.doc2bow(sentence.get_cuted_sentence())return self.model[vec_bow]def bow2vec(self):vec = []length = max(self.dictionary) + 1for content in self.corpus:sentence_vectors = np.zeros(length)for co in content:sentence_vectors[co[0]] = co[1] 
#将句子出现的单词TF-IDF表示放入矩阵中vec.append(sentence_vectors)return vec#求最相似的句子def similarity(self, sentence):sentence_vec = self.sentence2vec(sentence)sims = self.index[sentence_vec]sim = max(enumerate(sims), key=lambda item: item[1])index = sim[0]score = sim[1]sentence = self.sentences[index]sentence.set_score(score)return sentence  #返回一个类

在孪生神经网络中,每个CNN都有一个卷积层,卷积后连接一个池化层,进行数据的降维。在每个卷积层上都会使用多个滤波器来提取不同类型的特征。最大池化和全连接层之后,引入dropout进行正则化,用以消除模型的过拟合问题。

	def fc_layer(self, bottom, n_weight, name):  #全连接层assert len(bottom.get_shape()) == 2n_prev_weight = bottom.get_shape()[1]initer = tf.contrib.layers.xavier_initializer()W = tf.get_variable(name + 'W', dtype=tf.float32, shape=[n_prev_weight, n_weight], initializer=initer, regularizer=tf.contrib.layers.l2_regularizer(scale=0.0000001))#y=Wx+b线性模型b = tf.get_variable(name + 'b', dtype=tf.float32, initializer=tf.constant(0.01, shape=[n_weight],dtype=tf.float32),regularizer=tf.contrib.layers.l2_regularizer(scale=0.0000001))fc = tf.nn.bias_add(tf.matmul(bottom, W), b)return fcdef _cnn_layer(self, input):  #卷积和池化层all = []max_len = input.get_shape()[1]for i, filter_size in enumerate(self.window_sizes):with tf.variable_scope('filter{}'.format(filter_size)):cnn_out = tf.layers.conv1d(input, self.n_filters, filter_size, padding='valid',
activation=tf.nn.relu, name='q_conv_' + str(i))  #卷积pool_out = tf.reduce_max(cnn_out, axis=1, keepdims=True)  #池化tanh_out = tf.nn.tanh(pool_out)  #tanh激活函数all.append(tanh_out)cnn_outs = tf.concat(all, axis=-1)dim = cnn_outs.get_shape()[-1]cnn_outs = tf.reshape(cnn_outs, [-1, dim])return cnn_outs

隐藏层的意义是把输入数据的特征,抽象到另一个维度空间,展现其更抽象化的特征,更好的进行线性划分。

    def _HL_layer(self, bottom, n_weight, name):      #隐藏层assert len(bottom.get_shape()) == 3n_prev_weight = bottom.get_shape()[-1]max_len = bottom.get_shape()[1]initer = tf.contrib.layers.xavier_initializer()  #初始化W = tf.get_variable(name + 'W', dtype=tf.float32, shape=[n_prev_weight, n_weight], initializer=initer, regularizer=tf.contrib.layers.l2_regularizer(scale=0.0000001))b = tf.get_variable(name + 'b', dtype=tf.float32, initializer=tf.constant(0.1, shape=[n_weight],dtype=tf.float32),regularizer=tf.contrib.layers.l2_regularizer(scale=0.0000001))  #y=Wx+b线性模型bottom_2 = tf.reshape(bottom, [-1, n_prev_weight])hl = tf.nn.bias_add(tf.matmul(bottom_2, W), b)  #y=Wx+b单个神经元hl_tanh = tf.nn.tanh(hl)  #激活函数HL = tf.reshape(hl_tanh, [-1, max_len, n_weight])return HLdef _build(self, embeddings):  #构建层if embeddings is not None:self.Embedding=tf.Variable(tf.to_float(embeddings), trainable=False, name='Embedding')else:  #嵌入构建self.Embedding = tf.get_variable('Embedding',shape=[self.vocab_size, self.embedding_size], initializer=tf.uniform_unit_scaling_initializer())self.q_embed = tf.nn.dropout(tf.nn.embedding_lookup(self.Embedding, self._ques), keep_prob=self.dropout_keep_prob)self.a_embed = tf.nn.dropout(tf.nn.embedding_lookup(self.Embedding, self._ans), keep_prob=self.dropout_keep_prob)with tf.variable_scope('siamese') as scope:#计算隐藏和卷积层#hl_q = self._HL_layer(self.q_embed, self.hidden_size, 'HL_layer')conv1_q = self._cnn_layer(self.q_embed)scope.reuse_variables()  #权值共享#hl_a = self._HL_layer(self.a_embed, self.hidden_size, 'HL_layer')conv1_a = self._cnn_layer(self.a_embed)with tf.variable_scope('fc') as scope:con = tf.concat([conv1_q, conv1_a], axis=-1)logits = self.fc_layer(con, 1, 'fc_layer')res = tf.nn.sigmoid(logits)return logits, resdef _add_loss_op(self, logits):#损失节点loss = tf.nn.sigmoid_cross_entropy_with_logits(logits=logits,
labels=tf.cast(tf.reshape(self._y, [-1, 1]), dtype=tf.float32))reg_losses = tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES)l2_loss = sum(reg_losses)pointwise_loss = tf.reduce_mean(loss) + l2_losstf.summary.scalar('loss', pointwise_loss)return pointwise_loss
def _add_acc_op(self):
#精确度节点predictions = tf.to_int32(tf.round(self.res))correct_prediction = tf.equal(predictions, self._y)accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))tf.summary.scalar('accuracy', accuracy)return accuracydef _add_train_op(self, loss):#训练节点with tf.name_scope('train_op'):#记录训练步骤self.global_step=tf.Variable(0, name='global_step', trainable=False)optimizer = tf.train.AdamOptimizer(self.learning_rate)#计算梯度,得到梯度和变量gradsAndVars = optimizer.compute_gradients(loss)#将梯度应用到变量下,生成训练器train_op = optimizer.apply_gradients(gradsAndVars, global_step=self.global_step)#用summary绘制tensorBoardfor g, v in gradsAndVars:if g is not None:tf.summary.histogram("{}/grad/hist".format(v.name), g)tf.summary.scalar("{}/grad/sparsity".format(v.name), tf.nn.zero_fraction(g))self.summary_op = tf.summary.merge_all()return train_op

2)优化损失函数

确定模型架构后进行编译,这是二分类问题,使用交叉熵作为损失函数。由于所有标签都带有相似的权重,通常使用精确度作为性能指标。Adam是常用的梯度下降方法,使用它来优化模型参数。

#定义损失函数和优化器
loss = tf.nn.sigmoid_cross_entropy_with_logits(logits=logits,labels=tf.cast(tf.reshape(self._y, [-1, 1]), dtype=tf.float32))optimizer = tf.train.AdamOptimizer(self.learning_rate)
predictions = tf.to_int32(tf.round(self.res))
correct_prediction = tf.equal(predictions, self._y)
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

3. 模型训练及保存

在定义模型架构和编译之后,使用训练集训练模型,使模型可以对语义相似的问句正确分类,用训练集来拟合并保存模型。

1)模型训练

def devStep(corpus):iterator = Iterator(corpus)dev_Loss = []   #损失dev_Acc = []     #准确率dev_Prec = []dev_Recall = []dev_F_beta = []for batch_x in iterator.next(config.batch_size, shuffle=False):batch_q,batch_a,batch_qmask,batch_amask,label= zip(*batch_x)batch_q = np.asarray(batch_q)   #批次batch_a = np.asarray(batch_a)loss, summary, step, predictions = sess.run([model.total_loss,model.summary_op,model.global_step, model.res],feed_dict={model._ques: batch_q,  #传入训练集model._ans: batch_a,model._y: label,model.dropout_keep_prob: 1.0})predictions = [1 if i >= 0.5 else 0 for i in predictions]acc, recall, prec, f_beta = get_binary_metrics(pred_y=predictions, true_y=label)  #预测值dev_Loss.append(loss)dev_Acc.append(acc)dev_Prec.append(prec)dev_Recall.append(recall)dev_F_beta.append(f_beta)evalSummaryWriter.add_summary(summary, step)return mean(dev_Loss), mean(dev_Acc), mean(dev_Recall), mean(dev_Prec), mean(dev_F_beta)  #返回参数best_acc = 0.0for epoch in range(config.num_epochs):  #轮次train_time1 = time.time()print("----- Epoch {}/{} -----".format(epoch + 1, config.num_epochs))train_Loss = []  #损失train_Acc = []  #准确率train_Prec = [] train_Recall = []train_F_beta = []for batch_x in iterator.next(config.batch_size, shuffle=True):batch_q,batch_a,batch_qmask,batch_amask,label= zip(*batch_x)batch_q = np.asarray(batch_q)  #批次batch_a = np.asarray(batch_a)train_loss, train_acc, train_prec, train_recall, train_f_beta = trainStep(batch_q, batch_a, label)   #输出训练结果train_Loss.append(train_loss)train_Acc.append(train_acc)train_Prec.append(train_prec)train_Recall.append(train_recall)train_F_beta.append(train_f_beta)print("---epoch %d  -- train loss %.3f -- train acc %.3f -- train recall %.3f -- train precision %.3f""-- train f_beta %.3f" % (epoch+1, np.mean(train_Loss), np.mean(train_Acc), np.mean(train_Recall),np.mean(train_Prec), np.mean(train_F_beta)))#打印准确率test_loss, test_acc, test_recall, test_prec, test_f_beta = devStep(test_corpus)print("---epoch %d  -- test loss %.3f -- test acc %.3f -- test recall %.3f -- test precision %.3f""-- test f_beta %.3f" % (epoch + 1, test_loss, test_acc, test_recall, test_prec, test_f_beta))

2)模型保存

方便训练时读取,将模型保存为ckpt格式的文件,利用TensorFlow中的tf.train.Saver进行保存。

saver = tf.train.Saver(tf.global_variables(), max_to_keep=10)
#定义保存的对象best_saver = tf.train.Saver(tf.global_variables(), max_to_keep=5)ckpt = tf.train.get_checkpoint_state(save_path)checkpoint_path=os.path.join(save_path, 'acc{:.3f}_{}.ckpt'.format(test_acc, epoch + 1))bestcheck_path = os.path.join(best_path, 'acc{:.3f}_{}.ckpt'.format(test_acc, epoch + 1))saver.save(sess, checkpoint_path, global_step=epoch)#保存模型

4. 模型应用

一是通过中控模块调用召回和精排模型;二是通过训练好的召回和精排模型进行语义分类,并且获取输出。

1)GUI模块

GUI模块是本项目的前端。提供了2个文本框,1个显示用户输入,1个显示对话内容。提供了1个“发送”button,调取control.py中的接口,返回选取的回答内容。

def setupUi(self, Dialog):  #设置界面Dialog.setObjectName("智能聊天机器人")Dialog.resize(582, 434)#palette = QPalette()#palette.setBrush(QPalette.Background, QBrush(QPixmap("./background.jpg")))#Dialog.setPalette(palette)palette = QPalette()pix = QPixmap("./background.jpg")pix = pix.scaled(Dialog.width(), Dialog.height())palette.setBrush(QPalette.Background, QBrush(pix))Dialog.setPalette(palette)self.label = QtWidgets.QLabel(Dialog)self.label.setGeometry(QtCore.QRect(40, 30, 361, 51))self.label.setStyleSheet("color: rgb(205, 85, 85);\n"
"font: 16pt \"黑体\";\n"
"text-decoration: underline;")self.label.setObjectName("dialog")self.plainTextEdit = QtWidgets.QPlainTextEdit(Dialog)self.plainTextEdit.setGeometry(QtCore.QRect(40, 80, 501, 181))self.plainTextEdit.setObjectName("plainTextEdit")self.plainTextEdit.setFocusPolicy(QtCore.Qt.NoFocus)self.plainTextEdit_2 = QtWidgets.QPlainTextEdit(Dialog)self.plainTextEdit_2.setGeometry(QtCore.QRect(40, 310, 401, 41))self.plainTextEdit_2.setObjectName("plainTextEdit_2")self.plainTextEdit.setStyleSheet("font: 14pt \"黑体\";\n")self.pushButton = QtWidgets.QPushButton(Dialog)self.pushButton.setGeometry(QtCore.QRect(480, 320, 75, 23))self.pushButton.setStyleSheet("font: 14pt \"黑体\";\n"
"background-color: rgb(255, 192, 203);")self.pushButton.setObjectName("pushButton")self.label_2 = QtWidgets.QLabel(Dialog)self.label_2.setGeometry(QtCore.QRect(50, 280, 54, 12))self.label_2.setText("")self.label_2.setObjectName("label_2")self.label_3 = QtWidgets.QLabel(Dialog)self.label_3.setGeometry(QtCore.QRect(50, 280, 71, 16))self.label_3.setStyleSheet("font: 75 12pt \"Aharoni\";")self.label_3.setObjectName("label_3")self.retranslateUi(Dialog)QtCore.QMetaObject.connectSlotsByName(Dialog)

2)中控模块

中控模块设定2个阈值max_sim和min_sim,用于缩减响应时间。如果recall_score<min_ sim,说明问答库数量少或者问句噪声大,需要复查分析;如果min_sim<recall_score<max_ sim,进行召回(recall)和精排(rerank);如果recall_score>max_sim, 只进行召回,直接得出答案。

import time    #导入模块
from Rerank.data_helper import *
from Recall import recall_model
from Rerank import rerank_model
class SmartQA:   #定义类def __init__(self):  #初始化self.top_k = 5self.min_sim = 0.10self.max_sim = 0.90self.embeding_size = 200self.vocab_file = './data/word_vocab.txt'self.embed_file = './word2vec/5000-small.txt'self.embedding = load_embedding(self.embed_file, self.embeding_size, self.vocab_file)#分为recall和rerank两部分def search_main(self, question):#粗排candi_questions, questionList, answerList = recall_model.main(question, self.top_k)answer_dict = {}corpus = []indxs = []matchmodel_simscore = []sim_questions = []for indx, candi in zip(*candi_questions):#如果在粗排阶段就已经找到了非常相似的问题,则马上返回这个答案,终止循环if candi > self.max_sim:indxs.append(indx)breakelse:#如果召回的数据噪声大,生成一个文件,复查分析matchmodel_simscore.append(candi)corpus.append((question, questionList[indx]))indxs.append(indx)sim_questions.append(questionList[indx])if candi_questions[1][0] < self.min_sim:final_answer = '我还没找到相似的答案,请说得再清楚一点'return final_answer, sim_questionsif len(indxs) == 1:#找到非常相似的答案sim=[questionList[indx] for indx, candi in zip(*candi_questions)]return answerList[indxs[0]], simelse:if len(indxs) != 0 :deepmodel_simscore = rerank_model.main(corpus, self.embedding)  #使用精排模型final = list(zip(indxs, matchmodel_simscore, deepmodel_simscore))   #输出结果for id, score1, score2 in final:final_score = (score1 + score2) / 2answer_dict[id] = final_scoreif answer_dict:  #如果识别成功answer_dict = sorted(answer_dict.items(), key=lambda asd: asd[1], reverse=True)final_answer = answerList[answer_dict[0][0]]else:final_answer = '请说得再清楚一点.'return final_answer, sim_questions
def answer(question):  #定义回答的问题handler = SmartQA()final_answer, sim_questions = handler.search_main(question)return final_answer
if __name__ == "__main__":  #主函数handler = SmartQA()while (1):question = input('用户说: \n')if question == 'end':print('byebye~')breaks1 = time.time()final_answer, sim_questions = handler.search_main(question)s2 = time.time()print('机器人:', final_answer)

3)相关代码

本部分包括召回(Recall) 模型和精排(Rerank) 模型。
1)召回模型
召回模型相关代码如下:

import pandas as pd     #导入模块
import matplotlib as mpl
import numpy as np
from nltk.probability import FreqDist
from .jiebaSegment import *
from .sentenceSimilarity import SentenceSimilarity
mpl.rcParams['font.sans-serif'] = ['Microsoft YaHei']  # enable chinese
#设置外部词
seg = Seg()
seg.load_userdict('./data/userdict.txt')
def read_corpus():qList = []#问题的关键词列表qList_kw = []aList = []data = pd.read_csv('./data/qa_.csv', header=None)data_ls = np.array(data).tolist()for t in data_ls:qList.append(t[0])qList_kw.append(seg.cut(t[0]))aList.append(t[1])return qList_kw, qList, aList
def invert_idxTable(qList_kw):  #定一个简单的倒排表invertTable = {}for idx, tmpLst in enumerate(qList_kw):for kw in tmpLst:if kw in invertTable.keys():invertTable[kw].append(idx)else:invertTable[kw] = [idx]return invertTable
def filter_questionByInvertTab(inputQuestionKW, questionList, answerList, invertTable):  #过滤问题idxLst = []questions = []answers = []for kw in inputQuestionKW:if kw in invertTable.keys():idxLst.extend(invertTable[kw])idxSet = set(idxLst)for idx in idxSet:questions.append(questionList[idx])answers.append(answerList[idx])return questions, answers
def main(question, top_k):#topk控制选出的回答个数qList_kw, questionList, answerList = read_corpus()questionList_s = questionListanswerList_s = answerList#初始化模型ss = SentenceSimilarity(seg)ss.set_sentences(questionList_s)ss.TfidfModel()  #TF-IDF模型question_k = ss.similarity_k(question, top_k)return question_k, questionList_s, answerList_s
if __name__ == '__main__':#设置外部词seg = Seg()seg.load_userdict('./userdict/userdict.txt')#读取数据List_kw, questionList, answerList = read_corpus()#初始化模型ss = SentenceSimilarity(seg)ss.set_sentences(questionList)ss.TfidfModel()         while True:question = input("请输入问题(q退出): ")if question == 'q':breakquestion_k = ss.similarity_k(question, 5)print("女票: {}".format(answerList[question_k[0][0]]))for idx, score in zip(*question_k):print("same questions: {}, score: {}".format(questionList[idx], score))

2)精排模型

精排模型相关代码如下:

import time   #导入各种模块
import logging
import warnings
warnings.filterwarnings("ignore")
import numpy as np
import tensorflow as tf
import os
import tqdm
import sys
from copy import deepcopy
stdout = sys.stdout
from Rerank.data_helper import *
from Rerank.data_preprocess import *
from Rerank.model import SiameseQACNN
from Rerank.model_utils import *
from Rerank.metrics import *
from sklearn.metrics import accuracy_score
class NNConfig(object):  #定义类def __init__(self, embeddings):  #初始化self.ans_length = 15self.num_epochs = 10self.ques_length = 15self.batch_size = 32self.window_sizes = [1, 1, 2]self.hidden_size = 128self.output_size = 128self.keep_prob = 0.5self.n_filters = 128self.embeddings = np.array(embeddings).astype(np.float32)self.vocab_size = 3258self.embedding_size = 300self.learning_rate = 0.0001self.optimizer = 'adam'self.clip_value = 5self.l2_lambda = 0.00001self.eval_batch = 100
def train(train_corpus, test_corpus, config):  #定义训练iterator = Iterator(train_corpus)if not os.path.exists(save_path):os.makedirs(save_path)if not os.path.exists(best_path):os.makedirs(best_path)#定义计算图with tf.Graph().as_default():session_conf = tf.ConfigProto(allow_soft_placement=True, log_device_placement=False)with tf.Session(config=session_conf) as sess:#训练print('Start training and evaluating ...')outDir = os.path.abspath(os.path.join(os.path.curdir, "summarys"))print("Writing to {}\n".format(outDir))trainSummaryDir = os.path.join(outDir, "train")trainSummaryWriter = tf.summary.FileWriter(trainSummaryDir, sess.graph)evalSummaryDir = os.path.join(outDir, "eval")evalSummaryWriter = tf.summary.FileWriter(evalSummaryDir, sess.graph)model = SiameseQACNN(config)#初始化所有变量saver = tf.train.Saver(tf.global_variables(), max_to_keep=10)best_saver = tf.train.Saver(tf.global_variables(), max_to_keep=5)ckpt = tf.train.get_checkpoint_state(save_path)print('Configuring TensorBoard and Saver ...')if ckpt and tf.train.checkpoint_exists(ckpt.model_checkpoint_path):print('Reloading model parameters ...')saver.restore(sess, ckpt.model_checkpoint_path)else:print('Created new model parameters ...')sess.run(tf.global_variables_initializer())#计算训练参数total_parameters = count_parameters()print('Total trainable parameters : {}'.format(total_parameters))def trainStep(batch_q, batch_a, batchY):_, loss, summary, step, predictions = sess.run([model.train_op, model.total_loss, model.summary_op, model.global_step, model.res],feed_dict={model._ques: batch_q,model._ans: batch_a,model._y: label,model.dropout_keep_prob: config.keep_prob})predictions = [1 if i >= 0.5 else 0 for i in predictions]acc, recall, prec, f_beta = get_binary_metrics(pred_y=predictions, true_y=batchY)trainSummaryWriter.add_summary(summary, step)return loss, acc, prec, recall, f_betadef devStep(corpus):iterator = Iterator(corpus)  #定义各种参数dev_Loss = []dev_Acc = []dev_Prec = []dev_Recall = []dev_F_beta = []for batch_x in iterator.next(config.batch_size, shuffle=False):batch_q, batch_a, batch_qmask, batch_amask, label = zip(*batch_x)batch_q = np.asarray(batch_q)  #获取批次batch_a = np.asarray(batch_a)loss, summary, step, predictions = sess.run( #输出结果[model.total_loss, model.summary_op, model.global_step, model.res],feed_dict={model._ques: batch_q,model._ans: batch_a,model._y: label,model.dropout_keep_prob: 1.0})predictions = [1 if i >= 0.5 else 0 for i in predictions]acc, recall, prec, f_beta = get_binary_metrics(pred_y=predictions, true_y=label)  #得到参数值dev_Loss.append(loss)dev_Acc.append(acc)dev_Prec.append(prec)dev_Recall.append(recall)dev_F_beta.append(f_beta)evalSummaryWriter.add_summary(summary, step)return mean(dev_Loss), mean(dev_Acc), mean(dev_Recall), mean(dev_Prec), mean(dev_F_beta)best_acc = 0.0for epoch in range(config.num_epochs):  #轮次train_time1 = time.time()print("----- Epoch {}/{} -----".format(epoch + 1, config.num_epochs))  #输出训练参数train_Loss = []train_Acc = []train_Prec = []train_Recall = []train_F_beta = []for batch_x in iterator.next(config.batch_size, shuffle=True):batch_q, batch_a, batch_qmask, batch_amask, label = zip(*batch_x)batch_q = np.asarray(batch_q)  #批次数据batch_a = np.asarray(batch_a)train_loss, train_acc, train_prec, train_recall, train_f_beta = trainStep(batch_q, batch_a, label)  #训练参数获取train_Loss.append(train_loss)train_Acc.append(train_acc)train_Prec.append(train_prec)train_Recall.append(train_recall)train_F_beta.append(train_f_beta)print("---epoch %d  -- train loss %.3f -- train acc %.3f -- train recall %.3f -- train precision %.3f""-- train f_beta %.3f" % (epoch+1, np.mean(train_Loss), np.mean(train_Acc), np.mean(train_Recall),np.mean(train_Prec), np.mean(train_F_beta)))  #打印训练参数值test_loss, test_acc, test_recall, test_prec, test_f_beta = devStep(test_corpus)print("---epoch %d  -- test loss %.3f -- test acc %.3f -- test recall %.3f -- test precision %.3f""-- test f_beta %.3f" % (epoch + 1, test_loss, test_acc, test_recall, test_prec, test_f_beta))  #打印测试参数值checkpoint_path = os.path.join(save_path, 'acc{:.3f}_{}.ckpt'.format(test_acc, epoch + 1))  #检查点路径bestcheck_path = os.path.join(best_path, 'acc{:.3f}_{}.ckpt'.format(test_acc, epoch + 1))  #最佳检查路径saver.save(sess, checkpoint_path, global_step=epoch)if test_acc > best_acc:best_acc = test_accbest_saver.save(sess, bestcheck_path, global_step=epoch)
def main():  #主函数embedding = load_embedding(embeding, embeding_size, vocab_file)preprocess_data1 = preprocess(train_file)  #预处理preprocess_data2 = preprocess(test_file)train_data = read_train(preprocess_data1, stopword_file, vocab_file)   #训练数据test_data = read_train(preprocess_data2, stopword_file, vocab_file)   #测试数据train_corpus = load_train_data(train_data, max_q_length, max_a_length)test_corpus = load_train_data(test_data, max_q_length, max_a_length)config = NNConfig(embedding)   #配置参数config.ques_length = max_q_lengthconfig.ans_length = max_a_length#config.embeddings = embeddingtrain(deepcopy(train_corpus), test_corpus, config)
if __name__ == '__main__':  #主函数save_path = "./model/checkpoint"best_path = "./model/bestval"train_file = '../data/train.csv'test_file = '../data/test.csv'stopword_file = '../stopwordList/stopword.txt'embeding = '../word2vec/5000-small.txt'vocab_file = '../data/word_vocab.txt'max_q_length = 15max_a_length = 15embeding_size = 200main()

系统测试

本部分包括训练准确率、测试效果及模型应用。

1. 训练准确率

测试准确率在90%左右,损失随训练次数增多而下降,并趋于稳定,如下图所示。

在这里插入图片描述

2. 测试效果

将文本输入模型进行测试,如下图所示 。

在这里插入图片描述

模型训练效果

3. 模型生成

本部分包括程序下载运行、应用使用说明和测试结果。

1)程序下载运行

下载程序压缩包后,在Python环境 下执行gui.py命令即可。

2)应用使用说明

解压程序压缩包后,文件目录如下:

在这里插入图片描述

其中,qacnn.py是模型的训练文件,可以单独运行; Control.py控制Recall和Rerank模型的选择,可以单独运行;GUI.py是本项目的图形化界面,调用control.py的接口。

3)测试结果

图形化界面测试结果如图所示。

在这里插入图片描述

工程源代码下载

详见本人博客资源下载页

其它资料下载

如果大家想继续了解人工智能相关学习路线和知识体系,欢迎大家翻阅我的另外一篇博客《重磅 | 完备的人工智能AI 学习——基础知识学习路线,所有资料免关注免套路直接网盘下载》
这篇博客参考了Github知名开源平台,AI技术平台以及相关领域专家:Datawhale,ApacheCN,AI有道和黄海广博士等约有近100G相关资料,希望能帮助到所有小伙伴们。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/20700.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

喜羊羊贴吧顶帖软件实战教学

喜羊羊贴吧顶帖软件实战教学#贴吧顶帖#贴吧推广 大家好&#xff0c;欢迎来到百收网SEO这期视频&#xff0c;给大家更新一下百度贴最新的一个顶帖视频教程。首先我们今天用的顶帖软件是我们的喜羊羊173 的一个版本&#xff0c;软件的话在我们的群文件去下载&#xff0c;就是我们…

贴吧发布软件<神奇的>使用教程实战教学

贴吧发布软件<神奇的>使用教程实战教学.百度贴吧全自动发帖软件#贴吧发帖软件 各位同学大家好&#xff0c;欢迎回到百收SEO网络&#xff0c;这节课是我们百度贴软件自动发帖的一个实战教学的一个公开课&#xff0c;发帖软件还是我们用的一个喜羊羊的工具箱&#xff0c;然…

讯飞星火 VS 文心一言:谁是中文大语言模型的TOP1?

在百度发布文心一言一个多月后,科大讯飞也发布了自己的大模型“讯飞星火大模型”。本篇博客就测评一下这两个在中文圈最受好评的大语言模型,顺便辅以ChatGPT为参考。大家一起来看看到底谁是中文大语言模型的TOP1? 目录 体验网址 1、旅游攻略 2、数理逻辑题 3、故事创作…

大学生实践| 微软ATP“师徒制”AI实战项目收获满满!

ChatGPT在极短时间内掀起了一轮AI狂潮&#xff0c;AI数据、AI大模型、AIGC……对我们AI实践项目感兴趣的同学也越来越多&#xff01;微软(亚洲)互联网工程院下属的微软ATP为大学生们提供了丰富的企业级实践项目。 2个月内&#xff01;本期优秀的Chen同学在微软AI工程师团队带领…

1天训完45亿参数大模型!高校计算能力首次比肩科技巨头

衡宇 发自 凹非寺量子位 | 公众号 QbitAI “第一次&#xff0c;高校拥有了和科技巨头一样的计算能力。” 说这句话的人是中国工程院院士、阿里云创始人王坚。 他口中让高校计算能力倍增的&#xff0c;是复旦大学刚刚发布的云上科研智算平台。 名为CFFF&#xff08;Computing fo…

介绍一下IEEE PHM2012数据集

ChatGPT: IEEE PHM2012数据集是一个用于轴承剩余寿命预测的公共数据集&#xff0c;由IEEE联合无损检测协会组织。该数据集是通过对一种在机械系统中广泛使用的轴承进行可控实验得到的。 数据集包含四个通道的振动数据和一个温度信号&#xff0c;每个通道的采样率为20kHz&…

2022年天马杯新闻文本扩充生成赛道冠军方案分享

Z Lab数据实验室成员 HeapOverflow 01 前言 去年又摸了一个比赛&#xff0c;这次基本上是自己主导完成大部分建模和优化工作的了。这次能拿下第一&#xff0c;一方面是比赛参加的人比较少&#xff08;也是因为限制学生参加&#xff09;来卷的大概也就几十个人&#xff0c;另一方…

百万网友AI导师李沐离职投身大模型,B站“组会”还会有吗?(文末赠书)

目录 1 求学之路&#xff1a;全能学霸2 AI之路&#xff1a;与深度学习结缘3 一战封神&#xff1a;亚马逊首席科学家4 动手学习深度学习 前阵子“沐神”李沐离开亚马逊、加入创业公司BosonAI的消息&#xff0c;引起了业内比较广泛的讨论。 而BosonAI的创始人正好是他的博士生导师…

揭秘 “移动云杯”高校赛道——法律科技创新子赛道

【赛道介绍】 法律科技创新大赛以人工智能、大数据等技术为基础&#xff0c;拓展政法工作现代化中的创新应用&#xff0c;加强科学的方法论和先进的技术手段与传统政法业务的融合&#xff0c;搭建法律科技领域思想、技术、应用的交流实践平台&#xff0c;助推法律科技产业人才培…

资源分享(nlp、kaggle、pytorch、datawhale)

文章目录 一、.机器学习、深度学习库、优秀课程1.1 资源库1.2 优秀课程1.3 优秀作者、公众号 二、NLP相关2.1 NLP相关2.2 transformer资源2.3 Hugging Face&#xff1a;2.4 其它文章 三、CV相关四、数据挖掘4.1 pandas、sns工具类 五、kaggle赛事5.1 NLP赛事 六、 其它赛事&…

chatgpt赋能python:自动填表程序:让烦人的表格填写工作自动化

自动填表程序&#xff1a;让烦人的表格填写工作自动化 随着互联网的发展&#xff0c;填写表格已经成为我们日常生活和工作中不可避免的一部分。不管是在线申请、调查问卷还是财务报表&#xff0c;表格的填写让人感到烦恼而且耗费时间。为了解决填表的烦恼&#xff0c;python的…

chatgpt赋能python:Python如何自动发邮件

Python 如何自动发邮件 Python 是世界上最受欢迎的编程语言之一&#xff0c;Python 可以实现各种各样的功能&#xff0c;自动化发邮件也是其中之一。本文将介绍 Python 的邮件发送库和如何自动发送邮件&#xff0c;希望能为读者提供帮助。 为什么要自动发邮件&#xff1f; 自…

Python某地区二手房房价数据分析

房价数据分析 数据简单清洗 data.csv 数据显示 # 导入模块 import pandas as pd # 导入数据统计模块 import matplotlib # 导入图表模块 import matplotlib.pyplot as plt # 导入绘图模块# 避免中文乱码 matplotlib.rcParams[font.sans-serif] [SimHei] # 设置字体为…

house_price(房价预测)

最近在阅读一些AI项目&#xff0c;写入markdown&#xff0c;持续更新&#xff0c;算是之后也能回想起做法 项目 https://github.com/calssion/Fun_AI Kaggle--House Prices: Advanced Regression Techniques Kaggle address(网址)&#xff1a;https://www.kaggle.com/c/house…

kaggle房价预测

通过前面的学习&#xff0c;实战一个房价预测项目&#xff0c;kaggle房价预测&#xff0c;该数据集涵盖了2006-2010年期间亚利桑那州埃姆斯市的房价&#xff0c;包含了很多特征。 导入所需的包 %matplotlib inline import pandas as pd import torch import numpy as np from…

Pandas与ChatGPT的超强结合,爆赞!

来自公众号&#xff1a;数据STUDIO Python Pandas是一个为Python编程提供数据操作和分析功能的开源工具包。这个库已经成为数据科学家和分析师的必备工具。它提供了一种有效的方法来管理结构化数据(Series和DataFrame)。 在人工智能领域&#xff0c;Pandas经常用于机器学习和深…

一个浏览器插件畅享Chat GPT,还有更多实用小组件、高清壁纸

即使chatGPT已经火爆了一段时间了&#xff0c;但它仍然是目前备受关注的话题。而在国内使用需要一定的成本&#xff0c;许多人甚至根本找不到注册的办法&#xff0c;因此也衍生了不少代注册服务。但其实国内也有不少可以体验和chatGPT几乎同样服务的办法&#xff0c;这篇文章就…

docker一键部署网址导航+博客+管理系统(强势开源)

花森门户 码云仓库地址&#xff1a;https://gitee.com/HuaSenJioJio/huasenjio-compose Github仓库地址&#xff1a;https://github.com/huasenjio/huasenjio-compose &#x1f4cc; 关于 花森系列网站增添新作品&#xff0c;&#xff08;huasenjio-compose&#xff09;官方仓…

【LLMs】关于LLMs的语义搜索

&#x1f50e;大家好&#xff0c;我是Sonhhxg_柒&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流&#x1f50e; &#x1f4dd;个人主页&#xff0d;Sonhhxg_柒的博客_CSDN博客 &#x1f4c3; &#x1f381;欢迎各位→点赞…

AI_Papers:第一期

2023.02.06—2023.02.12 文摘词云 Top Papers Subjects: cs.CL 1.Multimodal Chain-of-Thought Reasoning in Language Models 标题&#xff1a;语言模型中的多模式思维链推理 作者&#xff1a;Zhuosheng Zhang, Aston Zhang, Mu Li, Hai Zhao, George Karypis, Alex Sm…