<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Optimization on kenji.blog</title><link>http://kenji.blog/de/categories/optimization/</link><description>Recent content in Optimization on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>de</language><copyright>kenjinote</copyright><lastBuildDate>Fri, 11 Sep 2026 01:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/de/categories/optimization/index.xml" rel="self" type="application/rss+xml"/><item><title>Techniken zur Behebung von GPU-Speichermangel in der KI-Entwicklung (CPU-Offloading usw.)</title><link>http://kenji.blog/de/p/ai-gpu-vram-optimization-cpu-offloading/</link><pubDate>Fri, 11 Sep 2026 01:00:00 +0900</pubDate><guid>http://kenji.blog/de/p/ai-gpu-vram-optimization-cpu-offloading/</guid><description>&lt;img src="http://kenji.blog/p/ai-gpu-vram-optimization-cpu-offloading/img/eyecatch.jpg" alt="Featured image of post Techniken zur Behebung von GPU-Speichermangel in der KI-Entwicklung (CPU-Offloading usw.)" />&lt;h1 id="einführung-ki-entwicklung-und-die-vram-wand">Einführung: KI-Entwicklung und &amp;ldquo;Die VRAM-Wand&amp;rdquo;
&lt;/h1>&lt;p>In den letzten Jahren hat die generative KI-Technologie, wie große Sprachmodelle (LLMs) und Diffusionsmodelle (Diffusion Models), rasante Fortschritte gemacht. Wenn jedoch Entwickler und Forscher versuchen, diese modernsten KI-Modelle in lokalen Umgebungen zu trainieren (Fine-Tuning) oder auszuführen (Inference), stoßen viele auf ein sehr physisches Hindernis: &lt;strong>&amp;ldquo;GPU-Speichermangel (VRAM)&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;p>Selbst bei High-End-GPUs für Endverbraucher, wie der NVIDIA GeForce RTX 4090, beträgt der maximale VRAM 24 GB, was es völlig unmöglich macht, ein riesiges Modell wie Llama 3 70B direkt zu laden. GPUs für Rechenzentren wie H100 (80 GB) oder B200 (192 GB) sind extrem teuer und für Einzelpersonen oder kleine Teams nicht leicht zugänglich. Wenn man diese &amp;ldquo;VRAM-Wand (The Wall of VRAM)&amp;rdquo; nicht durchbrechen kann, kann man modernste Modelle gar nicht erst berühren.&lt;/p>
&lt;p>In diesem Artikel werden wir fortgeschrittene Techniken gründlich erläutern, um diese physische Einschränkung des VRAM-Limits durch software- und hardwarearchitektonische Kniffe sowohl beim Training als auch bei der Inferenz zu überwinden. Wir werden CPU-Offloading, KV-Cache-Optimierung, Gradient Checkpointing und neueste Unified-Memory-Architekturen anhand von mathematischen Formeln und Diagrammen vertiefen. Wenn Sie diesen Artikel lesen, werden Sie das Verhalten von VRAM tiefgreifend verstehen und praktisches Wissen erwerben, um riesige Modelle mit begrenzten Ressourcen zu handhaben.&lt;/p>
&lt;hr>
&lt;h1 id="1-anatomie-des-vram-verbrauchs-von-ki-modellen-inferenz--training">1. Anatomie des VRAM-Verbrauchs von KI-Modellen (Inferenz / Training)
&lt;/h1>&lt;p>Der erste Schritt zur Behebung des VRAM-Mangels besteht darin, aus einer Mikroperspektive genau zu verstehen, &amp;ldquo;was&amp;rdquo; und &amp;ldquo;wie viel&amp;rdquo; Speicher verbraucht. Anstatt ihn als Blackbox zu behandeln, können wir, wenn wir ihn mit mathematischen Formeln genau abschätzen, die richtigen Optimierungsmethoden auswählen.&lt;/p>
&lt;h2 id="11-speicherberechnung-für-modellparameter-gewichte">1.1 Speicherberechnung für Modellparameter (Gewichte)
&lt;/h2>&lt;p>Die grundlegende Speichermenge, die von den Parametern (Weights), aus denen ein KI-Modell besteht, verbraucht wird, wird durch die Gesamtzahl der Parameter des Modells und den Datentyp (Precision), der zu ihrer Darstellung verwendet wird, bestimmt.&lt;/p>
&lt;p>Die im Deep Learning häufig verwendeten Datentypen und die Anzahl der Bytes pro Parameter ($B$) sind wie folgt:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>FP32 (einfache Genauigkeit):&lt;/strong> 4 Bytes (Standardgenauigkeit für Training)&lt;/li>
&lt;li>&lt;strong>FP16 / BF16 (halbe Genauigkeit):&lt;/strong> 2 Bytes (allgemeine Inferenz und Mixed-Precision-Training)&lt;/li>
&lt;li>&lt;strong>INT8 (8-Bit-Ganzzahl):&lt;/strong> 1 Byte (quantisierte Modelle)&lt;/li>
&lt;li>&lt;strong>INT4 (4-Bit-Ganzzahl-Quantisierung):&lt;/strong> 0,5 Bytes (extreme Quantisierung wie GPTQ, AWQ, GGUF)&lt;/li>
&lt;/ul>
&lt;p>Wenn die Anzahl der Parameter des gesamten Modells $P$ ist, wird die Basisspeichermenge $M_{weights}$, die von den Gewichten selbst belegt wird, durch die folgende Formel ausgedrückt:&lt;/p>
$$ M_{weights} = P \times B $$&lt;p>Wenn man beispielsweise das von Meta veröffentlichte &amp;ldquo;Llama 3 8B&amp;rdquo;-Modell (ca. 8 Milliarden Parameter) in FP16 (halbe Genauigkeit) lädt, sieht die Berechnung wie folgt aus:&lt;/p>
$$ M_{weights} = 8,000,000,000 \times 2 \text{ bytes} \approx 16,000,000,000 \text{ bytes} \approx 16 \text{ GB} $$&lt;p>Das bedeutet, dass allein das Laden der Gewichte des Modells in die GPU 16 GB VRAM verbraucht. Bei einer RTX 3060 (12 GB) tritt an diesem Punkt ein Out-of-Memory (OOM) Fehler auf. Wenn man das Modell jedoch auf INT4 quantisiert, wird es $8 \times 0.5 = 4 \text{ GB}$ groß und kann problemlos geladen werden.&lt;/p>
&lt;h2 id="12-speicherverbrauch-bei-der-inferenz-wachstum-des-kv-caches">1.2 Speicherverbrauch bei der Inferenz: Wachstum des KV-Caches
&lt;/h2>&lt;p>Bei der LLM-Inferenz (insbesondere bei der autoregressiven Textgenerierung) ist es der &lt;strong>KV-Cache (Key-Value Cache)&lt;/strong>, der den VRAM genauso stark oder stärker als die Gewichte beansprucht.
In der Transformer-Architektur werden die Key- und Value-Tensoren in jeder Attention-Schicht kontinuierlich im VRAM gecacht, um eine Neuberechnung der Informationen von Tokens, die in der Vergangenheit generiert und verarbeitet wurden, zu verhindern. Dies verbessert zwar die Berechnungsgeschwindigkeit (Compute), aber die Speichernutzung steigt linear und explosiv an, je länger die Kontextlänge (Eingabeprompt-Länge + generierte Länge) wird.&lt;/p>
&lt;p>Die Menge an KV-Cache-Speicher $M_{kv\_token}$, die bei der Verarbeitung von 1 Token verbraucht wird, wird basierend auf der Modellarchitektur mit der folgenden Formel genau berechnet:&lt;/p>
$$ M_{kv\_token} = 2 \times N_{layers} \times N_{heads\_kv} \times D_{head} \times B $$&lt;p>Hier haben die Variablen folgende Bedeutungen:&lt;/p>
&lt;ul>
&lt;li>$2$ : Weil es zwei Tensoren für Key und Value gibt&lt;/li>
&lt;li>$N_{layers}$ : Anzahl der Transformer-Schichten (Layers)&lt;/li>
&lt;li>$N_{heads\_kv}$ : Anzahl der KV-Attention-Heads (Bei GQA: Grouped Query Attention ist dies geringer als die normale Anzahl der Heads)&lt;/li>
&lt;li>$D_{head}$ : Anzahl der Dimensionen pro Head (normalerweise Anzahl der Dimensionen der verborgenen Schicht $D_{model} / N_{heads}$)&lt;/li>
&lt;li>$B$ : Anzahl der Bytes des Datentyps (2 für FP16)&lt;/li>
&lt;/ul>
&lt;p>Die gesamte KV-Cache-Menge $M_{kv\_total}$ wird berechnet, indem dies mit der Sequenzlänge ($L_{seq}$) und der Batch-Größe ($BatchSize$) multipliziert wird.&lt;/p>
$$ M_{kv\_total} = M_{kv\_token} \times L_{seq} \times BatchSize $$&lt;p>&lt;strong>Konkretes Beispiel: Bei Llama 2 7B&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>$N_{layers} = 32$&lt;/li>
&lt;li>$N_{heads\_kv} = 32$ (Bei MHA)&lt;/li>
&lt;li>$D_{head} = 128$&lt;/li>
&lt;li>FP16 ($B=2$)&lt;/li>
&lt;li>Batch-Größe 1, Sequenzlänge 8192 (8K Kontext)&lt;/li>
&lt;/ul>
$$ M_{kv\_total} = 2 \times 32 \times 32 \times 128 \times 2 \times 8192 \times 1 = 4,294,967,296 \text{ bytes} \approx 4 \text{ GB} $$&lt;p>Wenn man den Kontext auf 32K (32768 Token) verlängert, verbraucht allein der KV-Cache etwa 16 GB. Wenn man die Batch-Größe auf 4 erhöht, sind es 64 GB. Es ist eine große Herausforderung bei der Inferenz, dass sie weit mehr VRAM benötigt als das Modell selbst.&lt;/p>
&lt;h2 id="13-speicherverbrauch-beim-training-optimierer-gradienten-und-aktivierungen">1.3 Speicherverbrauch beim Training: Optimierer, Gradienten und Aktivierungen
&lt;/h2>&lt;p>Im Vergleich zur Inferenz verbraucht das Modelltraining (Pre-Training oder Fine-Tuning) weitaus mehr VRAM. Das liegt daran, dass Informationen für die Backpropagation (Rückpropagation) gespeichert werden müssen, nicht nur ein einfacher Forward-Pass (Vorwärtspropagation). Der Speicher für das Training besteht hauptsächlich aus den folgenden 4 Elementen:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Modellgewichte (Model Weights):&lt;/strong> Wie bei der Inferenz, aber beim Mixed-Precision-Training können sowohl FP16 als auch FP32 (Master Weights) aufbewahrt werden.&lt;/li>
&lt;li>&lt;strong>Gradienten (Gradients):&lt;/strong> Gradienten pro Parameter, die durch die Backpropagation berechnet werden. Bei FP16 sind das 2 Bytes pro Parameter.&lt;/li>
&lt;li>&lt;strong>Optimiererzustände (Optimizer States):&lt;/strong> Hochfunktionale Optimierer wie AdamW speichern das erste Moment (Momentum) und das zweite Moment (Variance) für jeden Parameter. Um die Trainingsstabilität zu erhalten, werden diese normalerweise in FP32 (4 Bytes) aufbewahrt. Das bedeutet, dass $4 + 4 = 8$ Bytes pro Parameter für die zwei Momente verbraucht werden.&lt;/li>
&lt;li>&lt;strong>Aktivierungen (Activations):&lt;/strong> Um die Gradienten in der Backpropagation zu berechnen, muss die Ausgabe (Zwischenzustand) jeder Schicht während des Forward-Passes im Speicher gehalten werden. Dies hängt stark von der Batch-Größe und der Sequenzlänge ab und wird sehr groß.&lt;/li>
&lt;/ol>
&lt;p>Zusammenfassend benötigt das Mixed-Precision-Training (gemischte Genauigkeit) mit einem Standard-Adam-Optimierer &lt;strong>ca. 16 bis 20 Bytes&lt;/strong> pro Parameter (Mastergewichte 4 + FP16-Gewichte 2 + Gradienten 2 + Optimierer 8 + α) an Speicher.&lt;/p>
$$ M_{train\_param} \approx P \times 16 \text{ bytes} $$&lt;p>Das Training eines 7B (7 Milliarden Parameter) Modells erfordert $7B \times 16 = 112 \text{ GB}$ allein für Parameterbezogenes, dazu kommen die Aktivierungen, sodass schätzungsweise über 140 GB VRAM benötigt werden. Um dies mit 24 GB VRAM auszuführen, sind die starken Optimierungstechniken unerlässlich, die im nächsten Kapitel erläutert werden.&lt;/p>
&lt;hr>
&lt;h1 id="2-vram-spartechniken-bei-der-inferenz">2. VRAM-Spartechniken bei der Inferenz
&lt;/h1>&lt;p>Viele Softwaretechnologien, die Hardwaregrenzen überschreiten, wurden als Ansätze entwickelt, um riesige Modelle bei der Inferenz auszuführen.&lt;/p>
&lt;h2 id="21-cpu-offloading-und-layer-splitting">2.1 CPU-Offloading und Layer-Splitting
&lt;/h2>&lt;p>Wenn ein riesiges Modell nicht in eine oder mehrere GPUs passt, ist eine Methode, einen Teil des Modells in den Systemspeicher (CPU-RAM) zu platzieren und Berechnungen durchzuführen, während es nur bei Bedarf in die GPU übertragen wird. Dies ist das &lt;strong>CPU-Offloading&lt;/strong>. &lt;code>llama.cpp&lt;/code> und Hugging Face&amp;rsquo;s &lt;code>Accelerate&lt;/code> unterstützen diese Funktion.&lt;/p>
&lt;pre class="mermaid">
graph TD
A[&amp;#34;System RAM (DDR4 / DDR5)&amp;#34;] --&amp;gt; B[&amp;#34;GPU VRAM (GDDR6X)&amp;#34;]
B[&amp;#34;GPU VRAM (GDDR6X)&amp;#34;] --&amp;gt; C[&amp;#34;Tensor-Kerne (Berechnung)&amp;#34;]
subgraph &amp;#34;Layer Splitting and Offloading&amp;#34;
D[&amp;#34;Untere Schichten 1-15 (an GPU gebunden)&amp;#34;]
E[&amp;#34;Obere Schichten 16-32 (auf CPU ausgelagert)&amp;#34;]
end
E[&amp;#34;Obere Schichten 16-32 (auf CPU ausgelagert)&amp;#34;] -.-&amp;gt; B[&amp;#34;GPU VRAM (GDDR6X)&amp;#34;]
&lt;/pre>
&lt;p>&lt;strong>Mechanismus und Herausforderungen:&lt;/strong>
Da das Transformer-Modell eine Struktur aufweist, in der Schichten (Layers) in Reihe geschichtet sind, beginnt die Berechnung der nächsten Schicht erst, wenn die Berechnung einer Schicht abgeschlossen ist. Indem man dies ausnutzt, werden nur die Schichten, die in die GPU passen (z. B. Schicht 1 bis 15), dauerhaft im VRAM gehalten (Pinned), und die restlichen Schichten (Schicht 16 bis 32) werden in den großen, aber langsamen CPU-RAM gelegt. Während der Inferenz, wenn die Berechnung bis Schicht 15 abgeschlossen ist, werden die Gewichte der 16. Schicht vom CPU über den PCIe-Bus zur GPU übertragen (kopiert), und die Berechnung wird auf der GPU ausgeführt.&lt;/p>
&lt;p>Die &lt;strong>PCIe-Bandbreite (Bandwidth) ist jedoch ein massiver Flaschenhals&lt;/strong>. Die theoretische maximale Bandbreite von PCIe 4.0 x16 beträgt 32 GB/s (in eine Richtung), was zwei Größenordnungen langsamer ist im Vergleich zur internen VRAM-Bandbreite moderner GPUs (z. B. ist der GDDR6X der RTX 4090 1008 GB/s und der HBM3 des H100 mehr als 3 TB/s). Daher sinkt die Inferenzgeschwindigkeit (Tokens per Second) dramatisch, wenn CPU-Offloading stark genutzt wird.
Um den Geschwindigkeitsverlust zu minimieren, ist es in der Praxis entscheidend, so viele Schichten wie möglich auf die GPU zu laden (Maximierung der GPU Layers) und die Anzahl der offloadeten Schichten zu minimieren.&lt;/p>
&lt;h2 id="22-kv-cache-quantisierung-und-pagedattention">2.2 KV-Cache-Quantisierung und PagedAttention
&lt;/h2>&lt;p>Auch gegen den KV-Cache, den Hauptverursacher des VRAM-Verbrauchs während der Inferenz, werden zwei starke Optimierungen durchgeführt.&lt;/p>
&lt;p>&lt;strong>1. KV-Cache-Quantisierung (KV Cache Quantization):&lt;/strong>
Eine Methode, bei der nicht nur die Gewichte des Modells, sondern auch der zur Laufzeit dynamisch generierte KV-Cache selbst in INT8, INT4 oder sogar FP8 quantisiert und im VRAM gespeichert wird. Dies reduziert die Größe des KV-Caches auf die Hälfte oder ein Viertel. Neueste Inferenz-Engines (vLLM und llama.cpp) haben diese Funktion integriert und erreichen erhebliche VRAM-Einsparungen, während der Genauigkeitsverlust minimiert wird.&lt;/p>
&lt;p>&lt;strong>2. PagedAttention:&lt;/strong>
Das Konzept des &amp;ldquo;Paging&amp;rdquo; im virtuellen Speicher des Betriebssystems auf den KV-Cache anzuwenden, ist die &lt;strong>PagedAttention&lt;/strong>, die durch eine Inferenz-Engine namens vLLM eingeführt wurde. Bei herkömmlichen Inferenz-Engines wurden basierend auf der eingestellten maximalen Sequenzlänge im Voraus zusammenhängende VRAM-Bereiche reserviert (Pre-allocation). Dies führte bei kurzen Eingaben zu Fragmentierung oder einer Verschwendung ungenutzten Speichers, wodurch oft über 60 % des VRAMs verschwendet wurden.&lt;/p>
&lt;p>PagedAttention unterteilt den KV-Cache in Blöcke (Pages) fester Größe, die in nicht zusammenhängenden physischen Speicherbereichen verteilt abgelegt werden können. Dadurch wird die Speicherverschwendung nahezu auf Null reduziert (beschränkt auf interne Fragmentierung), und die Batch-Größe kann bei gleicher VRAM-Kapazität erheblich gesteigert werden.&lt;/p>
&lt;pre class="mermaid">
graph LR
A[&amp;#34;Logischer KV-Cache&amp;#34;] --&amp;gt; B[&amp;#34;Physische VRAM-Blöcke&amp;#34;]
A1[&amp;#34;Token 1, 2, 3, 4&amp;#34;] --&amp;gt; B3[&amp;#34;Block 3 (Zugewiesen)&amp;#34;]
A2[&amp;#34;Token 5, 6, 7, 8&amp;#34;] --&amp;gt; B1[&amp;#34;Block 1 (Zugewiesen)&amp;#34;]
A3[&amp;#34;Zukünftige Tokens...&amp;#34;] -.-&amp;gt; B2[&amp;#34;Block 2 (Frei)&amp;#34;]
&lt;/pre>
&lt;h2 id="23-flashattention-überwindung-der-speicherkomplexität-von-attention-berechnungen">2.3 FlashAttention: Überwindung der Speicherkomplexität von Attention-Berechnungen
&lt;/h2>&lt;p>Der VRAM-Mangel wird nicht nur durch die Speichermenge für Daten verursacht, sondern auch durch den Mangel an &amp;ldquo;temporärem Arbeitsbereich&amp;rdquo; während der Berechnung. Der Standard-Self-Attention-Mechanismus des Transformers erfordert, dass eine riesige Attention-Matrix der Größe $N \times N$ im VRAM für eine Sequenzlänge $N$ materialisiert (Materialize) wird. Dies führt zu einer Speicherkomplexität von $O(N^2)$ und ist die Hauptursache für OOM bei langen Kontexten.&lt;/p>
&lt;p>Das Problem wurde durch &lt;strong>FlashAttention&lt;/strong> (und FlashAttention-2, 3) gelöst.
FlashAttention ist ein Algorithmus, der sich der Hardwarearchitektur der GPU (die hierarchische Struktur aus riesigem, aber langsamem HBM und winzigem, aber extrem schnellem SRAM) bewusst ist. Unter Verwendung einer Technik namens Tiling (Kachelung) werden Daten blockweise in den SRAM geladen und die Attention-Berechnung wird dort abgeschlossen, wodurch der Prozess, eine $N \times N$-Matrix in den HBM (VRAM) zu schreiben, vollständig vermieden wird.&lt;/p>
&lt;p>Dadurch sank die Speicherkomplexität der Attention-Schicht drastisch von $O(N^2)$ auf $O(N)$ (proportional zur Sequenzlänge), und die Begrenzungen der Kontextlänge wurden erheblich gelockert.&lt;/p>
&lt;h2 id="24-der-aufstieg-von-unified-memory-und-apple-silicon">2.4 Der Aufstieg von Unified Memory und Apple Silicon
&lt;/h2>&lt;p>Ein Ansatz, der dieses Problem grundlegend von der PC-Architektur aus angeht, ist die &lt;strong>Unified Memory Architecture (UMA)&lt;/strong>, die von Apple Silicon (Max und Ultra der M1/M2/M3/M4-Serie) und einigen neuesten APUs (wie AMD Strix Point) übernommen wurde.&lt;/p>
&lt;p>In diesen Architekturen teilen sich CPU und GPU auf dem Motherboard genau denselben physischen Speicher (z. B. bis zu 192 GB LPDDR5). Daher existiert das Konzept einer &amp;ldquo;langsamen Datenübertragung von CPU zu GPU über PCIe&amp;rdquo; physikalisch nicht.&lt;/p>
&lt;pre class="mermaid">
graph TD
subgraph &amp;#34;Unified Memory Architecture (e.g. Apple Silicon)&amp;#34;
A[&amp;#34;CPU-Kerne&amp;#34;] &amp;lt;--&amp;gt; C[&amp;#34;Gemeinsamer Speichercontroller&amp;#34;]
B[&amp;#34;GPU-Kerne / Neural Engine&amp;#34;] &amp;lt;--&amp;gt; C[&amp;#34;Gemeinsamer Speichercontroller&amp;#34;]
C[&amp;#34;Gemeinsamer Speichercontroller&amp;#34;] &amp;lt;--&amp;gt; D[&amp;#34;Gemeinsamer Speicherpool (z. B. 192 GB)&amp;#34;]
end
&lt;/pre>
&lt;p>Der größte Vorteil dieser Architektur besteht darin, dass es keine klare Wand namens VRAM gibt und fast der gesamte Systemspeicher direkt zum Laden riesiger LLMs verwendet werden kann. Mit einem Mac Studio, das über 192 GB Unified Memory verfügt, können riesige Modelle der 70B-Klasse oder größer (wie Grok-1) ohne Quantisierung auf ein einziges Gerät geladen und mit hoher Geschwindigkeit interferiert werden. Die Speicherzugriffsbandbreite erreicht beim M2 Ultra 800 GB/s und steht damit herkömmlichen diskreten GPUs für Verbraucher in nichts nach. Es ist ein sehr starker Ansatz, der das Dilemma zwischen &amp;ldquo;Speicherkapazität&amp;rdquo; und &amp;ldquo;Bandbreite&amp;rdquo; auf Hardwareebene löst.&lt;/p>
&lt;hr>
&lt;h1 id="3-vram-spartechniken-beim-training-fine-tuning">3. VRAM-Spartechniken beim Training (Fine-Tuning)
&lt;/h1>&lt;p>Auch beim Training, das noch mehr VRAM erfordert als die Inferenz, gab es viele Durchbrüche. Um mit begrenzten Ressourcen Fine-Tuning durchzuführen, ist die Kombination der folgenden Technologien unerlässlich.&lt;/p>
&lt;h2 id="31-gradient-checkpointing">3.1 Gradient Checkpointing
&lt;/h2>&lt;p>Bei der Backpropagation im Deep Learning müssen die Zwischenausgaben (Activations) aller Schichten aus dem Forward-Pass im Speicher gehalten werden, um Gradienten zu berechnen. Wenn Sequenzlänge oder Batch-Größe ansteigen, beginnt dieser Aktivierungsspeicher den VRAM zu dominieren.&lt;/p>
&lt;p>&lt;strong>Gradient Checkpointing (Gradient Checkpointing / Activation Recomputation)&lt;/strong> ist eine geniale Technik, die den Kompromiss zwischen Speicherkapazität und Berechnungszeit (Compute) ausnutzt.
Anstatt alle Zwischenausgaben im Speicher zu speichern, werden nur die Ausgaben bestimmter Schichten (Checkpoints) gespeichert. Wenn während der Backpropagation ein Zwischenwert benötigt wird, der nicht gecheckt wurde, &lt;strong>wird der Forward-Pass ab dem nächstgelegenen gespeicherten Checkpoint erneut berechnet (Recomputation), um den Wert wiederherzustellen&lt;/strong>.&lt;/p>
&lt;p>Der Rechenaufwand steigt um etwa 20 bis 30 %, wodurch die gesamte Trainingszeit länger wird. Der durch Aktivierungen verursachte VRAM-Verbrauch kann jedoch drastisch von $O(N)$ ($N$ ist die Anzahl der Schichten) auf $O(\sqrt{N})$ reduziert werden. Beim aktuellen Training großer Modelle ist dies ein so wesentlicher Parameter, dass man sagen kann, ohne ihn könne man nicht anfangen.&lt;/p>
&lt;h2 id="32-lora-und-qlora-low-rank-adaptation">3.2 LoRA und QLoRA (Low-Rank Adaptation)
&lt;/h2>&lt;p>Der Hauptdarsteller, der das VRAM-Problem grundlegend gelöst hat, ist &lt;strong>LoRA&lt;/strong>, ein typisches Beispiel für PEFT (Parameter-Efficient Fine-Tuning).&lt;/p>
&lt;p>Die ursprüngliche riesige Gewichtsmatrix $W_0 \in \mathbb{R}^{d \times k}$ des Modells wird eingefroren (Frozen) und nicht trainiert. Stattdessen werden zwei sehr kleine Matrizen mit niedrigem Rang $A \in \mathbb{R}^{r \times k}$ und $B \in \mathbb{R}^{d \times r}$ parallel eingeführt, und nur dieses $A$ und $B$ werden trainiert. (Hier ist der Rang $r$ ein sehr kleiner Wert, sodass $r \ll d, k$).&lt;/p>
$$ W_{adapted} = W_0 + \Delta W = W_0 + B A $$&lt;p>Dadurch wird die Anzahl der trainierbaren Parameter auf unter 1 % (manchmal unter 0,1 %) des Originals reduziert, und dementsprechend sinken auch die &amp;ldquo;Gradienten&amp;rdquo; und &amp;ldquo;Optimiererzustände&amp;rdquo;, die sehr viel Speicher gefressen haben, auf unter 1 %.&lt;/p>
&lt;p>Eine noch extremere Weiterentwicklung hiervon ist &lt;strong>QLoRA (Quantized LoRA)&lt;/strong>.
Bei QLoRA wird das Basisgewicht $W_0$ des Modells extrem auf 4-Bit (NF4: NormalFloat4 Format) quantisiert und in den VRAM geladen. Und die kleinen LoRA-Matrizen $A, B$ werden in BF16 (16-Bit) trainiert, um die Rechengenauigkeit aufrechtzuerhalten.
Die 4-Bit-Quantisierung reduziert die VRAM-Größe des Basismodells auf ein Viertel, während eine Technologie namens &lt;strong>Paged Optimizers&lt;/strong> verwendet wird, um den Zustand des Optimierers automatisch vorübergehend in den CPU-RAM zu sichern (offload), falls der VRAM knapp wird. Dadurch ist es nun möglich, selbst superriesige Modelle wie das Llama 3 70B auf einer einzelnen GPU mit 24 GB VRAM (wie einer RTX 4090) zu fine-tunen.&lt;/p>
&lt;h2 id="33-deepspeed-zero-und-offloading">3.3 DeepSpeed ZeRO und Offloading
&lt;/h2>&lt;p>In Umgebungen, in denen mehrere GPUs (Multi-GPU) verwendet werden, löst einfache Datenparallelität (Data Parallelism) das VRAM-Problem nicht. Da jede GPU eine Kopie des gesamten Modells hält, kann das VRAM-Kapazitätslimit der einzelnen GPU nicht überschritten werden.&lt;/p>
&lt;p>&lt;strong>ZeRO (Zero Redundancy Optimizer)&lt;/strong> der von Microsoft entwickelten &lt;strong>DeepSpeed&lt;/strong>-Bibliothek ist eine Technologie, die die Parameter, Gradienten und Optimiererzustände des Modells gründlich über mehrere GPUs verteilt (Shard). Dadurch kann der &amp;ldquo;Gesamtwert&amp;rdquo; des VRAMs mehrerer GPUs wie ein riesiger Speicherpool behandelt werden.&lt;/p>
&lt;pre class="mermaid">
graph TD
subgraph &amp;#34;ZeRO Stage 3 (Parameter Partitioning)&amp;#34;
A[&amp;#34;GPU 0&amp;#34;] --&amp;gt; D[&amp;#34;Partition 0 (Speichert 1/3 der Gewichte/Grads/Opts)&amp;#34;]
B[&amp;#34;GPU 1&amp;#34;] --&amp;gt; E[&amp;#34;Partition 1 (Speichert 1/3 der Gewichte/Grads/Opts)&amp;#34;]
C[&amp;#34;GPU 2&amp;#34;] --&amp;gt; F[&amp;#34;Partition 2 (Speichert 1/3 der Gewichte/Grads/Opts)&amp;#34;]
end
D[&amp;#34;Partition 0 (Speichert 1/3 der Gewichte/Grads/Opts)&amp;#34;] &amp;lt;--&amp;gt; E[&amp;#34;Partition 1 (Speichert 1/3 der Gewichte/Grads/Opts)&amp;#34;]
E[&amp;#34;Partition 1 (Speichert 1/3 der Gewichte/Grads/Opts)&amp;#34;] &amp;lt;--&amp;gt; F[&amp;#34;Partition 2 (Speichert 1/3 der Gewichte/Grads/Opts)&amp;#34;]
&lt;/pre>
&lt;ul>
&lt;li>&lt;strong>ZeRO Stage 1:&lt;/strong> Aufteilen der Optimiererzustände auf jede GPU&lt;/li>
&lt;li>&lt;strong>ZeRO Stage 2:&lt;/strong> Aufteilen der Gradienten auf jede GPU&lt;/li>
&lt;li>&lt;strong>ZeRO Stage 3:&lt;/strong> Aufteilen der Modellparameter (Gewichte) selbst auf jede GPU&lt;/li>
&lt;/ul>
&lt;p>Wenn zudem die Funktion &lt;strong>ZeRO-Offload&lt;/strong> genutzt wird, können die mit ZeRO verteilten Aktualisierungsberechnungen für Optimiererzustände und Gradienten in den &lt;strong>CPU-Speicher ausgelagert (Offload)&lt;/strong> werden, anstatt sie auf der GPU auszuführen. Dies reduziert die Belastung des GPU-VRAMs drastisch und ermöglicht das Training großer Modelle selbst auf begrenzter GPU-Hardware. Die Berechnung erfolgt auf der CPU und die Ergebnisse werden über PCIe an die GPU zurückgegeben, sodass die Trainingsgeschwindigkeit sinkt, aber das Worst-Case-Szenario, dass das &amp;ldquo;Training aufgrund von Speichermangel abstürzt&amp;rdquo;, wird vermieden.&lt;/p>
&lt;hr>
&lt;h1 id="4-implementierungsbeispiel-hugging-face-accelerate-und-deepspeed">4. Implementierungsbeispiel: Hugging Face Accelerate und DeepSpeed
&lt;/h1>&lt;p>Abschließend zeigen wir anhand einfacher Beispiele, wie CPU-Offloading und VRAM-Optimierung tatsächlich im Python-Code implementiert werden.&lt;/p>
&lt;h2 id="41-automatisches-offloading-mit-hugging-face-device_mapauto">4.1 Automatisches Offloading mit Hugging Face &lt;code>device_map=&amp;quot;auto&amp;quot;&lt;/code>
&lt;/h2>&lt;p>Die Bibliotheken &lt;code>transformers&lt;/code> und &lt;code>accelerate&lt;/code> von Hugging Face teilen die Schichten beim Laden des Modells automatisch zwischen GPU und CPU auf.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">transformers&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">AutoModelForCausalLM&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">AutoTokenizer&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">model_id&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;meta-llama/Llama-2-13b-hf&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Mit device_map=&amp;#34;auto&amp;#34; wird alles, was nicht in den VRAM passt, in den CPU-RAM offloadet&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Mit load_in_8bit=True werden die Gewichte auf 8-Bit quantisiert, um noch mehr Speicher zu sparen&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">model&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">AutoModelForCausalLM&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">from_pretrained&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model_id&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">device_map&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;auto&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">load_in_8bit&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">offload_folder&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;offload_dir&amp;#34;&lt;/span> &lt;span class="c1"># Falls der Platz nicht ausreicht, kann sogar auf Festplatte (SSD) ausgelagert werden&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Wenn Sie diesen Code ausführen, analysiert die zugrunde liegende Bibliothek &lt;code>accelerate&lt;/code> den freien Speicherplatz im VRAM des Systems und im CPU-RAM und ordnet die Schichten (Dispatch) auf die optimale Weise an.&lt;/p>
&lt;h2 id="42-deepspeed-cpu-offloading-einstellungen-zero-2">4.2 DeepSpeed CPU-Offloading Einstellungen (ZeRO-2)
&lt;/h2>&lt;p>Hier ist ein Beispiel für eine Konfigurationsdatei (JSON), um CPU-Offloading mit DeepSpeed während des Trainings zu aktivieren.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;fp16&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;enabled&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;zero_optimization&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;stage&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;offload_optimizer&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;device&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;cpu&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;pin_memory&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;allgather_partitions&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;allgather_bucket_size&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mf">2e8&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;overlap_comm&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;reduce_scatter&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;reduce_bucket_size&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mf">2e8&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;contiguous_gradients&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;train_batch_size&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">16&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;gradient_accumulation_steps&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">4&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Indem &lt;code>offload_optimizer&lt;/code> auf &lt;code>&amp;quot;cpu&amp;quot;&lt;/code> in dieser Konfiguration gesetzt wird, wird die CPU des Host-Systems angewiesen, die Zustandsspeicherung und die Aktualisierungsberechnungen des Optimierers (wie Adam), die viel VRAM verbrauchen, durchzuführen. So kann der VRAM der GPU exklusiv der wichtigsten Aufgabe gewidmet werden: der Forward/Backward-Berechnung des Modells. Indem &lt;code>pin_memory: true&lt;/code> eingestellt wird, werden Page-Faults verhindert und der PCIe-Transfer zwischen CPU und GPU so weit wie möglich beschleunigt.&lt;/p>
&lt;hr>
&lt;h1 id="zusammenfassung">Zusammenfassung
&lt;/h1>&lt;p>GPU-Speichermangel (Out of Memory) in der KI-Entwicklung ist ein ewiges Problem, das Entwickler mit der Zunahme der Modellgröße auch in Zukunft verfolgen wird. Doch durch ein tiefgreifendes Verständnis der in diesem Artikel erläuterten Hardware (Architektur) und einer angemessenen Kombination von Optimierungstechniken auf Software- und Algorithmus-Ebene wird Inferenz und Training riesiger Modelle in lokalen Umgebungen, was zunächst unmöglich erscheint, möglich.&lt;/p>
&lt;p>&lt;strong>Zusammenfassung der Maßnahmen für die Inferenz:&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Quantisierung (INT4 / INT8 / FP8):&lt;/strong> Komprimiert die Größe des Modells selbst drastisch und reduziert den VRAM-Verbrauch.&lt;/li>
&lt;li>&lt;strong>CPU-Offloading:&lt;/strong> Verschiebt Schichten, die nicht in den VRAM passen, in den Systemspeicher (Kompromiss mit der Geschwindigkeitsreduzierung aufgrund der PCIe-Bandbreite).&lt;/li>
&lt;li>&lt;strong>KV-Cache-Optimierung:&lt;/strong> Sichert die Kontextlänge (Context Length) durch Paging (PagedAttention), Cache-Quantisierung und FlashAttention.&lt;/li>
&lt;li>&lt;strong>Unified-Memory-Nutzung:&lt;/strong> Nutzt UMA wie bei Apple Silicon, um großen Speicher direkt für die Inferenz zu verwenden.&lt;/li>
&lt;/ol>
&lt;p>&lt;strong>Zusammenfassung der Maßnahmen für das Training:&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>&lt;strong>PEFT (LoRA / QLoRA):&lt;/strong> Beschränkt die Parameter für das Training und quantisiert das Basismodell extrem.&lt;/li>
&lt;li>&lt;strong>Gradient Checkpointing (Gradient Checkpointing):&lt;/strong> Verwirft Zwischenausgaben im Forward-Pass und berechnet sie im Backward-Pass neu, wodurch der VRAM-Verbrauch im Austausch für Rechenzeit begrenzt wird.&lt;/li>
&lt;li>&lt;strong>ZeRO &amp;amp; CPU-Offloading (DeepSpeed):&lt;/strong> Verteilt Optimiererzustände und Gradienten auf mehrere GPUs oder lagert sie in den CPU-Speicher aus, um die VRAM-Grenze zu durchbrechen.&lt;/li>
&lt;/ol>
&lt;p>Lassen Sie uns durch den geschickten Einsatz dieser fortgeschrittenen Technologien die Leistung der KI-Entwicklung innerhalb begrenzter Hardwareressourcen maximieren. Es wird erwartet, dass in diesem sich schnell entwickelnden Bereich in Zukunft weitere speichersparende Algorithmen auftauchen werden. Der Schlüssel wird darin liegen, die Trends bei den neuesten Bibliotheken regelmäßig zu überprüfen und sie in die Implementierung aufzunehmen.&lt;/p></description></item></channel></rss>