使用Elasticsearch做一个简易的检索式聊天机器人

Elasticsearch是一个全文搜索引擎,可以快速地储存、搜索和分析海量数据。它是一个开源的搜索引擎,建立在 A p a c h e L u c e n e T M Apache Lucene^{TM} ApacheLuceneTM基础之上。ElasticSearch不仅仅是一个简易的Lucene封装,它可以被形容为:

  • 具有分布式实时文档存储,每个字段可以被索引与搜索
  • 一个分布式实时分析搜索引擎
  • 能胜任上百个服务节点的扩展,并支持PB级别的结构化或者非结构化数据

关于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()

聊天效果截图如下:
使用Elasticsearch做一个简易的检索式聊天机器人_第1张图片
好吧,聊天体验看来并不愉快。总结原因,一个是搜集的语料可能有问题或者是我整理的过程出错了,还有一个主要的原因是这里没有结合语义等信息,只是单纯的检索匹配。
虽然效果不理想,但不失为一个制作聊天机器人的尝试,对于这个检索式机器人我的下一步优化方案大致是:

  • 清理语料,提升语料质量
  • 结合语义信息
  • 完善搜索匹配方式

你可能感兴趣的:(Elasticsearch,检索式聊天机器人,聊天机器人)