Textklassifikation – Vorverarbeitung der Daten

Im vorherigen Beitrag haben wir uns mit den drei Fragen “Warum ist automatisierte Textklassifikation sinnvoll?”, “Wo kann Textklassifikation eingesetzt werden?” und “Was sind die Schritte von den Daten bis zur Klassifizierung?” beschäftigt. In diesem Blogbeitrag möchten wir den ersten Schritt genauer erläutern, den Daten in einem typischen Textklassifizierungsszenario durchlaufen: Die Datenvorverarbeitung (s. Abbildung 1). 

Abbildung 1: Drei Schritte der Textklassifikation: Datenvorverarbeitung, Modelltraining und Modellevaluierung.

Als Beispiel dient uns ein Projekt, in dem deutschsprachige Zeitschriftenartikel klassifiziert werden. An ihm lassen sich die Grundlagen der Datenvorverarbeitung sehr gut aufzeigen. Wir haben uns für den Datensatz “Ten Thousand German News Articles Dataset” entschieden. Der Datensatz enthält 10.273 Zeitschriftenartikel einer österreichischen Online-Zeitschrift, die in neun Kategorien aufgeteilt sind. Die folgende Abbildung zeigt die Anzahl an Texten je Kategorie:

Abbildung 2: Anzahl an Texten in jeder Kategorie des Datensatzes.

Wie uns Datenvorverarbeitung hilft

Wer die Vorverarbeitung seiner Daten zu vermeiden probiert, 

dessen Modell-Accuracy nie steigen wird. 

Der Input für Machine-Learning-Modelle sind numerische Daten. Dementsprechend ist das Ziel der Textvorverarbeitung, die Textdaten in eine berechenbare und analysierbare Form zu bringen, die von Domäne und Ansatz unabhängig ist und als Zahl dargestellt werden kann. 

Wir stellen euch im Rahmen dieses Beitrags die am häufigsten benutzten Techniken zur Vorverarbeitung von Textdaten für Textklassifikationsaufgaben vor und erläutern, wann diese sinnvoll sind. Jede Vorverarbeitungstechnik wird anhand eines Beispiels und eines Code-Snippets erklärt. 

Bei der Datenvorverarbeitung haben wir Methoden gewählt, die die folgenden Ziele verfolgen: 

  • Die Daten in eine einheitliche Form bringen
  • Die relevantesten Daten auswählen

Durch Vereinheitlichung soll der Machine-Learning-Algorithmus unterschiedliche Schreibweisen eines Begriffs auf einen Nenner bringen. Zum Beispiel sind die Worte „der Ball”, „des Balles” und „die Bälle” für ein Modell nicht identisch, da ihre Zeichenzusammensetzung anders ist. Durch eine Vorverarbeitung werden alle Wörter in ihren Wortstamm „ball” umgewandelt. Für das Modell wäre es sonst sehr schwierig, in manchen Fällen sogar unmöglich, Zusammenhänge zwischen Wörtern zu erkennen, die unterschiedlich geschrieben sind. 

Die Selektion hat das Ziel, dem Machine-Learning-Algorithmus nur Daten zu übergeben, die für die Klassifizierung relevant sind. Wir möchten, dass sich das Modell auf das Wesentliche konzentriert, sprich: auf die Wörter mit den Kerninformationen. Ein klassisches Beispiel ist das Entfernen von Artikeln oder Füllwörtern wie „also”, „der”, „ein” etc. Hier geht es darum, den Informationsgehalt der Daten zu optimieren, um den Lernprozess zu beschleunigen. 

Techniken zur Vorverarbeitung von Texten

Wir stellen euch neun Vorverarbeitungstechniken vor, die in der Praxis bei der Textklassifikation häufig angewandt werden. Dazu haben wir die bekannten Bibliotheken NLTK und SpaCy genutzt. 

