博客

如何使用我们的同义词 API 自动生成和上传同义词

了解如何使用 LLM 自动识别和生成同义词,从而以编程方式将术语加载到 Elasticsearch 同义词 API 中。

提高搜索结果的质量对于提供高效的用户体验至关重要。优化搜索的方法之一是通过同义词自动扩展查询词。这样就可以更广泛地解释查询,涵盖各种语言,从而改进结果匹配。

本博客将探讨如何使用大型语言模型 (LLM) 自动识别和生成同义词,并允许以编程方式将这些术语加载到 Elasticsearch 的同义词 API 中。

何时使用同义词?

与矢量搜索相比,使用同义词是一种更快、更具成本效益的解决方案。它的实现较为简单,因为它不需要深厚的嵌入知识,也不需要复杂的矢量摄取过程。

此外,由于矢量搜索需要更大的存储容量和内存来嵌入索引和检索,因此资源消耗较低。

另一个重要方面是搜索区域化。有了同义词,就可以根据当地语言和习俗调整术语。这在嵌入式可能无法匹配区域表达或特定国家术语的情况下非常有用。例如,有些单词或缩略语在不同地区可能有不同的含义,但当地用户自然会将其视为同义词。在巴西,这种情况非常普遍。"Abacaxi" 和"ananás" 是同一种水果(菠萝),但在东北部的一些地区,第二个术语更常用。同样,东南部著名的"pão francês" 在东北部可能被称为"pão careca" 。

如何使用 LLM 生成同义词?

为了自动获取同义词,我们可以使用 LLM,它可以分析术语的上下文,并建议适当的变体。这种方法可以动态扩展同义词,确保搜索范围更广、更准确,而无需依赖固定词典。

在本演示中,我们将使用 LLM 生成电子商务产品的同义词。由于查询词的变化,许多搜索结果很少或没有结果。有了同义词,我们就可以解决这个问题。例如,搜索"智能手机" 可以涵盖不同型号的手机,确保用户找到所需的产品。

准备工作

在开始之前,我们需要设置环境并定义所需的依赖关系。我们将使用 Elastic 提供的解决方案,在 Docker 中本地运行 Elasticsearch 和 Kibana。代码将使用 Python 3.9.6 版本编写,依赖关系如下:

pip install openai==1.59.8 elasticsearch==8.15.1

创建产品索引

最初,我们将创建一个不支持同义词的产品索引。这样我们就可以验证查询,然后将其与包含同义词的索引进行比较。

为了创建索引,我们在 Kibana DevTools 中使用以下命令批量加载产品数据集:

POST _bulk
{"index": {"_index": "products", "_id": 10001}}
{"category": "Electronics", "name": "iPhone 14 Pro"}
{"index": {"_index": "products", "_id": 10007}}
{"category": "Electronics", "name": "MacBook Pro 16-inch"}
{"index": {"_index": "products", "_id": 10013}}
{"category": "Electronics", "name": "Samsung Galaxy Tab S8"}
{"index": {"_index": "products", "_id": 10037}}
{"category": "Electronics", "name": "Apple Watch Series 8"}
{"index": {"_index": "products", "_id": 10049}}
{"category": "Electronics", "name": "Kindle Paperwhite"}
{"index": {"_index": "products", "_id": 10067}}
{"category": "Electronics", "name": "Samsung QLED 4K TV"}
{"index": {"_index": "products", "_id": 10073}}
{"category": "Electronics", "name": "HP Spectre x360 Laptop"}
{"index": {"_index": "products", "_id": 10079}}
{"category": "Electronics", "name": "Apple AirPods Pro"}
{"index": {"_index": "products", "_id": 10115}}
{"category": "Electronics", "name": "Amazon Echo Show 10"}
{"index": {"_index": "products", "_id": 10121}}
{"category": "Electronics", "name": "Apple iPad Air"}
{"index": {"_index": "products", "_id": 10127}}
{"category": "Electronics", "name": "Apple AirPods Max"}
{"index": {"_index": "products", "_id": 10151}}
{"category": "Electronics", "name": "Sony WH-1000XM4 Headphones"}
{"index": {"_index": "products", "_id": 10157}}
{"category": "Electronics", "name": "Google Pixel 6 Pro"}
{"index": {"_index": "products", "_id": 10163}}
{"category": "Electronics", "name": "Apple MacBook Air"}
{"index": {"_index": "products", "_id": 10181}}
{"category": "Electronics", "name": "Google Pixelbook Go"}
{"index": {"_index": "products", "_id": 10187}}
{"category": "Electronics", "name": "Sonos Beam Soundbar"}
{"index": {"_index": "products", "_id": 10199}}
{"category": "Electronics", "name": "Apple TV 4K"}
{"index": {"_index": "products", "_id": 10205}}
{"category": "Electronics", "name": "Samsung Galaxy Watch 4"}
{"index": {"_index": "products", "_id": 10211}}
{"category": "Electronics", "name": "Apple MacBook Pro 16-inch"}
{"index": {"_index": "products", "_id": 10223}}
{"category": "Electronics", "name": "Amazon Echo Dot (4th Gen)"}

