QLoRA Обучение большой языковой модели на GPU емкостью 16 ГБ.
Обучение большой языковой модели QLoRA на GPU с емкостью 16 ГБ.
Давайте изучим, как работает квантование, и представим пример улучшения 7-миллиардного параметрической модели Блума на T4 16GB GPU в Google Colab.
Эта статья является частью бесплатного курса по большим языковым моделям, доступного на GitHub.
Мы собираемся комбинировать технику снижения веса моделей, такую как квантование, с техникой эффективного настройки параметров, например LoRA. Результатом этой комбинации является QLoRA, позволяющий нам эффективно настраивать большие модели с минимальным использованием ресурсов.
В предыдущей статье мы изучили, как настроить большую языковую модель с помощью LoRA.
- Интеграция генеративного искусственного интеллекта и обучения с подкреплением для самосовершенствования
- Управление жизненным циклом ML в масштабе. Часть 1 Фреймворк для архитектуры рабочих нагрузок ML с использованием Amazon SageMaker
- Краткая история нейронных сетей
Эффективная настройка параметров с LoRA. Оптимальная обучающая среда для больших языковых моделей.
LoRA является одной из самых эффективных и эффективных техник настройки параметров, применимых к большим языковым моделям. В посте…
levelup.gitconnected.com
В этом материале мы добавим квантование в нашу модель, что позволит нам настраивать гораздо большие и, следовательно, более мощные модели.
Мы сможем настраивать модели с 7 миллиардами параметров, такие как Lllama-2 7B или Bloom 7B, на видеокарте с всего 16 ГБ памяти. Это делает QLoRA одним из самых эффективных методов настройки моделей, которые могут реально использоваться.
И мы сможем достичь этого с минимальными усилиями, воспользовавшись фантастическими и замечательными ребятами из Hugging Face. Которые предоставляют библиотеку, подобную PEFT, позволяющую нам наслаждаться всеми этими преимуществами всего несколькими строками кода.
Как работает квантование?
Основная идея проста: мы собираемся снизить точность чисел с плавающей запятой, которые обычно занимают 32 бита, до целых чисел с 8 или даже 4 битами.
Это снижение происходит в параметрах модели, весах нейронных слоев, и в значениях активации, которые проходят через слои модели.
Это означает, что мы не только добиваемся улучшения размера хранения модели и потребления памяти, но также получаем большую гибкость в ее вычислениях.
Естественно, есть потеря точности, но особенно в случае 8-битового квантования, эта потеря минимальна.
Давайте взглянем на небольшой пример.
Я создам функцию для квантования и другую для обратного преобразования квантования (или как оно там называется).
На самом деле, я хочу увидеть потерю точности, которая происходит при переходе от числа с плавающей запятой 32 бита к квантованному числу с 8/4 битами и затем возвращению к исходному значению с 32 битами.
#Импорт необходимых библиотек
import numpy as np
import math
import matplotlib.pyplot as plt
#Функции для квантования и обратного преобразования
def quantize(value, bits=4):
quantized_value = np.round(value * (2**(bits - 1) - 1))
return int(quantized_value)
def unquantize(quantized_value, bits=4):
value = quantized_value / (2**(bits - 1) - 1)
return float(value)
quant_4 = quantize(0.622, 4)
print (quant_4)
quant_8 = quantize(0.622, 8)
print(quant_8)
При квантовании значения 0.622 мы получаем следующие результаты:
- 4 бита: 4.
- 8 бит: 79
Восстановим эти значения до их исходной точности и посмотрим, что получится.
unquant_4 = unquantize(quant_4, 4)print(unquant_4)unquant_8 = unquantize(quant_8, 8)print(unquant_8)
- 4 бита без квантования: 0.57142
- 8 бит без квантования: 0.62204
Если мы учтем, что исходное число было 0.622, можно сказать, что квантование в 8 бит едва ли теряет точность, а потери от квантования в 4 бита управляемы.
Всегда важно учитывать предполагаемое использование квантованной модели. Для задач, таких как генерация текста или генерация исходного кода, потеря точности может быть не критически важной. Однако в моделях, используемых для распознавания изображений при диагностике болезней, может быть неприятно, если есть значительная потеря точности.
Давайте построим кривую с неквантованными значениями косинуса.
x = np.linspace(-1, 1, 50)y = [math.cos(val) for val in x]y_quant_8bit = np.array([quantize(val, bits=8) for val in y])y_unquant_8bit = np.array([unquantize(val, bits=8) for val in y_quant_8bit])y_quant_4bit = np.array([quantize(val, bits=4) for val in y])y_unquant_4bit = np.array([unquantize(val, bits=4) for val in y_quant_4bit])
plt.figure(figsize=(10, 12))plt.subplot(4, 1, 1)plt.plot(x, y, label="Оригинал")plt.plot(x, y_unquant_8bit, label="Без квантования 8 бит")plt.plot(x, y_unquant_4bit, label="Без квантования 4 бита")plt.legend()plt.title("Сравнение графика")plt.grid(True)
Как мы видим на графике, неквантованная линия, представляющая 8-битные значения, почти идеально совпадает с линией для исходных значений. В отличие от этого, с линией, представляющей неквантованные значения 4-битного квантования, мы можем наблюдать заметные скачки. Разница в точности между 8-битным и 4-битным квантованием довольно значительна. Это важный фактор, который следует учитывать при принятии решения о квантовании нашей модели.
Сказав это, мы собираемся использовать квантование в 4 бита, потому что, как уже упоминалось, для генерации текста мы не заметим большой разницы, и нам необходимо загрузить модель на одну 16-гигабайтную видеокарту.
QLoRA: Настройка модели с квантованием в 4 бита с использованием LoRA.
Чтобы следовать за записной книжкой вместе с статьей, она доступна на Github:
Large-Language-Model-Notebooks-Course/5-Fine Tuning/QLoRA_Tuning_PEFT.ipynb at main ·…
Практический курс по большим языковым моделям. . Сотрудничество с peremartra/Large-Language-Model-Notebooks-Course…
github.com
Модель, которую я собираюсь использовать, это Bloom 7B. Это одна из хорошо установленных моделей в Hugging Face, очень мощная и работает на уровне LLAMA. Это модель, которую мы не могли загрузить на 16-гигабайтную видеокарту без квантования.
В предыдущей статье, я тренировал модель из этой же семьи, но гораздо меньшую, поэтому вы можете изучить различия между двумя записными книжками.
Мы можем начать загрузку необходимых библиотек.
!pip -q install accelerate!pip -q install datasets!pip -q install trl
Библиотеки trl и accelerate являются частью экосистемы HuggingFace и позволяют нам выполнить настройку модели.
Библиотека datasets содержит множество предобработанных наборов данных, включая тот, который мы собираемся использовать в нашем примере.
Вы могли заметить, что отсутствуют две основные библиотеки: transformers и peft. Первая служит основным интерфейсом для моделей Hugging Face, а вторая содержит реализацию различных техник настройки модели. PEFT означает Parameter Efficient Fine Tuning.
Давайте установим эти библиотеки особым способом.
# Установите последние версии peft & transformers library# если вы хотите работать с самыми новыми моделями!pip install -q git+https://github.com/huggingface/peft.git!pip install -q git+https://github.com/huggingface/transformers.git
Таким образом, мы устанавливаем последние версии этих библиотек напрямую из репозитория проекта на GitHub, который включает реализации последних моделей, таких как Mistral или LLAMA-2. В нашем случае это может быть не обязательно, потому что семейство моделей Bloom уже некоторое время поддерживается в доступной версии этих библиотек.
Импортируем необходимые классы.
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfigfrom trl import SFTTrainerimport torch
Загрузка модели.
# Используйте любую модель, если вы хотите сделать некоторые быстрые тесты, просто используйте самую маленькую.#model_name = "bigscience/bloomz-560m"#model_name="bigscience/bloom-1b1"model_name = "bigscience/bloom-7b1"target_modules = ["query_key_value"]
Выбранная модель – это Bloom 7B, но если вы проводите тесты, я рекомендую использовать одну из меньших моделей, чтобы минимизировать время обучения и использование ресурсов. Когда вы будете удовлетворены результатами, вы можете попробовать модель 7B и посмотреть результаты.
Для загрузки модели нам понадобится класс конфигурации, который определяет, как мы хотим выполнить квантование. Мы достигнем этого с помощью класса BitsAndBytesConfig из библиотеки Transformers.
bnb_config = BitsAndBytesConfig( load_in_4bit=True, bnb_4bit_use_double_quant=True, bnb_4bit_quant_type="nf4", bnb_4bit_compute_dtype=torch.bfloat16)
Мы указываем использование 4-битного квантования и также включаем двойное квантование для уменьшения потери точности.
Для параметра bnb_4bit_quant_type я использовал рекомендуемое значение в статье “QLoRA: Efficient Finetuning of Quantized LLMs.“
Теперь мы можем перейти к загрузке модели.
device_map = {"": 0}foundation_model = AutoModelForCausalLM.from_pretrained(model_name, quantization_config=bnb_config, device_map=device_map, use_cache = False)
Таким образом, у нас будет разными количествами бит модели в памяти. Если хотите, можете попытаться загрузить модель без квантования, просто удалив параметр quantization. Вероятно, вы не сможете ее загрузить из-за ограничений памяти.
Теперь давайте загрузим токенизатор, и все будет готово для тестирования модели.
tokenizer = AutoTokenizer.from_pretrained(model_name)tokenizer.pad_token = tokenizer.eos_token
Тестирование модели без настройки.
Чтобы определить, имеет ли настройка модели положительный эффект, лучше всего выполнить тест с только что загруженной моделью перед внесением каких-либо изменений.
С этой целью я создам функцию, которая принимает модель, ввод пользователя и максимальную длину ответа.
# Эта функция возвращает результаты от полученной модели и входов.def get_outputs(model, inputs, max_new_tokens=100): outputs = model.generate( input_ids=inputs["input_ids"], attention_mask=inputs["attention_mask"], max_new_tokens=max_new_tokens, repetition_penalty=1.5, #Избегайте повторений. early_stopping=False, #Модель может остановиться до достижения max_length eos_token_id=tokenizer.eos_token_id, ) return outputs
Теперь мы можем отправить запрос модели и проверить ее ответ, что позволит нам сравнить его с ответом, который она предоставит после донастройки.
#Выполнение оригинальной моделиinput_sentences = tokenizer("Хочу, чтобы вы выступали в роли мотивационного тренера. ", return_tensors="pt").to('cuda')foundational_outputs_sentence = get_outputs(foundation_model, input_sentences, max_new_tokens=50)print(tokenizer.batch_decode(foundational_outputs_sentence, skip_special_tokens=True))
[«Хочу, чтобы вы выступали в роли мотивационного тренера. Я не имею в виду, что вы должны говорить людям, что им следует делать, а скорее поощрять и помогать мотивировать их собственные действия.
Можно начать с таких вопросов:
Каковы ваши цели?
Как это поможет их достижению?
Затем»]
Ответ довольно хорош. Bloom – это хорошо подготовленная модель, способная предоставлять точные ответы в различных ситуациях. Я провел тот же тест с 560M Bloom, и ответ был действительно другим: [«Хочу, чтобы вы выступали в роли мотивационного тренера. Не бойтесь вызовов.»].
Подготовка набора данных.
В качестве используемого набора данных используется один из наборов данных, доступных в библиотеке datasets: fka/awesome-chatgpt-prompts.
Давайте взглянем на некоторые примеры, содержащиеся в наборе данных:
- Я хочу, чтобы вы выступали в качестве консоли javascript. Я буду печатать команды, а вы отвечаете на них тем, что должно отображаться в консоли javascript. Отвечайте только терминальным выводом внутри одного уникального блока кода и ничего больше. Не пишите объяснения. Не вводите команды, если я не дам указания. Когда мне нужно что-то сказать на английском, я сделаю это, поместив текст в фигурные скобки {вот так}. Моя первая команда – console.log(“Привет, мир”);
- Я хочу, чтобы вы выступали в качестве туристического гида. Я укажу свое местоположение, а вы предложите мне место для посещения рядом с моим местоположением. В некоторых случаях я также дам вам знать о типе мест, которые я хочу посетить. Вы также можете предложить мне места, похожие на указанное место, которые находятся рядом с ним. Мой первый запрос: “Я нахожусь в Стамбуле/Бейоглу и хочу посетить только музеи.”
- Я хочу, чтобы вы выступали в качестве сценариста. Вам нужно разработать увлекательный и творческий сценарий для полнометражного фильма или веб-сериала, который может завлечь зрителей. Начните с разработки интересных персонажей, задания их в окружение и диалогов между персонажами и т.д. Когда ваша разработка персонажей завершена, создайте захватывающий сюжет, полный поворотов и неожиданностей, который будет держать зрителей в напряжении до самого конца. Мой первый запрос: “Мне нужно написать романтическую драму, действие которой происходит в Париже.”
Теперь у нас ясное представление о стиле ответов, которые мы ожидаем от донастроенной модели. Давайте посмотрим, сможем ли мы достичь этого.
from datasets import load_datasetdataset = "fka/awesome-chatgpt-prompts"#Создание набора данных для создания подсказок.data = load_dataset(dataset)data = data.map(lambda samples: tokenizer(samples["prompt"]), batched=True)train_sample = data["train"].select(range(50))del datatrain_sample = train_sample.remove_columns('act')display(train_sample)
Dataset({ features: [‘prompt’, ‘input_ids’, ‘attention_mask’], num_rows: 50 })
В наборе данных два столбца. Я решил оставить только тот, который содержит подсказки, так как считаю, что другой столбец не содержит полезной информации.
Однако это всего лишь решение дизайна, принятое мной, и я призываю вас прокомментировать строку, удаляющую столбец act, и посмотреть, получится ли модель лучше или нет.
Донастройка с помощью QLoRA.
Теперь у нас есть все, что нам нужно: модель, токенизатор и загруженный набор данных.
Мы можем начать процесс донастройки, используя QLoRA для создания новой модели, способной генерировать подсказки, подобные тем, которые содержатся в наборе данных.
Первым шагом будет создание объекта конфигурации LoRA, в котором мы установим переменные, определяющие характеристики процесса донастройки.
# TARGET_MODULES# https://github.com/huggingface/peft/blob/39ef2546d5d9b8f5f8a7016ec10657887a867041/src/peft/utils/other.py#L220import peftfrom peft import LoraConfig, get_peft_modellora_config = LoraConfig( r=16, #Чем больше R, тем больше параметров для обучения. lora_alpha=16, #масштабный коэффициент, который регулирует величину весовой матрицы. Как выше, тем больше вес тренировки. target_modules=target_modules, lora_dropout=0.05, #Помогает избежать переобучения. bias="none", #указывает, должен ли обучаться параметр смещения. task_type="CAUSAL_LM")
Давайте рассмотрим установленные значения:
- r: это указывает на размер перепараметризации. Имейте в виду, что чем меньше значение, тем меньше параметров обучается. Обучение большего количества параметров дает больший шанс выучить связь между входными и выходными данными, но это также более вычислительно затратно. Значение 16 является разумным компромиссом, позволяющим контролировать параметры и достигать правильного результата.
- lora_alpha: этот коэффициент регулирует величину матрицы весов. В небольших моделях он обычно не оказывает существенного влияния, но в более крупных моделях он помогает придать больший вес донастройке по сравнению с остальными неизмененными весами.
- target_modules: это указывает, какие модули мы хотим обучать. На первый взгляд это может показаться сложным решением, прежде всего потому, что вам нужно знать внутренние имена модулей в модели. К счастью, вы можете посмотреть документацию, предоставленную Hugging Face, где указываются доступные модули для каждой семьи моделей.
- lora_dropout: Если вы ранее обучали модели глубокого обучения, вы, вероятно, знакомы с методом исключения (dropout). Он используется для предотвращения переобучения. В данном случае, учитывая короткую продолжительность обучения и ограниченные данные, вы можете экспериментировать со значением dropout равным 0.
- bias: Имеется три варианта — none, all и lora_only. Для классификации текста обычно используется значение none. Для более сложных задач вы можете выбирать между all и lora_only.
Теперь создадим каталог, который будет содержать вновь донастроенную модель, которую нужно указать в качестве аргумента класса TrainingArguments.
#Создание каталога для моделиimport osworking_dir = './'output_directory = os.path.join(working_dir, "peft_lab_outputs")
#Создание TrainingArgsimport transformersfrom transformers import TrainingArguments # , Trainertraining_args = TrainingArguments( output_dir=output_directory, auto_find_batch_size=True, # Найти правильный размер партии, который подходит для размера данных. learning_rate= 2e-4, # Больший коэффициент обучения, чем при полной донастройке. num_train_epochs=5)
Класс TrainingArguments получает параметры, с которыми мы все знакомы, такие как количество эпох обучения и коэффициент обучения.
Теперь у нас есть все, что нужно для обучения модели.
- Модель.
- TrainingArgs.
- Набор данных.
- Настройка LoRA.
tokenizer.pad_token = tokenizer.eos_tokentrainer = SFTTrainer( model=foundation_model, args=training_args, train_dataset=train_sample, peft_config = lora_config, dataset_text_field="prompt", tokenizer=tokenizer, data_collator=transformers.DataCollatorForLanguageModeling(tokenizer, mlm=False))trainer.train()
TrainOutput(global_step=65, training_loss=2.7377777099609375, metrics={‘train_runtime’: 404.0462, ‘train_samples_per_second’: 0.619, ‘train_steps_per_second’: 0.161, ‘total_flos’: 966262938697728.0, ‘train_loss’: 2.7377777099609375, ‘epoch’: 5.0})
Теперь мы можем сохранить эту модель, которая должна работать правильно.
#Сохранение моделиpeft_model_path = os.path.join(output_directory, f"lora_model")trainer.model.save_pretrained(peft_model_path)
Проверка донастроенной модели.
#Импорт peftfrom peft import AutoPeftModelForCausalLM, PeftConfig#import osdevice_map = {"": 0}working_dir = './'output_directory = os.path.join(working_dir, "peft_lab_outputs")peft_model_path = os.path.join(output_directory, f"lora_model")#Загрузка модели.loaded_model = AutoPeftModelForCausalLM.from_pretrained( peft_model_path, torch_dtype=torch.bfloat16, is_trainable=False, load_in_4bit=True, device_map = 'auto')
#Запрос к донастроенной модели.input_sentences = tokenizer("I want you to act as a motivational coach. ", return_tensors="pt").to('cuda')foundational_outputs_sentence = get_outputs(loaded_model, input_sentences, max_new_tokens=50)print(tokenizer.batch_decode(foundational_outputs_sentence, skip_special_tokens=True))
[«Я хочу, чтобы вы выступали в качестве мотивационного тренера. Вы будете работать с человеком, который испытывает сложности в своей карьере и не может добиться успеха. У этого человека может быть некоторый опыт ранее, но сейчас он ищет новые возможности, которые могут помочь ему достичь большего.\nТекущая ситуация клиента»]
Мне нравится эта реакция!
Заключение.
Давайте сравним ответы:
- Предварительно обученная модель: Я хочу, чтобы вы выступали в качестве мотивационного тренера. Я не имею в виду это в смысле указывания людям, что они должны делать, а скорее воодушевления и помощи им мотивировать свои собственные действия.\nВы можете начать, задавая такие вопросы:\n\nКаковы ваши цели?\nКак это поможет достичь их?\n\nДалее.
- Модель с тонкой настройкой: Я хочу, чтобы вы выступали в качестве мотивационного тренера. Я предоставлю некоторые детали об индивидуале, который нуждается в помощи в повышении уверенности, и ваша цель – «Идеи, как помочь человеку повысить самоуверенность». Ваше первое предложение должно быть «Предоставление поддержки, когда они в ней больше всего нуждаются»; мой ответ
Ясно, что процесс тонкой настройки оказал положительное влияние на структуру ответа. Модель с тонкой настройкой сгенерировала ответ, который гораздо ближе к тому, что мы ожидали от задания. Я считаю этот эксперимент успешным.
Я уверен, что с более продолжительным обучением можно достичь лучших результатов.
Вы можете проводить тесты, изменяя обучающие переменные и делая собственные выводы. Если вы хотите бросить себе вызов, попробуйте повторить упражнение, настраивая модель Mistral 7B!
Ресурсы.
Полный курс о Больших языковых моделях доступен на Github. Чтобы быть в курсе новых статей, рекомендуется следить за репозиторием или отмечать его звездочкой. Таким образом, вы будете получать уведомления при добавлении нового контента.
GitHub – peremartra/Large-Language-Model-Notebooks-Course: Практический курс по большим языковым моделям…
Практический курс по большим языковым моделям. . Вклад в peremartra/Large-Language-Model-Notebooks-Course…
github.com
Если вам нужна дополнительная информация о том, как работает QLoRA: https://arxiv.org/abs/2305.14314
Эта статья является частью серии, в которой мы исследуем практическое применение больших языковых моделей. Вы можете найти остальные статьи в следующем списке:
Практический курс по большим языковым моделям
Просмотреть список10 историй
Я регулярно пишу о глубоком обучении и искусственном интеллекте. Рассмотрите возможность подписаться на меня в VoAGI , чтобы получать обновления о новых статьях. И, конечно же, добро пожаловать соединиться со мной в LinkedIn и Twitter.