Nutzer-Storys

Mühelose Textklassifizierung mit Elasticsearch

Elasticsearch wird normalerweise als Suchmaschine und Analytics Engine eingesetzt. Die Funktionen der Textmining-API sind dagegen weniger bekannt.

In diesem Artikel möchte ich Ihnen zeigen, wie Sie Texte mit Elasticsearch klassifizieren können. Ich habe einen Hintergrund in Computerlinguistik und mehrere Jahre Berufserfahrung als Freiberufler im Bereich Textmining. Daher hatte ich die Gelegenheit, die folgenden Techniken in verschiedenen Szenarien zu implementieren und zu testen.

Als ich Elasticsearch zum ersten Mal entdeckte, war ich fasziniert von der Benutzerfreundlichkeit, der Geschwindigkeit und den Konfigurationsoptionen. Mit Elasticsearch fand ich immer wieder einfachere Wege, meine Probleme zu lösen, für die ich normalerweise Tools und Techniken aus dem Gebiet der natürlichen Sprachverarbeitung (Natural Language Processing, NLP) eingesetzt hätte.

Irgendwann wurde mir klar, dass Elasticsearch wirklich viele Dinge vorkonfiguriert lösen kann, die ich ansonsten von Grund auf selbst implementiert hätte.

Die meisten NLP-Aufgaben beginnen mit einer herkömmlichen Vorverarbeitungspipeline:

  1. Daten sammeln
  2. Rohtext extrahieren
  3. Satzaufteilung
  4. Tokenisierung
  5. Normalisierung (Wortstammerkennung, Lemmatisierung)
  6. Stoppwörter entfernen
  7. Wortklassen markieren

Für manche NLP-Aufgaben, wie etwa syntaktisches Parsing, sind tiefe linguistische Analysen erforderlich.

Für diese Arten von Aufgaben stellt Elasticsearch keine ideale vorkonfigurierte Architektur und Datenstruktur bereit. Daher müssen wir für Aufgaben, die über die Tokenebene hinausgehen, eigene Plugins schreiben oder verwenden, die auf den kompletten Text zugreifen.

Für Aufgaben wie Klassifizierung, Clustering, Schlüsselwortextraktion, Ähnlichkeitsmessung usw. brauchen wir jedoch nur eine normalisierte und möglicherweise gewichtete Wortsammlung (Bag of Words) für das jeweilige Dokument.

Die Schritte 1 und 2 können wir mit dem Ingest Attachment Processor Plugin (Mapper Attachments Plugin vor Version 5.0) in Elasticsearch lösen.

Die Extraktion von Rohtext für diese Plugins basiert auf Apache Tika und unterstützt die meisten gängigen Datenformate (HTML, PDF, Word usw.).

Die Schritte 4 bis 6 lösen wir mit den vorkonfigurierten Sprach-Analyzern.

Beispielzuordnung:

  {
    "properties":{
       "content":{
          "type":"text",
          "analyzer":"german"
       }
    }
  }

Wenn ein Feld den Zuordnungstyp „text“ hat (vor 5.0: „analyzed string“), und der Analyzer eine der nativ von Elasticsearch unterstützten Sprachen verwendet, werden Tokenisierung, Wortstammerkennung und Stoppwörterentfernung beim Indexieren automatisch ausgeführt.

Sie brauchen als keinen benutzerdefinierten Code oder andere Tools, um beliebige von Apache Tika unterstützte Dokumente in eine Wortsammlung zu konvertieren.

Die Sprach-Analyzer können auch über die REST API aufgerufen werden, wenn Elasticsearch ausgeführt wird.

curl -XGET "http://localhost:9200/_analyze?analyzer=english" -d'
  {
   "text" : "This is a test."
  }'
  {
    "tokens":[
       {
          "token":"test",
          "start_offset":10,
          "end_offset":14,
          "type":"<ALPHANUM>",
          "position":3
       }
    ]
  }

Ohne Elasticsearch funktioniert der Ansatz in etwa wie folgt:

Der Text wird mit eigens entwickeltem Code gesammelt, und Dokumente werden manuell analysiert oder mit der Tika-Bibliothek, einer herkömmlichen NLP-Bibliothek oder einer API wie etwa NLTK, OpenNLP, Stanford NLP, Spacy oder einer anderen Software, die irgendwo in einer Forschungsabteilung entwickelt wurde. Die in Forschungsabteilungen entwickelten Tools sind jedoch oft nicht besonders hilfreich für Unternehmen. Häufig werden proprietäre Datenformate verwendet, die Tools müssen in der Befehlszeile kompiliert und ausgeführt werden, und das Ergebnis wird oft einfach nach stdout weitergeleitet. REST APIs sind eine Ausnahme.

Mit den Sprach-Analyzern von Elasticsearch reicht es dagegen, wenn Sie Ihre Zuordnung konfigurieren und die Daten indexieren. Die Vorverarbeitung erfolgt automatisch beim Indexieren.

Herkömmlicher Ansatz für die Textklassifizierung