用 LLM 生成同义词

在这一步中,我们将使用 LLM 来动态生成同义词。为此,我们将整合 OpenAI 应用程序接口,定义适当的模型和提示。LLM 将接收产品类别和名称,确保同义词与上下文相关。

import json
import logging

from openai import OpenAI

def call_gpt(prompt, model):
    try:
        logging.info("generate synonyms by llm...")
        response = client.chat.completions.create(
            model=model,
            messages=[{"role": "user", "content": prompt}],
            temperature=0.7,
            max_tokens=1000
        )
        content = response.choices[0].message.content.strip()
        return content
    except Exception as e:
        logging.error(f"Failed to use model: {e}")
        return None

def generate_synonyms(category, products):
   synonyms = {}

   for product in products:
       prompt = f"You are an expert in generating synonyms for products. Based on the category and product name provided, generate synonyms or related terms. Follow these rules:\n"
       prompt += "1. **Format**: The first word should be the main item (part of the product name, excluding the brand), followed by up to 3 synonyms separated by commas.\n"
       prompt += "2. **Exclude the brand**: Do not include the brand name in the synonyms.\n"
       prompt += "3. **Maximum synonyms**: Generate a maximum of 3 synonyms per product.\n\n"
       prompt += f"The category is: **{category}**, and the product is: **{product}**. Return only the synonyms in the requested format, without additional explanations."

       response = call_gpt(prompt, "gpt-4o")
       synonyms[product] = response

   return synonyms

从创建的产品索引中,我们将检索"Electronics" 类别中的所有项目,并将其名称发送到 LLM。预期输出结果如下

{
  "iPhone 14 Pro": ["iPhone", "smartphone", "mobile", "handset"],
  "MacBook Pro 16-inch": ["MacBook", "Laptop", "Notebook", "Ultrabook"],
  "Samsung Galaxy Tab S8": ["Tab", "Tablet", "Slate", "Pad"],
  "Bose QuietComfort 35 Headphones": ["Headphones", "earphones", "earbuds", "headset"]
}

有了生成的同义词,我们就可以使用同义词 API 将其注册到 Elasticsearch 中。

使用同义词 API 管理同义词

同义词 API 提供了在系统内直接管理同义词集的有效方法。每个同义词集都由同义词规则组成,其中一组词在搜索中被视为等同词。

创建同义词集示例

PUT _synonyms/my-synonyms-set
{
  "synonyms_set": [
    {
      "id": "rule-1",
      "synonyms": "hello, hi"
    },
    {
      "synonyms": "bye, goodbye"
    }
  ]
}

这样就创建了一个名为"my-synonyms-set," 的集合,其中"hello" 和"hi" 被视为等同词,"bye" 和"goodbye 也被视为等同词。"

为产品目录创建同义词

下面是建立同义词集并将其插入 Elasticsearch 的方法。同义词规则是根据 LLM 建议的同义词映射生成的。每条规则都有一个 ID(与 slug 格式的产品名称相对应)和 LLM 计算出的同义词列表。

import json
import logging

from elasticsearch import Elasticsearch
from slugify import slugify

