通过ES机器学习实现智能问答

本文使用文本嵌入模型(text_embedding)对用户查询进行深度语义解析,突破传统关键词匹配的局限,从海量数据中准确提取高度相关的内容。使用问答模型(question_answering)对关联文本进行精细解读,精准抽取答案,回答与文本相关的问题。文本嵌入模型和问答模型的组合可以用于智能搜索引擎、个性化推荐等场景,可以提升信息检索和问题解答的精准度。

准备工作

上传模型

本文选择huggingface仓库中的question_answering模型luhua/chinese_pretrain_mrc_macbert_large和text_embedding模型thenlper/gte-large-zh。将模型上传到阿里云ES中,请参见通过Eland上传第三方NLP模型

由于中国内地网络访问huggingface较慢,本文采用离线上传模型的方式。

  1. 下载模型。

  2. 将模型上传到ECS中。

  3. 在ECS中执行如下命令,在模型文件目录下解压模型。

    cd /model/
    tar -xzvf luhua--chinese_pretrain_mrc_macbert_large.tar.gz
    tar -xzvf thenlper--gte-large-zh.tar.gz
    cd
  4. 在ECS中执行如下命令,将模型上传到ES中。

    1. 上传问答模型luhua--chinese_pretrain_mrc_macbert_large:

      eland_import_hub_model       
      --url 'http://es-cn-lbj3l7erv0009****.elasticsearch.aliyuncs.com:9200'       
      --hub-model-id '/model/root/.cache/huggingface/hub/models--luhua--chinese_pretrain_mrc_macbert_large/snapshots/f2d95d06f16a3043002c9702f66c834f4e0aa944'       
      --task-type question_answering       
      --es-username elastic       
      --es-password  ****       
      --es-model-id models--luhua--chinese_pretrain_mrc_macbert_large \
    2. 上传文本嵌入模型thenlper--gte-large-zh:

      eland_import_hub_model       
      --url 'http://es-cn-lbj3l7erv0009****.elasticsearch.aliyuncs.com:9200'       
      --hub-model-id '/model/root/.cache/huggingface/hub/models--thenlper--gte-large-zh/snapshots/952432e6b99137bbfd8397d5ad92f920be5f22e9'       
      --task-type text_embedding       
      --es-username elastic       
      --es-password  ****       
      --es-model-id models--thenlper--gte-large-zh \

部署模型

  1. 登录Kibana。具体操作,请参见登录Kibana控制台

  2. 单击Kibana页面左上角的image图标,选择Analytics > Machine Learning

  3. 在左侧菜单栏,单击模型管理(Model Management) > 已训练模型(Trained Models)。

  4. (可选)在页面上方,单击同步作业和已训练模型(Synchronize your jobs and trained models),在弹出的面板中单击同步(Synchronize)。

  5. 将鼠标移动到目标模型操作(Actions)列的前面,单击image图标,启动模型。

    image

  6. 在弹出的对话框中,配置模型后,单击启动(Start)。

    页面右下角弹出已成功启动的提示对话框,表明模型部署成功。

    说明

    模型无法启动可能是集群内存不足,升配集群后再试。无法启动的具体原因,请在提示对话框中单击请参阅完整的错误信息查看。

验证模型的可用性

  1. 单击Kibana页面左上角的image图标,选择Management > 开发工具(Dev Tools)。

  2. 验证模型的可用性

    • 执行以下命令,验证问答模型models--luhua--chinese_pretrain_mrc_macbert_large的可用性。

      POST /_ml/trained_models/models--luhua--chinese_pretrain_mrc_macbert_large/_infer
      {
       "docs":[{"text_field": "没有必要。安装两个杀毒软件可以增强电脑的安全性,但是会导致以下弊端: 占用硬盘资源过多; 在同时启用两款软件的实时防护时,会导致系统硬件资源大量损耗,例如CPU处理过多,导致计算机工作能力下降; >在进行杀毒时,会导致系统占用资源过多,不但可能影响系统工作能力,还可能误报对方组件,导致.."}],
       "inference_config": {"question_answering": {"question": "电脑可以同时安装多个杀毒软件吗"}}
      }
      
      

      返回结果如下所示,结果符合预期。

      {
        "inference_results": [
          {
            "predicted_value": "没有必要",
            "start_offset": 0,
            "end_offset": 4,
            "prediction_probability": 0.8775923604355836
          }
        ]
      }
    • 执行以下命令,验证文本嵌入模型models--thenlper--gte-large-zh的可用性。

      POST /_ml/trained_models/models--thenlper--gte-large-zh/_infer
      {
        "docs":[{"text_field": "没有必要。安装两个杀毒软件可以增强电脑的安全性,但是会导致以下弊端: 占用硬盘资源过多; 在同时启用两款软件的实时防护时,会导致系统硬件资源大量损耗,例如CPU处理过多,导致计算机工作能力下降; >在进行杀毒时,会导致系统占用资源过多,不但可能影响系统工作能力,还可能误报对方组件,导致.."}]
      }

      部分返回结果如下所示,结果符合预期。

      {
        "inference_results": [
          {
            "predicted_value": [
              1.389997959136963,
              -0.6398589611053467,
              -0.5746312737464905,
              -0.5629222393035889,
              -0.49914881587028503,
              0.5277091264724731,
              -1.2194437980651855,
              0.19847321510314941,
              ..............
              0.6711148619651794,
              1.6224931478500366,
              2.0970489978790283,
              -0.4506820738315582,
              -0.298383504152298
            ]
          }
        ]
      }

准备数据集

索引数据时需要示例数据集。

  1. 下载示例数据集。

    文本以extracted_data.json数据集为例,请参见extracted_data.json

  2. 将数据集上传到ECS中,例如文本将数据集上传到/model/目录下。

步骤一:索引数据

  1. 通过ingest pipeline在索引中配置模型,以便在数据写入过程中完成文本转换。

    说明

    针对索引已存储数据需要应用模型完成向量转换,请参见reindex方式重建索引

    PUT _ingest/pipeline/text-embedding-pipeline
    {
      "processors": [
        {
          "inference": {
            "model_id": "models--thenlper--gte-large-zh",
            "target_field": "text_embedding",
            "field_map": {
              "context": "text_field"
            }
          }
        }
      ]
    }
    
    #####创建索引mapping,指定ingest pipeline
    PUT question_answering
    {
      "settings": {
        "index": {
              "number_of_shards": 2,
              "number_of_replicas": 1,
              "default_pipeline": "text-embedding-pipeline"
          }
      }, 
      "mappings": {
        "properties": {
          "text_embedding.predicted_value": {
            "type": "dense_vector",
            "dims": 1024,
            "index": true,
            "similarity": "l2_norm"
          },
          "context": {
            "type": "text",
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            }
          },
          "title": {
            "type": "text",
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            }
          }
        }
      }
    }
  2. 通过Python导入准备的数据集(python 3.10):

    # -*- coding:utf-8 -*-
    import json
    from pprint import pprint
    import os
    import time
    from elasticsearch import helpers
    import time
    from elasticsearch import Elasticsearch
    
    elastic_user="elastic"
    elastic_password="****"
    elastic_endpoint="es-cn-lbj3l7erv0009****.elasticsearch.aliyuncs.com"
    url = f"http://{elastic_user}:{elastic_password}@{elastic_endpoint}:9200"
    es = Elasticsearch(url)
    print(es.info())
    with open('/model/extracted_data.json') as f:
     data_json = json.load(f)
    
    # Prepare the documents to be indexed
    documents = []
    for doc in data_json:
     documents.append({
     "_index": "question_answering",
     "_source": doc,
     })
    
    # Use helpers.bulk to index
    helpers.bulk(es, documents)
    
    print("Done indexing documents into `question_answering` index!")
    time.sleep(5)

步骤二、通过kibana检索

