Моделі та платформи ШІ
Оптимізація пам’яті для великомасштабних мовних моделей та їх налаштування
Великомасштабні мовні моделі (LLM) типу GPT-4, Bloom і LLaMA досягли вражаючих можливостей завдяки масштабуванню до мільярдів параметрів. Однак розгортання цих величезних моделей для висновку або налаштування є складним завданням через їх величезні вимоги до пам’яті. У цьому технічному блозі ми розглянемо техніки оцінки та оптимізації споживання пам’яті під час висновку та налаштування LLM на різних апаратних конфігураціях.
Поняття про вимоги до пам’яті
Пам’ять, необхідна для завантаження LLM, визначається головним чином кількістю параметрів і числовою точністю, використовуваною для зберігання параметрів. Просте правило:
- Завантаження моделі з X мільярдами параметрів вимагає приблизно 4X ГБ відеопам’яті у форматі 32-бітного浮點ного числа
- Завантаження моделі з X мільярдами параметрів вимагає приблизно 2X ГБ відеопам’яті у форматі 16-бітного bfloat16/float16
Наприклад, завантаження моделі GPT-3 з 175 мільярдами параметрів вимагало б приблизно 350 ГБ відеопам’яті у форматі bfloat16. На сьогодні найбільші комерційні GPU, такі як NVIDIA A100 і H100, пропонують лише 80 ГБ відеопам’яті, що вимагає використання технік паралелізму тензорів і паралелізму моделей.
Під час висновку пам’ять використовується в основному для моделі параметрів і тимчасових тензорів активації. Вищий оцінковий показник пікової пам’яті під час висновку становить суму пам’яті, необхідної для завантаження параметрів моделі, і пам’яті для активації.
Кількісна оцінка пам’яті під час висновку
Давайте розглянемо вимоги до пам’яті під час висновку за допомогою моделі OctoCode, яка має близько 15 мільярдів параметрів у форматі bfloat16 (~ 31 ГБ). Ми використаємо бібліотеку Transformers для завантаження моделі та генерації тексту:
from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline import torch <p>model = AutoModelForCausalLM.from_pretrained("bigcode/octocoder", torch_dtype=torch.bfloat16, device_map="auto", pad_token_id=0) tokenizer = AutoTokenizer.from_pretrained("bigcode/octocoder") pipe = pipeline("text-generation", model=model, tokenizer=tokenizer)</p> <p>prompt = "Питання: Будь ласка, напишіть функцію Python для перетворення байтів у гігабайти.\n\nВідповідь:" result = pipe(prompt, max_new_tokens=60)[0]["generated_text"][len(prompt):]</p> <p>def bytes_to_gigabytes(bytes): return bytes / 1024 / 1024 / 1024</p> <p>bytes_to_gigabytes(torch.cuda.max_memory_allocated())
Вихід:
29.0260648727417Піковий використання пам’яті GPU становить близько 29 ГБ, що відповідає нашій оцінці 31 ГБ для завантаження параметрів моделі у форматі bfloat16.
Оптимізація пам’яті під час висновку за допомогою кванталізації
Хоча bfloat16 є звичайною точністю, використовуваною для навчання LLM, дослідники виявили, що кванталізація ваг моделі до нижчих типів даних, таких як 8-бітові цілі числа (int8) або 4-бітові цілі числа, може суттєво зменшити використання пам’яті з мінімальною втратою точності для завдань висновку, таких як генерація тексту.
Давайте розглянемо економію пам’яті за допомогою кванталізації 8-біт і 4-біт:
&lt;/div&gt; # Кванталізація 8-біт model = AutoModelForCausalLM.from_pretrained("bigcode/octocoder", load_in_8bit=True, pad_token_id=0) pipe = pipeline("text-generation", model=model, tokenizer=tokenizer) result = pipe(prompt, max_new_tokens=60)[0]["generated_text"][len(prompt):] bytes_to_gigabytes(torch.cuda.max_memory_allocated())</pre>
Вихід:
15.219234466552734# Кванталізація 4-біт model = AutoModelForCausalLM.from_pretrained("bigcode/octocoder", load_in_4bit=True, low_cpu_mem_usage=True, pad_token_id=0) pipe = pipeline("text-generation", model=model, tokenizer=tokenizer) result = pipe(prompt, max_new_tokens=60)[0]["generated_text"][len(prompt):] bytes_to_gigabytes(torch.cuda.max_memory_allocated())
Вихід:
9.543574333190918З кванталізацією 8-біт вимоги до пам’яті зменшуються з 31 ГБ до 15 ГБ, а кванталізація 4-біт зменшує їх ще більше до 9,5 ГБ! Це дозволяє запускати модель OctoCode з 15 мільярдами параметрів на споживчому GPU типу RTX 3090 (24 ГБ відеопам’яті).
Однак, зверніть увагу, що більш агресивна кванталізація, наприклад 4-біт, іноді може привести до погіршення точності порівняно з 8-бітною або bfloat16 точністю. Існує компроміс між економією пам’яті та точністю, який користувачі повинні оцінити для свого випадку використання.
Кванталізація є потужною технікою, яка може дозволити розгортання LLM у середовищах з обмеженими ресурсами, такими як хмарні інстанси, пристрої=edge або навіть мобільні телефони, суттєво зменшуючи використання пам’яті.
Оцінка пам’яті для налаштування
Хоча кванталізація в основному використовується для ефективного висновку, техніки, такі як паралелізм тензорів і паралелізм моделей, є важливими для управління вимогами до пам’яті під час навчання або налаштування великомасштабних мовних моделей.
Пікове споживання пам’яті під час налаштування зазвичай у 3-4 рази вище, ніж під час висновку, через додаткові вимоги до пам’яті для:
- Градієнтів
- Станів оптимізатора
- Активацій з прямого проходу, збережених для зворотного проходу
Консервативна оцінка полягає в тому, що налаштування LLM з X мільярдами параметрів вимагає близько 4 * (2X) = 8X ГБ відеопам’яті у форматі bfloat16.
Наприклад, налаштування моделі LLaMA з 7 мільярдами параметрів вимагало б приблизно 7 * 8 = 56 ГБ відеопам’яті на GPU у форматі bfloat16. Це перевищує пам’ять сучасних GPU, що вимагає розподілених технік налаштування.
Розподільні техніки налаштування
Були запропоновані кілька розподільних методів налаштування для подолання обмежень пам’яті GPU для великих моделей:
- Паралелізм даних: Класичний підхід паралелізму даних дублює всю модель на декілька GPU, а потім розбиває і розподіляє пакети навчальних даних. Це зменшує час навчання лінійно з кількістю GPU, але не зменшує пікове використання пам’яті на кожному GPU.
- ZeRO Stage 3: Розширений підхід паралелізму даних, який розбиває параметри моделі, градієнти та стани оптимізатора на GPU. Це зменшує використання пам’яті порівняно з класичним паралелізмом даних, зберігаючи лише необхідні дані на кожному GPU під час різних фаз навчання.
- Паралелізм тензорів: Замість дублювання моделі паралелізм тензорів розбиває параметри моделі на ряди або стовпці та розподіляє їх на GPU. Кожен GPU працює з частиною параметрів, градієнтів та станів оптимізатора, що призводить до суттєвої економії пам’яті.
- Паралелізм конвеєра: Ця техніка розбиває шари моделі на різні GPU/робітники, причому кожен пристрій виконує підмножину шарів. Активації передаються між робітниками, зменшуючи пікове використання пам’яті, але збільшуючи накладні витрати на комунікацію.
Оцінка використання пам’яті для цих розподільних методів є не тривіальною, оскільки розподіл параметрів, градієнтів, активацій та станів оптимізатора варіюється залежно від техніки. Крім того, різні компоненти, такі як тіло трансформера та голова мовної моделі, можуть демонструвати різні поведінки розподілу пам’яті.
Рішення LLMem
Дослідники недавно запропонували LLMem, рішення, яке точно оцінює споживання пам’яті GPU при застосуванні розподільних методів налаштування до LLM на декілька GPU.
LLMem враховує фактори, такі як рекомбінація параметрів перед обчисленням (ZeRO Stage 3), збирання виводів у зворотному проході (паралелізм тензорів) та різні стратегії розподілу пам’яті для тіла трансформера та голови мовної моделі.
Експериментальні результати показують, що LLMem може оцінювати пікове використання пам’яті GPU для налаштування LLM на одному GPU з похибкою до 1,6%, перевершуючи середню похибку DNNMem на рівні 42,6%. При застосуванні розподільних методів налаштування до LLM з понад мільярдом параметрів на декілька GPU LLMem досягає вражаючої середньої похибки 3,0%.













