Elasticsearch:多语言语义搜索

2023年 10月 7日 31.3k 0

在此示例中,我们将使用多语言嵌入模型 multilingual-e5-base 对混合语言文档的 toy 数据集执行搜索。 使用这个模型,我们可以通过两种方式进行搜索:

  • 跨语言,例如使用德语查询来查找英语文档
  • 在非英语语言中,例如使用德语查询来查找德语文档

虽然此示例仅使用密集检索,但也可以将密集检索和传统词汇检索与混合搜索相结合。 有关词法多语言搜索的更多信息,请参阅博客文章 “在 Elasticsearch 中使用语言识别进行多语言搜索”。

使用的数据集包含 MIRACL 数据集的维基百科段落片段。

安装

Elasticsearch 及 Kibana

如果你还没有安装好自己的 Elasticsearch 及 Kibana,请参考如下的链接来进行安装:

  • 如何在 Linux,MacOS 及 Windows 上进行安装 Elasticsearch

  • Kibana:如何在 Linux,MacOS 及 Windows 上安装 Elastic 栈中的 Kibana

在安装的时候,我们可以选择 Elastic Stack 8.x 的安装指南来进行安装。在本博文中,我将使用最新的 Elastic Stack 8.10 来进行展示。

在安装 Elasticsearch 的过程中,我们需要记下如下的信息:

Python 安装包

在本演示中,我们将使用 Python 来进行展示。我们需要安装访问 Elasticsearch 相应的安装包 elasticsearch:



1.  pip install elasticsearch
2.  pip install sentence_transformers
3.  pip install torch


我们将使用 Jupyter Notebook 来进行展示。



1.  $ pwd
2.  /Users/liuxg/python/elser
3.  $ jupyter notebook


创建应用并演示

初始化及连接 Elasticsearch



1.  import getpass
2.  import textwrap
3.  import torch

5.  from elasticsearch import Elasticsearch
6.  from sentence_transformers import SentenceTransformer

8.  device = "cuda" if torch.cuda.is_available() else "cpu"

10.  model = SentenceTransformer("intfloat/multilingual-e5-base", device=device)


然后我们创建一个客户端对象来实例化 Elasticsearch 类的实例。



1.  ELASTCSEARCH_CERT_PATH = "/Users/liuxg/elastic/elasticsearch-8.10.0/config/certs/http_ca.crt"

3.  client = Elasticsearch(  ['https://localhost:9200'],
4.      basic_auth = ('elastic', 'vXDWYtL*my3vnKY9zCfL'),
5.      ca_certs = ELASTCSEARCH_CERT_PATH,
6.      verify_certs = True)
7.  print(client.info())


使用所需的映射创建 Elasticsearch 索引

我们需要添加一个字段来支持密集向量存储和搜索。 注意下面的 passage_embedding 字段,它用于存储 passage字段的密集向量表示。



1.  # Define the mapping
2.  mapping = {
3.      "mappings": {
4.          "properties": {
5.              "language": {"type": "keyword"},
6.              "id": {"type": "keyword"},
7.              "title": {"type": "text"},
8.              "passage": {"type": "text"},
9.              "passage_embedding": {
10.                  "type": "dense_vector",
11.                  "dims": 768,
12.                  "index": "true",
13.                  "similarity": "cosine"
14.              }
15.          }
16.      }
17.  }

19.  # Create the index (deleting any existing index)
20.  client.indices.delete(index="articles", ignore_unavailable=True)
21.  client.indices.create(index="articles", body=mapping)


摄入数据

让我们索引一些数据。 请注意,我们使用 sentence transformer 模型嵌入 passage 字段。 建立索引后,你将看到文档包含一个 passage_embedding 字段(“type”:“dense_vector”),其中包含浮点值向量。 这就是 passage 字段在向量空间中的嵌入。 我们将使用该字段使用 kNN 执行语义搜索。



