Grafik von Cristofer Maximilian - https://unsplash.com/de/@cristofer

Textklassifikation mit dem Naive-Bayes-Klassifikator

Der Naive Bayes-Klassifikator ist ein einfacher und effizienter Algorithmus für Klassifikationsaufgaben, welcher von der Unabhängigkeit der Merkmalen ausgeht. In diesem Beitrag möchte ich diesen vorstellen.

Henrik Bartsch

Henrik Bartsch

Die Texte in diesem Artikel wurden teilweise mit Hilfe künstlicher Intelligenz erarbeitet und von uns korrigiert und überarbeitet. Für die Generierung wurden folgende Dienste verwendet:

Einführung

In unserer heutigen Gesellschaft arbeiten wir häufig in digitalen Social-Media-Anwendungen oder kommunizieren über digitale Kommunikationsschnittstellen wie E-Mail. Dabei fallen große Datenmengen an, die häufig klassifiziert oder aus denen Informationen extrahiert werden können. Dazu werden spezielle Textklassifikatoren verwendet - wie zum Beispiel der Naive-Bayes-Klassifikator. In diesem Beitrag möchte ich über diesen sprechen und ihn vorstellen.

Mathematische Grundlagen

Der Naive Bayes-Klassifikator basiert auf den Ergebnissen der Stochastik. Genau auf dieser Stochastik basiert auch direkt unsere erste Annahme, die namensgebend ist: Unser Algorithmus geht davon aus, dass alle Merkmale aus dem Datensatz unabhängig voneinander zu dem Label führen. Eine solche Annahme wird als “naiv” bezeichnet, dementsprechend heißt dieser Algorithmus auch Naive-Bayes-Classifier. 1 2

Diese Annahme ist in der Realität selten erfüllt. Trotz dieser Einschränkung erzielt der Algorithmus in vielen Anwendungen gute Ergebnisse.

Der andere namensgebende Teil des Algorithmus ist das sogenannte Satz von Bayes. Dieser beschreibt, dass wir die Wahrscheinlichkeit des Ereignisses BB unter der Bedingung, dass das Ereignis AA eingetreten ist, wie folgt berechnen können:

P(AB)=P(BA)P(A)P(B).P(A|B) = \frac{P(B|A) \cdot P(A)}{P(B)}.

Für die Gültigkeit des Satzes muss P(B)>0P(B)>0 erfüllt sein. 3 4

Für die Klassifikation mit dem Naive Bayes Classifier nehmen wir nun weiterhin an, dass wir eine Menge von Klassen C:={c1,c2,}C := \{ c_1,c_2,\dots \} und unsere einzelnen Datenpunkte als Merkmalsvektor XRnX \in \mathbb{R}^n vorliegen haben. Die Anzahl der Klassen in unserem Datensatz wird im Folgenden als C\vert C \vert notiert. Der Algorithmus berechnet zunächst die Wahrscheinlichkeit für jede Klasse aus den Merkmalswerten in XX, dargestellt als P(cX)P(c \vert X). Diese Wahrscheinlichkeit wird mit Hilfe des Satzes von Bayes berechnet:

P(cX)=P(Xc)P(c)P(X).P(c|X) = \frac{P(X|c) \cdot P(c)}{P(X)}.

In dieser Gleichung haben wir die folgenden Wahrscheinlichkeiten:

  1. P(Xc)P(X|c) ist die Wahrscheinlichkeit, dass der Merkmalsvektor XX zur Klasse cCc \in C gehört,

  2. P(c)P(c) ist die a priori Wahrscheinlichkeit der Klasse cc und

  3. P(X)P(X) ist eine Normalisierungskostante, so dass eine Summation über {c1,,cn}\{ c_1, \dots, c_n \} insgesamt den Wert 11 ergeben würde.

Zur Berechnung der Wahrscheinlichkeit P(Xc)P(X \vert c) benötigen wir nun die Annahme, dass alle Merkmale grundsätzlich voneinander verschieden sind. Mit dieser Annahme können wir anschließend die Wahrscheinlichkeit berechnen zu

P(Xc)=i=1nP(xic).P(X|c) = \prod_{i=1}^n P(x_i|c).

Dabei multiplizieren wir die einzelnen Merkmalswahrscheinlichkeiten, um die Gesamtwahrscheinlichkeit der Merkmalswerte zu erhalten. In dieser Gleichung wird die ii-te Komponente des Merkmalsvektors XX durch xix_i dargestellt.

