在 Elasticsearch 中使用日语语言的自然语言处理 (NLP) 模型来实现语义搜索

blog-search-results-dark-720x420.png

从每天产生的大量内部文档和产品信息中快速查找所需的文档,是工作和日常生活中的一项极为重要的任务。然而,如果要搜索的文档数量庞大,即使是计算机实时重新读取所有文档并找到目标文件,也会是一个耗时的过程。在这种情况下,Elasticsearch® 和其他搜索引擎软件应运而生。在使用搜索引擎时,首先要创建搜索索引数据,以便使用文档中包含的关键搜索词来快速查找这些文档。

不过,即使用户大致知道自己要搜索什么类型的信息,他们也可能想不起合适的关键词,或者可能会搜索具有相同含义的另一种措辞。借助 Elasticsearch,可以定义同义词和相似术语来处理这种情况,但在某些情况下,仅仅使用对应表将搜索查询转换为更合适的查询,可能没那么容易。

为了解决这个问题,Elasticsearch 8.0 版发布了能够通过短语的语义内容进行搜索的矢量搜索功能。与此同时,我们还发布了一系列的博客文章,介绍如何使用 Elasticsearch 执行矢量搜索和其他 NLP 任务。不过,在发布 8.8 版之前,它都无法正确分析除英语以外的其他语言文本。

在发布的 8.9 版中,Elastic 增加了可在文本分析处理中正确分析日语的功能。通过这项功能,Elasticsearch 能够对日语文本执行矢量搜索等语义搜索以及日语情感分析等自然语言处理任务。本文将提供具体的分步说明,介绍如何使用这些功能。

准备工作

在实施语义搜索之前,请确认使用这项功能的准备工作。Elasticsearch 集群中的各个节点都分配有节点角色。同时,Elasticsearch Machine Learning 节点是驱动 Machine Learning 模型的节点。要使用这项功能,Elasticsearch 集群中必须有一个 Machine Learning 节点处于活跃状态,请务必提前确认这一点。此外,您还必须拥有白金级或更高级别的许可证,才能使用 Machine Learning 节点。不过,如果您只想对功能进行测试并确保运行正常,也可以使用试用版许可证。若要在开发环境或类似的实例中验证操作功能,可以在 Kibana® 屏幕上或通过 API 激活试用版。

执行语义搜索的过程

在 Elasticsearch 中执行语义搜索需要完成以下步骤。

  1. (准备)在工作站上安装 Eland 和相关的库。

  2. 导入 Machine Learning 模型,以执行自然语言处理任务。

  3. 在导入的 Machine Learning 模型中索引文本分析结果。

  4. 使用 Machine Learning 模型执行 kNN 搜索。

自然语言处理不仅仅支持语义搜索。在本博文的后半部分,我们将举例说明如何使用可执行文本分类任务的 Machine Learning 模型,对文本进行情感分析(积极和消极分类)。

让我们一起来深入了解如何执行以下任务。

Eland 安装

Elasticsearch 现在能够像自然语言处理平台一样运行。但实际上,Elasticsearch 并未真正实现深入的自然语言处理。任何必要的自然语言处理都必须由用户作为 Machine Learning 模型导入 Elasticsearch 中。这个导入过程是使用 Eland 执行的。由于用户可以通过这种方式自由导入外部模型,因此他们可以随时根据需求来添加 Machine Learning 功能。

Eland 是 Elastic 提供的一个 Python 库,可让用户将 Elasticsearch 数据与 PyTorch 和 scikit-learn 等综合性的 Python 机器学习库联结起来。Eland 中绑定的 eland_import_hub_model 命令行工具可用于将已发布到 Hugging Face 的 NPL 模型导入 Elasticsearch 中。本文后面内容中涉及的所有命令行任务都假定使用像 Google Colaboratory 这样的 Python 笔记本。(当然,也可以使用其他类型的终端,如 Mac 或 Linux 计算机。在这种情况下,请忽略下面命令开头的“!”。)

首先,安装相关的库。

!pip install torch==1.13
!pip install transformers
!pip install sentence_transformers
!pip install fugashi
!pip install ipadic
!pip install unidic_lite

要使用日语模型,必须要安装 fugashi、ipadic 和 unidic_lite 库。

在安装这些库之后,还可以安装 Eland。要使用日语模型,需要 Eland 8.9.0 或更高版本,因此请务必注意版本号。

!pip install eland

安装完成后,请使用下面的命令确认已安装的 Eland 可用。