1.  articles = [
2.      {
3.          "language": "en",
4.          "id": "1643584#0",
5.          "title": "Bloor Street",
6.          "passage": """Bloor Street is a major east–west residential and commercial thoroughfare in Toronto, Ontario, Canada. Bloor Street runs from the Prince Edward Viaduct, which spans the Don River Valley, westward into Mississauga where it ends at Central Parkway. East of the viaduct, Danforth Avenue continues along the same right-of-way. The street, approximately long, contains a significant cross-sample of Toronto's ethnic communities. It is also home to Toronto's famous shopping street, the Mink Mile.""",
7.      },
8.      {
9.          "language": "en",
10.          "id": "2190499#0",
11.          "title": "Elphinstone College",
12.          "passage": """Elphinstone College is an institution of higher education affiliated to the University of Mumbai. Established in 1856, it is one of the oldest colleges of the University of Mumbai. It is reputed for producing luminaries like Bal Gangadhar Tilak, Bhim Rao Ambedkar, Virchand Gandhi, Badruddin Tyabji, Pherozshah Mehta, Kashinath Trimbak Telang, Jamsetji Tata and for illustrious professors that includes Dadabhai Naoroji. It is further observed for having played a key role in spread of Western education in the Bombay Presidency.""",
13.      },
14.      {
15.          "language": "en",
16.          "id": "8881#0",
17.          "title": "Doctor (title)",
18.          "passage": """Doctor is an academic title that originates from the Latin word of the same spelling and meaning. The word is originally an agentive noun of the Latin verb "" 'to teach'. It has been used as an academic title in Europe since the 13th century, when the first Doctorates were awarded at the University of Bologna and the University of Paris. Having become established in European universities, this usage spread around the world. Contracted "Dr" or "Dr.", it is used as a designation for a person who has obtained a Doctorate (e.g. PhD). In many parts of the world it is also used by medical practitioners, regardless of whether or not they hold a doctoral-level degree.""",
19.      },
20.      {
21.          "language": "de",
22.          "id": "9002#0",
23.          "title": "Gesundheits- und Krankenpflege",
24.          "passage": """Die Gesundheits- und Krankenpflege als Berufsfeld umfasst die Versorgung und Betreuung von Menschen aller Altersgruppen, insbesondere kranke, behinderte und sterbende Erwachsene. Die Gesundheits- und Kinderkrankenpflege hat ihren Schwerpunkt in der Versorgung von Kindern und Jugendlichen. In beiden Fachrichtungen gehört die Verhütung von Krankheiten und Gesunderhaltung zum Aufgabengebiet der professionellen Pflege.""",
25.      },
26.      {
27.          "language": "de",
28.          "id": "7769762#0",
29.          "title": "Tourismusregion (Österreich)",
30.          "passage": """Unter Tourismusregion versteht man in Österreich die in den Landestourismusgesetzen verankerten Tourismusverbände mehrerer Gemeinden, im weiteren Sinne aller Gebietskörperschaften.""",
31.      },
32.      {
33.          "language": "de",
34.          "id": "2270104#0",
35.          "title": "London Wall",
36.          "passage": """London Wall ist die strategische Stadtmauer, die die Römer um Londinium gebaut haben, um die Stadt zu schützen, die über den wichtigen Hafen an der Themse verfügte. Bis ins späte Mittelalter hinein bildete diese Stadtmauer die Grenzen von London. Heute ist "London Wall" auch der Name einer Straße, die an einem noch bestehenden Abschnitt der Stadtmauer verläuft.""",
37.      },
38.      {
39.          "language": "de",
40.          "id": "2270104#1",
41.          "title": "London Wall",
42.          "passage": """Die Mauer wurde Ende des zweiten oder Anfang des dritten Jahrhunderts erbaut, wahrscheinlich zwischen 190 und 225, vermutlich zwischen 200 und 220. Sie entstand somit etwa achtzig Jahre nach dem im Jahr 120 erfolgten Bau der Festung, deren nördliche und westliche Mauern verstärkt und in der Höhe verdoppelt wurden, um einen Teil der neuen Stadtmauer zu bilden. Die Anlage wurde zumindest bis zum Ende des vierten Jahrhunderts weiter ausgebaut. Sie zählt zu den letzten großen Bauprojekten der Römer vor deren Rückzug aus Britannien im Jahr 410.""",
43.      },
44.  ]


我们的数据集是一个 Python 列表,其中包含两种语言的维基百科文章中的段落词典。 我们将使用 helpers.bulk 方法批量索引我们的文档。

以下代码迭代文章并创建要执行的操作列表。 每个操作都是一个字典,其中包含对 Elasticsearch 索引的 “index” 操作。 该 passage 使用我们选择的模型进行编码,并将编码向量添加到文章文档中。 请注意,E5 模型要求使用前缀指令 “passage:” 来告诉模型要嵌入 passage。 在查询方面,查询字符串将以 “query:” 为前缀。 然后,文章文档将添加到操作列表中。

最后,我们调用 bulk 方法,指定索引名称和操作列表。



1.  actions = []
2.  for article in articles:
3.      actions.append({"index": {"_index": "articles"}})
4.      passage = article["passage"]
5.      passageEmbedding = model.encode(f"passage: {passage}").tolist()
6.      article["passage_embedding"] = passageEmbedding
7.      actions.append(article)

9.  client.bulk(index="articles", operations=actions)


多语言语义搜索

接下来,我们将使用两种查询进行搜索:

  • 以英语查询以查找任何语言的文档
  • 以德语查询仅查找德语文档(使用过滤器),以显示模型在非英语语言中的功能

再次注意,查询以 “query:” 为前缀,模型需要它来正确编码查询。

对于不熟悉德语的人来说,可以快速翻译一下:

  • "health" -> "Gesundheit"
  • "wall" -> "Mauer"


1.  def pretty_response(response):
2.      for hit in response["hits"]["hits"]:
3.          score = hit["_score"]
4.          language = hit["_source"]["language"]
5.          id = hit["_source"]["id"]
6.          title = hit["_source"]["title"]
7.          passage = hit["_source"]["passage"]
8.          print()
9.          print(f"ID: {id}")
10.          print(f"Language: {language}")
11.          print(f"Title: {title}")
12.          print(f"Passage: {textwrap.fill(passage, 120)}")
13.          print(f"Score: {score}")




1.  def query(q, language=None):
2.      knn = {
3.          "field": "passage_embedding",
4.          "query_vector" : model.encode(f"query: {q}").tolist(),
5.          "k": 2,
6.          "num_candidates": 5
7.      }

9.      if language:
10.          knn["filter"] = {
11.              "term": {
12.                  "language": language,
13.              }
14.          }

16.      return client.search(index="articles", knn=knn)


pretty_response(query("health"))

请注意,在上面的结果中,我们看到有关医疗保健的文档,即使是德语,也与查询 “health” 匹配得更好,而英语文档则没有具体谈论健康,而是更广泛地谈论医生。 这就是多语言嵌入的力量,它可以跨语言嵌入含义。

pretty_response(query("wall", language="de"))

在上面,我们使用过滤器在德语的文档里搜索英文单词 wall。

pretty_response(query("Mauer", language="de"))

在上面,我们在德语文档里搜索德语单词 “Mauer”。我们可以看到 “London Wall” 文档也被正确搜索到了。

有关 jupyter 的文件可以在地址下载。

相关文章

JavaScript2024新功能:Object.groupBy、正则表达式v标志
PHP trim 函数对多字节字符的使用和限制
新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
为React 19做准备:WordPress 6.6用户指南
如何删除WordPress中的所有评论

发布评论