栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

基于bert特征提取的FAQ问答系统构建

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

基于bert特征提取的FAQ问答系统构建

faq的问答系统是目前用的比较广泛的问答系统,由于它落地简单,并且大部分场景都需要,构建一个faq问答系统可以作为一个baseline快速应用到实际场景中。下面就介绍如何快速构建一个faq问答的baseline。

一、环境的搭建

faq的核心技术是信息检索,信息检索的常用工具则是es,es既可以对faq知识库存储,又可以快速查询文本。同时也有配套的可视化工具kibana,方便数据查询和管理。此外,es的社区也很活跃,文档和讨论的问题也方便使用。

elasticsearch+kibana安装

使用docker容器拉取镜像文件,注意elasticsearch和kibana的版本要一致,本人使用的版本均为7.8.1的版本。

启动容器,根据官网的教程启动docker容器,选用单个节点

docker run -p 9200:9200 -p 9300:9300 --name es7.8
-e "discovery.type=single-node" 
-e ES_JAVA_OPS="-Xms512m -Xmx512m" 
-d elasticsearch:7.8.1

如果是中文检索,需要安装中文分词器,先到GitHub - medcl/elasticsearch-analysis-ik: The IK Analysis plugin integrates Lucene IK analyzer into elasticsearch, support customized dictionary.上下载对应版本的ik分词器,然后进入容器解压到对应文件夹。

安装完es后再安装kibana

docker run --link es7.8.1:elasticsearch -p 5601:5601 -d kibana:7.8.1

二、python操作es

创建索引

es = Elasticsearch('http://127.0.0.1:9200')
base_info_index_mappings = {
    "settings":{
        "index.analysis.analyzer.default.type": "ik_max_word"
    },
    "mappings":{
        "properties": {
            "id": {
                "type": "keyword"
            },
            "standard_question": {
                "type": "text",
                "analyzer": "ik_max_word",
                "search_analyzer": "ik_max_word"
            },
            "sim_question": {
                "type": "text",
                "analyzer": "ik_max_word",
                "search_analyzer": "ik_max_word"
            },
            "answer": {
                "type": "text",
                "analyzer": "ik_max_word",
                "search_analyzer": "ik_max_word"
            },
            "query_vector": {
                "type": "dense_vector",
                "dims": 768
            },
            "update_date": {
                "type": "date",
                "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd"
            },
            "create_time": {
                "type": "date",
                "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd"
            },
        }
    }
}

批量插入数据

def insert_data(index_name, items, batch_size=100):
    '''
    批量插入数据到索引中
    :param index_name:
    :param items:
    :param batch_size:
    :return:
    '''
    batch = []
    err_count = 0
    for i, columns in enumerate(items):
        [sim_question, std_question, answer, vector] = columns
        id = uuid.uuid1()
        update_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        create_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')

        try:
            body = {
                '_index': index_name,
                '_source': {
                    'id': id,
                    'standard_question': std_question,
                    'sim_question': sim_question,
                    'answer': answer,
                    'query_vector': vector,
                    'update_time': update_time,
                    'create_time': create_time
                }
            }
        except Exception as e:
            err_count += 1
            print(e)
        if len(batch)0:
        helpers.bulk(es, batch)
        print('最终id是{}'.format(cur_id))
    print('err_count:{}'.format(err_count))
    return '插入数据完成'

其中的query_vector通过bert模型提前计算出768维向量并存入es的dense_vector格式数据字段中。关于bert向量计算可参考下部分代码

    def extract_vectors(self, model, text):
        '''
        抽取句子向量
        :param text:
        :return:
        '''
        batch_token_ids, batch_segment_ids, batch_mask = [], [], []
        word_ids, segment_ids, word_mask = encode(text)
        batch_token_ids.append(word_ids)
        batch_segment_ids.append(segment_ids)
        batch_mask.append(word_mask)
        inputs = dict(
            input_word_ids=batch_token_ids,
            input_mask=batch_mask,
            input_type_ids=batch_segment_ids,
        )
        infer_input = {
            "input_word_ids": tf.convert_to_tensor(inputs['input_word_ids']),
            "input_mask": tf.convert_to_tensor(inputs['input_mask']),
            "input_type_ids": tf.convert_to_tensor(inputs['input_type_ids']),
        }

        outputs = model(infer_input)
        encoder_outputs = outputs[0]
        last_encoder = encoder_outputs[-1]

        mean_vecor = tf.reduce_mean(last_encoder[:, 1:len(text) + 1, :], axis=1)
        print(mean_vecor.shape)
        mean_vecor_norm = tf.norm(mean_vecor, ord=2, axis=0)
        mean_vecor = mean_vecor / mean_vecor_norm
        print(mean_vecor.numpy()[0].shape)
        return mean_vecor.numpy()[0].tolist()

三、查询

es可以计算向量的余弦相似度,通过向量检索的形式可以找出最相近的问题,检索代码如下:

script_query = {
    "script_score": {
        "query": {
            "match_all": {
        },
        },
        "script": {
            "source": "cosineSimilarity(params.query_vector, 'query_vector') + 1.0",
            "params": {"query_vector": query_vector}
        }
    }
}
body = {
    "_source": ["standard_question", "sim_question", "answer"],
    "size": 5,
    "query": script_query,
}
res = search_data_by_body('faq_test_index', body)

其中query_vector为实时用bert抽取的查询文本向量。该方法速度很快,满足上线要求。

四、总结

以上就是一个faq问答系统的baseline,有一个比较重要的步骤是在之前就做好了,就是知识库的构建,需要某一业务场景的常用的faq问题及答案的总结。在baseline的基础上,后续可以不断丰富知识库,也可以通过精排、意图分类等方法提高查询的准确率。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/946647.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号