Natural Language Processing - Erklärung und praktische Einführung mit Python

Johannes Gengenbach | 30.09.2020 | NLP

Natural Language Processing bezeichnet die Verarbeitung menschlicher (natürlicher) Sprache durch Computer. Dabei handelt es sich um keine einfache Aufgabe, weil natürliche Sprachen und formale Sprachen (z. B. Programmiersprachen) fundamental unterschiedlich sind. Das Ziel von NLP sind computerbasierte Systeme, die natürliche Sprache erfassen, verstehen und selbst erzeugen können.

Was ist Natural Language Processing?

Natural Language Processing (NLP) bezeichnet Methoden und Technologien zur Verarbeitung natürlicher Sprache durch Computer. Dabei handelt es sich um ein interdisziplinäres Feld in den Bereichen Linguistik, Informatik und Künstliche Intelligenz.

NLP ist kein neues Phänomen. Bereits in den 1950er Jahren befasste sich Alan Turing mit der Vorstellung von Computern, welche natürliche Sprache interpretieren und erzeugen. In der Geschichte von NLP gab es verschiedene Perioden, welche geprägt waren von unterschiedlichen Vorstellungen darüber wie NLP-Systeme am besten zu funktionieren haben.

Am ältesten ist die Vorstellung von regelbasiertem Natural Language Processing. Hierbei programmieren Entwickler manuell Regeln und Algorithmen, welche natürliche Sprache (in Textform) verarbeiten. Dieser Ansatz funktioniert ziemlich gut für bestimmte NLP-Aufgaben aber hat auch gravierende Nachteile. So ist das manuelle Programmieren von NLP Regeln für Menschen sehr schwierig, zeitaufwändig und fehleranfällig.

Die Nachteile des regelbasierten Ansatzes und andauernde Fortschritte im Bereich Machine Learning brachten den Ansatz des statistischen NLP hervor. Dabei werden NLP-Aufgaben als (semi-)supervised oder unsupervised Machine Learning Problem formuliert und mit klassischen ML-Verfahren gelöst. Diese Verfahren benötigen aussagekräftige Features (Merkmale), welche von Programmierern manuell aus den Sprachdaten erzeugt werden müssen.

Das manuelle Erzeugen von Features (Feature Engineering) ist überflüssig bei NLP mit künstlichen neuronalen Netzen. Dieser Ansatz sorgte in den letzten Jahren für die größten Erfolge bei vielen NLP-Anwendungen wie der Erkennung, Übersetzung und Erzeugung von natürlicher Sprache durch Computer.

Was sind typische NLP Methoden?

Bei der Vielfältigkeit natürlicher Sprache wundert es nicht, dass es auch vielfältige NLP Methoden gibt. Diese lassen sich in elementare und komplexere Verfahren untergliedern. In NLP-Pipelines (Folge von Datenverarbeitungselementen) werden die elementaren Methoden häufig zur Vor- und Nachbearbeitung eingesetzt.

Elementare Methoden

Speech-to-Text

Das Ergebnis von Speech-to-Text ist eine textuelle Repräsentation von aufgezeichneten Sprachdaten (Audio). Umgekehrt wird beim Text-to-Speech eine gesprochene Repräsentation aus Textdaten erstellt.

Satzerkennung

Hier geht es um die automatische Erkennung von Sätzen in Fließtext. Meistens werden Sätze durch Punktzeichen voneinander getrennt. Allerdings werden Punktzeichen auch bei Abkürzungen verwendet, was das Problem etwas weniger trivial macht.

Tokenisierung

Bei der Tokenisierung geht es um die Segmentierung eines Textes in kleinere Einheiten (Tokens). Meistens wird ein Text auf Wortebene segmentiert aber es sind auch andere Ebenen (Sätze, Absätze ..) denkbar. Tokenisierung ist häufig eine Voraussetzung für andere Verarbeitungsschritte.

Entfernung von Stoppwörtern

