Zum Inhalt

Mein Notizbuch für Python & Machine Learning

Tokenisierung mit Byte Pair Encoding (BPE)

Die Tokenisierung ist ein entscheidender Schritt in vielen NLP-Anwendungen (Natural Language Processing). Sie bestimmt, wie Wörter (bzw. Text) in kleinere Einheiten – sogenannte Tokens – zerlegt werden. Diese Tokens können einzelne Buchstaben, Wortteile (Subwords) oder ganze Wörter sein. Besonders bei modernen Sprachmodellen und Anwendungen wie maschineller Übersetzung und Textklassifikation wird eine geschickte Tokenisierung immer wichtiger.

---
config:
  sankey:
    showValues: false
    linkColor: 'target'
    width: 450
    height: 300
---
%%{
init: {
    'theme': 'base',
    'themeVariables': {
    'primaryTextColor': '#888888'
    }
}
}%%
sankey-beta

Tokenisierung_,Token,10.0
Tokenisierung_,is,10.0
Tokenisierung_,ierung,10.0
mit_ Byte_,mit,10.0
mit_ Byte_,Byte,10.0
Pair_,P,10.0
Pair_,air,10.0
Encoding_,Enc,10.0
Encoding_,oding,10.0

Ein gängiges Verfahren dafür ist Byte Pair Encoding (BPE). Ursprünglich entstammt BPE einer Datenkompressionstechnik, hat jedoch Einzug in die NLP-Welt gefunden, um das Problem von seltenen Wörtern und unbekanntem Vokabular besser zu handhaben. Dieser Blog-Beitrag zeigt Schritt für Schritt, wie man einen BPE-Tokenizer aufbaut und wie ein solcher Tokenizer anschließend zum Einsatz kommt.

Git Strategie

Eine Git-Strategie definiert einen planmäßigen Ansatz für die Nutzung von Git in Softwareentwicklungsprojekten, besonders im Hinblick auf Branching, Merging und Release-Management.

%%{init: {
'logLevel': 'debug',
'theme': 'neutral',
'gitGraph': {'mainBranchName': 'production'}, 
'themeVariables': {'git0': '#5193e8', 'git1': '#e87451'}
}}%%
gitGraph:
    commit id:"Initial commit ..."
    branch development
    checkout development
    commit id:"init dev branch ..."
    commit id:" ..."
    commit
    checkout production
    commit id:" ...."
    commit id:"other commits ..." tag:"v1"
    commit id:" ....."
    merge development

Sie bestimmt, wie und wann Branches erstellt, zusammengeführt und verwaltet werden, um Entwicklung, Testing und Auslieferung von Software effizient und fehlerfrei zu gestalten.

Kontrastive Embeddings für die Text-zu-Bild-Suche

In diesem Beitrag stelle ich ein Projekt vor, das sich mit Text-zu-Bild-Retrieval1 befasst. Die Grundidee: Nutzerinnen und Nutzer geben eine kurze Beschreibung ein und erhalten jene Bilder vorgeschlagen, die am ehesten zum eingegebenen Text passen.

Um dieses Ziel zu erreichen, kommt ein Dual-Encoder-Ansatz zum Einsatz, der Bilder und Texte in einen gemeinsamen Merkmalsraum einbettet. Dadurch können Ähnlichkeiten verlässlich bestimmt und Bild-Text-Paare nach ihrer Übereinstimmung sortiert werden. Im Gegensatz zu klassischen Regressionslösungen entsteht durch kontrastives Training ein besonders aussagekräftiger Embedding-Raum, in dem das gesuchte Bild leichter auffindbar ist.

Im Folgenden erläutere ich die Datenstruktur, das Modell-Design sowie die rechtlichen Rahmenbedingungen (Compliance-Filter). Anschließend illustrieren Codebeispiele, wie das Modell trainiert und getestet wird und welche Performancekennzahlen (etwa Recall@5) für das Ranking in der Praxis besonders wichtig sind.

Optimierung von Algorithmen durch Verständnis der Berechnungskomplexität

In der Welt der Algorithmen spielt die Berechnungskomplexität eine zentrale Rolle. Sie gibt einen Einblick, wie viel Rechenaufwand erforderlich ist, um einen bestimmten Algorithmus auszuführen. Dieser Beitrag führt durch die Grundlagen der Berechnungskomplexität und zeigt anhand von Python-Code und mathematischen Beispielen, wie die Laufzeit von Algorithmen abgeschätzt werden kann.

Synchrone Gradientenaggregation bei Parameter-Servern

