文章目录
- 目的
- 训练环境
- 准备工作
- 训练
- 推理测试
- 遇到的问题
- UnboundLocalError: cannot access local variable 'default_conversation' where it is not associated with a value
- ImportError: cannot import name 'split_torch_state_dict_into_shards' from 'huggingface_hub'
- ModuleNotFoundError: No module named 'dropout_layer_norm'
- use_cache=True is incompatible with gradient checkpointing. Setting use_cache=False.
- KeyError: 'Cache only has 0 layers, attempted to access layer with index 0'
- CUDA out of memory
- Please install apex from source (https://github.com/NVIDIA/apex) to use the fused layernorm kernel
- KeyError: 'llama'
- RuntimeError: torch.cat(): expected a non-empty list of Tensors
目的
掌握训练手段,使用2000+QA数据集,对llama3-8B做全参数微调,使模型能完全按数据集中的问题进行回答,保证准确性。
训练环境
操作系统:
- ubuntu22.04
机器规格:
- CPU:96c;
- 内存:736 GiB;
- GPU:8 * NVIDIA V100 (32GB)
软件信息:
- Python 3.11.5;
- ColossalAI 0.4.0;
- cuda_11.8;
- pytorch 2.1.0+cu118
准备工作
模型准备
自行去huggingface或modelscope下载,不做赘述。
模型下载需要先申请权限,填表格时,国家选择中国以外的其他国家,否则容易被驳回,懂的都懂。
huggingface地址:https://huggingface.co/meta-llama/Meta-Llama-3-8B-Instruct
ColossalAI
注意:0.4.0测试完有一些bug,最好是下载0.4.1或之后的,以下所有相关截图我就不更换了。
https://github.com/hpcaitech/ColossalAI/tree/v0.4.0
或者直接用git clone进行下载,下载后进入根目录,执行:
pip install .
执行完之后,可以看到0.4.0已经安装
进入Colossal-LLaMA,先别着急执行pip install,打开requirements文件看了一下,不知道是不是官方缺心眼,一眼看去,好多包版本都不对,如果直接安装,会造成版本冲突、NO CUDA Driver FOUND等错,只能检查一下那些是已经安装的,把已经安装的从文件里去掉!
最后经过检查,只有以下几个是没有安装的,保留这几个即可:
huggingface-hub==0.22.1
packaging==24.0
autoflake==2.2.1
black==23.9.1
tensorboard==2.14.0
flash-attn>=2.0.0,<=2.0.5
然后执行:
pip install -r requirements.txt
执行完之后,会提示一些版本冲突,按要求更新一下
还有最重要的一步,不做这一步,后面会报错:No module named ‘dropout_layer_norm’
安装 xentropy, layer_norm and rotary
git clone git@github.com:Dao-AILab/flash-attention.git
# At the root folder
cd csrc/xentropy && pip install .
# At the root folder
cd csrc/layer_norm && pip install .
# At the root folder
cd csrc/rotary && pip install .
至此,所有依赖安装完毕。
可以执行 colossalai check -i
进行依赖检查。
数据集准备
- 原始数据集格式:
{"messages": [{"from": "human", "content": "What are the three primary colors?"}, {"from": "assistant", "content": "The three primary colors are red, blue, and yellow."}]}
{"messages": [{"from": "human", "content": "解释个人电脑和服务器之间的区别。"}, {"from": "assistant", "content": "个人电脑和服务器是两种不同类型的计算机系统,它们的主要区别在于用途、硬件配置和性能。 个人电脑,顾名思义,是为个人使用而设计的计算机。它们通常用于日常的工作、娱乐和学习,可以运行各种各样的应用程序和游戏。个人电脑的硬件配置一般是按照标准配置来设计的,不过也可以根据个人需求进行定制。 而服务器是为了满足大量用户的需求而设计的计算机系统,它们通常用于为用户提供各种网络服务,如网站、电子邮件和文件传输等。服务器通常需要高性能的硬件配置,并且可以承受高负载和长时间的运行。由于服务器需要支持大量用户的访问,它们通常配备多核处理器、大容量内存和大容量硬盘驱动器,以提高系统的运行速度和稳定性。 总之,个人电脑和服务器之间的主要区别在于它们的用途、硬件配置和性能。个人电脑用于个人使用,而服务器用于支持大量用户的访问。服务器的硬件配置通常比个人电脑更高,以保证系统的性能和稳定性。"}]}
- 数据预处理:
python /mnt/data/tool/ColossalAI-0.4.0/applications/Colossal-LLaMA/prepare_sft_dataset.py \
--data_input_dirs "/mnt/data/dataset/llama3/prepare/original/2000items" \
--tokenizer_dir "/mnt/data/model/modelscope/Meta-Llama-3-8B-Instruct" \
--data_output_dirs "/mnt/data/dataset/llama3/prepare/2000items-llama3" \
--max_length 2048 \
--num_spliced_dataset_bins 10 \
--llama_version 3
可以看到0.4.0版本不再需要配置chat_template.json,简便了许多。
执行完之后,在data_output_dirs可以看到SFT阶段需要的数据集:
准备训练脚本
#!/bin/bash
# NCCL IB environment variables
export NCCL_IB_HCA=mlx5_1:1,mlx5_2:1,mlx5_3:1,mlx5_4:1
export NCCL_IB_DISABLE=0
export NCCL_SOCKET_IFNAME=eth0
export NCCL_IB_GID_INDEX=3
export NCCL_IB_TIMEOUT=23
export NCCL_IB_RETRY_CNT=7
export OMP_NUM_THREADS=8
PROJECT_NAME="sft"
PARENT_SAVE_DIR="/mnt/data/sft-output/" # Path to a folder to save checkpoints
PARENT_TENSORBOARD_DIR="/mnt/data/sft-output/tensorboard/" # Path to a folder to save logs
PARENT_CONFIG_FILE="/mnt/data/sft-output/configs/config" # Path to a folder to save training config logs
PRETRAINED_MODEL_PATH="/mnt/data/model/modelscope/Meta-Llama-3-8B-Instruct" # huggingface or local model path
PRETRAINED_TOKENIZER_PATH="/mnt/data/model/modelscope/Meta-Llama-3-8B-Instruct" # huggingface or local tokenizer path
# NCCL IB environment variables
export NCCL_IB_HCA=mlx5_1:1,mlx5_2:1,mlx5_3:1,mlx5_4:1
export NCCL_IB_DISABLE=0
export NCCL_SOCKET_IFNAME=eth0
export NCCL_IB_GID_INDEX=3
export NCCL_IB_TIMEOUT=23
export NCCL_IB_RETRY_CNT=7
export OMP_NUM_THREADS=8
PROJECT_NAME="sft"
PARENT_SAVE_DIR="/mnt/data/sft-output/" # Path to a folder to save checkpoints
PARENT_TENSORBOARD_DIR="/mnt/data/sft-output/tensorboards/tensorboard" # Path to a folder to save logs
PARENT_CONFIG_FILE="/mnt/data/sft-output/configs/config" # Path to a folder to save training config logs
PRETRAINED_MODEL_PATH="/mnt/data/model/modelscope/Meta-Llama-3-8B-Instruct" # huggingface or local model path
PRETRAINED_TOKENIZER_PATH="/mnt/data/model/modelscope/Meta-Llama-3-8B-Instruct" # huggingface or local tokenizer path
EPOCH=10
MODEL="llama3-8b"
declare -a dataset=(
/mnt/data/dataset/llama3/prepare/2000items-llama3/arrow/part-00000
/mnt/data/dataset/llama3/prepare/2000items-llama3/arrow/part-00001
/mnt/data/dataset/llama3/prepare/2000items-llama3/arrow/part-00002
/mnt/data/dataset/llama3/prepare/2000items-llama3/arrow/part-00003
/mnt/data/dataset/llama3/prepare/2000items-llama3/arrow/part-00004
/mnt/data/dataset/llama3/prepare/2000items-llama3/arrow/part-00005
/mnt/data/dataset/llama3/prepare/2000items-llama3/arrow/part-00006
/mnt/data/dataset/llama3/prepare/2000items-llama3/arrow/part-00007
/mnt/data/dataset/llama3/prepare/2000items-llama3/arrow/part-00008
/mnt/data/dataset/llama3/prepare/2000items-llama3/arrow/part-00009
)
TIMESTAMP=$(date +%Y-%m-%d-%H-%M-%S)
FULL_PROJECT_NAME="${PROJECT_NAME}-${MODEL}-${EPOCH}-${TIMESTAMP}"
SAVE_DIR="${PARENT_SAVE_DIR}${FULL_PROJECT_NAME}"
TENSORBOARD_DIR="${PARENT_TENSORBOARD_DIR}-${FULL_PROJECT_NAME}"
CONFIG_FILE="${PARENT_CONFIG_FILE}${FULL_PROJECT_NAME}.json"
colossalai run --nproc_per_node 4 --master_port 30013 /mnt/data/tool/ColossalAI-0.4.0/applications/Colossal-LLaMA/train.py \
--pretrained $PRETRAINED_MODEL_PATH \
--dataset ${dataset[@]} \
--plugin zero2_cpu \
--save_dir $SAVE_DIR \
--tensorboard_dir $TENSORBOARD_DIR \
--config_file $CONFIG_FILE \
--num_epochs $EPOCH \
--accumulation_steps 1 \
--micro_batch_size 2 \
--lr 5e-5 \
--max_length 2048 \
--save_interval 10000 \
--use_grad_checkpoint
训练
前置准备工作都完成了,训练阶段就只需要运行脚本了,等待日志显示success即可。
(以下的截图是3个epoch训练的)
现在有一个问题:每个epoch都会生成一个很大的文件夹:
提交了一个PR,加了一个开关,用户可以选择是否需要每个epoch保存一次checkpoint,具体使用在PR中有介绍。
https://github.com/hpcaitech/ColossalAI/pull/5941
修改后,每个epoch不在自动保存checkpoint,效果如下:
模型的输出目录,只有一个最终的模型文件了。
推理测试
加载模型
from transformers import AutoTokenizer, AutoModelForCausalLM
import torch
model_path = "/mnt/data/sft-output/sft-2024-07-23-21-35-36/modeling"
tokenizer = AutoTokenizer.from_pretrained("/mnt/data/model/modelscope/Meta-Llama-3-8B-Instruct")
model = AutoModelForCausalLM.from_pretrained(
model_path,
torch_dtype=torch.bfloat16,
device_map="auto",
)
提问
messages = [
{"role": "system", "content": "You are a chatbot, you must give a correct anwser"},
{"role": "user", "content": "社保公积金的调整规则及其操作时间如何?"},
]
input_ids = tokenizer.apply_chat_template(
messages,
add_generation_prompt=True,
return_tensors="pt"
).to(model.device)
terminators = [
tokenizer.eos_token_id,
tokenizer.convert_tokens_to_ids("<|eot_id|>")
]
outputs = model.generate(
input_ids,
max_new_tokens=512,
eos_token_id=terminators,
do_sample=True,
temperature=0.0000001,
top_p=0.9,
)
response = outputs[0][input_ids.shape[-1]:]
print(tokenizer.decode(response, skip_special_tokens=True))
关于这个问题的原数据集如下:
用GPT将问题进行变形,回答不变,让提问能有一定的泛化性。
{"messages": [{"from":"human","content":"社保公积金的调整规则及其操作时间如何?"},{"from":"assistant","content":"增员:当月15号(含)前入职的当月增员,当月15号以后入职的下月增员,系统操作时间为每月17-25号。减员,最后工作日在当月15号以前的当月减员,最后工作日在当月15号(含)以后的下月减员,系统操作时间为每月10-20号。"}]}
{"messages": [{"from":"human","content":"能讲解一下关于社保公积金增减员的规定和操作时间吗?"},{"from":"assistant","content":"增员:当月15号(含)前入职的当月增员,当月15号以后入职的下月增员,系统操作时间为每月17-25号。减员,最后工作日在当月15号以前的当月减员,最后工作日在当月15号(含)以后的下月减员,系统操作时间为每月10-20号。"}]}
{"messages": [{"from":"human","content":"社保公积金增减员的操作时间和规定是什么?"},{"from":"assistant","content":"增员:当月15号(含)前入职的当月增员,当月15号以后入职的下月增员,系统操作时间为每月17-25号。减员,最后工作日在当月15号以前的当月减员,最后工作日在当月15号(含)以后的下月减员,系统操作时间为每月10-20号。"}]}
{"messages": [{"from":"human","content":"社保公积金的增减员操作应该怎么进行,操作时间是什么时候?"},{"from":"assistant","content":"增员:当月15号(含)前入职的当月增员,当月15号以后入职的下月增员,系统操作时间为每月17-25号。减员,最后工作日在当月15号以前的当月减员,最后工作日在当月15号(含)以后的下月减员,系统操作时间为每月10-20号。"}]}
{"messages": [{"from":"human","content":"关于社保公积金增减员的操作时间和规则能详细介绍一下吗?"},{"from":"assistant","content":"增员:当月15号(含)前入职的当月增员,当月15号以后入职的下月增员,系统操作时间为每月17-25号。减员,最后工作日在当月15号以前的当月减员,最后工作日在当月15号(含)以后的下月减员,系统操作时间为每月10-20号。"}]}
{"messages": [{"from":"human","content":"请问,社保公积金增减员的规定和操作时间是什么样的?"},{"from":"assistant","content":"增员:当月15号(含)前入职的当月增员,当月15号以后入职的下月增员,系统操作时间为每月17-25号。减员,最后工作日在当月15号以前的当月减员,最后工作日在当月15号(含)以后的下月减员,系统操作时间为每月10-20号。"}]}
{"messages": [{"from":"human","content":"我该怎样理解社保公积金的增减员规则以及其操作时间?"},{"from":"assistant","content":"增员:当月15号(含)前入职的当月增员,当月15号以后入职的下月增员,系统操作时间为每月17-25号。减员,最后工作日在当月15号以前的当月减员,最后工作日在当月15号(含)以后的下月减员,系统操作时间为每月10-20号。"}]}
{"messages": [{"from":"human","content":"社保公积金的增减员是如何规定的,相关操作时间又是怎样的?"},{"from":"assistant","content":"增员:当月15号(含)前入职的当月增员,当月15号以后入职的下月增员,系统操作时间为每月17-25号。减员,最后工作日在当月15号以前的当月减员,最后工作日在当月15号(含)以后的下月减员,系统操作时间为每月10-20号。"}]}
{"messages": [{"from":"human","content":"能否详细讲解一下社保公积金增减员的操作时间和规则?"},{"from":"assistant","content":"增员:当月15号(含)前入职的当月增员,当月15号以后入职的下月增员,系统操作时间为每月17-25号。减员,最后工作日在当月15号以前的当月减员,最后工作日在当月15号(含)以后的下月减员,系统操作时间为每月10-20号。"}]}
{"messages": [{"from":"human","content":"请问社保公积金的增减员规定以及操作时间是如何的?"},{"from":"assistant","content":"增员:当月15号(含)前入职的当月增员,当月15号以后入职的下月增员,系统操作时间为每月17-25号。减员,最后工作日在当月15号以前的当月减员,最后工作日在当月15号(含)以后的下月减员,系统操作时间为每月10-20号。"}]}
相同的问题,换一个提问方式:
可以看到,换了一种提问方式(问题原文在数据集中不存在),模型也能很准确的进行回答。
后续做了一些测试,大致结论如下:
- 对于超长文本的数据集,回答准确性无法保证;
- 因为epoch设置得很低,epoch=1,所以2000条左右数据中,后半部分的问题回答效果均不佳,后续调高epoch做了对比测试,epoch=8时,准确率已达到了90%+;
至此,训练结束,虽有坎坷,亦有收货。
遇到的问题
UnboundLocalError: cannot access local variable ‘default_conversation’ where it is not associated with a value
这个错出现在数据处理阶段,原因是代码bug,需要改源码,改动也比较简单,就是给这个变量赋一个默认值,我给官方提了一个issue和pr。
ImportError: cannot import name ‘split_torch_state_dict_into_shards’ from ‘huggingface_hub’
怀疑是huggingface_hub版本不兼容,目前安装的版本是:
换了一个高版本,解决了。
pip install huggingface-hub==0.22.1
ModuleNotFoundError: No module named ‘dropout_layer_norm’
dropout_layer_norm这个东西是flash_attn中的,官方有说明,2024.1之后不再使用这个了,反正是奇奇怪怪的错误。
有一些文章供参考:
https://github.com/Dao-AILab/flash-attention/issues/587
https://github.com/Dao-AILab/flash-attention/tree/main/csrc/layer_norm
https://github.com/Dao-AILab/flash-attention/tree/main/flash_attn/ops/triton
https://github.com/OpenGVLab/InternVideo/blob/main/InternVideo2/multi_modality/INSTALL.md
这个问题对我这个门外汉来说,卡了很久,还尝试看了下flash_attention的源码,最后解决方法是手动编译安装,方法如下:
git clone git@github.com:Dao-AILab/flash-attention.git
# At the root folder
cd csrc/xentropy && pip install .
# At the root folder
cd csrc/layer_norm && pip install .
# At the root folder
cd csrc/rotary && pip install .
use_cache=True is incompatible with gradient checkpointing. Setting use_cache=False.
这个是一个警告,并不影响流程。
按字面理解,因为用了梯度保存的策略,需要将这个参数设置为false,很好解决。但是脚本并不支持这个参数的设置,试了通过环境变量的方式也不行。
多说一句,如果使用的梯度累积的策略,当达到指定步数时,会执行落盘操作,会非常非常耗时,而且会产生一个非常大的文件夹,大概70GB+,所以,按需求配置,因为是测试,我一般会把保存间隔设置非常大,避免进行累加保存。
KeyError: ‘Cache only has 0 layers, attempted to access layer with index 0’
flash_attn冲突,暂时不知道如何解决,将脚本中的--use_flash_attn
去掉。
CUDA out of memory
这是一个很常见的错误,原因就是显卡显存不足导致的
有几个解决方案:
- 减小batch_size;
- 插件选择zero2_cpu(–plugin “zero2_cpu”),这个可以使用CPU去协助运行,就是会很慢;
- 使用梯度策略,训练参数加上–use_grad_checkpoint(低版本是:–grad_checkpoint)
- 换显存大的机器。
Please install apex from source (https://github.com/NVIDIA/apex) to use the fused layernorm kernel
这是一个警告,虽然不影响运行,但是也膈应,顺便解决下。
git clone https://github.com/NVIDIA/apex
cd apex
# if pip >= 23.1 (ref: https://pip.pypa.io/en/stable/news/#v23-1) which supports multiple `--config-settings` with the same key...
pip install -v --disable-pip-version-check --no-cache-dir --no-build-isolation --config-settings "--build-option=--cpp_ext" --config-settings "--build-option=--cuda_ext" ./
# otherwise
pip install -v --disable-pip-version-check --no-cache-dir --no-build-isolation --global-option="--cpp_ext" --global-option="--cuda_ext" ./
查看pip版本:
pip --version
KeyError: ‘llama’
我遇到这个错的背景是:使用applications/ColossalChat对llama2-7B-Chat做SFT,没有对ColossalChat执行install。
cd applications/ColossalChat/
pip install .
这个问题需要结合自己的情况去看。
RuntimeError: torch.cat(): expected a non-empty list of Tensors
RuntimeErrorRuntimeError: : The param bucket max size 12582912 is exceededby tensor (size 131334144)The param bucket max size 12582912 is exceededby tensor (size 131334144)
During handling of the above exception, another exception occurred:
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
Traceback (most recent call last):
File "/mnt/data/tool/ColossalAI-0.4.0/applications/Colossal-LLaMA/train.py", line 425, in <module>
File "/mnt/data/tool/ColossalAI-0.4.0/applications/Colossal-LLaMA/train.py", line 425, in <module>
main()
main()
File "/mnt/data/tool/ColossalAI-0.4.0/applications/Colossal-LLaMA/train.py", line 357, in main
File "/mnt/data/tool/ColossalAI-0.4.0/applications/Colossal-LLaMA/train.py", line 357, in main
optimizer.step()
File "/home/pai/lib/python3.11/site-packages/colossalai/zero/low_level/low_level_optim.py", line 717, in step
optimizer.step()
File "/home/pai/lib/python3.11/site-packages/colossalai/zero/low_level/low_level_optim.py", line 717, in step
tensor_bucket.all_gather(self._bucket_store.moe_extra_dp_pg)
File "/home/pai/lib/python3.11/site-packages/colossalai/zero/low_level/bookkeeping/tensor_bucket.py", line 65, in all_gather
tensor_bucket.all_gather(self._bucket_store.moe_extra_dp_pg)
flat = self.flatten()
File "/home/pai/lib/python3.11/site-packages/colossalai/zero/low_level/bookkeeping/tensor_bucket.py", line 65, in all_gather
^^^^^^^^^^^^^^
File "/home/pai/lib/python3.11/site-packages/colossalai/zero/low_level/bookkeeping/tensor_bucket.py", line 54, in flatten
flat = self.flatten()
^^^^^^^^^^ ^return _flatten_dense_tensors(self._bucket)^
^^
File "/home/pai/lib/python3.11/site-packages/colossalai/zero/low_level/bookkeeping/tensor_bucket.py", line 54, in flatten
^^^^^^^^^^^^^^^^ ^return _flatten_dense_tensors(self._bucket)^
^^^^^^^^^^^^^^^^ ^ ^
File "/home/pai/lib/python3.11/site-packages/torch/_utils.py", line 498, in _flatten_dense_tensors
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/pai/lib/python3.11/site-packages/torch/_utils.py", line 498, in _flatten_dense_tensors
return torch._C._nn.flatten_dense_tensors(tensors)
return torch._C._nn.flatten_dense_tensors(tensors)^
^^^^^^^^^^^^^^^^^^^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^
^^^^RuntimeError^^: ^torch.cat(): expected a non-empty list of Tensors^
^^^^^^^^^^^^^^^^^^^^^^
RuntimeError: torch.cat(): expected a non-empty list of Tensors
Traceback (most recent call last):
File "/home/pai/lib/python3.11/site-packages/colossalai/zero/low_level/low_level_optim.py", line 715, in step
tensor_bucket.add_to_bucket(param_to_gather, write_back_tensor=working_param)
File "/home/pai/lib/python3.11/site-packages/colossalai/zero/low_level/bookkeeping/tensor_bucket.py", line 34, in add_to_bucket
raise RuntimeError(msg)
RuntimeError: The param bucket max size 12582912 is exceededby tensor (size 131334144)
来看看GPT是怎么回答的:
在深度学习中,模型的训练通常在GPU(显卡)上进行,因为GPU有大量的并行处理单元,可以大大加速模型的训练。
上述错误信息中的"bucket max size 12582912 is exceeded by tensor (size 131334144)"表示一个张量的大小超过了分桶策略的最大值。这个张量通常会存储在显卡的内存中,如果显卡的内存不足,就可能导致这个错误。
具体来说,如果你的模型很大,或者你的批量大小(batch size)很大,那么可能会产生很大的张量,从而超过显卡的内存限制。在这种情况下,你可以尝试以下解决方案:
1. 减小模型的大小:可以通过减少层的数量、减少每层的神经元数量等方式来减小模型的大小。
2. 减小批量大小:减小批量大小可以减小每个张量的大小,但可能会影响训练效果和效率。
3. 使用更高规格的显卡:如果条件允许,你可以考虑使用具有更大内存的显卡。
4. 使用模型并行:如果模型太大,无法在一块显卡上完全存储,可以考虑使用模型并行,将模型的不同部分放在不同的显卡上。
5. 使用梯度累积:即不是每一步都进行梯度更新,而是积累几步的梯度再进行更新,这样可以减小内存使用。
需要注意的是,这些方法可能会影响模型的训练效果和效率,需要根据具体情况进行权衡。
不知道是不是和我显卡规格有关,相关专业的大佬,麻烦给我解答一下…
针对这个问题,我提了一个issue,里面有解答:https://github.com/hpcaitech/ColossalAI/issues/5935
下载0.4.1之后的代码,到根目录下执行 pip install .
重新运行脚本解决。
训练的效果,符合预期,而且比llama2好很多。
现在北京时间是2024-07-25,北京有暴雨警告,llama3.1发布了。。。