memorySpeichereffizientes RL

Wir freuen uns, effizienteres Reinforcement Learning (RL) in Unsloth mit mehreren algorithmischen Verbesserungen vorzustellen:

  • 1,2- bis 1,7-fach erhöhte Kontextlängen ohne Verlangsamung und ohne zusätzlichen Speicherverbrauch!

  • 10% schnellere RL-Trainingläufe mit überarbeiteten Kerneln und asynchronen Datenbewegungen

  • 2x schneller torch.compile Mal während des Modellladens

Unsloth bereits erhöht die RL-Trainingsgeschwindigkeit, das Kontextfenster und reduziert den VRAM-Verbrauch um 50–90% gegenüber allen anderen Setups mit FA2, aber jetzt Unsloths Standby verbessert dies noch weiter. Unsere Standby-Funktion begrenzt einzigartig die Geschwindigkeitsverschlechterung im Vergleich zu anderen Implementierungen und macht das Training manchmal sogar schneller!

Jetzt kann Qwen3-32B LoRA 16-Bit 6.144 Kontextlängen erreichen vs. 3.600 (1,7x länger) zuvor auf einer 1xH100 80GB GPU. Llama-3.1-8B QLoRA 4bit kann 47.500 Längen erreichen vs. 42.000 zuvor (1,13x länger).

Wir haben RL-Läufe um 10% beschleunigt durch verschiedene Kernel-Optimierungen und den LoRA-Kommunikationskanal zwischen CPU und GPU entfernt, wenn vom Trainings- in den Inferenzmodus gewechselt wird. Schließlich haben wir benutzerdefinierte torch.compile Flags verwendet, um vLLM's Rollout um 10% zu beschleunigen, und die Kompilierzeit um das 2-fache reduziert.

Wie man Optimierungen aktiviert

nn.Linear Unsloths Standby Funktion, setzen Sie die Umgebungsvariable UNSLOTH_VLLM_STANDBY vor jedem Unsloth-Import. Setzen Sie dann gpu_memory_utilization = 0.95 und das war's!

import os
os.environ["UNSLOTH_VLLM_STANDBY"] = "1"

from unsloth import FastLanguageModel
import torch
model, tokenizer = FastLanguageModel.from_pretrained(
    model_name = "unsloth/Qwen3-8B-Base",
    max_seq_length = 2048, # Kann für längere Reasoning-Spuren erhöht werden
    load_in_4bit = False, # False für LoRA 16bit
    fast_inference = True,
    max_lora_rank = 32, # Größerer Rang = intelligenter, aber langsamer
    gpu_memory_utilization = 0.95,
)

🎓Nie mehr gpu_memory_utilization!

Mit Unsloths neuen RL-Verbesserungen müssen Sie sich NIE wieder um das Feinabstimmen oder Setzen von gpu_memory_utilization kümmern - setzen Sie es einfach auf 90% oder 95% GPU-Auslastung - 100% funktioniert leider nicht, da etwas Platz für kleine Tensoren benötigt wird. Früher musste man es zwischen 30% und 95% abstimmen - das ist jetzt nicht mehr nötig! Setzen Sie es auf das Maximum und Unsloth kümmert sich um den Rest!

⁉️Warum verwendet RL so viel Speicher?

GRPO (und viele RL-Varianten) sind stark auf Generation angewiesen, die hauptsächlich von vLLM angetrieben wird. Das hat jedoch hohe Kosten, da konstant GPU-Speicher für Gewichte, Aktivierungen und den KV-Cache benötigt wird.

Inference benötigt viel VRAM

Während Training ebenfalls VRAM verwendet!

Das bedeutet, RL muss gleichzeitig zwei Sätze von VRAM / Speicher auf der GPU halten:

  1. Inference-Engine (hat Modellgewichte, KV-Cache)

  2. Training-Engine (hat Modellgewichte, Aktivierungen, Gradienten, Optimizer-Zustände)

Aktuelle RL-Frameworks müssen 50/50 für eine 80GB GPU aufteilen, mit 50% für Inferenz und 50% für Training. Und das Verschieben von Gewichten vom Trainings- in den Inferenzmodus kann einige Zeit dauern.

80GB GPU
Inference Engine (50%)
Training Engine (50%)

Modellgewichte

16GB

16GB

KV-Cache

24GB

Aktivierungen, Gradienten, Optimizer-Zustände

24GB

Frühere Unsloth-Versionen optimierten das Obige bereits intelligent, da wir vLLM's Gewichtsspeicher direkt teilen, was die doppelte Speichernutzung der Modellgewichte entfernt. Das schafft zum Beispiel 16GB Platz frei, die verwendet werden können, um die Kontextlänge oder die Generationsgeschwindigkeit zu erhöhen. Außerdem müssen wir keine Speicherbewegungen durchführen, was das Training beschleunigt.