In den meisten Sprachen gibt es Wörter die häufig vorkommen aber wenig Relevanz/Information für die Bedeutung eines Texts haben. Bei vielen NLP Anwendungen werden solche Wörter entfernt.

Lemmatisierung/Stemming

In natürlichen Sprachen gibt es häufig durch Grammatikregeln abgewandelte Wörter. Stemming und Lemmatisierung reduzieren dabei abgewandelte Wörter wieder auf ihre Grundform - was häufig erwünscht ist. Der Unterschied zwischen beiden Verfahren besteht darin, dass Stemming auch nicht existierende Wörter produzieren kann.

Komplexere Methoden

Topic Modeling

Topic Modeling bezeichnet den Einsatz von statistischen Verfahren zur Entdeckung der Themen in einer Dokumentensammlung. Themen sind dabei als Cluster von ähnlichen Wörtern zu verstehen. Topic Modeling kann dabei helfen die enorme Menge an unstrukturierten Textdaten automatisch zu organisieren und Erkenntnisse daraus zu gewinnen. LDA ist eine leistungsfähige Topic Modeling Methode.

Sentiment Analyse

Sentiment Detection Methoden zielen darauf ab, die in Texten geäußerte Haltung als positiv, neutral oder negativ zu erkennen. Beispielsweise kann ein Unternehmen mit so einer Technik das eigene Kundenfeedback analysieren. Dabei gibt es regelbasierte und Machine Learning basierte Ansätze zur Sentiment Detection. Im praktischen NLP Beispiel werden wir ein Machine Learning Sentiment Detection System coden.

Automatische Zusammenfassung

Im NLP gibt es auch Methoden/Systeme zur automatischen Zusammenfassung von Texten. Zum einen können diese Zusammenfassungen aus extrahierten "wichtigen" Sätzen der Texte bestehen. Zum anderen kann es sich bei den automatischen Zusammenfassungen auch um neue und abstrakte Zusammenfassungen handeln. Die meisten Zusammenfassungssysteme in der Praxis basieren jedoch auf der Extraktionsmethode.

Erzeugung natürlicher Sprache

Natural Language Generation (NLG) umfasst Methoden zur automatischen Erzeugung von Text. Oftmals basiert der zu erzeugende Text auf strukturierten Daten. In der Praxis reicht die Spanne von einfachen vorlagenbasierten NLG-Systemen hin zu ausgefeilteren Systemen mit einem Grammatikverständnis. Außerdem werden vor allem in jüngster Zeit Deep Learning basierte Modelle wie GPT-3 zur Erzeugung von natürlicher Sprache verwendet.

Word2Vec

Viele NLP Methoden können nicht einfach auf unstrukturierten Fließtext angewendet werden, sondern benötigen eine andere Repräsentation der Wörter im Text. Eine klassische Repräsentation ist Bag-of-Words. Hier wird ein Satz/Text/Dokument als Menge der enthaltenen Wörter repräsentiert, wobei die Reihenfolge und Grammatik unberücksichtigt bleibt.

Word2Vec oder allgemeiner Word Embeddings stellen eine verbesserte Repräsentation von Text dar. Hier werden mittels Deep Learning Wörter als Vektoren (Punkte im mehrdimensionalen Koordinatensystem) abgebildet. Dabei werden die Werte der Vektoren so gewählt, dass semantisch ähnliche Wörter nah beieinander liegen.

Weitere Methoden

Neben den hier aufgezählten elementaren und komplexeren NLP Methoden gibt es noch einige andere wie z. B. Named Entity Recognition, Word Sense Disambiguation und Question Answering.

Praktisches NLP Beispiel in Python

Im folgenden werden wir ein Sentiment Detection System coden, welches die Stimmung von Yelp Food Reviews analysiert. Dabei sollen die Reviews vom System als positiv (1) oder negativ (0) klassifiziert werden.

Das System basiert auf Supervised Machine Learning. Dafür benötigt man einen gelabelten Datensatz. Der Datensatz enthält die Text-Reviews (unabhängige Variable) und zugehörige Stimmungen (abhängige Variable). ML-Algorithmen lernen aus den Daten eine Funktion, mit welcher den Text-Reviews eine (positive/negative) Stimmung zugeordnet wird.

