admin 发布的文章

一般分三大类

  • Fine-Tuning:全量微调
  • PEFT:高效微调(SOTA PEFT),部分参数的微调方法
  • RLHF:基于人工反馈机制的强化方法

一、大模型微调需要的参数量

当模型的参数量大小为 B ,模型所占用的硬盘空间为 的 2 倍, 推理所需的显存一般是 ** 的 2 倍。对于全参数微调所需显存,目前来说普遍的说法是约为推理所需显存的 4 倍(包括模型推理(1倍)、梯度(1倍)、优化器状态(AdamW 2倍,SGD 1倍))

例如:当模型的参数量大小为 7B ,模型所占用的硬盘空间为14G,推理所需的显存一般是14G多15G,对于全参数微调所需显存,目前来说普遍的说法是约为推理所需显存的 4 倍,也就是60G左右。

一、 Fine-Tuning:全参微调

定义:全微调是指对整个预训练模型进行微调,包括所有的模型参数。在这种方法中,预训练模型的所有层和参数都会被更新和优化,以适应目标任务的需求。
适用范围:这种微调方法通常适用于任务和预训练模型之间存在较大差异的情况,或者任务需要模型具有高度灵活性和自适应能力的情况。Full Fine-tuning需要较大的计算资源和时间,但可以获得更好的性能。

1.Fine tuning

经典的Fine tuning方法包括将预训练模型base-model与少量特定任务数据一起继续训练。在这个过程中,预训练模型的权重被更新,以更好地适应任务。所需的Fine-tuning量取决于预训练语料库和任务特定语料库之间的相似性。如果两者相似,可能只需要少量的Fine tuning。如果两者不相似,则可能需要更多的Fine tuning。

二、PEFT、高效微调(SOTA PEFT):部分参数的微调方法

微信截图_20240429141957.png

微调顶层:只微调预训练模型的顶层,以适应新的任务。
逐层微调:从底层开始,逐层微调预训练模型,直到所有层都被微调。
冻结底层:将预训练模型的底层固定不变,只对顶层进行微调。

Lora(在旁边添加训练参数)(Low-Rank Adaptation):由微软在2021年提出,通过低秩矩阵分解来实现高效的参数调整。
P-tuning:( 2021年3月) 清华大学提出的可自动学习模版的P-tuning,这种方法通过引入连续提示(continuous prompts)的方式替代传统的离散文本提示(discrete text prompt),以更少的可训练参数量来适应特定下游任务。

在P-Tuning中,通常会为输入序列添加一个或多个可学习的向量(虚拟token),这些向量作为额外的“prompt”嵌入到模型的输入序列中,用于指导模型生成与特定任务相关的输出。相比于直接对整个模型进行微调,P-Tuning仅需要调整这部分新增的prompt参数,从而显著减少所需的计算资源和时间。

P-tuning2:(在每一层前面加入训练参数 2021年10月)基于Prefix Tuning,增加了多个Embedding层,连续的Prompts,在预训练的每一层都增加了prompts,适合GLM模型的微调

Adapter(在前面添加训练参数)Adapter Tuning:由Houlsby N等人在2019年提出,通过增加模型层数来引入额外的灵活性,但这也导致了额外的推理延迟
Prefix-tuning(在中间添加训练参数)由斯坦福大学提出,在原有模型基础上,增加一个可被训练的Embedding层,用于给提示词增加前缀,从而更好的让模型理解提示词的意图,通过修改输入的前缀来调整模型的行为,但这种方法难以训练,并且可能影响模型性能。
Prompt tuning:由谷歌提出,类似于Prefix-Tuning,通过修改输入提示来调整模型行为。

QLora

在这些方法中,LORA因其优越的性能和较低的成本而备受推崇。

1、LoRA

LoRA是一种用于微调大型预训练语言模型(如GPT-3或BERT)的方法。它的核心思想是在模型的关键层中添加小型、低秩的矩阵来调整模型的行为,而不是直接改变整个模型的结构。

这样做的好处是,可以在不增加太多额外计算负担的情况下,有效调整模型,同时保持其原有的性能。

LoRA的工作原理如下:

数学原理:原参数矩阵,加上一个小的、简单的低秩矩阵(B*A升维之后,+W),来生成新的参数矩阵。
在 Transformer 的 multi-head attention 中使用 LoRA.

选择要调整的权重矩阵:在大型模型(如GPT)中,我们首先确定要微调的权重矩阵。通常,这些矩阵位于模型的多头自注意力(Multi-head Self-Attention)和前馈神经网络(Feed-Forward Neural Network)部分。

