Bild-Schulung-NiFi

Named Entity Recognition in Solr mit OpenNLP zur Anreicherung von Inhalten

Mit Machine Learning Entitäten erkennen

In einem vorherigen Blogbeitrag wurde beschrieben, wie Machine Learning im Enterprise Search Umfeld eingesetzt werden kann, um mehr Nutzen aus diesen Anwendungen ziehen zu können.

In diesem Blog-Beitrag wird nun darauf eingegangen, wie mit ausschließlicher Verwendung von Open Source Software Entitäten zur Indexierungszeit erkannt und extrahiert werden können, um Inhalte anzureichern und sie so besser auffindbar zu machen.

Technisch ist dies möglich mit Solr und der neuen OpenNLP-Integration, die seit der Version 7.3 verfügbar ist. Die technische Grundvoraussetzung, um dem nachfolgenden Schritt-für-Schritt Tutorial zu folgen, ist also Solr 7.3 oder neuer.

Um in Solr zur Indexierungszeit Named Entity Recognition (NER) betreiben zu können, wurde OpenNLP integriert, sodass innerhalb einer UpdateRequestProcessorChain die Inhalte analysiert werden und mittels Modellen entschlüsselt wird, welche Entitäten vorkommen. Diese Entitäten werden dann pro Art der Entität, um die es sich handelt (z.B. Personen, Orte oder Organisationen), in einem separaten Feld indexiert, um diese später verwenden zu können.

Doch bevor wir uns der Konfiguration der UpdateRequestProcessorChain widmen, folgen noch die weiteren Voraussetzungen, die erfüllt werden müssen, um OpenNLP innerhalb Solr verwenden zu können.

Maximale Größe von Konfigurationsdateien erhöhen

Wenn Solr im SolrCloud-Modus betrieben wird, ist die maximal zulässige Größe von Konfigurationsdateien anzupassen, die über den ZooKeeper an die entsprechenden Nodes verteilt werden. Per Default ist diese Größe 1MB und die Modelle, die zur Erkennung von Entitäten nötig sind, sind etwas größer.

Für Solr (alle Nodes einer SolrCloud benötigen diese Konfiguration) ist dies in der Datei solr.in.sh bzw. solr.in.cmd zu hinterlegen:

SOLR_OPTS=“$SOLR_OPTS -Djute.maxbuffer=50000000″

Für ZooKeeper ist folgende Zeile in der Konfiguration zu setzen:

jute.maxbuffer=50000000

Diese beiden Zeilen setzen die maximale Größe von Konfigurationsdateien auf 50MB.

OpenNLP-Bibliotheken zu Solr hinzufügen

Des Weiteren sind noch Bibliotheken in das Solr-Home Verzeichnis zu kopieren, um entsprechende Klassen nutzen zu können. Es sind insgesamt drei an der Zahl und alle befinden sich in der heruntergeladenen Solr-Distribution ($installDir ist das Verzeichnis, in das Solr entpackt wurde):

$installDir/contrib/analysis-extras/lib/opennlp-*.jar
$installDir/contrib/analysis-extras/lucene-libs/lucene-analyzers-opennlp-*.jar
$installDir/dist/solr-analysis-extras-*.jar

Diese drei Bibliotheken müssen in ein Verzeichnis namens lib im Solr-Home jeder Solr Node kopiert werden. So werden sie beim Starten von Solr geladen und können verwendet werden.

Solr starten und konfigurieren

Solr kann nun gestartet werden. Der Einfachheit halber wird hier eine SolrCloud mit einer Instanz und einem Embedded ZooKeeper verwendet:

bin/solr start -c

Die UpdateProcessors, die später für NER verwendet werden, benötigen für die Analyse des zu indexierenden Textes einen Feldtyp, der bereits zwei Modelle verwendet:

Ein Modell für das Tokenisieren des Textes und ein Modell für das Erkennen von Sätzen. Da wir später noch je ein Modell für das Erkennen von Personen, Organisationen und Orten benötigen, laden wir diese fünf Modelle in einem Schritt in unsere entsprechende Konfiguration hoch.

Diese kommen nicht mit Solr mit, da für das Abdecken aller Sprachen zahlreiche Modelle nötig wären, was die Größe des Solr-Downloads um ein Vielfaches vergrößern würde – ganz abgesehen davon, dass es leider nicht für jede Sprache bereits trainierte Modelle gibt.

Etwas ältere (aber kompatible) Modelle können auf der Seite von OpenNLP heruntergeladen werden: http://opennlp.sourceforge.net/models-1.5/

Folgende Modelle werden benötigt:

  • en-token.bin
  • en-sent.bin
  • en-ner-location.bin
  • en-ner-organization.bin
  • en-ner-person.bin

Natürlich ist es möglich, nicht nur im Englischen Entitäten zu erkennen und auch andere Arten von Entitäten als die erwähnten zu extrahieren. All dies ist eine Frage des Trainierens von entsprechenden Modellen.

Diese Modelle müssen nun über das ZooKeeper Command Line Interface (zkcli) in die Konfiguration von Solr geladen werden. Ausgehend von einer Konfiguration namens _default ist pro Modell ein Befehl folgender Art auszuführen:

./zkcli.sh -z localhost:9983 -cmd putfile /configs/_default/ en-token.bin ./models/en-token.bin

Sind alle Modelle hochgeladen, kann eine Collection opennlp mit der Konfiguration _default Ã¼ber Solr’s Startskript angelegt werden:

bin/solr create_collection -c opennlp -n _default

Was nun noch fehlt: Ein Feldtyp, der später von der UpdateRequestProcessorChain verwendet wird, und die UpdateRequestProcessorChain selbst.

Bei Verwendung eines Data-driven Schemas kann der Feldtyp über die Schema API einfach über einen curl-Request hinzugefügt werden:

curl -X POST -H 'Content-type:application/json' --data-binary
' {
  "add-field-type" : {
     "name":"opennlp-en-tokenization",
     "class":"solr.TextField",
     "positionIncrementGap":"100",
     "analyzer" : {
        "tokenizer":{
           "class":"solr.OpenNLPTokenizerFactory",
           "sentenceModel":"en-sent.bin",
           "tokenizerModel":"en-token.bin" }
     }
  }
} ' http://localhost:8983/solr/opennlp/schema

Jetzt folgt noch, die UpdateRequestProcessorChain einzutragen in die solrconfig.xml, eine Aktualisierung dieser Datei im ZooKeeper und das Erkennen und Extrahieren von Entitäten kann beginnen. Der entsprechende Teil in der solrconfig.xml ist folgendermaßen zu konfigurieren:

<updateRequestProcessorChain name="extract-entities">

      <processor class="solr.OpenNLPExtractNamedEntitiesUpdateProcessorFactory">

        <str name="modelFile">en-ner-person.bin</str>

        <str name="analyzerFieldType">opennlp-en-tokenization</str>

        <str name="source">text_txt</str>

        <str name="dest">people_ss</str>

       </processor>

      <processor class="solr.processor.OpenNLPExtractNamedEntitiesUpdateProcessorFactory">

        <str name="modelFile">en-ner-location.bin</str>

        <str name="analyzerFieldType">opennlp-en-tokenization</str>

        <str name="source">text_txt</str>

        <str name="dest">location_ss</str>

      </processor>

      <processor class="solr.OpenNLPExtractNamedEntitiesUpdateProcessorFactory">

        <str name="modelFile">en-ner-organization.bin</str>

        <str name="analyzerFieldType">opennlp-en-tokenization</str>

        <str name="source">text_txt</str>

        <str name="dest">organization_ss</str>

      </processor>

      <processor class="solr.LogUpdateProcessorFactory" />

      <processor class="solr.RunUpdateProcessorFactory" />

     </updateRequestProcessorChain>

Hinweis: Soll diese Verarbeitungskette generell bei allen zu indexierenden Dokumenten durchlaufen werden, ist diese noch im UpdateHandler zu verankern. In diesem Beispiel wird die UpdateRequestProcessorChain beim Indexierungsrequest selbst referenziert werden, sodass dieser Schritt für dieses Beispiel nicht nötig ist.

Nach dem Hochladen der aktualisierten Datei in den ZooKeeper, ist ein Reload der Collection nötig, um diese Änderungen für Solr bemerkbar zu machen: http://localhost:8983/solr/admin/collections?action=RELOAD&name=opennlp

