为什么需要PEFT微调

简单来讲就是全参微调代价太大

alt text

PEFT几种方式

adapter

原理:

  • 针对每个Transfomer层,增加两个Adapter结构(分别是注意力层和前馈层之后,但在norm之前)。训练时,固定原来预训练模型参数不变,只对新增的Adapter结构和Layer Norm层进行微调

缺点:

  • 增加了模型层数,推理延迟

alt text

prefix tuning

原理:

  • 在输入token之前构造一段virtual tokens作为prefix,训练时只更新prefix部分参数,其他参数不变
  • 在每一层前端都加入virtual tokens
  • 为防止直接更新prefix参数导致训练不稳定和性能下降,在prefix层前面加了MLP结构,训练完成后,只保留Prefix都参数

缺点:

  • 额外token占用,降低有效序列长度
    alt text

prompt tuning

原理:

  • 给每个任务定义各自的prompt,拼接到数据上作为输入,但旨在输入层加入prompt tokens,且不加入MLP(显式加入token)

alt text

p-tuning

动机:

  • prompt tuning中,prompt的构造方式对结果影响较大

原理:

  • 将Prompt转换为可学习的Embedding层,并用MLP+LSTM的方式来对Prompt Embedding进行表征(只在输入层)
  • 与prefix tuning区别:只在输入层加,不一定在前面加,可在任意位置加
  • 视为对prompt tuning对改进

缺点:

  • v1 版本在小模型表现较差,NLU效果较好,序列标注效果较差

alt text

p-tuning v2

原理:

  • 每层都加
  • 移除重参数化编码器
  • 更多可学习参数
  • 不同任务提示长度不同
  • 引入多任务
  • 视为prefix tuning对改进

缺点:

  • 微调后容易导致旧知识的遗忘

alt text

lora

原理:

  • 通过低秩矩阵模拟参数的改变量,从而以极小的参数量来实现大模型的间接训练
  • 在原始的模型旁边增加一个通路,通过两个矩阵A,B相乘,先降纬再升纬。中间层纬度为r,模拟本征?
  • 将两部分参数相加
  • 其他:r一般取1,2,,4,8

alt text

lora变种

  • lora+
    • 给A、B设置不同的学习率
  • lora-drop
    • lora矩阵可添加到神经网络的任何一层,lora-drop可以选择那些层由lora微调,哪些层不需要
  • adalora
    • lora-drop中各层的适配器要么被完全训练,要么完全不被训练。而AdaLora可以决定不同的适配器具有不同的秩。

框架

  • Llamafactory框架 国内开源
  • SWIFT (魔塔)

微调主要参数设置

  • learing_rate:
    根据gpt3 paper,一般来看,模型越大,学习率越小(无严格定义,实验导向)
    alt text

  • lr_scheduler_type 学习率控制器

    • 常数学习率(constant):学习率在整个训练过程中保持不变
    • 带预热的常数学习率(constant_with_warmup):在训练初期有一个预热阶段,学习率逐渐增加到初始学习率,然后保持常数
    • alt text
    • 线性衰减(linear):从初始学习率下降到0
    • alt text
    • 余弦退火(cosine):学习率按照余弦函数变化,在训练过程中逐步减小
    • alt text
    • 余弦退火与重启(cosine_with_restarts):带重启的余弦退火调度器,在训练过程中周期性地重置学习率,然后按余弦函数衰减,每隔一段时间重启余弦曲线
    • alt text
    • 多项式衰减(polynomial):多项式衰减调度器,学习率按照多项式函数下降。
  • warmup_ratio 预热:

原理:

  • 由于模型的权重是随机初始化的,刚开始训练时若学习率较大,可能带来训练不稳定。选择warmup预热学习率的方式,可以使得开始训练的几个epoch或者一些step内学习率较小

计算

  • alt text
  • gradient_accumulation_steps:
    • 梯度累加:每次获取一个batch的数据,计算1次梯度,梯度不清空,累加到预设值之后,再更新网络参数,然后清空梯度,进行下一次循环
  • per_device_train_batch_size:每卡的batch_szie数量
  • sft数据
    • META(lima:less is more for alignment)
    • 质量>>数量
      • 多样性
      • 高质量
      • 一致性
    • 若面临混合调节需求(需要在多种数据集tune,获取不同能力)
      • 跷跷板现象
      • 通用能力下降(灾难性遗忘)
      • DMT(双阶段混合微调)
        • 第一阶段在特定任务数据集进行多任务训练(如代码,数学推理等)
        • 第二阶段使用混合数据进行SFT,包括通过数据和一定比例k的特定任务数据

Llamafactory_FT_Qwen案例

基于Llamaindex微调qwen2.5-7b

1.数据集准备

https://www.cnblogs.com/chentiao/p/17386131.html

2.服务器搭建

由于算力限制选择阿里云服务器

https://free.aliyun.com/?spm=a2c4g.11174283.0.0.46a0527f6LNsEe&productCode=learn

https://help.aliyun.com/document_detail/2329850.html?spm=a2c4g.2261126.0.0.f3be1d2ddJscIy

3.Qwen2.5-7b

!git lfs install 记得下载lfs不然无法下载完整的模型文件

!git clone https://www.modelscope.cn/Qwen/Qwen2.5-7B-Instruct.git

4.Llamafactory

拉取环境

!git clone https://github.com/hiyouga/LLaMA-Factory.git

cd到Llamafactory目录下,加载依赖项
!pip install -e .[torch,metrics]

4.1加载数据

将转换好的.json数据放入Llamafactory中的data文件中,并修改配置文件data_set.json

1
2
3
4
5
6
"train": {
"file_name": "your_data_name.json"
},
"test": {
"file_name": "your_data_name.json"
}

4.2trian

cd到Llamafactory目录下
!llamafactory-cli webui 启动webui界面

alt text
下拉找到训练就可以开始微调训练
alt text


  • 参数配置

    train中的可选参数很多,这里先搁置不进行一一介绍,感兴趣的小伙伴可以自己先进行了解

4.3Evaluate & Predict

alt text

  • 评价指标解释
    • predict_bleu-4 (30.835%):
      BLEU-4分数(BiLingual Evaluation Understudy)BLEU是衡量机器翻译或文本生成模型输出与参考答案相似度的常用指标。bleu-4表示计算了4-gram的匹配率,数值越高表示预测的文本质量越高。

    • predict_model_preparation_time (0.004秒):
      模型在进行预测前的准备时间,通常指模型加载和初始化的时间,单位是秒。数值越小表示准备时间越短。

    • predict_rouge-1 (52.03%):
      ROUGE-1是文本生成评价指标之一,它衡量生成文本与参考文本之间的1-gram重叠程度。rouge-1的数值越高,代表模型预测的词汇与参考答案越接近。

    • predict_rouge-2 (28.15%):
      ROUGE-2同样是ROUGE系列指标之一,衡量2-gram的重叠率。和ROUGE-1一样,数值越高,代表模型输出的文本与目标文本越接近。

    • predict_rouge-l (38.01%):
      ROUGE-L使用最长公共子序列(LCS)来衡量模型生成文本和参考文本之间的相似度。该指标更关注文本的顺序结构,数值越高代表输出文本在顺序上与参考答案更相似。

    • predict_runtime (1255.0994秒):
      这是模型进行预测所花费的总时间,单位为秒。该值通常会随模型复杂度、硬件配置以及数据集大小而变化。

    • predict_samples_per_second (0.17 样本/秒):
      这是模型每秒能够处理的样本数。值越高表示模型的处理效率越高。

    • predict_steps_per_second (0.085 步骤/秒):
      这是模型每秒执行的步骤(steps),每个步骤通常包括一次前向传播和梯度更新。数值越高,表示模型在预测过程中的计算效率越高。

4.4Export

(chat部分就是一个聊天界面,可以在线测试微调后的效果)
Export是将我们微调的模型进行导出,我的理解是(将训练好的lora部分和原本的模型进行合并)

这部分只用设置输出路径就可以进行导出了