引入两个低秩矩阵:接着,我们引入两个低秩矩阵,记为A和B,这两个矩阵的维度比原始权重矩阵小得多,例如,如果原始矩阵的尺寸是dd,那么,A和B的尺寸可能是dr和r*d,其中r是一个远小于d的数。

计算低秩更新:通过计算这两个低秩矩阵的乘积,生成一个新的矩阵AB,这个新矩阵的秩(即r)远小于原始权重矩阵的秩。这个乘积实际上是一个低秩近似,可以视为对原始权重矩阵的一种调整。

结合原始权重:最后,这个新生成的低秩矩阵AB被加到原始的权重矩阵上。这样,原始的权重矩阵得到了微调,但大部分权重保持不变。这个过程可以用数学公式表示为:新权重 = 原始权重 + AB。

1、P-tuning系列

P-tuning V1

英文名称: GPT Understands, Too
中文名称: GPT也懂
链接: https://arxiv.org/abs/2103.10385
作者: Xiao Liu, Yanan Zheng, Zhengxiao Du, Ming Ding, Yujie Qian, Zhilin Yang, Jie Tang
机构: 清华大学, 麻省理工学院
日期: 2021-03-18

目标:大模型的 Prompt 构造方式严重影响下游任务的效果。离散化的 token 的搜索出来的结果可能并不是最优的,导致性能不稳定。本篇论文旨在探讨,如何提升预训练语言模型进行自然语言提示的有效性。

方法:作者提出了 P-Tuning,设计了一种连续可微的 virtual token(同 Prefix-Tuning 类似)。将 Prompt 转换为可以学习的 Embedding 层,用 MLP+LSTM 的方式来对 Prompt Embedding 进行处理。
微信截图_20240429140514.png

结论:弥合 GPT 和 NLU 应用程序之间的差距 (2021 年),P 调参后的 GPT 可以比在 NLU 调参的类似大小的 BERT 效果更好。
0240426110655.png
微信截图_20240429140632.png
主图:一个关于“英国的首都是 [MASK]”的提示搜索的例子。在蓝色区域表示上下文(“英国”),红色区域表示目标(“[MASK]”),橙色区域表示提示。在(a)中,提示生成器只接收离散的奖励;在(b)中,连续的提示嵌入和提示编码器可以通过可微的方式进行优化。
参考:https://kexue.fm/search/Prompt+Tuning/

P-tuning V2

英文名称: P-Tuning v2: Prompt Tuning Can Be Comparable to Fine-tuning Universally Across Scales and Tasks
中文名称: P-Tuning v2:提示调整可以在各种规模和任务上普遍与微调相媲美
链接: http://arxiv.org/abs/2110.07602v3
作者: Xiao Liu, Kaixuan Ji, Yicheng Fu, Weng Lam Tam, Zhengxiao Du, Zhilin Yang, Jie Tang
机构: 清华大学, 北京人工智能学会, 上海启智研究院
日期: 2021-10-14

目标:研究的目的是探索如何通过优化提示调整方法,在各种模型规模和自然语言理解任务中实现普遍有效的目标。

方法:最显著的改进是在预训练模型的每一层应用连续提示,而不仅仅是输入层。深度提示调整增加了连续提示的容量,并缩小了不同设置下微调差距的范围,尤其适用于小型模型和复杂任务。

结论:研究发现,经过适当优化的提示调整方法可以在各种模型规模和自然语言理解任务中达到与微调相当的性能,而只需调整 0.1%-3% 的参数(P-tuning 调整 0.01% 参数)。P-Tuning v2 被认为可以作为微调的替代方法,并为未来研究提供了一个强有力的基准。

0426145459.png

5.png
主图:P-tuning 到 P-tuning v2 对比。橙色块(即 h0,…,hi)指的是可训练的提示嵌入;蓝色块是由冻结的预训练语言模型存储或计算的嵌入。

参考:
1.https://zhuanlan.zhihu.com/p/687481429
2.https://www.zhihu.com/tardis/zm/art/627642632?source_id=1003
3.https://zhuanlan.zhihu.com/p/675231376

一、什么是优化器

用于优化模型的参数。当前向计算完成后,根据loss获得参数的梯度后,优化器就是如何利用梯度来更新和计算模型的网络参数,使得在后期计算中能够让模型的损失最小化。优化器通过不断更新模型参数来拟合训练数据,从而使得在新数据上表现良好。

