Universelles Decoder-Modell

Beispielstudie

Die Methode

Als universelles Decoder-Modell wurde das Off-the-Shelf-Modell Meta-Llama-3.1-8B-Instruct (Grattafiori et al., 2024) verwendet. Zum Zeitpunkt der Durchführung der Beispielstudie war es unseres Wissens nach das leistungsfähigste universelle Decoder-Modell, das noch auf einem handelsüblichen Laptop genutzt werden konnte. Dieses Modell wurde mit öffentlich zugänglichen Texten (~15 Billionen Wörten) vortrainiert und anschließend anhand öffentlich verfügbarer Instruktionsdatensätze sowie über 25 Millionen synthetisch generierter Beispiele auf das Führen von Dialogen trainiert (vgl. Grattafiori et al., 2024). Da es sich bei dem Modell um ein universelles Decoder-Modell handelt, ermittelt das Modell nicht die Wahrscheinlichkeit des Vorliegens einer Ausprägung, sondern generiert einen Textoutput (s. auch Abschn. 2.1 im Paper). Um diesen Output weiterverarbeiten zu können, wurde das Modell explizit instruiert, die Antwort in maschinenlesbarem Format zu formulieren.

Der hier beschriebene Code ist deutlich umfangreicher als im Beispiel unter “Anleitungen” beschrieben, da er einige zusätzliche Funktionen enthält, die für die praktische Anwendung hilfreich sind. Dazu gehören:

  • Backup-Funktion: Zwischenergebnisse werden gespeichert, um bei Unterbrechungen den Prozess fortsetzen zu können.
  • Fehlerbehandlung: Das Skript kann Fehler bei der Verarbeitung einzelner Texte handhaben und den Prozess fortsetzen.
  • GPU/CPU-Fallback: Das Skript versucht zunächst, die GPU zu verwenden, und wechselt bei einem Fehler zur CPU.

Klassifikation

Python
import pandas as pd
from transformers import pipeline
import os
import json

# für CPU-Fallback:
# pip install accelerate
# und
import torch

daten = pd.read_csv("beispielstudie/data/00_goldstandard.csv")
datenliste = list(daten["text"])

backup_dir = "beispielstudie/data/backup"
os.makedirs(backup_dir, exist_ok=True)

generate_gpu = pipeline("text-generation", model="meta-llama/Meta-Llama-3.1-8B-Instruct", max_new_tokens=256, do_sample=False)

def get_cpu_pipeline():
    print("Lade CPU-Pipeline...")
    cpu_pipeline = pipeline(
        "text-generation",
        model="meta-llama/Meta-Llama-3.1-8B-Instruct",
        model_kwargs={"torch_dtype": torch.bfloat16},
        device_map="cpu",
        max_new_tokens=256, do_sample=False
    )
    print("CPU-Pipeline geladen!")
    return cpu_pipeline

generate_cpu = None

ergebnis = []

for idx, text in enumerate(datenliste, start=1):
    backup_file = f"{backup_dir}/{idx}.csv"
    
    if os.path.exists(backup_file):
        print(f"Text {idx} bereits klassifiziert, lade aus Backup...")
        try:
            existing_result = pd.read_csv(backup_file)
            ergebnis.append({
                "text": existing_result.iloc[0]["text"],
                "response": existing_result.iloc[0]["response"]
            })
            continue
        except Exception as e:
            print(f"Fehler beim Laden des Backups für Text {idx}: {e}")
            print("Führe neue Klassifikation durch...")
    
    print(f"Verarbeite Text {idx} von {len(datenliste)} ...") 
    
    instructions = [
        {"role": "system",
         "content": "Du bist ein trainierter Assistent für Inhaltsanalyse, der die allgemeine Stimmung beziehungsweise Tonalität von Texten analysiert. Antworte immer präzise im JSON-Format mit sentiment (positiv, neutral, negativ) und reasoning (eine Begründung auf Deutsch). Gib ausschließlich den JSON-Response, beginnend mit '{' und endend mit '}', mit diesen zwei Parametern zurück"},
        {"role": "user", "content": text}
    ]
    
    try:
        outputs = generate_gpu(instructions)
        print(f"Text {idx} erfolgreich auf GPU verarbeitet")
    except Exception as e:
        print(f"GPU-Fehler bei Text {idx} ({e}). Verwende CPU...")
         
        if generate_cpu is None:
            generate_cpu = get_cpu_pipeline()
        outputs = generate_cpu(instructions)
        print(f"Text {idx} erfolgreich auf CPU verarbeitet")
    
    result = {
        "text": text,
        "response": outputs[0]["generated_text"][-1]['content']
    }
    
    ergebnis.append(result)
    
    try:
        pd.DataFrame([result]).to_csv(backup_file, index=False)
        print(f"Backup für Text {idx} gespeichert")
    except Exception as e:
        print(f"Fehler beim Speichern des Backups für Text {idx}: {e}")

def parse_response(response):
    try:
        parsed = json.loads(response)
        return parsed['sentiment'], parsed['reasoning']
    except (json.JSONDecodeError, KeyError):
        return None, None

parsed_data = [(entry['text'], *parse_response(entry['response'])) for entry in ergebnis]
parsed_data = pd.DataFrame(parsed_data, columns=['text', 'sentiment', 'reasoning'])

vergleich = pd.DataFrame(parsed_data).drop("text", axis=1).join(daten)
vergleich.to_csv("beispielstudie/data/03_universelles_Decoder-Modell.csv", index=False)
1
Import der notwendigen Bibliotheken.
2
Laden der Daten.
3
Erstellen eines Backup-Verzeichnisses für Zwischenergebnisse.
4
Initialisierung der Textgenerierungs-Pipeline für die GPU.
5
Definition einer Funktion zur Initialisierung der CPU-Pipeline als Fallback.
6
Initialisierung einer Liste zur Speicherung der Ergebnisse.
7
Iteration über die Texte in den Daten.
8
Überprüfung, ob ein Backup für den aktuellen Text existiert, und Laden dieses Backups, falls vorhanden.
9
Definition der Anweisungen für das Modell, einschließlich der System- und Benutzerrollen.
10
Versuch, den Text auf der GPU zu verarbeiten, und bei einem Fehler Wechsel zur CPU.
11
Speichern des generierten Ergebnisses in der Ergebnisliste.
12
Speichern des Ergebnisses in einer Backup-Datei.
13
Definition einer Funktion zum Parsen der JSON-Antwort des Modells. Bei Fehlern im generierten JSON werden None-Werte zurückgegeben.
14
Parsen der Antworten und Erstellen eines DataFrames mit den Ergebnissen.
15
Zusammenführen der Ergebnisse mit den Originaldaten und Speichern in einer CSV-Datei.

Ergebnisse

Schauen wir uns eine kleine Auswahl der Ergebnisse an. Wir setzen einen Seed, um die gleiche Auswahl an Texten zu bekommen wie bei den anderen Modellen.

Code
R
set.seed(42)
readr::read_csv(here::here("beispielstudie/data/03_universelles_Decoder-Modell.csv"), show_col_types = FALSE) |>
  dplyr::arrange(desc(id)) |>
  dplyr::select(sentiment, reasoning, sentiment_gs, textart, text) |>
  dplyr::sample_n(5)
# A tibble: 5 × 5
  sentiment reasoning                                 sentiment_gs textart text 
  <chr>     <chr>                                            <dbl> <chr>   <chr>
1 positiv   Der Text beschreibt eine positive und mo…            1 Zeitun… "Dac…
2 neutral   Der Text enthält eine Vielzahl von Theme…           -1 Zeitun… "Inl…
3 positiv   Die Textpassagen beschreiben die Geburt …            1 Zeitun… "Bea…
4 positiv   Die Verwendung von positiven Worten wie …            1 Tweet   "Her…
5 positiv   Der Text enthält positive Aussagen wie '…            1 Facebo… "Wir…

Literatur

Grattafiori, A., Dubey, A., Jauhri, A., Pandey, A., Kadian, A., Al-Dahle, A., Letman, A., Mathur, A., Schelten, A., Vaughan, A., Yang, A., Fan, A., Goyal, A., Hartshorn, A., Yang, A., Mitra, A., Sravankumar, A., Korenev, A., Hinsvark, A., … Ma, Z. (2024, November 23). The Llama 3 Herd of Models. https://doi.org/10.48550/arXiv.2407.21783