以“阿里云Elasticsearch应用场景有哪些”问题为例,在kibana上进行检索演示:

  1. 单击Kibana页面左上角的image图标,选择Management > 开发工具(Dev Tools)。

  2. 调用阿里云ES机器学习推理接口,将文档转换为向量数据。

    ES 8.7及以上版本

    ES 8.7及以上版本可以通过query_vector_builder构建。

    GET question_answering/_search
    {
      "_source": ["context","title"],
      "knn": {
        "field": "text_embedding.predicted_value",
        "k": 5,
        "num_candidates": 10,
        "query_vector_builder": {
          "text_embedding": { 
            "model_id": "models--thenlper--gte-large-zh", 
            "model_text": "阿里云elasticsearch应用场景有哪些" 
          }
        }
      }
    }

    返回结果如下所示,可以看出前5条文档和ES相关性比较大,第一条内容主要介绍阿里云ES服务。

    {
      "took": 30,
      "timed_out": false,
      "_shards": {
        "total": 2,
        "successful": 2,
        "skipped": 0,
        "failed": 0
      },
      "hits": {
        "total": {
          "value": 5,
          "relation": "eq"
        },
        "max_score": 0.003581697,
        "hits": [
          {
            "_index": "question_answering",
            "_id": "evdbaI0BU_1of7kX5_Hn",
            "_score": 0.003581697,
            "_source": {
              "context": "阿里云Elasticsearch是基于开源Elasticsearch构建的全托管Elasticsearch云服务,在100%兼容开源功能的同时,支持开箱即用、按需付费。不仅提供云上开箱即用的Elasticsearch、Logstash、Kibana、Beats在内的Elastic Stack生态组件,还与Elastic官方合作提供免费X-Pack(白金版高级特性)商业插件,集成了安全、SQL、机器学习、告警、监控等高级特性,被广泛应用于实时日志分析处理、信息检索、以及数据的多维查询和统计分析等场景。",
              "title": "什么是阿里云Elasticsearch"
            }
          },
          {
            "_index": "question_answering",
            "_id": "2VBdaI0Bg5EzGjIAN-Tx",
            "_score": 0.003581697,
            "_source": {
              "context": "阿里云Elasticsearch是基于开源Elasticsearch构建的全托管Elasticsearch云服务,在100%兼容开源功能的同时,支持开箱即用、按需付费。不仅提供云上开箱即用的Elasticsearch、Logstash、Kibana、Beats在内的Elastic Stack生态组件,还与Elastic官方合作提供免费X-Pack(白金版高级特性)商业插件,集成了安全、SQL、机器学习、告警、监控等高级特性,被广泛应用于实时日志分析处理、信息检索、以及数据的多维查询和统计分析等场景。",
              "title": "什么是阿里云Elasticsearch"
            }
          },
          {
            "_index": "question_answering",
            "_id": "t49gaI0BNqB2ciGcDbmF",
            "_score": 0.003581697,
            "_source": {
              "context": "阿里云Elasticsearch是基于开源Elasticsearch构建的全托管Elasticsearch云服务,在100%兼容开源功能的同时,支持开箱即用、按需付费。不仅提供云上开箱即用的Elasticsearch、Logstash、Kibana、Beats在内的Elastic Stack生态组件,还与Elastic官方合作提供免费X-Pack(白金版高级特性)商业插件,集成了安全、SQL、机器学习、告警、监控等高级特性,被广泛应用于实时日志分析处理、信息检索、以及数据的多维查询和统计分析等场景。",
              "title": "什么是阿里云Elasticsearch"
            }
          },
          {
            "_index": "question_answering",
            "_id": "efdbaI0BU_1of7kX5_Hn",
            "_score": 0.0027631863,
            "_source": {
              "context": "登录Kibana控制台的具体操作,请参见登录Kibana控制台。Kibana控制台的用户名默认为elastic,密码为您创建阿里云Elasticsearch实例时设置的密码。如果忘记密码可以重置,重置密码的注意事项及具体操作,请参见重置实例访问密码。",
              "title": "如何登录Kibana控制台,用户名和密码是什么?"
            }
          },
          {
            "_index": "question_answering",
            "_id": "2FBdaI0Bg5EzGjIAN-Tx",
            "_score": 0.0027631863,
            "_source": {
              "context": "登录Kibana控制台的具体操作,请参见登录Kibana控制台。Kibana控制台的用户名默认为elastic,密码为您创建阿里云Elasticsearch实例时设置的密码。如果忘记密码可以重置,重置密码的注意事项及具体操作,请参见重置实例访问密码。",
              "title": "如何登录Kibana控制台,用户名和密码是什么?"
            }
          }
        ]
      }
    }

    ES 8.7以下版本

    1. 调用ES机器学习推理接口。

      POST /_ml/trained_models/thenlper__gte-large-zh/_infer
      {
        "docs":[{"text_field": "阿里云elasticsearch应用场景有哪些"}]
      }

      返回结果如下所示:

      image.png

    2. 复制向量数据,使用knn搜索索引中相似的文档。

      GET question_answering/_search
      { 
        "_source": ["context","title"],
        "knn": {
          "field": "text_embedding.predicted_value",
          "k": 5,
          "num_candidates": 10,
          "query_vector": [
            0.2767493426799774,
            0.05577810853719711,
            0.2760164141654968,
            -0.9484721422195435,
            ..............
            0.8358230590820312,
            0.6053569316864014,
            -0.5380803942680359
          ]
        }
      }
  3. 将第一个文档的context复制下来调用QA模型实现问答。

    POST /_ml/trained_models/models--luhua--chinese_pretrain_mrc_macbert_large/_infer
    {
      "docs":[{"text_field": "阿里云Elasticsearch是基于开源Elasticsearch构建的全托管Elasticsearch云服务,在100%兼容开源功能的同时,支持开箱即用、按需付费。不仅提供云上开箱即用的Elasticsearch、Logstash、Kibana、Beats在内的Elastic Stack生态组件,还与Elastic官方合作提供免费X-Pack(白金版高级特性)商业插件,集成了安全、SQL、机器学习、告警、监控等高级特性,被广泛应用于实时日志分析处理、信息检索、以及数据的多维查询和统计分析等场景"}],
      "inference_config": {"question_answering": {"question": "阿里云elasticsearch应用场景有哪些"}}
    }

    返回结果如下:

    {
      "inference_results": [
        {
          "predicted_value": "实时日志分析处理",
          "start_offset": 220,
          "end_offset": 228,
          "prediction_probability": 0.014678373776954107
        }
      ]
    }