Für die Textklassifizierung wird oft ein überwachter Machine-Learning-Ansatz verwendet. Dabei wird ein Modell mit einer Reihe von beschrifteten Dokumenten trainiert. Wir können uns eine minimale Darstellung dieses Modells als JSON-Dokument mit zwei Feldern vorstellen:

„content“ und „category“

Normalerweise werden für die Textklassifizierung Tools wie etwa SciKit Learn, Weka, NLTK, Apache Mahout eingesetzt.

Erstellen der Modelle

Die meisten Machine-Learning-Algorithmen benötigen eine Vektorraum-Darstellung der Daten. Der Merkmalsraum besteht oft beispielsweise aus den 10.000 wichtigsten Wörtern in einem bestimmten Datensatz. Wie können wir messen, wie wichtig ein Wort ist?

Normalerweise mit TF-IDF. Diese Formel wurde in den 1970er-Jahren erfunden. TF-IDF ist eine Gewichtung, die einen Begriff in einem bestimmten Dokument relativ zum Rest des Datensatzes bewertet. Begriffe mit hohem TF-IDF-Wert in einem Dokument sind besonders wichtige Schlüsselwörter, und das jeweilige Dokument unterscheidet sich durch dieses Wort von allen anderen Dokumenten.

Die Schlüsselwörter mit den höchsten TF-IDF-Werten in einer Gruppe von Dokumenten können beispielsweise ein Thema darstellen. Bei der Textklassifizierung wird oft ein Merkmalsraum aus den n Wörtern mit den höchsten TF-IDF-Werten gebildet.

Jedes Dokument wird zu einem Merkmalsvektor konvertiert, und anschließend wird mit allen Trainingsinstanzen für sämtlich Klassen/Kategorien ein Modell erstellt. Danach können neue Dokumente anhand dieses Modells klassifiziert werden. Daher müssen wir das Dokument in einen Merkmalsvektor konvertieren, um anschließend Ähnlichkeiten berechnen zu können. Das Dokument wird mit der Kategorie mit der höchsten Punktzahl beschriftet.

Textklassifizierung mit Elasticsearch

Alle oben genannten Schritte können mit Elasticsearch (oder Lucene) viel einfacher gelöst werden.

Dazu reichen vier Schritte:

  1. Zuordnungen konfigurieren ("content" : "text", "category" : "keyword")
  2. Dokumente indexieren
  3. More Like This-Abfrage (MLT) ausführen
  4. Treffer aus der Abfrage mit einem kleinen Skript nach Punktzahl aggregieren
PUT sample
  POST sample/document/_mapping
  {
    "properties":{
       "content":{
          "type":"text",
          "analyzer":"english"
       },
       "category":{
          "type":"text",
          "analyzer":"english",
          "fields":{
             "raw":{
                "type":"keyword"
             }
          }
       }
    }
  }
  POST sample/document/1
  {
    "category":"Apple (Fruit)",
    "content":"Granny Smith, Royal Gala, Golden Delicious and Pink Lady are just a few of the thousands of different kinds of apple that are grown around the world! You can make dried apple rings at home - ask an adult to help you take out the core, thinly slice the apple and bake the rings in the oven at a low heat."
  }
  POST sample/document/2
  {
    "category":"Apple (Company)",
    "content":"Apple is an American multinational technology company headquartered in Cupertino, California, that designs, develops, and sells consumer electronics, computer software, and online services. Its hardware products include the iPhone smartphone, the iPad tablet computer, the Mac personal computer, the iPod portable media player, the Apple Watch smartwatch, and the Apple TV digital media player. Apple's consumer software includes the macOS and iOS operating systems, the iTunes media player, the Safari web browser, and the iLife and iWork creativity and productivity suites. Its online services include the iTunes Store, the iOS App Store and Mac App Store, Apple Music, and iCloud."
  }

MLT-Abfragen sind sehr wichtig beim Textmining.

Wie funktionieren diese Abfragen? Sie können beliebigen Text verarbeiten, die n wichtigsten Schlüsselwörter relativ zum tatsächlichen „Modell“ extrahieren und eine boolesche Übereinstimmungsabfrage für diese Schlüsselwörter ausführen. Diese Art von Abfrage wird oft verwendet, um ähnliche Dokumente zu sammeln.

Wenn alle Dokumente mit Klasse und Kategorie beschriftet sind und wir eine ähnliche Anzahl an Trainingsinstanzen pro Klasse haben, dann entspricht dieser Prozess der Klassifizierung. Führen Sie einfach eine MLT-Abfrage aus, in der Sie das Eingabedokument als „like“-Feld verwenden, und schreiben Sie ein kleines Skript, das Punktzahl und Kategorie der n wichtigsten Treffer aggregiert.

