# remotes::install_github("ccsmainz/hfapi", dependencies = FALSE, force = TRUE)
library(tidyverse)
theme_set(theme_minimal())
library(hfapi)
library(magick)
4 Zero-Shot Klassifikation
Wie bereits bekannt laden wir zu Beginn das tidyverse Paket und setzen ein schöneres Theme für alle Grafiken, die wir erstellen. Außerdem benötigen wir das hfapi
Paket, um mit der Hugging Face API zu interagieren. Ebenfalls notwenig ist das Paket magick
, um Bilder darzustellen.
4.1 Zero-shot Bildklassifikation
Zero-shot Bildklassifikation erlaubt es, Bilder in beliebige Kategorien einzusortieren. Dafür brauchen wir lediglich das Bildmaterial und die Klassen, die uns interessieren. Hier nutzen wir ein Stock Photo von einer Demonstration.
::image_read("https://images.pexels.com/photos/2975498/pexels-photo-2975498.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2") magick
Anschließend brauchen wir lediglich die Funktion image_zeroshot()
, welche auf das von OpenAI entwickelt Modell CLIP über die Hugging Face API zugreift. Den Funktion erhält das Bild sowie unsere Kategorien. Da CLIP anhand von englischen Bild-Text Paaren trainiert wurde, sollten wir die Kategorien entsprechend übersetzen. Nachfolgend fragen wir das Modell, ob es sich bei unserem Bild eher um eine Sportveranstaltung oder Demonstration handelt.
::image_zeroshot("https://images.pexels.com/photos/2975498/pexels-photo-2975498.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2", labels = c("demonstration", "sporting event")) hfapi
# A tibble: 2 × 2
score label
<dbl> <chr>
1 1.00 demonstration 2 0.000232 sporting event
Das Modell liefert uns die Klassen zurück und die dazugehörigen Zuversichtsscores. In unserem Fall ist es sich sehr sicher, dass es sich bei unserem Bild um eine Demonstration handelt.
4.2 Zero-shot Textklassifikation
Für die zero-shot Textklassifikation stehen uns prinzipiell zwei Arten von Modellen zu Verfügung:
- Generative große Sprachmodelle wie etwa GPT 3.5
- Modelle, die für Natural Language Inference (NLI) trainiert wurden
In diesem Kapitel nutzen wir nur Modelle, die für NLI trainiert wurden, da diese frei verfügbar sind, während generative Modelle oft mit (erheblichen) Kosten verbunden sind. So ist die Verwendung von GPT 3.5 über die OpenAI API etwa kostenpflichtig. Frei verfügbare große Sprachmodelle benötigen hingegen meist spezielle Hardware, die über Cloudservices gemietet werden muss.
4.2.1 Generative LLM (z.B. ChatGPT)
Die Klassifikationsaufgabe wird bei generativen Modellen als sogenannter prompt, eine Art Aufgabenbeschreibung, gestellt. Dabei kann etwa die Rolle der KI sowie das erwartete Ausgabeformat spezifiziert werden. Die Formulierung des prompts hat Einfluss auf das Klassifikationsergebnis, weshalb häufig verschiedene Beschreibungen der Aufgabe ausprobiert werden, um die Klassifikationsgüte zu steigern. Ein prompt zum Thema Negative Campaigning (Wahlwerbung, die den politischen Gegner negativ darstellt) könnte etwa wie folgt formuliert werden:
Please assess the following German text and determine whether one of the parties
is the target of negative campaigning. For each of the parties listed below,
respond with a 1 if the text contains negative campaigning against them,
and 0 if it does not. Please follow this format strictly.
SPD:
CDU/CSU:
FDP:
AfD:
die Grünen:
die Linke:
Here is the text:
Auch wenn die zu klassifizierenden Texte nicht englisch sind, sollte der prompt dennoch auf Englisch sein, da dies in der Regel zu besseren Ergebnissen führt. Grund dafür ist, dass die meisten Trainingsdaten für große Sprachmodelle in englischer Sprache vorliegen.
Das (fiktive) Ergebnis des obigen prompts sollte dann wie folgt aussehen:
SPD: 1
CDU/CSU: 0
FDP: 1
AfD: 0
die Grünen: 0
die Linke: 1
4.2.2 Natural Language Inference (NLI)
NLI behandelt die Folgerungsbeziehungen zwischen zwei Texteinheiten, der Hypothese und dem Text. Modelle, die für NLI trainiert werden, sollen lernen, ob aus dem Text die aufgestellte Hypothese folgt oder nicht (bzw. es keinen Zusammenhang gibt). Da sich alle Textklassifikationsaufgaben als Folgerungsbeziehungen umformulieren lassen, eignen sich diese Modelle für die zero-shot Textklassifikation. So lässt sich beispielsweise die Hypothese aufstellen “Dieser Text behandelt das Thema Immigration” und das Modell würde bei jedem Text im Datensatz prüfen, ob diese Hypothese aus dem jeweiligen Text folgt. Diese Herangehensweise wollen wir nachfolgend nutzen, um die Tweets von Donald Trump inhaltlich zu kategorisieren.
Zunächst laden wir den Datensatz:
load(url("http://varianceexplained.org/files/trump_tweets_df.rda"))
<- trump_tweets_df |>
tweets select(id, statusSource, text, created) |>
# Die verschiedenen Betriebssysteme extrahieren
extract(statusSource, "source", "Twitter for (.*?)<") |>
filter(source %in% c("iPhone", "Android")) |>
mutate(text = stringi::stri_enc_toutf8(text, validate = T))
tweets
# A tibble: 1,390 × 4
id source text created
<chr> <chr> <chr> <dttm>
1 762669882571980801 Android "My economic policy speech wil… 2016-08-08 15:20:44
2 762641595439190016 iPhone "Join me in Fayetteville, Nort… 2016-08-08 13:28:20
3 762439658911338496 iPhone "#ICYMI: \"Will Media Apologiz… 2016-08-08 00:05:54
4 762425371874557952 Android "Michael Morell, the lightweig… 2016-08-07 23:09:08
5 762400869858115588 Android "The media is going crazy. The… 2016-08-07 21:31:46 # ℹ 1,385 more rows
Für die zero-shot Textklassifikation benötigen wir die Funktion text_zeroshot()
aus unserem hfapi
Paket. Die Funktion benötigt wie image_zeroshot()
eine Liste der Kategorien, die uns interessieren. Hier nutzen wir, wieder in englischer Sprache, “about immigration” und “about foreign politics”. Die Funktion wandelt die Kategorien intern in die Hypothese “This example is about immigration” um. Dazu wird standardmäßig ein Template mit der Form “This exmaple is {}” verwendet, wobei die geschweiften Klammern als Platzhalter.
|>
tweets filter(id %in% c("760246732152311808", "760783130978648064", "761892829434183684")) |>
pull(text) |> # Textspalte nach dem Filtern auswählen
::text_zeroshot(url = "https://api-inference.huggingface.co/models/facebook/bart-large-mnli", labels = c("about immigration", "about foreign politics")) hfapi
# A tibble: 6 × 3
sequence labels scores
<chr> <chr> <dbl>
1 Hillary Clinton is being badly criticized for her poor perform… about… 0.698
2 Hillary Clinton is being badly criticized for her poor perform… about… 0.302
3 Our incompetent Secretary of State, Hillary Clinton, was the o… about… 0.991
4 Our incompetent Secretary of State, Hillary Clinton, was the o… about… 0.00917
5 Hillary Clinton raked in money from regimes that horribly oppr… about… 0.983 # ℹ 1 more row
Unser Output enthält den zu klassifizierenden Text für jede Kategorie ein Mal. Da wir zwei Kategorien haben, erhalten wir für jeden Text ein Duplikat. Sollten wir drei Kategorien untersuchen, erhalten wir jeden Text drei Mal (1 Original, 2 Duplikate). Des Weiteren erhalten wir die vorhergesagten Klassen und die Zuversichtsscores. Auf den ersten Blick sieht das Ergebnis plausibel aus. Lediglich für den ersten Tweet lässt sich feststellen, dass unsere Kategorien nicht wirklich passen, weshalb wir eher niedrige Zuversichtsscores erhalten.
Wir können diese Zuversichtsscores nutzen, um die Kategorien als vorhanden (1) oder nicht vorhanden (0) zu codieren. Nachfolgend wollen wir eine Klasse als vorhanden codieren, wenn der Zuversichtsscore über dem Schwellenwert .8 liegt.
<- tweets |>
tweets_topics slice_head(n = 50) |> # Erste 50 Tweets
pull(text) |> # Textspalte nach dem Filtern auswählen
::text_zeroshot(
hfapiurl = "https://api-inference.huggingface.co/models/facebook/bart-large-mnli",
labels = c("about immigration", "about foreign politics")
|>
) mutate(prediction = if_else(scores >= .8, 1, 0))
Anschließend können wir auszählen wie häufig die beiden Kategorien vorkommen und das Ergebnis grafisch darstellen.
|>
tweets_topics group_by(labels) |>
count(prediction) |>
mutate(prediction = as.character(prediction)) %>% # Nur für die grafische Darstellung
ggplot(aes(x = labels, y = n, fill = prediction)) +
geom_col(position = "dodge") +
labs(x = "", y = "n")
Scheinbar enthalten die ersten 50 Tweets keine Aussagen zur Migration, dafür aber einige, wenige Bemerkungen zur Außenpolitik.