In der modernen Welt des maschinellen Lernens und der künstlichen Intelligenz spielen neuronale Netzwerke eine zentrale Rolle. Mit der wachsenden Komplexität und Größe dieser Modelle steigt jedoch auch der Bedarf, das Training effizient auf mehrere Maschinen zu verteilen. Hierbei erweisen sich Parameter-Server als unverzichtbares Werkzeug, um die enormen Rechenanforderungen durch verteiltes Training zu bewältigen. Parameter-Server ermöglichen es, die Berechnungen auf mehrere Rechner (Worker-Knoten1) aufzuteilen und die Modellparameter zentral zu speichern und zu aktualisieren.

sequenceDiagram
    participant DB as Datenbank
    participant W1 as Worker 1
    participant W2 as Worker 2
    participant PS as Parameter-Server

    DB->>W1: Sendet Trainingsdaten
    DB->>W2: Sendet Trainingsdaten

    W1->>PS: Sendet Gradienten
    W2->>PS: Sendet Gradienten

    PS-->>W1: Aggregierte & aktualisierte Parameter
    PS-->>W2: Aggregierte & aktualisierte Parameter

Dependency Injection und Interface Pattern in Python

Anwendung von Dependency Injection und Interface Pattern in Python am Beispiel eines
symmetrischen Verschlüsselungsprojekts: github.com/eugen-hoppe/symmetric

In der modernen Softwareentwicklung sind Design Patterns unerlässlich, da sie bewährte Lösungen für häufig auftretende Probleme bieten. Sie helfen, den Code wartbar, modular und erweiterbar zu gestalten. In diesem Beitrag wird das eigene GitHub-Projekt symmetric vorgestellt, das eine Bibliothek für symmetrische Verschlüsselung in Python umfasst. Im Fokus stehen zwei wichtige Design Patterns: das Interface Pattern und Dependency Injection.

Model-View-Controller in Python

Model-View-Controller (MVC) ist ein Architekturmuster in der Softwareentwicklung, das verwendet wird, um Anwendungen in drei Hauptkomponenten zu strukturieren: das Modell, die Ansicht und den Controller.

graph LR
    Controller -- "Anfrage über Datenänderung" ---> Model
    Controller -- "Auswahl der View" ---> View
    Model -- "Übermittlung der Daten" ---> Controller

Optimierung der Reihenfolge im Dockerfile

Die Strukturierung der Reihenfolge im Dockerfile ist aus mehreren Gründen wichtig, vor allem im Hinblick auf Effizienz und Sicherheit der App.

Hauptgründe für die Reihenfolge

Hintergrund: Layers im Docker System

Docker-Images bestehen aus Schichten, wobei jede Dockerfile-Anweisung in der Regel eine neue Schicht erzeugt. Diese Schichten sind Änderungsstapel am Dateisystem, die aufeinander aufbauen. Das Schichtensystem ermöglicht eine effiziente Speichernutzung und die Wiederverwendung von Schichten zwischen verschiedenen Images. Wichtig sind hierbei das Caching zur Beschleunigung des Build-Prozesses und die Auswirkungen auf die Image-Größe, Sicherheit sowie die Wiederverwendbarkeit und das Teilen von Schichten, um die Effizienz von Docker insgesamt zu steigern.

Ausnahmebehandlung in Python

Generierung eines neuen Ausnahmeobjekts

Um in Python eine neue Ausnahme zu werfen, während die abgefangene Exception im Traceback nachverfolgt werden kann, nutzt man das Schlüsselwort raise in Verbindung mit einem from, um die ursprüngliche Ausnahme anzugeben. Das from-Schlüsselwort erlaubt es Python, sowohl die neue als auch die ursprüngliche Ausnahme im Traceback zu verbinden und auszugeben, was für das Debugging sehr nützlich sein kann.

Hier ist ein Beispiel, wie man das machen kann:

try:
    # Code, der eine Ausnahme auslösen könnte
    raise ValueError("Ein Wertfehler ist aufgetreten")
except ValueError as original_exception:
    # Eine neue Ausnahme werfen und 
    # die ursprüngliche Ausnahme im Traceback behalten
    raise RuntimeError("Ein neuer Fehler") from original_exception
Traceback (most recent call last):
File "***.py", line 3, in <module>
    raise ValueError("Ein Wertfehler ist aufgetreten")
ValueError: Ein Wertfehler ist aufgetreten

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
File "***.py", line 7, in <module>
    raise RuntimeError("Ein neuer Fehler") from original_exception
RuntimeError: Ein neuer Fehler