GET sample/document/_search
  {
    "query":{
       "more_like_this":{
          "fields":[
             "content",
             "category"
          ],
          "like":"The apple tree (Malus pumila, commonly and erroneously called Malus domestica) is a deciduous tree in the rose family best known for its sweet, pomaceous fruit, the apple. It is cultivated worldwide as a fruit tree, and is the most widely grown species in the genus Malus. The tree originated in Central Asia, where its wild ancestor, Malus sieversii, is still found today. Apples have been grown for thousands of years in Asia and Europe, and were brought to North America by European colonists. Apples have religious and mythological significance in many cultures, including Norse, Greek and European Christian traditions.",
          "min_term_freq":1,
          "max_query_terms":20
       }
    }
  }

Dieses Beispiel dient lediglich zur Veranschaulichung des Workflows. Für eine echte Klassifizierung brauchen Sie etwas mehr Daten. Machen Sie sich also keine Sorgen, wenn Sie mit diesem Beispiel kein echtes Ergebnis erhalten. Fügen Sie einfach weitere Daten hinzu, dann funktioniert das Beispiel.

Das folgende kurze Python-Skript verarbeitet die Antwort und gibt die wahrscheinlichste Kategorie für das Eingabedokument zurück.

from operator import itemgetter
  def get_best_category(response):
     categories = {}
     for hit in response['hits']['hits']:
         score = hit['_score']
         for category in hit['_source']['category']: 
             if category not in categories:
                 categories[category] = score
             else:
                 categories[category] += score
     if len(categories) > 0:
         sortedCategories = sorted(categories.items(), key=itemgetter(1), reverse=True)
         category = sortedCategories[0][0]
     return category

Und schon haben Sie Ihre Elasticsearch-Textklassifizierung!

Anwendungsfälle

Textklassifizierung ist ein sehr häufiger praktischer Anwendungsfall für NLP. Denken Sie etwa an E-Commerce-Daten (Produkte). E-Commerce-Shops werden oft mit Affiliate-Links betrieben. Die Daten werden von mehreren Shops bereitgestellt und sind oft mit einer Kategorie markiert. Jeder Shop verwendet jedoch eigene Markierungen für die Kategorien. Daher müssen die Kategoriesysteme vereinheitlicht und die Daten mit dem neuen Kategoriebaum neu klassifiziert werden. Oder stellen Sie sich eine Business-Intelligence-Anwendung vor, die Unternehmenswebsites nach der jeweiligen Branche klassifiziert (Friseur, Bäckerei usw.).

Auswertung

Ich habe diesen Ansatz mit einem Standarddatensatz für die Textklassifizierung ausgewertet: Dem 20 Newsgroups Dataset. Die höchste Präzision (92 % korrekte Beschriftungen) wurde mit einem hohen Schwellenwert für Qualitätswerte erreicht, der nur 12 % der Dokumente umfasste. Wenn wir alle Dokumente beschriften (100 % Abruf) sind 72 % der Vorhersagen korrekt.

Die besten Algorithmen für die Textklassifizierung des 20 Newsgroups Dataset sind normalerweise SVM und Naive Bayes. Diese Algorithmen haben eine höhere Genauigkeit über den gesamten Datensatz.

Warum sollten wir also Elasticsearch für die Klassifizierung verwenden, wenn wir bessere Algorithmen haben?

Dafür gibt es einige praktische Gründe: es dauert sehr lange, ein SVM-Modell zu trainieren. Dies kann Probleme verursachen, besonders wenn Sie in einem Startup arbeiten oder sich schnell an unterschiedliche Kunden oder Anwendungsfälle anpassen müssen. In diesen Fällen können Sie Ihr Modell möglicherweise nicht bei jeder Datenänderung neu trainieren. Mir ist es bei einem Projekt für eine große deutsche Bank selbst so ergangen. Sie arbeiten also mit veralteten Modellen, die garantiert keine besonders guten Bewertungen ergeben.

Mit dem Elasticsearch-Ansatz wird das Training beim Indexieren ausgeführt, und Ihr Modell kann jederzeit dynamisch und ohne jegliche Ausfälle in Ihrer Anwendung aktualisiert werden. Wenn Sie Ihre Daten ohnehin in Elasticsearch gespeichert haben, brauchen Sie keine zusätzliche Infrastruktur. Mit mehr als 10 % extrem genauen Ergebnissen können Sie normalerweise die erste Seite füllen. Das reicht in vielen Fällen schon für einen guten ersten Eindruck.

Warum sollten wir also Elasticsearch verwenden, wenn es andere Tools gibt?

Weil Ihre Daten bereits vorhanden sind, und weil die zugrunde liegenden Statistiken ohnehin vorab berechnet werden. Sie bekommen also etwas NLP umsonst dazu!


Saskia Vola

Saskia Vola hat Computerlinguistik an der Universität Heidelberg studiert, und arbeitet seit 2009 im Bereich Textmining. Nach einigen Jahren in der Berliner Startupszene beschloss sie, komplett als Freiberuflerin zu arbeiten und das Leben als digitale Nomadin zu genießen. Nachdem sie immer mehr relevante Projektangebote erhalten hatte, gründete sie die Plattform textminers.io für NLP-/AI-Freiberufler.