es = Elasticsearch(
    "http://localhost:9200",
    api_key="your_api_key"
)

def mount_synonyms(results):
   synonyms_set = [{"id": slugify(product), "synonyms": synonyms} for product, synonyms in
                   results.items()]

   try:
       response = es.synonyms.put_synonym(id="products-synonyms-set",
                                                 synonyms_set=synonyms_set)

       logging.info(json.dumps(response.body, indent=4))
       return response.body
   except Exception as e:
       logging.error(f"Error create synonyms: {str(e)}")
       return None

下面是创建同义词集的请求有效载荷:

{
   "synonyms_set":[
      {
         "id": "iphone-14-pro",
         "synonyms": "iPhone, smartphone, mobile, handset"
      },
      {
         "id": "macbook-pro-16-inch",
         "synonyms": "MacBook, Laptop, Notebook, Computer"
      },
      {
         "id": "samsung-galaxy-tab-s8",
         "synonyms": "Tablet, Slate, Pad, Device"
      },
      {
         "id": "garmin-forerunner-945",
         "synonyms": "Forerunner, smartwatch, fitness watch, GPS watch"
      },
      {
         "id": "bose-quietcomfort-35-headphones",
         "synonyms": "Headphones, Earphones, Headset, Cans"
      }
   ]
}

在集群中创建同义词集后,我们就可以进行下一步,即使用定义的同义词集创建支持同义词的新索引。

下面是完整的 Python 代码,其中包含 LLM 生成的同义词和同义词 API 定义的同义词集创建:

import json
import logging

from elasticsearch import Elasticsearch
from openai import OpenAI
from slugify import slugify

logging.basicConfig(level=logging.INFO)

client = OpenAI(
   api_key="your-key",
)

es = Elasticsearch(
    "http://localhost:9200",
    api_key="your_api_key"
)


def call_gpt(prompt, model):
   try:
       logging.info("generate synonyms by llm...")
       response = client.chat.completions.create(
           model=model,
           messages=[{"role": "user", "content": prompt}],
           temperature=0.7,
           max_tokens=1000
       )
       content = response.choices[0].message.content.strip()
       return content
   except Exception as e:
       logging.error(f"Failed to use model: {e}")
       return None


def generate_synonyms(category, products):
   synonyms = {}

   for product in products:
       prompt = f"You are an expert in generating synonyms for products. Based on the category and product name provided, generate synonyms or related terms. Follow these rules:\n"
       prompt += "1. **Format**: The first word should be the main item (part of the product name, excluding the brand), followed by up to 3 synonyms separated by commas.\n"
       prompt += "2. **Exclude the brand**: Do not include the brand name in the synonyms.\n"
       prompt += "3. **Maximum synonyms**: Generate a maximum of 3 synonyms per product.\n\n"
       prompt += f"The category is: **{category}**, and the product is: **{product}**. Return only the synonyms in the requested format, without additional explanations."

       response = call_gpt(prompt, "gpt-4o")
       synonyms[product] = response

   return synonyms


def get_products(category):
   query = {
       "size": 50,
       "_source": ["name"],
       "query": {
           "bool": {
               "filter": [
                   {
                       "term": {
                           "category.keyword": category
                       }
                   }
               ]
           }
       }
   }
   response = es.search(index="products", body=query)

   if response["hits"]["total"]["value"] > 0:
       product_names = [hit["_source"]["name"] for hit in response["hits"]["hits"]]
       return product_names
   else:
       return []


def mount_synonyms(results):
   synonyms_set = [{"id": slugify(product), "synonyms": synonyms} for product, synonyms in
                   results.items()]

   try:
       es_client = get_client_es()
       response = es_client.synonyms.put_synonym(id="products-synonyms-set",
                                                 synonyms_set=synonyms_set)

       logging.info(json.dumps(response.body, indent=4))
       return response.body
   except Exception as e:
       logging.error(f"Erro update synonyms: {str(e)}")
       return None


if __name__ == '__main__':
   category = "Electronics"
   products = get_products("Electronics")
   llm_synonyms = generate_synonyms(category, products)
   mount_synonyms(llm_synonyms)

