提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
目标
1. 能够说出实现聊天机器人的需求
2. 能够说出实现聊天机器人的流程
提示:以下是本篇文章正文内容,下面案例可供参考
一、流程介绍
1.项目流程和环境的安装
整体架构的描述如下:
- 接收用户的问题之后,对问题进行基础的处理
- 对处理后的问题进行分类,判断其意图
- 如果用户希望闲聊,那么调用闲聊模型返回结果
- 如果用户希望咨询问题,那么调用问答模型返回结果
2.闲聊模型
闲聊模型使用了seq2seq模型实现,主要包含:
- 对数据的embedding
- 编码层
- attention机制的处理
- 解码层
2. 问答模型
问答模型使用了召回和排序的机制来实现,保证获取速度的同时也保证了准确率
- 问题分析:对问题进行基础的处理,包括分词,词性的获取,词向量的获取
- 问题的召回:通过机器学习的方法进行海选,海选出大致满足要求的相似问题的前K个
- 问题的排序:通过深度学习模型计算问题和K个问题的相似度,返回最相似的问题和对应的答案
- 设置阈值,返回结果
3. fasttext安装
文档地址:https://fasttext.cc/docs/en/support.html
安装步骤
4. pysparnn 安装
文档地址:https://github.com/facebookresearch/pysparnn
安装步骤:
- 下载:
git clone https://github.com/facebookresearch/pysparnn.git
- 安装:
python setupy.py install
二、语料准备
1. 分词词典
词典的来源:
- 各种输入法的词典
例如:(搜狗输入法词典)https://pinyin.sogou.com/dict/cate/index/97?rf=dictindex
例如:(百度输入法词典)https://shurufa.baidu.com/dict_list?cid=211
- 手动收集,根据自己的需求,可以手动相应的词典
1.1 词典处理
输入法的词典都是特殊格式,需要使用特殊的工具才能够把它转化为文本格式
工具名称:深蓝词库转换.exe
下载地址:https://github.com/studyzy/imewlconverter
1.2 对多个词典文件内容进行合并
下载使用不同平台的多个词典之后,把所有的txt文件合并到一起使用
2. 准备停用词
2.1 停用词的准备
常用停用词下载地址:https://github.com/goto456/stopwords
2.2 停用词的准备
对于停用词的具体内容,不同场景下可能需要保留和去除的词语不一样,可以保留一下有用的词
3. 语料准备
- 准备问答对
问答对有两部分,一部分是咨询老师整理的问答对,一部分是excel中的问答对,最终我们需要把问答对分别整理到两个txt文档中,一个是问题,一个是答案。
excel中的问答对直接使用pandas就能处理
python_path = "./data/user_data.xlsx"
def load_duanwen():
import pandas as pd
ret = pd.read_excel(python_path)
column_list = ret.columns
assert '问题' in column_list and "答案" in column_list,"excel 中必须包含问题和答案"
for q,a in zip(ret["问题"],ret["问题"]):
q = re.sub("\s+"," ",q)
q = q.strip()
print(q,a)
- 相似问答对的采集
后续在判断问题相似度的时候,需要有语料用来进行模型的训练,输入两个句子,输出相似度,这个语料不好获取,所以决定从百度知道入手,采集百度知道上面的相似问题。
上面采集的数据会存在部分噪声,部分问题搜索到的结果语义上并不是太相似。
4. 文本分词
"""
分词
"""
import logging
import jieba
import jieba.posseg as psg
import config
import string
# 关闭jieba log输出
jieba.setLogLevel(logging.INFO)
# 加载自定义词典
jieba.load_userdict(config.user_dict_path)
# 单词分割,英文部分
letters = string.ascii_lowercase+"+"
# 加载停用词
stopwords = [i.strip() for i in open(config.stopwords).readlines()]
def cut_sentence_by_word(sentence):
"""实现中英文分词"""
temp = ""
result = []
for word in sentence:
# 把英文分词进行拼接
if word.lower() in letters:
temp += word
else:
if temp!="": # 出现中文,把英文添加到结果中
result.append(temp.lower())
temp = ""
result.append(word.strip())
if temp!="": # 把最后英文添加到结果中
result.append(temp.lower())
return result
def cut(sentence,by_word = False,use_stopwords=False,with_sg = False):
"""
:param sentence: 句子
:param by_word: 是否按照单个字分词
:param use_stopwords: 是否使用停用词
:param with_sg: 是否返回词性
:return:
"""
if by_word:
result = cut_sentence_by_word(sentence)
else:
result = psg.lcut(sentence)
result = [(i.word,i.flag) for i in result]
if not with_sg:
result = [i[0] for i in result]
if use_stopwords:
result = [word for word in result if word not in stopwords]
return result
三、意图识别和文本分类
文本分类的目的就是为了进行意图识别
1. 机器学习中的常见的分类方法
在机器学习中可以使用朴素贝叶斯、决策树、随机森林进行文本分类。
步骤:
- 特征工程:对文本进行处理,转化为能够被计算的向量来表示。我们可以考虑使用所有词语的出现次数,也可以考虑使用tidf这种方法来处理。
- 对模型进行训练
- 对模型进行评估
优化:
使用机器学习的方法进行文本分类的时候,为了让结果更好,会从两个角度出发:
- 特征工程的过程中处理的更加细致,比如可以删除一些出现次数太多或太少的词语
- 使用不同的算法进行训练,获取不同算法的结果,选择最好的。或者使用集成学习方法
2. 深度学习实现文本分类
在深度学习中常见的操作是:
- 对文本进行embedding的操作,转化为向量
- 之后再通过多层的神经网络进行线性或非线性的变化得到结果
- 变化后的结果和目标值进行计算得到损失函数,比如对数似然损失
- 通过最小化损失函数,去更新原来模型的参数
四、fastText实现文本分类
1. fastText的介绍
文档地址:https://fasttext.cc/docs/en/support.html
fastText是一个单词表示学习和文本分类的库
优点:在标准的多喝CPU上,在10分钟之内能够训练10亿词级别语料库的词向量,能够在1分钟内给30万多类别的50多万句子进行分类。
fastText模型输入一个词的序列(一段文本或者一句话),输出这个词序列属于不同类别的概率。
2. 安装和基本使用
2.1 安装
此处为方法二
下载:git clone https://github.com/facebookresearch/fastText.git
安装python setup.py install
2.2 基本使用
1. 把数据准备为需要的格式
需要的数据格式如下:
以上格式是fastText要求的格式,其中chat、QA字段可以自定义,就是目标值,__label__
之前的为特征值,需要用\t
进行分割,特征值需要进行分词,__label__
后面的就是目标值。
准备特征文本
问答部分使用之前通过模块构造的样本和爬虫抓取的百度上的相似问题
准备闲聊文本
使用小黄鸡的预料,地址:https://github.com/fateleak/dgk_lost_conv/tree/master/results
2. 把文本转化为需要的格式
对两部分文本进行分词、合并、转化为需要的格式
from lib.cut_sentence import cut
import config
import json
# 小黄鸡聊天数据路径
xiaohuangji_path = ""
# 问答数据路径
byhand_path = ""
crawled_path = ""
def keywords_in_line(line):
"""判断line中是否存在不符合要求的词"""
keywords_list = ["传智播客","python","java","javaee","linux","c++","人工智能","大数据","前端","后端"]
for word in line:
if word in keywords_list:
return True
else:
return False
def process_xiaohuangji(file):
"""处理小黄鸡的预料"""
num = 0
for line in open(xiaohuangji_path).readlines():
if line.startswith("E"):
continue
elif line.startswith("M"):
line = line[1:].strip()
line_cuted = cut(line)
if not keywords_in_line(line_cuted):
result = " ".join(line_cuted)+"\t"+"__label__chat"
file.write(result+"\n")
num += 1
return num
def process_byhand_data(file):
"""处理手动构造的数据"""
num = 0
total_lines = json.loads(byhand_path).read()
for key in total_lines:
for lines in total_lines[key]:
for line in lines:
line_cuted = cut(line)
result = " ".join(line_cuted) + "\t" + "__label__QA"
file.write(result + "\n")
num += 1
return num
def process_crawled_data(file):
"""处理爬虫的数据"""
num = 0
for line in open(crawled_path).readlines():
line_cuted = cut(line)
result = " ".join(line_cuted) + "\t" + "__label__QA"
file.write(result + "\n")
num += 1
return num
def process():
f = open(config.classify_corpus_path,"a")
# 1. 处理小黄鸡
num_chat = process_xiaohuangji(f)
# 2. 处理手动构造的数据
num_QA = process_byhand_data(f)
# 3. 处理爬虫的数据
num_QA += process_crawled_data(f)
f.close()
print(num_chat,num_QA)
2. 进行模型的训练、保存和加载、预测
# 1. 训练
model = fastText.train_supervised("./data/text_classify,txt",wordNgrams=1,epoch=20)
# 2. 保存
model.save_model("./data/ft_classify.model")
# 3. 加载
model = fastText.load_model("./data/ft_classify.model")
textlist = []
# 4. 预测,传入句子列表
ret = model.predict(textlist)
五、分类模型的准备
模型的训练、保存和评估
import fastText
import config
# 模型的保存
def build_classify_model():
model = fastText.train_supervised(config.classify_corpus_path,mincount = 5,epoch = 20,wordNgram=1)
model.save_model(config.classify_model_path)
# 模型的加载
def get_classify_model():
model = fastText.load_model(config.classify_model_path)
return model
def eval(by_word=False):
"""
模型的评估
:param by_word:是否按照单个字为特征
:return: 平均准确率
"""
model = get_classify_model(by_word)
# 处理classify_test.txt 分割特征和真实值
input = []
target = []
eval_data_path = config.classify_corpus_test_path if not by_word else config.classify_corpus_by_word_test_path
for line in open(eval_data_path).readlines():
temp = line.split("__label__")
if len(temp)<2:continue
input.append(temp[0].strip())
target.append(temp[1].strip())
# 使用特征和模型进行预测
labels,acc_list = model.predict(input)
# 计算准确率
sum = 0
print(len(labels),len(target))
for i,j in zip(labels,target):
if i[0].replace("__label__") == j:
sum+=1
acc = sum/len(labels) # 平均准确率
return acc
模型的预测
model = get_classify_model()
test = ["java","今天 天气 怎么 样","python"]
ret = model.predict(test)
六、模型的封装
为了在项目中更好的使用模型,需要对模型进行简单的封装,输入文本,返回结果。
"""
意图识别模型的封装
"""
import fastText
import config
from lib.cut_sentence import cut
class classify:
def __init__(self):
self.ft_word_model = fastText.load_model(config.fastText_word_model_path)
self.ft_model = fastText.load_model(config.fastText_model_path)
def is_qa(self,sentence_info):
"""判断是否是问答"""
python_qa_list = [" ".join(sentence_info["cuted_sentence"])]
result = self.ft_word_model.predict(python_qa_list)
# 按照单字分词的分类
python_qs_list = [" ".join(cut(sentence_info["sentence"],by_word=True))]
words_result = self.ft_word_model.predict(python_qs_list)
acc,word_acc = self.get_qa_prob(result,words_result)
if acc>0.95 or word_acc>0.95:
return True
else:
return False
def get_qa_prob(self,result,words_result):
label,acc,word_label,word_acc = zip(*result,*words_result)
label = label[0]
acc = acc[0]
word_label = word_label[0]
word_acc = word_acc[0]
if label == "__label__chat":
acc = 1-acc
if word_label == "__label__chat":
word_acc = 1-word_acc
return acc,word_acc