quanteda
In diesem Tutorial wenden wir zum ersten Mal dem überwachten maschinellen Lernen (supervised learning) zu. Der Text von Loftis/Mortensen der zu dieser Sitzung gelesen werden soll, beschreibt diese Methode bereits sehr gut. Sitzung 8 wird das Thema dann nochmals aufgreifen.
Supervised learning erfordert Dokumente mit bereits vorhandenen Klassifizierungen als Grundlage. Insofern ähnelt die Methode dem Wörterbuchansatz. Der Unterschied besteht aber darin, dass wir beim supervised learning die Daten in einen Trainings- und einen Testdatensatz aufteilen. Die Trainingsdaten enthalten (in den meisten Fällen) manuell codierte Klassifizierungen unserer Dokumente, mit denen dann der Algorithmus (in diesem Tutorial Naïve Bayes) trainiert wird. Dadurch ist es uns möglich unkodierte Dokumente anhand der gegebenen (manuell codierten) Klassifizierungen maschinell zu klassifizieren. Hierbei handelt es sich um Vorhersagen bzw. Schätzungen bezüglich der Klassifizierungen der dem Computer unbekannten Dokumente.
Ein Naïve Bayes-Klassifikator berechnet die Wahrscheinlichkeit für jede Klassifaktion auf Basis der Features einer DFM. Er entscheidet sich schließlich für die Klassifaktion mit der höchsten Wahrscheinlichkeit und wählt diese als die entsprechende Kategorie/Klassifikation für das uncodierte Dokument aus. Er basiert auf dem Bayes-Theorem für bedingte Wahrscheinlichkeiten.
Naïve Bayes ist "naiv" wegen seiner starken Unabhängigkeitsannahmen. Das Bayes-Theorem nimmt an, dass alle Merkmale gleich wichtig sind und dass alle Merkmale unabhängig sind. Das ist natürlich eine sehr starke Annahme. Auch aus diesem Grund ist Naïve Bayes ein relativ einfacher Klassifikationsalgorithmus, der nicht viel Zeit und Rechenkapazität erfordert.
Um einen Naïve Bayes-Klassifikator anzuwenden, nutzen wir die quanteda
-Funktion textmodel_nb
. Das von quanteda
bereitgestellte Tutorial gibt ebenfalls einen kurzen aber guten Einblick in die Wirkungsweise: hier zu finden.
Die Arbeitschritte zur Durchfühung von überwachten maschinellen Klassifikation ähneln sich grundsätzlich:
Für unser Beispiel verwenden wir wieder den Korpus zu den Antrittsreden der U.S. Präsidenten. Da wir für eine Naïve Bayes-Klassifikation auch eine manuell codierte Variable benötigen, werden wir im Verlauf dieses Tutorials eine eigene Kodierung vornehmen. Für diese Kodierung stellen wir uns folgende (Forschungs)Frage: Erkennt ein maschnineller Lernalgorithmus ob eine Antrittsrede nach dem Ende des zweiten Weltkriegs gehalten wurde?
Als erstes erstellen wir eine DTM auf der Grundlage des quanteda
-Datensatzes data_corpus_inaugural
(den wir inaug_speeches
nennen):
library(quanteda)
library(quanteda.textplots)
library(quanteda.textstats)
inaug_speeches <- data_corpus_inaugural
inaug_speeches
## Corpus consisting of 59 documents and 4 docvars.
## 1789-Washington :
## "Fellow-Citizens of the Senate and of the House of Representa..."
##
## 1793-Washington :
## "Fellow citizens, I am again called upon by the voice of my c..."
##
## 1797-Adams :
## "When it was first perceived, in early times, that no middle ..."
##
## 1801-Jefferson :
## "Friends and Fellow Citizens: Called upon to undertake the du..."
##
## 1805-Jefferson :
## "Proceeding, fellow citizens, to that qualification which the..."
##
## 1809-Madison :
## "Unwilling to depart from examples of the most revered author..."
##
## [ reached max_ndoc ... 53 more documents ]
dtm_speeches <- inaug_speeches %>%
tokens(remove_numbers = TRUE, remove_punct = TRUE,remove_symbols = TRUE) %>%
tokens_remove(pattern = stopwords("english")) %>%
tokens_tolower() %>%
dfm() # wir entfernen alle Füllwörter, die Interpunktion, Symbole, Zahlen
dtm_speeches
## Document-feature matrix of: 59 documents, 9,212 features (92.66% sparse) and 4 docvars.
## features
## docs fellow-citizens senate house representatives among
## 1789-Washington 1 1 2 2 1
## 1793-Washington 0 0 0 0 0
## 1797-Adams 3 1 0 2 4
## 1801-Jefferson 2 0 0 0 1
## 1805-Jefferson 0 0 0 0 7
## 1809-Madison 1 0 0 0 0
## features
## docs vicissitudes incident life event filled
## 1789-Washington 1 1 1 2 1
## 1793-Washington 0 0 0 0 0
## 1797-Adams 0 0 2 0 0
## 1801-Jefferson 0 0 1 0 0
## 1805-Jefferson 0 0 2 0 0
## 1809-Madison 0 0 1 0 1
## [ reached max_ndoc ... 53 more documents, reached max_nfeat ... 9,202 more features ]
Anschließend generieren eine zufällige Anordnung der Spalten in der DTM (dieser Schritt erleichtert uns das Erstellen von Test- und Trainingsdaten:
# Setzen eines Seeds für Replikationszwecke (Mit diesem seed garantieren wir, dass die zufällige Anordnung bei jedem Durchlauf gleich bleibt. Damit sichern wir die Replizierbarkeit der Ergebnisse)
set.seed(2123) # Die Zahlen sind egal, so lange es bei jedem Druchlauf die gleichen Zahlen sind
dtm <- dtm_speeches[sample(1:nrow(dtm_speeches)),]
Nachdem wir nun eine DTM der Antrittsreden erstellt haben, erstellen wir unsere eigene Variable. Zur Erinnerung, wir fragen uns, ob ein maschnineller Lernalgorithmus erkennt das eine Antrittsrede nach dem Ende des zweiten Weltkriegs gehalten wurde. Wir erstellen die Variable mittels dem docvars
-Befehl und definieren einen logischen Operator, der Reden die vor 1945 gehalten wurden als TRUE klassifiziert. Dementsprechend nennen wir die Variable "is_prewar":
docvars(dtm, "is_prewar") <- docvars(dtm, "Year") < 1945
head(dtm@docvars)
## docname_ docid_ segid_ Year President FirstName
## 1 1981-Reagan 1981-Reagan 1 1981 Reagan Ronald
## 2 2001-Bush 2001-Bush 1 2001 Bush George W.
## 3 1937-Roosevelt 1937-Roosevelt 1 1937 Roosevelt Franklin D.
## 4 1817-Monroe 1817-Monroe 1 1817 Monroe James
## 5 1889-Harrison 1889-Harrison 1 1889 Harrison Benjamin
## 6 1973-Nixon 1973-Nixon 1 1973 Nixon Richard Milhous
## Party is_prewar
## 1 Republican FALSE
## 2 Republican FALSE
## 3 Democratic TRUE
## 4 Democratic-Republican TRUE
## 5 Republican TRUE
## 6 Republican FALSE
Wie ihr seht sind die einzelnen Reden nicht mehr chronologisch angeordnet. Aus diesem Grund ist die zufällige Auswahl in Trainings- und Testdaten relativ einfach:
train_dtm <- dtm[1:35,]
test_dtm <- dtm[36:nrow(dtm),]
Zur Übersicht kontrollieren wir die Verteilungen unserer "is_prewar"-Variable in beiden DTMs:
print(prop.table(table(docvars(
train_dtm, "is_prewar"
))) * 100)
##
## FALSE TRUE
## 34.28571 65.71429
print(prop.table(table(docvars(
test_dtm, "is_prewar"
))) * 100)
##
## FALSE TRUE
## 33.33333 66.66667
Wir sehen das die uns interessierende Variable gut in beiden Datensätzen verteilt ist.
Im nächsten Schritt trainieren wir den Naïve Bayes-Klassifikator. Die generelle Logik des Bayes-Theorem ist, dass die Wahrscheinlichkeit von A abhängig ist von B:
Wenden wir jetzt die textmodel_nb
-Funktion auf unseren Trainingsdatensatz an. Um die Funktion nutzen zu können, müssen wir (neuerdings...) das Paket quanteda.textmodels
installieren und laden. Anschließend nutzen wir die summary
-Funktion um unsere Ergebnisse zu inspizieren:
library(quanteda.textmodels) # vergesst nicht das paket vorher zu installieren!
nb_model <- textmodel_nb(train_dtm, y = docvars(train_dtm, "is_prewar"))
summary(nb_model)
##
## Call:
## textmodel_nb.dfm(x = train_dtm, y = docvars(train_dtm, "is_prewar"))
##
## Class Priors:
## (showing first 2 elements)
## FALSE TRUE
## 0.5 0.5
##
## Estimated Feature Scores:
## fellow-citizens senate house representatives among vicissitudes
## FALSE 0.0000496 9.921e-05 1.984e-04 0.0000496 0.000744 4.960e-05
## TRUE 0.0004892 8.154e-05 8.154e-05 0.0002174 0.001386 5.436e-05
## incident life event filled greater anxieties notification
## FALSE 0.0000496 0.001736 9.921e-05 9.921e-05 0.0006944 9.921e-05 4.960e-05
## TRUE 0.0001902 0.001386 2.718e-04 8.154e-05 0.0005979 5.436e-05 2.718e-05
## transmitted order received 14th day present month
## FALSE 4.960e-05 0.000248 0.0000496 4.960e-05 0.0014385 0.0002976 9.921e-05
## TRUE 5.436e-05 0.001141 0.0002174 2.718e-05 0.0005979 0.0011958 2.718e-05
## one hand summoned country whose voice can
## FALSE 0.002480 0.0007937 1.984e-04 0.002083 0.0003472 9.921e-05 0.005208
## TRUE 0.002745 0.0006523 5.436e-05 0.003289 0.0009241 2.718e-04 0.004376
## never hear veneration
## FALSE 0.001389 2.976e-04 4.960e-05
## TRUE 0.001604 5.436e-05 2.718e-05
Jetzt wenden wir unser trainiertes Naïve Bayes-Modell auf unsere Testdaten an und betrachten unsere Ergebnisse. Dafür nutzen wir die predict
-Funktion:
pred_nb <- predict(nb_model, newdata = test_dtm, force = TRUE) # Wir schätzen unser Modell auf unsere Testdaten
# Tabelle zum Vergleich der Vorhersagen (predictions) (Reihen) mit der von uns codierten "is_prewar"-Variable (Spalten)
table(prediction = pred_nb, is_prewar = docvars(test_dtm, "is_prewar"))
## is_prewar
## prediction FALSE TRUE
## FALSE 8 0
## TRUE 0 16
Wir sehen, dass unsere Vorhersage eine perfekte Genauigkeit erreicht hat und keine falsch-negativen oder falsch-postiven Ergebnisse vorhanden sind. Das bedeutet also, dass unser Naïve Bayes-Modell genau vorhersagen konnte welche Rede vor bzw. nach 1945 gehalten wurde.
Es versteht sich von selbst, dass die hier präsentierten Schritte nur einen möglichen Weg repräsentieren. Natürlich ist auch das gewählte Beispiel eher weniger Aussagekräftig und lediglich zu Demonstrationszwecken gewählt worden. Am besten ihr sucht auf eigene Faust nach weiteren Beispielen, Tutorials, und alternativen Wegen eine Klassifikation durchzuführen.