80GB GPU
Inference Engine (50%)
Training Engine (50%)

Modellgewichte

16GB GETEILT

<<< GETEILT

KV-Cache

24GB + 8GB= 32GB

Aktivierungen, Gradienten, Optimizer-Zustände

24GB + 8GB=32GB

🦥Unsloth Standby

Aber wir können noch weiter gehen - wir stellen zunächst fest, dass RL Inferenz, dann Training, dann wieder Inferenz, dann Training usw. durchführt.

Das bedeutet, der Speicherplatz für Inferenz und Training kann theoretisch wiederverwendet werden, da Inferenz und Training separate Modi sind - hier kommt vLLM's Schlafmodus-Funktionarrow-up-right ins Spiel, die 2 Optionen hat:

  1. level = 1 kopiert Gewichte auf die CPU und löscht den KV-Cache

  2. level = 2 löscht Gewichte und löscht den KV-Cache

Aber zur Erinnerung: In Unsloth teilen wir vLLM's Speicherplatz für die Gewichte - das bedeutet, wir brauchen eine neue Möglichkeit, den KV-Cache zu löschen und das Löschen der Gewichte zu ignorieren, und wir nennen das Unsloth Standby.

80GB GPU
Inference Engine
Training Engine

Modellgewichte

16GB GETEILT

<<< GETEILT

Mehrzweck

64GB Platz

KV-Cache

Aktivierungen, Gradienten, Optimizer-Zustände

Um dies zu aktivieren, fügen Sie einfach Folgendes zu allen RL / GRPO-Trainingsläufen hinzu, bevor Sie Unsloth importieren:

🧪Performance-Experimente

Hier erfahren Sie, wie wir die Speichernutzung und Kontextlänge für GRPO benchmarked haben. Beachten Sie, dass wir 2 Generationen pro Prompt durchführen, weil für GRPO zu funktionieren, wir mindestens 2 Generationen benötigen, um Mittelwert und Varianz der Stichprobe zu berechnen. Ohne 2 Generationen ist die Standardabweichung einer Stichprobe 0. Das verursacht, dass die Vorteile, die dies verwenden: (Belohnung - Mittelwert)/Std nicht definiert sind.

Z=riμ1n(riμ)2Zn=1=r1μ11(r1μ)2=00=undefinedZ=\frac{r_i - \mu}{\sqrt{\frac{1}{n}\sum(r_i-\mu)^2}} \\ Z_{n=1}=\frac{r_1 - \mu}{\sqrt{\frac{1}{1}\sum(r_1-\mu)^2}}=\frac{0}{0}=\text{undefined}

Das bedeutet, speziell für GRPO ist eine maximale Kontextlänge von 6.144 für Qwen-3 32B tatsächlich 6.144 multipliziert mit 2 Generationen, d. h. 12.288 in der Länge.

Wir stellen Experimente für Llama-3.1 8B sowohl für LoRA (16bit) als auch QLoRA (4bit) unten bereit:

Wenn Sie Unterschiede in der Trainingszeit bemerken, sind sie nicht groß. In unserem direkten Vergleich stellten wir <1% Trainingszeit-Verlangsamungen oder sogar Beschleunigungen fest, die dem Messfehler zugeschrieben werden können.

Wir vermuten außerdem, dass Beschleunigungen durch reduzierten Speicherdruck möglich sind, sodass weniger Speicherbereinigung auf der CUDA-Speicherallocator-Seite erforderlich sein könnte.

Im obigen Bild sehen Sie den Unterschied zwischen Baseline- und Standby-Modus auf einer einzelnen T4-GPU für Qwen 3 4B. Wir können vLLM's gpu_memory_utilisation auf bis zu 0,95 erhöhen, ohne zu befürchten, dass es das Training beeinträchtigt. Das bedeutet, Sie können höhere Kontextlängen-Sequenzen unterbringen und mehr Sequenzen können verarbeitet werden. Im ersten Fall zum Beispiel haben wir genug Speicher, um 32K-Längen-Sequenzen unterzubringen und zu verarbeiten, sofern das Training dies erlaubt, wohingegen früher alle Eingaben, die länger als 2K waren, möglicherweise nicht passten und zu OOMs (Out of Memory) führten.

Experimente
Konfiguration
Status
GPU-Speichernutzung
Kommentare

standby True

vllm_gpu_util 0.95

num_gen 2

grad_acc_steps 2

Läuft 40 Schritte / 40 Minuten

14,5 GiB (gesetzt durch vllm_gpu_util)

Genug, um in 32K KVCache mit Chunkgrößen von 2–4K zu passen oder z. B. 16K KVCache + 16K Chunks

standby True

