大模型微调技术
p-tuning v2 在多种任务上下进行微调,之后对于不同的任务如token classification与sentence classification添加了随机初始化的任务头(AutoModelForTokenClassification、AutoModelForSequenceClassification),而非使用自然语言的方式,可以说V2是集大成者。手动尝试最优的提示无异于大海捞针,于
微调的两种方式:1.权重W的微调(强化原始模型参数中的某些值,使得模型在特定任务上表现的更好) 2.输入参数X的微调(使得输入能够更加符合模型的要求)
理解篇
- prompt tuning方式
注:改变X输入
固定预训练参数,为每一个任务额外添加一个或多个embedding,之后拼接query正常输入LLM,并只训练这些embedding。左图为单任务全参数微调,右图为prompt tuning。
代码样例:
from peft import PromptTuningConfig, get_peft_model
peft_config = PromptTuningConfig(task_type="SEQ_CLS", num_virtual_tokens=10)
model = AutoModelForCausalLM.from_pretrained(model_name_or_path, return_dict=True)
model = get_peft_model(model, peft_config)
- prefix tuning 方式
prefix tuning依然是固定预训练参数,但除为每一个任务额外添加一个或多个embedding之外,利用多层感知编码prefix,注意多层感知机就是prefix的编码器,不再像prompt tuning继续输入LLM,采用小的神经网络模型对输入数据进行编码,并将编码后的向量进行拼接。
embedding = torch.nn.Embedding(num_virtual_tokens, token_dim)
transform = torch.nn.Sequential(
torch.nn.Linear(token_dim, encoder_hidden_size),
torch.nn.Tanh(),
torch.nn.Linear(encoder_hidden_size, num_layers * 2 * token_dim),
)
代码样例:
peft_config = PrefixTuningConfig(task_type="CAUSAL_LM", num_virtual_tokens=20)
model = AutoModelForCausalLM.from_pretrained(model_name_or_path, return_dict=True)
model = get_peft_model(model, peft_config)
- LoRA方式
LoRA冻结了预训练模型的参数,并在每一层decoder中加入dropout+Linear+Conv1d额外的参数(加外挂,以并联的形式在一侧加入AB两个矩阵,注:初始化时B矩阵采用全0矩阵)
代码样例:
peft_config = LoraConfig(task_type="SEQ_CLS", inference_mode=False, r=8, lora_alpha=16, lora_dropout=0.1)
model = AutoModelForCausalLM.from_pretrained(model_name_or_path, return_dict=True)
model = get_peft_model(model, peft_config)
4.p-tuning方式
手动尝试最优的提示无异于大海捞针,于是便有了自动离散提示搜索的方法(作图),但提示是离散的,神经网络是连续的,所以寻找的最优提示可能是次优的。p-tuning依然是固定LLM参数,利用多层感知机和LSTM对prompt进行编码,编码之后与其他向量进行拼接之后正常输入LLM。注意,训练之后只保留prompt编码之后的向量即可,无需保留编码器。
self.lstm_head = torch.nn.LSTM(
input_size=self.input_size,
hidden_size=self.hidden_size,
num_layers=num_layers,
dropout=lstm_dropout,
bidirectional=True,
batch_first=True,
)
self.mlp_head = torch.nn.Sequential(
torch.nn.Linear(self.hidden_size * 2, self.hidden_size * 2),
torch.nn.ReLU(),
torch.nn.Linear(self.hidden_size * 2, self.output_size),
)
self.mlp_head(self.lstm_head(input_embeds)[0])
以上代码可清晰展示出prompt编码器的结构。
- p-tuning v2方式
p-tuning的问题是在小参数量模型上表现差,于是有了V2版本,类似于LoRA每层都嵌入了新的参数(称之为Deep FT)。p-tuning v2 在多种任务上下进行微调,之后对于不同的任务如token classification与sentence classification添加了随机初始化的任务头(AutoModelForTokenClassification、AutoModelForSequenceClassification),而非使用自然语言的方式,可以说V2是集大成者。
v1到v2的可视化:蓝色部分为参数冻结,橙色部分为可训练部分
代码样例:
peft_config = PrefixTuningConfig(task_type="SEQ_CLS", num_virtual_tokens=20)
model = AutoModelForSequenceClassification.from_pretrained(model_name_or_path, return_dict=True)
model = get_peft_model(model, peft_config)
代码篇
注:以下代码在pytorch 1.12.1版本下运行,其他包都是最新版本
按照官方文档搭建环境:
pip install deepspeed>=0.9.0
git clone https://github.com/microsoft/DeepSpeedExamples.git
cd DeepSpeedExamples/applications/DeepSpeed-Chat/
pip install -r requirements.txt
全参数微调,会爆OOM
deepspeed --num_gpus 1 main.py
–data_path Dahoas/rm-static
–data_split 2,4,4
–model_name_or_path facebook/opt-6.5b
–gradient_accumulation_steps 2
–lora_dim 128
–zero_stage 0
–deepspeed
–output_dir $OUTPUT
&> $OUTPUT/training.log
修改为deepspeed ,便能愉快的run起来
deepspeed main.py
–data_path Dahoas/rm-static
–data_split 2,4,4
–model_name_or_path facebook/opt-6.7b
–per_device_train_batch_size 4
–per_device_eval_batch_size 4
–max_seq_len 512
–learning_rate 9.65e-6
–weight_decay 0.1
–num_train_epochs 2
–gradient_accumulation_steps 1
–lr_scheduler_type cosine
–num_warmup_steps 0
–seed 1234
–lora_dim 128
–gradient_checkpointing
–zero_stage 3
–deepspeed
–output_dir $OUTPUT_PATH
&> $OUTPUT_PATH/training.log
可以加上LoRA
deepspeed --num_gpus 1 main.py
–data_path Dahoas/rm-static
–data_split 2,4,4
–model_name_or_path facebook/opt-6.7b
–per_device_train_batch_size 8
–per_device_eval_batch_size 8
–max_seq_len 512
–learning_rate 1e-3
–weight_decay 0.1
–num_train_epochs 2
–gradient_accumulation_steps 16
–lr_scheduler_type cosine
–num_warmup_steps 0
–seed 1234
–gradient_checkpointing
–zero_stage 0
–lora_dim 128
–lora_module_name decoder.layers.
–deepspeed
–output_dir $OUTPUT_PATH
&> $OUTPUT_PATH/training.log
peft代码实战,以下代码省略了数据处理
from datasets import load_dataset,load_from_disk
import transformers
from transformers import AutoModelForCausalLM, AutoTokenizer,default_data_collator
from peft import prepare_model_for_int8_training, LoraConfig, get_peft_model
MICRO_BATCH_SIZE = 1
BATCH_SIZE = 1
GRADIENT_ACCUMULATION_STEPS = BATCH_SIZE // MICRO_BATCH_SIZE
EPOCHS = 3
LEARNING_RATE = 3e-6
CUTOFF_LEN = 256
LORA_R = 16
LORA_ALPHA = 32
LORA_DROPOUT = 0.05
模型加载,并使用int8进行训练
model_path = "facebook/opt-6.7b"
output_dir = "model"
model = AutoModelForCausalLM.from_pretrained(model_path)
tokenizer = AutoTokenizer.from_pretrained(model_path, add_eos_token=True)
model = prepare_model_for_int8_training(model)
config = LoraConfig(
r=LORA_R,
lora_alpha=LORA_ALPHA,
target_modules=None,
lora_dropout=LORA_DROPOUT,
bias="none",
task_type="CAUSAL_LM",
)
model = get_peft_model(model, config)
tokenizer.pad_token_id = 0
data = load_from_disk("data")
训练与保存
trainer = transformers.Trainer(
model=model,
train_dataset=data["train"],
eval_dataset=data["validation"],
args=transformers.TrainingArguments(
per_device_train_batch_size=MICRO_BATCH_SIZE,
per_device_eval_batch_size=MICRO_BATCH_SIZE,
gradient_accumulation_steps=GRADIENT_ACCUMULATION_STEPS,
warmup_steps=1000,
num_train_epochs=EPOCHS,
learning_rate=LEARNING_RATE,
# bf16=True,
fp16=True,
logging_steps=1,
output_dir=output_dir,
save_total_limit=4,
),
data_collator=default_data_collator,
)
model.config.use_cache = False
trainer.train(resume_from_checkpoint=False)
model.save_pretrained(output_dir)
直接运行依然会OOM
编写accelerate配置文件accelerate.yaml
compute_environment: LOCAL_MACHINE
deepspeed_config:
gradient_accumulation_steps: 1
gradient_clipping: 1.0
offload_optimizer_device: none
offload_param_device: none
zero3_init_flag: true
zero3_save_16bit_model: true
zero_stage: 3
distributed_type: DEEPSPEED
downcast_bf16: ‘yes’
dynamo_backend: ‘yes’
fsdp_config: {}
machine_rank: 0
main_training_function: main
megatron_lm_config: {}
mixed_precision: fp16
num_machines: 1
num_processes: 2
rdzv_backend: static
same_network: true
use_cpu: true
deepspeed配置文件:ds.json
{
“fp16”: {
“enabled”: true,
“loss_scale”: 0,
“loss_scale_window”: 500,
“initial_scale_power”: 16,
“hysteresis”: 2,
“min_loss_scale”: 1
},
"optimizer": {
"type": "AdamW",
"params": {
"lr": "auto",
"betas": "auto",
"eps": 1e-8,
"weight_decay": "auto"
}
},
"scheduler": {
"type": "WarmupLR",
"params": {
"warmup_min_lr": 0,
"warmup_max_lr": 2e-05,
"warmup_num_steps": 0
}
},
"zero_optimization": {
"stage": 2,
"offload_optimizer": {
"device": "cpu",
"pin_memory": false
},
"allgather_partitions": true,
"allgather_bucket_size": 2e8,
"overlap_comm": true,
"reduce_scatter": true,
"reduce_bucket_size": 2e8,
"contiguous_gradients": true
},
"gradient_accumulation_steps":2,
"gradient_clipping": "auto",
"steps_per_print": 2000,
"train_batch_size": 4,
"train_micro_batch_size_per_gpu": 1,
"wall_clock_breakdown": false
}
启动
accelerate launch --dynamo_backend=nvfuser --config_file accelearte.yaml finetune.py
注:其他方法与Lora使用方法差距不大,不再赘述,在peft项目中均有代码样例。
(1) Fine-Tuning(标准微调)
优点:
简单易用:直接在预训练模型上进行微调。
适应性强:可以针对特定任务调整整个模型的参数。
效果显著:通常能显著提高模型在特定任务上的表现。
缺点:
计算成本高:需要调整模型的大量参数。
数据需求较高:为了有效微调,通常需要较多的标注数据。
适用场景:当有足够的标注数据和计算资源时,适用于大多数NLP任务。
(2)Parameter-Efficient Fine-Tuning(PEFT)
优点:
参数高效:只修改或优化模型的一小部分参数。
节省计算资源:比完全微调需要的资源少。
缺点:
可能效果有限:对于某些复杂任务,仅优化少量参数可能不足以达到最佳效果。
适用场景:资源受限的情况,或者需要快速适应新任务时。
(3) Prompt-Tuning
优点:
无需改变模型架构:通过设计任务相关的提示(prompt),引导模型生成所需的输出。
资源消耗少:不需要改变模型参数。
缺点:
需要精心设计prompt:有效的prompt设计可能需要丰富的经验和实验。
适用场景:快速适应新任务,尤其适用于资源有限的场景。
(4) LoRA(Low-Rank Adaptation)
优点:
参数高效:通过引入低秩矩阵来调整模型,减少需要优化的参数数量。
节省内存和计算资源。
缺点:
效果可能有限:对于某些复杂任务可能无法达到完全微调的效果。
需要一定的技术知识来实现和调试。
适用场景:需要参数高效调整的场景,特别是在资源有限的情况下。
(5) P-Tuning
优点:
可解释性强:通过可训练的prompt向量进行微调。
资源消耗相对较少。
缺点:
需要适当的调整和实验来找到最佳配置。
可能不适用于所有类型的任务。
适用场景:适用于需要提高模型解释性的任务,以及资源有限的情况。
更多推荐
所有评论(0)