优化器不计算梯度,它只是梯度的更新者,它决定了以什么样的形式更新参数。

二、优化器算法

1、梯度下降法

https://zhuanlan.zhihu.com/p/687987540

一、pycharm中运行/调试torch分布式训练

整体比较简单,可以参考:我下面的二、pycharm中运行/调试deepspeed分布式训练
关键步骤为:
软链接distributed文件
通过对调用分布式的命令分析,我们首先需要找到torch.distributed.launch这个文件,并将它软链接到我们的Pycharm项目目录下。为什么使用软链接而不是直接复制呢?因为软链接不会变更文件的路径,从而使得launch.py文件可以不做任何改动的情况下去import它需要的包。

在Ubuntu中,通过以下命令创建软链接

ln -s /yourpython/lib/python3.6/site-packages/torch/distributed/ /yourprogram/
以上命令没有直接链接launch.py而是它的父目录distributed,是因为这样比较容易知道launch.py是一个软链接,不与项目中的其他文件混淆。

设置Pycharm运行参数
打开Pycharm,依次点击Run->Edit Configurations 进入参数配置界面
微信截图_20240314134956.png

只需要配置Script path为launch.py路径;Parameters为launch.py运行参数,参考命令行调用的方法,设置如下。

--nproc_per_node=4
tools/train.py --cfg xxx.yaml
通过以上步骤就可以在Pycharm中运行分布式训练了。不过,如果是在调试模型最好还是修改一下trian.py文件,通过单GPU方式调试,并不是说分布式模式不能调试,仅仅是因为在单GPU方式下,对于数据流更好把控,减少调试时间

二、pycharm中运行/调试deepspeed分布式训练

1.pycharm版本

我用的是2020.1

2.环境

(1)首先服务器上需要配好对应的环境,并且保留代码一份,以及对应的模型,用conda配好虚拟环境
(2)本地只需要拷贝对应的代码和数据,不用拷贝模型
(3)代码,我主要是复现大语言模型的预训练,https://github.com/hiyouga/LLaMA-Factory,其他代码同理
最主要的启动脚本,其他代码也是同理,主要是使用deepspeed启动就行。
pretarin.sh

deepspeed  --master_port=9901 src/train_bash.py \
    --deepspeed ./ds_config.json \
    --stage pt \
    --do_train \
    --model_name_or_path ../Yi-34B  \
    --dataset input_test \
    --finetuning_type full \
    --lora_target q_proj,v_proj \
    --output_dir Yi-34B_output_test \
    --overwrite_cache \
    --per_device_train_batch_size 4 \
    --gradient_accumulation_steps 4 \
    --lr_scheduler_type cosine \
    --logging_steps 5 \
    --save_steps 300 \
    --learning_rate 5e-5 \
    --num_train_epochs 1.0 \
    --preprocessing_num_workers 20 \
    --plot_loss \
    --bf16

3.本地配置:deepspeed安装

将虚拟环境的deepspeed安装包压缩,拷贝到本地。
我服务器虚拟环境的位置:/home/centos/anaconda3/envs/factory/lib/python3.10/site-packages/deepspeed/,
将deepspeed包压缩为zip包,拷贝到本地项目目录:D:codeLLaMA-Factory 然后解压,D:codeLLaMA-Factorydeepspeed

4.远程配置:软链接

查看:vim /home/centos/anaconda3/envs/factory/bin/deepspeed 可以知道实际使用的hideepspeed.launcher.runner
文件,

通过对调用分布式的命令分析,我们首先需要找到deepspeed.launcher.runner这个文件,并将它软链接到我们的Pycharm项目目录下。为什么使用软链接而不是直接复制呢?因为软链接不会变更文件的路径,从而使得runner.py文件可以不做任何改动的情况下去import它需要的包。

在centos中,通过以下命令创建软链接

ln -s /home/centos/anaconda3/envs/factory/lib/python3.10/site-packages/deepspeed/  /data/liulei/cpt/LLaMA-Factory/

如果要删除软连接用:

unlink  /data/liulei/cpt/LLaMA-Factorydeepspeed/

5.pycharm配置

配置本地代码用远程服务器的python解析器:

(1)从set里面进去
微信截图_.png

(2)add新的解析器
微信截图_20240314111021.png

(3)通过ssh 添加远程的,写Ip和用户名,后面输入密码
11531.png

(4)从远程服务器中选择解析器、远程服务器代码和本地代码进行映射

12024.png

2247.png

(5)配置debug执行入口命令

20240314112930.png

(6)脚本、参数、python解析器、代码路径配置

