Elasticsearch是一个全文搜索引擎,可以快速地储存、搜索和分析海量数据。它是一个开源的搜索引擎,建立在 A p a c h e L u c e n e T M Apache Lucene^{TM} ApacheLuceneTM基础之上。ElasticSearch不仅仅是一个简易的Lucene封装,它可以被形容为:
关于Elasticsearch就不做过多的介绍了,下面开始做聊天机器人。
首先是Elasticsearch的安装,可以参考这篇文章:【ElasticSearch】win10 安装elasticSearch 6.6.1,另外我们还需要安装elasticsearch-analysis-ik用于中文分词。
然后接下来我们需要了解一下Elasticsearch的基本用法,当然我这里采用的是Python版本,它的基本使用可以参考这篇文章:Elasticsearch 基本介绍及其与 Python 的对接实现或者是参考官方API文档。
接下来是要准备语料了,我们可以使用小黄鸡的语料,链接为:用于对话系统的中英文语料。这个链接里还有其他的语料,需要的可以自取。
我将语料整理为csv格式,数据包含两列,一列为question,一列为answer,大致如下:
question,answer
呵呵,是王若猫的。
"我还喜欢她,怎么办",我帮你告诉她?发短信还是打电话?
短信,嗯嗯。我也相信
你知道谁么,肯定不是我,是阮德培
许兵是谁,吴院四班小帅哥
然后调用如下代码,将数据插入Elasticsearch。
class ESUtils(object):
def __init__(self, index_name, create_index=False):
self.es = Elasticsearch()
self.index = index_name
if create_index:
mapping = {
'properties': {
'question': {
'type': 'text',
'analyzer': 'ik_max_word',
'search_analyzer': 'ik_smart'
}
}
}
# 创建index
if self.es.indices.exists(index=self.index):
self.es.indices.delete(index=self.index)
self.es.indices.create(index=self.index)
# 创建mapping
self.es.indices.put_mapping(body=mapping, index=self.index)
def insert_qa_pairs(self, qa_pairs, data_source):
count = self.es.count(index=self.index)['count'] # 获取当前数据库中的已有document数量
def gen_data():
for i, qa in enumerate(qa_pairs):
yield {
'_index': self.index,
'_id': i + count,
'data_source': data_source,
'question': qa[0],
'answer': qa[1]
}
bulk(self.es, gen_data())
上述代码第一次调用需要创建index,则__init__方法的create_index参数要为True。之后调用insert_qa_pairs方法插入数据。这里的qa_pairs可以通过以下代码读取之前整理好的csv文件得到。
def get_qa_pairs(csv_path):
qa_pairs = pd.read_csv(csv_path)
qa_pairs = list(zip(qa_pairs['question'], qa_pairs['answer']))
return qa_pairs
经过上述步骤,我们就准备好了聊天数据了(需要注意的是,这里都是单轮对话数据,多轮对话功能还在进一步开发中)。
接着,使用下面的代码我们就可以愉快的开始聊天了。
import sys
from elasticsearch import Elasticsearch
class ESChat(object):
def __init__(self, ip, port, index_name):
self.es = Elasticsearch(hosts=[ip], port=port)
self.index = index_name
def search(self, input_str):
"""
Args:
input_str: 用户问句
Returns: 由匹配的question、answer和其分数score构成的字典的列表
"""
dsl = {
"query": {
"match": {
"question": input_str
}
}
}
hits = self.es.search(index=self.index, body=dsl)["hits"]["hits"]
qa_pairs = []
for h in hits:
qa_pairs.append(
{'score': h['_score'], 'question': h['_source']['question'], 'answer': h['_source']['answer']})
return qa_pairs
def chat(self):
"""聊天方法,在系统输出'> '后输入句子,得到系统回复,输入exit结束聊天。"""
sys.stdout.write("> ")
sys.stdout.flush()
sentence = sys.stdin.readline().strip()
while sentence:
if sentence == 'exit':
break
print(self.search(sentence)[0]['answer'])
print("> ", end='')
sys.stdout.flush()
sentence = sys.stdin.readline().strip()
if __name__ == '__main__':
es_chat = ESChat(ip='localhost', port=9200, index_name='qa')
es_chat.chat()
聊天效果截图如下:
好吧,聊天体验看来并不愉快。总结原因,一个是搜集的语料可能有问题或者是我整理的过程出错了,还有一个主要的原因是这里没有结合语义等信息,只是单纯的检索匹配。
虽然效果不理想,但不失为一个制作聊天机器人的尝试,对于这个检索式机器人我的下一步优化方案大致是: