语言大模型是通过大规模数据集训练而来可以帮我们进行文本生成、内容总结,但对于一些小众知识、内部数据模型不一定知道怎么回答,这时候可能会胡言乱语。目前要想在特定领域小众知识或私密数据时模型能够表现出比较好的水平目前主要有两种方式可以实现:模型微调、外挂知识库也就是RAG模式,RAG模式之前我们有介绍过,本篇文章主要是介绍模型微调。
模型微调训练是将知识库(私有知识)的内容整理成训练数据集,将这些整理好的数据集来训练大语言模型,最终让模型“学会”该知识库的内容,至于效果如何很大程度取决于该数据集的质量和训练的调参以及基础模型的质量,这种方式流程较复杂、算力要求高相对比外挂知识库RAG方式门槛更高;
目前大模型是通过参数规模、训练数据规模、算力规模堆积起来的语言大模型小的有几亿参数量大的达到几千亿参数量乃至万亿参数量,一般公司也只能运行推理百亿规模模型,从0训练一个大模型无论是算力或是数据都不具备这样的能力,模型微调所需要的技术、算力资源就要少得多。下图为LLaMA-Factory官方估计的大模型微调所需要显存资源情况。
微调概念
大模型训练完成之后超参数已经固定下来了,大模型在特定领域知识上可能表现不佳这时候想要大模型在该领域知识范围达到比较好的水平,可以通过在预训练大模型的基础上使用该特定领域知识再次训练模型已达到调整超参数使得大模型读懂该领域知识的目的。
微调目的: 调整超参数使大模型在特定领域知识内达到比较好的水平;
微调类型: 全量微调(Full Fine-tuning)、参数高效微调(Parameter-Efficient Fine-Tuning, PEFT)
全量微调: 通过预训练模型的所有参数进行训练调整适配特定领域知识,但需要有足够规模的计算资源。
参数高效微调(PEFT): 最小化微调参数数量,只需要微调更新模型的部分参数调整所依赖的计算资源比全量微调小得多。PEFT主要算法包括Prefix Tuning、Prompt Tuning 、Adapter Tuning 、IA3 (Intrinsic Adapter 3) 、LORA (Low-Rank Adaptation) 、Q-LoRA(Quantized Low-Rank Adapter) 和DoRA(Weight-Decomposed Low-Rank Adaptation 、GaLore(Gradient Low-Rank Projection) 、BAdam 等方式。
Adapter Tuning: 这是一种通过在模型的层之间插入小型的神经网络模块(称为adapters)来实现微调的技术。这些adapters包含可训练的权重,而模型的原始参数保持不变。Adapter Tuning通过学习降维后的特征,有效地减少了参数数量,同时使用skip-connection技术,即使在最差情况下,Adapter层也可以退化为identity,从而保持模型的稳定性。
Prompt Tuning: 这是一种通过在输入序列前添加额外的Token来适配下游任务的方法。这些额外的Token是可训练的,而预训练语言模型的参数保持不变。Prompt Tuning通过构建或选择适当的提示(Prompt),引导模型生成符合期望的输出。
IA3 (Intrinsic Adapter 3): IA3是一种新的高效微调方法,它在冻结大模型参数的情况下,通过在输入时给定一些样本包含数据和标签,同时给一个待预测数据,由模型输出这条数据的预测值。这个过程中模型的参数不发生变化。IA3提出了一个新的高效微调方法,基于T0模型提出了T-Few,在下游任务中不需要对任务进行额外模型调整,即可进行少样本学习。IA3 将模型的激活(自我注意和编码器-解码器注意力块中的键和值,以及位置前馈网络的中间激活)乘以三个学习向量,可训练参数数量比 LoRA 还要少。
LORA (Low-Rank Adaptation): LORA通过对基座模型参数中的每个矩阵W增加两个低秩矩阵A和B,其中A是 n * r 的,B是 r * n 的。新模型的参数为W_new = W + A * B,冻结固定W,只调整A和B。LORA通常只调整Attention中的矩阵,不调整FFN。预训练模型的原始权重矩阵被冻结,在训练期间只有较小的矩阵会更新减少训练参数量,同时保持模型性能。
微调工具
比较常见的微调框架工具有LLaMA-Factory、PEFT等,LLaMA-Factory提供了全流程微调的解决方案包括全量微调、高效微调等还提供了基础数据集的支持,微调脚本与Web界面。而PEFT只是一个高效微调框架可以很方便与Transformers等集成使用。
PEFT微调
这里使用PEFT框架对Qwen2.5-0.5B模型进行LORA微调,看其在特定领域知识内是否会有明显改善。LoRA(Low-Rank Adaptation)的核心思想是通过在模型的特定层或模块中插入低秩矩阵来近似全参数微调的效果。
对参数矩阵W增加两个低秩矩阵A和B,其中A是 n * r 的,B是 r * n 的,这里已Qweb2.5-0.5B为例介绍LoRA微调的总体实现,原模型结构如下:
Qwen2ForCausalLM((model): Qwen2Model((embed_tokens): Embedding(151936, 896)(layers): ModuleList((0-23): 24 x Qwen2DecoderLayer((self_attn): Qwen2SdpaAttention((q_proj): Linear(in_features=896, out_features=896, bias=True)(k_proj): Linear(in_features=896, out_features=128, bias=True)(v_proj): Linear(in_features=896, out_features=128, bias=True)(o_proj): Linear(in_features=896, out_features=896, bias=False)(rotary_emb): Qwen2RotaryEmbedding())(mlp): Qwen2MLP((gate_proj): Linear(in_features=896, out_features=4864, bias=False)(up_proj): Linear(in_features=896, out_features=4864, bias=False)(down_proj): Linear(in_features=4864, out_features=896, bias=False)(act_fn): SiLU())(input_layernorm): Qwen2RMSNorm((896,), eps=1e-06)(post_attention_layernorm): Qwen2RMSNorm((896,), eps=1e-06)))(norm): Qwen2RMSNorm((896,), eps=1e-06)(rotary_emb): Qwen2RotaryEmbedding())(lm_head): Linear(in_features=896, out_features=151936, bias=False))
在加载基座模型后可使用LoraConfig配置具体需要进行LoRA微调的参数如:target_modules、秩、alpha、dropout等值。
target_modules: 需要进行微调的模块,可选值有q_proj、k_proj、v_proj、o_proj、gate_proj等。
r: 秩,直接影响了低秩矩阵的大小,决定了微调的参数量
alpha: 缩放因子,用于调节低秩矩阵对模型权重的贡献程度,多大可能会过拟合。
dropout: 值在0和1之间,随机丢弃某些权重的概率。
model = AutoModelForCausalLM.from_pretrained(model_name,low_cpu_mem_usage=True)config = LoraConfig(r=8, lora_alpha=128, lora_dropout=0.0,target_modules=["q_proj", "k_proj", "v_proj"] #, "o_proj", "gate_proj", "up_proj", "down_proj"])model = get_peft_model(model, config)
知道了秩r的值、需要微调的模块以及该模块的输入输出,即可计算出微调的过程中训练的参数量是多少,LoRA核心思想是通过在模型的特定层或模块中插入低秩矩阵来近似全参数微调的效果。对于每个目标模块,LoRA增加的参数量计算公式为:
参数量 =输入维度 * r + r * 输出维度= r × ( 输入维度 + 输出维度 ) 。
以上微调LoraConfig配置为例,通过该配置微调Qwen2.5-0.5B所增加的参数量计算结果如下:
r = 8 q = 896 * 896 k = 896 * 128 v = 896 * 128 有24个解码层q =24*(896*8+8*896) k =24*(896*8+8*128)v =24*(896*8+8*128)
带入上面公式可得到q、k、v每个模块的参数量,q+k+v所得的就是当前模型微调的总参数量也是可训练的总参数量:737280;
通过AutoModelForCausalLM模型print_trainable_parameters方法输出可知Qwen2.5-0.5B总参数量为:4亿9千万,通过LORA微调实际可微调的参数为439万占比为0.88%,冻结了99%的参数,可通过下面方法验证计算结果是否正确;
#打印模型可训练参数信息model.print_trainable_parameters()trainable params: 737,280 || all params: 494,770,048 || trainable%: 0.1490
all params 为模型总参数量,trainable params为可训练参数量,trainable可训练参数比为0.1490%,all params为原来模型参数量+本次微调新增参数量。
本篇文章只介绍了微调的基本概念,后续将介绍微调的具体流程与代码实现。
参考资料:
peft