Chapter 13
Chapter 12 - Fine-tuning Generation Models
Notebookhands-on-llm31 cells
Chapter 12 - Fine-tuning Generation Models
探索两步走的方法来微调生成模型
In [ ]python · cell 2
python
# %%capture
# !pip install -q accelerate peft bitsandbytes transformers trl sentencepieceIn [1]python · cell 3
python
import os
os.environ["HF_HOME"] = "/openbayes/home/huggingface"12.1 Supervised Fine-Tuning (SFT)
12.1.1 数据处理
In [2]python · cell 6
python
from transformers import AutoTokenizer
from datasets import load_dataset
# 加载一个 tokenizer 来使用它的聊天模板
tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen2.5-0.5B-Instruct")
def format_prompt(example):
chat = [
{"role": "system", "content": "你是一个非常棒的人工智能助手,是UP主 “用代码打点酱油的chaofa” 开发的"},
{"role": "user", "content": example["input"]},
{"role": "assistant", "content": example["target"]}
]
prompt = tokenizer.apply_chat_template(chat, tokenize=False)
return {"text": prompt}
# Load and format the data using the template TinyLLama is using
dataset = load_dataset("YeungNLP/firefly-train-1.1M", split="train[:500]")
dataset = dataset.map(format_prompt)Output
Special tokens have been added in the vocabulary, make sure the associated word embeddings are fine-tuned or trained. Repo card metadata block was not found. Setting CardData to empty.
Map: 0%| | 0/500 [00:00<?, ? examples/s]
In [3]python · cell 7
python
# Example of formatted prompt
print(dataset["text"][100])Output
<|im_start|>system 你是一个非常棒的人工智能助手,是UP主 “用代码打点酱油的chaofa” 开发的<|im_end|> <|im_start|>user 我当时在三司,访求太祖、仁宗的手书敕令没有见到,然而人人能传诵那些话,禁止私盐的建议也最终被搁置。 翻译成文言文:<|im_end|> <|im_start|>assistant 余时在三司,求访两朝墨敕不获,然人人能诵其言,议亦竟寝。<|im_end|>
12.1.2 Models - Quantization
In [4]python · cell 9
python
# 量化,为了省显存
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
model_name = "Qwen/Qwen2.5-0.5B-Instruct"
# 4-bit quantization configuration - Q in QLoRA
bnb_config = BitsAndBytesConfig(
load_in_4bit=True, # Use 4-bit precision model loading
bnb_4bit_quant_type="nf4", # Quantization type
bnb_4bit_compute_dtype="float16", # Compute dtype
bnb_4bit_use_double_quant=True, # Apply nested quantization
)
# Load the model to train on the GPU
model = AutoModelForCausalLM.from_pretrained(
model_name,
device_map="auto",
# Leave this out for regular SFT
quantization_config=bnb_config,
)
model.config.use_cache = False
model.config.pretraining_tp = 1 # 上面这两个配置,只有在 k-bit 量化的时候需要设置
# Load LLaMA tokenizer
tokenizer = AutoTokenizer.from_pretrained(model_name, trust_remote_code=True)
# tokenizer.pad_token = "<PAD>" # qwen2 的 pad token 不是 <pad>,所以用 <im_end>,因此需要注释掉
tokenizer.padding_side = "left" # 训练不重要,推理比较重要Output
Special tokens have been added in the vocabulary, make sure the associated word embeddings are fine-tuned or trained.
12.1.3 配置
12.1.3.1 LoRA 配置
In [5]python · cell 12
python
from peft import LoraConfig, prepare_model_for_kbit_training, get_peft_model
# Prepare LoRA Configuration
peft_config = LoraConfig(
lora_alpha=32, # LoRA Scaling
lora_dropout=0.1, # Dropout for LoRA Layers
r=64, # Rank,可训练数据越多,设置越大
bias="none",
task_type="CAUSAL_LM",
target_modules= ['k_proj', 'v_proj', 'q_proj']
# Layers to target
# ['k_proj', 'gate_proj', 'v_proj', 'up_proj', 'q_proj', 'o_proj', 'down_proj']
)
# prepare model for training
model = prepare_model_for_kbit_training(model)
# 如果没有 prepare_model_for_kbit_training,
# 且 training args 中配置了 gradient_checkpointing=True (这个其实也是为了省显存,其实不重要)
# 那么需要设置 model.enable_input_require_grads()
# model.enable_input_require_grads()
model = get_peft_model(model, peft_config)12.1.3.2 训练配置
In [6]python · cell 14
python
from transformers import TrainingArguments
output_dir = "./results"
# Training arguments
training_arguments = TrainingArguments(
output_dir=output_dir,
per_device_train_batch_size=2,
gradient_accumulation_steps=4,
optim="adamw_torch",
learning_rate=2e-4,
lr_scheduler_type="cosine",
num_train_epochs=1,
logging_steps=10,
fp16=True,
gradient_checkpointing=True
)12.1.4 Training!
In [7]python · cell 16
python
from trl import SFTTrainer
# Set supervised fine-tuning parameters
trainer = SFTTrainer(
model=model,
train_dataset=dataset,
dataset_text_field="text", # 注意 dataset 中的 text 字段
tokenizer=tokenizer,
args=training_arguments,
max_seq_length=512,
# Leave this out for regular SFT
peft_config=peft_config,
)
# Train model
trainer.train()
# Save QLoRA weights
trainer.model.save_pretrained("qwen2.5-0.5b-instruct-chaofa")Output
/openbayes/home/envs/hands-on-llm/lib/python3.10/site-packages/huggingface_hub/utils/_deprecation.py:100: FutureWarning: Deprecated argument(s) used in '__init__': dataset_text_field, max_seq_length. Will not be supported from version '1.0.0'. Deprecated positional argument(s) used in SFTTrainer, please use the SFTConfig to set these arguments instead. warnings.warn(message, FutureWarning) /openbayes/home/envs/hands-on-llm/lib/python3.10/site-packages/transformers/training_args.py:1965: FutureWarning: `--push_to_hub_token` is deprecated and will be removed in version 5 of 🤗 Transformers. Use `--hub_token` instead. warnings.warn( /openbayes/home/envs/hands-on-llm/lib/python3.10/site-packages/trl/trainer/sft_trainer.py:269: UserWarning: You passed a `max_seq_length` argument to the SFTTrainer, the value you passed will override the one in the `SFTConfig`. warnings.warn( /openbayes/home/envs/hands-on-llm/lib/python3.10/site-packages/trl/trainer/sft_trainer.py:307: UserWarning: You passed a `dataset_text_field` argument to the SFTTrainer, the value you passed will override the one in the `SFTConfig`. warnings.warn(
Map: 0%| | 0/500 [00:00<?, ? examples/s]
/openbayes/home/envs/hands-on-llm/lib/python3.10/site-packages/trl/trainer/sft_trainer.py:397: UserWarning: You passed a tokenizer with `padding_side` not equal to `right` to the SFTTrainer. This might lead to some unexpected behaviour due to overflow issues when training a model in half-precision. You might consider adding `tokenizer.padding_side = 'right'` to your code. warnings.warn( /openbayes/home/envs/hands-on-llm/lib/python3.10/site-packages/torch/utils/checkpoint.py:464: UserWarning: torch.utils.checkpoint: the use_reentrant parameter should be passed explicitly. In version 2.4 we will raise an exception if use_reentrant is not passed. use_reentrant=False is recommended, but if you need to preserve the current default behavior, you can pass use_reentrant=True. Refer to docs for more details on the differences between the two variants. warnings.warn(
<IPython.core.display.HTML object>
[62/62 00:59, Epoch 0/1]
| Step | Training Loss |
|---|---|
| 10 | 4.036000 |
| 20 | 3.625800 |
| 30 | 3.099800 |
| 40 | 2.696700 |
| 50 | 2.733300 |
| 60 | 2.779900 |
/openbayes/home/envs/hands-on-llm/lib/python3.10/site-packages/huggingface_hub/file_download.py:1142: FutureWarning: `resume_download` is deprecated and will be removed in version 1.0.0. Downloads always resume when possible. If you want to force a new download, use `force_download=True`. warnings.warn(
12.1.4.1 Merge Adapter (LoRA 和 base model 合并)
In [8]python · cell 18
python
from peft import AutoPeftModelForCausalLM
model = AutoPeftModelForCausalLM.from_pretrained(
"qwen2.5-0.5b-instruct-chaofa",
low_cpu_mem_usage=True,
device_map="auto",
)
# Merge LoRA and base model
merged_model = model.merge_and_unload()12.1.4.2 Inference
In [10]python · cell 20
python
from transformers import pipeline
pipe = pipeline(task="text-generation", model=merged_model, tokenizer=tokenizer)
prompt_example = """<|im_start|>system
你是一个非常棒的人工智能助手,是UP主 “用代码打点酱油的chaofa” 开发的。<|im_end|>
<|im_start|>user
天气太热了,所以我今天没有学习一点。
翻译成文言文:<|im_end|>
<|im_start|>assistant
"""
print(pipe(prompt_example, max_new_tokens=50)[0]["generated_text"])Output
<|im_start|>system 你是一个非常棒的人工智能助手,是UP主 “用代码打点酱油的chaofa” 开发的。<|im_end|> <|im_start|>user 天气太热了,所以我今天没有学习一点。 翻译成文言文:<|im_end|> <|im_start|>assistant 天气甚热,故今日无学一息。
12.2 Preference Tuning (PPO/DPO)
12.2.1 Data Preprocessing
In [ ]python · cell 23
python
from datasets import load_dataset
def format_prompt(example):
"""Format the prompt to using the <|user|> template TinyLLama is using"""
# Format answers
system = "<|system|>\n" + example['system'] + "</s>\n"
prompt = "<|user|>\n" + example['input'] + "</s>\n<|assistant|>\n"
chosen = example['chosen'] + "</s>\n"
rejected = example['rejected'] + "</s>\n"
return {
"prompt": system + prompt,
"chosen": chosen,
"rejected": rejected,
}
# Apply formatting to the dataset and select relatively short answers
dpo_dataset = load_dataset("argilla/distilabel-intel-orca-dpo-pairs", split="train")
dpo_dataset = dpo_dataset.filter(
lambda r:
r["status"] != "tie" and
r["chosen_score"] >= 8 and
not r["in_gsm8k_train"]
)
dpo_dataset = dpo_dataset.map(format_prompt, remove_columns=dpo_dataset.column_names)
dpo_datasetOutput
Downloading readme: 0%| | 0.00/10.1k [00:00<?, ?B/s]
Downloading data: 0%| | 0.00/79.2M [00:00<?, ?B/s]
Generating train split: 0%| | 0/12859 [00:00<?, ? examples/s]
Filter: 0%| | 0/12859 [00:00<?, ? examples/s]
Map: 0%| | 0/5922 [00:00<?, ? examples/s]
Dataset({
features: ['chosen', 'rejected', 'prompt'],
num_rows: 5922
})12.2.2 Models - Quantization
In [ ]python · cell 25
python
from peft import AutoPeftModelForCausalLM
from transformers import BitsAndBytesConfig, AutoTokenizer
# 4-bit quantization configuration - Q in QLoRA
bnb_config = BitsAndBytesConfig(
load_in_4bit=True, # Use 4-bit precision model loading
bnb_4bit_quant_type="nf4", # Quantization type
bnb_4bit_compute_dtype="float16", # Compute dtype
bnb_4bit_use_double_quant=True, # Apply nested quantization
)
# Merge LoRA and base model
model = AutoPeftModelForCausalLM.from_pretrained(
"TinyLlama-1.1B-qlora",
low_cpu_mem_usage=True,
device_map="auto",
quantization_config=bnb_config,
)
merged_model = model.merge_and_unload()
# Load LLaMA tokenizer
model_name = "TinyLlama/TinyLlama-1.1B-intermediate-step-1431k-3T"
tokenizer = AutoTokenizer.from_pretrained(model_name, trust_remote_code=True)
tokenizer.pad_token = "<PAD>"
tokenizer.padding_side = "left"Output
/usr/local/lib/python3.10/dist-packages/peft/tuners/lora/bnb.py:325: UserWarning: Merge lora module to 4-bit linear may get different generations due to rounding errors. warnings.warn(
12.2.3 Configuration
In [ ]python · cell 27
python
from peft import LoraConfig, prepare_model_for_kbit_training, get_peft_model
# Prepare LoRA Configuration
peft_config = LoraConfig(
lora_alpha=32, # LoRA Scaling
lora_dropout=0.1, # Dropout for LoRA Layers
r=64, # Rank
bias="none",
task_type="CAUSAL_LM",
target_modules= # Layers to target
['k_proj', 'gate_proj', 'v_proj', 'up_proj', 'q_proj', 'o_proj', 'down_proj']
)
# prepare model for training
model = prepare_model_for_kbit_training(model)
model = get_peft_model(model, peft_config)In [ ]python · cell 28
python
from trl import DPOConfig
output_dir = "./results"
# Training arguments
training_arguments = DPOConfig(
output_dir=output_dir,
per_device_train_batch_size=2,
gradient_accumulation_steps=4,
optim="paged_adamw_32bit",
learning_rate=1e-5,
lr_scheduler_type="cosine",
max_steps=200,
logging_steps=10,
fp16=True,
gradient_checkpointing=True,
warmup_ratio=0.1
)In [ ]python · cell 29
python
from trl import DPOTrainer
# Create DPO trainer
dpo_trainer = DPOTrainer(
model,
args=training_arguments,
train_dataset=dpo_dataset,
tokenizer=tokenizer,
peft_config=peft_config,
beta=0.1,
max_prompt_length=512,
max_length=512,
)
# Fine-tune model with DPO
dpo_trainer.train()
# Save adapter
dpo_trainer.model.save_pretrained("TinyLlama-1.1B-dpo-qlora")Output
/usr/local/lib/python3.10/dist-packages/huggingface_hub/utils/_deprecation.py:100: FutureWarning: Deprecated argument(s) used in '__init__': max_prompt_length, max_length. Will not be supported from version '1.0.0'. Deprecated positional argument(s) used in DPOTrainer, please use the DPOConfig to set these arguments instead. warnings.warn(message, FutureWarning) /usr/local/lib/python3.10/dist-packages/peft/tuners/lora/bnb.py:325: UserWarning: Merge lora module to 4-bit linear may get different generations due to rounding errors. warnings.warn( /usr/local/lib/python3.10/dist-packages/trl/trainer/dpo_trainer.py:358: UserWarning: You passed `max_length` to the DPOTrainer, the value you passed will override the one in the `DPOConfig`. warnings.warn( /usr/local/lib/python3.10/dist-packages/trl/trainer/dpo_trainer.py:371: UserWarning: You passed `max_prompt_length` to the DPOTrainer, the value you passed will override the one in the `DPOConfig`. warnings.warn( /usr/local/lib/python3.10/dist-packages/trl/trainer/dpo_trainer.py:411: UserWarning: When using DPODataCollatorWithPadding, you should set `remove_unused_columns=False` in your TrainingArguments we have set it for you, but you should do it yourself in the future. warnings.warn(
Map: 0%| | 0/5922 [00:00<?, ? examples/s]
max_steps is given, it will override any value given in num_train_epochs /usr/local/lib/python3.10/dist-packages/torch/utils/checkpoint.py:464: UserWarning: torch.utils.checkpoint: the use_reentrant parameter should be passed explicitly. In version 2.4 we will raise an exception if use_reentrant is not passed. use_reentrant=False is recommended, but if you need to preserve the current default behavior, you can pass use_reentrant=True. Refer to docs for more details on the differences between the two variants. warnings.warn( /usr/local/lib/python3.10/dist-packages/torch/utils/checkpoint.py:91: UserWarning: None of the inputs have requires_grad=True. Gradients will be None warnings.warn( Could not estimate the number of tokens of the input, floating-point operations will not be computed
<IPython.core.display.HTML object>
[200/200 12:52, Epoch 0/1]
| Step | Training Loss |
|---|---|
| 10 | 0.692400 |
| 20 | 0.678200 |
| 30 | 0.646000 |
| 40 | 0.606300 |
| 50 | 0.595600 |
| 60 | 0.616800 |
| 70 | 0.593700 |
| 80 | 0.531900 |
| 90 | 0.559200 |
| 100 | 0.639000 |
| 110 | 0.496500 |
| 120 | 0.586000 |
| 130 | 0.630000 |
| 140 | 0.590100 |
| 150 | 0.577500 |
| 160 | 0.591000 |
| 170 | 0.606900 |
| 180 | 0.627800 |
| 190 | 0.668600 |
| 200 | 0.555400 |
In [ ]python · cell 30
python
from peft import PeftModel
# Merge LoRA and base model
model = AutoPeftModelForCausalLM.from_pretrained(
"TinyLlama-1.1B-qlora",
low_cpu_mem_usage=True,
device_map="auto",
)
sft_model = model.merge_and_unload()
# Merge DPO LoRA and SFT model
dpo_model = PeftModel.from_pretrained(
sft_model,
"TinyLlama-1.1B-dpo-qlora",
device_map="auto",
)
dpo_model = dpo_model.merge_and_unload()In [ ]python · cell 31
python
from transformers import pipeline
# Use our predefined prompt template
prompt = """<|user|>
Tell me something about Large Language Models.</s>
<|assistant|>
"""
# Run our instruction-tuned model
pipe = pipeline(task="text-generation", model=dpo_model, tokenizer=tokenizer)
print(pipe(prompt)[0]["generated_text"])Output
<|user|> Tell me something about Large Language Models.</s> <|assistant|> Large Language Models (LLMs) are a type of artificial intelligence (AI) that can generate human-like language. They are trained on large amounts of data, including text, audio, and video, and are capable of generating complex and nuanced language. LLMs are used in a variety of applications, including natural language processing (NLP), machine translation, and chatbots. They can be used to generate text, speech, or images, and can be trained to understand different languages and dialects. One of the most significant applications of LLMs is in the field of natural language generation (NLG). LLMs can be used to generate text in a variety of languages, including English, French, and German. They can also be used to generate speech, such as in chatbots or voice assistants. LLMs have the potential to revolutionize the way we communicate and interact with each other. They can help us create more engaging and personalized content, and they can also help us understand each other better.