Dabei verwenden wir zunächst einen Bayes Classifier mit Bag-of-Words und anschließend ein Künstliches Neuronals Netz mit Word Embeddings.

Den Yelp Reviews Datensatz findet Ihr hier. Entpackt die ZIP Datei und legt die Datei yelp_labelled.txt in euer Arbeitsverzeichnis.

Ihr benötigt auch eine Python Entwicklungsumgebung und die Packages numpy, pandas, scikit-learn und keras (tensorflow). Dieses Tutorial zu Python für ML ist hilfreich beim Setup.

Daten und Preprocessing

Zunächst importieren wir Numpy und Pandas. Außerdem erstellen wir einen leeren DataFrame.


import numpy as np
import pandas as pd

# create an empty dataframe
df = pd.DataFrame({'text': [], 'sentiment': []})

Daraufhin befüllen wir den DataFrame mit Daten aus dem Yelp Datensatz von der Datei.


# fill df with data
text = []
sentiment = []
with open('yelp_labelled.txt','r+') as f:
    lines = f.readlines()
    for line in lines:
        values = line.split("\t")
        text.append(values[0])
        sentiment.append(float(values[1][0]))
        
df['text'] = text
df['sentiment'] = sentiment

Hier seht ihr die ersten beiden Reviews mit zugehörigen Stimmungen:

Nach einer Umwandlung zu Bag-of-Words sehen die Daten so aus :

Bayes Classifier mit Bag-of-Words

Für den Bayes Classifier werden wir alle Reviews in das Bag-of-Words Format überführen.

Davor müssen wir die Daten in unabhängige/abhängige Variablen unterteilen und die Trainings-/Test-Daten erstellen:


from sklearn.model_selection import train_test_split

X = df['text']
y = df['sentiment']

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

Nun bringen wir die Textdaten in das Bag-of-Words Format.


from sklearn.feature_extraction.text import CountVectorizer

count_vec = CountVectorizer()
count_vec.fit(X_train)

X_train = count_vec.transform(X_train)
X_test  = count_vec.transform(X_test)

Anschließend trainieren wir einen Bayes Classifier auf den Trainingsdaten und evaluieren ihn auf den Testdaten.


from sklearn.naive_bayes import GaussianNB

clf = GaussianNB()
clf.fit(X_train.toarray(), y_train)

accuracy = clf.score(X_test.toarray(), y_test)

print(f'Accuracy for Bayes Classifier: {accuracy}')

Output: Accuracy for Bayes Classifier: 0.7

Das Ergebnis bedeutet, dass der Bayes Classifier in 70% aller Fälle mit seinen Bewertungen der Reviews richtig liegt.

Wir können dem Bayes Classifier auch zwei komplett neue Reviews übergeben und diese klassifizieren:

  • The food was really bad!
  • I will always come back to enjoy a delicious meal!

pred_text = pd.Series({0: 'The food was really bad!', 1: 'I will always come back to enjoy a delicious meal!'})

pred_text = count_vec.transform(pred_text)

clf.predict(pred_text.toarray())

Output: array([0., 1.])

Der Output bedeutet, dass das erste Review als negativ (0) und das zweite als positiv (1) bewertet wird. Gar nicht so schlecht!

Künstliches Neuronales Netz mit Word Embeddings

Bei diesem Verfahren werden wir die Daten in Training, Validierung und Test aufteilen:


X = df['text']
y = df['sentiment']

X_test = X[800:]
y_test = y[800:]

X = X.iloc[:800]
y = y.iloc[:800]

X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, random_state=42, shuffle=False)

Für Word Embeddings müssen wir die Textdaten als Folge von Integern darstellen.


from keras.preprocessing.text import Tokenizer
print(X_train[0])

tokenizer = Tokenizer()
tokenizer.fit_on_texts(X_train)