5部署

5.1 Ollama部署

在服务器上部署

首先拉取ollma文件

modelscope download --model=modelscope/ollama-linux --local_dir ./ollama-linux --revision v0.3.12

新建创建 Modelfile 文件,写入

FROM ./your_model_path

  • 在ollama文件中创建模型

ollama create mymodel -f Modelfile

  • Llama.cpp

    如果在ollama创建模型文件的时候遇见 Models based on 'Qwen2ForCausalLM' are not yet supported 的问题,可使用llama.cpp,导出gguf格式的文件再进行部署推理

    首先拉取llama.cpp文件

    !git clone https://github.com/ggerganov/llama.cpp.git

    1
    2
    3
    4
    5
    6
    7
    8
    # 需要安装相关的库
    cd llama.cpp
    pip install -r requirements.txt
    # 验证环境
    python convert_hf_to_gguf.py -h

    # 使用脚本进行模型转换,可以选择量化方式
    python convert_hf_to_gguf.py ../yourmodelpath --outfile out_file_name.gguf --outtype f16

得到.gguf文件
然后重新使用ollama create mymodel -f Modelfile创建模型

得到.gguf文件
然后重新使用ollama create mymodel -f Modelfile创建模型,Modelfile是一个指向文件,需要指到gguf文件的路径,还可以在里面配置一个模型参数。

解决方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#在modelfile文件中对大模型参数进行配置

FROM ./llama.cpp/qwen_7b_law.gguf

# set the temperature to 1 [higher is more creative, lower is more coherent]
PARAMETER temperature 0.7
PARAMETER top_p 0.8
PARAMETER repeat_penalty 1.05
PARAMETER top_k 20

TEMPLATE """{{ if .Messages }}
{{- if or .System .Tools }}<|im_start|>system
{{ .System }}
{{- if .Tools }}

# Tools

You are provided with function signatures within <tools></tools> XML tags:
<tools>{{- range .Tools }}
{"type": "function", "function": {{ .Function }}}{{- end }}
</tools>

For each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:
<tool_call>
{"name": <function-name>, "arguments": <args-json-object>}
</tool_call>
{{- end }}<|im_end|>
{{ end }}
{{- range $i, $_ := .Messages }}
{{- $last := eq (len (slice $.Messages $i)) 1 -}}
{{- if eq .Role "user" }}<|im_start|>user
{{ .Content }}<|im_end|>
{{ else if eq .Role "assistant" }}<|im_start|>assistant
{{ if .Content }}{{ .Content }}
{{- else if .ToolCalls }}<tool_call>
{{ range .ToolCalls }}{"name": "{{ .Function.Name }}", "arguments": {{ .Function.Arguments }}}
{{ end }}</tool_call>
{{- end }}{{ if not $last }}<|im_end|>
{{ end }}
{{- else if eq .Role "tool" }}<|im_start|>user
<tool_response>
{{ .Content }}
</tool_response><|im_end|>
{{ end }}
{{- if and (ne .Role "assistant") $last }}<|im_start|>assistant
{{ end }}
{{- end }}
{{- else }}
{{- if .System }}<|im_start|>system
{{ .System }}<|im_end|>
{{ end }}{{ if .Prompt }}<|im_start|>user
{{ .Prompt }}<|im_end|>
{{ end }}<|im_start|>assistant
{{ end }}{{ .Response }}{{ if .Response }}<|im_end|>{{ end }}"""

# set the system message
SYSTEM """You are Qwen, created by Alibaba Cloud. You are a helpful assistant."""
  • 最后使用ollama推理
    ollama run mymodel

ollama常用指令:

1
2
3
4
5
6
7
8
9
10
ollama serve # 启动ollama
ollama create # 从模型文件创建模型
ollama show # 显示模型信息
ollama run # 运行模型,会先自动下载模型
ollama pull # 从注册仓库中拉取模型
ollama push # 将模型推送到注册仓库
ollama list # 列出已下载模型
ollama ps # 列出正在运行的模型
ollama cp # 复制模型
ollama rm # 删除模型