Ein alternativer Ansatz für eine diskrete Merkmalsklassifikation basiert auf einer Multinomialverteilung, um die Wahrscheinlichkeiten der Merkmalswerte modellieren zu können. In diesem Fall benötigen wir zusätzlich den Wert kik_i, der die Anzahl des Auftretens des ii-ten Merkmalswertes darstellt, sowie den Binomialkoeffizient (xiki)\binom{x_i}{k_i}. Hieraus ergibt sich für uns

P(Xc)=i=1n(xiki)P(kic).P(X|c) = \prod_{i=1}^n \binom{x_i}{k_i} \cdot P(k_i|c).

Für unsere a-priori-Wahrscheinlichkeit P(c)P(c) mit cCc \in C gilt im Allgemeinen eine Gleichverteilung der Klassen. Daraus ergibt sich eine Gleichverteilung der Wahrscheinlichkeiten der einzelnen Klassen, so dass wir die Wahrscheinlichkeit P(c)P(c) berechnen können nach

P(c)=1C.P(c) = \frac{1}{|C|}.

Um im nächsten Schritt P(cX)P(c|X) berechnen zu können, benötigen wir noch die Normierungskostante P(X)P(X). Diese kann mit folgender Gleichung berechnet werden:

P(X)=cCP(Xc)P(c)P(X) = \sum_{c \in C} P(X|c) \cdot P(c)

Um schließlich eine Vorhersage über die Klassenzugehörigkeit eines Merkmalsvektors XX^∗ treffen zu können, führen wir einfach die folgende Berechnung durch und erhalten unsere wahrscheinlichste Klasse cc: 1 5

c=arg maxcCP(cX)c = \argmax_{c' \in C} P(c'|X^*)

Anwendungsbeispiel

Im Folgenden möchte ich den Naive Bayes Classifier anhand eines “Natural Language Processing”-Beispiels vorstellen. Dazu verwenden wir den Datensatz ag_news_subset, der Nachrichtenartikel aus ~2000 Nachrichtenquellen verwendet und diese in vier verschiedene Klassen einteilt. Eine entsprechende Klassifikation soll durch einen von uns trainierten Algorithmus mit möglichst hoher Genauigkeit durchgeführt werden. 6 Dazu verwenden wir einen Naive-Bayes-Klassifikator.

Wir beginnen mit der Durchführung aller relevanten Imports:

naive_bayes.py
import tensorflow_datasets as tfds
import tensorflow as tf
import numpy as np

from sklearn.feature_extraction.text import CountVectorizer
from sklearn.naive_bayes import GaussianNB

Dann laden wir den Datensatz herunter. Unmittelbar nach dem Download können wir den Datensatz bearbeiten, um Zugriff auf alle Features und Labels zu erhalten.

naive_bayes.py
text_data = tfds.load("ag_news_subset")

train_text, train_y_ = tfds.as_dataframe(text_data["train"])["description"].values, tfds.as_dataframe(text_data["train"])["label"].values
test_text, test_y = tfds.as_dataframe(text_data["test"])["description"].values, tfds.as_dataframe(text_data["test"])["label"].values

Im nächsten Schritt verwenden wir einen Vectorizer, um alle im Datensatz vorhandenen Dokumente in eine numerische Repräsentation umzuwandeln. Dies ist notwendig, da Algorithmen aus dem Bereich des Machine Learnings nicht in der Lage sind, Wörter direkt zu verarbeiten. Häufig werden diese durch verschiedene Vectorizer in hochdimensionale Vektorräume eingebettet und können so verarbeitet werden.

Es ist hierbei wichtig anzumerken, dass wir den Vectorizer an alle Dokumente anpassen. Der Grund dafür ist, dass theoretisch Wörter im Testdatensatz vorkommen könnten, die im Trainingsdatensatz nicht vorkommen - dies würde entweder zu Fehlern führen oder potenziell interessante Datenpunkte ignorieren (abhängig von der Implementierung). Da wir dies nicht wollen, kombinieren wir zuerst alle Daten und übergeben sie dann an die Funktion vectorizer.fit_transform([...]).

naive_bayes.py
text_total = np.concatenate([train_text, test_text])
vectorizer = CountVectorizer()
tokens = vectorizer.fit_transform(text_total)

Mehr Informationen zum CountVectorizer können hier gefunden werden.

Vor dem Training des Klassifikators werden die Merkmale noch in eine Array-Darstellung umgewandelt. Dies liegt in diesem Fall an der Implementierung des Vectorizers, der grundsätzlich dünn besetzte Matrizen als Ergebnis zurückliefert. Da unser Klassifikator jedoch keine dünnen Matrizen akzeptiert, ist es notwendig, diese in eine nicht dünne Matrix umzuwandeln.