微信截图_20240314113559.png

入口脚本:D:codeLLaMA-Factorydeepspeedlauncherrunner.py
参数:注意这里面的脚步,就是上面的pretrain.sh启动脚本,但是需要把deepspeed命令去掉,并且把反斜杠去掉,且要把默认参数True 补充完整。


--master_port=9901  src/train_bash.py      --deepspeed ./ds_config.json      --stage pt     --do_train True      --model_name_or_path ../Yi-34B-Chat      --dataset input_test      --finetuning_type full      --lora_target q_proj,v_proj      --output_dir path_to_pt_checkp1      --overwrite_cache True      --per_device_train_batch_size 1      --gradient_accumulation_steps 1      --lr_scheduler_type cosine      --logging_steps 1     --save_steps 100      --learning_rate 5e-5      --num_train_epochs 1.0      --plot_loss True      --fp16

完成就可以进行debug了,然后你就发现在实际运行的时候还是会报错的。需要几个地方的代码修改。

(7)本地对应代码修改

问题:1

ssh://centos@18:22/home/centos/anaconda3/envs/factory/bin/python -u /home/centos/.pycharm_helpers/pydev/pydevd.py --multiproc --qt-support=auto --client 0.0.0.0 --port 34567 --file /data/liulei/cpt/LLaMA-Factory/deepspeed/launcher/runner.py --master_port=9901 src/train_bash.py --deepspeed ./ds_config.json --stage pt --do_train True --model_name_or_path ../Yi-34B-Chat --dataset input_test --finetuning_type full --lora_target q_proj,v_proj --output_dir path_to_pt_checkp1 --overwrite_cache True --per_device_train_batch_size 1 --gradient_accumulation_steps 1 --lr_scheduler_type cosine --logging_steps 1 --save_steps 100 --learning_rate 5e-5 --num_train_epochs 1.0 --plot_loss True --fp16
/home/centos/.pycharm_helpers/pydev/pydevd.py:1806: DeprecationWarning: currentThread() is deprecated, use current_thread() instead
  dummy_thread = threading.currentThread()
pydev debugger: process 232478 is connecting
Connected to pydev debugger (build 201.6668.115)
Traceback (most recent call last):
  File "/home/centos/.pycharm_helpers/pydev/pydevd.py", line 1438, in _exec
    pydev_imports.execfile(file, globals, locals)  # execute the script
  File "/home/centos/.pycharm_helpers/pydev/_pydev_imps/_pydev_execfile.py", line 18, in execfile
    exec(compile(contents+"\n", file, 'exec'), glob, loc)
  File "/data/liulei/cpt/LLaMA-Factory/deepspeed/launcher/runner.py", line 24, in <module>
    from .multinode_runner import PDSHRunner, OpenMPIRunner, MVAPICHRunner, SlurmRunner, MPICHRunner, IMPIRunner
ImportError: attempted relative import with no known parent package

解决办法:

将远程文件/data/liulei/cpt/LLaMA-Factory/deepspeed/launcher/runner.py 映射的本地文件D:\code\LLaMA-Factory\deepspeed\launcher\runner.py进行修改

修改前:

from .multinode_runner import PDSHRunner, OpenMPIRunner, MVAPICHRunner, SlurmRunner, MPICHRunner, IMPIRunner
from .constants import PDSH_LAUNCHER, OPENMPI_LAUNCHER, MVAPICH_LAUNCHER, SLURM_LAUNCHER, MPICH_LAUNCHER, IMPI_LAUNCHER
from ..constants import TORCH_DISTRIBUTED_DEFAULT_PORT
from ..nebula.constants import NEBULA_EXPORT_ENVS
from ..utils import logger

from ..autotuning import Autotuner
from deepspeed.accelerator import get_accelerator


修改后:
from deepspeed.launcher.multinode_runner import PDSHRunner, OpenMPIRunner, MVAPICHRunner, SlurmRunner, MPICHRunner, IMPIRunner
from deepspeed.launcher.constants import PDSH_LAUNCHER, OPENMPI_LAUNCHER, MVAPICH_LAUNCHER, SLURM_LAUNCHER, MPICH_LAUNCHER, IMPI_LAUNCHER
from deepspeed.constants import TORCH_DISTRIBUTED_DEFAULT_PORT
from deepspeed.nebula.constants import NEBULA_EXPORT_ENVS
from deepspeed.utils import logger

from deepspeed.autotuning import Autotuner

然后重新运行debug,就发现本地可以愉快的跑起来了。