!eland_import_hub_model -h

导入 NLP 模型

启用矢量搜索的主要方法与这篇文章中用于英语语言的方法相同。我们在这里简要回顾一下相同的过程。

如上所述,必须将适当的 Machine Learning 模型导入 Elasticsearch 中,才能在 Elasticsearch 中实现 NLP 处理。您可以使用 PyTorch 自行实现一个 Machine Learning 模型,但这也需要具备足够的机器学习和自然语言处理专业知识,以及机器学习所需的计算能力。不过,现在有一个名为 Hugging Face 的在线存储库,被机器学习和自然语言处理领域的研究人员和开发人员广泛使用,而且有许多模型都发布到这个存储库中。在这个例中,我们将使用发布到 Hugging Face 的模型来实现语义搜索功能。

首先,选取 Hugging Face 上的一个模型,将日语句子嵌入(矢量化为)数值矢量中。在本文中,我们将使用下面链接的模型。

下面我们来介绍一下在 8.9 版中选择日语模型时,需要注意的一些事项。

首先,只支持 BERT 模型算法。查看 Hugging Face 上的标签和其他信息,确认所需的 NLP 模型是经过 BERT 训练的模型。

此外,对于 BERT 和其他 NLP 任务,输入的文本都要经过“预分词”处理,也就是会在单词级别上将文本分割为多个单元。在本例中,我们使用了日语语言的形态分析引擎对日语文本进行预分词。Elasticsearch 8.9 版支持使用 MeCab 进行形态分析。在 Hugging Face 的模型页面上,打开“Files and versions”(文件和版本)选项卡,查看 tokenizer_config.json 文件的内容。确认 word_tokenizer_type 的值为 mecab。

{
    "do_lower_case": false,
    "word_tokenizer_type": "mecab",
    "subword_tokenizer_type": "wordpiece",
    "mecab_kwargs": {
        "mecab_dic": "unidic_lite"
    }
}

如果您想使用的模型的 word_tokenizer_type 值不是 mecab,非常遗憾,Elasticsearch 目前不支持其他模型。对于需要支持的任何具体单词分词器类型 (word_tokenizer_type),我们欢迎大家提供反馈意见。

在确定要导入的模型后,导入所需的步骤就与英语模型相同了。首先,使用 eland_import_hub_model 将模型导入 Elasticsearch 中。若要了解如何使用 eland_import_hub_model,请参考本页面

!eland_import_hub_model \
--url "https://your.elasticserach" \
--es-api-key "your_api_key" \
--hub-model-id cl-tohoku/bert-base-japanese-v2 \
--task-type text_embedding \
--start

成功导入模型后,可以在 Kibana 的“Machine Learning”>“Model Management”(模型管理)>“Trained Models”(已训练模型)下找到它。打开模型的“Config”(配置)选项卡,确认使用“bert_ja”进行分词,并且模型已经过正确设置,可以处理日语。

推理配置

模型上传完成后,让我们来测试一下。单击“Actions”(操作)列中的按钮以打开菜单。

操作

选择“Test model”(测试模型),然后在“Input text”(输入文本)下输入任何日语短语,单击“Test”(测试)按钮。

测试经过训练的模型

然后,您就可以看到这个模型将输入的日语文本矢量化为了一个数值字符串,如下所示。模型看起来运行正常。

使用矢量嵌入实现语义搜索

至此,模型已上传完毕;接下来我们可在 Elasticsearch 中实现语义搜索(矢量搜索)功能了。

首先,为了执行矢量搜索,必须对嵌入原始日语文本中的矢量值建立索引。为此,我们将创建一个包含推理处理器的管道,以便先将日语文本进行矢量化,然后再使用之前上传的模型将它输入到索引中。

PUT _ingest/pipeline/japanese-text-embeddings
{
  "description": "Text embedding pipeline",
  "processors": [
    {
      "inference": {
        "model_id": "cl-tohoku__bert-base-japanese-v2",
        "target_field": "text_embedding",
        "field_map": {
          "title": "text_field"
        }
      }
    }
  ],
  "on_failure": [
    {
      "set": {
        "description": "Index document to 'failed-<index>'",
        "field": "_index",
        "value": "failed-{{{_index}}}"
      }
    },
    {
      "set": {
        "description": "Set error message",
        "field": "ingest.failure",
        "value": "{{_ingest.on_failure_message}}"
      }
    }
  ]
}

