一、说明
Microsoft 的基于 Transformer 的小语言模型。它可以根据 MIT 许可在HuggingFace上使用。
它在 96 个 A100 GPU 上使用 1.4T 令牌进行了 14 天的训练。Phi-2 是一个 27 亿个参数的预训练 Transformer,不使用 RLHF 或指示微调。它进行下一个标记预测,并可用于问答、聊天格式和代码生成中的文本生成。
事实证明,phi-2 在多个基准测试和编码和数学等任务上优于许多具有 7B 和 13B 参数的模型。
小语言模型之所以具有优异的性能,是因为使用了经过提炼的高质量训练数据或“教科书质量”的数据。小语言模型使用知识蒸馏。也就是说,他们接受了从 LLMS 中提取的核心/基本知识的培训。然后采用剪枝和量化技术来删除模型的非必要部分。训练数据通常是综合数据集的混合物,这些数据集是专门创建的,旨在教导模型执行科学、日常活动、心理理论等领域的常识推理和一般知识。它还可能包含具有高教育意义的选择性网络数据价值和质量。小语言模型使用创新技术进行扩展。
接下来,我们将看到有关如何使用 HuggingFace 中的 phi-2 进行提示的分步 Python 代码,然后我们将在 veggo 数据集上对其进行微调。我使用 T4 GPU 在 Google Colab 免费层上运行了此代码笔记本。
二、安装依赖库
我的代码借鉴自 GitHub 上Harper Carrol 的这篇优秀教程。
- 安装所需的库
#@title Install required libraries
!pip install accelerate==0.25.0
!pip install bitsandbytes==0.41.1
!pip install datasets==2.14.6
!pip install peft==0.6.2
!pip install transformers==4.36.2
!pip install torch==2.1.0
!pip install einops==0.4.1
!pip install huggingface_hub
2.所需进口
import torch
import transformers
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig, TrainingArguments, pipeline, logging
from datasets import Dataset
3.我们将使用Google Colab Free tier(T4)上的cuda设备来运行模型
torch.set_default_device("cuda")
4.创建模型和分词器
#create the model object and the corresponding tokenizer
model = AutoModelForCausalLM.from_pretrained("microsoft/phi-2", torch_dtype="auto", trust_remote_code=True)
tokenizer = AutoTokenizer.from_pretrained("microsoft/phi-2", trust_remote_code=True)
5. 让我们运行一些提示并查看模型响应
# https://huggingface.co/microsoft/phi-2
# This prompt is for code completion
# here the prompt is written within the tokenizer()
inputs = tokenizer('''def fibonacci(n):"""This function prints the terms in Fibonacci series upto n"""''', return_tensors="pt", return_attention_mask=False)outputs = model.generate(**inputs, max_length=100)
text = tokenizer.batch_decode(outputs)[0]
print(text)
#https://huggingface.co/microsoft/phi-2
# here a string containing the prompt is defined separately from the tokenizer() and then passed to it
prompt = '''def fibonacci(n):"""This function prints the terms in Fibonacci series upto n"""'''
inputs = tokenizer(prompt, return_tensors="pt", return_attention_mask=False)
outputs = model.generate(**inputs, max_length=100)
text = tokenizer.batch_decode(outputs)[0]
print(text)
# here we see the output of phi-2 for a question-answering prompt
prompt = 'What is thee relevance of mathematics for understanding physics?'
inputs = tokenizer(prompt, return_tensors="pt", return_attention_mask=False)
outputs = model.generate(**inputs, max_length=200)
text = tokenizer.batch_decode(outputs)[0]
print(text)
三、在HuggingFace的veggo微调 phi-2 模型
现在我们将在HuggingFace 的“veggo”数据集上
ViGGO是视频游戏领域的英文数据到文本生成数据集。目标响应以会话形式以意义表示形式呈现。该数据集大约有 5,000 个非常干净的数据点,因此该数据集可用于评估神经模型的迁移学习、低资源或少样本能力。
6. 让我们设置加速器来加速训练/微调
#@title Set up accelerator to speed up the training/finetuning
from accelerate import FullyShardedDataParallelPlugin, Accelerator
from torch.distributed.fsdp.fully_sharded_data_parallel import FullOptimStateDictConfig, FullStateDictConfigfsdp_plugin = FullyShardedDataParallelPlugin(state_dict_config=FullStateDictConfig(offload_to_cpu=True, rank0_only=False),optim_state_dict_config=FullOptimStateDictConfig(offload_to_cpu=True, rank0_only=False),
)accelerator = Accelerator(fsdp_plugin=fsdp_plugin)
7. 使用有效的 HuggingFace 访问令牌登录您的 Huggingface 帐户。
您应该在 HuggingFace 上有一个帐户,然后您可以创建一个免费的访问令牌。
#@title login to your huggingface account using your access token
# you can find your access token at https://huggingface.co/settings/tokens
from huggingface_hub import notebook_login
notebook_login()
8.加载viggo数据集
#@title load viggo dataset
from datasets import load_datasettrain_dataset = load_dataset('gem/viggo', split='train')
eval_dataset = load_dataset('gem/viggo', split='validation')
test_dataset = load_dataset('gem/viggo', split='test')
9. 加载基础模型phi-2
#@title load base model microsoft/phi-2
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM, DataCollatorForLanguageModelingbase_model_id = "microsoft/phi-2"
model = AutoModelForCausalLM.from_pretrained(base_model_id, load_in_8bit=True, torch_dtype=torch.float16, trust_remote_code=True)
10. 在下面的代码单元中,我们设置 tokenizer 对象, tokenize() 函数将 tokenizer 应用于每个提示,并创建一个“labels”列,其值与数据中的“input_ids”列相同。
generate_and_tokenize_prompt() 函数将每个数据点转换为适合传递给 phi-2 模型的提示格式。它从数据点中提取“目标”和“含义表示”。最后,我们使用 map() 函数将此函数应用于 train 和 val 数据集中的每个数据点。
#@title set up the tokenizer for base model
tokenizer = AutoTokenizer.from_pretrained(base_model_id,add_eos_token=True,add_bos_token=True, use_fast=False, # needed for now, should be fixed soon
)#@title setup tokenize function to make labels and input_ids the same for the self-supervised fine-tuning.
def tokenize(prompt):result = tokenizer(prompt)result["labels"] = result["input_ids"].copy()return result#@title convert each sample into a promptdef generate_and_tokenize_prompt(data_point):full_prompt =f"""Given a target sentence construct the underlying meaning representation of the input sentence as a single function with attributes and attribute values.This function should describe the target string accurately and the function must be one of the following ['inform', 'request', 'give_opinion', 'confirm', 'verify_attribute', 'suggest', 'request_explanation', 'recommend', 'request_attribute'].The attributes must be one of the following: ['name', 'exp_release_date', 'release_year', 'developer', 'esrb', 'rating', 'genres', 'player_perspective', 'has_multiplayer', 'platforms', 'available_on_steam', 'has_linux_release', 'has_mac_release', 'specifier']### Target sentence:{data_point["target"]}### Meaning representation:{data_point["meaning_representation"]}"""return tokenize(full_prompt)#@title Reformat the prompt and tokenize each sample:tokenized_train_dataset = train_dataset.map(generate_and_tokenize_prompt)
tokenized_val_dataset = eval_dataset.map(generate_and_tokenize_prompt)
11. 模型的输入张量通常使用 max_length 参数将每个输入填充到统一长度。
为了确定该参数的值,我们可以绘制每个 input_id 的长度分布,并将 max_length 设置为等于最长 input_id 的长度。在本例中,选择的 max_length 为 320。
12. 接下来,我们将再次应用 tokenize(),并将 max_length 参数设置为 320。
max_length = 320 # appropriate max length for this dataset# redefine the tokenize function and tokenizertokenizer = AutoTokenizer.from_pretrained(base_model_id,padding_side="left",add_eos_token=True, add_bos_token=True, trust_remote_code=True,use_fast=False, # needed for now, should be fixed soon
)
tokenizer.pad_token = tokenizer.eos_tokendef tokenize(prompt):result = tokenizer(prompt,truncation=True,max_length=max_length,padding="max_length",)result["labels"] = result["input_ids"].copy()return result#@title tokenize train and validation datasets using generate_and_tokenize_prompt function
tokenized_train_dataset = train_dataset.map(generate_and_tokenize_prompt)
tokenized_val_dataset = eval_dataset.map(generate_and_tokenize_prompt)
四、使用LoRA来微调phi-2
13.让我们使用LoRA(低阶适应)来微调phi-2
低秩适应是一种快速微调大型语言模型的技术。它冻结预训练的模型权重,并将可训练的秩分解矩阵注入到 Transformer 架构的每一层中,从而减少下游任务的可训练参数的数量。它可以将可训练参数的数量减少10000倍,将GPU内存需求减少3倍。
要使用 LoRA 微调模型,您需要:
- 实例化基本模型。
- 创建一个配置 (
LoraConfig
),在其中定义 LoRA 特定参数。 - 用 包裹基本模型
get_peft_model()
以获得可训练的PeftModel
. PeftModel
像平常训练基本模型一样训练。
LoraConfig
允许您通过以下参数控制 LoRA 如何应用于基础模型:
r
:更新矩阵的秩,以 表示int
。较低的秩会导致较小的更新矩阵和较少的可训练参数。target_modules
:应用 LoRA 更新矩阵的模块(例如,注意力块)。alpha
:LoRA 比例因子。bias
:指定是否bias
应训练参数。可以是'none'
,'all'
或者'lora_only'
。modules_to_save
:除了 LoRA 层之外的模块列表,要设置为可训练并保存在最终检查点中。这些通常包括模型的自定义头,该头是为微调任务随机初始化的。layers_to_transform
:LoRA 转换的层列表。如果未指定,target_modules
则变换中的所有图层。layers_pattern
target_modules
:如果layers_to_transform
指定,则匹配 中图层名称的模式。默认情况下,PeftModel
将查看公共层模式(layers
、h
、blocks
等),将其用于奇异和自定义模型。rank_pattern
:从图层名称或正则表达式到与 指定的默认排名不同的排名的映射r
。alpha_pattern
:从图层名称或正则表达式到 alpha 的映射,与 指定的默认 alpha 不同lora_alpha
。
我们将把 LoRA 应用到模型的 Wqkv、fc1、fc2 层。
from peft import LoraConfig, get_peft_modelconfig = LoraConfig(r=8,lora_alpha=16,target_modules=["Wqkv","fc1","fc2",],bias="none",lora_dropout=0.05, # Conventionaltask_type="CAUSAL_LM",
)model = get_peft_model(model, config)# Apply the acceleratort to the model for faster traning.
model = accelerator.prepare_model(model)
五、 使用 LoRA 微调/训练模型
您将需要设置训练参数或配置参数,例如保存模型的输出目录。我正在将微调后的模型保存/推送到我的 HuggingFace 帐户,您也可以将微调后的模型保存在本地目录或 Colab 目录中。
其他训练参数包括warmup_steps、per_device_train_batch_size、gradient_accumulation_steps、max_steps、learning_rate、logging_steps、optim、logging_dir、save_strategy、save_steps、evaluation_strategy、eval_steps、do_eval、push_to_hub、report_to、run_name等。
maz_steps 确定要执行的最大训练步骤,越长,您的模型就越精细,完成训练所需的时间也越长。当 max_steps = 1000 时,我花了 90 分钟在免费的 Google Colab 上进行训练。学习率也会影响训练时间。
#Train the model and push each check point to Huggingface
import transformerstokenizer.pad_token = tokenizer.eos_tokentrainer = transformers.Trainer(model=model,train_dataset=tokenized_train_dataset,eval_dataset=tokenized_val_dataset,args=transformers.TrainingArguments(output_dir="./phi2-finetunedonviggodataset",warmup_steps=5,per_device_train_batch_size=1,gradient_accumulation_steps=4,max_steps=500,learning_rate=2.5e-5, logging_steps=50,optim="paged_adamw_8bit",logging_dir="./logs", # Directory for storing logssave_strategy="steps", # Save the model checkpoint every logging stepsave_steps=50, # Save checkpoints every 50 stepsevaluation_strategy="steps", # Evaluate the model every logging stepeval_steps=50, # Evaluate and save checkpoints every 50 stepsdo_eval=True, # Perform evaluation at the end of trainingpush_to_hub=True,),data_collator=transformers.DataCollatorForLanguageModeling(tokenizer, mlm=False),
)model.config.use_cache = False
trainer.train()
现在您已经在 viggo 数据集上微调了 phi-2,并将其保存在 output_dir 或您的 Huggingface 帐户中。
16.接下来,我们将比较基本模型(没有微调)和微调模型(上面训练过的)上示例提示的性能
#Load the base model
import torch
from transformers import AutoTokenizer, AutoModelForCausalLMbase_model_id = "microsoft/phi-2"base_model = AutoModelForCausalLM.from_pretrained(base_model_id,load_in_8bit=True,device_map="auto",trust_remote_code=True,torch_dtype=torch.float16,
)eval_tokenizer = AutoTokenizer.from_pretrained(base_model_id,add_bos_token=True,trust_remote_code=True,use_fast=False,
)#create a sample prompt for evaluation on base model
eval_prompt = """Given a target sentence construct the underlying meaning representation of the input sentence as a single function with attributes and attribute values.
This function should describe the target string accurately and the function must be one of the following ['inform', 'request', 'give_opinion', 'confirm', 'verify_attribute', 'suggest', 'request_explanation', 'recommend', 'request_attribute'].
The attributes must be one of the following: ['name', 'exp_release_date', 'release_year', 'developer', 'esrb', 'rating', 'genres', 'player_perspective', 'has_multiplayer', 'platforms', 'available_on_steam', 'has_linux_release', 'has_mac_release', 'specifier']### Target sentence:
Earlier, you stated that you didn't have strong feelings about PlayStation's Little Big Adventure. Is your opinion true for all games which don't have multiplayer?### Meaning representation:
"""# tokenize the above prompt and generate the response from base model
model_input = eval_tokenizer(eval_prompt, return_tensors="pt").to('cuda')
base_model.eval()
with torch.no_grad():print(eval_tokenizer.decode(base_model.generate(**model_input, max_new_tokens=100)[0], skip_special_tokens=True))
17. 现在让我们从我的 HuggingFace 帐户加载经过微调的模型,并在其上测试相同的提示。
from peft import PeftModel
ft_model = PeftModel.from_pretrained(base_model, "nimrita/phi2-finetunedonviggodataset", force_download=True)eval_prompt = """Given a target sentence construct the underlying meaning representation of the input sentence as a single function with attributes and attribute values.
This function should describe the target string accurately and the function must be one of the following ['inform', 'request', 'give_opinion', 'confirm', 'verify_attribute', 'suggest', 'request_explanation', 'recommend', 'request_attribute'].
The attributes must be one of the following: ['name', 'exp_release_date', 'release_year', 'developer', 'esrb', 'rating', 'genres', 'player_perspective', 'has_multiplayer', 'platforms', 'available_on_steam', 'has_linux_release', 'has_mac_release', 'specifier']### Target sentence:
Earlier, you stated that you didn't have strong feelings about PlayStation's Little Big Adventure. Is your opinion true for all games which don't have multiplayer?### Meaning representation:
"""model_input = eval_tokenizer(eval_prompt, return_tensors="pt").to('cuda')
ft_model = ft_model.to('cuda')
ft_model.eval()
with torch.no_grad():print(eval_tokenizer.decode(ft_model.generate(**model_input, max_new_tokens=100)[0], skip_special_tokens=True))
您刚刚微调了 phi-2。