Это руководство охватывает основные этапы работы с LLM, включая установку зависимостей, загрузку и настройку модели, обучение и генерацию текста и использование LoRA и PEFT.
Минимальная конфигурация GPU, создаваемого Jupyter Server — 1 GPU Tesla A100 40Gb.
Полный исходный код доступен для копирования в репозитории на GitHub.
Перед началом работы необходимо установить библиотеки: pip, torch, transformers, accelerate, datasets, loralib, einops, scipy и sentencepiece. Они нужны для работы с нейронными сетями и обработки естественного языка.
!pip install -Uqqq pip!pip install -qqq bitsandbytes torch transformerspeft peft \accelerate datasets loralib==0.1.1 einops==0.6.1 scipy sentencepiece
import osimport torchimport transformersfrom datasets import load_datasetfrom peft import (LoraConfig,PeftConfig,PeftModel,get_peft_model,prepare_model_for_kbit_training)from transformers import (AutoModelForCausalLM,AutoTokenizer,BitsAndBytesConfig)os.environ["CUDA_VISIBLE_DEVICES"] = "0"
Выберите подходящую предварительно обученную модель. В этом примере используется модель Intel/neural-chat-7b-v3-1, но можно выбрать другую, заменив значение переменной MODEL_NAME. Для сжатия модели мы используем библиотеку BitsAndBytes:
MODEL_NAME = "Intel/neural-chat-7b-v3-1"bnb_config = BitsAndBytesConfig(load_in_4bit=True,bnb_4bit_use_double_quant=True,bnb_4bit_quant_type="nf4",bnb_4bit_compute_dtype=torch.bfloat16)
Создается объект конфигурации bnb_config, который определяет параметры для сжатия модели с помощью библиотеки bitsandbytes. Это позволяет эффективно использовать память и ускорить вычисления.
Используются следующие параметры:
load_in_4bit — загрузка модели в 4-битном формате с уменьшением ее размера в памяти;
bnb_4bit_use_double_quant — использование двойной квантизации для дополнительного уменьшения размера модели;
bnb_4bit_quant_type — тип квантизации ("nf4"), который определяет, как модель будет сжиматься;
bnb_4bit_compute_dtype — тип данных для вычислений (torch.bfloat16), который позволяет уменьшить потребление памяти и ускорить вычисления, сохраняя при этом точность.
model = AutoModelForCausalLM.from_pretrained(MODEL_NAME,device_map="auto",trust_remote_code=True,quantization_config=bnb_config)tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)tokenizer.pad_token = tokenizer.eos_token
LoRA — это метод настройки моделей, который позволяет изменять только небольшую часть параметров модели.
def print_trainable_parameters(model):"""Calculates and displays the total number of model parameters and the number of trainable parameters."""trainable_params = 0all_param = 0for _, param in model.named_parameters():all_param += param.numel()if param.requires_grad:trainable_params += param.numel()print(f"trainable params: {trainable_params} || all params: {all_param} || trainables%: {100 * trainable_params / all_param}")
model.gradient_checkpointing_enable()
Этот метод активирует механизм контрольных точек градиента в модели. Благодаря этому экономится память при обучении, поскольку хранится только необходимая для вычисления градиентов информация.
model = prepare_model_for_kbit_training(model)
Функция prepare_model_for_kbit_training адаптирует модель для обучения с использованием определенного количества битов, что является частью оптимизации для уменьшения занимаемой памяти.
config = LoraConfig(r=8,lora_alpha=32,#target_modules=["query_key_value"],target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],lora_dropout=0.05,bias="none",task_type="CAUSAL_LM")
Здесь создается конфигурация для LoRA (Low-Rank Adaptation), с помощью которой можно адаптировать только небольшую часть весов модели. Конфигурация имеет следующие параметры:
r и lora_alpha — параметры, контролирующие размер и мощность адаптации;
target_modules — список модулей модели, к которым будет применена адаптация LoRA;
lora_dropout — применение dropout к адаптированным весам;
bias — настройка использования смещения в адаптации;
task_type — тип задачи, для которой настраивается модель (в данном случае генерация текста).
model = get_peft_model(model, config)
Функция get_peft_model применяет PEFT (Parameter Efficient Fine-tuning) к модели, используя предварительно заданную конфигурацию LoRA. Это позволяет более эффективно провести тонкую настройку модели.
print_trainable_parameters(model)
Прежде чем начать обучение, можно проверить, как модель генерирует ответы на основе заданного запроса.
Ниже приведен многострочный фрагмент, задающий текстовый запрос для модели. Текст между <human> и <assistant> указывает на диалог между человеком и ассистентом. Метод .strip() удаляет начальные и конечные пробелы и переносы строк.
prompt = """<human>: midjourney prompt for a girl sit on the mountain<assistant>:""".strip()
max_new_tokens — максимальное количество новых токенов, которые модель может сгенерировать;
temperature — степень случайности в выборе слов, где меньшее значение приводит к более предсказуемому тексту;
top_p — вероятностный порог выбора слов; рассматриваться будут только слова с вероятностью выше этого порога;
num_return_sequences — количество возвращаемых последовательностей;
pad_token_id и eos_token_id — идентификаторы токенов заполнения и окончания предложения.
generation_config = model.generation_configgeneration_config.max_new_tokens = 200generation_config.temperature = 0.7generation_config.top_p = 0.7generation_config.num_return_sequences = 1generation_config.pad_token_id = tokenizer.eos_token_idgeneration_config.eos_token_id = tokenizer.eos_token_id
Запрос преобразуется в формат, пригодный для модели с помощью токенизатора. return_tensors="pt" указывает на то, что возвращаемые тензоры должны быть в формате PyTorch. Затем данные перемещаются на выбранное устройство (GPU).
device = "cuda:0"encoding = tokenizer(prompt, return_tensors="pt").to(device)
В блоке with torch.inference_mode() происходит генерация текста без расчета градиентов, что уменьшает потребление памяти и ускоряет процесс. Метод model.generate() генерирует текст, используя заданные input_ids (токенизированный запрос), attention_mask (маска внимания для запроса) и ранее настроенные параметры генерации generation_config.
%%timewith torch.inference_mode():outputs = model.generate(input_ids = encoding.input_ids,attention_mask = encoding.attention_mask,generation_config = generation_config)
Полученный ответ декодируется обратно в читаемый текст. skip_special_tokens=True указывает на то, что специальные токены, например, токен окончания предложения, должны быть пропущены при декодировании.
print(tokenizer.decode(outputs[0], skip_special_tokens=True))
Для обучения модели используйте набор данных, подходящий для вашей задачи. В этом примере загружаются данные из CSV-файла.
Здесь используется функция load_dataset из библиотеки datasets для загрузки набора данных из CSV-файла. Файл midjourney_prompt_dataset.csv содержит данные, которые будут использоваться для обучения или тестирования модели. Функция load_dataset автоматически обрабатывает файл и конвертирует его в формат, удобный для работы с библиотекой.
data = load_dataset("csv", data_files="midjourney_prompt_dataset.csv")
Эта функция сначала генерирует текстовый запрос с помощью generate_prompt, а затем токенизирует его с помощью ранее загруженного токенизатора. Опции padding=True и truncation=True обеспечивают добавление паддинга (заполнение до единой длины) и обрезку длинных запросов соответственно.
def generate_prompt(data_point):return f"""<human>: {data_point["User"]}<assistant>: {data_point["Prompt"]}""".strip()def generate_and_tokenize_prompt(data_point):full_prompt = generate_prompt(data_point)tokenized_full_prompt = tokenizer(full_prompt, padding=True, truncation=True)return tokenized_full_prompt
Здесь выбирается подмножество данных для обучения data["train"], после чего оно перемешивается методом shuffle(). Затем с помощью метода .map() к каждому элементу набора данных применяется функция generate_and_tokenize_prompt, которая преобразует и токенизирует данные. В результате получается подготовленный к обучению набор данных, где каждый элемент представляет собой токенизированный текстовый запрос.
data = data["train"].shuffle().map(generate_and_tokenize_prompt)
Настройте параметры обучения и начните процесс обучения модели.
Здесь создается объект TrainingArguments, который содержит различные параметры для обучения:
per_device_train_batch_size — размер батча обучения на каждом устройстве;
gradient_accumulation_steps — количество шагов накопления градиента перед их обратным распространением;
num_train_epochs — количество эпох обучения;
learning_rate — скорость обучения;
fp16 — использование 16-битной точности с плавающей запятой для ускорения обучения и снижения потребления памяти;
save_total_limit — максимальное количество сохраняемых
;logging_steps — частота логирования;
output_dir — директория для сохранения результатов обучения;
optim — оптимизатор; здесь используется 8-битная версия AdamW;
lr_scheduler_type — тип планировщика скорости обучения;
warmup_ratio — доля общего числа шагов обучения, в течение которых скорость обучения линейно увеличивается до заданной.
training_args = transformers.TrainingArguments(per_device_train_batch_size=1,gradient_accumulation_steps=8,num_train_epochs=4,learning_rate=2e-4,fp16=True,save_total_limit=3,logging_steps=1,output_dir="experiments",optim="paged_adamw_8bit",lr_scheduler_type="cosine",warmup_ratio=0.05,)
Trainer отвечает за процесс обучения модели и имеет следующие параметры:
model — модель, которая будет обучаться;
train_dataset — набор данных для обучения;
args — аргументы обучения, определенные выше;
data_collator — объект, который формирует батчи из данных.
Здесь используется DataCollatorForLanguageModeling, который подходит для задач языкового моделирования. mlm=False указывает, что обучение проводится без маскирования токенов (не MLM).
trainer = transformers.Trainer(model=model,train_dataset=data,args=training_args,data_collator=transformers.DataCollatorForLanguageModeling(tokenizer, mlm=False))
Эта настройка отключает кэширование в модели, что может быть полезно для экономии памяти во время обучения.
model.config.use_cache = False
Метод train() объекта Trainer запускает процесс обучения модели с использованием заданных данных, аргументов и настроек.
trainer.train()
После обучения сохраните модель и проверьте ее способность генерировать текст.
Сохраняет в папку trained-model текущее состояние модели, которое включает в себя веса модели и ее конфигурацию. Сохранение модели позволяет использовать ее позже без необходимости повторного обучения.
model.save_pretrained("trained-model")
Здесь загружается конфигурация PEFT (Parameter-Efficient Fine-Tuning) из сохраненной модели. Далее создается новый экземпляр модели с помощью этой конфигурации. Параметры return_dict=True, quantization_config=bnb_config, device_map="auto" и trust_remote_code=True настраивают поведение модели, включая формат возвращаемых данных, настройки квантизации, автоматическое распределение по устройствам и доверие к исполняемому коду.
config = PeftConfig.from_pretrained('./trained-model')model = AutoModelForCausalLM.from_pretrained(config.base_model_name_or_path,return_dict=True,quantization_config=bnb_config,device_map="auto",trust_remote_code=True)
Загружается токенизатор, соответствующий модели, и устанавливается токен заполнения (`pad_token`) равным токену окончания предложения (`eos_token`), что часто необходимо для моделей генерации текста.
tokenizer = AutoTokenizer.from_pretrained(config.base_model_name_or_path)tokenizer.pad_token = tokenizer.eos_token
Создается экземпляр модели PEFT из сохраненной модели, что позволяет использовать параметры, полученные в результате тонкой настройки.
model = PeftModel.from_pretrained(model, './trained-model')
Задается текстовый запрос, который затем токенизируется и преобразуется в формат тензоров PyTorch для отправки на вычислительное устройство.
generation_config = model.generation_configgeneration_config.max_new_tokens = 200generation_config.temperature = 0.7generation_config.top_p = 0.7generation_config.num_return_sequences = 1generation_config.pad_token_id = tokenizer.eos_token_idgeneration_config.eos_token_id = tokenizer.eos_token_id
device = "cuda:0"prompt = """<human>: midjourney prompt for a girl sit on the mountain<assistant>:""".strip()encoding = tokenizer(prompt, return_tensors="pt").to(device)
В блоке with torch.no_grad() отключается вычисление градиентов для экономии ресурсов. Метод model.generate используется для генерации текста на основе входных данных (input_ids, attention_mask) и предварительно настроенных параметров генерации (`generation_config`).
%%timewith torch.no_grad():model.config.use_cache = Falseoutputs = model.generate(input_ids = encoding.input_ids,attention_mask = encoding.attention_mask,generation_config = generation_config)
Декодирует и выводит первый элемент сгенерированного текста, преобразуя его из последовательности токенов обратно в читаемый формат.
print(tokenizer.decode(outputs[0]))