<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>GPU on kenji.blog</title><link>http://kenji.blog/ru/tags/gpu/</link><description>Recent content in GPU on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>ru</language><copyright>kenjinote</copyright><lastBuildDate>Fri, 11 Sep 2026 01:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/ru/tags/gpu/index.xml" rel="self" type="application/rss+xml"/><item><title>Методы решения проблемы нехватки памяти GPU при разработке ИИ (разгрузка CPU и т.д.)</title><link>http://kenji.blog/ru/p/ai-gpu-vram-optimization-cpu-offloading/</link><pubDate>Fri, 11 Sep 2026 01:00:00 +0900</pubDate><guid>http://kenji.blog/ru/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 Методы решения проблемы нехватки памяти GPU при разработке ИИ (разгрузка CPU и т.д.)" />&lt;h1 id="введение-разработка-ии-и-стена-vram">Введение: Разработка ИИ и «Стена VRAM»
&lt;/h1>&lt;p>В последние годы технологии генеративного ИИ, такие как большие языковые модели (LLM) и диффузионные модели (Diffusion Models), стремительно развиваются. Однако, когда разработчики и исследователи пытаются обучать (дообучать) или запускать инференс (Inference) этих передовых ИИ-моделей в локальной среде, они часто сталкиваются с &lt;strong>«нехваткой памяти GPU (VRAM)»&lt;/strong> — крайне физическим препятствием.&lt;/p>
&lt;p>Даже высокопроизводительные потребительские GPU, такие как NVIDIA GeForce RTX 4090, имеют максимум 24 ГБ VRAM, и загрузить такую огромную модель, как Llama 3 70B, целиком в нее совершенно невозможно. GPU для дата-центров, такие как H100 (80 ГБ) или B200 (192 ГБ), очень дороги и недоступны для частных лиц или небольших команд. Если не преодолеть эту «стену VRAM» (The Wall of VRAM), невозможно даже прикоснуться к самым передовым моделям.&lt;/p>
&lt;p>В этой статье мы подробно, как с точки зрения инференса, так и обучения, рассмотрим продвинутые технические приемы, позволяющие преодолеть это физическое ограничение VRAM с помощью архитектурных решений в программном и аппаратном обеспечении. Мы углубимся в такие темы, как разгрузка CPU, оптимизация KV-кэша, чекпоинтинг градиентов (Gradient Checkpointing) и новейшая архитектура объединенной памяти (Unified Memory), сопровождая это математическими формулами и схемами. Прочитав эту статью, вы глубоко поймете поведение VRAM и приобретете практические знания для работы с огромными моделями на ограниченных ресурсах.&lt;/p>
&lt;hr>
&lt;h1 id="1-анатомия-потребления-vram-моделями-ии-инференс-и-обучение">1. Анатомия потребления VRAM моделями ИИ (Инференс и Обучение)
&lt;/h1>&lt;p>Первым шагом к решению проблемы нехватки VRAM является точное понимание с микроскопической точки зрения того, «что» и «сколько» памяти потребляется. Относясь к этому не как к черному ящику, а точно оценивая с помощью математических формул, вы сможете выбрать правильные методы оптимизации.&lt;/p>
&lt;h2 id="11-расчет-памяти-для-параметров-модели-весов">1.1 Расчет памяти для параметров модели (весов)
&lt;/h2>&lt;p>Базовый объем памяти, потребляемый параметрами (Weights), составляющими ИИ-модель, определяется общим количеством параметров модели и типом данных (Precision: точность), используемым для их представления.&lt;/p>
&lt;p>Ниже приведены типы данных, обычно используемые в глубоком обучении, и количество байт на один параметр ($B$):&lt;/p>
&lt;ul>
&lt;li>&lt;strong>FP32 (Число с плавающей запятой одинарной точности):&lt;/strong> 4 байта (стандартная точность при обучении)&lt;/li>
&lt;li>&lt;strong>FP16 / BF16 (Число с плавающей запятой половинной точности):&lt;/strong> 2 байта (стандартно для инференса и обучения со смешанной точностью)&lt;/li>
&lt;li>&lt;strong>INT8 (8-битное целое число):&lt;/strong> 1 байт (квантованные модели)&lt;/li>
&lt;li>&lt;strong>INT4 (4-битное целочисленное квантование):&lt;/strong> 0.5 байта (экстремальное квантование, такое как GPTQ, AWQ, GGUF)&lt;/li>
&lt;/ul>
&lt;p>Если общее количество параметров модели равно $P$, то базовый объем памяти $M_{weights}$, занимаемый самими весами, выражается следующей формулой:&lt;/p>
$$ M_{weights} = P \times B $$&lt;p>Например, если мы хотим загрузить модель «Llama 3 8B» (около 8 миллиардов параметров), опубликованную Meta, в формате FP16 (половинная точность), расчет будет следующим:&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>Иными словами, простая загрузка весов модели в GPU потребляет 16 ГБ VRAM. На RTX 3060 (12 ГБ) в этот момент возникнет ошибка Out of Memory (OOM). Однако, если квантовать модель до INT4, получится $8 \times 0.5 = 4 \text{ ГБ}$, что позволит без проблем загрузить её.&lt;/p>
&lt;h2 id="12-потребление-памяти-при-инференсе-увеличение-kv-кэша">1.2 Потребление памяти при инференсе: Увеличение KV-кэша
&lt;/h2>&lt;p>При инференсе LLM (особенно при авторегрессионной генерации текста) &lt;strong>KV-кэш (Key-Value Cache)&lt;/strong> оказывает на VRAM такое же или даже большее давление, чем веса.
В архитектуре Transformer, чтобы избежать повторного вычисления информации о ранее сгенерированных и обработанных токенах, тензоры Key и Value на каждом слое внимания (attention) кэшируются в VRAM. Это увеличивает скорость вычислений (Compute), но по мере увеличения длины контекста (длина входного промпта + длина генерации) потребление памяти взрывообразно растет линейным образом.&lt;/p>
&lt;p>Объем памяти KV-кэша $M_{kv\_token}$, потребляемый при обработке одного токена, строго рассчитывается на основе архитектуры модели по следующей формуле:&lt;/p>
$$ M_{kv\_token} = 2 \times N_{layers} \times N_{heads\_kv} \times D_{head} \times B $$&lt;p>Где каждая переменная имеет следующее значение:&lt;/p>
&lt;ul>
&lt;li>$2$ : Из-за наличия двух тензоров: Key и Value&lt;/li>
&lt;li>$N_{layers}$ : Количество слоев (layers) Transformer&lt;/li>
&lt;li>$N_{heads\_kv}$ : Количество голов внимания KV (в случае GQA: Grouped Query Attention это число меньше обычного количества голов)&lt;/li>
&lt;li>$D_{head}$ : Размерность каждой головы (обычно размерность скрытого слоя $D_{model} / N_{heads}$)&lt;/li>
&lt;li>$B$ : Количество байт для типа данных (2 для FP16)&lt;/li>
&lt;/ul>
&lt;p>Общий объем KV-кэша $M_{kv\_total}$ получается умножением этого значения на длину последовательности ($L_{seq}$) и размер батча ($BatchSize$).&lt;/p>
$$ M_{kv\_total} = M_{kv\_token} \times L_{seq} \times BatchSize $$&lt;p>&lt;strong>Конкретный пример: для Llama 2 7B&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>$N_{layers} = 32$&lt;/li>
&lt;li>$N_{heads\_kv} = 32$ (в случае MHA)&lt;/li>
&lt;li>$D_{head} = 128$&lt;/li>
&lt;li>FP16 ($B=2$)&lt;/li>
&lt;li>Размер батча 1, длина последовательности 8192 (контекст 8K)&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>Если увеличить контекст до 32K (32768 токенов), только KV-кэш потребует около 16 ГБ. Если увеличить размер батча до 4, это будет уже 64 ГБ. Требование VRAM, намного превышающее размер самой модели, является серьезной проблемой при инференсе.&lt;/p>
&lt;h2 id="13-потребление-памяти-при-обучении-оптимизатор-градиенты-и-активации">1.3 Потребление памяти при обучении: Оптимизатор, Градиенты и Активации
&lt;/h2>&lt;p>По сравнению с инференсом, обучение модели (pre-training или fine-tuning) потребляет гораздо больше VRAM. Это связано с тем, что необходимо сохранять информацию для обратного распространения ошибки (backpropagation), а не только для прямого прохода (forward pass). Память во время обучения в основном состоит из следующих 4 элементов:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Веса модели (Model Weights):&lt;/strong> Так же, как и при инференсе, но при обучении со смешанной точностью часто сохраняются как FP16, так и FP32 (master weights).&lt;/li>
&lt;li>&lt;strong>Градиенты (Gradients):&lt;/strong> Градиенты для каждого параметра, вычисляемые при обратном распространении. В случае FP16 это 2 байта на параметр.&lt;/li>
&lt;li>&lt;strong>Состояния оптимизатора (Optimizer States):&lt;/strong> Продвинутые оптимизаторы, такие как AdamW, сохраняют первый момент (Momentum) и второй момент (Variance) для каждого параметра. Для поддержания стабильности обучения они обычно хранятся в FP32 (4 байта). То есть на 2 момента расходуется $4 + 4 = 8$ байт/параметр.&lt;/li>
&lt;li>&lt;strong>Активации (Activations):&lt;/strong> Для вычисления градиентов при обратном распространении необходимо хранить в памяти выходные данные (промежуточные состояния) каждого слоя при прямом проходе. Это сильно зависит от размера батча и длины последовательности, и может стать очень большим.&lt;/li>
&lt;/ol>
&lt;p>В итоге, при обучении со смешанной точностью (Mixed Precision Training) с использованием стандартного оптимизатора Adam, потребуется &lt;strong>около 16–20 байт&lt;/strong> (master-веса 4 + веса FP16 2 + градиенты 2 + оптимизатор 8 + α) памяти на один параметр.&lt;/p>
$$ M_{train\_param} \approx P \times 16 \text{ bytes} $$&lt;p>Для обучения модели 7B (7 миллиардов параметров) только для параметров потребуется $7B \times 16 = 112 \text{ ГБ}$, к которым добавляются активации, и в итоге потребуется более 140 ГБ VRAM. Чтобы выполнить это на 24 ГБ VRAM, абсолютно необходимы мощные методы оптимизации, которые будут описаны в следующих главах.&lt;/p>
&lt;hr>
&lt;h1 id="2-техники-экономии-vram-при-инференсе">2. Техники экономии VRAM при инференсе
&lt;/h1>&lt;p>В качестве подходов к запуску огромных моделей при инференсе было разработано множество программных технологий, преодолевающих аппаратные ограничения.&lt;/p>
&lt;h2 id="21-разгрузка-cpu-cpu-offloading-и-разделение-слоев">2.1 Разгрузка CPU (CPU Offloading) и разделение слоев
&lt;/h2>&lt;p>Когда огромная модель не помещается на одном или нескольких GPU, метод размещения части модели в системной памяти (CPU RAM) и вычислений с переносом данных в GPU только тогда, когда это необходимо, называется &lt;strong>разгрузкой CPU (CPU Offloading)&lt;/strong>. &lt;code>llama.cpp&lt;/code> и &lt;code>Accelerate&lt;/code> от Hugging Face поддерживают эту функцию.&lt;/p>
&lt;pre class="mermaid">
graph TD
A[&amp;#34;Системная 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;Тензорные ядра (Вычисления)&amp;#34;]
subgraph &amp;#34;Layer Splitting and Offloading&amp;#34;
D[&amp;#34;Нижние слои 1-15 (Закреплены в GPU)&amp;#34;]
E[&amp;#34;Верхние слои 16-32 (Разгружены на CPU)&amp;#34;]
end
E[&amp;#34;Верхние слои 16-32 (Разгружены на CPU)&amp;#34;] -.-&amp;gt; B[&amp;#34;GPU VRAM (GDDR6X)&amp;#34;]
&lt;/pre>
&lt;p>&lt;strong>Механизм и проблемы:&lt;/strong>
Поскольку архитектура Transformer состоит из слоев, уложенных последовательно, вычисление следующего слоя не начнется до тех пор, пока не закончится вычисление текущего. Используя это, только слои, которые помещаются в GPU (например, слои с 1-го по 15-й), постоянно находятся (закреплены) в VRAM, а остальные слои (с 16-го по 32-й) размещаются в большой, но медленной RAM CPU. Во время инференса, как только вычисления для первых 15 слоев завершаются, веса 16-го слоя переносятся (копируются) из CPU в GPU через шину PCIe, и вычисления выполняются на GPU.&lt;/p>
&lt;p>Однако &lt;strong>пропускная способность (Bandwidth) PCIe становится сильным узким местом&lt;/strong>. Теоретическая максимальная пропускная способность PCIe 4.0 x16 составляет 32 ГБ/с (в одном направлении), что на два порядка медленнее по сравнению с внутренней пропускной способностью VRAM современных GPU (например, GDDR6X на RTX 4090 составляет 1008 ГБ/с, HBM3 на H100 — более 3 ТБ/с), поэтому интенсивное использование разгрузки CPU резко снижает скорость инференса (Tokens per Second).
Чтобы минимизировать падение скорости, практический подход заключается в том, чтобы поместить как можно больше слоев в GPU (максимизация GPU Layers) и минимизировать количество слоев, разгружаемых на CPU.&lt;/p>
&lt;h2 id="22-квантование-kv-кэша-и-pagedattention">2.2 Квантование KV-кэша и PagedAttention
&lt;/h2>&lt;p>Для KV-кэша, который является главным виновником потребления VRAM при инференсе, применяются две мощные оптимизации.&lt;/p>
&lt;p>&lt;strong>1. Квантование KV-кэша (KV Cache Quantization):&lt;/strong>
Это метод квантования не только весов модели, но и динамически генерируемого во время работы KV-кэша до INT8, INT4 или даже FP8 и его сохранения в VRAM. Это позволяет сократить размер KV-кэша в два-четыре раза. В современных движках инференса (vLLM или llama.cpp) эта функция встроена, что позволяет значительно экономить VRAM при минимальном снижении точности.&lt;/p>
&lt;p>&lt;strong>2. PagedAttention:&lt;/strong>
&lt;strong>PagedAttention&lt;/strong> — это применение концепции «пейджинга» виртуальной памяти ОС к KV-кэшу, впервые внедренная в движке инференса vLLM. В традиционных движках инференса непрерывная область VRAM предварительно выделялась (Pre-allocation) в соответствии с заданным максимальным размером последовательности. В результате, если фактический вход был коротким, возникала фрагментация и пустая трата неиспользуемой памяти, и до 60% VRAM могло тратиться впустую.&lt;/p>
&lt;p>PagedAttention разделяет KV-кэш на блоки (страницы) фиксированного размера и позволяет хранить их децентрализованно в несмежном пространстве физической памяти. Это сводит потери памяти практически к нулю (ограничиваясь только внутренней фрагментацией) и позволяет значительно увеличить размер батча при том же объеме VRAM.&lt;/p>
&lt;pre class="mermaid">
graph LR
A[&amp;#34;Логический KV-кэш&amp;#34;] --&amp;gt; B[&amp;#34;Физические блоки VRAM&amp;#34;]
A1[&amp;#34;Токены 1, 2, 3, 4&amp;#34;] --&amp;gt; B3[&amp;#34;Блок 3 (Выделен)&amp;#34;]
A2[&amp;#34;Токены 5, 6, 7, 8&amp;#34;] --&amp;gt; B1[&amp;#34;Блок 1 (Выделен)&amp;#34;]
A3[&amp;#34;Будущие токены...&amp;#34;] -.-&amp;gt; B2[&amp;#34;Блок 2 (Свободен)&amp;#34;]
&lt;/pre>
&lt;h2 id="23-flashattention-преодоление-сложности-памяти-при-вычислении-внимания">2.3 FlashAttention: Преодоление сложности памяти при вычислении внимания
&lt;/h2>&lt;p>Нехватка VRAM вызывается не только объемом памяти для хранения данных, но и нехваткой «временного рабочего пространства» во время вычислений. Стандартный механизм Self-Attention в Transformer требует материализации огромной матрицы внимания $N \times N$ в VRAM для длины последовательности $N$. Это означает сложность памяти $O(N^2)$, что является основной причиной OOM для длинных контекстов.&lt;/p>
&lt;p>Решением этого стала технология &lt;strong>FlashAttention&lt;/strong> (и FlashAttention-2, 3).
FlashAttention — это алгоритм, учитывающий аппаратную архитектуру GPU (иерархическую структуру огромной, но медленной HBM и крошечной, но сверхбыстрой SRAM). Используя метод, называемый тайлингом (Tiling), данные загружаются в SRAM поблочно, и вычисления внимания завершаются там, что полностью позволяет избежать процесса записи матрицы $N \times N$ в HBM (VRAM).&lt;/p>
&lt;p>В результате сложность памяти слоя внимания резко снизилась с $O(N^2)$ до $O(N)$ (пропорционально длине последовательности), и ограничение на длину контекста было значительно ослаблено.&lt;/p>
&lt;h2 id="24-расцвет-объединенной-памяти-unified-memory-и-apple-silicon">2.4 Расцвет объединенной памяти (Unified Memory) и Apple Silicon
&lt;/h2>&lt;p>Подход к этой проблеме с самых основ архитектуры ПК — это &lt;strong>архитектура объединенной памяти (Unified Memory Architecture: UMA)&lt;/strong>, которая используется в Apple Silicon (серии Max и Ultra в M1/M2/M3/M4) и некоторых новейших APU (таких как AMD Strix Point).&lt;/p>
&lt;p>В этих архитектурах CPU и GPU на материнской плате разделяют одну и ту же физическую память (например, до 192 ГБ LPDDR5). Поэтому концепции «медленной передачи данных от CPU к GPU через PCIe» физически не существует.&lt;/p>
&lt;pre class="mermaid">
graph TD
subgraph &amp;#34;Unified Memory Architecture (e.g. Apple Silicon)&amp;#34;
A[&amp;#34;Ядра CPU&amp;#34;] &amp;lt;--&amp;gt; C[&amp;#34;Общий контроллер памяти&amp;#34;]
B[&amp;#34;Ядра GPU / Neural Engine&amp;#34;] &amp;lt;--&amp;gt; C[&amp;#34;Общий контроллер памяти&amp;#34;]
C[&amp;#34;Общий контроллер памяти&amp;#34;] &amp;lt;--&amp;gt; D[&amp;#34;Общий пул памяти (напр., 192 ГБ)&amp;#34;]
end
&lt;/pre>
&lt;p>Самое большое преимущество этой архитектуры в том, что нет четкой стены VRAM, и почти всю системную память можно напрямую использовать для загрузки огромных LLM. Mac Studio с 192 ГБ объединенной памяти может загрузить на одно устройство огромные модели класса 70B и выше (например, Grok-1) без квантования и быстро выполнять инференс. Пропускная способность доступа к памяти на M2 Ultra достигает 800 ГБ/с, что сравнимо со скоростью дискретных потребительских GPU. Это очень мощный подход, который на аппаратном уровне решает дилемму «объем памяти» против «пропускной способности».&lt;/p>
&lt;hr>
&lt;h1 id="3-техники-экономии-vram-при-обучении-fine-tuning">3. Техники экономии VRAM при обучении (Fine-Tuning)
&lt;/h1>&lt;p>Много прорывов произошло и во время обучения (Training), которое требует еще больше VRAM, чем инференс. Чтобы проводить файн-тюнинг на ограниченных ресурсах, необходимо комбинировать следующие технологии.&lt;/p>
&lt;h2 id="31-чекпоинтинг-градиентов-gradient-checkpointing">3.1 Чекпоинтинг градиентов (Gradient Checkpointing)
&lt;/h2>&lt;p>В обратном распространении ошибки (backpropagation) в глубоком обучении необходимо хранить промежуточные выходы всех слоев (Activations) во время прямого прохода в памяти, чтобы вычислить градиенты. Когда длина последовательности и размер батча становятся большими, эта память активаций начинает доминировать в VRAM.&lt;/p>
&lt;p>&lt;strong>Чекпоинтинг градиентов (Gradient Checkpointing / Activation Recomputation)&lt;/strong> — это гениальная техника, использующая компромисс между объемом памяти и временем вычислений (Compute).
Вместо того, чтобы сохранять все промежуточные выходы в памяти, сохраняются только выходы определенных слоев (чекпоинтов). Когда во время обратного распространения требуется не сохраненное промежуточное значение, &lt;strong>оно восстанавливается путем повторного вычисления прямого прохода от ближайшего сохраненного чекпоинта (перевычисление)&lt;/strong>.&lt;/p>
&lt;p>Объем вычислений увеличивается примерно на 20-30%, и общее время обучения становится больше, но потребление VRAM активациями можно резко сократить с $O(N)$ (где $N$ — количество слоев) до $O(\sqrt{N})$. В настоящее время при обучении крупномасштабных моделей это настолько важная настройка, что без неё даже нельзя начать работу.&lt;/p>
&lt;h2 id="32-lora-и-qlora-low-rank-adaptation">3.2 LoRA и QLoRA (Low-Rank Adaptation)
&lt;/h2>&lt;p>Главным героем, коренным образом решившим проблему нехватки VRAM, стал &lt;strong>LoRA&lt;/strong> — видный представитель семейства PEFT (Parameter-Efficient Fine-Tuning).&lt;/p>
&lt;p>Оригинальная гигантская матрица весов модели $W_0 \in \mathbb{R}^{d \times k}$ замораживается (Frozen) и не обучается. Вместо этого параллельно вводятся две очень маленькие матрицы низкого ранга $A \in \mathbb{R}^{r \times k}$ и $B \in \mathbb{R}^{d \times r}$, и обучаются только эти $A$ и $B$. (Где ранг $r$ — это очень маленькое значение, $r \ll d, k$).&lt;/p>
$$ W_{adapted} = W_0 + \Delta W = W_0 + B A $$&lt;p>В результате количество обучаемых параметров становится менее 1% (иногда менее 0.1%) от исходного, и, соответственно, объем памяти, потребляемый «градиентами» и «состояниями оптимизатора», резко падает до менее 1%.&lt;/p>
&lt;p>Дальнейшее экстремальное развитие этого подхода — &lt;strong>QLoRA (Quantized LoRA)&lt;/strong>.
В QLoRA веса базовой модели $W_0$ экстремально квантуются до 4 бит (формат NF4: NormalFloat4) и загружаются в VRAM. При этом небольшие матрицы LoRA $A, B$ обучаются в формате BF16 (16 бит) для сохранения точности вычислений.
Благодаря 4-битному квантованию размер базовой модели в VRAM уменьшается в четыре раза от исходного, а с использованием технологии &lt;strong>Paged Optimizers&lt;/strong> (страничные оптимизаторы) состояния оптимизатора автоматически эвакуируются (разгружаются) в CPU RAM, когда VRAM близка к исчерпанию. Это сделало возможным файн-тюнинг сверхбольших моделей, таких как Llama 3 70B, даже на одном GPU с 24 ГБ VRAM (например, RTX 4090).&lt;/p>
&lt;h2 id="33-deepspeed-zero-и-оффлоадинг-offloading">3.3 DeepSpeed ZeRO и Оффлоадинг (Offloading)
&lt;/h2>&lt;p>В среде, использующей несколько GPU (мульти-GPU), простое распараллеливание данных (Data Parallelism) не решает проблему VRAM. Поскольку каждый GPU содержит копию всей модели, предел емкости VRAM отдельного устройства не может быть преодолен.&lt;/p>
&lt;p>&lt;strong>ZeRO (Zero Redundancy Optimizer)&lt;/strong> в библиотеке &lt;strong>DeepSpeed&lt;/strong>, разработанной Microsoft — это технология, которая кардинально разделяет (shards) параметры модели, градиенты и состояния оптимизатора между несколькими GPU. Это позволяет использовать «суммарный» объем VRAM нескольких GPU как один гигантский пул памяти.&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;Секция 0 (Хранит 1/3 Весов/Град./Оптим.)&amp;#34;]
B[&amp;#34;GPU 1&amp;#34;] --&amp;gt; E[&amp;#34;Секция 1 (Хранит 1/3 Весов/Град./Оптим.)&amp;#34;]
C[&amp;#34;GPU 2&amp;#34;] --&amp;gt; F[&amp;#34;Секция 2 (Хранит 1/3 Весов/Град./Оптим.)&amp;#34;]
end
D[&amp;#34;Секция 0 (Хранит 1/3 Весов/Град./Оптим.)&amp;#34;] &amp;lt;--&amp;gt; E[&amp;#34;Секция 1 (Хранит 1/3 Весов/Град./Оптим.)&amp;#34;]
E[&amp;#34;Секция 1 (Хранит 1/3 Весов/Град./Оптим.)&amp;#34;] &amp;lt;--&amp;gt; F[&amp;#34;Секция 2 (Хранит 1/3 Весов/Град./Оптим.)&amp;#34;]
&lt;/pre>
&lt;ul>
&lt;li>&lt;strong>ZeRO Stage 1:&lt;/strong> Состояния оптимизатора разделяются между GPU.&lt;/li>
&lt;li>&lt;strong>ZeRO Stage 2:&lt;/strong> Градиенты также разделяются между GPU.&lt;/li>
&lt;li>&lt;strong>ZeRO Stage 3:&lt;/strong> Сами параметры (веса) модели также разделяются между GPU.&lt;/li>
&lt;/ul>
&lt;p>Кроме того, при использовании функции &lt;strong>ZeRO-Offload&lt;/strong> вычисления для обновления состояний оптимизатора и градиентов, разделенных с помощью ZeRO, могут выполняться не на GPU, а &lt;strong>разгружаться (offload) в память CPU&lt;/strong> и выполняться на хост-CPU. Это максимально снижает нагрузку на VRAM GPU и позволяет обучать огромные модели даже в средах с ограниченными ресурсами GPU. Скорость обучения снижается из-за того, что вычисления выполняются на CPU, а результаты возвращаются на GPU через PCIe, но это позволяет избежать наихудшего сценария: «краха обучения из-за нехватки памяти».&lt;/p>
&lt;hr>
&lt;h1 id="4-пример-реализации-hugging-face-accelerate-и-deepspeed">4. Пример реализации: Hugging Face Accelerate и DeepSpeed
&lt;/h1>&lt;p>Напоследок мы приведем простые примеры того, как на самом деле реализовать разгрузку CPU и оптимизацию VRAM в коде на Python.&lt;/p>
&lt;h2 id="41-автоматическая-разгрузка-с-помощью-hugging-face-device_mapauto">4.1 Автоматическая разгрузка с помощью Hugging Face &lt;code>device_map=&amp;quot;auto&amp;quot;&lt;/code>
&lt;/h2>&lt;p>При использовании библиотек &lt;code>transformers&lt;/code> и &lt;code>accelerate&lt;/code> от Hugging Face, во время загрузки модели слои автоматически распределяются между GPU и CPU.&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"># Благодаря device_map=&amp;#34;auto&amp;#34;, часть, не помещающаяся в VRAM, разгружается в CPU RAM&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># load_in_8bit=True квантует веса до 8 бит для еще большей экономии памяти&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"># При нехватке места возможна разгрузка даже на диск (SSD)&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>При выполнении этого кода стоящая за ним библиотека &lt;code>accelerate&lt;/code> анализирует свободный объем VRAM и CPU RAM в системе и оптимально распределяет (Dispatch) слои.&lt;/p>
&lt;h2 id="42-настройка-разгрузки-cpu-в-deepspeed-zero-2">4.2 Настройка разгрузки CPU в DeepSpeed (ZeRO-2)
&lt;/h2>&lt;p>Пример файла конфигурации (JSON) для включения разгрузки CPU в DeepSpeed во время обучения.&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>В этой конфигурации установка параметра &lt;code>offload_optimizer&lt;/code> в значение &lt;code>&amp;quot;cpu&amp;quot;&lt;/code> заставляет выполнять вычисления для обновления и хранения состояний оптимизатора (такого как Adam), который потребляет много VRAM, на CPU системы. Это позволяет VRAM GPU сосредоточиться на самой важной задаче — прямом/обратном вычислении модели. Настройка &lt;code>pin_memory: true&lt;/code> предотвращает ошибки отсутствия страниц (page faults) и максимально ускоряет передачу данных между CPU и GPU по PCIe.&lt;/p>
&lt;hr>
&lt;h1 id="заключение">Заключение
&lt;/h1>&lt;p>Нехватка памяти GPU (Out of Memory) при разработке ИИ останется вечной проблемой для разработчиков по мере роста масштабов моделей. Однако глубокое понимание аппаратного обеспечения (архитектуры), подобного тому, что было описано в этой статье, в правильном сочетании с методами оптимизации на уровне программного обеспечения и алгоритмов, делает инференс и обучение огромных моделей в локальной среде, что на первый взгляд кажется невозможным, вполне осуществимым.&lt;/p>
&lt;p>&lt;strong>Краткое описание мер при инференсе:&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Квантование (INT4 / INT8 / FP8):&lt;/strong> Радикально сжимает размер самой модели и уменьшает объем занимаемой VRAM.&lt;/li>
&lt;li>&lt;strong>Разгрузка CPU:&lt;/strong> Перенос слоев, не помещающихся в VRAM, в системную память (компромисс со снижением скорости из-за пропускной способности PCIe).&lt;/li>
&lt;li>&lt;strong>Оптимизация KV-кэша:&lt;/strong> Обеспечение длины контекста (Context Length) с использованием пейджинга (PagedAttention), квантования кэша и FlashAttention.&lt;/li>
&lt;li>&lt;strong>Использование объединенной памяти (Unified Memory):&lt;/strong> Использование UMA, такой как Apple Silicon, для прямого использования памяти большого объема для инференса.&lt;/li>
&lt;/ol>
&lt;p>&lt;strong>Краткое описание мер при обучении:&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>&lt;strong>PEFT (LoRA / QLoRA):&lt;/strong> Ограничение параметров для обучения и экстремальное квантование базовой модели.&lt;/li>
&lt;li>&lt;strong>Чекпоинтинг градиентов (Gradient Checkpointing):&lt;/strong> Отбрасывание промежуточных выходов прямого прохода и их перевычисление во время обратного распространения, чтобы снизить потребление VRAM ценой времени вычислений.&lt;/li>
&lt;li>&lt;strong>ZeRO и разгрузка CPU (DeepSpeed):&lt;/strong> Разделение состояний оптимизатора и градиентов между несколькими GPU или их разгрузка в память CPU для преодоления ограничений VRAM.&lt;/li>
&lt;/ol>
&lt;p>Активно используя эти передовые технологии, давайте максимизировать производительность при разработке ИИ в рамках ограниченных аппаратных ресурсов. Ожидается, что в этой быстро развивающейся области в будущем появятся новые алгоритмы для экономии памяти. Ключом к успеху будет регулярное отслеживание тенденций в новейших библиотеках и их внедрение в реализацию.&lt;/p></description></item></channel></rss>