Die ersten drei – Lowercasing, Lemmatisierung und Stemming – gehören in den Bereich der Umformung von Daten, und mit den nachfolgenden vier Techniken werden wir Satzzeichen, Stoppwörter, Zahlen und nicht ASCII konformen Wörtern entfernen, damit wir nur die relevantesten Daten auswählen.   

Die Umsetzung jeder der Techniken nacheinander wird anhand des folgenden kleinen Beispieltextes veranschaulicht: 

21-Jähriger fällt wohl bis Saisonende aus. Wien – Rapid muss wohl bis Saisonende auf Offensivspieler Thomas Murg verzichten. Der im Winter aus Ried gekommene 21-Jährige erlitt beim 0:4-Heimdebakel gegen Admira Wacker Mödling am Samstag einen Teilriss des Innenbandes im linken Knie. 

Lowercasing

Den Datensatz zu Kleinbuchstaben konvertieren 

und dann von großartigen Ergebnissen profitieren… 

Um die Bedeutung eines Textes zu verstehen, spielen Klein-und Großbuchstaben keine Rolle. Deshalb werden beim Lowercasing alle Buchstaben in Kleinbuchstaben umgewandelt.  In Python gibt es verschiedene Möglichkeiten dafür. Eine der einfachsten ist die Standardmethode lower() für Strings: 

def to_lower(in_string: str):
    out_string = in_string.lower()
    
    return out_string

Nach den Lowercasing läse sich unser Beispielsatz so:  

21-jähriger fällt wohl bis saisonende aus. wien – rapid muss wohl bis saisonende auf offensivspieler thomas murg verzichten. der im winter aus ried gekommene 21-jährige erlitt beim 0:4-heimdebakel gegen admira wacker mödling am samstag einen teilriss des innenbandes im linken knie. 

Lemmatisierung und Stemming

Die Vielfalt an Wörtern reduzieren, 

und „schwupps” kann das Modell Texte klassifizieren.

Lemmatisierung ist der Prozess der Umwandlung eines Wortes in seine Grundform (Lemma). Das ist die Form, in der ein Wort im Wörterbuch zu finden wäre. Beispielsweise wird „erlitt” zu „erleiden” umgeformt. Damit wird die Anzahl der Flexionsformen reduziert. Flexionsformen sollen verschiedene grammatikalischen Merkmale ausdrücken. Für die Kategorisierung eines Textes sind sie nicht relevant. Die oben genannten Python-Bibliotheken NLTK und Spacy bieten die Funktionalität zur Lemmatisierung. Wir müssen aber in Gedanken behalten, dass die Ergebnisse der Lemmatisierung von der entsprechenden Bibliothek und von der Implementierung der Technik abhängig sind, und nicht immer grammatikalisch korrekt sind. Das ist auch bei unserem Beispiel zu beobachten.    

import spacy as sp

def lemmatize(in_string: str):
    nlp = sp.load('de_core_news_md') 
    nlp_object = nlp(in_string) 
    output_lemmatized = ' '.join([x.lemma_ for x in nlp_object])
    output_lemmatized = ' '.join(output_lemmatized.split())
    
    return output_lemmatized

Der Beispieltext nach Lemmatisierung: 

21-jähriger fällen wohl bis saisonende aus. wien – rapid muss wohl bis saisonende auf offensivspieler thomas murg verzichten. der im winter aus ried gekommen 21-jährige erleiden beim 0:4-heimdebakel gegen admira wacker mödling am samstag ein teilriss der innenbandes im link knien. 

Stemming ist eine andere Methode, die – ähnlich der Lemmatisierung –  auch zur Vereinheitlichung von verschiedenen Formen eines Wortes benutzt wird. Stemming entfernt Präfixe und Suffixe, um Wörter auf ihren Wortstamm zu reduzieren.  In der Praxis gibt es verschiedene Algorithmen, die Stemming anwenden. Einer der bekanntesten für die englische Sprache ist der Porter Stemmer. Aber Achtung: Stemming (wie Lemmatisierung) erzeugt nicht immer das richtige Ergebnis und sollte daher mit Bedacht eingesetzt werden. 

from nltk.stem.snowball import GermanStemmer

def stemming(in_string: str) -> str:
    stemmer = GermanStemmer()
    output_stemmed = ' '.join([stemmer.stem(x) for x in in_string.split()])
    
    return output_stemmed

Der Beispieltext nach Stemming:

21-jahrig fallt wohl bis saison aus. wien – rapid muss wohl bis saison auf offensivspiel thomas murg verzichten. der im wint aus ried gekomm 21-jahrig erlitt beim 0:4-heimdebakel geg admira wack modling am samstag ein teilriss des innenband im link knie.

Die nächsten Vorverarbeitungsschritte werden wir auf unseren Beispielsatz nach Lemmatisierung anwenden.

Entfernen von Satzzeichen

Ohne Punkt und Komma, lohnt sich das?
Prüfe und behalte Augenmaß. 

Aus der Sicht eines Modells zur Textklassifikation sind die Satzzeichen weitere Elemente des Textes, deren Bedeutung und Zusammenhang zu den anderen Elementen gelernt werden soll. Das ist oft aber unnötig, denn genau wie die Anfangsbuchstaben, haben die meisten Satzzeichen keine Relevanz für die Textklassifikation. Doch es gibt Ausnahmen: So kann ein Komma dem Modell helfen, verschiedene Repräsentationen von Zahlen und deren Bedeutung zu lernen: So wäre 1998 eine Jahreszahl und 19,98 ein Geldwert. Würden wir hier das Komma entfernen, könnte das Modell den gleichen Input für gänzlich verschiedene Zahlenwerte bekommen: „1998“ und „1998“. Das Komma wäre also wichtig, um die Bedeutung von verschiedenen Zahlenformaten zu lernen. 

Entfernen lassen sich Satzzeichen mit einer vordefinierten Konstante im String-Modul string.punctuation von Python, die die folgenden ASCII-Satzzeichen umfasst: 

!“#$%&'()*+,-./:;<=>?@[]^_`{|}~

def remove_punctuation(in_string: str):
    out_string = in_string.translate(str.maketrans('', '', string.punctuation))
    out_string = ' '.join(out_string.split())
   
    return out_string

Nun unser Beispielsatz ohne Satzzeichen: 

21 jähriger fällen wohl bis saisonende aus wien rapid muss wohl bis saisonende auf offensivspieler thomas murg verzichten der im winter aus ried gekommen 21jährige erleiden beim 04heimdebakel gegen admira wacker mödling am samstag ein teilriss der innenbandes im link knien 

Entfernen von Stoppwörtern

Wenn Häufigkeit und Bedeutungsgehalt negativ korrelieren, 

kannst du davon nicht profitieren.

Im Information Retrieval sind Stoppwörter sehr häufig auftretende Wörter, die gewöhnlich keine Bedeutung für die Erfassung des Inhalts eines Dokuments besitzen. In jeder Sprache gibt es andere Stoppwörter. Für die Arbeit mit dem Algorithmus können feste Stoppwortlisten genutzt werden, die es für viele Sprachen gibt. Stoppwörter zeichnen sich dadurch aus, dass sie sehr häufig in einem Text vorkommen und wichtig sind für die Grammatik. Ohne sie, kann sich das Modell besser auf die eigentlichen Informationen in einem Text konzentrieren.  

Beispiele für Stoppwörter in der deutschen Sprache sind: 

  • bestimmte und unbestimmte Artikel (z.B. der, die, ein, einer, etc.) 
  • häufige Präpositionen (z.B. an, in, von, etc.),  
  • die Negation nicht 

Wie das Entfernen von Satzzeichen ist auch das Entfernen von Stoppwörtern abhängig von der Lernaufgabe des Modells und vom Datensatz. Diese Vorverarbeitungstechnik wird gerne bei der Suchmaschinenoptimierung oder Informationsextraktion genutzt, bei der Textklassifikation ist sie nicht immer hilfreich. 

from spacy.lang.de import German

def remove_stop_words(in_string: str):
    nlp = German()
   
    nlp_object = nlp(in_string)
    # Create list of word tokens
    token_list = []
    for token in nlp_object:
        token_list.append(token.text)

    # Create list of word tokens after removing stopwords
    filtered_sentence = []

    for word in token_list:
        lexeme = nlp.vocab[word]
        if not lexeme.is_stop:
            filtered_sentence.append(word)
   
    filtered_sentence = ' '.join(filtered_sentence)
    filtered_sentence = ' '.join(filtered_sentence.split())
           
    return filtered_sentence

Hier ist unser Beispielsatz ohne Stoppwörter: 

21jähriger fällen saisonende wien rapid saisonende offensivspieler thomas murg verzichten winter ried gekomm 21jährige erleiden 04heimdebakel admira wacker mödling samstag teilriss innenbandes link knien

Entfernen von Zahlen

Zum Verstehen der Bedeutung eines Textes komplett –

anstatt die Ziffern 1 bis 10, schau dir an die Buchstaben a bis z…

Ob das Entfernen von Zahlen dem Modell wirklich hilft, hängt wieder vom Anwendungsfall und vom Datensatz ab. In unserem Fall haben wir uns dafür entschieden, alle Zahlen zu entfernen, denn sie beinhalten keine Bedeutung für die Texte. Dementsprechend helfen sie nicht dabei, die richtige Kategorie den einzelnen Texten zuzuweisen. 

def remove_numbers(in_string: str):
   
    # A regex pattern that matches all the numbers in a given string
    pattern = r'[0-9]'
   
    sentence_no_numbers = " ".join((re.sub(pattern, "", in_string)).split()) 
    sentence_no_numbers = ' '.join(sentence_no_numbers.split())
   
    return sentence_no_numbers

Der Beispieltext nach Entfernen der Zahlen: 

jähriger fällen saisonende wien rapid saisonende offensivspieler thomas murg verzichten winter ried gekomm ährige erleiden heimdebakel admira wacker mödling samstag teilriss innenbandes link knien 

Ersetzen von Zeichen 

Komplexe Zeichen solltest du simplifizieren, 

damit sich die Wörter in jeder Kodierung richtig repräsentieren.

In unserem Datenset haben wir alle Umlaute – ä, ö, ü, und die Ligatur ß durch durch ae, oe, ue bzw. ss ersetzt. Das ist ein Schritt, der zur Normalisierung von Texten gehört. Das Ersetzen von diakritischen (ä, ö, ü, ß) Zeichen kann in einigen Fällen besonders hilfreich sein. Ein gutes Beispiel dafür ist die Zeichenkodierung in ASCII, die heutzutage immer noch eine der verbreitetsten Arten von Zeichenkodierung ist, aber nur einfache Buchstaben erlaubt – auf Englisch auch als “printable characters” bekannt. Da diakritische Zeichen kein Teil davon sind, ist es nicht möglich deutsche Wörter in ASCII richtig zu repräsentieren, es sei denn, die Umlaute und ß sind ersetzt. Werden diese Zeichen ersetzt, können alle deutschen Wörter komplett mit einfachen ASCII-Zeichen dargestellt werden.  

def replace_special_letters(in_string: str) -> str:
    replacements_dic = {
        "ä": "ae",
        "ö": "oe",
        "ü": "ue",
        "ß": "ss"
    }
    # Checks if parts of the strings match an item from the
    # dictionary of predefined replacements
    rc = re.compile('|'.join(map(re.escape, replacements_dic)))

    # Small helper function to do the replacement if a match
    # in the string is found
    def translate(match):
        return replacements_dic[match.group(0)]
    out_string = rc.sub(translate, in_string)
    return out_string

Der Beispieltext nach Ersetzen von den diakritischen Zeichen: 

jaehriger faellen saisonende wien rapid saisonende offensivspieler thomas murg verzichten winter ried gekommen jaehrige erleiden heimdebakel admira wacker moedling samstag teilriss innenbandes link knien 