In vielen Fällen ist es sinnvoll, eine dünn besetzte Matrix zurückzugeben, da die Ergebnisse oft sehr hochdimensional sind und daher große Mengen an Arbeitsspeicher benötigen. Um dies zu vermeiden, werden dünn besetzte Matrizen verwendet, die im Prinzip nur die Elemente ungleich Null mit ihren zugehörigen Positionen speichern, anstatt alle Elemente zu speichern.

naive_bayes.py
train_x, test_x = tokens.toarray(), tokens.toarray()

Im vorletzten Schritt trainieren wir den Klassifikator auf dem Trainingsdatensatz. Dies kann wie folgt durchgeführt werden:

naive_bayes.py
gaussianNB = GaussianNB()
gaussianNB = gaussianNB.fit(train_x, train_y)

Im letzten Schritt kann die Genauigkeit des Klassifikators sowohl für den Trainings- als auch für den Testdatensatz bestimmt werden.

naive_bayes.py
print(gaussianNB.score(train_x, train_y))
naive_bayes.py
print(gaussianNB.score(test_x, test_y))

Bei der Ausführung des Codes wurde eine Genauigkeit von 98,63%98,63 \% auf dem Trainingsdatensatz und 78,75%78,75 \% auf dem Testdatensatz erreicht. Dies zeigt, dass der Algorithmus auch für komplexere Aufgaben geeignet ist.

Sollten Anwender:innen diesen Algorithmus das identische Skript durchlaufen lassen, ist es wahrscheinlich, dass andere Ergebnisse herauskommen. Aufgrund der Beschränkungen unserer Hardware war es nur möglich, die Klassifikation auf einem Teil des Datensatzes durchzuführen. Vor allem der Arbeitsspeicher ist ein limitierender Faktor, um den gesamten Datensatz verarbeiten zu können.

Vor- und Nachteile des Naive-Bayes-Classifiers

Bei der Verwendung eines Naive Bayes-Klassifikators sind eine Reihe von Vor- und Nachteilen zu berücksichtigen.

Vorteile

Die Vorteile des Naive-Bayes-Classifiers können wie folgt beschrieben werden: 2

  1. Effizientes Training: Aufgrund des geringen Rechenaufwandes des Algorithmus ist dieser in der Lage, große Datenmengen schnell zu verarbeiten. Durch die Annahme, dass die einzelnen Merkmale voneinander unabhängig sind, kann die Anzahl der benötigten Trainingsdaten theoretisch reduziert werden.

  2. Schnelle Ausführung: Durch die Einfachheit des Algorithmus kann die Klassenzugehörigkeit nach dem Training schnell vorhergesagt werden.

  3. Skalierbarkeit: Der Naive-Bayes-Algorithmus ist gut geeignet, mit hochdimensionalen Daten umzugehen und auf diesen gute Ergebnisse zu erzielen. Außerdem kann er sowohl mit diskreten als auch mit kontinuierlichen Daten umgehen.

  4. Robustheit: Der Naive Bayes-Klassifikator ist ein Algorithmus, der nicht so stark von Ausreißern beeinflusst wird wie andere Algorithmen.

Nachteile

Der große Vorteil von Naive Bayes liegt in der Einfachheit und Effizienz des Lernprozesses. Dies beruht auf der Annahme, dass alle Merkmale unabhängig voneinander sind. Obwohl dies in der Praxis zu robusten Ergebnissen führt, sind diese bei weitem nicht optimal - vor allem in Bereichen, die mit natürlicher Sprache arbeiten, sind z.B. Wortfolgen relevant. Eine unabhängige Betrachtung der Features ist in diesem Sinne möglich, wird in der Regel aber keine optimale Lösung finden.

Sollen komplexe Zusammenhänge in den Daten gelernt werden, kann es sinnvoll sein, auf andere Algorithmen auszuweichen. Beispiele für Algorithmen mit potenziell besseren Lösungen sind: 2 7

  1. Decision Trees

  2. Random Forests

  3. Support Vector Machines oder auch

  4. Neuronale Netze.

Anwendungsgebiete

Der Naive Bayes-Klassifikator kann in datengetriebenen Umgebungen in verschiedenen Bereichen eingesetzt werden. Nachfolgend sind einige Beispiele aufgeführt:

  1. Spam-Erkennung,

  2. Nachrichtenklassifizierung oder

  3. Stimmungsanalyse (Sentiment Analysis)

Aufgrund der geringen Komplexität des Algorithmus findet dieser schnell gute Lösungen in den Daten. 1 2

Quellen

Footnotes

  1. wikipedia.org 2 3

  2. guru99.com 2 3 4

  3. wikipedia.org

  4. wolfram.com

  5. arxiv.org

  6. di.unipt.it

  7. opengenus-org