一、引言
这里的Transformers指的是huggingface开发的大模型库,为huggingface上数以万计的预训练大模型提供预测、训练等服务。
🤗 Transformers 提供了数以千计的预训练模型,支持 100 多种语言的文本分类、信息抽取、问答、摘要、翻译、文本生成。它的宗旨是让最先进的 NLP 技术人人易用。
🤗 Transformers 提供了便于快速下载和使用的API,让你可以把预训练模型用在给定文本、在你的数据集上微调然后通过 model hub 与社区共享。同时,每个定义的 Python 模块均完全独立,方便修改和快速研究实验。
🤗 Transformers 支持三个最热门的深度学习库: Jax, PyTorch 以及 TensorFlow — 并与之无缝整合。你可以直接使用一个框架训练你的模型然后用另一个加载和推理。
本文重点介绍如何打印微调参数,以及微调参数占比计算。
二、计算微调参数占比
2.1 概述
基于LoRA进行模型微调时,需要先冻结全部参数,再指定相应的Linear层进行微调,那么如何计算全部参数,如何计算微调参数以及如何计算微调参数占全部参数的比例呢?
2.2 模型参数结构一览
这里以Qwen2为例,在微调前,对大模型结构有所认知,对于QLoRA量化微调算法来说,只微调大模型的线性层(Linear层),后面会看到在LoRAConfig中,仅指定了"q_proj"、"k_proj"等线性层,这个很重要,微调哪些参数,心中要有数
Qwen2ForCausalLM( (model): Qwen2Model( (embed_tokens): Embedding(152064, 3584) (layers): ModuleList( (0-27): 28 x Qwen2DecoderLayer( (self_attn): Qwen2SdpaAttention( (q_proj): Linear4bit(in_features=3584, out_features=3584, bias=True) (k_proj): Linear4bit(in_features=3584, out_features=512, bias=True) (v_proj): Linear4bit(in_features=3584, out_features=512, bias=True) (o_proj): Linear4bit(in_features=3584, out_features=3584, bias=False) (rotary_emb): Qwen2RotaryEmbedding() ) (mlp): Qwen2MLP( (gate_proj): Linear4bit(in_features=3584, out_features=18944, bias=False) (up_proj): Linear4bit(in_features=3584, out_features=18944, bias=False) (down_proj): Linear4bit(in_features=18944, out_features=3584, bias=False) (act_fn): SiLU() ) (input_layernorm): Qwen2RMSNorm() (post_attention_layernorm): Qwen2RMSNorm() ) ) (norm): Qwen2RMSNorm() ) (lm_head): Linear(in_features=3584, out_features=152064, bias=False) )
2.3 微调参数占比计算
我们采用代码中的
print_trainable_parameters计算全部参数、微调参数、微调参数占比,在这之前:
- 首先,用第一个循环代码for param in model.parameters():将所有参数冻结(freeze),
- 其次,通过get_peft_model和LoraConfig指定计划微调的Linear层
- 最后,采用print_trainable_parameters计算param.requires_grad=True可梯度更新的参数量、总参数量和占比
for param in model.parameters(): param.requires_grad = False # freeze the model - train adapters later if param.ndim == 1: # cast the small parameters (e.g. layernorm) to fp32 for stability param.data = param.data.to(torch.float32) class CastOutputToFloat(nn.Sequential): def forward(self, x): return super().forward(x).to(torch.float32) model.lm_head = CastOutputToFloat(model.lm_head) def print_trainable_parameters(model): """ Prints the number of trainable parameters in the model. """ trainable_params = 0 all_param = 0 for _, 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} || trainable%: {100 * trainable_params / all_param}" ) config = LoraConfig( r=64, lora_alpha=16, target_modules=["q_proj", "v_proj", "v_proj", "o_proj", "gate_proj", "up_proj","down_proj"], lora_dropout=0.05, bias="none", task_type="CAUSAL_LM", ) model = get_peft_model(model, config) print_trainable_parameters(model)
- 遍历模型参数: 使用
for _, param in model.named_parameters():
循环遍历模型中的所有参数。named_parameters()
返回一个迭代器,每个元素包含参数的名称和参数本身,这里下划线_
表示我们忽略了参数名称,直接使用参数值。- 计算参数数量: 对于每个参数,通过
param.numel()
计算其元素数量(即参数的大小),并累加到all_params
以得到模型的总参数数。如果参数param.requires_grad
为True,说明该参数在训练时是可更新的,于是将其大小累加到trainable_params
。
三、总结
本文先对Qwen2模型结构进行一览,做到心中有数,之后讲解如何编写print_trainable_parameters(model)方法,如何冻结所有参数,如何指定需要微调的参数以及计算占比,后续会详细讲LoRA微调方法,这里对计算微调参数占比方式进行记录。