Entfernen von nicht ASCII konformen Wörtern

Hier werden wir einen berühmten Spruch übertragen – 

es ist eine Kunst, mit wenigen Zeichen viel zu sagen…

Eine nach unserem Anwendungsfall erstellte Funktion zur Vorverarbeitung der Texte, ist das Entfernen aller Wörter, die ein nicht einfaches ASCII-Zeichen enthalten. Der Grund dafür ist die Art von Texten mit denen wir arbeiten, und zwar Artikel aus einer Online-Zeitschrift, in denen oft beispielsweise Zitate von fremden Medien in der entsprechenden Sprache vorkommen oder verschiedene fremde Marken (Škoda) erwähnt werden. Jedes einzelne Wort davon hat eine niedrige Häufigkeit, dementsprechend auch keine bedeutende Relevanz zum Informationsgehalt des Textes. 

Da wir schon Zahlen, Umlaute und ß entfernt oder ersetzt haben, wollten wir nur Wörter aus einfachen ASCII-Zeichen in unseren Texten belassen. Auf diese Weise garantieren wir, dass alle fremdsprachigen Wörter, die keine Bedeutung zur Kategorie des Textes haben, entfernt werden. Deutsche Wörter, die bedeutungstragend sind, gehen so nicht verloren. 

def remove_non_ascii_words(in_string: str) -> str:

    def isascii(s: str):
        return len(s) == len(s.encode())
   
    ascii_words = []
    for word in in_string.split():
        if isascii(word):
            ascii_words.append(word)
   
    sentence_only_ascii_words = ' '.join(ascii_words)
   
    return sentence_only_ascii_words

Ein kleiner Beispielsatz mit nicht ASCII konformen Wörtern: 

jaehriger odvažovat faellen Škoda saisonende wien offensivspieler thomas 

Ergebnis nach Entfernung der Non-Ascii-Wörter: 

jaehriger faellen saisonende wien offensivspieler thomas   

Fazit 

Alle hier vorgestellten Techniken können einem Modell dabei helfen, Texte besser zu verstehen und schneller zu verarbeiten. Wichtig ist jedoch, dass keiner dieser Schritte obligatorisch ist. Je nach Art der Daten, dem Anwendungsfall und der Art der Informationen, die man gewinnen möchte, muss individuell entschieden werden, welche Technik sinnvoll ist und in welchem Maße sie angewendet werden sollte. Es gibt viele Schritte, die zu einer erfolgreichen Textklassifizierung führen.

In diesem Artikel wollten wir zeigen, dass Textklassifizierung und im Grunde fast alle Machine-Learning-Prozesse, mit einer soliden Vorverarbeitung der Daten beginnen. Diese Schritte können drastische Auswirkungen auf das Ergebnis haben, völlig unabhängig davon, welches Modell oder welcher Algorithmus später verwendet wird und wie dieses/dieser konfiguriert ist.

In unserem nächsten Beitrag werfen wir einen Blick hinein ins Modelltraining: Wie lernt eine Maschine? Wie geht sie vor, wenn es zum Beispiel darum geht, verschiedene, Artikelkategorien zu unterscheiden? Es lohnt sich also reinzuschauen.

Stay tuned! 

Autoren: Luca Pomer, Conrad Dollinger, Jeffrey Remien und Galina Angelova

Alle Teile dieser Serie:

Teil 1: Textklassifikation – eine Einführung
Teil 2: Textklassifikation – Vorverarbeitung der Daten
Teil 3: Textklassifikation – Modelle trainieren und evaluieren

Alle Beiträge von Luca Pomer

As a developer at OPITZ CONSULTING, I am active in the analytics community, consistently seeking out new opportunities to create machine learning solutions. In my role, I have the opportunity to work with a wide range of technologies. Whether I am developing new algorithms or creating web interfaces, my goal is always to find innovative ways to solve complex problems and make a positive impact.

Schreibe einen Kommentar