创建支持同义词的索引

将创建一个新索引,对products 索引中的所有数据进行重新索引。该索引将使用synonyms_filter ,它应用了之前创建的products-synonyms-set

以下是配置为使用同义词的索引映射:

PUT products_02
{
  "settings": {
    "analysis": {
      "filter": {
        "synonyms_filter": {
          "type": "synonym",
          "synonyms_set": "products-synonyms-set",
          "updateable": true
        }
      },
      "analyzer": {
        "synonyms_analyzer": {
          "type": "custom",
          "tokenizer": "standard",
          "filter": [
            "lowercase",
            "synonyms_filter"
          ]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "ID": {
        "type": "long"
      },
      "category": {
        "type": "keyword"
      },
      "name": {
        "type": "text",
        "analyzer": "standard",
        "search_analyzer": "synonyms_analyzer"
      }
    }
  }
}

重新索引products 索引

现在,我们将使用Reindex APIproducts 索引中的数据迁移到包含同义词支持的新products_02 索引中。在 Kibana DevTools 中执行了以下代码:

POST _reindex
{
  "source": {
    "index": "products"
  },
  "dest": {
    "index": "products_02"
  }
}

迁移后,products_02 索引将被填充,并可使用配置的同义词集验证搜索。

使用同义词验证搜索

让我们比较一下两个索引的搜索结果。我们将在两个索引上执行相同的查询,并验证是否使用同义词来检索结果。

products 索引中搜索(不含同义词)

我们将使用 Kibana 执行搜索并分析结果。在分析> 发现菜单中,我们将创建一个数据视图,以可视化我们创建的索引中的数据。

在 Discovery 中,单击数据视图并定义名称和索引模式。对于"产品" 索引,我们将使用"产品"模式。然后,我们将重复该过程,使用"products_02"模式为"products_02" 索引创建一个新的数据视图。

配置好数据视图后,我们就可以返回 Analytics> Discovery 并开始验证。

在这里,选择 DataView 产品并对"tablet" 一词进行搜索后,我们没有得到任何结果,尽管我们知道有"Kindle Paperwhite" 和"Apple iPad Air" 这样的产品。

products_02 索引中搜索(支持同义词)

在支持同义词的"products_synonyms" 数据视图上执行相同查询时,产品被成功检索。这表明配置的同义词集工作正常,确保搜索词的不同变体都能返回预期结果。

我们可以直接在 Kibana DevTools 中运行相同的查询来获得相同的结果。只需使用 Elasticsearch Search API 搜索 products_02 索引即可:

结论

在 Elasticsearch 中使用同义词提高了产品目录搜索的准确性和覆盖范围。与众不同的关键在于使用了LLM,它可以根据上下文自动生成同义词,无需预定义清单。该模型分析了产品名称和类别,确保与电子商务相关的同义词。

此外,同义词 API简化了词典管理,允许动态修改同义词集。有了这种方法,搜索变得更加灵活,更能适应不同的用户查询模式。

这一过程可以通过新数据和模型调整不断改进,确保提供越来越高效的研究体验。

参考资料

在本地运行 Elasticsearch

https://www.elastic.co/guide/en/elasticsearch/reference/current/run-elasticsearch-locally.html

同义词应用程序接口

https://www.elastic.co/guide/en/elasticsearch/reference/current/synonyms-apis.html

相关内容

使用判断列表评估搜索查询的相关性

Jhon Guzmán

使用 ML 生成过滤器和切面

Andre Luiz

你懂的,语境--第一部分:混合搜索和语境工程的演变

Woody Walton

扩展 Elasticsearch 中的后期交互模型 — 第 2 部分

Peter Straßer

混合搜索不头疼:用检索器简化混合搜索

Mike Pellegrini

准备好打造最先进的搜索体验了吗?

足够先进的搜索不是一个人的努力就能实现的。Elasticsearch 由数据科学家、ML 操作员、工程师以及更多和您一样对搜索充满热情的人提供支持。让我们联系起来,共同打造神奇的搜索体验,让您获得想要的结果。

亲自试用