<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
  <channel>
    <title><![CDATA[Python - Elasticsearch Labs]]></title>
    <description><![CDATA[Articles and tutorials from the Search team at Elastic]]></description>
    <copyright><![CDATA[© 2026. Elasticsearch B.V. All Rights Reserved]]></copyright>
    <image>
      <title><![CDATA[Python - Elasticsearch Labs]]></title>
      <url>https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1121c0bf0e8a6e65/6a88da6340a1841030ef456f/search-labs-thumbnail.png</url>
      <link>https://www.elastic.co/de/search-labs/blog/category/python-programming</link>
    </image>
    <link>https://www.elastic.co/de/search-labs/blog/category/python-programming</link>
    <atom:link href="https://www.elastic.co/de/search-labs/rss/category/python-programming.xml" rel="self" type="application/rss+xml"/>
    <language><![CDATA[de]]></language>
    <lastBuildDate>Mon, 28 Sep 2026 07:33:53 GMT</lastBuildDate>
  <item>
    <title><![CDATA[Multimodale Suche nach Berggipfeln mit Elasticsearch und SigLIP-2 ]]></title>
    <description><![CDATA[Lernen Sie, wie Sie die multimodale Suche von Text zu Bild und von Bild zu Bild mithilfe von SigLIP-2-Einbettungen und der Elasticsearch kNN-Vektorsuche implementieren. Projektschwerpunkt: Auffinden von Fotos des Gipfels des Mount Ama Dablam während einer Everest-Trekkingtour.]]></description>
    <content:encoded><![CDATA[<p>Wollten Sie schon immer einmal Ihr Fotoalbum nach Bedeutung durchsuchen? Versuchen Sie Suchanfragen wie „Zeig mir meine Bilder, auf denen ich eine blaue Jacke trage und auf einer Bank sitze“, „Zeig mir Bilder vom Mount Everest“ oder „Sake und Sushi“. Schnapp dir eine Tasse Kaffee (oder dein Lieblingsgetränk) und lies weiter. In diesem Blog zeigen wir Ihnen, wie Sie eine multimodale hybride Suchanwendung erstellen. Multimodal bedeutet, dass die App verschiedene Arten von Eingaben verstehen und durchsuchen kann – Text, Bilder und Audio – und nicht nur Wörter. Hybrid bedeutet, dass Techniken wie Keyword-Matching, kNN-Vektorsuche und Geofencing kombiniert werden, um präzisere Ergebnisse zu liefern.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdfa1ec1ccd450e94/6a17da751d1b8308ee93e344/0ec6bbb45013846b59ee00d2bf73ee2182ee7392-1920x1080.gif" alt="Bibliothek mit Fotos verschiedener Berggipfel von der Mount-Everest-Wanderung." /><p>Um dies zu erreichen, verwenden wir Googles SigLIP-2, um Vektoreinbettungen sowohl für Bilder als auch für Texte zu generieren und diese in der Elasticsearch-Vektordatenbank zu speichern. Zum Zeitpunkt der Abfrage wandeln wir die Sucheingabe, Text oder Bild, in Einbettungen um und führen schnelle kNN-Vektorsuchen durch, um Ergebnisse abzurufen. Diese Konfiguration ermöglicht eine effiziente Text-zu-Bild- und Bild-zu-Bild-Suche. Eine Streamlit-Benutzeroberfläche erweckt dieses Projekt zum Leben, indem sie uns ein Frontend zur Verfügung stellt, mit dem wir nicht nur textbasiert nach passenden Fotos aus dem Album suchen und diese anzeigen können, sondern auch den Berggipfel auf dem hochgeladenen Bild identifizieren und weitere Fotos dieses Berges im Fotoalbum anzeigen können.
Wir beschreiben außerdem die Schritte, die wir zur Verbesserung der Suchgenauigkeit unternommen haben, und geben praktische Tipps und Tricks. Zur weiteren Erkundung stellen wir ein <a href="https://github.com/navneet83/multimodal-mountain-peak-search">GitHub-Repository</a> und ein <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/notebooks/multimodal_mountain_peak_search.ipynb">Colab-Notebook</a> zur Verfügung.</p><h2>Wie alles begann</h2><p>Dieser Blogbeitrag entstand auf Anregung eines 10-Jährigen, der mich bat, ihm alle Bilder des Mount Ama Dablam von meiner Everest-Basislager-Trekkingtour zu zeigen. Während wir das Fotoalbum durchsahen, wurde ich auch gebeten, mehrere andere Berggipfel zu identifizieren, von denen ich einige nicht benennen konnte.</p><p>Das brachte mich auf die Idee, dass dies ein unterhaltsames Computer-Vision-Projekt werden könnte. Was wir erreichen wollten:</p><ul><li><p>Finde Bilder eines Berggipfels anhand seines Namens</p></li><li><p>Errate den Namen des Berggipfels anhand eines Bildes und finde ähnliche Gipfel im Fotoalbum.</p></li><li><p>Konzeptabfragen zum Laufen bringen (<em>Person</em>, <em>Fluss</em>, <em>Gebetsfahnen</em> <em>usw.)</em></p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf82df9d7005fc3fe/6a17da78abe0f2e77bdfe8b9/e9d0d720a9b565d5b749bdc915068852d4f157ad-1200x1600.png" alt="Mount Ama Dablam " /><h2>Zusammenstellung des Dreamteams: SigLIP-2, Elasticsearch &amp; Streamlit</h2><p>Es wurde schnell klar, dass wir, um dies zu ermöglichen, sowohl den Text („Ama Dablam“) als auch die Bilder (Fotos aus meinem Album) in Vektoren umwandeln müssten, die sinnvoll verglichen werden können, d. h. im selben Vektorraum. Sobald wir das getan haben, besteht die Suche nur noch darin, „die nächstgelegenen Nachbarn zu finden“.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5f80b69a9d5bd28a/6a17da7a4b055ddd1243209e/20e6f8b7d4fa48414f407ec200adbe00ee28d517-1536x1024.png" alt="SigLIP-2, Elasticsearch &amp; Streamlit – ein Dreamteam." /><p>Um Bildeinbettungen zu generieren, verwenden wir einen mehrsprachigen<a href="https://huggingface.co/blog/vlms-2025"> Bild-Sprach-Encoder</a>, sodass ein Foto eines Berges und ein Ausdruck wie „Ama Dablam“ im selben Vektorraum landen.</p><p><a href="https://huggingface.co/blog/siglip2"><strong>SigLIP-2</strong></a>, das kürzlich von Google veröffentlicht wurde, passt hier gut hinein. Es kann Einbettungen ohne aufgabenspezifisches Training generieren (eine <strong>Zero-Shot</strong> -Einstellung) und eignet sich hervorragend für unseren Anwendungsfall: unbeschriftete Fotos und Gipfel mit unterschiedlichen Namen und Sprachen. Da es für die Zuordnung von Text zu Bild trainiert wurde, werden ein Bergbild von der Wanderung und eine kurze Texteingabeaufforderung als Einbettungen sehr ähnlich dargestellt, selbst wenn die Abfragesprache oder die Rechtschreibung variiert.</p><p>SigLIP-2 bietet ein gutes Verhältnis von Qualität zu Geschwindigkeit, unterstützt mehrere Eingangsauflösungen und läuft sowohl auf der CPU als auch auf der GPU. Der SigLIP-2 ist im Vergleich zu Vorgängermodellen wie dem ursprünglichen CLIP robuster für Außenaufnahmen. Während unserer Tests lieferte SigLIP-2 durchweg zuverlässige Ergebnisse. Es wird zudem sehr gut unterstützt, was es zur naheliegenden Wahl für dieses Projekt macht.</p><p>Als nächstes benötigen wir eine Vektordatenbank zum Speichern der Einbettungen und zur Durchführung der Power-Suche. Es sollte nicht nur die Cosinus-kNN-Suche über Bildeinbettungen unterstützen, sondern auch Geofencing und Textfilter in einer einzigen Abfrage anwenden. Elasticsearch passt hier gut: Es verarbeitet Vektoren (HNSW kNN auf dense_vector-Feldern) sehr gut, unterstützt die hybride Suche, die Text-, Vektor- und Geo-Abfragen kombiniert, und bietet standardmäßig Filter- und Sortierfunktionen. Es ist außerdem horizontal skalierbar, sodass man problemlos von einer Handvoll Fotos auf Tausende wachsen kann. Der offizielle <a href="https://www.elastic.co/docs/reference/elasticsearch/clients/python">Elasticsearch Python-Client</a> hält die Infrastruktur einfach und lässt sich nahtlos in das Projekt integrieren. Schließlich benötigen wir noch ein schlankes Frontend, in dem wir Suchanfragen eingeben und Ergebnisse anzeigen können. Für eine schnelle, Python-basierte Demo ist Streamlit hervorragend geeignet. Es bietet die grundlegenden Funktionen, die wir benötigen – Datei-Upload, ein responsives Bildraster und Dropdown-Menüs zum Sortieren und Geofencing. Es lässt sich leicht klonen und lokal ausführen und funktioniert auch in einem Colab-Notebook.</p><h2>Implementierung</h2><h3>Elasticsearch-Indexierungsdesign und Indexierungsstrategie</h3><p>Für dieses Projekt verwenden wir zwei Indizes: <code>peaks_catalog</code> und <code>photos</code>.</p><h4>Peaks_Katalogindex</h4><p>Dieser Index dient als kompakter Katalog markanter Berggipfel, die während der Everest-Basislager-Trekkingtour sichtbar sind. Jedes Dokument in diesem Index entspricht einem einzelnen Berggipfel, wie zum Beispiel dem Mount Everest. Für jedes Berggipfeldokument speichern wir Namen/Aliasse, optionale Breiten- und Längengradkoordinaten sowie einen einzelnen Prototypvektor, der durch die Kombination von SigLIP-2-Texteingabeaufforderungen (+ optionalen Referenzbildern) erstellt wird.</p><p><strong>Indexzuordnung:</strong></p><p>Feld</p><p>Typ</p><p>Beispiel</p><p>Zweck/Anmerkungen</p><p>Vektor-/Indexierung</p><p>Ausweis</p><p>Stichwort</p><p>ama-dablam</p><p>Stabiler Slug/ID</p><p>—</p><p>Namen</p><p>Text + Stichwort-Unterfeld</p><p>["Ama Dablam","Amadablam"]</p><p>Aliase / mehrsprachige Namen; names.raw für genaue Filter</p><p>—</p><p>Breitengrad</p><p>Geopunkt</p><p>{"lat":27.8617,"lon":86.8614}</p><p>GPS-Koordinaten des Gipfels als Kombination aus Breitengrad und Längengrad (optional)</p><p>—</p><p>elev_m</p><p>ganze Zahl</p><p>6812</p><p>Höhenangabe (optional)</p><p>—</p><p>text_embed</p><p>dense_vector</p><p>768</p><p>Kombinierter Prototyp (Aufforderungen und optional 1–3 Referenzbilder) für diesen Peak</p><p>index:true, similarity:"cosine", index_options:{type:"hnsw", m:16, ef_construction:128}</p><p>Dieser Index wird vor allem für Bild-zu-Bild-Suchen verwendet, beispielsweise zur Identifizierung von Berggipfeln anhand von Bildern. Wir verwenden diesen Index auch, um die Ergebnisse der Text-zu-Bild-Suche zu verbessern.</p><p>Zusammenfassend lässt sich sagen, dass die <code>peaks_catalog</code> die Frage „Welcher Berg ist das?“ in ein fokussiertes Nächste-Nachbar-Problem umwandelt und so das konzeptionelle Verständnis effektiv von der Komplexität der Bilddaten trennt.</p><p><strong>Indexierungsstrategie für den peaks_catalog-Index: </strong>Wir beginnen mit der Erstellung einer Liste der markantesten Gipfel, die während der EBC-Trekkingtour sichtbar sind. Für jeden Gipfel speichern wir seine geografische Position, seinen Namen, Synonyme und seine Höhe in einer <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/data/peaks.yaml">YAML-Datei</a>. Im nächsten Schritt wird für jeden Peak <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L351">die Einbettung generiert</a> und im Feld <code>text_embed</code> gespeichert. Um robuste Einbettungen zu erzeugen, verwenden wir die folgende Technik:</p><ul><li><p>Erstellen Sie einen Textprototyp mit folgendem Werkzeug:</p><ul><li><p>Namen der Gipfel</p></li><li><p><a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L301">Prompt-Ensemble</a> (Verwendung mehrerer unterschiedlicher Prompts, um dieselbe Frage zu beantworten), zum Beispiel:</p><ul><li><p>„Ein Naturfoto des Berggipfels {name} im Himalaya, Nepal“</p></li><li><p>„ {name} markanter Gipfel in der Khumbu-Region, alpine Landschaft“</p></li><li><p>„ {name} Berggipfel, Schnee, felsiger Grat“</p></li></ul></li><li><p>optionales <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L333">Anti-Konzept</a> (sagt SigLIP-2, wonach nicht gematcht werden soll): einen kleinen Vektor für „Gemälde, Illustration, Poster, Karte, Logo“ abziehen, um eine Bevorzugung von echten Fotos zu erreichen.</p></li></ul></li><li><p>Optional <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L388C13-L388C29">kann ein Bildprototyp erstellt werden,</a> falls Referenzbilder des Gipfels vorhanden sind.</p></li></ul><p>Anschließend <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L392">verschmelzen wir den Text- und Bildprototyp</a> , um die endgültige Einbettung zu erzeugen. Abschließend wird das Dokument mit allen erforderlichen Feldern <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L396">indexiert</a> :</p>def l2norm(v: np.ndarray) -&gt; np.ndarray:
    return v / (np.linalg.norm(v) + 1e-12)
def compute_blended_peak_vec(
        emb: Siglip2,
        names: List[str],
        peak_id: str,
        peaks_images_root: str,
        alpha_text: float = 0.5,
        max_images: int = 3,
) -&gt; Tuple[np.ndarray, int, int, List[str]]:
    """
    Build blended vector for a single peak.

    Returns:
      vec           : np.ndarray (L2-normalized)
      found_count   : number of reference images discovered
      used_count    : number of references used (&lt;= max_images)
      used_filenames: list of filenames used (for logging)
    """
    # 1) TEXT vector
    tv = embed_text_blend(emb, names)

    # 2) IMAGE refs: prefer folder by id; fallback to slug of the primary name
    root = Path(peaks_images_root)
    candidates = [root / peak_id]
    if names:
        candidates.append(root / slugify(names[0]))

    all_refs: List[Path] = []
    for c in candidates:
        if c.exists() and c.is_dir():
            all_refs = list_ref_images(c)
            if all_refs:
                break

    found = len(all_refs)
    used_list = all_refs[:max_images] if (max_images and found &gt; max_images) else all_refs
    used = len(used_list)

    img_v = embed_image_mean(emb, used_list) if used_list else None

    # 3) Blend TEXT and IMAGE vectors, clamp alpha to [0,1]
    a = max(0.0, min(1.0, float(alpha_text)))
    vec = l2norm(tv if img_v is None else (a * tv + (1.0 - a) * img_v)).astype("float32")
    return vec, found, used, [p.name for p in used_list]<p>Beispieldokument aus dem Index <code>peaks_catalog</code> :</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1219f5d0e39b512c/6a17da7c57726263161bcace/bc05fbd0c4f8d721d5170c28a3884a9eda80bb7d-1210x1132.png" alt="Ein Beispieldokument aus dem peaks_catalog-Index in Elasticsearch." /><h4>Fotoindex</h4><p>Dieser Hauptindex speichert detaillierte Informationen über alle Fotos im Album. Jedes Dokument stellt ein einzelnes Foto dar und enthält folgende Informationen:</p><ul><li><p>Relativer Pfad zum Foto im Fotoalbum. Dies kann verwendet werden, um das passende Bild anzuzeigen oder das Bild in die Suchoberfläche zu laden.</p></li><li><p>GPS- und Zeitinformationen des Bildes.</p></li><li><p>Dichter Vektor für die Bildkodierung, generiert durch SigLIP-2.</p></li><li><p><code>predicted_peaks</code> Das ermöglicht es uns, nach Gipfelnamen zu filtern.

<strong>Indexzuordnung</strong></p></li></ul><p>Feld</p><p>Typ</p><p>Beispiel</p><p>Zweck/Anmerkungen</p><p>Vektor-/Indexierung</p><p>Weg</p><p>Stichwort</p><p>data/images/IMG_1234.HEIC</p><p>Wie die Benutzeroberfläche das Miniaturbild/Vollbild öffnet</p><p>—</p><p>Bildausschnitt</p><p>dense_vector</p><p>768</p><p>SigLIP-2 Bildeinbettung</p><p>index:true, similarity:"cosine", index_options:{type:"hnsw", m:16, ef_construction:128}</p><p>vorhergesagte_Spitzen</p><p>Stichwort</p><p>["ama-dablam","pumori"]</p><p>Top-K-Vorhersagen zur Indexierungszeit (kostengünstiger UX-Filter / Facette)</p><p>—</p><p>GPS</p><p>Geopunkt</p><p>{"lat":27.96,"lon":86.83}</p><p>Aktiviert Geofilter</p><p>—</p><p>Schusszeit</p><p>date</p><p>2023-10-18T09:41:00Z</p><p>Aufnahmezeit: Sortieren/Filtern</p><p>—</p><p><strong>Indexierungsstrategie für den Fotoindex: </strong>Für jedes Foto im Album gehen wir wie folgt vor:
 Extrahieren Sie die Bildinformationen <code>shot_time</code> und <code>gps</code> <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L526">aus den Bildmetadaten</a>.</p><ul><li><p><a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L511">SigLIP-2 Bildeinbettung</a>: Das Bild wird durch das Modell geleitet und der Vektor anschließend L2-normalisiert. Speichere die Einbettung im Feld <code>clip_image</code> .</p></li><li><p><a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L519">Die Peaks werden vorhergesagt</a> und im Feld <code>predicted_peaks</code> gespeichert. Dazu nehmen wir zunächst den im vorherigen Schritt erzeugten Bildvektor des Fotos und führen dann eine schnelle kNN-Suche im Feld text_embed im Index <code>peaks_catalog</code> durch. Wir behalten die obersten 3-4 Spitzen bei und ignorieren den Rest.</p></li><li><p>Wir berechnen das Feld <code>_id</code> , indem wir einen <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L509">Hashwert</a> aus Bildname und Pfad erstellen. Dadurch wird sichergestellt, dass nach mehreren Durchläufen keine Duplikate entstehen.</p></li></ul><p>Sobald alle Felder für das Foto ermittelt wurden, werden die Fotodokumente mithilfe <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L530">der Massenindizierung</a> stapelweise indexiert:</p>def bulk_index_photos(
        es: Elasticsearch,
        images_root: str,
        photos_index: str = "photos",
        peaks_index: str = "peaks_catalog",
        topk_predicted: int = 5,
        batch_size: int = 200,
        refresh: str = "false",
) -&gt; None:
    """Walk a folder of images, embed + enrich, and bulk index to Elasticsearch."""
    root = Path(images_root)
    if not root.exists():
        raise SystemExit(f"Images root not found: {images_root}")

    emb = Siglip2()
    batch: List[Dict[str, Any]] = []
    n_indexed = 0

    for p in iter_images(root):
        rel = relpath_within(root, p)
        _id = id_for_path(rel)

        # 1) Image embedding (and reuse it for predicted_peaks)
        try:
            with Image.open(p) as im:
                ivec = emb.image_vec(im.convert("RGB")).astype("float32")
        except (UnidentifiedImageError, OSError) as e:
            print(f"[skip] {rel} — cannot embed: {e}")
            continue

        # 2) Predict top-k peak names
        try:
            top_names = predict_peaks(es, ivec.tolist(), peaks_index=peaks_index, k=topk_predicted)
        except Exception as e:
            print(f"[warn] predict_peaks failed for {rel}: {e}")
            top_names = []

        # 3) EXIF enrichment (safe)
        gps = get_gps_decimal(str(p))
        shot = get_shot_time(str(p))

        # 4) Build doc and stage for bulk
        doc = {"path": rel, "clip_image": ivec.tolist(), "predicted_peaks": top_names}
        if gps:
            doc["gps"] = gps
        if shot:
            doc["shot_time"] = shot

        batch.append(
            {"_op_type": "index", "_index": photos_index, "_id": _id, "_source": doc}
        )

        # 5) Periodic flush
        if len(batch) &gt;= batch_size:
            helpers.bulk(es, batch, refresh=refresh)
            n_indexed += len(batch)
            print(f"[photos] indexed {n_indexed} (last: {rel})")
            batch.clear()

    # Final flush
    if batch:
        helpers.bulk(es, batch, refresh=refresh)
        n_indexed += len(batch)
        print(f"[photos] indexed {n_indexed} total.")

    print("[done] photos indexing")<p>Beispieldokument aus dem Fotoindex:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt744b7e6326937cfc/6a17da7e6df731d3040a0da8/1dc1406ac2a97440b6804838795b3c2205c4c6b2-1080x1234.png" alt="Ein Beispieldokument aus dem Fotoindex in Elasticsearch." /><p>Zusammenfassend lässt sich sagen, dass der Fotoindex ein schneller, filterbarer und kNN-fähiger Speicher aller Fotos im Album ist. Die Kartierung ist bewusst minimalistisch gehalten – gerade so strukturiert, dass die Ergebnisse schnell abgerufen, übersichtlich dargestellt und nach Raum und Zeit unterteilt werden können. Dieser Index dient beiden Suchanwendungsfällen. Das Python-Skript zur Erstellung beider Indizes finden Sie <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/create_indices.py">hier</a>.</p><p>Die untenstehende Visualisierung der Kibana-Karten zeigt Dokumente aus dem Fotoalbum als grüne Punkte und Berggipfel ab dem Index <code>peaks_catalog</code> als rote Dreiecke an, wobei die grünen Punkte gut mit dem Wanderweg zum Everest-Basislager übereinstimmen.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb5bf016e8d9c3e84/6a17da80be608681f10045e6/1c75d0ed0ce53d28a94bf2f47a354e25581d2baf-1600x1402.png" alt="Eine Visualisierung in Kibana Maps, die Dokumente aus dem Fotoalbum als grüne Punkte und Berggipfel aus dem peaks_catalog-Index als rote Dreiecke darstellt, wobei die grünen Punkte gut mit dem Wanderweg zum Everest Base Camp übereinstimmen." /><h2>Suchanwendungsfälle</h2><p><strong>Namenssuche (Text-zu-Bild): Mit</strong> dieser Funktion können Benutzer mithilfe von Textanfragen Fotos von Berggipfeln (und sogar abstrakten Konzepten wie „Gebetsfahnen“) finden. Um dies zu erreichen, wird die Texteingabe mithilfe von SigLIP-2 <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/query_by_peak_name.py#L87C5-L87C20">in einen Textvektor umgewandelt</a> . Für eine robuste Textvektorgenerierung verwenden wir die gleiche Strategie wie für die Erstellung von Text-Embeddings im <code>peaks_catalog</code> -Index: <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/query_by_peak_name.py#L104">Kombination</a> der Texteingabe mit einem kleinen <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/query_by_peak_name.py#L100">Prompt-Ensemble</a>, Subtraktion eines minor<a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/query_by_peak_name.py#L103"> Anti-Concept-Vektors</a> und Anwendung <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/query_by_peak_name.py#L104">der L2-Normalisierung</a> zur Erzeugung des endgültigen Abfragevektors. Anschließend wird eine kNN- <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/query_by_peak_name.py#L140">Abfrage</a> auf dem Feld <code>photos.clip_image</code> ausgeführt, um die am besten übereinstimmenden Peaks auf Basis der Kosinusähnlichkeit zu ermitteln und so die ähnlichsten Bilder zu finden. Optional können die Suchergebnisse relevanter gestaltet werden, indem Geo- und <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/query_by_peak_name.py#L152">Datumsfilter</a> und/oder ein <code>photos.predicted_peaks</code> -Termfilter als Teil der Abfrage angewendet werden (siehe Abfragebeispiele unten). Dadurch werden ähnlich aussehende Gipfel ausgeschlossen, die auf der Wanderung gar nicht sichtbar sind.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9bb9abf5ce64fcbb/6a17da81e8fbce20db3a17da/b5fac28ffdbedb820505365ca07df125cd01b939-946x370.png" alt="Wie die multimodale Suche nach Namen (Text-zu-Bild) in Elasticsearch funktioniert." /><p><strong>Elasticsearch-Abfrage mit Geofilter:</strong></p>POST photos/_search
{
  "knn": {
    "field": "clip_image",
    "query_vector": [ ... ],
    "k": 60,
    "num_candidates": 2000
  },
  "query": {
    "bool": {
      "filter": [
        { "geo_bounding_box": { "gps": { "top_left": "...", "bottom_right": "..." } } }
      ]
    }
  },
  "_source": ["path","predicted_peaks","gps","shot_time"]
}

Response (first two documents):
{
 "hits": {
   "total": {
     "value": 56,
     "relation": "eq"
   },
   "max_score": 0.5779596,
   "hits": [
     {
       "_index": "photos",
       "_id": "d01da3a1141981486c3493f6053c79e92a788463",
       "_score": 0.5779596,
       "_source": {
         "path": "IMG_2738.HEIC",
         "predicted_peaks": [
           "Pumori",
           "Kyajo Ri",
           "Khumbila",
           "Nangkartshang",
           "Kongde Ri"
         ],
         "gps": {
           "lat": 27.97116388888889,
           "lon": 86.82331111111111
         },
         "shot_time": "2023-11-03T08:07:13"
       }
     },
     {
       "_index": "photos",
       "_id": "c79d251f07adc5efaedc53561110a7fd78e23914",
       "_score": 0.5766071,
       "_source": {
         "path": "IMG_2761.HEIC",
         "predicted_peaks": [
           "Kyajo Ri",
           "Makalu",
           "Baruntse",
           "Cho Oyu",
           "Khumbila"
         ],
         "gps": {
           "lat": 27.975558333333332,
           "lon": 86.82515
         },
         "shot_time": "2023-11-03T08:51:08"
       }
     }
}<p><strong>Bildsuche (Bild-zu-Bild): Mit</strong> dieser Funktion können wir einen Berg auf einem Bild identifizieren und weitere Bilder desselben Berges innerhalb des Fotoalbums finden. Beim Hochladen eines Bildes wird dieses vom SigLIP-2-Bildcodierer verarbeitet, um einen <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/identify_from_picture_find_similar_peaks.py#L228">Bildvektor</a> zu erzeugen. Anschließend wird eine <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/identify_from_picture_find_similar_peaks.py#L234">kNN-Suche</a> im Feld <code>peaks_catalog.text_embed</code> durchgeführt, um die am besten passenden Peaknamen zu identifizieren. Anschließend <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/identify_from_picture_find_similar_peaks.py#L257">wird aus diesen übereinstimmenden Peaknamen ein Textvektor generiert</a> und eine weitere <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/identify_from_picture_find_similar_peaks.py#L263">kNN-Suche</a> im Fotoindex durchgeführt, um entsprechende Bilder zu finden.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltab9d16333e2a9e69/6a17da827f6f155448c099cc/3a3d5635bee7a222b95529dd7f9fbee016381610-1226x550.png" alt="Wie die multimodale Bildersuche (Bild-zu-Bild) in Elasticsearch funktioniert." /><p><strong>Elasticsearch-Abfrage:</strong></p><p>Schritt 1: Finde die passenden Peaknamen</p>GET peaks_catalog/_search
{
 "knn": {
   "field": "text_embed",
   "query_vector": [...image-vector... ],
   "k": 3,
   "num_candidates": 500
 },
 "_source": [
   "id",
   "names",
   "latlon",
   "text_embed"
 ]
}


Response (first two documents):
{
 "took": 2,
 "timed_out": false,
 "_shards": {
   "total": 1,
   "successful": 1,
   "skipped": 0,
   "failed": 0
 },
 "hits": {
   "total": {
     "value": 3,
     "relation": "eq"
   },
   "max_score": 0.58039916,
   "hits": [
     {
       "_index": "peaks_catalog",
       "_id": "pumori",
       "_score": 0.58039916,
       "_source": {
         "id": "pumori",
         "names": [
           "Pumori",
           "Pumo Ri"
         ],
         "latlon": {
           "lat": 28.01472,
           "lon": 86.82806
         },
         "text_embed": [
                  ... embeddings...
         ]
       }
     },
     {
       "_index": "peaks_catalog",
       "_id": "kyajo-ri",
       "_score": 0.57942784,
       "_source": {
         "id": "kyajo-ri",
         "names": [
           "Kyajo Ri",
           "Kyazo Ri"
         ],
         "latlon": {
           "lat": 27.909167,
           "lon": 86.673611
         },
         "text_embed": [
           ... embeddings...
         ]
       }
     }
   ]
 }
}<p>Schritt 2: Führen Sie eine Suche im Index <code>photos</code> durch, um die passenden Bilder zu finden (dieselbe Abfrage wie im Anwendungsfall der Text-zu-Bild-Suche):</p>POST photos/_search
{
 "knn": {
   "field": "clip_image",
   "query_vector": [ ...image-vector... ],
   "k": 30,
   "num_candidates": 2000
 },
 "_source": [
   "path",
   "gps",
   "shot_time",
   "predicted_peaks",
   "clip_image"
 ],
 "query": {
   "bool": {
     "filter": [
       {
         "term": {
           "predicted_peaks": "Pumori"
         }
       }
     ]
   }
 }
}


Response (first two documents):
{
 "hits": {
   "total": {
     "value": 56,
     "relation": "eq"
   },
   "max_score": 0.5779596,
   "hits": [
     {
       "_index": "photos",
       "_id": "d01da3a1141981486c3493f6053c79e92a788463",
       "_score": 0.5779596,
       "_source": {
         "path": "IMG_2738.HEIC",
         "predicted_peaks": [
           "Pumori",
           "Kyajo Ri",
           "Khumbila",
           "Nangkartshang",
           "Kongde Ri"
         ],
         "gps": {
           "lat": 27.97116388888889,
           "lon": 86.82331111111111
         },
         "shot_time": "2023-11-03T08:07:13"
       }
     },
     {
       "_index": "photos",
       "_id": "c79d251f07adc5efaedc53561110a7fd78e23914",
       "_score": 0.5766071,
       "_source": {
         "path": "IMG_2761.HEIC",
         "predicted_peaks": [
           "Kyajo Ri",
           "Makalu",
           "Baruntse",
           "Cho Oyu",
           "Khumbila"
         ],
         "gps": {
           "lat": 27.975558333333332,
           "lon": 86.82515
         },
         "shot_time": "2023-11-03T08:51:08"
       }
     }
}<h2>Streamlit-Benutzeroberfläche</h2><p>Um alles zusammenzuführen, haben wir eine einfache Streamlit-Benutzeroberfläche entwickelt, die es uns ermöglicht, beide Suchanwendungsfälle durchzuführen. In der linken Spalte wird eine scrollbare Liste von Gipfeln (aggregiert aus <code>photos.predicted_peaks</code>) mit Kontrollkästchen und einem Mini-Karten-/Geofilter angezeigt. Ganz oben befinden sich ein <strong>Suchfeld für den Namen</strong> und eine Schaltfläche zum Hochladen <strong>eines Fotos zur Identifizierung</strong> . Im mittleren Bereich befindet sich ein responsives Miniaturraster mit kNN-Werten, vorhergesagten Spitzenwerten und Erfassungszeiten. Jedes Bild enthält eine Schaltfläche <strong>„Bild anzeigen“</strong> für Vorschauen in voller Auflösung.</p><p><strong>Suche durch Hochladen eines Bildes:</strong> Wir prognostizieren den Peak und finden übereinstimmende Peaks aus dem Fotoalbum.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd1fb2b0304a310d2/6a17da8425daab7cda08a0fa/dca540cbf5279e6d6102c5a0c0351ddd4ac91cda-1600x1112.png" alt="Eine einfache, intuitive Benutzeroberfläche, die die multimodale Suche nach den Gipfeln des Mount Ama Dablam sowohl per Text-zu-Bild- als auch per Bild-zu-Bild-Suche ermöglicht." /><p><strong>Suche nach Text</strong>: Finde die passenden Höhepunkte im Album anhand des Textes</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt496c1ae8f7886320/6a17da86abe0f2da48dfe8bd/b1e8618db746cd49ea4962d3dc73031387b975dd-1600x1166.png" alt="Wie man in der Berggipfelbibliothek mithilfe der Textsuche nach einem Gipfel des Mount Everest sucht." /><h2>Fazit</h2><p>Was mit der Frage begann: <em>Können wir bitte die </em>Bilder<em><strong>von Ama Dablam</strong></em>sehen<em>?</em> entwickelte sich zu einem kleinen, funktionierenden <strong>multimodalen Suchsystem</strong> . Wir haben Rohdaten von Trekkingfotos aufgenommen, diese in <strong>SigLIP-2-Einbettungen</strong> umgewandelt und <strong>Elasticsearch</strong> verwendet, um ein schnelles <strong>kNN</strong> über Vektoren durchzuführen, sowie einfache Geo-/Zeitfilter, um die richtigen Bilder anhand ihrer <em>Bedeutung</em> anzuzeigen. Dabei haben wir die Belange mithilfe zweier Indizes getrennt: einem winzigen <code>peaks_catalog</code> Index von kombinierten Prototypen (zur Identifizierung) und einem skalierbaren <code>photos</code> Index von Bildvektoren und EXIF-Daten (zum Abruf). Es ist praktisch, reproduzierbar und leicht erweiterbar.</p><p>Falls Sie es feinabstimmen möchten, gibt es einige Einstellungen, mit denen Sie experimentieren können:</p><ul><li><p><strong>Einstellungen für die Abfragezeit:</strong> <code>k</code> (wie viele Nachbarn Sie zurückbekommen möchten) und <code>num_candidates</code> (wie breit die Suche vor der endgültigen Bewertung sein soll). Diese Einstellungen werden <a href="https://www.elastic.co/search-labs/blog/elasticsearch-knn-and-num-candidates-strategies">hier</a> im Blog besprochen.</p></li><li><p><strong>Indexzeiteinstellungen:</strong> <code>m</code> (Graphkonnektivität) und <code>ef_construction</code> (Genauigkeit der Build-Zeit vs. Speicher). Experimentieren Sie bei Abfragen auch mit <code>ef_search</code> – ein höherer Wert bedeutet in der Regel eine bessere Trefferquote, allerdings mit einem gewissen Nachteil bei der Latenz. Weitere Einzelheiten zu diesen Einstellungen finden Sie in <a href="https://www.elastic.co/search-labs/blog/hnsw-graph">diesem Blog</a> .</p></li></ul><p>Zukünftig werden native Modelle/Reranker für <strong>multimodale</strong> und <strong>mehrsprachige</strong> Suche in Kürze im Elastic-Ökosystem verfügbar sein. Dies dürfte die Bild-/Textsuche und das hybride Ranking von Haus aus noch weiter verbessern.<a href="https://ir.elastic.co/news/news-details/2025/Elastic-Completes-Acquisition-of-Jina-AI-a-Leader-in-Frontier-Models-for-Multimodal-and-Multilingual-Search/default.aspx?utm_source=chatgpt.com"> ir.elastic.co+1</a></p><p>Wenn Sie das selbst ausprobieren möchten:</p><ul><li><p><strong>GitHub-Repository:</strong> <a href="https://github.com/navneet83/multimodal-mountain-peak-search"><em>https://github.com/navneet83/multimodal-mountain-peak-search</em></a></p></li><li><p><strong>Colab-Schnellstartanleitung:</strong> <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/notebooks/multimodal_mountain_peak_search.ipynb">https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/notebooks/multimodal_mountain_peak_search.ipynb</a></p></li></ul><p>Damit ist unsere Reise zu Ende, und es ist Zeit, zurückzufliegen. Ich hoffe, das war hilfreich, und falls etwas kaputtgeht (oder verbessert wird), würde ich gerne erfahren, was Sie geändert haben.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdce2fff1569d2a8b/6a17da894b055dd1f24320a2/d324d1e1472f1bfbd8f25747f57bdeeb9c7f16b2-1600x1200.png" alt="" />]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/multimodal-search-siglip-2-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/multimodal-search-siglip-2-elasticsearch</guid>
    <category><![CDATA[Vektordatenbank]]></category>
    <category><![CDATA[Hybride Suche]]></category>
    <category><![CDATA[KI]]></category>
    <category><![CDATA[Python]]></category>
    <dc:creator><![CDATA[Navneet Kumar]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltccb66279debb05f9/6a17da8b63baffe228741b15/ffcf93358a7c5dadcea82faf3de460bf060d003c-1600x1200.png" length="0" type="image/png"/>
    <pubDate>Tue, 04 Nov 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Training von LTR-Modellen in Elasticsearch mit Urteilslisten basierend auf Be&shy;nut&shy;zer&shy;ver&shy;hal&shy;tens&shy;dat&shy;en]]></title>
    <description><![CDATA[Lernen Sie, wie Sie UBI-Daten verwenden, um Beurteilungslisten zu erstellen und so das Training Ihrer Learning-to-Rank-Modelle (LTR) in Elasticsearch zu automatisieren.]]></description>
    <content:encoded><![CDATA[<p>Eine große Herausforderung bei der Verwendung von <a href="https://www.elastic.co/docs/solutions/search/ranking/learning-to-rank-ltr"><em><strong>Learning-to-Rank-</strong></em></a> Modellen besteht darin, eine qualitativ hochwertige <a href="https://www.elastic.co/search-labs/blog/judgment-lists"><em><strong>Beurteilungsliste</strong></em></a> zu erstellen, mit der das Modell trainiert werden kann. Traditionell beinhaltet dieser Prozess eine <em><strong>manuelle</strong></em> Bewertung der Relevanz von Suchanfrage und Dokument, um jedem Dokument eine Note zuzuweisen. Dies ist ein langsamer Prozess, der sich nicht gut skalieren lässt und schwer zu pflegen ist (stellen Sie sich vor, Sie müssten eine Liste mit Hunderten von Einträgen manuell aktualisieren).</p><p>Was wäre, wenn wir die Interaktionen realer Nutzer mit unserer Suchanwendung nutzen könnten, um diese Trainingsdaten zu erstellen? Die Nutzung <a href="https://www.elastic.co/search-labs/blog/elasticsearch-plugin-user-behavior-insights"><em><strong>von UBI</strong></em></a> -Daten ermöglicht uns genau das. Entwicklung eines automatischen Systems, das unsere Suchanfragen, Klicks und sonstige Interaktionen erfassen und nutzen kann, um eine Bewertungsliste zu erstellen. Dieser Prozess lässt sich viel einfacher skalieren und wiederholen als eine manuelle Interaktion und führt tendenziell zu besseren Ergebnissen. In diesem Blogbeitrag werden wir untersuchen, wie wir in Elasticsearch gespeicherte UBI-Daten abfragen können, um aussagekräftige Signale zu berechnen und so einen Trainingsdatensatz für ein <a href="https://www.elastic.co/search-labs/blog/elasticsearch-learning-to-rank-introduction"><em><strong>LTR-</strong></em></a> Modell zu generieren.</p><p><em><strong>Das vollständige Experiment finden Sie </strong></em><a href="https://github.com/Alex1795/elastic-ltr-judgement_list-blog.git"><em><strong>hier</strong></em></a><em><strong>.</strong></em></p><h2>Warum UBI-Daten für das Training Ihres LTR-Modells nützlich sein können</h2><p>UBI-Daten bieten gegenüber einer manuellen Annotation mehrere Vorteile:</p><ul><li><p><strong>Volumen:</strong> Da die Daten zum bedingungslosen Grundeinkommen aus realen Interaktionen stammen, können wir viel mehr Daten sammeln, als wir manuell generieren könnten. Dies setzt natürlich voraus, dass wir über genügend Traffic verfügen, um diese Daten zu generieren.</p></li><li><p><strong>Tatsächliche Nutzerabsicht:</strong> Traditionell basiert eine manuelle Beurteilungsliste auf der Auswertung der verfügbaren Daten durch Experten. Andererseits spiegeln UBI-Daten das tatsächliche Nutzerverhalten wider. Das bedeutet, dass wir bessere Trainingsdaten generieren können, die die Genauigkeit unseres Suchsystems verbessern, da sie darauf basieren, wie Benutzer tatsächlich mit Ihren Inhalten interagieren und einen Nutzen darin finden, anstatt auf theoretischen Annahmen darüber, was relevant sein sollte.</p></li><li><p><strong>Kontinuierliche Aktualisierungen:</strong> Beurteilungslisten müssen von Zeit zu Zeit aktualisiert werden. Wenn wir sie aus UBI-Daten erstellen, erhalten wir aktuelle Daten, die zu aktualisierten Urteilslisten führen.</p></li><li><p><strong>Kosteneffizienz:</strong> Da keine manuelle Erstellung einer Beurteilungsliste erforderlich ist, kann der Prozess beliebig oft effizient wiederholt werden.</p></li><li><p><strong>Natürliche Abfrageverteilung</strong>: UBI-Daten repräsentieren reale Benutzerabfragen, die tiefgreifendere Veränderungen bewirken können. Nutzen unsere Nutzer beispielsweise natürliche Sprache, um in unserem System zu suchen? In diesem Fall sollten wir möglicherweise einen semantischen Suchansatz oder einen hybriden Suchansatz implementieren.</p></li></ul><p>Es gibt allerdings auch einige Warnhinweise:</p><ul><li><p><strong>Verzerrungsverstärkung: </strong>Beliebte Inhalte erhalten mit größerer Wahrscheinlichkeit Klicks, einfach weil sie mehr Aufmerksamkeit erregen. Dies könnte dazu führen, dass beliebte Artikel verstärkt werden und bessere Alternativen möglicherweise in den Hintergrund treten.</p></li><li><p><strong>Unvollständige Abdeckung: </strong>Neuen Inhalten fehlen jegliche Interaktionen, daher ist es schwierig für sie, in den Suchergebnissen weit oben zu erscheinen. Bei seltenen Anfragen können zudem nicht genügend Datenpunkte vorhanden sein, um aussagekräftige Trainingsdaten zu erzeugen.</p></li><li><p><strong>Saisonale Schwankungen:</strong> Wenn Sie erwarten, dass sich das Nutzerverhalten im Laufe der Zeit drastisch ändert, geben historische Daten möglicherweise nicht viel Aufschluss darüber, was ein gutes Ergebnis ist.</p></li><li><p><strong>Aufgabenunklarheit:</strong> Ein Klick garantiert nicht immer, dass der Nutzer gefunden hat, wonach er gesucht hat.</p></li></ul><h2>Notenberechnung</h2><h3>Noten für LTR-Schulung</h3><p>Um LTR-Modelle zu trainieren, benötigen wir eine numerische Darstellung, die angibt, wie relevant ein Dokument für eine Suchanfrage ist. In unserer Implementierung handelt es sich bei dieser Zahl um einen kontinuierlichen Wert von 0,0 bis 5,0+, wobei höhere Werte eine höhere Relevanz anzeigen.</p><p>Um zu veranschaulichen, wie dieses Bewertungssystem funktioniert, betrachten Sie folgendes manuell erstellte Beispiel:</p><p>Abfrage</p><p>Dokumentinhalt</p><p>Grad</p><p>Erläuterung</p><p>"bestes Pizza-Rezept"</p><p>"Authentisches italienisches Pizzateigrezept mit Schritt-für-Schritt-Fotos"</p><p>4.0</p><p>Äußerst relevant, genau das, wonach der Nutzer sucht.</p><p>"bestes Pizza-Rezept"</p><p>„Geschichte der Pizza in Italien“</p><p>1.0</p><p>Es passt zwar thematisch, es geht um Pizza, ist aber kein Rezept.</p><p>"bestes Pizza-Rezept"</p><p>"Schnelles 15-Minuten-Pizza-Rezept für Anfänger"</p><p>3.0</p><p>Relevant, ein gutes Ergebnis, aber es verfehlt vielleicht das Ziel, das „beste“ Rezept zu sein.</p><p>"bestes Pizza-Rezept"</p><p>"Autowartungsleitfaden"</p><p>0,0</p><p>Überhaupt nicht relevant, steht in keinem Zusammenhang mit der Anfrage.</p><p>Wie wir hier sehen können, ist die Bewertung eine numerische Darstellung der Relevanz eines Dokuments für unsere Beispielanfrage nach dem „besten Pizza-Rezept“. Anhand dieser Werte kann unser LTR-Modell lernen, welche Dokumente in den Ergebnissen weiter oben angezeigt werden sollten.</p><p>Die Berechnung der Noten ist der Kern unseres Trainingsdatensatzes. Hierfür gibt es <a href="https://www.elastic.co/search-labs/blog/judgment-lists">verschiedene Ansätze</a> , jeder mit seinen eigenen Stärken und Schwächen. Wir könnten beispielsweise eine binäre Bewertung vergeben: 1 für relevant, 0 für nicht relevant. Oder wir könnten einfach die Anzahl der Klicks in einem Ergebnisdokument für jede Suchanfrage zählen.</p><p>In diesem Blogbeitrag werden wir einen anderen Ansatz verfolgen, <em><strong>indem wir das Nutzerverhalten als Eingabe betrachten und eine Note als Ausgabe berechnen</strong></em>. Wir werden auch Verzerrungen korrigieren, die dadurch entstehen könnten, dass höhere Ergebnisse tendenziell häufiger angeklickt werden, unabhängig von der Relevanz des Dokuments.</p><h2>Notenberechnung – COEC-Algorithmus</h2><p>Der COEC-Algorithmus (<a href="https://www.wsdm-conference.org/2010/proceedings/docs/p351.pdf">Clicks over Expected Clicks</a>) ist eine Methode zur Berechnung von Beurteilungsnoten aus den Klicks der Nutzer.
Wie bereits erwähnt, neigen Nutzer dazu, auf weiter oben positionierte Ergebnisse zu klicken, selbst wenn das Dokument nicht das relevanteste für die Suchanfrage ist; dies wird als <a href="https://eugeneyan.com/writing/position-bias/">Positionsbias</a> bezeichnet. Die Grundidee des COEC-Algorithmus besteht darin, dass nicht alle Klicks gleich wichtig sind; ein Klick auf ein Dokument an Position 10 deutet darauf hin, dass das Dokument für die Suchanfrage viel relevanter ist als ein Klick auf ein Dokument an Position 1. Um die Forschungsarbeit zum COEC-Algorithmus (siehe Link oben) zu zitieren:</p><p><em>„Es ist bekannt, dass die Klickrate (CTR) von Suchergebnissen oder Anzeigen je nach Position der Ergebnisse deutlich abnimmt.“</em></p><p>Mehr zum Thema Positionsbias können Sie <a href="https://www.researchgate.net/publication/200110550_An_experimental_comparison_of_click_position-bias_models">hier</a> lesen.</p><p>Um dies mit dem COEC-Algorithmus zu lösen, gehen wir wie folgt vor:</p><p><strong>1. Festlegung von Positionsbaselines:</strong> Wir berechnen die Klickrate (CTR) für jede Suchposition von 1 bis 10. Das bedeutet, wir ermitteln, welcher Prozentsatz der Nutzer typischerweise auf Position 1, Position 2 usw. klickt. Dieser Schritt erfasst die natürliche Positionsverzerrung der Nutzer.

Wir berechnen die CTR wie folgt:</p><p>Wo:</p><p>p = Position. Von 1 bis 10
 Cp = Gesamtzahl der Klicks (auf beliebige Dokumente) an Position p über alle Abfragen hinweg
 Ip = Gesamteindrücke: Wie oft ein Dokument an Position p über alle Suchanfragen hinweg erschienen ist.</p><p>Hier gehen wir davon aus, dass höhere Positionen mehr Klicks erhalten.</p><p><strong>2.</strong> <strong>Berechnung der erwarteten Klicks (EC)</strong>:</p><p>Diese Kennzahl legt fest, wie viele Klicks ein Dokument basierend auf seinen Platzierungspositionen und der Klickrate (CTR) für diese Positionen hätte erhalten sollen. Wir berechnen EC wie folgt:</p><p>Wo:</p><p>Qd = Alle Anfragen, bei denen das Dokument d vorkam
 pos(d,q) = Position des Dokuments d in den Abfrageergebnissen q</p><p>3. <strong>Tatsächliche Klicks zählen: </strong>Wir zählen die tatsächliche Gesamtzahl der Klicks, die ein Dokument über alle Suchanfragen hinweg erhalten hat, bei denen es erschien, im Folgenden <strong>A(d) genannt.</strong></p><p>4. <strong>Berechnen Sie den COEC-Wert:</strong> Dies ist das Verhältnis der tatsächlichen Klicks (A(d)) zu den erwarteten Klicks (EC(d)):</p><p>Diese Metrik normalisiert Positionsverzerrungen folgendermaßen:</p><ul><li><p>Ein Wert von 1,0 bedeutet, dass das Dokument angesichts seiner Positionen genau wie erwartet funktioniert hat.</p></li><li><p>Ein Wert über 1,0 bedeutet, dass das Dokument im Vergleich zu den bisherigen Ergebnissen besser abgeschnitten hat. Dieses Dokument ist daher für die Anfrage relevanter.</p></li><li><p>Ein Wert unter 1,0 bedeutet, dass das Dokument im Vergleich zu den bisherigen Ergebnissen schlechter abgeschnitten hat. Dieses Dokument ist daher für die Anfrage weniger relevant.</p></li></ul><p><em><strong>Das Endergebnis ist eine Bewertungszahl, die das widerspiegelt, wonach die Nutzer suchen, wobei positionsbezogene Erwartungen berücksichtigt werden, die aus realen Interaktionen mit unserem Suchsystem abgeleitet wurden.</strong></em></p><h2>Technische Umsetzung</h2><p>Wir werden ein Skript erstellen, um eine Beurteilungsliste zu generieren, mit der ein LTR-Modell trainiert werden kann.</p><p>Die Eingabe für dieses Skript sind die in Elastic indexierten UBI-Daten (Abfragen und Ereignisse).</p><p>Das Ergebnis ist eine Beurteilungsliste in einer CSV-Datei, die aus diesen UBI-Dokumenten mithilfe des COEC-Algorithmus generiert wird. Diese Beurteilungsliste kann mit <a href="https://www.elastic.co/search-labs/blog/elasticsearch-learning-to-rank-introduction">Eland</a> verwendet werden, um relevante Merkmale zu extrahieren und ein LTR-Modell zu trainieren.</p><h3>Schnellstart</h3><p>Um aus den Beispieldaten in diesem Blog eine Bewertungsliste zu erstellen, können Sie folgende Schritte befolgen:</p><p>1. Klonen Sie das Repository:</p>git clone https://github.com/Alex1795/elastic-ltr-judgement_list-blog.git  
cd elastic-ltr-judgement_list-blog<p>2. Installieren Sie die erforderlichen Bibliotheken</p><p>Für dieses Skript benötigen wir die folgenden Bibliotheken:</p><ul><li><p><em>pandas</em>: um die Urteilsliste zu speichern</p></li><li><p><em>elasticsearch</em>: Um die UBI-Daten aus unserer Elastic-Bereitstellung zu erhalten</p></li></ul><p>Wir benötigen außerdem Python 3.11.</p>pip install -r requirements.txt<p>3. Aktualisieren Sie die Umgebungsvariablen für Ihre Elastic-Bereitstellung in einer <a href="https://github.com/Alex1795/elastic-ltr-judgement_list-blog/blob/main/.env-example">.env-Datei.</a></p><ul><li><p>ES_HOST</p></li><li><p>API-Schlüssel</p></li></ul><p>Um die Umgebungsvariablen hinzuzufügen, verwenden Sie:</p>source .env<p>4. Erstellen Sie die Indizes ubi_queries und ubi_events und laden Sie die Beispieldaten hoch. Führen Sie die Datei setup.py aus:</p>python setup.py<p>5. Führen Sie das Python-Skript aus:</p>python judgement_list-generator.py<p>Wenn Sie diese Schritte befolgen, sollte eine neue Datei namens judgement_list.csv erscheinen, die folgendermaßen aussieht:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt94317eda8f7af194/6a170aa46f7f04542f914821/2531090131ac9fe3e4e1d79de9d156fc47a7825a-782x531.png" alt="" /><p>Dieses Skript berechnet die Noten unter Anwendung des zuvor erläuterten COEC-Algorithmus mithilfe der unten gezeigten Funktion <strong>calculate_relevance_grade()</strong> .</p><h2>Datenarchitektur</h2><h3>Ubi-Anfragen</h3><p>Unser UBI-Abfrageindex enthält Informationen über die in unserem Suchsystem ausgeführten Abfragen. Dies ist ein Beispieldokument:</p>{
          "client_id": "client_002",
          "query": "italian pasta recipes",
          "query_attributes": {
            "search_type": "recipe",
            "category": "food",
            "cuisine": "italian"
          },
          "query_id": "q002",
          "query_response_id": "qr002",
          "query_response_object_ids": [
            "doc_011",
            "doc_012",
            "doc_013",
            "doc_014",
            "doc_015",
            "doc_016",
            "doc_017",
            "doc_018",
            "doc_019",
            "doc_020"
          ],
          "timestamp": "2024-08-14T11:15:00Z",
          "user_query": "italian pasta recipes"
        }<p>Hier sehen wir Daten vom Benutzer (client_id), aus den Ergebnissen der Abfrage (query_response_object_ids) und die Abfrage selbst (timestamp, user_query).</p><h3>Ubi-Klickereignisse</h3><p>Unser ubi_events-Index enthält Daten von jedem Klick eines Nutzers auf ein Dokument in den Suchergebnissen. Dies ist ein Beispieldokument:</p>{
          "action_name": "click",
          "application": "recipe_search",
          "client_id": "client_001",
          "event_attributes": {
            "object": {
              "description": "Authentic Italian Pizza Dough Recipe with Step-by-Step Photos",
              "device": "desktop",
              "object_id": "doc_001",
              "position": {
                "ordinal": 1,
                "page_depth": 1
              },
              "user": {
                "city": "New York",
                "country": "USA",
                "ip": "192.168.1.100",
                "location": {
                  "lat": 40.7128,
                  "lon": -74.006
                },
                "region": "NY"
              }
            }
          },
          "message": "User clicked on document doc_001",
          "message_type": "click",
          "query_id": "q001",
          "timestamp": "2024-08-14T10:31:00Z",
          "user_query": "best pizza recipe"
        }<h2>Skript zur Generierung der Urteilsliste</h2><h3>Allgemeine Skriptübersicht</h3><p>Dieses Skript automatisiert die Generierung der Beurteilungsliste mithilfe von UBI-Daten aus Abfragen und Klickereignissen, die in Elasticsearch gespeichert sind. Es führt folgende Aufgaben aus:</p><ul><li><p>Ruft die UBI-Daten in Elasticsearch ab und verarbeitet sie.</p></li><li><p>Korreliert UBI-Ereignisse mit seinen Abfragen.</p></li><li><p>Berechnet die Klickrate (CTR) für jede Position.</p></li><li><p>Berechnet die erwarteten Klicks (EC) für jedes Dokument.</p></li><li><p>Zählt die tatsächlichen Klicks für jedes Dokument.</p></li><li><p>Berechnet den COEC-Score für jedes Abfrage-Dokument-Paar.</p></li><li><p>Erstellt eine Bewertungsliste und speichert diese in einer CSV-Datei.</p></li></ul><p>Lassen Sie uns die einzelnen Funktionen durchgehen:</p><h3>connect_to_elasticsearch()</h3>def connect_to_elasticsearch(host, api_key):
    """Create and return Elasticsearch client"""
    try:
        es = Elasticsearch(
            hosts=[host],
            api_key=api_key,
            request_timeout=60
        )
        # Test the connection
        if es.ping():
            print(f"✓ Successfully connected to Elasticsearch at {host}")
            return es
        else:
            print("✗ Failed to connect to Elasticsearch")
            return None
    except Exception as e:
        print(f"✗ Error connecting to Elasticsearch: {e}")
        return None<p>Diese Funktion gibt ein Elasticsearch-Clientobjekt unter Verwendung des Hosts und des API-Schlüssels zurück.</p><h3>fetch_ubi_data()</h3>def fetch_ubi_data(es_client: Elasticsearch, queries_index: str, events_index: str,
                   size: int = 10000) -&gt; Tuple[List[Dict], List[Dict]]:
    """
    Fetch UBI queries and events data from Elasticsearch indices.

    Args:
        es_client: Elasticsearch client
        queries_index: Name of the UBI queries index
        events_index: Name of the UBI events index
        size: Maximum number of documents to fetch

    Returns:
        Tuple of (queries_data, events_data)
    """
    logger.info(f"Fetching data from {queries_index} and {events_index}")

    # Fetch queries with error handling
    try:
        queries_response = es_client.search(
            index=queries_index,
            body={
                "query": {"match_all": {}},
                "size": size
            }
        )
        queries_data = [hit['_source'] for hit in queries_response['hits']['hits']]
        logger.info(f"Fetched {len(queries_data)} queries")

    except Exception as e:
        logger.error(f"Error fetching queries from {queries_index}: {e}")
        raise

    # Fetch events (only click events for now) with error handling
    try:
        events_response = es_client.search(
            index=events_index,
            body={
                "query": {
                    "term": {"message_type.keyword": "CLICK_THROUGH"}
                },
                "size": size
            }
        )
        events_data = [hit['_source'] for hit in events_response['hits']['hits']]
        logger.info(f"Fetched {len(events_data)} click events")

    except Exception as e:
        logger.error(f"Error fetching events from {events_index}: {e}")
        raise

    logger.info(f"Data fetch completed successfully - Queries: {len(queries_data)}, Events: {len(events_data)}")

    return queries_data, events_data<p>Diese Funktion ist die Datenextraktionsschicht; sie stellt eine Verbindung zu Elasticsearch her, um UBI-Abfragen mittels einer match_all-Abfrage abzurufen und filtert UBI-Ereignisse, um nur 'CLICK_THROUGH'-Ereignisse zu erhalten.</p><h3>process_ubi_data()</h3>def process_ubi_data(queries_data: List[Dict], events_data: List[Dict]) -&gt; pd.DataFrame:
    """
    Process UBI data and generate judgment list.

    Args:
        queries_data: List of query documents from UBI queries index
        events_data: List of event documents from UBI events index

    Returns:
        DataFrame with judgment list (qid, docid, grade, keywords)
    """
    logger.info("Processing UBI data to generate judgment list")

    # Group events by query_id
    clicks_by_query = {}
    for event in events_data:
        query_id = event['query_id']
        if query_id not in clicks_by_query:
            clicks_by_query[query_id] = {}

        # Extract clicked document info
        object_id = event['event_attributes']['object']['object_id']
        position = event['event_attributes']['object']['position']['ordinal']

        clicks_by_query[query_id][object_id] = {
            'position': position,
            'timestamp': event['timestamp']
        }

    judgment_list = []

    # Process each query
    for query in queries_data:
        query_id = query['query_id']
        user_query = query['user_query']
        document_ids = query['query_response_object_ids']

        # Get clicks for this query
        query_clicks = clicks_by_query.get(query_id, {})

        # Generate judgment for each document shown
        for doc_id in document_ids:
            grade = calculate_relevance_grade(doc_id, query_clicks, document_ids, queries_data, events_data)

            judgment_list.append({
                'qid': query_id,
                'docid': doc_id,
                'grade': grade,
                'query': user_query
            })

    df = pd.DataFrame(judgment_list)
    logger.info(f"Generated {len(df)} judgment entries for {df['qid'].nunique()} unique queries")

    return df<p>Diese Funktion ist für die Generierung der Urteilsliste zuständig. Die Verarbeitung der UBI-Daten beginnt mit der Verknüpfung von UBI-Ereignissen und -Abfragen. Anschließend wird für jedes Dokument-Abfrage-Paar die Funktion calculate_relevance_grade() aufgerufen, um die Einträge für die Bewertungsliste zu erhalten. Schließlich gibt es die resultierende Liste als Pandas-DataFrame zurück.</p><h3>calculate_relevance_grade()</h3>def calculate_relevance_grade(document_id: str, clicks_data: Dict,
                              query_response_ids: List[str], all_queries_data: List[Dict] = None,
                              all_events_data: List[Dict] = None) -&gt; float:
    """
    Calculate COEC (Click Over Expected Clicks) relevance score for a document.

    Args:
        document_id: ID of the document
        clicks_data: Dictionary of clicked documents with their positions for current query
        query_response_ids: List of document IDs shown in search results (ordered by position)
        all_queries_data: All queries data for calculating position CTR averages
        all_events_data: All events data for calculating position CTR averages

    Returns:
        COEC relevance score (continuous value, typically 0.0 to 5.0+)
    """

    # If no global data provided, fall back to simple position-based grading
    if all_queries_data is None or all_events_data is None:
        logger.warning("No global data provided, falling back to position-based grading")
        # Simple fallback logic
        if document_id in clicks_data:
            position = clicks_data[document_id]['position']
            if position &gt; 3:
                return 4.0
            elif position &gt;= 1 and position &lt;= 3:
                return 3.0
        if document_id in query_response_ids:
            position = query_response_ids.index(document_id) + 1
            if position &lt;= 5:
                return 2.0
            elif position &gt;= 6 and position &lt;= 10:
                return 1.0
        return 0.0

    # Calculate rank-aggregated click-through rates
    position_ctr_averages = {}
    position_impression_counts = {}
    position_click_counts = {}

    # Initialize counters
    for pos in range(1, 11):  # Positions 1-10
        position_impression_counts[pos] = 0
        position_click_counts[pos] = 0

    # Count impressions (every document shown contributes)
    for query in all_queries_data:
        for i, doc_id in enumerate(query['query_response_object_ids'][:10]):  # Top 10 positions
            position = i + 1
            position_impression_counts[position] += 1

    # Count clicks by position
    for event in all_events_data:
        if event.get('action_name') == 'click':
            position = event['event_attributes']['object']['position']['ordinal']
            if position &lt;= 10:
                position_click_counts[position] += 1

    # Calculate average CTR per position
    for pos in range(1, 11):
        if position_impression_counts[pos] &gt; 0:
            position_ctr_averages[pos] = position_click_counts[pos] / position_impression_counts[pos]
        else:
            position_ctr_averages[pos] = 0.0

    # Calculate expected clicks for this specific document
    expected_clicks = 0.0

    # Count how many times this document appeared at each position for any query
    for query in all_queries_data:
        if document_id in query['query_response_object_ids']:
            position = query['query_response_object_ids'].index(document_id) + 1
            if position &lt;= 10:
                expected_clicks += position_ctr_averages[position]

    # Count total actual clicks for this document across all queries
    actual_clicks = 0
    for event in all_events_data:
        if (event.get('action_name') == 'click' and
                event['event_attributes']['object']['object_id'] == document_id):
            actual_clicks += 1

    # Calculate COEC score
    if expected_clicks &gt; 0:
        coec_score = actual_clicks / expected_clicks
    else:
        coec_score = 0.0

    logger.debug(
        f"Document {document_id}: {actual_clicks} clicks / {expected_clicks:.3f} expected = {coec_score:.3f} COEC")

    return coec_score<p>Dies ist die Funktion, die den COEC-Algorithmus implementiert. Es berechnet die Klickrate (CTR) für jede Position, vergleicht dann die tatsächlichen Klicks für ein Dokument-Abfrage-Paar und berechnet schließlich den tatsächlichen COEC-Wert für jedes Paar.</p><h3>generate_judgment_statistics()</h3>def generate_judgment_statistics(df: pd.DataFrame) -&gt; Dict:
    """Generate statistics about the judgment list."""
    stats = {
        'total_judgments': len(df),
        'unique_queries': df['qid'].nunique(),
        'unique_documents': df['docid'].nunique(),
        'grade_distribution': df['grade'].value_counts().to_dict(),
        'avg_judgments_per_query': len(df) / df['qid'].nunique() if df['qid'].nunique() &gt; 0 else 0,
        'queries_with_clicks': len(df[df['grade'] &gt; 1]['qid'].unique()),
        'click_through_rate': len(df[df['grade'] &gt; 1]) / len(df) if len(df) &gt; 0 else 0
    }
    return stats<p>Es generiert nützliche Statistiken aus der Bewertungsliste, wie z. B. die Gesamtzahl der Anfragen, die Gesamtzahl der eindeutigen Dokumente oder die Notenverteilung. Dies dient lediglich der Information und hat keinen Einfluss auf die endgültige Urteilsliste.</p><h2>Ergebnisse und Auswirkungen</h2><p>Wenn Sie die Anweisungen im Abschnitt „Schnellstart“ befolgen, sollte eine CSV-Datei mit einer Urteilsliste mit 320 Einträgen angezeigt werden (ein <a href="https://github.com/Alex1795/elastic-ltr-judgement_list-blog/blob/main/judgment_list.csv">Beispiel</a> finden Sie im Repository). Mit diesen Feldern:</p><ul><li><p>qid: eindeutige ID der Abfrage</p></li><li><p>docid: eindeutige Kennung für ein resultierendes Dokument</p></li><li><p>Note: die berechnete Note für das Abfrage-Dokument-Paar</p></li><li><p>Anfrage: Die Benutzeranfrage</p></li></ul><p> Schauen wir uns die Ergebnisse der Suchanfrage „Italienische Rezepte“ an:</p><p>qid</p><p>docid</p><p>Grad</p><p>Abfrage</p><p>q1-italienische-rezepte</p><p>Grundrezept für Pasta</p><p>0,0</p><p>Italienische Rezepte</p><p>q1-italienische-rezepte</p><p>Rezept_Pizza_Margherita</p><p>3,333333</p><p>Italienische Rezepte</p><p>q1-italienische-rezepte</p><p>Rezept-Risotto-Anleitung</p><p>10.0</p><p>Italienische Rezepte</p><p>q1-italienische-rezepte</p><p>Rezept_französisches_Croissant</p><p>0,0</p><p>Italienische Rezepte</p><p>q1-italienische-rezepte</p><p>Rezept_spanische_Paella</p><p>0,0</p><p>Italienische Rezepte</p><p>q1-italienische-rezepte</p><p>Rezept_griechische_Moussaka</p><p>1,875</p><p>Italienische Rezepte</p><p>Aus den Ergebnissen geht hervor, dass für die Suchanfrage „Italienische Rezepte“ Folgendes gilt:</p><ul><li><p>Das Risotto-Rezept ist definitiv das beste Ergebnis der Suchanfrage und erhielt zehnmal mehr Klicks als erwartet.</p></li><li><p>Auch die Pizza Margherita ist ein hervorragendes Ergebnis.</p></li><li><p>Die griechische Moussaka erzielt (überraschenderweise) ebenfalls ein gutes Ergebnis und schneidet besser ab, als ihre Platzierung in der Ergebnisliste vermuten lässt. Das bedeutet, dass einige Nutzer, die nach italienischen Rezepten suchten, stattdessen an diesem Rezept interessiert waren. Vielleicht interessieren sich diese Nutzer generell für mediterrane Gerichte. Letztendlich bedeutet dies, dass es sich um ein gutes Ergebnis handeln könnte, das unter den beiden anderen, oben besprochenen, "besseren" Treffern angezeigt werden könnte.</p></li></ul><h2>Fazit</h2><p>Die Verwendung von UBI-Daten ermöglicht es uns, das Training von LTR-Modellen zu automatisieren und so qualitativ hochwertige Beurteilungslisten aus unseren eigenen Nutzern zu erstellen. Die UBI-Daten liefern einen großen Datensatz, der die Nutzung unseres Suchsystems widerspiegelt. Durch die Verwendung des COEC-Algorithmus zur Generierung der Noten berücksichtigen wir inhärente Verzerrungen und spiegeln gleichzeitig wider, was ein Benutzer als besseres Ergebnis ansieht. Die hier beschriebene Methode kann auf reale Anwendungsfälle angewendet werden, um ein besseres Sucherlebnis zu bieten, das sich mit den tatsächlichen Nutzungstrends weiterentwickelt.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/training-learning-to-rank-models-elasticsearch-ubi-data</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/training-learning-to-rank-models-elasticsearch-ubi-data</guid>
    <category><![CDATA[Python]]></category>
    <category><![CDATA[Elastic Cloud Hosted]]></category>
    <dc:creator><![CDATA[Alexander Dávila]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt037eb2f4d380fe65/6a170aa67d8d67397170e6e6/762bf09c28829d626d42c2cfadc719e1dd618d1b-1536x1024.png" length="0" type="image/png"/>
    <pubDate>Wed, 15 Oct 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Geodaten-Suche mit Elasticsearch und ES|QL]]></title>
    <description><![CDATA[Geodaten-Suche in der Elasticsearch Query Language (ES|QL). Elasticsearch verfügt über leistungsstarke Geodaten-Suchfunktionen, die nun in ES|QL integriert werden, um die Benutzerfreundlichkeit und OGC-Vertrautheit deutlich zu verbessern.]]></description>
    <content:encoded><![CDATA[<p>Elasticsearch verfügt seit vielen Jahren über leistungsstarke <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/geospatial-analysis.html">Funktionen für die Geodaten-Suche und -Analyse</a> , aber die API unterschied sich deutlich von dem, was typische GIS-Anwender gewohnt waren. Im vergangenen Jahr haben wir <a href="https://www.elastic.co/search-labs/blog/esql-piped-query-language-goes-ga">die Abfragesprache ES|QL hinzugefügt</a>, eine Pipe-Abfragesprache, die genauso einfach oder sogar noch einfacher als SQL ist. Es eignet sich besonders für die Anwendungsfälle Suche, Sicherheit und Beobachtbarkeit, in denen Elastic hervorragende Leistungen erbringt. Wir fügen außerdem Unterstützung für die Geodaten-Suche und -Analyse innerhalb von ES|QL hinzu, was die Nutzung deutlich vereinfacht, insbesondere für Anwender aus SQL- oder <a href="https://en.wikipedia.org/wiki/Geographic_information_system">GIS-</a> Bereichen.</p><p>Elasticsearch 8.12 und 8.13 führten die grundlegende Unterstützung für Geodaten-Typen in ES|QL ein. Dies wurde durch die Hinzufügung von Geodaten-Suchfunktionen in Version 8.14 deutlich verbessert. Wichtiger noch: Diese Unterstützung wurde so konzipiert, dass sie sich eng an den <a href="https://en.wikipedia.org/wiki/Simple_Features">Simple Feature Access-</a> Standard des <a href="https://en.wikipedia.org/wiki/Open_Geospatial_Consortium">Open Geospatial Consortium (OGC)</a> anlehnt, der auch von anderen räumlichen Datenbanken wie PostGIS verwendet wird. Dadurch wird die Nutzung für GIS-Experten, die mit diesen Standards vertraut sind, deutlich vereinfacht.</p><p>In diesem Blog zeigen wir Ihnen, wie Sie mit ES|QL Geodatenabfragen durchführen und wie sich dies im Vergleich zu den entsprechenden SQL- und Query-DSL-Funktionen verhält. Wir zeigen Ihnen außerdem, wie Sie mit ES|QL räumliche Verknüpfungen durchführen und wie Sie die Ergebnisse in Kibana Maps visualisieren. Bitte beachten Sie, dass sich alle hier beschriebenen Funktionen in der „technischen Vorschau“ befinden. Wir freuen uns über Ihr Feedback, wie wir diese verbessern können.</p><h2>Suche nach Geodaten</h2><p>Beginnen wir mit einer Beispielabfrage:</p>FROM airport_city_boundaries
| WHERE ST_INTERSECTS(
      city_boundary,
      "POLYGON((109.4 18.1, 109.6 18.1, 109.6 18.3, 109.4 18.3, 109.4 18.1))"::geo_shape
  )
| KEEP abbrev, airport, region, city, city_location
<p>Hierbei wird nach Stadtgrenzenpolygonen gesucht, die sich mit einem rechteckigen Suchpolygon um den internationalen Flughafen Sanya Phoenix (SYX) schneiden.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3897df6bed5d6061/6a17d7c2abe0f29eccdfe861/e48bac8f246c8842f2ea97ddd54910045262aeb1-1440x808.png" alt="ESQL-Geodatensuche" /><p>In einem Beispieldatensatz mit Flughäfen, Städten und Stadtgrenzen findet diese Suche das sich überschneidende Polygon und gibt die gewünschten Felder aus dem übereinstimmenden Dokument zurück:</p><p>Abkürzung</p><p>Flughafen</p><p>Region</p><p>Stadt</p><p>Stadtstandort</p><p>SYX</p><p>Sanya Phoenix Int'l</p><p>天涯区</p><p>Sanya</p><p>Punkt(109.5036 18.2533)</p><p>Das war einfach! Vergleichen Sie dies nun mit der klassischen Elasticsearch Query DSL für dieselbe Abfrage:</p>GET /airport_city_boundaries/_search
{
  "_source": ["abbrev", "airport", "region", "city", "city_location"],
  "query": {
    "geo_shape": {
      "city_boundary": {
        "shape": {
          "type": "polygon",
          "coordinates" : [[
            [109.4, 18.1],
            [109.6, 18.1],
            [109.6, 18.3],
            [109.4, 18.3],
            [109.4, 18.1]
          ]]
        }
      }
    }
  }
}
<p>Beide Abfragen sind in ihrer Absicht einigermaßen klar, aber die ES|QL-Abfrage ähnelt stark SQL. Die gleiche Abfrage in PostGIS sieht folgendermaßen aus:</p>SELECT abbrev, airport, region, city, city_location
FROM airport_city_boundaries
WHERE ST_INTERSECTS(
    city_boundary,
    'SRID=4326;POLYGON((109.4 18.1, 109.6 18.1, 109.6 18.3, 109.4 18.3, 109.4 18.1))'::geometry
);
<p>Schauen Sie sich das ES|QL-Beispiel noch einmal an. So ähnlich, nicht wahr?</p>FROM airport_city_boundaries
| WHERE ST_INTERSECTS(
      city_boundary,
      "POLYGON((109.4 18.1, 109.6 18.1, 109.6 18.3, 109.4 18.3, 109.4 18.1))"::geo_shape
  )
| KEEP abbrev, airport, region, city, city_location
<p>Wir haben festgestellt, dass bestehende Benutzer der Elasticsearch API ES|QL als wesentlich einfacher zu bedienen empfinden. Wir gehen davon aus, dass bestehende SQL-Anwender, insbesondere Spatial SQL-Anwender, feststellen werden, dass sich ES|QL sehr vertraut anfühlt und ihnen sehr ähnlich ist.</p><h4>Warum nicht SQL?</h4><p>Und wie sieht es mit Elasticsearch SQL aus? Es existiert schon eine Weile und verfügt über einige Geodatenfunktionen. Elasticsearch SQL wurde jedoch als Wrapper für die ursprüngliche Query-API geschrieben, was bedeutete, dass nur Abfragen unterstützt wurden, die in die ursprüngliche API transpiliert werden konnten. ES|QL hat diese Einschränkung nicht. Da es sich um einen völlig neuen Stack handelt, sind viele Optimierungen möglich, die in SQL nicht möglich waren. Unsere Benchmarks zeigen, dass ES|QL <a href="https://elasticsearch-benchmarks.elastic.co/#tracks/esql/nightly/default/6M">sehr oft schneller ist als die Query API</a>, insbesondere bei Aggregationen!</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt18bda964c8b24e36/6a17d7c3e3179155d22d568a/b8b6c2b2e45850d832805ed1e71e522f4955f53c-1440x813.png" alt="Polygon-Schnittpunkt-Benchmark" /><h2>Unterschiede zu SQL</h2><p>Wie das vorherige Beispiel zeigt, ist ES|QL SQL zwar in gewisser Weise ähnlich, es gibt aber auch einige wichtige Unterschiede. ES|QL ist beispielsweise eine Pipe-Abfragesprache, die mit einem Quellbefehl wie FROM beginnt und dann alle nachfolgenden Befehle mit dem Pipe-Zeichen | miteinander verkettet. Dadurch wird es sehr einfach verständlich, wie jeder Befehl eine Datentabelle empfängt und eine bestimmte Aktion an dieser Tabelle durchführt, z. B. Filtern mit <code>WHERE</code>, Hinzufügen von Spalten mit <code>EVAL</code> oder Durchführen von Aggregationen mit <code>STATS</code>. Anstatt mit <code>SELECT</code> zu beginnen, um die endgültigen Ausgabespalten zu definieren, können ein oder mehrere <code>KEEP</code> -Befehle verwendet werden, wobei der letzte Befehl die endgültigen Ausgaberesultate angibt. Diese Struktur vereinfacht die Argumentation bezüglich der Anfrage.</p><p>Wenn wir uns den Befehl <code>WHERE</code> im obigen Beispiel genauer ansehen, erkennen wir, dass er dem PostGIS-Beispiel sehr ähnlich sieht:</p><p><em>ES|QL</em></p>WHERE ST_INTERSECTS(
    city_boundary,
    "POLYGON((109.4 18.1, 109.6 18.1, 109.6 18.3, 109.4 18.3, 109.4 18.1))"::geo_shape
)
<p><em>PostGIS</em></p>WHERE ST_INTERSECTS(
    city_boundary,
    'SRID=4326;POLYGON((109.4 18.1, 109.6 18.1, 109.6 18.3, 109.4 18.3, 109.4 18.1))'::geometry
)
<p>Abgesehen von den Unterschieden bei den Anführungszeichen für Zeichenketten besteht der größte Unterschied darin, wie wir die Zeichenkette in einen räumlichen Datentyp umwandeln. In PostGIS verwenden wir das Suffix <code>::geometry</code> , während wir in ES|QL das Suffix <code>::geo_shape</code> verwenden. Dies liegt daran, dass ES|QL innerhalb von Elasticsearch ausgeführt wird und der Typumwandlungsoperator <code>::</code> verwendet werden kann, um eine Zeichenkette in einen der <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/esql-limitations.html#_supported_types">unterstützten ES|QL-Typen</a> umzuwandeln, in diesem Fall in einen <code>geo_shape</code>. Darüber hinaus implizieren die Typen <code>geo_shape</code> und <code>geo_point</code> in Elasticsearch das räumliche Koordinatensystem WGS84, das häufiger unter der SRID-Nummer 4326 bezeichnet wird. In PostGIS muss dies explizit angegeben werden, daher die Verwendung des Präfixes <code>SRID=4326;</code> für die WKT-Zeichenkette. Wird dieses Präfix entfernt, wird die SRID auf 0 gesetzt, was eher den Elasticsearch-Typen <code>cartesian_point</code> und <code>cartesian_shape</code> entspricht, die nicht an ein bestimmtes Koordinatensystem gebunden sind.</p><p>Sowohl ES|QL als auch PostGIS bieten eine Syntax für Typkonvertierungsfunktionen:</p><p><em>ES|QL</em></p>WHERE ST_INTERSECTS(
    city_boundary,
    TO_GEOSHAPE("POLYGON((109.4 18.1, 109.6 18.1, 109.6 18.3, 109.4 18.3, 109.4 18.1))")
)
<p><em>PostGIS</em></p>WHERE ST_INTERSECTS(
    city_boundary,
    ST_SetSRID(
      ST_GeomFromText('POLYGON((109.4 18.1, 109.6 18.1, 109.6 18.3, 109.4 18.3, 109.4 18.1))'),
      4326
    )
)
<h2>OGC-Funktionen</h2><p>Elasticsearch 8.14 führt die folgenden vier OGC-Funktionen für die räumliche Suche ein:</p><p>ES|QL</p><p>PostGIS</p><p>Beschreibung</p><p>ST_INTERSECTS</p><p>ST_Schnittpunkte</p><p>Gibt true zurück, wenn sich zwei Geometrien schneiden, und andernfalls false.</p><p>ST_DISJOINT</p><p>ST_Disjoint</p><p>Gibt true zurück, wenn sich zwei Geometrien nicht schneiden, und andernfalls false. Die Umkehrfunktion von ST_INTERSECTS.</p><p>ST_CONTAINS</p><p>ST_Contains</p><p>Gibt true zurück, wenn eine Geometrie eine andere enthält, andernfalls false.</p><p>ST_WITHIN</p><p>ST_Within</p><p>Gibt true zurück, wenn sich eine Geometrie innerhalb einer anderen befindet, und andernfalls false. Das Inverse von ST_CONTAINS.</p><p>Diese Funktionen verhalten sich ähnlich wie ihre PostGIS-Pendants und werden auf die gleiche Weise verwendet. Beispielsweise gibt <code>ST_INTERSECTS</code> true zurück, wenn sich zwei Geometrien schneiden, und andernfalls false. Wenn Sie den Dokumentationslinks in der obigen Tabelle folgen, werden Sie feststellen, dass sich alle ES|QL-Beispiele innerhalb einer <code>WHERE</code> -Klausel nach einer <code>FROM</code> -Klausel befinden, während alle PostGIS-Beispiele literale Geometrien verwenden. Tatsächlich unterstützen beide Plattformen die Verwendung der Funktionen in jedem Teil der Abfrage, in dem sie sinnvoll sind.</p><p>Das erste Beispiel in der PostGIS-Dokumentation für <code>ST_INTERSECTS</code> lautet:</p>SELECT ST_Intersects(
    'POINT(0 0)'::geometry,
    'LINESTRING ( 2 0, 0 2 )'::geometry
);
<p>Das ES|QL-Äquivalent dazu wäre:</p>ROW ST_INTERSECTS(
    "POINT(0 0)"::geo_point,
    "LINESTRING ( 2 0, 0 2 )"::geo_shape
)
<p>Beachten Sie, dass wir im PostGIS-Beispiel die SRID nicht angegeben haben. Dies liegt daran, dass in PostGIS bei Verwendung des Typs <code>geometry</code> alle Berechnungen auf einem planaren Koordinatensystem durchgeführt werden. Wenn also beide Geometrien die gleiche SRID haben, spielt es keine Rolle, welche SRID es ist. In Elasticsearch gilt dies auch für die meisten Funktionen. Es gibt jedoch Ausnahmen, bei denen <code>geo_shape</code> und <code>geo_point</code> sphärische Berechnungen verwenden, wie wir im nächsten Blogbeitrag über die Suche nach räumlichen Distanzen sehen werden.</p><h2>ES|QL Vielseitigkeit</h2><p>Wir haben oben also Beispiele für die Verwendung von räumlichen Funktionen in <code>WHERE</code> -Klauseln und in <code>ROW</code> -Befehlen gesehen. Wo sonst würden sie Sinn machen? Eine sehr nützliche Stelle dafür ist der Befehl <code>EVAL</code> . Mit diesem Befehl können Sie einen Ausdruck auswerten und das Ergebnis zurückgeben. Nehmen wir beispielsweise an, wir wollen feststellen, ob die Schwerpunkte aller Flughäfen, gruppiert nach ihren Ländernamen, innerhalb einer Grenze liegen, die das jeweilige Land umschließt:</p>FROM airports
| EVAL in_uk = ST_INTERSECTS(location, TO_GEOSHAPE("POLYGON((1.2305 60.8449, -1.582 61.6899, -10.7227 58.4017, -7.1191 55.3291, -7.9102 54.2139, -5.4492 54.0078, -5.2734 52.3756, -7.8223 49.6676, -5.0977 49.2678, 0.9668 50.5134, 2.5488 52.1065, 2.6367 54.0078, -0.9668 56.4625, 1.2305 60.8449))"))
| EVAL in_iceland = ST_INTERSECTS(location, TO_GEOSHAPE("POLYGON ((-25.4883 65.5312, -23.4668 66.7746, -18.4131 67.4749, -13.0957 66.2669, -12.3926 64.4159, -20.1270 62.7346, -24.7852 63.3718, -25.4883 65.5312))"))
| EVAL within_uk = ST_WITHIN(location, TO_GEOSHAPE("POLYGON((1.2305 60.8449, -1.582 61.6899, -10.7227 58.4017, -7.1191 55.3291, -7.9102 54.2139, -5.4492 54.0078, -5.2734 52.3756, -7.8223 49.6676, -5.0977 49.2678, 0.9668 50.5134, 2.5488 52.1065, 2.6367 54.0078, -0.9668 56.4625, 1.2305 60.8449))"))
| EVAL within_iceland = ST_WITHIN(location, TO_GEOSHAPE("POLYGON ((-25.4883 65.5312, -23.4668 66.7746, -18.4131 67.4749, -13.0957 66.2669, -12.3926 64.4159, -20.1270 62.7346, -24.7852 63.3718, -25.4883 65.5312))"))
| STATS centroid = ST_CENTROID_AGG(location), count=COUNT() BY in_uk, in_iceland, within_uk, within_iceland
| SORT count ASC
<p>Die Ergebnisse sind erwartungsgemäß: Die Schwerpunkte der britischen Flughäfen liegen innerhalb der Grenzen Großbritanniens und nicht innerhalb der Grenzen Islands, und umgekehrt.</p><p>Schwerpunkt</p><p>Anzahl</p><p>in_uk</p><p>in Island</p><p>innerhalb Großbritanniens</p><p>innerhalb Islands</p><p>PUNKT (-21,946634463965893 64.13187285885215)</p><p>1</p><p>FALSCH</p><p>wahr</p><p>FALSCH</p><p>wahr</p><p>PUNKT (-2,597342072712148 54,33551226578214)</p><p>17</p><p>wahr</p><p>FALSCH</p><p>wahr</p><p>FALSCH</p><p>PUNKT (0,04453958108176276 23,74658354606057)</p><p>873</p><p>FALSCH</p><p>FALSCH</p><p>FALSCH</p><p>FALSCH</p><p>Tatsächlich können diese Funktionen in jedem Teil der Abfrage verwendet werden, in dem ihre Signatur sinnvoll ist. Sie alle nehmen zwei Argumente entgegen, die entweder ein Literal eines räumlichen Objekts oder ein Feld eines räumlichen Typs sind, und sie alle geben einen booleschen Wert zurück. Eine wichtige Überlegung ist, dass das Koordinatenreferenzsystem (CRS) der Geometrien übereinstimmen muss, andernfalls wird ein Fehler zurückgegeben. Das bedeutet, dass Sie die Typen <code>geo_shape</code> und <code>cartesian_shape</code> nicht im selben Funktionsaufruf mischen können. Allerdings können Sie die Typen <code>geo_point</code> und <code>geo_shape</code> mischen, da der Typ <code>geo_point</code> ein Sonderfall des Typs <code>geo_shape</code> ist und beide das gleiche Koordinatenreferenzsystem verwenden. Die Dokumentation zu jeder der oben definierten Funktionen listet die unterstützten Typkombinationen auf.</p><p>Darüber hinaus kann jedes Argument ein räumliches Literal oder ein Feld sein, in beliebiger Reihenfolge. Sie können sogar zwei Felder, zwei Literale, ein Feld und ein Literal oder ein Literal und ein Feld angeben. Die einzige Voraussetzung ist, dass die Datentypen kompatibel sind. Diese Abfrage vergleicht beispielsweise zwei Felder im selben Index:</p>FROM airport_city_boundaries
| EVAL in_city = ST_INTERSECTS(city_location, city_boundary)
| STATS count=COUNT(*) BY in_city
| SORT count ASC
| EVAL cardinality = CASE(count &lt; 10, "very few", count &lt; 100, "few", "many")
| KEEP cardinality, count, in_city
<p>Die Anfrage zielt im Grunde darauf ab, ob sich der Ort innerhalb der Stadtgrenzen befindet, was im Allgemeinen zutreffen sollte, aber es gibt immer Ausnahmen:</p><p>Kardinalität</p><p>Anzahl</p><p>in_city</p><p>wenige</p><p>29</p><p>FALSCH</p><p>viele</p><p>740</p><p>wahr</p><p>Eine weitaus interessantere Frage wäre, ob sich der Flughafenstandort innerhalb der Grenzen der Stadt befindet, die der Flughafen bedient. Der Standort des Flughafens befindet sich jedoch in einem anderen Index als derjenige, der die Stadtgrenzen enthält. Dies erfordert eine Methode, um Daten aus diesen beiden separaten Indizes effektiv abzufragen und zu korrelieren.</p><h2>Räumliche Verknüpfungen</h2><p>ES|QL unterstützt keine <code>JOIN</code> -Befehle, aber Sie können einen Sonderfall eines Joins mit dem <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/esql-commands.html#esql-enrich"><code>ENRICH</code></a><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/esql-commands.html#esql-enrich"> -Befehl</a> erreichen, der sich ähnlich wie ein 'left join' in SQL verhält. Dieser Befehl funktioniert ähnlich wie ein „Left Join“ in SQL und ermöglicht es Ihnen, Ergebnisse aus einem Index mit Daten aus einem anderen Index anzureichern, basierend auf einer räumlichen Beziehung zwischen den beiden Datensätzen.</p><p>Nehmen wir beispielsweise an, wir reichern die Ergebnisse einer Tabelle mit Flughäfen um zusätzliche Informationen über die jeweilige Stadt an, indem wir die Stadtgrenze ermitteln, die den Flughafenstandort enthält, und führen dann einige statistische Auswertungen der Ergebnisse durch:</p>FROM airports
| ENRICH city_boundaries ON city_location WITH airport, region, city_boundary
| MV_EXPAND city_boundary
| EVAL boundary_wkt_length = LENGTH(TO_STRING(city_boundary))
| STATS centroid = ST_CENTROID_AGG(location), count = COUNT(city_location), min_wkt = MIN(boundary_wkt_length), max_wkt = MAX(boundary_wkt_length) BY region
| SORT count DESC
| LIMIT 5
<p>Dies liefert die Top 5 Regionen mit den meisten Flughäfen, zusammen mit dem Schwerpunkt aller Flughäfen, die übereinstimmenden Regionen zugeordnet sind, und der Längenspanne der WKT-Darstellung der Stadtgrenzen innerhalb dieser Regionen:</p><p>Schwerpunkt</p><p>Anzahl</p><p>min_wkt</p><p>max_wkt</p><p>Region</p><p>PUNKT (-32.56093470960719 32.598117914802714)</p><p>90</p><p>207</p><p>207</p><p>null</p><p>PUNKT (-73.94515332765877 40.70366442203522)</p><p>9</p><p>438</p><p>438</p><p>Stadt New York</p><p>PUNKT (-83.10398317873478 42.300230911932886)</p><p>9</p><p>473</p><p>473</p><p>Detroit</p><p>PUNKT (-156.3020245861262 20.176383580081165)</p><p>5</p><p>307</p><p>803</p><p>Hawaii</p><p>PUNKT (-73.88902732171118 45.57078813901171)</p><p>4</p><p>837</p><p>837</p><p>Montréal</p><p>Was ist hier also wirklich geschehen? Wo trat das vermeintliche <code>JOIN</code> auf? Der Kern der Anfrage liegt im Befehl <code>ENRICH</code> :</p>FROM airports
| ENRICH city_boundaries ON city_location WITH airport, region, city_boundary
<p>Dieser Befehl weist Elasticsearch an, die aus dem Index <code>airports</code> abgerufenen Ergebnisse anzureichern und einen Join <code>intersects</code> zwischen dem Feld <code>city_location</code> des ursprünglichen Index und dem Feld <code>city_boundary</code> des Index <code>airport_city_boundaries</code> durchzuführen, den wir bereits in einigen Beispielen verwendet haben. Einige dieser Informationen sind in dieser Abfrage jedoch nicht klar ersichtlich. Was wir sehen, ist der Name einer Anreicherungsrichtlinie <code>city_boundaries</code>, und die fehlenden Informationen sind in dieser Richtliniendefinition enthalten.</p>{
  "geo_match": {
    "indices": "airport_city_boundaries",
    "match_field": "city_boundary",
    "enrich_fields": ["city", "airport", "region", "city_boundary"]
  }
}
<p>Hier sehen wir, dass eine <code>geo_match</code> -Abfrage durchgeführt wird (<code>intersects</code> ist der Standardwert), das Feld, mit dem abgeglichen werden soll, ist <code>city_boundary</code>, und die <code>enrich_fields</code> sind die Felder, die wir dem Originaldokument hinzufügen möchten. Eines dieser Felder, nämlich <code>region</code> wurde tatsächlich als Gruppierungsschlüssel für den Befehl <code>STATS</code> verwendet, was ohne diese 'left join'-Funktion nicht möglich gewesen wäre. Weitere Informationen zu Anreicherungsrichtlinien finden Sie in der <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/enrich-setup.html">Anreicherungsdokumentation</a>. Beim Lesen dieser Dokumente werden Sie feststellen, dass darin die Verwendung von Anreicherungsindizes zur Anreicherung von Daten während der Indexierung durch die Konfiguration von Aufnahmepipelines beschrieben wird. Dies ist für ES|QL nicht erforderlich, da der Befehl <code>ENRICH</code> zur Abfragezeit funktioniert. Es genügt, den Anreicherungsindex mit den erforderlichen Daten und der Anreicherungsrichtlinie vorzubereiten und dann den Befehl <code>ENRICH</code> in Ihren ES|QL-Abfragen zu verwenden.</p><p>Möglicherweise stellen Sie auch fest, dass die am häufigsten vorkommende Region <code>null</code> war. Was könnte das bedeuten? Zur Erinnerung: Ich habe diesen Befehl mit einem 'Left Join' in SQL verglichen. Das bedeutet, dass, wenn keine übereinstimmende Stadtgrenze für einen Flughafen gefunden wird, der Flughafen trotzdem zurückgegeben wird, jedoch mit <code>null</code> Werten für die Felder ab dem <code>airport_city_boundaries</code> Index. Es stellte sich heraus, dass es 89 Flughäfen gab, bei denen kein passender Eintrag <code>city_boundary</code> gefunden wurde, und einen Flughafen mit einer Übereinstimmung, bei dem das Feld <code>region</code> den <code>null</code> hatte. Dies führte zu einer Zählung von 90 Flughäfen, bei denen keine <code>region</code> in den Ergebnissen vorkam. Ein weiteres interessantes Detail ist die Notwendigkeit des Befehls <code>MV_EXPAND</code> . Dies ist notwendig, da der Befehl <code>ENRICH</code> für jede Eingabezeile mehrere Ergebnisse zurückgeben kann, und <code>MV_EXPAND</code> hilft dabei, diese Ergebnisse in mehrere Zeilen aufzuteilen, eine für jedes Ergebnis. Dies erklärt auch, warum für "Hawaii" unterschiedliche <code>min_wkt</code> und <code>max_wkt</code> Ergebnisse angezeigt werden: Es gab mehrere Regionen mit dem gleichen Namen, aber unterschiedlichen Grenzen.</p><h2>Kibana-Karten</h2><p>Kibana hat die Unterstützung für Spatial ES|QL in der Kartenanwendung hinzugefügt. Das bedeutet, dass Sie nun ES|QL verwenden können, um in Elasticsearch nach Geodaten zu suchen und die Ergebnisse auf einer Karte zu visualisieren.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt91922eb4ef43d290/6a16f7161949f737bfe7a7b5/bd78470bd8a4bc60f0db7006bd804b8fe87e2fea-1440x683.png" alt="Kibana Layers ES|QL" /><p>Im Menü „Ebenen hinzufügen“ gibt es eine neue Ebenenoption mit der Bezeichnung „ES|QL“. Wie alle bisher beschriebenen Geodatenfunktionen befindet sich auch diese in der „technischen Vorschauphase“. Durch Auswahl dieser Option können Sie der Karte eine Ebene hinzufügen, die auf den Ergebnissen einer ES|QL-Abfrage basiert. Man könnte beispielsweise eine Ebene zur Karte hinzufügen, die alle Flughäfen der Welt anzeigt.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5185ef7461d7e81d/6a16f718839dfa1559dcfcb1/1dd28d3d0509f92d26b0bb5320a2925f7a54c5d9-1440x736.png" alt="Kibana ES|QL - Flughäfen" /><p>Oder Sie könnten eine Ebene hinzufügen, die die Polygone ab dem Index <code>airport_city_boundaries</code> anzeigt, oder noch besser, wie wäre es mit der komplexen <code>ENRICH</code> -Abfrage oben, die Statistiken darüber generiert, wie viele Flughäfen sich in jeder Region befinden?</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt51ee3543bb811d98/6a16f71a8b73cbbe63189df1/679a0a401faa613c7cedddd07c64f614ac2b7144-1440x727.png" alt="Kibana ES|QL – Regionsstatistik" /><h2>Was kommt als Nächstes?</h2><p>Vielleicht ist Ihnen aufgefallen, dass wir in zwei der obigen Beispiele noch eine weitere räumliche Funktion <code>ST_CENTROID_AGG</code> eingefügt haben. Dies ist eine Aggregationsfunktion, die im Befehl <code>STATS</code> verwendet wird, und die erste von vielen räumlichen Analysefunktionen, die wir ES|QL hinzufügen wollen. Wir werden darüber bloggen, sobald wir mehr zu zeigen haben!</p><p>Zuvor möchten wir Ihnen jedoch ein besonders spannendes Feature vorstellen, an dem wir gearbeitet haben: die Möglichkeit, räumliche Distanzsuchen durchzuführen – eine der am häufigsten genutzten räumlichen Suchfunktionen von Elasticsearch. Können Sie sich vorstellen, wie die Syntax für Distanzsuchen aussehen könnte? Vielleicht ähnlich einer OGC-Funktion? Bleiben Sie dran für den nächsten Blogbeitrag dieser Reihe, um es herauszufinden!</p><p>Spoiler-Alarm: Elasticsearch 8.15 wurde soeben veröffentlicht und beinhaltet die räumliche Distanzsuche mit ES|QL!</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/esql-geospatial-search-part-one</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/esql-geospatial-search-part-one</guid>
    <category><![CDATA[Python]]></category>
    <category><![CDATA[ES|QL]]></category>
    <dc:creator><![CDATA[Craig Taverner]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd05627be20e89dfb/6a17d7c6414c640256944fdb/de886289dcb56494920875303b622b030b9b810f-720x420.jpg" length="0" type="image/jpeg"/>
    <pubDate>Mon, 12 Aug 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Bewertung der Suchrelevanz Teil 1 – Der BEIR-Benchmark]]></title>
    <description><![CDATA[Lernen Sie, Ihr Suchsystem im Zusammenhang mit einem besseren Verständnis des BEIR-Benchmarks zu bewerten, mit Tipps und Techniken zur Verbesserung Ihrer Suchbewertungsprozesse.]]></description>
    <content:encoded><![CDATA[<p>Dies ist der erste Beitrag einer Reihe von Blogbeiträgen, in denen wir uns damit befassen, wie Sie Ihre eigenen Suchsysteme im Zusammenhang mit einem besseren Verständnis des BEIR-Benchmarks bewerten können. Wir stellen Ihnen spezielle Tipps und Techniken vor, mit denen Sie Ihre Suchbewertungsprozesse im Zusammenhang mit dem besseren Verständnis von BEIR verbessern können. Wir stellen Ihnen auch die häufigsten Fallstricke vor, die eine Bewertung weniger zuverlässig machen. Abschließend möchten wir darauf hinweisen, dass LLMs ein leistungsstarkes neues Tool für Suchingenieure darstellen. Anhand eines Beispiels zeigen wir, wie Sie diese zur Bewertung der Suche einsetzen können.</p><h2>Den BEIR-Benchmark bei der Bewertung der Suchrelevanz verstehen</h2><p>Für die Verbesserung eines Systems müssen Sie messen können, wie gut es funktioniert. Im Zusammenhang mit der Suche <a href="https://arxiv.org/abs/2104.08663">BEIR</a> (oder gleichwertig der Abschnitt „Retrieval” der Bestenliste <a href="https://huggingface.co/spaces/mteb/leaderboard">MTEB</a>) gilt als der „heilige Gral” für die Informationsabruf-Community, was nicht weiter verwunderlich ist. Es handelt sich um einen sehr gut strukturierten Benchmark mit vielfältigen Datensätzen für unterschiedliche Aufgaben. Genauer gesagt werden folgende Bereiche abgedeckt:</p><ul><li><p>Abrufen von Argumenten (ArguAna, Touche2020)</p></li><li><p>Open-Domain-QA (HotpotQA, Natural Questions, FiQA)</p></li><li><p>Passagenabruf (MSMARCO)</p></li><li><p>Abrufen doppelter Fragen (Quora, CQADupstack)</p></li><li><p>Faktenprüfung (FEVER, Climate-FEVER, Scifact)</p></li><li><p>Biomedizinische Informationsabfrage (TREC-COVID, NFCorpus, BioASQ)</p></li><li><p>Abrufen von Entitäten (DBPedia)</p></li><li><p>Zitationsvorhersage (SCIDOCS)</p></li></ul><p>Es liefert eine einzige Statistik, nDCG@10, die angibt, wie gut ein System die relevantesten Dokumente für jedes Aufgabenbeispiel in den von ihm zurückgegebenen besten Ergebnissen abgleicht. Für ein Suchsystem, mit dem ein Mensch interagiert, ist die Relevanz der betsen Ergebnisse entscheidend. Allerdings gibt es bei der Bewertung von Suchvorgängen viele Nuancen, die durch eine einzelne zusammenfassende Statistik nicht erfasst werden.</p><h2>Struktur eines BEIR-Datensatzes</h2><p>Jeder Benchmark hat drei Artefakte:</p><ul><li><p>der Korpus oder die Dokumente, die abgerufen werden sollen</p></li><li><p>Die Abfragen</p></li><li><p>die Relevanzbewertungen für die Abfragen (auch bekannt als <code>qrels</code>).</p></li></ul><p>Relevanzbewertungen werden als Wert zwischen null und größer angegeben. Werte ungleich Null zeigen an, dass das Dokument in gewisser Weise mit der Abfrage in Zusammenhang steht.</p><p>Datensatz</p><p>Korpusgröße</p><p>#Abfragen im Testset</p><p>#qrels positiv gekennzeichnet</p><p>#qrels gleich Null</p><p>#duplicates im Korpus</p><p>Arguana</p><p>8.674</p><p>1.406</p><p>1.406</p><p>0</p><p>96</p><p>Climate-FEVER</p><p>5.416.593</p><p>1.535</p><p>4.681</p><p>0</p><p>0</p><p>DBPedia</p><p>4.635.922</p><p>400</p><p>15.286</p><p>28.229</p><p>0</p><p>FEVER</p><p>5.416.568</p><p>6.666</p><p>7.937</p><p>0</p><p>0</p><p>FiQA-2018</p><p>57.638</p><p>648</p><p>1.706</p><p>0</p><p>0</p><p>HotpotQA</p><p>5.233.329</p><p>7.405</p><p>14.810</p><p>0</p><p>0</p><p>Natürliche Fragen</p><p>2.681.468</p><p>3.452</p><p>4.021</p><p>0</p><p>16.781</p><p>NFCorpus</p><p>3.633</p><p>323</p><p>12.334</p><p>0</p><p>80</p><p>Quora</p><p>522.931</p><p>10.000</p><p>15.675</p><p>0</p><p>1.092</p><p>SCIDOCS</p><p>25.657</p><p>1.000</p><p>4.928</p><p>25.000</p><p>2</p><p>Scifact</p><p>5.183</p><p>300</p><p>339</p><p>0</p><p>0</p><p>Touche2020</p><p>382.545</p><p>49</p><p>932</p><p>1.982</p><p>5.357</p><p>TREC-COVID</p><p>171.332</p><p>50</p><p>24.763</p><p>41.663</p><p>0</p><p>MSMARCO</p><p>8.841.823</p><p>6.980</p><p>7.437</p><p>0</p><p>324</p><p>CQADupstack (Summe)</p><p>457.199</p><p>13.145</p><p>23.703</p><p>0</p><p>0</p><p><strong>Tabelle 1</strong>: Datensatzstatistiken. Die Zahlen wurden auf dem Testabschnitt der Datensätze berechnet (<code>dev</code> für <code>MSMARCO</code>).</p><p><strong>Tabelle 1</strong> enthält einige Statistiken zu den Datensätzen, aus denen der <code>BEIR</code>-Benchmark besteht, wie zum Beispiel die Anzahl der Dokumente im Korpus, die Anzahl der Abfragen im Testdatensatz und die Anzahl der positiven/negativen Paare (Abfrage, Dokument) in der <code>qrels</code>-Datei. Ein kurzer Blick auf die Daten lässt uns sofort Folgendes ableiten:</p><ul><li><p>Die meisten Datensätze enthalten keine negativen Beziehungen in der <code>qrels</code>-Datei, d. h. null Werte, was Dokumente ausdrücklich als irrelevant für die jeweilige Abfrage kennzeichnen würde.</p></li><li><p>Die durchschnittliche Anzahl von Dokumentbeziehungen pro Abfrage (<code>#qrels</code> / <code>#queries</code>) variiert von 1,0 im Ticket von <code>ArguAna</code> bis 493,5 (<code>TREC-COVID</code>), jedoch mit einem Wert von <code>&lt;</code>5 für die Mehrheit der Tickets.</p></li><li><p>Bei einigen Datensätzen gibt es doppelte Dokumente im Korpus, was in einigen Fällen zu einer falschen Auswertung führen kann, z. B. wenn ein Dokument als relevant für eine Abfrage angesehen wird, sein Duplikat jedoch nicht. Zum Beispiel haben wir in <code>ArguAna</code> 96 Tickets von doppelten Dokumentpaaren identifiziert, wobei pro Paar nur ein Dokument als relevant für eine Abfrage markiert wurde. Durch die „Erweiterung“ der ursprünglichen Qrels-Liste um die Duplikate haben wir einen relativen Anstieg des <code>nDCG@10</code>-Wertes um durchschnittlich ~1 % festgestellt.</p></li></ul>{
  "_id": "test-economy-epiasghbf-pro02b",
  "title": "economic policy international africa society gender house believes feminisation",
  "text": "Again employment needs to be contextualised with …",
  "metadata": {}
}
{
  "_id": "test-society-epiasghbf-pro02b",
  "title": "economic policy international africa society gender house believes feminisation",
  "text": "Again employment needs to be contextualised with …",
  "metadata": {}
}
<p><strong>Beispiel für duplizierte Paare in ArguAna. In der qrels-Datei scheint nur der erste Eintrag (als Gegenargument) für die Abfrage („test-economy-epiasghbf-pro02a“) relevant zu sein.</strong></p><p>Beim Vergleich von Modellen in der MTEB-Rangliste ist es naheliegend, sich auf die durchschnittliche Retrieval-Qualität zu konzentrieren. Dies ist ein guter Indikator für die Gesamtqualität des Modells, sagt jedoch nicht unbedingt etwas darüber aus, wie es für Sie funktionieren wird. Da die Ergebnisse pro Datensatz gemeldet werden, ist es sinnvoll zu verstehen, wie eng die verschiedenen Datensätze mit Ihrer Suchaufgabe zusammenhängen, und die Modelle nur anhand der relevantesten Datensätze neu zu bewerten. Wenn Sie noch tiefer eintauchen möchten, können Sie zusätzlich überprüfen, ob es Überschneidungen zwischen den Themen der verschiedenen Datensätze gibt. Die Stratifizierung von Qualitätsmaßen nach Themen ermöglicht eine viel differenziertere Bewertung ihrer spezifischen Stärken und Schwächen.</p><p>Wichtig ist hierbei, dass ein Dokument, das nicht in der <code>qrels</code>-Datei markiert ist, standardmäßig als für die Abfrage irrelevant angesehen wird. Wir befassen uns etwas eingehender mit diesem Bereich und sammeln einige Nachweise, um mehr Licht in die folgende Frage zu bringen: „Wie oft werden einem Evaluator Paare (Abfrage, Dokument) vorgelegt, für die es keine Ground-Truth-Informationen gibt?“. Der Grund dafür ist, dass bei nur verfügbaren oberflächlichen Markups (sodass nicht jedes relevante Dokument als solches gekennzeichnet ist) ein Informationsabrufsystem schlechter bewertet werden kann als ein anderes, nur weil es sich dafür „entscheidet“, andere relevante (aber nicht markierte) Dokumente anzuzeigen. Das ist ein häufiger Fehler bei der Erstellung qualitativ hochwertiger Evaluierungsdatensätze, insbesondere bei großen Datensätzen. Aus praktischen Gründen konzentriert sich die manuelle Kennzeichnung in der Regel auf die besten Ergebnisse, die vom aktuellen System zurückgegeben werden, sodass relevante Dokumente in den blinden Flecken möglicherweise übersehen werden. Daher ist es in der Regel vorzuziehen, mehr Ressourcen auf ein umfassenderes Markup weniger Abfragen als auf ein breites, oberflächliches Markup zu konzentrieren.</p><h2>Nutzung des BEIR-Benchmarks zur Bewertung der Suchrelevanz</h2><p>Um unsere Analyse zu beginnen, implementieren wir das folgende Szenario (siehe <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/evaluating-search-relevance-part-1/retrieve-and-rerank.ipynb">Notizbuch</a>):</p><ol><li><p>Zuerst laden wir den Korpus jedes Datensatzes in einen Elasticsearch-Index.</p></li><li><p>Für jede Abfrage im Testsatz rufen wir die 100 besten Dokumente mit BM25 ab.</p></li><li><p>Wir ordnen die abgerufenen Dokumente mithilfe verschiedener SOTA-Reranking-Modelle neu an.</p></li><li><p>Abschließend geben wir die „Bewertungsrate“ für die 10 besten Dokumente aus Schritt 2 (nach dem Abrufen) und Schritt 3 (nach dem Reranking) an. Mit anderen Worten: Wir berechnen den durchschnittlichen Prozentsatz der 10 besten Dokumente mit einer Bewertung in der <code>qrels</code>-Datei.</p></li></ol><p>Die Liste der von uns verwendeten Reranking-Modelle lautet wie folgt:</p><ul><li><p><a href="https://docs.cohere.com/reference/rerank">Coheres</a> <code>rerank-english-v2.0</code> und <code>rerank-english-v3.0</code></p></li><li><p><a href="https://huggingface.co/BAAI/bge-reranker-base">BGE-base</a></p></li><li><p><a href="https://huggingface.co/mixedbread-ai/mxbai-rerank-xsmall-v1">mxbai-rerank-xsmall-v1</a></p></li><li><p><a href="https://huggingface.co/cross-encoder/ms-marco-MiniLM-L-6-v2">MiniLM-L-6-v2</a></p></li></ul><p></p><p>Abruf</p><p>Reranking</p><p></p><p></p><p></p><p></p><p>Datensatz</p><p>BM25 (%)</p><p>Cohere Rerank v2 (%)</p><p>Cohere Rerank v3 (%)</p><p>BGE-base (%)</p><p>mxbai-rerank-xsmall-v1 (%)</p><p>MiniLM-L-6-v2 (%)</p><p>Arguana</p><p>7,54</p><p>4,87</p><p>7,87</p><p>4,52</p><p>4,53</p><p>6,84</p><p>Climate-FEVER</p><p>5,75</p><p>6,24</p><p>8,15</p><p>9,36</p><p>7,79</p><p>7,58</p><p>DBPedia</p><p>61,18</p><p>60,78</p><p>64,15</p><p>63,9</p><p>63,5</p><p>67,62</p><p>FEVER</p><p>8,89</p><p>9,97</p><p>10,08</p><p>10,19</p><p>9,88</p><p>9,88</p><p>FiQa-2018</p><p>7,02</p><p>11,02</p><p>10,77</p><p>8,43</p><p>9,1</p><p>9,44</p><p>HotpotQA</p><p>12,59</p><p>14,5</p><p>14,76</p><p>15,1</p><p>14,02</p><p>14,42</p><p>Natürliche Fragen</p><p>5,94</p><p>8.84</p><p>8,71</p><p>8,37</p><p>8,14</p><p>8,34</p><p>NFCorpus</p><p>31,67</p><p>32,9</p><p>33,91</p><p>30,63</p><p>32,77</p><p>32,45</p><p>Quora</p><p>12,2</p><p>10,46</p><p>13,04</p><p>11,26</p><p>12,58</p><p>12,78</p><p>SCIDOCS</p><p>8,62</p><p>9,41</p><p>9,71</p><p>8,04</p><p>8,79</p><p>8,52</p><p>Scifact</p><p>9,07</p><p>9,57</p><p>9,77</p><p>9,3</p><p>9,1</p><p>9,17</p><p>Touche2020</p><p>38,78</p><p>30,41</p><p>32,24</p><p>33,06</p><p>37,96</p><p>33,67</p><p>TREC-COVID</p><p>92,4</p><p>98,4</p><p>98,2</p><p>93,8</p><p>99,6</p><p>97,4</p><p>MSMARCO</p><p>3,97</p><p>6,00</p><p>6,03</p><p>6,07</p><p>5,47</p><p>6,11</p><p>CQADupstack (Durchschnitt)</p><p>5,47</p><p>6,32</p><p>6,87</p><p>5.89</p><p>6,22</p><p>6,16</p><p><strong>Tabelle 2</strong>: Bewertungsrate pro Paare (Datensatz, Reranker), berechnet anhand der 10 am häufigsten abgerufenen/neu geordneten Dokumente</p><p><strong>Aus Tabelle 2</strong> sehen wir, mit Ausnahme von <code>TREC-COVID</code> (&gt;90 % Abdeckung), <code>DBPedia</code> (~65 %), <code>Touche2020</code> und <code>nfcorpus</code> (~35 %), dass die Mehrheit der Datensätze eine Labeling-Rate zwischen 5 % und etwas mehr als 10 % nach dem Abrufen oder Reranking aufweist. Das heißt nicht, dass alle diese unmarkierten Dokumente relevant sind, aber es könnte ein Teilbereich davon geben, insbesondere diejenigen, die an oberster Stelle stehen, die positiv sein könnten.</p><p>Mit dem Aufkommen von auf allgemeine Anweisungen abgestimmten Sprachmodellen haben wir ein neues leistungsfähiges Tool, das die Beurteilung der Relevanz potenziell automatisieren kann. Diese Methoden sind in der Regel viel zu rechenaufwändig, um online für die Suche verwendet zu werden, aber hier geht es uns um die Offline-Auswertung. Im Folgenden verwenden wir sie, um die Hinweise darauf zu untersuchen, dass einige der BEIR-Datensätze unter oberflächlichen Markups leiden.</p><p>Zur weiteren Untersuchung dieser Hypothese haben wir uns entschlossen, uns auf MSMARCO zu konzentrieren und einen Teilbereich von 100 Abfragen zusammen mit den fünf (mit Cohere v2) höchsten Reranking-Dokumenten auszuwählen, die derzeit nicht als relevant markiert sind. Wir haben zwei verschiedene Bewertungsansätze verfolgt: Zunächst haben wir einen sorgfältig abgestimmten Prompt (mehr dazu in einem späteren Beitrag) verwendet, um das kürzlich veröffentlichte <a href="https://huggingface.co/microsoft/Phi-3-mini-4k-instruct">Phi-3-mini-4k-Modell</a> darauf vorzubereiten, die Relevanz (oder Nicht-Relevanz) eines Dokuments für die Abfrage vorherzusagen. Parallel dazu wurden diese Tickets auch manuell gekennzeichnet, um auch die Übereinstimmungsrate zwischen dem LLM-Ausgang und der menschlichen Bewertung zu ermitteln. Insgesamt können wir die folgenden beiden Schlüsse ziehen:</p><ul><li><p>Die Übereinstimmungsrate zwischen den LLM-Reaktionen und den menschlichen Beurteilungen lag bei knapp 80 %, was als Ausgangspunkt in diese Richtung durchaus gut erscheint.</p></li><li><p>In 57,6 % der Fälle (nach menschlicher Beurteilung) erwiesen sich die zurückgegebenen Dokumente tatsächlich als relevant für die Abfrage. Anders ausgedrückt: Bei 100 Abfragen werden 107 Dokumente als relevant eingestuft, aber es gibt mindestens 0,576 x 5 x 100 = 288 zusätzliche Dokumente, die tatsächlich relevant sind!</p></li></ul><p>Hier einige Beispiele aus dem Datensatz <code>MSMARCO</code>/<code>dev</code>, die die Abfrage, das annotierte positive Dokument (aus <code>qrels</code>) und ein falsch negatives Dokument aufgrund unvollständiger Auszeichnung enthalten:</p><p>Beispiel 1:</p>{
  "query":
    {
        "_id": 155234,
        "text": "do bigger tires affect gas mileage"
    },
  "positive_doc":
    {
        "_id": 502713,
        "text": "Tire Width versus Gas Mileage. Tire width is one of the only tire size factors that can influence gas mileage in a positive way. For example, a narrow tire will have less wind resistance, rolling resistance, and weight; thus increasing gas mileage.",
    },
    "negative_doc":
    {
        "_id": 7073658,
        "text": "Tire Size and Width Influences Gas Mileage. There are two things to consider when thinking about tires and their effect on gas mileage; one is wind resistance, and the other is rolling resistance. When a car is driving at higher speeds, it experiences higher wind resistance; this means lower fuel economy."
    }
}
<p>Beispiel 2:</p>{
  "query":
    {
        "_id": 300674,
        "text": "how many years did william bradford serve as governor of plymouth colony?"
    },
  "positive_doc":
    {
        "_id": 7067032,
        "text": "http://en.wikipedia.org/wiki/William_Bradford_(Plymouth_Colony_governor) William Bradford (c.1590 \u00e2\u0080\u0093 1657) was an English Separatist leader in Leiden, Holland and in Plymouth Colony was a signatory to the Mayflower Compact. He served as Plymouth Colony Governor five times covering about thirty years between 1621 and 1657."
    },
    "negative_doc":
    {
        "_id": 2495763,
        "text": "William Bradford was the governor of Plymouth Colony for 30 years. The colony was founded by people called Puritans. They were some of the first people from England to settle in what is now the United States. Bradford helped make Plymouth the first lasting colony in New England."
    }
}
<p>Die manuelle Auswertung solcher spezifischer Abfragen ist eine allgemein nützliche Methode, um die Suchqualität zu verstehen und quantitative Messgrößen wie nDCG@10 zu ergänzen. Bei einem repräsentativen Abfrageset, das Sie immer ausführen, wenn Sie Änderungen an der Suche vornehmen, erhalten Sie wichtige qualitative Informationen darüber, wie sich die Leistung verändert, die in den Statistiken nicht sichtbar sind. Beispielsweise erhalten Sie viel mehr Einblick in die falschen Ergebnisse Ihrer Suche: Sie erkennen offensichtliche Fehler in den Suchergebnissen, Kategorien verwandter Fehler, wie z. B. die Fehlinterpretation fachspezifischer Terminologie und so weiter.</p><p>Unser Ergebnis stimmt mit einschlägigen Studien zur <code>MSMARCO</code> Bewertung der Suchrelevanz überein. Zum Beispiel folgen <a href="https://arxiv.org/pdf/2109.00062">Arabzadeh et al.</a> einem ähnlichen Verfahren, bei dem sie Crowdsourcing-Mitarbeiter für Präferenzurteile einsetzen: Sie zeigen unter anderem, dass in vielen Fällen die von den Reranking-Modulen zurückgegebenen Dokumente im Vergleich zu den Dokumenten in der MSMARCO- <code>qrels</code>-Datei bevorzugt werden. Ein weiterer Nachweis stammt von den Autoren des <a href="https://arxiv.org/pdf/2010.08191">RocketQA</a>-Rerankers, die berichten, dass mehr als 70 % der Reranking-Dokumente nach einer manuellen Überprüfung als relevant eingestuft wurden.</p><p> Aktualisierung – 9. September: Nach einer genauen Neubewertung des Datensatzes haben wir 15 weitere Fälle relevanter Dokumente identifiziert, wodurch sich die Gesamtzahl von 273 auf 288 erhöht hat</p><h2>Wichtigste Erkenntnisse und nächste Schritte</h2><ul><li><p>Das Streben nach besseren Referenzwerten ist ein nie endender Prozess, da diese für Benchmarking und Modellvergleiche von entscheidender Bedeutung sind. LLMs können in einigen Bewertungsbereichen helfen, wenn sie mit Vorsicht angewendet und mit den richtigen Anweisungen abgestimmt werden.</p></li><li><p>Allgemeiner gesagt: Da Benchmarks niemals perfekt sein werden, könnte es vorteilhaft sein, von einem reinen Wertevergleich zu robusteren Methoden überzugehen, die statistisch signifikante Unterschiede erfassen. Die Arbeit von <a href="https://arxiv.org/pdf/2109.00062">Arabzadeh et al.</a> liefert hierfür ein gutes Beispiel, da auf Grundlage der Ergebnisse 95-prozentige Konfidenzintervalle erstellt wurden, die signifikante (oder nicht signifikante) Unterschiede zwischen den verschiedenen Durchläufen aufzeigen Im beigefügten <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/evaluating-search-relevance-part-1/retrieve-and-rerank.ipynb">Notizbuch</a> stellen wir eine Implementierung von Konfidenzintervallen unter Verwendung von <a href="https://en.wikipedia.org/wiki/Bootstrapping_(statistics)">Bootstrapping</a> zur Verfügung.</p></li><li><p>Aus der Perspektive des Endnutzers ist es sinnvoll, bei der Betrachtung von Benchmark-Ergebnissen über die Aufgabenausrichtung nachzudenken. Für einen KI-Ingenieur, der beispielsweise eine RAG-Pipeline entwickelt und weiß, dass der typische Anwendungsfall das Zusammenführen mehrerer Informationen aus verschiedenen Quellen beinhaltet, wäre es sinnvoller, die Leistung seines Retrieval-Modells anhand von Multi-Hop-QA-Datensätzen wie HotpotQA zu bewerten, anstatt den globalen Durchschnitt des gesamten BEIR-Benchmarks zu verwenden.</p></li></ul><p>Im <a href="https://www.elastic.co/search-labs/blog/evaluating-search-relevance-part-2">nächsten Blogbeitrag</a> werden wir uns eingehender mit der Verwendung von Phi-3 als LLM-as-a-Judge und der Optimierung zur Vorhersage der Relevanz befassen.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/evaluating-search-relevance-part-1</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/evaluating-search-relevance-part-1</guid>
    <category><![CDATA[ML-Forschung]]></category>
    <category><![CDATA[Python]]></category>
    <dc:creator><![CDATA[Thanos Papaoikonomou,Thomas Veasey]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9d9912c8d4187096/6a1704f5b0367d30e672bc17/54a6e5197f5721b36fc65f27387d29803ed35589-721x420.jpg" length="0" type="image/jpeg"/>
    <pubDate>Tue, 16 Jul 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[KI-Plagiat: Plagiatserkennung mit Elasticsearch]]></title>
    <description><![CDATA[Hier erfahren Sie, wie Sie mithilfe von Elasticsearch auf KI-Plagiate prüfen können. Der Schwerpunkt liegt dabei auf Anwendungsfällen mit NLP-Modellen und Vektorsuche.]]></description>
    <content:encoded><![CDATA[<p>Plagiat kann <strong>direkt</strong> sein, indem Teile oder der gesamte Inhalt kopiert werden, oder <strong>paraphrasiert</strong>, wobei das Werk des Autors durch die Änderung einiger Wörter oder Formulierungen umformuliert wird.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb6e139760e56ec98/6a171147dc55de0ad2e00edf/5d7073187fda829438aeec8d3a1194a5bea2ba57-1440x347.png" alt="" /><p>Es besteht ein Unterschied zwischen Inspiration und Paraphrasierung. Es ist möglich, einen Inhalt zu lesen, sich davon inspirieren zu lassen und die Idee dann mit eigenen Worten weiterzuentwickeln, selbst wenn man zu einem ähnlichen Schluss kommt.</p><p>Obwohl Plagiat schon seit langer Zeit ein Diskussionsthema ist, hat die beschleunigte Produktion und Veröffentlichung von Inhalten dazu geführt, dass es weiterhin relevant ist und eine ständige Herausforderung darstellt.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc2af1678cb04506b/6a171149cf4f251c2ab2d257/a0b9a98d729db09dae0a79315c001e6763c12704-1400x1016.png" alt="" /><p>Diese Herausforderung beschränkt sich nicht auf Bücher, wissenschaftliche Forschung oder Gerichtsdokumente, wo häufig Plagiatsprüfungen durchgeführt werden. Dies kann sich auch auf Zeitungen und sogar soziale Medien erstrecken.</p><p>Wie kann Plagiat angesichts der Informationsfülle und des einfachen Zugangs zu Veröffentlichungen effektiv und in großem Umfang bekämpft werden?</p><p>Universitäten, Regierungsstellen und Unternehmen nutzen verschiedene Instrumente, aber während eine einfache <a href="https://www.elastic.co/search-labs/lexical-and-semantic-search-with-elasticsearch">lexikalische Suche</a> direkte Plagiate effektiv aufdecken kann, liegt die größte Herausforderung in der Identifizierung <strong>paraphrasierter Inhalte.</strong></p><h2>Plagiatserkennung mit generativer KI</h2><p>Mit generativer KI entsteht eine neue Herausforderung. Gilt von KI generierter Inhalt als Plagiat, wenn er kopiert wird?</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8a1ed1b6fa3f1a56/6a17114b4a531bc40736aa69/9345b28d6d27c37469bc38e823c41780b4eabfe5-1440x875.png" alt="" /><p>In den <a href="https://openai.com/policies/terms-of-use">Nutzungsbedingungen</a> von <a href="https://openai.com/">OpenAI ist beispielsweise festgelegt, dass OpenAI keine Urheberrechte an Inhalten beansprucht, die von der API für Benutzer generiert werden.</a> In diesem Fall können die Nutzer ihrer generativen KI die generierten Inhalte nach Belieben ohne Quellenangabe verwenden.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbc2e08ab5b808e38/6a17114dab7f086cbadb9f93/e1f415f69a247666f02ddc81468944920c874cd7-968x814.png" alt="" /><p>Die Akzeptanz des Einsatzes von generativer KI zur Effizienzsteigerung ist jedoch weiterhin Gegenstand von Diskussionen.</p><p>In dem Bestreben, einen Beitrag zur Plagiatserkennung zu leisten, entwickelte OpenAI ein <a href="https://huggingface.co/roberta-base-openai-detector">Erkennungsmodell</a> , räumte aber später ein, dass dessen Genauigkeit nicht ausreichend hoch sei.</p><p><em>„Wir glauben, dass diese Genauigkeit für eine eigenständige Erkennung nicht ausreicht und mit metadatenbasierten Ansätzen, menschlichem Urteilsvermögen und Aufklärung der Öffentlichkeit kombiniert werden muss, um effektiver zu sein.“</em></p><p>Die Herausforderung besteht weiterhin; allerdings stehen mit der Verfügbarkeit von mehr Werkzeugen nun auch mehr Möglichkeiten zur Erkennung von Plagiaten zur Verfügung, selbst bei paraphrasierten und KI-generierten Inhalten.</p><h2>Plagiatserkennung mit Elasticsearch</h2><p>In Anbetracht dessen untersuchen wir in diesem Blog einen weiteren Anwendungsfall von Natural Language Processing (NLP)-Modellen und Vector Search, nämlich die Plagiatserkennung, die über die Metadatensuche hinausgeht.</p><p>Dies wird anhand von <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/plagiarism-detection-with-elasticsearch/plagiarism_detection_es.ipynb">Python-Beispielen</a> demonstriert, wobei wir einen <a href="https://sbert.net/datasets/emnlp2016-2018.json">Datensatz</a> von <a href="https://www.sbert.net/">SentenceTransformers</a> verwenden, der Artikel zum Thema NLP enthält. Wir prüfen die Abstracts auf Plagiat, indem wir eine „semantische Textähnlichkeit“ durchführen und dabei „Abstract“-Einbettungen berücksichtigen, die mit einem zuvor in Elasticsearch importierten <a href="https://huggingface.co/sentence-transformers/all-mpnet-base-v2">Text-Einbettungsmodell</a> generiert wurden. Um zudem KI-generierte Inhalte – also KI-Plagiate – zu identifizieren, wurde ein von OpenAI entwickeltes <a href="https://huggingface.co/roberta-base-openai-detector">NLP-Modell</a> in Elasticsearch importiert.</p><p>Die folgende Abbildung veranschaulicht den Datenfluss:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0464f88d3ed12070/6a17114fab7f084905db9f97/1ad89c98a2f42a497548ca3947749bad54ec1172-1440x880.png" alt="" /><p>Während der <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ingest.html">Ingest-Pipeline</a> mit einem <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/inference-processor.html">Inferenzprozessor</a> wird der Absatz „abstract“ einem 768-dimensionalen Vektor, dem „abstract_vector.predicted_value“, zugeordnet.</p><p>Abbildung:</p>"abstract_vector.predicted_value": { # Inference results field
"type": "dense_vector", 
"dims": 768, # model embedding_size
"index": "true", 
"similarity": "dot_product" # When indexing vectors for approximate kNN search, you need to specify the similarity function for comparing the vectors.
<p>Die Ähnlichkeit zwischen Vektordarstellungen wird mithilfe einer Vektorähnlichkeitsmetrik gemessen, die über den <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/dense-vector.html#dense-vector-params">Parameter</a> „Ähnlichkeit“ definiert ist.</p><p><a href="https://en.wikipedia.org/wiki/Cosine_similarity">Der Kosinus</a> ist das Standardähnlichkeitsmaß und wird wie folgt berechnet: '(1 + cosine(query, vector)) / 2'. Sofern Sie die ursprünglichen Vektoren nicht erhalten müssen und sie nicht im Voraus normalisieren können, ist die effizienteste Methode zur Durchführung der Kosinusähnlichkeit die Normalisierung aller Vektoren auf Einheitslänge. Dadurch wird vermieden, dass während der Suche zusätzliche Vektorlängenberechnungen durchgeführt werden, stattdessen wird 'dot_product' verwendet.</p><p>In derselben Pipeline erkennt ein weiterer Inferenzprozessor, der das <a href="https://huggingface.co/roberta-base-openai-detector">Textklassifizierungsmodell</a> enthält, ob der Inhalt „Real“ (wahrscheinlich von Menschen geschrieben) oder „Fake“ (wahrscheinlich von einer KI geschrieben) ist, und fügt jedem Dokument den Wert „openai-detector.predicted_value“ hinzu.</p><p>Ingest-Pipeline:</p>client.ingest.put_pipeline( 
    id="plagiarism-checker-pipeline",
    processors = [
    {
      "inference": { #for ml models - to infer against the data that is being ingested in the pipeline
        "model_id": "roberta-base-openai-detector", #text classification model id
        "target_field": "openai-detector", # Target field for the inference results
        "field_map": { #Maps the document field names to the known field names of the model.
        "abstract": "text_field" # Field matching our configured trained model input. 
        }
      }
    },
    {
      "inference": {
        "model_id": "sentence-transformers__all-mpnet-base-v2", #text embedding model id
        "target_field": "abstract_vector", # Target field for the inference results
        "field_map": {
        "abstract": "text_field" # Field matching our configured trained model input. Typically for NLP models, the field name is text_field.
        }
      }
    }
    
  ]
)
<p>Zum Zeitpunkt der Abfrage wird dasselbe Text-Embedding-Modell auch verwendet, um die Vektordarstellung der Abfrage 'model_text' in einem 'query_vector_builder'-Objekt zu generieren.</p><p>Bei einer k-Nächste-Nachbarn-Suche (kNN) werden die k nächsten Vektoren zum Anfragevektor anhand der Ähnlichkeitsmetrik ermittelt.</p><p>Der _Score jedes Dokuments wird aus der Ähnlichkeit abgeleitet, wobei ein höherer Score einer höheren Platzierung entspricht. Das bedeutet, dass das Dokument semantisch ähnlicher ist. Als Ergebnis geben wir drei Möglichkeiten aus: Bei einem Wert &gt; 0,9 gehen wir von „hoher Ähnlichkeit“ aus; bei einem Wert &lt; 0,7 von „geringer Ähnlichkeit“; andernfalls von „mittlerer Ähnlichkeit“. Je nach Anwendungsfall haben Sie die Möglichkeit, unterschiedliche Schwellenwerte festzulegen, um zu bestimmen, ab welchem _score-Wert ein Plagiat vorliegt oder nicht.</p><p>Zusätzlich wird eine Textklassifizierung durchgeführt, um auch KI-generierte Elemente in der Textanfrage zu erkennen.</p><p>Abfrage:</p>from elasticsearch import Elasticsearch
from elasticsearch.client import MlClient

#duplicated text - direct plagiarism test

model_text = 'Understanding and reasoning about cooking recipes is a fruitful research direction towards enabling machines to interpret procedural text. In this work, we introduce RecipeQA, a dataset for multimodal comprehension of cooking recipes. It comprises of approximately 20K instructional recipes with multiple modalities such as titles, descriptions and aligned set of images. With over 36K automatically generated question-answer pairs, we design a set of comprehension and reasoning tasks that require joint understanding of images and text, capturing the temporal flow of events and making sense of procedural knowledge. Our preliminary results indicate that RecipeQA will serve as a challenging test bed and an ideal benchmark for evaluating machine comprehension systems. The data and leaderboard are available at http://hucvl.github.io/recipeqa.'

response = client.search(index='plagiarism-checker', size=1,
    knn={
        "field": "abstract_vector.predicted_value",
        "k": 9,
        "num_candidates": 974,
        "query_vector_builder": { #The 'all-mpnet-base-v2' model is also employed to generate the vector representation of the query in a 'query_vector_builder' object.
            "text_embedding": {
                "model_id": "sentence-transformers__all-mpnet-base-v2",
                "model_text": model_text
            }
        }
    }
)

for hit in response['hits']['hits']:
    score = hit['_score']
    title = hit['_source']['title']
    abstract = hit['_source']['abstract']
    openai = hit['_source']['openai-detector']['predicted_value']
    url = hit['_source']['url']

    if score &gt; 0.9:
        print(f"\nHigh similarity detected! This might be plagiarism.")
        print(f"\nMost similar document: '{title}'\n\nAbstract: {abstract}\n\nurl: {url}\n\nScore:{score}\n\n")

        if openai == 'Fake':
            print("This document may have been created by AI.\n")

    elif score &lt; 0.7:
        print(f"\nLow similarity detected. This might not be plagiarism.")

        if openai == 'Fake':
            print("This document may have been created by AI.\n")

    else:
        print(f"\nModerate similarity detected.")
        print(f"\nMost similar document: '{title}'\n\nAbstract: {abstract}\n\nurl: {url}\n\nScore:{score}\n\n")

        if openai == 'Fake':
            print("This document may have been created by AI.\n")

ml_client = MlClient(client)

model_id = 'roberta-base-openai-detector' #open ai text classification model

document = [
    {
        "text_field": model_text
    }
]

ml_response = ml_client.infer_trained_model(model_id=model_id, docs=document)

predicted_value = ml_response['inference_results'][0]['predicted_value']

if predicted_value == 'Fake':
    print("\nNote: The text query you entered may have been generated by AI.\n")
<p>Ausgabe:</p>High similarity detected! This might be plagiarism.

Most similar document: 'RecipeQA: A Challenge Dataset for Multimodal Comprehension of Cooking Recipes'

Abstract: Understanding and reasoning about cooking recipes is a fruitful research direction towards enabling machines to interpret procedural text. In this work, we introduce RecipeQA, a dataset for multimodal comprehension of cooking recipes. It comprises of approximately 20K instructional recipes with multiple modalities such as titles, descriptions and aligned set of images. With over 36K automatically generated question-answer pairs, we design a set of comprehension and reasoning tasks that require joint understanding of images and text, capturing the temporal flow of events and making sense of procedural knowledge. Our preliminary results indicate that RecipeQA will serve as a challenging test bed and an ideal benchmark for evaluating machine comprehension systems. The data and leaderboard are available at[ http://hucvl.github.io/recipeqa](http://hucvl.github.io/recipeqa).

url:[http://aclweb.org/anthology/D18-1166](http://aclweb.org/anthology/D18-1166)

Score:1.0
<p>In diesem Beispiel wurde ein Plagiat festgestellt, nachdem einer der „abstract“-Werte aus unserem Datensatz als Textabfrage „model_text“ verwendet wurde. Der Ähnlichkeitswert beträgt 1,0, was auf eine hohe Ähnlichkeit hinweist – <strong>direktes Plagiat</strong>. Die vektorisierte Anfrage und das Dokument wurden erwartungsgemäß nicht als KI-generierter Inhalt erkannt.</p><p>Abfrage:</p>#similar text - paraphrase plagiarism test 

model_text = 'Comprehending and deducing information from culinary instructions represents a promising avenue for research aimed at empowering artificial intelligence to decipher step-by-step text. In this study, we present CuisineInquiry, a database for the multifaceted understanding of cooking guidelines. It encompasses a substantial number of informative recipes featuring various elements such as headings, explanations, and a matched assortment of visuals. Utilizing an extensive set of automatically crafted question-answer pairings, we formulate a series of tasks focusing on understanding and logic that necessitate a combined interpretation of visuals and written content. This involves capturing the sequential progression of events and extracting meaning from procedural expertise. Our initial findings suggest that CuisineInquiry is poised to function as a demanding experimental platform.'
<p>Ausgabe:</p>High similarity detected! This might be plagiarism.

Most similar document: 'RecipeQA: A Challenge Dataset for Multimodal Comprehension of Cooking Recipes'

Abstract: Understanding and reasoning about cooking recipes is a fruitful research direction towards enabling machines to interpret procedural text. In this work, we introduce RecipeQA, a dataset for multimodal comprehension of cooking recipes. It comprises of approximately 20K instructional recipes with multiple modalities such as titles, descriptions and aligned set of images. With over 36K automatically generated question-answer pairs, we design a set of comprehension and reasoning tasks that require joint understanding of images and text, capturing the temporal flow of events and making sense of procedural knowledge. Our preliminary results indicate that RecipeQA will serve as a challenging test bed and an ideal benchmark for evaluating machine comprehension systems. The data and leaderboard are available at[ http://hucvl.github.io/recipeqa](http://hucvl.github.io/recipeqa).

url:[http://aclweb.org/anthology/D18-1166](http://aclweb.org/anthology/D18-1166)

Score:0.9302529

Note: The text query you entered may have been generated by AI.
<p>Durch die Aktualisierung der Textabfrage 'model_text' mit einem KI-generierten Text, der die gleiche Botschaft vermittelt und gleichzeitig die Wiederholung ähnlicher Wörter minimiert, war die festgestellte Ähnlichkeit immer noch hoch, aber der Wert betrug 0,9302529 statt 1,0 — <strong>Paraphrasierungsplagiat</strong>. Es wurde auch erwartet, dass diese von einer KI generierte Anfrage erkannt werden würde.</p><p>Schließlich ergab die Betrachtung der Textanfrage 'model_text' als Text über Elasticsearch, der kein Abstract eines dieser Dokumente ist, eine festgestellte Ähnlichkeit von 0,68991005, was gemäß den betrachteten Schwellenwerten auf eine geringe Ähnlichkeit hinweist.</p><p>Abfrage:</p>#different text - not a plagiarism

model_text = 'Elasticsearch provides near real-time search and analytics for all types of data.'
<p>Ausgabe:</p>Low similarity detected. This might not be plagiarism.
<p>Obwohl Plagiate in der von der KI generierten Textanfrage sowie in Fällen von Paraphrasierung und direkt kopierten Inhalten korrekt identifiziert wurden, erfordert die Navigation durch das Feld der Plagiatserkennung die Berücksichtigung verschiedener Aspekte.</p><p>Im Kontext der Erkennung von KI-generierten Inhalten haben wir ein Modell untersucht, das einen wertvollen Beitrag leistet. Es ist jedoch entscheidend, die systembedingten Einschränkungen der eigenständigen Erkennung zu erkennen, weshalb andere Methoden zur Steigerung der Genauigkeit einbezogen werden müssen.</p><p>Die durch die Wahl der Text-Embedding-Modelle bedingte Variabilität ist ein weiterer zu berücksichtigender Aspekt. Unterschiedliche Modelle, die mit verschiedenen Datensätzen trainiert wurden, führen zu unterschiedlichen Ähnlichkeitsgraden, was die Bedeutung der generierten Text-Embeddings unterstreicht.</p><p>Schließlich haben wir in diesen Beispielen die Zusammenfassung des Dokuments verwendet. Die Plagiatserkennung betrifft jedoch häufig große Dokumente, weshalb es unerlässlich ist, die Herausforderung der Textlänge anzugehen. Häufig überschreitet der Text das Token-Limit eines Modells, sodass er vor dem Erstellen der Einbettungen in Abschnitte unterteilt werden muss. Ein <a href="https://www.elastic.co/guide/en/elasticsearch/reference/8.11/knn-search.html#nested-knn-search">praktischer Ansatz</a> zur Bewältigung dieses Problems besteht in der Verwendung verschachtelter Strukturen mit dense_vector.</p><h2>Fazit</h2><p>In diesem Blog haben wir die Herausforderungen bei der Erkennung von Plagiaten, insbesondere bei paraphrasierten und KI-generierten Inhalten, erörtert und gezeigt, wie semantische Textähnlichkeit und Textklassifizierung zu diesem Zweck eingesetzt werden können.</p><p>Durch die Kombination dieser Methoden haben wir ein Beispiel für die Plagiatserkennung geliefert, bei dem wir erfolgreich KI-generierte Inhalte, direkte und paraphrasierte Plagiate identifiziert haben.</p><p>Das Hauptziel war die Einrichtung eines Filtersystems, das die Erkennung vereinfacht, die menschliche Beurteilung bleibt jedoch für die Validierung unerlässlich.</p><p>Wenn Sie mehr über semantische Textähnlichkeit und NLP erfahren möchten, empfehlen wir Ihnen, auch diese Links zu besuchen:</p><ul><li><p><a href="https://www.elastic.co/what-is/semantic-search">Was ist die semantische Suche?</a></p></li><li><p><a href="https://www.elastic.co/what-is/natural-language-processing">Was ist natürliche Sprachverarbeitung (NLP)?</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/lexical-and-semantic-search-with-elasticsearch">Lexikalische und semantische Suche mit Elasticsearch</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/chunking-via-ingest-pipelines">Die Aufteilung großer Dokumente über Ingest-Pipelines und verschachtelte Vektoren ermöglicht eine einfache Passagensuche.</a></p></li></ul>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/ai-plagiarism-checker-with-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/ai-plagiarism-checker-with-elasticsearch</guid>
    <category><![CDATA[Vektordatenbank]]></category>
    <category><![CDATA[Python]]></category>
    <dc:creator><![CDATA[Priscilla Parodi]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt68a5bc2434a9b03b/6a1711510e2e49a09641a22a/83e05cd4f81799fbb7b7950ed87600e825ec81e9-1024x1024.png" length="0" type="image/png"/>
    <pubDate>Tue, 19 Dec 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Lexikalische und semantische Suche mit Elasticsearch]]></title>
    <description><![CDATA[In diesem Blogbeitrag werden wir verschiedene Ansätze zur Informationsbeschaffung mit Elasticsearch untersuchen, wobei wir uns auf die lexikalische und semantische Suche konzentrieren.]]></description>
    <content:encoded><![CDATA[<p>Die Suche ist der Prozess, bei dem die relevantesten Informationen auf der Grundlage Ihrer Suchanfrage oder kombinierter Suchanfragen gefunden werden; relevante Suchergebnisse sind Dokumente, die am besten zu diesen Suchanfragen passen. Obwohl die Suche mit verschiedenen Herausforderungen und Methoden verbunden ist, bleibt das letztendliche Ziel dasselbe: <strong>die bestmögliche Antwort auf Ihre Frage zu finden</strong>.</p><p>Mit Blick auf dieses Ziel werden wir in diesem Blogbeitrag verschiedene Ansätze zur Informationsbeschaffung mit Elasticsearch untersuchen, wobei der Schwerpunkt auf der Textsuche liegt: <strong>lexikalische und semantische Suche.</strong></p><h2>Voraussetzungen</h2><p>Um dies zu erreichen, werden wir Python-Beispiele bereitstellen, die verschiedene Suchszenarien auf einem <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/lexical-and-semantic-search-with-elasticsearch/products-ecommerce.json">Datensatz</a> demonstrieren, der zur Simulation von E-Commerce-Produktinformationen generiert wurde.</p><p>Dieser Datensatz enthält über 2.500 Produkte, jedes mit einer Beschreibung. Diese Produkte sind in 76 verschiedene Produktkategorien unterteilt, wobei jede Kategorie eine unterschiedliche Anzahl von Produkten enthält, wie unten dargestellt:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt34415151c00ced2b/6a17d8710b0bed6c9bdd342c/4104466050f3024b6bcaf382da2a702650f62227-1440x708.png" alt="" /><p><em>Treemap-Visualisierung – die 22 wichtigsten Werte von category.keyword (Produktkategorien)</em></p><p>Für die Einrichtung benötigen Sie:</p><ul><li><p>Python 3.6 oder höher</p></li><li><p>Der <a href="https://www.elastic.co/guide/en/elasticsearch/client/python-api/current/index.html">Elastic Python-Client</a></p></li><li><p>Elastic 8.8-Bereitstellung oder höher, mit 8 GB Arbeitsspeicher-Knoten für maschinelles Lernen</p></li><li><p>Das <a href="https://www.elastic.co/guide/en/machine-learning/8.9/ml-nlp-elser.html">Elastic Learned Sparse EncodeR-</a> Modell, das in Elastic vorinstalliert und auf Ihrem Bereitstellungsserver gestartet ist, ist bereits vorhanden.</p></li></ul><p>Wir werden Elastic Cloud verwenden, eine <a href="https://cloud.elastic.co/registration?onboarding_token=vectorsearch&amp;cta=cloud-registration&amp;tech=trial&amp;plcmt=article%20content&amp;pg=search-labs">kostenlose Testversion ist verfügbar</a>.</p><p>Neben den in diesem Blogbeitrag bereitgestellten Suchanfragen führt Sie ein <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/lexical-and-semantic-search-with-elasticsearch/ecommerce_dense_sparse_project.ipynb">Python-Notebook</a> durch die folgenden Prozesse:</p><ul><li><p>Stellen Sie mithilfe des Python-Clients eine Verbindung zu unserer Elastic-Bereitstellung her.</p></li><li><p>Laden Sie ein Text-Embedding-Modell in den Elasticsearch-Cluster.</p></li><li><p>Erstellen Sie einen Index mit Zuordnungen zum Indizieren von Merkmalsvektoren und dichten Vektoren.</p></li><li><p>Erstellen Sie eine Ingest-Pipeline mit Inferenzprozessoren für Text-Embedding und Text-Expansion.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0e95406ece8f08d9/6a17d8731d1b83d32e93e2f4/54a9a490a3b0cf1b9c2228bee8eddd3f566bd435-1418x1102.png" alt="" /><h2>Lexikalische Suche – spärliche Suche</h2><p>Die klassische Methode, mit der Elasticsearch Dokumente anhand einer Textabfrage nach Relevanz ordnet, verwendet die Lucene-Implementierung des <a href="https://en.wikipedia.org/wiki/Okapi_BM25">BM25-</a> Modells, eines <strong>Sparse-Modells für die lexikalische Suche</strong>. Diese Methode folgt dem traditionellen Ansatz der Textsuche und sucht nach exakten Übereinstimmungen der Begriffe.</p><p>Um diese Suche zu ermöglichen, wandelt Elasticsearch die Daten <strong>der Textfelder</strong> durch eine Textanalyse in ein durchsuchbares Format um.</p><p><strong>Die Textanalyse</strong> wird von einem<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/analyzer-anatomy.html"> Analysator</a> durchgeführt, einem Regelsatz, der den Prozess der Extraktion relevanter Token für die Suche steuert. Ein Analysator muss genau einen<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-tokenizers.html"> Tokenizer</a> haben. Der Tokenizer empfängt einen Zeichenstrom und zerlegt ihn in einzelne Tokens (in der Regel einzelne Wörter), wie im folgenden Beispiel:</p><h3>String-Tokenisierung für die lexikalische Suche</h3>#Performs text analysis on a string and returns the resulting tokens.

# Define the text to be analyzed
text = "Comfortable furniture for a large balcony"

# Define the analyze request
request_body = {
  "analyzer": "standard",
  "text": text
}

# Perform the analyze request
response = client.indices.analyze(analyzer=request_body["analyzer"], text=request_body["text"])

# Extract and display the analyzed tokens
tokens = [token["token"] for token in response["tokens"]]
print("Analyzed Tokens:", tokens)
<p>Ausgabe</p>Analyzed Tokens: ['comfortable', 'furniture', 'for', 'a', 'large', 'balcony']
<p>In diesem Beispiel verwenden wir den <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-standard-analyzer.html">Standardanalysator</a> , der für die meisten Anwendungsfälle gut geeignet ist, da er eine auf englischer Grammatik basierende Tokenisierung bietet. Die Tokenisierung ermöglicht den Abgleich einzelner Begriffe, wobei jedes Token weiterhin wörtlich abgeglichen wird.</p><p>Wenn Sie Ihr Sucherlebnis personalisieren möchten, können Sie einen anderen<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-analyzers.html"> integrierten Analysator</a> auswählen. Beispielsweise wird durch die Aktualisierung des Codes zur Verwendung des <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-stop-analyzer.html">Stoppwortanalysators</a> der Text an jedem Nicht-Buchstaben-Zeichen in Tokens zerlegt, wobei die Entfernung von Stoppwörtern unterstützt wird.</p>...
# Define the analyze request
request_body = {
  "analyzer": "stop",
  "text": text
}
...
<p>Ausgabe</p>Analyzed Tokens: ['comfortable', 'furniture', 'large', 'balcony']
<p>Wenn die integrierten Analysatoren Ihre Anforderungen nicht erfüllen, können Sie einen <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-custom-analyzer.html">benutzerdefinierten Analysator</a> erstellen, der die entsprechende Kombination aus null oder mehr <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-charfilters.html">Zeichenfiltern</a>, einem <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-tokenizers.html">Tokenizer</a> und null oder mehr <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-tokenfilters.html">Tokenfiltern</a> verwendet.</p>"analyzer":  {

  "my_analyzer": {

    "type": "custom", #For custom analyzers, use a type of custom or omit the type parameter.

    "tokenizer": "standard", #Built-in or customized tokenizer

    "filter": ["lowercase", "synonym"] #Built-in or customized token filters
  }
}
<p>Im obigen Beispiel, das einen Tokenizer und Tokenfilter kombiniert, wird der Text vom <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-lowercase-tokenfilter.html">Kleinbuchstabenfilter</a> in Kleinbuchstaben umgewandelt, bevor er vom <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-synonym-tokenfilter.html#:~:text=Elasticsearch%20will%20use%20the%20token,applied%20to%20the%20synonym%20entries.">Synonym-Tokenfilter</a> verarbeitet wird.</p><h2>Lexikalische Übereinstimmung</h2><p><a href="https://www.elastic.co/blog/practical-bm25-part-2-the-bm25-algorithm-and-its-variables">BM25</a> wird die Relevanz von Dokumenten für eine bestimmte Suchanfrage anhand der Häufigkeit der Begriffe und ihrer Wichtigkeit messen.</p><p>Der unten stehende Code führt eine <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-match-query.html">Match</a> -Abfrage durch, bei der bis zu zwei Dokumente unter Berücksichtigung der Werte des Feldes <em>"description"</em> aus dem Index <em>"ecommerce-search"</em> und der Suchanfrage <strong>"</strong><em><strong>Comfortable furniture for a large Balcony</strong></em><strong>"</strong> gesucht werden.</p><p>Durch eine Verfeinerung der Kriterien, nach denen ein Dokument als Treffer für diese Abfrage in Betracht gezogen werden soll, kann die Genauigkeit verbessert werden. Genauere Ergebnisse gehen jedoch mit einer geringeren Toleranz gegenüber Abweichungen einher.</p># BM25

response = client.search(size=2,
index="ecommerce-search",
query= {
  "match": {
    "description" : {  
      "query": "Comfortable furniture for a large balcony",
      "analyzer": "stop"
    }
  }
}
)

hits = response['hits']['hits']

if not hits:
  print("No matches found")

else:
  for hit in hits:
    score = hit['_score']
    product = hit['_source']['product']
    category = hit['_source']['category']
    description = hit['_source']['description']
    print(f"\nScore: {score}\nProduct: {product}\nCategory: {category}\nDescription: {description}\n")
<p>Ausgabe</p>Score: 15.607948
Product: Barbie Dreamhouse
Category: Toys
Description: is a classic Barbie playset with multiple rooms, furniture, a large balcony, a pool, and accessories. It allows kids to create their dream Barbie world.

Score: 9.137739
Product: Comfortable Rocking Chair
Category: Indoor Furniture
Description: enjoy relaxing moments with this comfortable rocking chair. Its smooth motion and cushioned seat make it an ideal piece of furniture for unwinding.
<p>Die Analyse der Ergebnisse zeigt, dass das relevanteste Produkt das „ <em>Barbie Dreamhouse</em> “ in der Kategorie „ <em>Spielzeug</em> “ ist. Dessen Beschreibung ist besonders relevant, da sie die Begriffe „ <em>Möbel</em> “, „ <em>groß“</em> und <em>„Balkon</em> “ enthält. Es ist das einzige Produkt, dessen Beschreibung drei Begriffe enthält, die mit der Suchanfrage übereinstimmen, und es ist außerdem das einzige, dessen Beschreibung den Begriff <em>„Balkon“</em> enthält.</p><p>Das zweitwichtigste Produkt ist ein „ <em>Komfortabler Schaukelstuhl</em> “, der unter „ <em>Innenmöbel</em> “ kategorisiert ist und dessen Beschreibung die Begriffe „ <em>bequem</em> “ und „ <em>Möbel</em> “ enthält. Nur 3 Produkte im Datensatz entsprechen mindestens 2 Begriffen dieser Suchanfrage, dieses Produkt ist eines davon.</p><p><em>Der Begriff „Komfortabel“</em> taucht in der Beschreibung von 105 Produkten auf, der <em>Begriff „Möbel“</em> in der Beschreibung von 4 Produkten aus 4 verschiedenen Kategorien: <em>Spielzeug</em>, <em>Möbel für den Innenbereich, Möbel für den Außenbereich und „Zubehör und Spielzeug für Hunde und Katzen“.</em></p><p>Wie Sie sehen konnten, ist das relevanteste Produkt im Hinblick auf die Suchanfrage ein Spielzeug und das zweitrelevanteste Produkt sind Möbel für den Innenbereich. Wenn Sie detaillierte Informationen über die Berechnung der Punktzahl wünschen, um zu verstehen, warum diese Dokumente übereinstimmen, können Sie den Parameter <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-explain.html"><em>explain</em></a> __query auf true setzen.</p><p>Obwohl beide Ergebnisse die relevantesten sind, sowohl hinsichtlich der Anzahl der Dokumente als auch des Vorkommens der Begriffe in diesem Datensatz, besteht die Absicht hinter der Anfrage "<em>Bequeme Möbel für einen großen Balkon</em>" darin, Möbel für einen tatsächlich großen Balkon zu finden, wobei unter anderem Spielzeug und Möbel für den Innenbereich ausgeschlossen werden.</p><p>Die lexikalische Suche ist relativ <strong>einfach und schnell</strong>, hat aber ihre Grenzen, da es nicht immer möglich ist, alle möglichen Begriffe und Synonyme zu kennen, ohne unbedingt die Absicht und die Suchanfragen des Benutzers zu kennen. Ein häufiges Phänomen beim Gebrauch natürlicher Sprache ist <strong>die Diskrepanz im Wortschatz</strong>. <a href="https://dl.acm.org/doi/abs/10.1145/32206.32212">Untersuchungen</a> zeigen, dass verschiedene Personen (Experten auf demselben Gebiet) im Durchschnitt in <strong>80 % der Fälle</strong> ein und dasselbe Objekt unterschiedlich benennen.</p><p>Diese Einschränkungen veranlassen uns, nach anderen Bewertungsmodellen zu suchen, die semantisches Wissen einbeziehen. Transformerbasierte Modelle, die sich durch ihre Fähigkeit auszeichnen, sequentielle Eingabe-Tokens wie natürliche Sprache zu verarbeiten, erfassen die zugrundeliegende Bedeutung Ihrer Suche, indem sie mathematische Darstellungen sowohl von Dokumenten als auch von Suchanfragen berücksichtigen. Dies ermöglicht eine dichte, kontextsensitive Vektordarstellung von Texten und ist somit die Grundlage für <strong>die semantische Suche</strong>, eine verfeinerte Methode zum Auffinden relevanter Inhalte.</p><h2>Semantische Suche – dichte Suche</h2><p>In diesem Zusammenhang wird nach der Umwandlung Ihrer Daten in aussagekräftige Vektorwerte <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/knn-search.html">der k-nächste-Nachbarn-Suchalgorithmus (kNN)</a> verwendet, um Vektordarstellungen in einem Datensatz zu finden, die einem Anfragevektor am ähnlichsten sind. Elasticsearch unterstützt zwei Methoden für die kNN-Suche: <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/knn-search.html#exact-knn">die exakte Brute-Force-kNN-Suche</a> und <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/knn-search.html#approximate-knn">die approximative kNN-Suche</a>, auch bekannt als ANN.</p><p>Brute-Force-kNN garantiert zwar genaue Ergebnisse, skaliert aber nicht gut mit großen Datensätzen. Approximatives kNN findet effizient annähernd nächste Nachbarn, indem es etwas Genauigkeit für eine verbesserte Leistung opfert.</p><p>Durch die Unterstützung von kNN-Suche und dichten Vektorindizes durch Lucene nutzt Elasticsearch den Hierarchical Navigable Small World (HNSW)-Algorithmus, der eine starke Suchleistung über eine Vielzahl von <a href="http://ann-benchmarks.com/">ann-Benchmark-Datensätzen</a> hinweg demonstriert. Eine approximative kNN-Suche kann in Python mithilfe des unten stehenden Beispielcodes durchgeführt werden.</p><h3>Semantische Suche mit approximativem kNN</h3># KNN - approximate kNN

response = client.search(index='ecommerce-search', size=2,
knn={
  "field": "description_vector.predicted_value",
  "k": 50, # Number of nearest neighbors to return as top hits.
#The optimal value of k is dependent on the data. It can vary in different scenarios.

  "num_candidates": 500, # Number of nearest neighbor candidates to consider per shard.

#Increasing num_candidates tends to improve the accuracy of the final k results.

  "query_vector_builder": { # Object indicating how to build a query_vector. kNN search enables you to perform semantic search by using a previously deployed text embedding model, the steps for this process are demonstrated in the Python notebook.
    "text_embedding": { 
      "model_id": "sentence-transformers__all-mpnet-base-v2", # Text embedding model id
      "model_text": "Comfortable furniture for a large balcony" # Query
    }
  }
}
)

for hit in response['hits']['hits']:
        
  score = hit['_score']
  product = hit['_source']['product']
  category = hit['_source']['category']
  description = hit['_source']['description']
  print(f"\nScore: {score}\nProduct: {product}\nCategory: {category}\nDescription: {description}\n")
<p>Dieser Codeblock verwendet Elasticsearchs kNN, um bis zu zwei Produkte mit einer Beschreibung zurückzugeben, die der vektorisierten Anfrage (query_vector_build) von "<em>Komfortable Möbel für einen großen Balkon</em>" ähnlich ist, wobei die Einbettungen des Feldes "<em>description</em> " im Produktdatensatz berücksichtigt werden.</p><p>Die Produkt-Embeddings wurden zuvor in einer Ingest-Pipeline mit einem Inferenzprozessor generiert, der das Text-Embedding-Modell <em>"</em><a href="https://huggingface.co/sentence-transformers/all-mpnet-base-v2"><em>all-mpnet-base-v2</em></a><em>"</em> enthielt, um aus den in die Pipeline aufgenommenen Daten Inferenz zu ziehen.</p><p>Dieses Modell wurde auf Grundlage der Auswertung vortrainierter Modelle mithilfe von <em>"</em><a href="https://github.com/UKPLab/sentence-transformers/blob/master/docs/package_reference/sentence_transformer/evaluation.md"><em>sentence_transformers.evaluation</em></a><em>"</em> ausgewählt. wobei verschiedene Klassen verwendet werden, um ein Modell während des Trainings zu beurteilen. Das Modell „all-mpnet-base-v2“ erzielte die beste durchschnittliche Leistung gemäß dem <a href="https://www.sbert.net/docs/pretrained_models.html">Sentence-Transformers-Ranking</a> und sicherte sich auch eine günstige Position im <a href="https://huggingface.co/spaces/mteb/leaderboard">Massive Text Embedding Benchmark (MTEB)</a> Leaderboard. Das Modell basiert auf dem vortrainierten<a href="https://huggingface.co/microsoft/mpnet-base"> microsoft/mpnet-base-</a> Modell und wurde anhand eines Datensatzes mit 1 Milliarde Satzpaaren feinabgestimmt. Es bildet Sätze auf einen 768-dimensionalen dichten Vektorraum ab.</p><p>Alternativ stehen viele andere Modelle zur Verfügung, die verwendet werden können, insbesondere solche, die speziell auf Ihre domänenspezifischen Daten abgestimmt sind.</p><p>Ausgabe</p>Score: 0.79207325
Product: Patio Sofa Set with Ottoman
Category: Outdoor Furniture
Description: is a versatile and comfortable patio sofa set, including a sofa, ottoman, and coffee table, great for outdoor lounging.

Score: 0.7836937
Product: Patio Sofa Set with Canopy
Category: Outdoor Furniture
Description: is a luxurious and comfortable patio sofa set with a canopy, providing shade and style for outdoor lounging.
<p><em>Das Ergebnis kann je nach gewähltem Modell,</em> <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/knn-search.html#knn-search-filter-example"><em>Filtern</em></a> <em>und</em> <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/knn-search.html#tune-approximate-knn-for-speed-accuracy"><em>ungefährem kNN-Tuning</em></a>variieren<em>.</em></p><p>Die kNN-Suchergebnisse befinden sich beide in der Kategorie „ <em>Gartenmöbel</em> “, obwohl das Wort „ <em>Garten</em> “ nicht explizit in der Suchanfrage erwähnt wurde. Dies unterstreicht die Bedeutung des semantischen Verständnisses im Kontext.</p><p>Die dichte Vektorsuche bietet mehrere Vorteile:</p><ul><li><p>Aktivierung der semantischen Suche</p></li><li><p>Skalierbarkeit zur Verarbeitung sehr großer Datensätze</p></li><li><p>Flexibilität im Umgang mit einer breiten Palette von Datentypen</p></li></ul><p><strong>Die Suche in dichten Vektoren birgt jedoch auch ihre eigenen Herausforderungen</strong>:</p><ul><li><p>Auswahl des richtigen Einbettungsmodells für Ihren Anwendungsfall</p></li><li><p>Sobald ein Modell ausgewählt ist, kann es notwendig sein, das Modell feinabzustimmen, um die Leistung auf einem domänenspezifischen Datensatz zu optimieren. Dieser Prozess erfordert die Einbeziehung von Domänenexperten.</p></li><li><p>Darüber hinaus kann die Indizierung hochdimensionaler Vektoren rechenaufwändig sein.</p></li></ul><h2>Semantische Suche – gelernter spärlicher Abruf</h2><p>Lassen Sie uns einen alternativen Ansatz erkunden: das gelernte spärliche Retrieval, eine andere Möglichkeit, semantische Suchen durchzuführen.</p><p>Als Sparse-Modell nutzt es den Lucene-basierten invertierten Index von Elasticsearch, der von jahrzehntelangen Optimierungen profitiert. Dieser Ansatz geht jedoch über das einfache Hinzufügen von Synonymen mit lexikalischen Bewertungsfunktionen wie BM25 hinaus. Stattdessen werden gelernte Assoziationen mithilfe eines tiefergehenden sprachlichen Wissens einbezogen, um die Relevanz zu optimieren.</p><p>Durch die Erweiterung der Suchanfragen um relevante Begriffe, die in der ursprünglichen Anfrage nicht enthalten sind,<strong>verbessert der Elastic Learned</strong> <a href="https://www.elastic.co/guide/en/machine-learning/current/ml-nlp-elser.html">Sparse Encoder die Sparse Vector</a> Embeddings, wie Sie im folgenden Beispiel sehen können.</p><h3>Suche nach dünnbesetzten Vektoren mit elastischem, gelerntem, dünnbesetztem Encoder</h3># Elastic Learned Sparse Encoder

response = client.search(index='ecommerce-search', size=2,
query={
  "text_expansion": {
    "ml.tokens": {
      "model_id":"elser_model",
      "model_text":"Comfortable furniture for a large balcony"                
    }
  }
}
)

for hit in response['hits']['hits']:

  score = hit['_score']
  product = hit['_source']['product']
  category = hit['_source']['category']
  description = hit['_source']['description']
  print(f"\nScore: {score}\nProduct: {product}\nCategory: {category}\nDescription: {description}\n")
<p>Ausgabe</p>Score: 14.405318
Product: Garden Lounge Set with Side Table
Category: Garden Furniture
Description: is a comfortable and stylish garden lounge set, including a sofa, chairs, and a side table for outdoor relaxation.

Score: 14.281318
Product: Rattan Patio Conversation Set
Category: Outdoor Furniture
Description: is a stylish and comfortable outdoor furniture set, including a sofa, two chairs, and a coffee table, all made of durable rattan material.
<p>Zu den Ergebnissen gehört in diesem Fall die Kategorie „ <em>Gartenmöbel</em> “, die Produkte anbietet, die den „ <em>Outdoor-Möbeln</em> “ sehr ähnlich sind.</p><p>Durch die Analyse von "ml.tokens", Aus dem Feld "rank_features", das die von Learned Sparse Retrieval generierten Token enthält, wird deutlich, dass sich unter den verschiedenen generierten Token Begriffe befinden, die zwar nicht Teil der Suchanfrage sind, aber dennoch eine relevante Bedeutung haben, wie zum Beispiel "<em>relax</em>" (bequem), "<em>sofa</em>" (Möbel) und "<em>outdoor</em>" (Balkon).</p><p>Das Bild unten hebt einige dieser Begriffe zusammen mit der Suchanfrage hervor, sowohl mit als auch ohne Begriffserweiterung.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7e869985347b19a7/6a17d875e31791dc2c2d56a1/dd86607fce6137d843a3ec002390eaa988b432f9-1440x502.png" alt="" /><p>Wie bereits erwähnt, ermöglicht dieses Modell eine kontextsensitive Suche und trägt dazu bei, das Problem der Diskrepanz zwischen Vokabular und Definition zu mindern, während es gleichzeitig besser interpretierbare Ergebnisse liefert. Es kann sogar dichte Vektormodelle übertreffen, wenn kein domänenspezifisches Nachtraining angewendet wird.</p><h2>Hybridsuche: Relevante Ergebnisse durch die Kombination von lexikalischer und semantischer Suche</h2><p>Wenn es um die Suche geht, gibt es keine Universallösung. Jede dieser Abrufmethoden hat ihre Stärken, aber auch ihre Herausforderungen. Je nach Anwendungsfall kann sich die beste Option ändern. Oftmals ergänzen sich die besten Ergebnisse verschiedener Abrufmethoden. Um die Relevanz zu erhöhen, werden wir daher versuchen, die Stärken der einzelnen Methoden zu kombinieren.</p><p>Es gibt verschiedene Möglichkeiten, eine <strong>hybride Suche</strong> zu implementieren, darunter die lineare Kombination, die Gewichtung jedes Ergebnisses und die reziproke Rangfusion (RRF), bei der die Angabe eines Gewichts nicht erforderlich ist.</p><h3>Elasticsearch: Das Beste aus beiden Welten mit lexikalischer und semantischer Suche</h3># BM25 + Elastic Learned Sparse Encoder (Linear Combination)

response = client.search(index='ecommerce-search', size=2,

query= {
  "bool": {
    "should": [
    {
      "match": {
        "description" : {  
          "query": "A dining table and comfortable chairs for a large balcony",
          "boost": 1
        }
      }
    },                   
    {
      "text_expansion": {
        "ml.tokens": {
          "model_id": "elser_model",
          "model_text": "A dining table and comfortable chairs for a large balcony",
          "boost": 1
        }
      }
     }
    ]
  }
}
)

# The boost value is 1 for the text expansion and match query. This means that the relevance score of the results of these queries are not boosted. You can specify a boost value to give a weight to each score in the sum. The scores will be calculated as: score = boost value * match_score + boost value * text_expansion_score

for hit in response['hits']['hits']:

  score = hit['_score']
  product = hit['_source']['product']
  category = hit['_source']['category']
  description = hit['_source']['description']
  print(f"\nScore: {score}\nProduct: {product}\nCategory: {category}\nDescription: {description}\n")
<p>In diesem Code haben wir eine hybride Suche mit zwei Abfragen durchgeführt, die den Wert "<em>Ein Esstisch und bequeme Stühle für einen großen Balkon</em>" hatten. Anstatt "<em>Möbel</em>" als Suchbegriff zu verwenden, geben wir an, wonach wir suchen, und beide Suchanfragen berücksichtigen die gleichen Feldwerte, "Beschreibung". Die Rangfolge wird durch eine lineare Kombination der BM25- und ELSER-Werte bei gleicher Gewichtung ermittelt.</p><p>Ausgabe</p>Score: 31.628141
Product: Garden Dining Set with Swivel Rockers
Category: Garden Furniture
Description: is a functional and comfortable garden dining set, including a table and chairs with swivel rockers for easy movement.

Score: 31.334227
Product: Garden Dining Set with Swivel Chairs
Category: Garden Furniture
Description: is a functional and comfortable garden dining set, including a table and chairs with swivel seats for convenience.
<p>Im folgenden Code verwenden wir denselben Wert für die Abfrage, kombinieren aber die Ergebnisse von BM25 (Abfrageparameter) und kNN (KNN-Parameter) mithilfe der Methode der reziproken Rangfusion, um die Dokumente zu kombinieren und zu ordnen.</p># BM25 + KNN (RRF)

response = client.search(index='ecommerce-search', size=2,
query={
  "bool": {
    "should": [
    {
      "match": {
        "description": {
        "query": "A dining table and comfortable chairs for a large balcony"
        }
      }
    }
    ]
  }
},
knn={
  "field": "description_vector.predicted_value",
  "k": 50,
  "num_candidates": 500,
  "query_vector_builder": {
    "text_embedding": {
      "model_id": "sentence-transformers__all-mpnet-base-v2",
      "model_text": "A dining table and comfortable chairs for a large balcony"
    }
  }
},
rank={
  "rrf": { # Reciprocal rank fusion
    "window_size": 50, # This value determines the size of the individual result sets per query.
    "rank_constant": 20 # This value determines how much influence documents in individual result sets per query have over the final ranked result set.
  }
}
)

for hit in response['hits']['hits']:
        
  rank = hit['_rank']
  category = hit['_source']['category']
  product = hit['_source']['product']
  description = hit['_source']['description']
  print(f"\nRank: {rank}\nProduct: {product}\nCategory: {category}\nDescription: {description}\n")
<p><em>Die RRF-Funktionalität befindet sich in der technischen Vorschauphase. Die Syntax wird sich voraussichtlich vor der allgemeinen Verfügbarkeit ändern.</em></p><p>Ausgabe</p>Rank: 1
Product: Patio Dining Set with Bench
Category: Outdoor Furniture
Description: is a spacious and functional patio dining set, including a dining table, chairs, and a bench for additional seating.

Rank: 2
Product: Garden Dining Set with Swivel Chairs
Category: Garden Furniture
Description: is a functional and comfortable garden dining set, including a table and chairs with swivel seats for convenience.
<p>Hier könnten wir auch andere Felder und Werte verwenden; einige Beispiele hierfür finden Sie im <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/lexical-and-semantic-search-with-elasticsearch/ecommerce_dense_sparse_project.ipynb">Python-Notebook</a>.</p><p>Wie Sie sehen, bietet Elasticsearch das Beste aus beiden Welten: die traditionelle lexikalische Suche und die Vektorsuche, egal ob spärlich oder dicht, um Ihr Ziel zu erreichen <strong>und die bestmögliche Antwort auf Ihre Frage zu finden.</strong></p><p>Wenn Sie mehr über die hier erwähnten Ansätze erfahren möchten, können Ihnen diese Blogs hilfreich sein:</p><ul><li><p><a href="https://www.elastic.co/blog/improving-information-retrieval-elastic-stack-hybrid">Verbesserung der Informationssuche im Elastic Stack: Hybride Suche</a></p></li><li><p><a href="https://www.elastic.co/blog/vector-search-elasticsearch-rationale">Vektorsuche in Elasticsearch: Die Gründe für das Design</a></p></li><li><p><a href="https://www.elastic.co/blog/lexical-ai-powered-search-elastic-vector-database">Wie Sie die Vorteile der lexikalischen und KI-gestützten Suche mit der Vektordatenbank von Elastic optimal nutzen können</a></p></li><li><p><a href="https://www.elastic.co/blog/may-2023-launch-sparse-encoder-ai-model">Wir stellen den Elastic Learned Sparse Encoder vor: Elastics KI-Modell für die semantische Suche</a></p></li><li><p><a href="https://www.elastic.co/blog/may-2023-launch-information-retrieval-elasticsearch-ai-model">Verbesserung der Informationswiedergewinnung im Elastic Stack: Vorstellung des Elastic Learned Sparse Encoder, unseres neuen Retrieval-Modells</a></p></li></ul><p>Elasticsearch bietet eine Vektordatenbank sowie alle Werkzeuge, die Sie zum Erstellen von Vektorsuchen benötigen:</p><ul><li><p>Elasticsearch <a href="https://www.elastic.co/elasticsearch/vector-database">-Vektordatenbank</a></p></li><li><p>Anwendungsfälle <a href="https://www.elastic.co/enterprise-search/vector-search">für die Vektorsuche</a> mit Elastic</p></li></ul><h2>Fazit</h2><p>In diesem Blogbeitrag haben wir verschiedene Ansätze zur Informationsbeschaffung mit Elasticsearch untersucht, wobei wir uns insbesondere auf die Text-, lexikalische und semantische Suche konzentriert haben. Um dies zu veranschaulichen, haben wir Python-Beispiele bereitgestellt, die verschiedene Suchszenarien anhand eines Datensatzes mit E-Commerce-Produktinformationen aufzeigen.</p><p>Wir haben die klassische lexikalische Suche mit BM25 überprüft und ihre Vorteile und Herausforderungen, wie z. B. Vokabeldiskrepanz, diskutiert. Wir haben die Bedeutung der Einbeziehung semantischen Wissens zur Überwindung dieses Problems hervorgehoben. Darüber hinaus haben wir die dichte Vektorsuche erörtert, die eine semantische Suche ermöglicht, und die mit dieser Abrufmethode verbundenen Herausforderungen behandelt, einschließlich des Rechenaufwands bei der Indizierung hochdimensionaler Vektoren.</p><p>Andererseits haben wir erwähnt, dass sich dünnbesetzte Vektoren außergewöhnlich gut komprimieren lassen. Daher haben wir den Learned Sparse Encoder von Elastic besprochen, der Suchanfragen erweitert, um relevante Begriffe einzubeziehen, die in der ursprünglichen Anfrage nicht enthalten sind.</p><p>Bei der Suche gibt es keine Universallösung. Jede Abrufmethode hat ihre Stärken und Schwächen. Deshalb haben wir auch das Konzept der hybriden Suche erörtert.</p><p>Wie Sie sehen konnten, bietet Elasticsearch die Vorteile beider Welten: die traditionelle lexikalische Suche und die Vektorsuche!</p><p>Bereit loszulegen? Sehen Sie sich das verfügbare <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/lexical-and-semantic-search-with-elasticsearch/ecommerce_dense_sparse_project.ipynb">Python-Notebook</a> an und starten Sie eine <a href="https://cloud.elastic.co/registration?onboarding_token=vectorsearch&amp;cta=cloud-registration&amp;tech=trial&amp;plcmt=article%20content&amp;pg=search-labs">kostenlose Testphase von Elastic Cloud</a>.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/lexical-and-semantic-search-with-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/lexical-and-semantic-search-with-elasticsearch</guid>
    <category><![CDATA[Vektordatenbank]]></category>
    <category><![CDATA[Python]]></category>
    <category><![CDATA[Abfragesprachen]]></category>
    <dc:creator><![CDATA[Priscilla Parodi]]></dc:creator>
    <pubDate>Tue, 03 Oct 2023 00:00:00 GMT</pubDate>
  </item>
  </channel>
</rss>