Dieses Tutorial enthält eine sehr kurze Einführung in die Verwendung von überwachtem maschinellem Lernen für die Textklassifizierung in R
.
Weitere Tutorials findet ihr zum Beispiel hier: - Klassifikation mit dem Caret-Paket - Cornelius Puschmann, Überwachtes maschinelles Lernen
Wir werden quanteda
für die Textverarbeitung, quanteda.textmodels
für einige Methoden des maschinelles Lernen und tidyverse
für die allgemeine Datenbereinigung verwenden:
library(quanteda)
## Package version: 3.0.0
## Unicode version: 10.0
## ICU version: 61.1
## Parallel computing: 4 of 4 threads used.
## See https://quanteda.io for tutorials and examples.
library(quanteda.textmodels)
library(quanteda.textplots)
library(quanteda.textstats)
library(tidyverse)
## ── Attaching packages ─────────────────────────────────────── tidyverse 1.3.0 ──
## ✓ ggplot2 3.3.3 ✓ purrr 0.3.4
## ✓ tibble 3.1.0 ✓ dplyr 1.0.5
## ✓ tidyr 1.1.3 ✓ stringr 1.4.0
## ✓ readr 1.4.0 ✓ forcats 0.5.1
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## x dplyr::filter() masks stats::filter()
## x dplyr::lag() masks stats::lag()
Weiterhin werden wir das Caret
-Paket für alternative Optionen zum maschinellen Lernen verwenden.
#install.packages(c("caret", "e1071", "LiblineaR")) # Falls noch nicht geschehen, dann bitte diese Pakete installieren
library(caret)
## Loading required package: lattice
##
## Attaching package: 'caret'
## The following object is masked from 'package:purrr':
##
## lift
Wie ihr bereits von letzter Woch wisst, benötigen wir für das maschinelle Lernen bereits codierte Trainingsdaten. Für dieses Beispiel nutzen wir Daten zu Filmkritiken, welche eine Sentiment-Variable aufweisen:
download.file("http://i.amcat.nl/data_corpus_movies.rda", "data_corpus_movies.rda")
load("data_corpus_movies.rda")
reviews <- data_corpus_movies
Wie üblich erstellen wir ein Training- und ein Testset (wir definieren set.seed für die Reproduzierbarkeit):
set.seed(1)
testset <- sample(docnames(reviews), 500) # wir wählen eine zufällige auswahl von 500 Kritiken aus
reviews_test <- reviews %>% corpus_subset(docnames(reviews) %in% testset) # Testset mit Kritiken die in unserer zufälligen Auswahl vorhanden sind
reviews_train <- reviews %>% corpus_subset(!docnames(reviews) %in% testset) # Trainingsset mit Kritiken die NICHT (!docnames(reviews) %in% testset) in unserer zufälligen Auswahl vorhanden sind
actual_train <- as.factor(docvars(reviews_train, "Sentiment"))
actual_test <- as.factor(docvars(reviews_test, "Sentiment"))
Nachdem wir jetzt ein Test- und ein Trainingsset erstellt haben, generieren wir für beide Datensätze eine DTM:
dfm_train <- reviews_train %>%
tokens(remove_numbers = TRUE, remove_punct = TRUE,remove_symbols = TRUE) %>%
tokens_remove(pattern = stopwords("english")) %>%
tokens_tolower() %>%
dfm()
dfm_test <- reviews_test %>%
tokens(remove_numbers = TRUE, remove_punct = TRUE,remove_symbols = TRUE) %>%
tokens_remove(pattern = stopwords("english")) %>%
tokens_tolower() %>%
dfm() %>%
dfm_match(featnames(dfm_train)) # Die Testdaten müssen die gleichen Merkmale (Wörter) verwenden wie die Trainingsdaten
# SCHREIBWEISE OHNE PIPELINE-OPERATOR
tokens_test <- tokens(reviews_test, remove_numbers = TRUE, remove_punct = TRUE,remove_symbols = TRUE)
tokens_test <- tokens_remove(tokens_test, pattern = stopwords("english"))
tokens_test <- tokens_tolower(tokens_test)
dfm_test <- dfm(tokens_test)
dfm_test <- dfm_match(dfm_test, featnames(dfm_train))
Jetzt trainieren wir das Modell (in diesem Fall mit Naïve Bayse):
reviews_nb <- textmodel_nb(dfm_train, actual_train) # wir wenden unsere DTM auf unseren Korpus an
summary(reviews_nb)
##
## Call:
## textmodel_nb.dfm(x = dfm_train, y = actual_train)
##
## Class Priors:
## (showing first 2 elements)
## neg pos
## 0.5 0.5
##
## Estimated Feature Scores:
## plot two teen couples go church party
## neg 0.002207 0.002308 0.0002089 4.874e-05 0.001445 5.570e-05 0.0002611
## pos 0.001377 0.002295 0.0001306 3.420e-05 0.001300 9.017e-05 0.0002145
## drink drive get accident one guys dies
## neg 4.874e-05 0.0001601 0.002625 6.962e-05 0.006701 0.0003899 0.0001358
## pos 3.420e-05 0.0001088 0.001996 1.835e-04 0.006881 0.0002829 0.0001182
## girlfriend continues see life nightmares deal watch
## neg 0.0002715 8.007e-05 0.002019 0.001298 2.785e-05 0.0002332 0.0007972
## pos 0.0002083 1.399e-04 0.002214 0.002326 2.798e-05 0.0003109 0.0007338
## movie sorta find critique mind-fuck generation touches
## neg 0.008003 1.392e-05 0.000926 8.007e-05 1.044e-05 1.044e-04 3.133e-05
## pos 0.005762 1.866e-05 0.001004 7.773e-05 3.109e-06 7.773e-05 9.639e-05
## cool idea
## neg 0.0002959 0.0005639
## pos 0.0001803 0.0004011
Um zu sehen, wie gut unser Modell funktioniert, testen wir es mit unseren Testdaten.
nb_pred <- predict(reviews_nb, newdata = dfm_test)
head(nb_pred)
## neg_cv004_12641 neg_cv014_15600 neg_cv018_21672 neg_cv019_16117 neg_cv021_17313
## neg neg neg neg pos
## neg_cv028_26964
## neg
## Levels: neg pos
mean(nb_pred == actual_test) # Hier vergleichen wir das vorhergesagte Sentiment mit dem tatsächlichen Sentiment
## [1] 0.812
81% Genauigkeit, das ist wirklich gut. Natürlich müssen wir bedenken, dass Filmkritiken eher einfache Texte sind. Ganz im Gegensatz zu Parteiprogrammen oder Gerichtsentscheidungen.
Wir können den regulären Tabellenbefehl verwenden, um eine Kreuztabelle zu erstellen. Kreuztabellen werden oft als 'Konfusionsmatrix' bezeichnet (da sie anzeigen, welche Art von Fehlern unsere Modelle machen):
confusion_matrix <- table(actual_test, nb_pred)
confusion_matrix
## nb_pred
## actual_test neg pos
## neg 206 44
## pos 50 200
Wir sehen, dass unser Modell insgesamt 94 Kritiken falsch klassifiziert hat (neg-pos und pos-neg).
Das caret
-Paket verfügt über eine Funktion, mit der wir alle "regulären" supervised learning Metriken in Tabellenform darstellen können:
confusionMatrix(nb_pred, actual_test, mode = "everything")
## Confusion Matrix and Statistics
##
## Reference
## Prediction neg pos
## neg 206 50
## pos 44 200
##
## Accuracy : 0.812
## 95% CI : (0.7749, 0.8453)
## No Information Rate : 0.5
## P-Value [Acc > NIR] : <2e-16
##
## Kappa : 0.624
##
## Mcnemar's Test P-Value : 0.6061
##
## Sensitivity : 0.8240
## Specificity : 0.8000
## Pos Pred Value : 0.8047
## Neg Pred Value : 0.8197
## Precision : 0.8047
## Recall : 0.8240
## F1 : 0.8142
## Prevalence : 0.5000
## Detection Rate : 0.4120
## Detection Prevalence : 0.5120
## Balanced Accuracy : 0.8120
##
## 'Positive' Class : neg
##
Was bedeutet Sensitivity, Specificity, Precision, Recall und F1? Schlag diese Begriffe unbedingt nach!
quanteda
Natürlich können wir auch außerhalb von quanteda
eine supervised classification durchführen. Zum Beispiel beinhaltet das caret
-Paket einige Methoden, um Modelle trainieren und testen zu können. Im folgenden konvertieren wir unsere Trainings- und Testsets von oben in ein für caret
lesbares Format:
trctrl <- trainControl(method = "none")
dtm_train <- convert(dfm_train, to='matrix')
dtm_test <- convert(dfm_test, to='matrix')
Im folgenden zeigen wir lediglich einen Algorithmus (SVM). Das caret
-Paket beinhaltet natürlich noch weitere Algorithmen. Für weitere Informationen schaut ihr hier.
Wir trainieren jetzt einen einfachen SVM (Support Vector Machine) Algorithmus mit dem LiblineaR
-Paket:
set.seed(1)
svm_model <- train(x = dtm_train, y = actual_train, method = "svmLinearWeights2",
trControl = trctrl, tuneGrid = data.frame(cost = 1, Loss = 0, weight = 1))
svm_pred <- predict(svm_model, newdata = dtm_test)
confusionMatrix(svm_pred, actual_test)
## Confusion Matrix and Statistics
##
## Reference
## Prediction neg pos
## neg 213 48
## pos 37 202
##
## Accuracy : 0.83
## 95% CI : (0.7941, 0.8619)
## No Information Rate : 0.5
## P-Value [Acc > NIR] : <2e-16
##
## Kappa : 0.66
##
## Mcnemar's Test P-Value : 0.2781
##
## Sensitivity : 0.8520
## Specificity : 0.8080
## Pos Pred Value : 0.8161
## Neg Pred Value : 0.8452
## Prevalence : 0.5000
## Detection Rate : 0.4260
## Detection Prevalence : 0.5220
## Balanced Accuracy : 0.8300
##
## 'Positive' Class : neg
##
Für weitere Informationen über den Algorithmus, einschließlich der Bedeutung der Parameter und deren Abstimmung, müsst ihr die Dokumentation des Pakets konsultieren. Die oben verlinkte Caret-Dokumentation sagt euch, welches Paket verwendet wird (in diesem Fall: LiblineaR
), und dieses Paket enthält eine technischere Erklärung des Algorithmus, einschließlich Beispielen und Referenzen.
Die meisten Algorithmen haben (Hyper-)Parameter, die definiert werden müssen, z. B. die Fehlklassifizierungskosten in SVM (in unserem Beispiel "cost = 1"). Meistens existieren keine wirklich fundierten theoretischen Grundlagen für die Definition der Parameter. Das bedeutet im Umkehrschluss, das wir als Forscherinnen meist in einem Trail-and-Error-Verfahren testen und ausprobieren und erst nach einigen Testläufen zu einem guten Eregbnis kommen.
Das caret
-Paket verfügt aber auch über Funktionen, um die Parameter maschinell zu definieren bzw. um diese Definition zu vereinfachen. Für weitere Informationen schaut euch bitte die folgenden Seiten an: