Textaufbereitung für große Sprachmodelle.
Wir zeigen, welche Daten große Sprachmodelle verwenden und wie man eigene Textdaten aufbereiten kann.

LLM Frage & Antwort

Branche
divers
Thema
NLP, LLM, Webscraping
Tools
BeautifulSoup, ebooklib
Projektdauer
2 Wochen

LLM-Datensatz

Datensatz des LLaMa Trainigs

Um ein großes Sprachmodell zu trainieren, wird ein großer Datensatz verwendet. Die meisten großen Sprachmodelle haben gemeinsam, dass diese zum Großteil auf Website-Daten trainiert werden. Wir schauen uns den Datensatz des zurzeit bekanntesten Sprachmodells LLaMA von Meta/Facebook, genauer an.

Die wichtigsten Fakten über den für das LLaMA-Training verwendeten Datensatz sind:

  • Unterschiedliche Quellen

    Der Datensatz besteht aus einer Mischung von Daten aus verschiedenen Quellen, darunter CommonCrawl (en), C4, GitHub, Wikipedia, Gutenberg und Books3, ArXiv und Stack Exchange. Die Daten sind hierbei relativ unterschiedlich und umfassen neben normalen Website-Daten auch Bücher, Code und wissenschaftliche Texte.
  • Öffentlich verfügbare Daten

    Die für das Training von LLaMA verwendeten Daten stammen aus öffentlich zugänglichen Datensätzen, was bezüglich der Lizenzbedingungen und weiterer rechtlicher Grundlagen wichtig ist.
  • Unterschiedliche Sprachen

    Der Großteil der verwendeten Daten sind in Englisch. Bei dem CommonCrawl und C4-Datensatz zum Beispiel werden nicht englische Seiten entfernt. Die wissenschaftlichen Paper sind ebenfalls hauptsächlich auf Englisch, ebenso wie die Kommentare im GitHub-Code, Beiträge auf StackExchange, ... . Lediglich Bestandteile von Wikipedia umfassen 20 unterschiedliche Sprachen, darunter Deutsch, Spanisch, Französisch und Italienisch.
  • Datenaufbereitung

    Bei jedem Datensatz werden aufwendigen Aufbereitungsschritte durchgeführt. Hierzu zählen die Deduplizierung, bei dem ähnliche Texte entfernt werden, die Filterung nach englischer Sprache (außer Wikipedia), das Herausfiltern von Inhalten geringer Qualität, das Entfernen von Hyperlinks oder Kommentaren und das Filtern nach Lizenzen von Code.

Zusammenfassend lässt sich sagen, dass der LLaMA-Trainingsdatensatz verschiedene Datensätze umfasst und diese aufwendige Aufbereitungsschritte durchlaufen, um einen qualitativ hochwertigen Datensatz zu erzeugen. Generell hat das Modell eine Kenntnis über verschiedenste Sprachen, wenn auch nur sehr begrenzt.

Open-Source-Datensatz

Die einzelnen Bestandteile des Datensatzes für das LLaMA-Training sind zwar frei verfügbar, der Datensatz selber nach der Aufbereitung jedoch nicht. Möchte man sich einen eigenen Datensatz zusammenstellen, folgen hier die Links zu den einzelnen Bestandteilen:


Datensatz Links
CommonCrawl CommonCrawl GitHub
C4 GitHub - Huggingface - Washington Post Analyse
GitHub Google BigQuery - Info Google Codelabs
Wikipedia Wikipedia Dumps - Huggingface
Books Huggingface Pile Books3 - Gutenberg - PG19
arXiv arxiv
StackExchange archive - Huggingface

Es gibt verschiedene Projekte, die einen vergleichbaren Datensatz zu dem des LLaMA-Netzes aufgebaut haben:
RedPajama Datensatz auf Huggingface
Hier ist der Nachteil, dass ebenfalls wieder hauptsächlich englische Texte verwendet wurden. Lediglich die Wikipedia-Artikel hatten unterschiedliche Sprachen.

Darüber hinaus wurde ein vergleichbares Sprachmodell zu den LLaMA-Modellen trainiert namens Falcon, der Datensatz hierzu wurde ebenfalls veröffentlicht: Huggingface

Der Vorteil gegenüber den oben vorgestellten Datensätzen ist, dass bei der großen Variante des Netzes ein weiterer (leider nicht veröffentlichter) Datensatz verwendet wurde, bei dem europäische Sprachen mit 7 % Anteil verwendet wurden. Hiervon sind 26 % der Daten Deutsch. Was einem Gesamtanteil von 1,82 % entspricht. Dies entspricht immerhin einem Gesamtanteil von 1,82 % was aber natürlich immer noch eher gering ist.

Datenerzeugung

Die Web-Datensätze werden durch Webscraping über Crawling erzeugt. Hierbei werden große Mengen an Internetseiten heruntergeladen und der Text extrahiert. In Europa gibt es ebenfalls ein Projekt namens OSCAR (Open Super-large Crawled Aggregated coRpus), welcher einen großen Open Source Datensatz erzeugt.
Generell muss das Scraping bedacht angegangen werden und aktuell gibt es noch rechtliche Bedenken. Bei StableDiffusion zum Beispiel (eine KI zur Bilderzeugung) wurde erst vor kurzem ein mögliches opt out Verfahren eingeführt, damit die Bilder nicht für das Training verwendet werden. Außerdem zeigt sich, dass Scraping von den Websitebetreibern kritisch angesehen wird und die Zugriffe nicht mehr kostenfrei erfolgen sollen. , Möchte man ein eigenes Webscraping aufbauen, kann dies mit bereits vorhandenen Tools wie Scrapy, StormCrawler o.ä. aufgebaut werden.

Eigene Daten

Wie können wir nun eigene Dokumente in die großen Sprachmodelle bringen? Generell gibt es unterschiedliche Herangehensweisen. Entweder können diese Texte in das Sprachmodell eintrainiert werden, oder man verwendet ein allgemeines Sprachmodell und nutzt Text-Embedding. Dabei werden ähnliche Textbausteine zu der Anfrage in vorhandenen Dokumenten gesucht und dem Sprachmodell zugeführt. Im ersten Schritt zeigen wir, wie man die Daten in das Sprachmodell eintrainieren kann.

HTML

    import requests 
    from bs4 import BeautifulSoup
    
    URL = "https://www.gutenberg.org/files/2229/2229-h/2229-h.htm"
    
    # Herunterladen der .html Website 
    r = requests.get(URL)
    soup = BeautifulSoup(r.content, "html.parser")
    
    # Entfernen des Inhaltsverzeichnisses, der Überschriften, ...  
    text = soup.find_all("p")
    text = [p.text for p in text]
    
    # Entfernen der Einleitung am Anfang
    text = text[5:]
    print(" ".join(text[:1]))
Output
        DIREKTOR.
            Ihr beiden, die ihr mir so oft,
            In Not und Trübsal, beigestanden,
            Sagt, was ihr wohl in deutschen Landen
            Von unsrer Unternehmung hofft?
            [...]

Anschließende Datenaufbereitung:

    # Filtern des Textes, damit nur die Konversationen und keine weiteren Einleitungen o.ä. verwendet wird: 
    final_text = []
    for raw_text in text:
        split_txt = raw_text.lstrip().split("\r\n", 1)
        if split_txt[0].replace(".", "").isupper():
            final_text.append("\n".join(split_txt)) 
            
    final_text[:2]
Output
    ['DIREKTOR.\nIhr beiden, die ihr mir so oft,\r\n [...]',
    'DICHTER.\nIhr fühlet nicht, wie schlecht ein solches Handwerk sei!\r\n [...]']

EPUB


    import requests
    import ebooklib
    from ebooklib import epub
    from bs4 import BeautifulSoup

    URL = "https://www.gutenberg.org/ebooks/2229.epub.noimages"
    r = requests.get(URL)
    
    with open('Faust.epub', 'wb') as f: 
        f.write(r.content)
    
    book = epub.read_epub("Faust.epub")
    items = list(book.get_items_of_type(ebooklib.ITEM_DOCUMENT))
    
    # Ab hier vergleichbar zu html-Dateien
    final_text = []
    for item in items: 
        soup = BeautifulSoup(item.get_body_content().decode('utf-8'), "html.parser")
    
        # Entfernen des Inhaltsverzeichnisses, der Überschriften, ..., u.ä.  
        text = soup.find_all("p")
        text = [p.text for p in text]
    
        # Filtern des Textes, damit nur die Konversationen und keine weiteren Einleitungen o.ä. verwendet wird: 
        for raw_text in text:
            split_txt = raw_text.lstrip().split("\n", 1)
            if split_txt[0].isupper():
                final_text.append("\n".join(split_txt)) 
                
    final_text[:2]
Output
    ['DIREKTOR.\nIhr beiden, die ihr mir so oft,\nIn Not und Trübsal, beigestanden,\n [...]',
    'DICHTER.\nIhr fühlet nicht, wie schlecht ein solches Handwerk sei!\n [...]']
    

PDF

Für die PDF verwenden wir einen PDF-Ausdruck der bereits verwendeten Website. Hier wurde der Text nur relativ einfach extrahiert und manche Abschnitte zur Vereinfachung verworfen. Der Aufwand, PDF-Dateien aufzubereiten, ist deutlich größer, insbesondere wenn der Text nicht eingebettet ist. Hierbei müsste dann in einem ersten Schritt noch eine OCR-Umwandlung erfolgen.


    def get_sprecher(text_list:list)->list:
        """Gibt die einzelnen Sprecher zurück. Der Anfang wird verworfen!"""
        sprecher_list = []

        for i, text in enumerate(text_list): 
            if text.isupper(): 
                sprecher_list.append(i)
                
        merged_sprecher = []
        for sprecher, _ in enumerate(sprecher_list): 
            try:
                merged_sprecher.append("\n".join(text_list[sprecher_list[sprecher]: sprecher_list[sprecher+1]]))
            except IndexError:
                merged_sprecher.append("\n".join(text_list[sprecher_list[sprecher]:]))            
        return merged_sprecher

    import fitz

    doc = fitz.open("Faust.pdf")
    
    alle_konversationen = []
    for page in range(3, 130): 
        text = doc[page].get_text()
        text = text.split("/139")[1].lstrip().split("\n")
        alle_konversationen.extend(get_sprecher(text))
    alle_konversationen[:2]
Output
['DIREKTOR.\nIhr beiden, die ihr mir so oft,\nIn Not und Trübsal, beigestanden,\n [...]',
        'DICHTER.\nIhr fühlet nicht, wie schlecht ein solches Handwerk sei!\n [...]']

In unserem nächsten Blogbeitrag zeigen wir, wie die aufbereiteten Daten verwendet werden können um ein großes Sprachmodell an Faust anzupassen.