步骤三:模拟问答场景

在ECS中执行Python3加载Python环境后,执行以下命令模拟问答场景。

# -*- coding:utf-8  -*-
import json
from pprint import pprint
import os
import time
from elasticsearch import helpers
import time
from elasticsearch import Elasticsearch

elastic_user="elastic"
elastic_password="Es123456"
elastic_endpoint="es-cn-lbj3l7erv0009bz81.public.elasticsearch.aliyuncs.com"
url = f"http://{elastic_user}:{elastic_password}@{elastic_endpoint}:9200"
es = Elasticsearch(url)

question = input("请输入您的问题: ")

query_args = {
    "field": "text_embedding.predicted_value",
    "k": 5,
    "num_candidates": 10,
    "query_vector_builder": {
        "text_embedding": {
            "model_id": "models--thenlper--gte-large-zh",
            "model_text": question
        }
    }
}

result=es.search(index="question_answering",source=["context","title"],knn=query_args)
# print(result["hits"]["hits"])
concatenated_content = ""
for hit in result["hits"]["hits"]:
    context=hit["_source"]["context"]
    concatenated_content += context+" "

#print(concatenated_content)
docs=[{"text_field": concatenated_content}]
inference_config= {"question_answering": {"question": question}}

response=es.ml.infer_trained_model(model_id="models--luhua--chinese_pretrain_mrc_macbert_large",docs=docs,inference_config=inference_config)
print("推理结果是: ")
print(response["inference_results"][0]["predicted_value"])

返回结果如下所示。

请输入您的问题:
张家港高铁站位置在哪
推理结果是:
塘桥镇新204国道东侧,人民路北路