vllm_gpu_util 0.9

num_gen 2

grad_acc_steps 2

Läuft 32 Schritte in 40 m

13,8 GiB (gesetzt durch…)

Ungefähr genug, um in ~28K KVCache mit Chunkgrößen von 2–4K zu passen oder z. B. 15K KVCache + 15K Chunks

standby False

vllm_gpu_util 0.9

num_gen 2

grad_acc_steps 2

Modell lädt, kann aber nicht trainieren, weil selbst Batch-Größe 1 nicht passt

OOM

standby False

vllm_gpu_util 0.8

num_gen 2

grad_acc_steps 2

Modell lädt, kann aber nicht trainieren, weil selbst Batch-Größe 1 nicht passt

OOM

standby False

vllm_gpu_util 0.7

num_gen 2

grad_acc_steps 2

Trainiert einwandfrei

28 Schritte dauern 39min

~15,1GiB

jede Eingabe, die etwas länger ist, führt zu OOM auf Colab

standby True

vllm_gpu_util 0.7

num_gen 2

grad_acc_steps 2

Trainiert einwandfrei

29 Schritte dauern 40min

13GiB, aber die meiste Zeit um die 10–11GB

Bei derselben Konfiguration sparen wir hier 2GiB bzw. 15% Speicher. Kann bei längeren Sequenzen höher sein

H100-Experimente

Modell
GPU
Seq-Länge
Anzahl Generationen
Grad-Acc-Schritte

Qwen2.5-14B-Instruct

NVIDIA H100 80GB PCIe

32,768

8

4

In unseren zusammenklappbaren Ergebnissen unten sehen Sie, dass es einen Unterschied von 9GiB im verwendeten Spitzenpeicher gibt (beachten Sie, dass in 90% der Fälle die GPU-Speichernutzung in unserem Fall dem Spitzenpeicher entspricht). Um das in Perspektive zu setzen: Mit TRL und LoRA konnten wir maximal ein 8B-Parameter-Modell mit einer Kontextlänge von 1024 feinabstimmen (32x weniger). Alles mit höherer Sequenzlänge (bei ähnlicher Konfiguration) führt dazu, dass der Prozess mit OOM fehlschlägt.

chevron-rightKlicken Sie für Unsloth Standby-Modus vs. keine Standby-Benchmarkshashtag

Das Bild unten zeigt, wie Standby im Vergleich zu Training ohne Standby mit Unsloth abschneidet. Es ist über 3 Läufe gemittelt, um sicherzustellen, dass die Metriken nicht verrauscht sind. Tatsächlich würden Sie, wenn Sie nahe genug heranzoomen, sehen, dass das Aktivieren des Standby es auch schneller macht, wahrscheinlich aufgrund des geringeren Speicherdrucks, wie zuvor besprochen.

Frühere A100 40GB-Experimente

In unseren früheren Experimenten auf einer A100 40GB GPU mit Qwen-2.5-3b-instruct und 8 Generationen pro Stichprobe beobachteten wir, dass ohne Standby das GRPO-Training (Modell in 16bit geladen, LoRA, nur Gewichte trainierbar) nur 6K Sequenzlängen zuließ. Mit unserer Standby-Funktion konnten wir 10K und mehr unterbringen! Zum Vergleich: TRL kann Ihnen nur Kontextlängen von bis zu 1K bei gleicher Batch-Größe bieten.

🎉Weitere Optimierungen

Wir wählen jetzt bessere Kompilierungsflags aus und reduzieren die Kompilierzeiten um 50% oder mehr. Wir haben es außerdem geschafft, jede vLLM-Version dynamisch zu patchen, um gc.collect besser aus Gründen der Rückwärtskompatibilität zu handhaben, inspiriert von diesem vLLM-Pull-Requestarrow-up-right. Das reduziert die Kompilierzeiten von 2 Minuten auf unter 40 Sekunden.

Wir haben außerdem torch.compile Flags optimiert und versucht, einige Flags zu aktivieren - leider combo_kernels und multi_kernel konnten mit vLLM 0.10 und Torch 2.8/2.9 nightly nicht korrekt funktionieren und coordinate_descent_tuning machte das Autotuning aller Kernel dramatisch langsamer. Früher dauerte die Kompilierung unter einer Minute, aber das Aktivieren davon dauerte über 13 Minuten und mehr, mit minimalen Leistungsgewinnen.

📚GRPO-Notebooks

Alle unsere GRPO-Notebooks haben Unsloth Standby standardmäßig aktiviert und alle Optimierungen! Siehe https://docs.unsloth.ai/get-started/unsloth-notebooksarrow-up-right für alle unsere GRPO-Notebooks, oder probieren Sie das Folgende:

Zuletzt aktualisiert

War das hilfreich?