X_train = tokenizer.texts_to_sequences(X_train)
X_val = tokenizer.texts_to_sequences(X_val)
X_test = tokenizer.texts_to_sequences(X_test)

vocab_size = len(tokenizer.word_index) + 1
print(X_train[0])

Output:
Wow... Loved this place.
[302, 118, 8, 16]

Das KNN verlangt, dass die Inputdaten immer die gleiche Länge besitzen. Das längste Review hat 32 Wörter also legen wir die Länge der Integer Folgen auf 32 fest. Falls ein Review weniger Wörter besitzt, werden diese als 0 dargestellt:


from keras.preprocessing.sequence import pad_sequences

maxlen = 32

X_train = pad_sequences(X_train, padding='post', maxlen=maxlen)
X_val = pad_sequences(X_val, padding='post', maxlen=maxlen)
X_test = pad_sequences(X_test, padding='post', maxlen=maxlen)

print(X_train[0, :])

Output: [302 118 8 16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]

Im folgenden definieren wir die Struktur des KNN mit den verschiedenen Layern:

  • Embedding: Hier werden die semantischen Vektoren aus den Integern gelernt.
  • MaxPooling: Hier wird der Output des Embedding Layers heruntergesamplet.
  • Dense 10: Diese Schicht besteht aus 10 Neuronen zur Erkennung von Mustern.
  • Dense 1: Diese Schicht dient der Klassifizierung. Das Neuron wird bei negativer Stimmung nicht aktiviert und bei positiver Stimmung aktiviert.


from keras.models import Sequential
from keras import layers

embedding_dim = 50

model = Sequential()
model.add(layers.Embedding(input_dim=vocab_size, 
                           output_dim=embedding_dim, 
                           input_length=maxlen))
model.add(layers.GlobalMaxPool1D())
model.add(layers.Dense(10, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))
model.compile(optimizer='adam',
              loss='binary_crossentropy',
              metrics=['accuracy'])
# model.summary()

Nun trainieren wir das KNN auf den Trainingsdaten und optimieren die Parameter mit den Validierungsdaten. Anschließend bestimmen wir die Accuracy auf den Testdaten:


history = model.fit(X_train, y_train,
                    epochs=50,
                    verbose=False,
                    validation_data=(X_val, y_val),
                    batch_size=10)

loss, accuracy = model.evaluate(X_test, y_test, verbose=False)
print("Testing Accuracy:  {:.4f}".format(accuracy))

Output: Testing Accuracy: 0.7900

Das Model liegt in 79% aller Fälle mit seiner Bewertung der Sentiment richtig. Hinweis: Die Ergebnisse können abweichen, je nach dem wie das neuronale Netz (neu) trainiert wurde.

Das KNN können wir ebenfalls für neue Reviews benutzen, um deren Stimmung zu analysieren:


pred_text = pd.Series({0: 'The food was really bad!', 1: 'I will always come back to enjoy a delicious meal!'})

X_pred = tokenizer.texts_to_sequences(pred_text)
X_pred = pad_sequences(X_pred, padding='post', maxlen=maxlen)

model.predict_classes(X_pred)

Output: array([[0], [1]], dtype=int32)

Der Output bedeutet das erste Review wird negativ (0) und das zweite positiv (1) bewertet. Ändert man im ersten Review das Wort "bad" zu "good", bewertet das KNN die Stimmung als positiv. Dazu mussten wir die Regel nicht hardcoden sondern sie wurde aus den Daten gelernt!

Fazit

NLP ist ein spannendes und zukunftsträchtiges Feld. Für Computer ist es nicht einfach die menschliche Sprache zu "verstehen". Dabei gibt es eine Vielzahl an Methoden für unterschiedliche NLP Aufgaben. Während früher NLP-Verfahren auf manuell programmierten Regeln basierten, wird heute vor allem KI bzw. Machine Learning dafür eingesetzt. Diesen Ansatz haben wir auch in unserem Beispiel zu Sentiment Detection mit Machine Learning gewählt.

Referenzen