17  Transformer

17.1 Vorab

17.1.1 Lernziele

  • Sie können die grundlegende Architektur eines Transformer-Modells beschreiben.
  • Sie können Transformer-Modelle mit der API von Hugging-Face berechnen.

17.1.2 Begleitliteratur

Der Blogpost von Jay Alammar gibt einen illustrierten Überblick über Transformer.

17.1.3 Benötigte Software (Python+R)

Wir benötigen Python, R sowie einige im Folgenden aufgeführte Python-Module.

import pandas as pd  # Data Frames
import os  # zB für Environment-Variablen
from transformers import pipeline  # Hugging Face Modelle
import time  # Rechenzeit
from sklearn.metrics import accuracy_score  # Modellgüte

Für den Schüleraustausch von R nach Python (und retour) nutzen wir das R-Paket reticulate:

Außerdem starte ich die “richtige” Python-Version, wo die benötigten Pakete (in der richtigen Version) installiert sind:

#use_virtualenv("r-tensorflow")

Check:

py_available()
## [1] TRUE

Welche Python-Version nutzt reticulate gerade?

py_config()
## python:         /Users/sebastiansaueruser/.virtualenvs/r-tensorflow/bin/python
## libpython:      /Users/sebastiansaueruser/.pyenv/versions/3.8.16/lib/libpython3.8.dylib
## pythonhome:     /Users/sebastiansaueruser/.virtualenvs/r-tensorflow:/Users/sebastiansaueruser/.virtualenvs/r-tensorflow
## version:        3.8.16 (default, Sep 15 2023, 17:53:02)  [Clang 14.0.3 (clang-1403.0.22.14.1)]
## numpy:          /Users/sebastiansaueruser/.virtualenvs/r-tensorflow/lib/python3.8/site-packages/numpy
## numpy_version:  1.24.3
## 
## NOTE: Python version was forced by VIRTUAL_ENV

17.2 Überblick

Transformer sind eine Architekturvariante neuronaler Netze. Sie stellen die Grundlage vieler aktueller großer Sprachmodelle1; da sie einige Vorzüge gegenüber Vorgängermodellen aufweisen, haben sie einen zentralen Platz für verschiedenen Aufgaben des NLP eingenommen.

Im Jahr 2017 erschien ein Paper auf Arxive mit dem Titel “Attention is all you need”, Vaswani u. a. (2023)2. Transformer basieren auf einer bestimmten Art von “Aufmerksamkeit”, genannt Selbst-Aufmerksamkeit (self-attention). Natürlich ist damit eine bestimmte Architektur im neuronalen Netzwerk gemeint, kein kognitivpsychologiches Konstruktr; allerdings lehnt sich die Methode an Konzepte der Kognitionspsychologie vage an.

Self-Attention weist zwei große Verteile auf: Erstens erlaubt es parallele Verarbeitung, was viele Vorgängermodelle nicht erlaubten. Zweitens kann es den Kontext eines Tokens, also den Text um ein bestimmtes Wort herum, deutlich besser “im Blick” (oder in der Aufmerksamkeit) behalten als viele Vorgängermodelle.

Gerade für Daten mit sequenziellem Charakter, wie Text oder Sprache, sind Transformer-Modelle gut geeignet3.

17.3 Grundkonzepte

17.4 Einführung in Hugging Face 🤗

Dieser Abschnitt orientiert sich an Tunstall u. a. (2022). Die Syntax zu allen Kapiteln des Buchs findet sich praktischerweise in diesem Github-Repo.

Bei 🤗 liegt der Schwerpunkt klar bei Python, nicht bei R. Allerdings erlaubt RStudio ein einfaches Wechseln zwischen R und Python: Funktionen und Daten aus Python können einfach mit dem $-Operator angesprochen werden. In diesem Post wirds das demonstriert.

Schauen wir uns das einführende Beispiel aus Tunstall u. a. (2022). an.

17.4.1 Hugging Face mit R

Hier ein ein Text-Schnipsel, dessen Sentiment wir detektieren wollen:

text <- ("Dear Amazon, last week I ordered an Optimus Prime action figure from your online store in Germany. Unfortunately, when I opened the package, I discovered to my horror that I had been sent an action figure of Megatron instead! As a lifelong enemy of the Decepticons, I hope you can understand my dilemma. To resolve the issue, I demand an exchange of Megatron for the Optimus Prime figure I ordered. Enclosed are copies of my records concerning this purchase. I expect to hear from you soon. Sincerely, Bumblebee.")

Und hier in der Python-Version:

text_py = r.text

Dann importieren wir die nötigen Module:

#import tensorflow
from transformers import pipeline

Natürlich müssen Python-Module installiert sein, bevor man sie nutzen kann, genau so wie R-Pakete.

Man kann die die Python-Module auch über R starten:

transformers <- reticulate::import("transformers")

17.4.2 Einfache Pipeline

17.4.3 Python

Wir bereiten das Modell vor; im Default wird distilbert-base-uncased-finetuned-sst-2-english verwendet.