在使用推理处理器时,model_id 中指定的模型将应用于目标字段(在本例中是 title)中保存的文本,因此,输出会存储在 target_field 中。此外,每个模型都需要不同的字段(在本例中是 text_field)作为过程的输入值。因此,field_map 用于指定过程目标的实际输入字段与 ML 模型所需的字段名称之间的对应关系。

设置好管道后,就可以用它来创建索引了。由于索引中需要有字段来存储矢量,因此我们需要定义适当的映射。在下面的示例中,text_embedding.predicted_value 字段被设置为存储 768 维度的 dense_vector 数据。请注意,维度的数量因模型而异。查看 Hugging Face 上的模型页面(模型的 config.json 中的 hidden_size 值)或其他位置,然后设置一个适当的数量。

PUT japanese-text-with-embeddings
{
  "mappings": {
    "properties": {
      "text_embedding.predicted_value": {
        "type": "dense_vector",
        "dims": 768,
        "index": true,
        "similarity": "cosine"
      }
    }
  }
}

如果已经存在包含用于搜索的日语文本数据的索引,就可以使用重新索引 API。在本例中,原始文本数据位于 japanese-text 索引中。包含这些文本矢量化内容的文档将注册到 japanese-text-embeddings 索引中。

POST _reindex?wait_for_completion=false
{
  "source": {
    "index": "japanese-text"
  },
  "dest": {
    "index": "japanese-text-with-embeddings",
    "pipeline": "japanese-text-embeddings"
  }
}

另外,也可以通过指定一个如下所示创建的管道并将其存储在索引中,直接注册文档来进行测试。

POST japanese-text-with-embeddings/_doc?pipeline=japanese-text-embeddings
{
  "title": "日本語のドキュメントをベクトル化してインデックスに登録する。"
}

注册完矢量化的文档后,此时就可以执行搜索了。kNN(k-最近邻)搜索是一种利用矢量的可用方法。我们现在将使用标准 _search API 中的 query_vector_builder 选项来执行 kNN 矢量搜索。使用 query_vector_builder 时,可以使用 model_id 中指定的模型,将 model_text 中的文本转换为包含嵌入该文本的矢量的查询。

GET japanese-text-with-embeddings/_search
{
  "knn": {
    "field": "text_embedding.predicted_value",
    "k": 10,
    "num_candidates": 100,
    "query_vector_builder": {
      "text_embedding": {
        "model_id": "cl-tohoku__bert-base-japanese-v2", 
        "model_text": "日本語でElasticsearchを検索したい"
      }
    }
  }
}

在执行这个搜索查询后,会收到以下类型的响应。

  "hits": [
      {
        "_index": "japanese-text-with-embeddings",
        "_id": "vOD6MIoBdRdLZd7EKaBy",
        "_score": 0.82438844,
        "_source": {
          "title": "日本語のドキュメントをベクトル化してインデックスに登録する。",
          "text_embedding": {
            "predicted_value": [
              -0.13586345314979553,
              -0.6291824579238892,
              0.32779985666275024,
              0.36690405011177063,
              (略、768次元のベクトルが表示される)
            ],
            "model_id": "cl-tohoku__bert-base-japanese-v2"
          }
        }
      }
  ]

搜索成功!搜索还包含了已嵌入日语的字段。在大多数实际用例中,不需要将这些文本包含在响应中。在这类情况下,可以使用 _source 参数或其他方法,从响应中排除这些信息(或采取其他操作)。

为了对搜索排名进行微调,可以使用已发布的倒数排序融合 (RRF) 功能,巧妙地将矢量搜索和标准关键字搜索的结果融合在一起。请务必也了解一下这项功能。

关于如何使用矢量搜索来实现语义搜索的讨论先到这里。虽然设置这项功能可能需要完成比标准搜索更多的工作,而且还可能会遇到一些独特的机器学习词汇,但在完成设置阶段后,您就能够以几乎与正常搜索相同的方式执行搜索,所以请尝试一下。

文本分类(情感分析)

至此,我们已经了解了可以使用 kNN 进行日语的矢量搜索,下面我们以相同的方式来看看如何使用其他 NLP 任务。

文本分类是一项将文本输入归入某种类别的任务。在本例中,我们将使用在 Hugging Face 上找到的情感分析模型,判断日语文本输入是带有积极情感还是消极情感 (koheiduck/bert-japanese-finetuned-sentiment)。查看这个模型的 tokenizer_config.json,可以看到它也使用 mecab 作为 word_tokenizer_type,因此可以与 Elasticsearch 的 bert_ja 一起使用。

