本文为稀土掘金技术社区首发签约文章,30天内禁止转载,30天后未获授权禁止转载,侵权必究!
随着 ChatGPT 的现象级走红,引领了AI大模型时代的变革,从而导致 AI 算力日益紧缺。与此同时,中美贸易战以及美国对华进行AI芯片相关的制裁导致 AI 算力的国产化适配势在必行。之前讲述了基于昇腾910使用ChatGLM-6B进行模型推理,本文将讲述针对ChatGLM-6B大模型在昇腾910加速卡上面进行模型训练,为了文章具有更好的阅读体验,具体代码放置在GitHub:llm-action。
环境搭建
- 操作系统版本/架构:EulerOS release 2.0 (SP8)/aarch64
- NPU:8卡 910 ProB 32G
- Python:3.7
- NPU 驱动:23.0.rc1,下载
- NPU 固件:6.3.0.1.241,下载
- CANN 工具包:6.3.RC1.alpha003,下载
- MindSpore:2.0.0-rc1,下载
- MindFormers:dev ( commit id:
6f2b0b8
)
运行环境与基于昇腾910使用ChatGLM-6B进行模型推理一致,这里就不再赘述了。
数据准备及数据格式转换
下面以 ADGEN (广告生成) 数据集为例来介绍微调的具体使用。
ADGEN 数据集为根据输入(content)生成一段广告词(summary),具体格式如下所示:
{
"content": "类型#上衣*版型#宽松*版型#显瘦*图案#线条*衣样式#衬衫*衣袖型#泡泡袖*衣款式#抽绳",
"summary": "这件衬衫的款式非常的宽松,利落的线条可以很好的隐藏身材上的小缺点,穿在身上有着很好的显瘦效果。领口装饰了一个可爱的抽绳,漂亮的绳结展现出了十足的个性,配合时尚的泡泡袖型,尽显女性甜美可爱的气息。"
}
请从官网下载 ADGEN 数据集或通过此链接下载,并将其解压到 AdvertiseGen
目录。
tar -zxvf AdvertiseGen.tar.gz
查看数据集大小:
> wc -l AdvertiseGen/*
> 1070 AdvertiseGen/dev.json
> 114599 AdvertiseGen/train.json
> 115669 total
使用 mindformers/tools/dataset_preprocess/glm/adgen_dataset.py
脚本将数据集处理成mindrecord格式。
执行命令生成训练数据集:
cd mindformers/tools/dataset_preprocess/glm
python adgen_dataset.py
--input_file /root/workspace/data/AdvertiseGen/train.json
--vocab_file /root/workspace/model/checkpoint_download/glm/ice_text.model
--output_file /root/workspace/data/AdvertiseGen-ms/train_0604_128.mindrecord
--max_source_length 64
--max_target_length 64
--mode train
运行过程:
100%|##########################################################################| 114599/114599 [11:25<00:00, 167.17it/s]
2023-06-20 14:26:59,086 - mindformers - INFO - Wrote 114599 total instances
执行命令生成评估数据集:
cd mindformers/tools/dataset_preprocess/glm
python adgen_dataset.py
--input_file /root/workspace/data/AdvertiseGen/dev.json
--vocab_file /root/workspace/model/checkpoint_download/glm/ice_text.model
--output_file /root/workspace/data/AdvertiseGen-ms/eval_0604_256.mindrecord
--max_source_length 256
--max_target_length 256
--mode eval
运行过程:
100%|##############################################################################| 1070/1070 [00:01<00:00, 772.11it/s]
2023-06-20 14:17:20,492 - mindformers - INFO - Wrote 1070 total instances
转换后的文件如下所示:
> tree AdvertiseGen-ms/
AdvertiseGen-ms/
├── eval_0711_256.mindrecord
├── eval_0711_256.mindrecord.db
├── train_0604_128.mindrecord
└── train_0604_128.mindrecord.db
其中,*.mindrecord
为数据文件,*.mindrecord.db
为索引文件。
补充说明:
一个MindRecord文件由数据文件和索引文件组成:
- 数据文件:包含文件头、标量数据页、块数据页,用于存储用户归一化后的训练数据,且单个MindRecord文件建议小于20G,用户可将大数据集进行分片存储为多个MindRecord文件。
- 索引文件:包含基于标量数据(如:图像Label、图像文件名等)生成的索引信息,用于方便的检索、统计数据集信息。
数据文件主要由以下几个关键部分组成:
- 文件头:文件头主要用来存储文件头大小、标量数据页大小、块数据页大小、Schema信息、索引字段、统计信息、文件分区信息、标量数据与块数据对应关系等,是MindRecord文件的元信息。
- 标量数据页:标量数据页主要用来存储整型、字符串、浮点型数据,如:图像的Label、图像的文件名、图像的长宽等信息,即适合用标量来存储的信息会保存在这里。
- 块数据页:块数据页主要用来存储二进制串、Numpy数组等数据,如:二进制图像文件本身、文本转换成的字典等。
模型准备及模型格式转换
模型准备及模型格式转换请参考基于昇腾910使用ChatGLM-6B进行模型推理,这里也不再赘述。
代码准备
模型训练的代码直接使用安装mindformers的代码即可,由于版本迭代很快,为了避免代码不兼容的情况,建议保持同样的commit id。
git clone https://gitee.com/mindspore/mindformers.git
cd mindformers/
git checkout 6f2b0b8
网络配置
当进行分布式训练时,需要通过昇腾软件中的HCCN Tool工具配置device的网卡IP,用于多个device间通信以实现网络模型参数的同步更新。
对于配置的要求如下:
- AI Server中的第0/4,1/5,2/6,3/7号网卡需处于同一网段,第0/1/2/3号网卡在不同网段,第4/5/6/7号网卡在不同网段。
- 对于集群场景,各AI Server对应的位置的device需处于同一网段,例如:AI Server1和AI Server2的0号网卡需处于同一网段,AI Server1 和 AI Server2 的1号网卡需处于同一网段。IP地址需要根据实际情况修改。
先检查/etc/下是否有hccn.conf配置文件,若不存在,需先执行如下命令进行设置:
hccn_tool -i 0 -ip -s address 192.168.100.101 netmask 255.255.255.0
hccn_tool -i 1 -ip -s address 192.168.101.101 netmask 255.255.255.0
hccn_tool -i 2 -ip -s address 192.168.102.101 netmask 255.255.255.0
hccn_tool -i 3 -ip -s address 192.168.103.101 netmask 255.255.255.0
hccn_tool -i 4 -ip -s address 192.168.100.100 netmask 255.255.255.0
hccn_tool -i 5 -ip -s address 192.168.101.100 netmask 255.255.255.0
hccn_tool -i 6 -ip -s address 192.168.102.100 netmask 255.255.255.0
hccn_tool -i 7 -ip -s address 192.168.103.100 netmask 255.255.255.0
参数说明:
-i 0
:指定设备ID。-netdetect
:指定网络检测对象IP属性。-s address
:表示设置属性为IP地址。192.168.100.101
表示服务器的设备0的ip地址。
查看/etc/hccn.conf
文件:
> cat /etc/hccn.conf
address_0=192.168.100.101
netmask_0=255.255.255.0
...
address_7=192.168.103.100
netmask_7=255.255.255.0
软件、模型、数据、代码以及网络环境准备好之后,接下来进行模型训练。
模型训练
下面分别对模型全量微调以及LoRA微调进行讲解。
全量微调
首先,生成用于Ascend芯片分布式通信的芯片资源信息配置文件(RANK_TABLE_FILE)。Ascend HCCL 的 RANK_TABLE_FILE 文件提供Ascend分布式训练作业的集群信息。下面生成8卡的rank_table_file文件。
> python ./mindformers/tools/hccl_tools.py --device_num "[0,8)"
start ./mindformers/tools/hccl_tools.py
visible_devices:['0', '1', '2', '3', '4', '5', '6', '7']
server_id:192.168.1.196
device_num_list: [0, 1, 2, 3, 4, 5, 6, 7]
rank_id:0, device_id:0, device_ip:192.168.100.101
...
rank_id:7, device_id:7, device_ip:192.168.103.100
Completed: hccl file was save in : /root/workspace/code/mindformers/hccl_8p_01234567_192.168.1.196.json
修改配置文件(configs/glm/run_glm_6b_finetune.yaml
),设置模型权重路径、训练和评估数据集路径等,目前使用张量并行为8进行训练。
load_checkpoint: 'https://b2.7b2.com/root/workspace/model/chatglm-convert/ms_glm_6b.ckpt'
train_dataset: &train_dataset
data_loader:
type: MindDataset
dataset_dir: "https://b2.7b2.com/root/workspace/data/AdvertiseGen-ms/train_0604_128.mindrecord"
shuffle: True
eval_dataset: &eval_dataset
data_loader:
type: MindDataset
dataset_dir: "https://b2.7b2.com/root/workspace/data/AdvertiseGen-ms/eval_0604_256.mindrecord"
shuffle: True
parallel_config:
data_parallel: 1
model_parallel: 8
pipeline_stage: 1
expert_parallel: 1
启动模型训练前,可以通过设置多卡间等待时间(HCCL_CONNECT_TIMEOUT)来避免长时间没收到其他卡的消息而失败的问题,默认为300(秒)。
export HCCL_CONNECT_TIMEOUT=1200
在scripts
目录下启动全量微调训练任务:
# cd /root/workspace/code/mindformers/scripts
# Usage Help: bash run_distribute.sh [RANK_TABLE_FILE] [CONFIG_PATH] [DEVICE_RANGE] [RUN_STATUS]
> bash run_distribute.sh /root/workspace/code/mindformers/hccl_8p_01234567_192.168.1.196.json ../configs/glm/run_glm_6b_finetune.yaml '[0,8]' finetune
start training for rank 0, device 0
...
start training for rank 7, device 7
参数说明
- RANK_TABLE_FILE: hccl_tools.py生成的分布式json文件。
- CONFIG_PATH: 配置文件。
- DEVICE_RANGE: 单机分布式卡的范围,如:
[0,8]
为8卡分布式,不包含8本身。 - RUN_STATUS: 任务运行状态,支持关键:train、eval、finetune、predict。
模型训练启动成功,输出目录的结构如下所示。
output/
├── checkpoint
├── log
└── strategy
其中,checkpoint文件夹放置权重文件,log文件夹方式日志文件,strategy文件夹放置模型切分策略文件。
部分训练日志如下所示:
...
[INFO] 2023-07-11 10:38:43,568 [/root/workspace/code/mindformers/scripts/mf_parallel1/mindformers/trainer/base_trainer.py:550] training_process: .........Build Running Wrapper From Config For Train..........
[INFO] 2023-07-11 10:38:43,568 [/root/workspace/code/mindformers/scripts/mf_parallel1/mindformers/trainer/base_trainer.py:391] create_model_wrapper: .........Build Model Wrapper for Train From Config..........
[INFO] 2023-07-11 10:38:43,582 [/root/workspace/code/mindformers/scripts/mf_parallel1/mindformers/trainer/base_trainer.py:562] training_process: .........Starting Init Train Model..........
[INFO] 2023-07-11 10:38:43,583 [/root/workspace/code/mindformers/scripts/mf_parallel1/mindformers/trainer/base_trainer.py:581] training_process: .........Build Callbacks For Train..........
[INFO] 2023-07-11 10:38:43,583 [/root/workspace/code/mindformers/scripts/mf_parallel1/mindformers/trainer/base_trainer.py:400] create_callbacks: .........Build Callbacks for Train From Config..........
[INFO] 2023-07-11 10:38:43,584 [/root/workspace/code/mindformers/scripts/mf_parallel1/mindformers/core/callback/callback.py:340] __init__: Integrated_save is changed to False when using auto_parallel.
[INFO] 2023-07-11 10:38:43,585 [/root/workspace/code/mindformers/scripts/mf_parallel1/mindformers/trainer/base_trainer.py:609] training_process: .........Starting Training Model..........
[INFO] 2023-07-11 10:38:43,585 [/root/workspace/code/mindformers/scripts/mf_parallel1/mindformers/trainer/base_trainer.py:610] training_process: .........Model Compiling, Please Wait a Moment...........
[INFO] 2023-07-11 10:47:36,427 [/root/workspace/code/mindformers/scripts/mf_parallel1/mindformers/core/callback/callback.py:269] print_output_info: Epoch:[ 1/ 1], step:[ 4/ 125], loss:[2.244/2.244], time:507844.205 ms, lr:[0.], overflow cond: True, loss_scale: 268435460.0
[INFO] 2023-07-11 10:47:37,342 [/root/workspace/code/mindformers/scripts/mf_parallel1/mindformers/core/callback/callback.py:146] epoch_end: Per sink_size step time: 533756.177 ms, per step time: 133439.044 ms, avg loss: 2.244
[INFO] 2023-07-11 10:47:44,861 [/root/workspace/code/mindformers/scripts/mf_parallel1/mindformers/core/callback/callback.py:269] print_output_info: Epoch:[ 1/ 1], step:[ 8/ 125], loss:[2.499/2.499], time:7480.938 ms, lr:[0.], overflow cond: True, loss_scale: 16777216.0
[INFO] 2023-07-11 10:47:44,874 [/root/workspace/code/mindformers/scripts/mf_parallel1/mindformers/core/callback/callback.py:146] epoch_end: Per sink_size step time: 7518.224 ms, per step time: 1879.556 ms, avg loss: 2.499
...
[INFO] 2023-07-11 10:48:35,199 [/root/workspace/code/mindformers/scripts/mf_parallel1/mindformers/core/callback/callback.py:146] epoch_end: Per sink_size step time: 1958.791 ms, per step time: 489.698 ms, avg loss: 2.091
[INFO] 2023-07-11 10:48:37,162 [/root/workspace/code/mindformers/scripts/mf_parallel1/mindformers/core/callback/callback.py:269] print_output_info: Epoch:[ 1/ 1], step:[ 116/ 125], loss:[2.220/2.220], time:1951.612 ms, lr:[2.4499998e-06], overflow cond: False, loss_scale: 16384.0
[INFO] 2023-07-11 10:48:37,163 [/root/workspace/code/mindformers/scripts/mf_parallel1/mindformers/core/callback/callback.py:146] epoch_end: Per sink_size step time: 1963.915 ms, per step time: 490.979 ms, avg loss: 2.220
[INFO] 2023-07-11 10:48:39,125 [/root/workspace/code/mindformers/scripts/mf_parallel1/mindformers/core/callback/callback.py:269] print_output_info: Epoch:[ 1/ 1], step:[ 120/ 125], loss:[2.092/2.092], time:1953.753 ms, lr:[2.5499999e-06], overflow cond: False, loss_scale: 16384.0
[INFO] 2023-07-11 10:48:39,126 [/root/workspace/code/mindformers/scripts/mf_parallel1/mindformers/core/callback/callback.py:146] epoch_end: Per sink_size step time: 1962.049 ms, per step time: 490.512 ms, avg loss: 2.092
[INFO] 2023-07-11 10:48:41,083 [/root/workspace/code/mindformers/scripts/mf_parallel1/mindformers/core/callback/callback.py:269] print_output_info: Epoch:[ 1/ 1], step:[ 124/ 125], loss:[2.346/2.346], time:1949.995 ms, lr:[2.65e-06], overflow cond: False, loss_scale: 16384.0
[INFO] 2023-07-11 10:48:41,084 [/root/workspace/code/mindformers/scripts/mf_parallel1/mindformers/core/callback/callback.py:146] epoch_end: Per sink_size step time: 1958.268 ms, per step time: 489.567 ms, avg loss: 2.346
[INFO] 2023-07-11 10:49:26,307 [/root/workspace/code/mindformers/scripts/mf_parallel1/mindformers/trainer/base_trainer.py:616] training_process: .........Training Over!.............
LoRA 简述
针对大模型来说,模型全量微调需要耗费很多的内存;除此之外,模型全量微调还会损失多样性,存在灾难性遗忘的问题,因此,催生了各种高效微调技术。而LoRA,无疑是如今最流行的高效微调方法之一。
LoRA(论文:LoRA: LOW-RANK ADAPTATION OF LARGE LANGUAGE MODELS),该方法的核心思想就是通过低秩分解来模拟参数的改变量,从而以极小的参数量来实现大模型的间接训练。
在涉及到矩阵相乘的模块,在原始的PLM旁边增加一个新的通路,通过前后两个矩阵A,B相乘,第一个矩阵A负责降维,第二个矩阵B负责升维,中间层维度为r,从而来模拟所谓的本征秩(intrinsic rank)。
更加详细的介绍可参考之前的文章:大模型参数高效微调技术原理综述(五)-LoRA、AdaLoRA、QLoRA。
LoRA 微调
首先,生成用于Ascend芯片分布式通信的芯片资源信息配置文件,由于仅使用LoRA进行参数高效微调,所需内存减少,此处用4卡张量并行即可训练,因此,这里仅生成使用4卡的rank_table_file文件。
> python ./mindformers/tools/hccl_tools.py --device_num "[0,4)"
start ./mindformers/tools/hccl_tools.py
visible_devices:['0', '1', '2', '3', '4', '5', '6', '7']
server_id:192.168.1.196
device_num_list: [0, 1, 2, 3]
rank_id:0, device_id:0, device_ip:192.168.100.101
rank_id:1, device_id:1, device_ip:192.168.101.101
rank_id:2, device_id:2, device_ip:192.168.102.101
rank_id:3, device_id:3, device_ip:192.168.103.101
Completed: hccl file was save in : /root/workspace/code/mindformers/hccl_4p_0123_192.168.1.196.json
修改配置文件(configs/glm/run_glm_6b_lora.yaml
),设置模型权重路径、训练和评估数据集路径等,具体配置同全量微调类似,目前使用张量并行为4进行训练。
load_checkpoint: "https://b2.7b2.com/root/workspace/model/chatglm-convert/ms_glm_6b.ckpt"
train_dataset: &train_dataset
data_loader:
type: MindDataset
dataset_dir: "https://b2.7b2.com/root/workspace/data/AdvertiseGen-ms/train_0604_128.mindrecord"
shuffle: True
eval_dataset: &eval_dataset
data_loader:
type: MindDataset
dataset_dir: "https://b2.7b2.com/root/workspace/data/AdvertiseGen-ms/eval_0604_256.mindrecord"
shuffle: True
parallel_config:
data_parallel: 1
model_parallel: 4
pipeline_stage: 1
expert_parallel: 1
在scripts
目录下启动LoRA微调训练任务:
# cd /root/workspace/code/mindformers/scripts
# Usage Help: bash run_distribute.sh [RANK_TABLE_FILE] [CONFIG_PATH] [DEVICE_RANGE] [RUN_STATUS]
> bash run_distribute.sh /root/workspace/code/mindformers/hccl_4p_0123_192.168.1.196.json ../configs/glm/run_glm_6b_lora.yaml '[0,4]' finetune
具体的参数同全量微调一致。
不管是全量微调,还是LoRA微调,如果使用多卡模型并行进行训练,模型训练完成之后,需要先合并模型权重文件。
模型权重合并
微调所得到的模型权重文件为根据模型切分策略切分后的权重,我们需要手动将切分后的权重合并为一个文件,以用于评估和推理。
全量微调权重合并
首先,获取模型切分策略文件,在执行全量参数微调时,模型完成编译后,将会在模型输出权重路径下strategy子目录生成类似名为ckpt_strategy*.ckpt
的切分策略文件,找到对应的文件路径。
接下来,执行以下脚本(merge_ckpt.py
),将8份模型文件合成一份。MindSpore提供了根据切分策略转换模型权重切分的接口(mindspore.transform_checkpoint_by_rank
)。
python3 merge_ckpt.py --src_postfix=31_4
--src_checkpoints_dir=/root/workspace/output/fullft_output
--src_strategy_file=/root/workspace/output/fullft_output/strategy/ckpt_strategy_rank_0.ckpt
--dst_checkpoints_dir=/root/workspace/output/fullft_merge_checkpoint/
参数说明:
- src_postfix: 训练最新生成模型文件中数字,用于合并文件时索引。
- src_checkpoints_dir: 训练生成的模型文件路径。
- src_strategy_file: 模型切分策略文件。
- dst_checkpoints_dir: 模型文件合并后文件夹路径。
合并之后的权重文件如下所示:
> tree -h /root/workspace/output/fullft_merge_checkpoint
/root/workspace/output/fullft_merge_checkpoint
├── [ 13G] filtered_transformed.ckpt
└── [ 63G] transformed.ckpt
LoRA微调权重合并
需要注意的是,在使用LoRA进行参数微调时,生成的切分策略文件将不包含被冻结的权重,导致权重文件合并失败;此时,需将 mindformers/models/glm/glm.py
文件中有关LoRA冻结权重的代码注释后,重新运行微调脚本,获取到正确的切分策略文件后停止训练进程;相关代码如下
@MindFormerRegister.register(MindFormerModuleType.MODELS)
class GLMForPreTrainingWithLora(GLMForPreTraining):
def __init__(self, config: GLMConfig = None, pet=None, **kwargs):
_ = kwargs
super().__init__(config)
# get Pet tuning model.
self.pet = pet
self.pet.pet_config.reg_rules = r'.*query_key_value*'
self.transformer = LoraAdapter.get_pet_model(self.transformer, self.pet.pet_config)
# freeze pretrained model
PetAdapter.freeze_pretrained_model(self, self.pet.pet_type) # 注释此行以生成新的策略文件
LoRA微调的权重合并与全量微调权重合并类似,具体命令如下:
python3 merge_ckpt_lora.py --src_postfix=31_4
--src_checkpoints_dir=/root/workspace/output/lora_output
--src_strategy_file=/root/workspace/code/mindformers/output/strategy/ckpt_strategy_rank_0.ckpt
--dst_checkpoints_dir=/root/workspace/output/lora_merge_checkpoint_v2/
合并之后的模型权重文件如下所示:
> tree -h /root/workspace/output/lora_merge_checkpoint_v2/
/root/workspace/output/lora_merge_checkpoint_v2/
├── [ 14G] filtered_transformed.ckpt
└── [ 14G] transformed.ckpt
模型权重合并之后,接下来进行模型评估。
模型评估
全量微调模型评估
执行以下脚本(run_mindformer.py
)进行模型评估:
python run_mindformer.py
--config ./configs/glm/run_glm_6b_infer.yaml
--run_mode eval
--load_checkpoint /root/workspace/output/fullft_merge_checkpoint/filtered_transformed.ckpt
--eval_dataset_dir /root/workspace/data/AdvertiseGen-ms/eval_0604_256.mindrecord
--device_id 7
参数说明:
config
: 指定用于评估的配置文件名称,此处为configs/glm/run_glm_6b_infer.yaml
。run_mode
: 指定执行模式,此为eval
,表示为评估模式load_checkpoint
: 指定要加载的checkpoint路径,替换为权重合并之后的真实路径。eval_dataset_dir
: 评估数据集的路径。device_id
: 指定要使用的设备编号。
评估完成后会打印评估指标 bleu-4
、rouge-1
、rouge-2
、rouge-l
。具体如下所示:
rouge-1: 25.986109999999993
rouge-2: 4.22121
rouge-l: 21.202881999999995
bleu-4: 4.540767999999999
注意:
由于默认评估指标为生成完整文本后与预期文本做比较,评估速度将受限于模型大小与文本生成速度,评估流程可能较为缓慢。
LoRA微调模型评估
LoRA微调模型评估同全量微调模型评估方法一致,具体命令如下。
python run_mindformer.py
--config ./configs/glm/run_glm_6b_lora_infer.yaml
--run_mode eval
--load_checkpoint /root/workspace/output/lora_merge_checkpoint_v2/filtered_transformed.ckpt
--eval_dataset_dir /root/workspace/data/AdvertiseGen-ms/eval_0711_256.mindrecord
--device_id 0
结语
本文主要讲述了针对华为昇腾910加速卡基于ChatGLM-6B进行模型训练(全量微调以及LoRA微调)、权重合并以及模型评估。
如果觉得我的文章能够能够给您带来帮助,期待您的点赞收藏加关注~~