classifier = pipeline("text-classification")
## No model was supplied, defaulted to distilbert-base-uncased-finetuned-sst-2-english and revision af0f99b (https://huggingface.co/distilbert-base-uncased-finetuned-sst-2-english).
## Using a pipeline without specifying a model name and revision in production is not recommended.
## All PyTorch model weights were used when initializing TFDistilBertForSequenceClassification.
## 
## All the weights of TFDistilBertForSequenceClassification were initialized from the PyTorch model.
## If your task is similar to the task the model of the checkpoint was trained on, you can already use TFDistilBertForSequenceClassification for predictions without further training.

17.5 Germeval Out-of-the-Box mit Hugging Face

Zuerst importieren wir die Daten.

data(germeval_train, package = "pradadata")
text <- germeval_train$text[1:2]
text[1:2]
## [1] "@corinnamilborn Liebe Corinna, wir würden dich gerne als Moderatorin für uns gewinnen! Wärst du begeisterbar?"                                 
## [2] "@Martin28a Sie haben ja auch Recht. Unser Tweet war etwas missverständlich. Dass das BVerfG Sachleistungen nicht ausschließt, kritisieren wir."
germeval_train_py = r.text

17.5.1 Standard-Pipeline

Hugging Face bietet eine sehr einfache Oberfläche: Im einfachsten Fall kann man mit pipeline() das Ziel der Analyse (wie Textklassifikation) oder das zu verwendende Modell angegeben. Dann wird das entsprechende Modell heruntergeladen und vorbereitet. Mit classifier() wird ein Datensatz dann entsprechend klassifiziert.

classifier = pipeline("text-classification")  
## No model was supplied, defaulted to distilbert-base-uncased-finetuned-sst-2-english and revision af0f99b (https://huggingface.co/distilbert-base-uncased-finetuned-sst-2-english).
## Using a pipeline without specifying a model name and revision in production is not recommended.
## All PyTorch model weights were used when initializing TFDistilBertForSequenceClassification.
## 
## All the weights of TFDistilBertForSequenceClassification were initialized from the PyTorch model.
## If your task is similar to the task the model of the checkpoint was trained on, you can already use TFDistilBertForSequenceClassification for predictions without further training.
outputs2 = classifier(germeval_train_py)
outputs2
## [{'label': 'NEGATIVE', 'score': 0.9950070381164551}, {'label': 'NEGATIVE', 'score': 0.9954568147659302}]

Tja, vielleicht sollten wir ein Modell verwenden, das die deutsche Sprache versteht?

17.5.2 Man spricht Deutsh

Auf Hugging Face gibt es eine Menge von Modellen. Welches nehm ich nur? DISTILBERT oder BERT-Varianten dürfte kein schlechter Start sein.

#classifier = pipeline("text-classification", model="distilbert-base-german-cased")
classifier = pipeline(
  "text-classification", model="oliverguhr/german-sentiment-bert")
## All PyTorch model weights were used when initializing TFBertForSequenceClassification.
## 
## All the weights of TFBertForSequenceClassification were initialized from the PyTorch model.
## If your task is similar to the task the model of the checkpoint was trained on, you can already use TFBertForSequenceClassification for predictions without further training.
outputs3 = classifier(germeval_train_py)
df = pd.DataFrame(outputs3)    
df.head()
##       label     score
## 0   neutral  0.987253
## 1  negative  0.918047
df_r <- py$pd
head(df_r)

17.6 Fine-Tuning

17.6.1 Grundlagen

Definition 17.1 (Fine-Tuning) Unter Fine-Tuning4 versteht man das Anpassen der Gewichte eines großen (neuronalen) Sprachmodells (Large Language Models (LLM); Foundational Model) an einen spezifischen Datensatz. \(\square\)

Fine-Tuning ist eine Art “Trick”, wie man die Power eines großen Sprachmodells an die Spezifika eines bestimmten (Ihres!) Datensatzes anzupassen. Insofern könnte man sagen, dass man mit Fine-Tuning die Vorteile eines LLM nutzen kann, auch wenn man einen kleinen Datensatz hat.

Tipp

Nutzen Sie Fine-Tuning, wo immer möglich. Sie sparen nicht nur Energie und Rechenzeit und verbessern damit Ihren ökologischen Fußabdruck (als Nutzer von LLM haben Sie (wir!) ganz schön viel Energie verbraucht). Sie verbessern mit etwas Glück auch die prädiktive Leistung Ihres Modells.

Definition 17.2 (Zero-Shot-Learning) Nutzt man ein LLM ohne Fine-Tuning, etwa indem man das Modell mittels eines Prompts zu einer Sentiment-Klassifikation auffordert, so spricht man von Zero-Shot-Learning. In diesem Fall lernt das Modell ohne (spezifisches) Train-Sample. \(\square\)

17.6.2 Fine-Tuning vorgekocht

Natürlich kann man ein Modell selber an einen spezifischen Datensatz fitten. In dem Fall werden anstelle von Zufallsgewichten im neuronalen Netz die Gewichte des Modells als Ausgangspunkt genommen. Allerdings kann es auch sein, dass es auf einem Hub wie Hugging Face schon vortrainierte (“gefinetunte”?) Modelle gibt, so dass man sich die Arbeit des selber Fine-Tunings sparen kann.