Und nun ist alles bereit, um Entitäten aus zu indexierenden Inhalten zu erkennen und in die dafür vorgesehenen Felder zu extrahieren. Die UpdateRequestProcessorChain ist so gestaltet, dass sie ein Input-Feld erwartet (text_txt) und den darin enthaltenen Text analysiert, Personen erkennt und diese in das Feld people_ss kopiert, Orte erkennt und diese in das Feld location_ss kopiert sowie Organisationen erkennt und in das Feld orgnization_ss erkennt. Für jede der Entitäten liegt ein Modell in der Konfiguration vor, sodass nun Inhalte zum Test indexiert werden können.

Inhalte indexieren

Nachfolgender Request schickt zwei Dokumente an Solr. Der Inhalt des Feldes text_txt ist hierbei jeweils der Beginn des Wikipedia-Artikels des aktuellen Präsidenten und seines Vorgängers.

curl -X POST

'http://localhost:8983/solr/opennlp/update/json?update.chain=extract-entities&commit=true'

  -H 'cache-control: no-cache'

  -H 'content-type: application/json'

  -d '[

  {

     "id" : "a",

   "text_txt" : "Donald John Trump (born June 14, 1946) is the 45th and current President of the United States. Before
entering politics, he was a businessman and television personality. Trump was born and raised in the New York
City borough of Queens. He received an economics degree from the Wharton School of the University of Pennsylvania
and was appointed president of his family'''s real estate business in 1971, renamed it The Trump Organization, and
expanded it from Queens and Brooklyn into Manhattan."

},

{

  "id" : "b",

  "text_txt" : "Barack Hussein Obama (born August 4, 1961) is an American politician who served as the 44th President
of the United States from January 20, 2009, to January 20, 2017. A member of the Democratic Party, he was the first
African American to be elected to the presidency and previously served as a United States Senator from Illinois (2005–2008)."

  }

]'

Um nun zu prüfen, welche Entitäten erkannt wurden, können wir folgenden Request an Solr schicken, der auf den Entitäten-Feldern facettiert und uns somit die erkannten Entitäten samt ihrer Anzahl in den beiden Texten zurückgibt:

http://localhost:8983/solr/opennlp/select?facet.field=location_ss&facet.field=organization_ss&facet.field=people_ss&facet=on&indent=on&omitHeader=true&q=id:(a OR b)&rows=0&wt=json

Solr´s Antwort:

{
   "response": {
     "numFound": 2,
     "start": 0,
     "docs": []
  },
  "facet_counts": {
    "facet_queries": {},
    "facet_fields": {
      "location_ss": [
        "United States",
        2,
        "Brooklyn",
        1,
        "Illinois",
        1,
        "Manhattan",
        1,
        "New York City",
        1,
        "Queens",
        1
      ],
      "organization_ss": [
        "Democratic Party",
        1,
        "Trump Organization",
        1,
        "University of Pennsylvania",
        1,
        "Wharton School",
        1
      ],
      "people_ss": [
        "Donald John Trump",
        1,
        "Hussein Obama",
        1
      ]
    },
    "facet_ranges": {},
    "facet_intervals": {},
    "facet_heatmaps": {}
  }
}

Und in der Antwort sehen wir zwar, dass nicht alles perfekt ist (der erste Vorname von Barack Obama ging beispielsweise verloren), aber ohne Trainieren eines eigenen Modells schon ein beachtlicher Stand erzielt werden konnte und sieben Orte, zwei Personen und vier Organisationen erkannt wurden. Es befinden sich keine falsch erkannten Entitäten in den Feldern, somit ist selbst mit den Bordmitteln für das Englische mit reinem Konfigurationsaufwand das Erkennen von Entitäten nicht nur möglich, sondern auch qualitativ mehr als in Ordnung!

Erkennen und Extrahieren von Entitäten ist also mit Solr und der OpenNLP-Integration möglich, um Inhalte zur Indexierungszeit automatisiert anzureichern und somit die Auffindbarkeit von Inhalten zu optimieren oder Grundlagen für mehr bzw. bessere Facetten oder eine Autosuggest-Funktion auf Entitäten-Basis zu ermöglichen.

Wenn Sie Interesse an Optimierungen Ihrer Suchlösung auf Basis des Gezeigten oder auch darüber hinaus mit Natural Language Processing haben, kontaktieren Sie uns jetzt!

Daniel Wrigley

Daniel Wrigley