与前面的操作一样,使用 Eland 将这个模型导入 Elasticsearch 中。

!eland_import_hub_model \
--url "https://your.elasticserach" \
--es-api-key "your_api_key" \
--hub-model-id koheiduck/bert-japanese-finetuned-sentiment \
--task-type text_classification \
--start

成功导入模型后,可以在 Kibana 的“Machine Learning”>“Model Management”(模型管理)>“Trained Models”(已训练模型)下找到它。

推理配置 2

在本例中,也是单击“Actions”(操作)菜单中的“Test model”(测试模型)。

操作 2

与之前一样,这将显示一个用于测试的对话框。在此处输入要分类的文本,然后文本将被分类为“积极”、“中立”或“消极”。作为测试,我们输入:“我很高兴现在能够使用 Elasticsearch 执行 NLP 任务。” 如下所示,这条输入生成的结果是 99.2% 为积极情感。

模型 2

下面是通过 API 执行的相同过程。

POST _ml/trained_models/koheiduck__bert-japanese-finetuned-sentiment/_infer
{
  "docs": [{"text_field": "ElasticsearchでNLPのタスクが実行できるようになって嬉しい。"}],
  "inference_config": {
    "text_classification": {
      "num_top_classes": 3
    }
  }
}

响应如下:

{
  "inference_results": [
    {
      "predicted_value": "POSITIVE",
      "top_classes": [
        {
          "class_name": "POSITIVE",
          "class_probability": 0.9921651090124636,
          "class_score": 0.9921651090124636
        },
        {
          "class_name": "NEUTRAL",
          "class_probability": 0.006682728902566756,
          "class_score": 0.006682728902566756
        },
        {
          "class_name": "NEGATIVE",
          "class_probability": 0.0011521620849697567,
          "class_score": 0.0011521620849697567
        }
      ],
      "prediction_probability": 0.9921651090124636
    }
  ]
}

由于这个过程也可以使用推理处理器执行,因此在对日语文本建立索引之前,可附加上这些分析结果。例如,将这个过程应用于特定产品评论的文本,有助于将用户对这些产品的评级转换为数值。

反馈

截至 Elasticsearch 8.9 版发布之日,日语语言 NLP 模型支持仍处于技术预览阶段。如果您发现任何错误,或者在使用非 BERT 算法或非 MeCab 分词器等方面需要支持,请联系 Elastic®

GitHub Issues 是向 Elastic 发送反馈最便捷的方式。在 elastic/elasticsearch 存储库中的“Issues”(问题)下,添加 :ml 标签并提出您的请求。然后,相关的团队将进行调查。

作为 Elastic 的咨询架构师(因此不属于开发团队),我通过 GitHub,以外部贡献者的身份发送了一个说明修改内容的拉取请求,以增加对日语语言的支持。如果您是开发人员,并希望在具体用例中请求添加一项功能,也可以像我一样试一试。

结论

目前,Elastic 正在投入大量资源,利用 Machine Learning 在搜索功能中实现 NLP 功能,而且有越来越多的这类功能可以在 Elasticsearch 中执行。不过,大多数功能都首先以英语支持为主,对其他语言的支持有限。

不管怎样,我们非常高兴地看到已决定为日语提供支持。我们希望这些新的 Elasticsearch 功能可帮助您实现更有意义的搜索。

本博文所描述的任何特性或功能的发布及上市时间均由 Elastic 自行决定。当前尚未发布的任何特性或功能可能无法按时提供或根本不会提供。

在本博文中,我们可能使用或提到了第三方生成式 AI 工具,这些工具由其各自所有者拥有和运营。Elastic 对第三方工具没有任何控制权,对其内容、操作或使用不承担任何责任或义务,对您使用此类工具可能造成的任何损失或损害也不承担任何责任或义务。在 AI 工具中使用个人、敏感或机密信息时,请务必谨慎。您提交的任何数据都可能用于 AI 训练或其他目的。Elastic 不保证您所提供信息的安全性或保密性。在使用任何生成式 AI 工具之前,您都应自行熟悉其隐私惯例和使用条款。

Elastic、Elasticsearch、ESRE、Elasticsearch Relevance Engine 及相关标志为 Elasticsearch N.V. 在美国和其他国家/地区的商标、徽标或注册商标。所有其他公司和产品名称均为其相应所有者的商标、徽标或注册商标。