Beispiel 17.1 In dieser Sammlung finden sich LLMs, die an deutschen Hate-Speech-Datasets weitertrainiert wurden. \(\square\)

Wir holen uns ein an deutschem Hate-Speech-Daten vortrainiertes Modell von Hugging Face:

pipe_bert_germeval = pipeline("text-classification", model="deepset/bert-base-german-cased-hatespeech-GermEval18Coarse")
## All PyTorch model weights were used when initializing TFBertForSequenceClassification.
## 
## All the weights of TFBertForSequenceClassification were initialized from the PyTorch model.
## If your task is similar to the task the model of the checkpoint was trained on, you can already use TFBertForSequenceClassification for predictions without further training.
Hinweis

Wenn man ein Modell zum ersten Mal anfragt, wird das Modell heruntergeladen; das kann ggf. etwas dauern (und braucht ewtas Speicherplatz). \(\square\)

Hier ist ein Beispiel-Satz:

text = '@Martin28a Sie haben ja auch Recht. Unser Tweet war etwas missverständlich. Dass das BVerfG Sachleistungen nicht ausschließt, kritisieren wir.'

Und dann lassen wir uns die Vorhersage des Modells ausgeben:

outputs = pipe_bert_germeval(text)
pd.DataFrame(outputs)
##    label     score
## 0  OTHER  0.994407

Dieses Modell wurde explizit am Datensatz germeval2018 (Coarse Classification) trainiert. Eine hohe Klassifikationsgüte ist daher vorprogrammiert. Bliebe noch zu prüfen, ob auch das Test-Sample zum Training verwendet wurde. \(\square\)

17.6.3 Fallbeispiel

Hier ist unser Germeval-Datensatz:

csv_file_path_test = 'https://github.com/sebastiansauer/pradadata/raw/master/data-raw/germeval_test.csv'

germeval_test = pd.read_csv(csv_file_path_test)

Nachdem der Datensatz als DataFrame vorliegt, konvertieren wir ihn noch zu einer Liste:

tweets = germeval_test["text"].tolist()

Zu Testzwecken ist es oft sinnvoll, sich einen “Toy-Datensatz” zu erstellen:

tweets_head = germeval_test["text"].head(2).tolist()

Und dann kommt das Vorhersagen. Zuerst, zum Testen, mit dem kleinen Spielzeug-Datensatz:

outputs = pipe_bert_germeval(tweets_head)
pd.DataFrame(outputs)
##    label     score
## 0  OTHER  0.971582
## 1  OTHER  0.571138

Schein zu klappen. Dann wagen wir uns also an den ganzen GermEval-(Test-)Datensatz:

start_time = time.time()

outputs = pipe_bert_germeval(tweets)
preds = pd.DataFrame(outputs)

end_time = time.time()
end_time - start_time

1250.577404975891

Da es einige Zeit gedauert hat, speichern wir uns die Predictions als CSV-Datei:

preds.to_csv("data/pipe_bert_germeval_preds.csv")

Und wenn man sie gespeichert hat, kann man sie wieder importieren:

preds = pd.read_csv("data/pipe_bert_germeval_preds.csv")

Zählen wir, wie oft jede Klasse vorhergesagt wurde:

preds["label"].value_counts()
## label
## OTHER      2442
## OFFENSE    1090
## Name: count, dtype: int64

Wir konvertieren die Label-Spalte der Vorhersagen in eine Python-Liste, da die Accuracy-Funktion dies verlangt:

preds_list = preds["label"].to_list()

Als Nächstes bewerten wir die Modellgüte5 im Test-Set.

Hier ist die Liste der wahren Werte (die sich in der Spalte c1 finden lassen):

y = germeval_test["c1"].values.tolist()
accuracy = accuracy_score(y, preds_list)
print("Accuracy:", accuracy)
## Accuracy: 0.7814269535673839

17.6.4 Overfitting?

Es ist nicht klar, ob unser Modell den GermEval-Test-Datensatz als Trainingsinput gesehen hat. In dem Fall wäre natürlich die Modellgüte von massivem Overfitting betroffen, also “too good to be true”; künftige Vorhersagen müssten mal also mit deutlich geringer Güte erwarten. \(\square\)

17.7 Vertiefung

Der Originalartikel von Vaswani u. a. (2023) gibt einen guten Einblick in die Konzepte; der Anspruch ist auf mittlerem Niveau. Von den Hugging-Face-Machern gibt es ein Buch, das - ebenfalls auf Einstiegs- bis mittlerem Niveau - einen Einblick in Transformer-Modelle im Hugging-Face-Ökosystem gewährt (Tunstall u. a. 2022). Rothman (2022) scheint gute Freunde bei Google zu haben, wenn man sein Buch über Transformer liest, jedenfalls sind die Modelle jener Firma in dem Buch gut gefeatured. Géron (2023a) Standardwerk zu Scikit-Learn bietet auch einen Einblick in Attention-Konzepte (Kap. 16). Übrigens ist das Buch (3. Auflage) jetzt auch in deutscher Sprache erhältlich (Géron 2023b).