Produkt

BM25 in der Praxis – Teil 1: Auswirkungen von Shards auf das Relevanzscoring in Elasticsearch

Dies ist der erste Beitrag aus der dreiteiligen Reihe BM25 in der Praxis zum Thema Ähnlichkeitsbewertung (Relevanz). Der nächste Beitrag ist unten verlinkt.

Hintergrund

In Elasticsearch 5.0 sind wir zu Okapi BM25 als unserem standardmäßigen Ähnlichkeitsalgorithmus gewechselt, der zur Bewertung von Ergebnissen in Bezug auf eine Abfrage verwendet wird. Ich werde in diesem Blog nicht zu sehr auf BM25 im Vergleich zu alternativen Messgrößen eingehen, aber wenn Sie eine Einführung in die theoretische Begründung von BM25 wünschen, können Sie sich die Präsentation BM25 Demystified von Elastic{ON} 2016 ansehen. Stattdessen werde ich die praktische Anwendung von BM25 für Sie behandeln (und hoffentlich entmystifizieren), einschließlich der verfügbaren Parameter und der Faktoren, die die Bewertung beeinflussen.

Bitte beachten Sie, dass sich dieser Blog weitgehend an diejenigen richtet, die eine Bewertung (Scoring) von Textdokumenten durchführen. Er konzentriert sich also vor allem darauf, unseren Nutzern, die suchen, zu helfen. Wenn Sie Logs oder Metriken Indexieren und Ergebnisse zurückgeben, die nach einer expliziten Metadaten- oder numerischen Reihenfolge wie dem Zeitstempel sortiert sind, dient dieser Blog möglicherweise eher dazu, Ihre Neugier zu stillen.

Verständnis der Auswirkungen von Shards auf das Scoring

Da ich hoffe, dass Sie zu Hause mitmachen, müssen wir zunächst verstehen, wie sich mehr als 1 Shard auf das Scoring auswirkt, da Elasticsearch standardmäßig 5 primäre Shards pro Index verwendet. Beginnen wir mit der Erstellung eines Index namens „people“. Die Einstellungen, die ich hier angebe, entsprechen den Standardwerten (und sind daher nicht zwingend zu definieren), aber ich werde sie dennoch explizit angeben, um sie zu Demonstrationszwecken zu verdeutlichen. Ich verwende hier Varianten meines Namens („Shane Connelly“), aber Sie können ihn gerne durch einen Namen Ihrer Wahl ersetzen, wenn Sie zu Hause mitmachen.

PUT people
{
  "settings": {
    "number_of_shards": 5,
    "index" : {
        "similarity" : {
          "default" : {
            "type" : "BM25"
          }
        }
    }
  }
}

Und jetzt fügen wir ein Dokument hinzu und suchen danach. Zunächst fügen wir nur meinen Vornamen hinzu:

PUT /people/_doc/1
{
  „title“: „Shane“
}
GET /people/_doc/_search
{
    "query": {
        "match": {
             „title“: „Shane“
         }
      }
}

Sie erhalten an dieser Stelle 1 Treffer mit einem Score von 0,2876821. Wir werden uns gleich damit befassen, wie dieser Score abgeleitet wird, aber schauen wir uns zunächst an, was passiert, wenn wir ein paar weitere Dokumente mit verschiedenen Varianten meines vollständigen Namens hinzufügen.

PUT /people/_doc/2
{
  „title“: „Shane C“
}
PUT /people/_doc/3
{
  „title“: „Shane Connelly“
}
PUT /people/_doc/4
{
  „title“: „Shane P Connelly“
}

Führen Sie nun dieselbe Suche erneut aus:

GET /people/_doc/_search
{
    "query": {
        "match": {
             „title“: „Shane“
         }
      }
}

An dieser Stelle sollten Sie tatsächlich 4 Treffer haben, aber wenn Sie sich die Bewertungen ansehen, werden Sie sich vielleicht wundern. Dokument 1 und 3 haben beide eine Bewertung von 0,2876821, aber Dokument 2 hat eine Bewertung von 0,19856805 und Dokument 4 eine Bewertung von 0,16853254. Das ist etwas, das neue Nutzer oft verwirrt. Dokument 2 und 3 sind sehr ähnlich – beide haben 2 Begriffe und beide stimmen mit „shane“ überein, aber Dokument 2 hat eine deutlich niedrigere Bewertung. Sie könnten annehmen, dass es einen Unterschied bei der Bewertung von „C“ gegenüber der Bewertung von „Connelly“ gibt, aber tatsächlich hat dies damit zu tun, wie die Dokumente in den Shards gelandet sind.

Zur Erinnerung: Elasticsearch unterteilt Dokumente in Shards, und jede Shard enthält eine Teilmenge der Daten. Wenn wir uns Folgendes ansehen:

GET /_cat/shards/people?v

Wenn Sie dies ausführen, werden Sie sehen, dass Shard 2 zwei Dokumente enthält, während die Shards 3 und 4 nur jeweils ein Dokument enthalten (die Shards 0 und 1 enthalten noch keine Dokumente). Dies bedeutet, dass die Gesamtzahl der Vorkommen des Begriffs „shane“ in diesen verschiedenen Shards unterschiedlich ist, was letztendlich den Unterschied bei den Bewertungen in diesem Fall verursacht. Standardmäßig berechnet Elasticsearch die Bewertungen auf Shard-Basis.

Nutzer beginnen damit, nur wenige Dokumente in ihren Index zu laden, und fragen sich: „Warum hat Dokument A eine höhere/niedrigere Bewertung als Dokument B?“ Manchmal liegt die Antwort darin, dass der Nutzer ein relativ hohes Verhältnis von Shards zu Dokumenten hat, sodass die Bewertungen über verschiedene Shards hinweg verzerrt sind. Es gibt einige Möglichkeiten, konsistentere Bewertungen über Shards hinweg zu erzielen:

  1. Je mehr Dokumente Sie in Ihren Index laden, desto normalisierter werden die Begriffsstatistiken Ihrer Shards. Bei einer ausreichenden Anzahl an Dokumenten werden Sie die geringfügigen Unterschiede in den Begriffsstatistiken und damit in der Bewertung der einzelnen Shards möglicherweise nicht bemerken.
  2. Sie könnten eine geringere Anzahl an Shards verwenden, um statistische Abweichungen bei den Worthäufigkeiten zu reduzieren. Wenn wir beispielsweise number_of_shards in den Index-Einstellungen auf 1 gesetzt hätten, würden wir sehr unterschiedliche Bewertungen erhalten. Wir würden Dokument 1 mit einer Bewertung von 0,13245322 sehen, Dokument 2 und 3 mit jeweils einer Bewertung von 0,105360515 und Dokument 4 mit einer Bewertung von 0,0874691. Es gibt einige Kompromisse bei der Verwendung unterschiedlicher Anzahlen von primären Shards, was in unserem Webinar zur quantitativen Cluster-Dimensionierung erörtert wird.
  3. Sie können ?suchen_type=dfs_query_then_fetch zur Anfrage hinzufügen, wodurch zunächst die verteilten Termfrequenzen (DFS = Distributed Frequency Suchen) erfasst und anschließend die Scores damit berechnet werden. Tatsächlich liefert dies denselben Score wie bei nur 1 Shard. Sehen Sie sich an, wie sich die Ergebnisse mit und ohne den Parameter „suchen_type“ unterscheiden:
    GET /people/_doc/_search?search_type=dfs_query_then_fetch
    {
        "query": {
            "match": {
                 „title“: „Shane“
             }
          }
    }
    Dies entspricht dem Ergebnis, wenn number_of_shards=1 festgelegt wurde. Sie fragen sich vielleicht: „Nun, wenn dies genauere Ergebnisse liefert, warum ist es dann nicht standardmäßig aktiviert?“ Die Antwort lautet, dass dies während der Verarbeitung einen zusätzlichen Rundgang erfordert, um alle Statistiken zu erfassen. Für einige Anwendungsfälle (bei denen die Genauigkeit der Bewertung nicht so wichtig ist wie die Geschwindigkeit) ist dieser Rundgang unnötig. Wenn zudem genügend Daten in den Shards vorhanden sind, können sich die Statistiken sehr stark annähern, wodurch auch Rundgänge überflüssig werden. Wenn Sie über genügend Daten verfügen, ist search_type=dfs_query_then_fetch meist nur dann erforderlich, wenn die Daten zwischen den Shards weiterhin ungleichmäßig verteilt sind, wie dies bei einigen benutzerdefinierten Routings der Fall ist.

OK, jetzt haben wir ein Verständnis dafür, wie sich Sharding auf unser Scoring auswirken kann (und wie man dies anpasst). Als Nächstes betrachten wir den BM25-Algorithmus und sehen uns an, wie verschiedene Variablen dabei eine Rolle spielen.

Setzen Sie diese Serie fort mit: Teil 2: Der BM25-Algorithmus und seine Variablen