西风吹老洞庭波,一夜湘君白发多。醉后不知天在水,满船清梦压星河。小伙伴好,我是微信公众号《小窗幽记机器学习》的小编:卖核弹的小女孩。为进一步支持国货文心一言,特以清明节为背景,写一首李白风格的古诗,并将其绘制出来,绘制图片仅供欣赏~
有很多小伙伴看我到之前分享的AI艺术系列:
都以为卖核弹的小女孩已经弃坑NLP转战CV,其实并没有。俗话说“小孩子才做选择题”,所以今天我选择NLP。今天这篇小作文主要介绍一下斯坦福 Alpaca 模型及其复现结果。先试着抛出四个符合社会主义核心价值观的问题看看生成结果是否符合网信办的要求:
模型简介
Stanford Alpaca是一个Instruction-following的LLaMA模型,即一个对LLaMA模型进行指令调优的结果模型。具体而言Stanford Alpaca 使用 OpenAI 的 text-davinci-003
模型以 self-instruct 方式 生成 52K 指令遵循(instruction-following)样本,并以此作为对LLaMA进行指令微调的训练集,最终得到Alpaca模型。
训练方法
对于学术界的预算条件,训练高质量的指令遵循模型(instruction-following model)面临两个重要挑战: 强大的预训练语言模型 和高质量的指令遵循数据 。对于第一个难题,可以采用最近Meta开(泄)源(露)的LLaMA系列模型。LLaMA系列包含了参数量为7B/13B/33B/65B的模型。然而,原模型的效果较差(如生成的结果文不对题、以及无法自然地结束生成等),需要后续进行微调。对于第二个难题,Self-Instruct提出一种利用现有的强大语言模型自动生成指令数据 。因此,斯坦福的 Alpaca 模型基于 LLaMA-7B 模型结合self-instruct 方式 生成的52k指令遵循(instruction-following)样本数据进行有监督的指令微调 ,就能达到类似 GPT-3.5 的效果。
该项目提供了对LLaMA模型进行微调的廉价亲民方法,大体思路如下:
首先,利用OpenAI提供的GPT模型(text-davinci-003
)API结合self-instruct方法生成质量较高的指令数据(仅52k),例如:
{
"instruction": "Rewrite the following sentence in the third person",
"input": "I am anxious",
"output": "She is anxious."
}, {
"instruction": "What are the three primary colors?",
"input": "",
"output": "The three primary colors are red, blue, and yellow."
}
使用 self-instruct 种子集中的 175 个人工编写的指令-输出(instruction-output)对,然后用该种子集作为 in-context 样本 prompt text-davinci-003
模型来生成更多指令。Alpaca通过简化生成 pipeline 改进了 self-instruct 方法,并显著降低了成本。Alpaca官方声称基于openai的API生成52k指令数据集的费用<500美元。关于self-instruct 方法的细节留待后续在本系列中详细说明,敬请期待留意。
有了这个指令遵循数据集,下一步是使用 Hugging Face 的训练框架微调 LLaMA 模型,在这个过程利用了FSDP(Fully Sharded Data Parallel)和混合精度训练等技术。成本方面,Alpaca在8个80GB A100 上微调一个 7B LLaMA 模型需要3个小时,这对大多数云计算提供商来说成本不到 100 美元。整体价格还算比较亲民,可盐可甜。
下面介绍如何基于 LLaMA-7B 尝试复现 Alpaca-7B。
环境安装
创建容器:
docker run --gpus all --privileged --name='test\_alpaca' -p 8809:8809 -p 8810:8810 -v /data/home/:/home -w /home -itd nvcr.io/nvidia/tritonserver:22.07-py3 bash
安装Python3.10
容器内默认的Python版本是3.8,而斯坦福Alpaca官方使用的是Python3.10,所以需要在容器内再安装一个Python版本:Python3.10。
安装Python3.10之前需要安装一些依赖
apt-get update -y
apt install build-essential zlib1g-dev libncurses5-dev libgdbm-dev libnss3-dev libssl-dev libreadline-dev libffi-dev libsqlite3-dev wget libbz2-dev -y
PS: python3.7以上的Python版本的需要安装libffi-devel
另外,还需要安装先升级OpenSSL,本实验所使用容器中已经安装openssl version
版本如下:
OpenSSL 1.1.1f 31 Mar 2020
安装Python3.10: 在Python官网下载Python10的源码:
wget https://www.python.org/ftp/python/3.10.11/Python-3.10.11.tar.xz
下载后解压安装:
tar -xf Python-3.10.11.tar.xz
cd Python-3.10.11
./configure --enable-optimizations --prefix=/opt/python3.10.11
make -j8
make install
如果要安装OpenSSL可以参考。
此时Python3.10安装在/opt/python3.10.11/
。如果想将系统默认的python3
指向python3.10
。which python3
:
/usr/bin/python3
which pip3
:
/usr/local/bin/pip3
只需要如下改动:
rm -rf /usr/bin/python3
ln -s /opt/python3.10.11/bin/python3 /usr/bin/python3
rm -rf /usr/local/bin/pip3
ln -s /opt/python3.10.11/bin/pip3 /usr/local/bin/pip3
另一种方式是直接将/opt/python3.10.11/bin/
添加到系统PATH中。
包安装
安装pytorch:
pip3 install torch==1.13 -i https://mirrors.cloud.tencent.com/pypi/simple
由于安装torch时候同时将torchrun
安装在/opt/python3.10.11/bin/torchrun
,所以需要将/opt/python3.10.11/bin/torchrun
创建一个软链接或者直接将整个/opt/python3.10.11/bin
添加到PATH。这里只添加一个软链接:
ln -s /opt/python3.10.11/bin/torchrun /usr/bin/torchrun
安装指定版本transformers: 目前,transformers官方版本并没有LLaMA相关的实现,但是已经合并到主分支,因此,在使用过程需要切换到对应的commit,从源代码进行相应的安装。
cd transformers
git checkout 0041be5
pip3 install . -i https://mirrors.cloud.tencent.com/pypi/simple
另一种方式是直接拉取push分支的Repo,直接pip3 install . -i
安装。
安装apex:
git clone https://github.com/NVIDIA/apex.git
cd apex
git checkout 22.04-dev
pip3 install -v --disable-pip-version-check --no-cache-dir --global-option="--cpp\_ext" --global-option="--cuda\_ext" ./
安装其他依赖:
numpy
rouge_score
fire
openai
sentencepiece
tokenizers==0.12.1
wandb
模型格式转换
需要将LLaMA原始权重文件转换为Transformers库对应的模型文件格式。如果不想自己转,也可以直接从Hugging Face下载转换好的模型
cd transformers
python3 src/transformers/models/llama/convert_llama_weights_to_hf.py --input_dir /home/model_zoo/llama --model_size 7B --output_dir /home/model_zoo/llama/7B/hf-llama-model
这个版本transformers
转换得到的结果是分别存于2个文件夹:llama-7b
和tokenizer
可以通过以下方式加载模型和分词器:
tokenizer = transformers.LlamaTokenizer.from_pretrained("/home/model\_zoo/llama/7B/hf-llama-model/tokenizer/")
model = transformers.LlamaForCausalLM.from_pretrained("/home/model\_zoo/llama/7B/hf-llama-model/llama-7b/")
为了方便将tokenizer目录的文件拷贝到llama-7b目录下。如果是直接用最新版的transformers
中转换脚本的话在hf-llama-model
会将模型参数文件和tokenizer相关文件平铺放一起。
数据准备
Stanford Alpaca提供用于训练的指令数据集alpaca_data.json,可以直接使用该数据集进行模型精调。但是在Alpaca-LoRA中提到该数据集存在一些噪声,因此,他们对该数据集做了清洗后得到了文件alpaca_data_cleaned.json。小伙伴们也可以采用该数据集进行训练,或许会得到更好结果。这里为了兼容中文,所以使用InstructionWild中的instinwild_en.json和instinwild_ch.json。
其他
如果想使用wandb,则可以在机器终端上运行wandb login
命令,然后填入自己wandb上账号的API key。
torchrun --nproc_per_node=8 --master_port=25001 train.py \
--model_name_or_path /home/model_zoo/llama/7B/hugging_face_format/ \
--data_path /home/data_zoo/InstructionWild/data/instinwild_ch_en.json \
--bf16 True \
--output_dir /home/Repository/LLM/stanford_alpaca/output/alpaca/sft_7b_en_cn \
--num_train_epochs 3 \
--per_device_train_batch_size 1 \
--per_device_eval_batch_size 1 \
--gradient_accumulation_steps 8 \
--evaluation_strategy "no" \
--save_strategy "steps" \
--save_steps 2000 \
--save_total_limit 1 \
--learning_rate 2e-5 \
--weight_decay 0. \
--warmup_ratio 0.03 \
--lr_scheduler_type "cosine" \
--logging_steps 1 \
--fsdp "full\_shard auto\_wrap" \
--fsdp_transformer_layer_cls_to_wrap 'LlamaDecoderLayer' \
--tf32 True
可以看出,虽然整体效果一般,但也还有可圈可点的地方。可能是由于开源的LLaMA模型在预训练阶段主要基于英语训练,虽然具有一定的多语言能力,然而由于它没有将中文语料加入预训练,所以LLaMA在中文上整体效果较弱。
运行完最后一个epoch后出现OOM:
{'train\_runtime': 5162.7837, 'train\_samples\_per\_second': 10.072, 'train\_steps\_per\_second': 0.157, 'train\_loss': 1.0267484738615347, 'epoch': 1.0}
100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 812/812 [1:26:02<00:00, 6.36s/it]
/opt/python3.10.11/lib/python3.10/site-packages/torch/distributed/fsdp/fully_sharded_data_parallel.py:2224:
UserWarning: Failed to clone() tensor with name _fsdp_wrapped_module._fpw_module.model.layers.28.mlp.down_proj.weight. This may mean that this state_dict entry could point to invalid memory regions after returning from
state_dict() call if this parameter is managed by FSDP.
Please check clone implementation of _fsdp_wrapped_module._fpw_module.model.layers.28.mlp.down_proj.weight.
Error: CUDA out of memory. Tried to allocate 172.00 MiB (GPU 3; 39.59 GiB total capacity; 35.81 GiB already allocated;
79.19 MiB free; 37.59 GiB reserved in total by PyTorch) If reserved memory is >> allocated memory try setting max_split_size_mb to
avoid fragmentation. See documentation for Memory Management and PYTORCH_CUDA_ALLOC_CONF
解决方案:参考官方Repo上issue,将safe_save_model_for_hf_trainer
改为如下:
def safe_save_model_for_hf_trainer(trainer: transformers.Trainer, output_dir: str):
"""Collects the state dict and dump to disk."""
# state\_dict = trainer.model.state\_dict()
from torch.distributed.fsdp import (
FullyShardedDataParallel as FSDP,
MixedPrecision,
BackwardPrefetch,
ShardingStrategy,
FullStateDictConfig,
StateDictType,
)
model=trainer.model
save_policy = FullStateDictConfig(offload_to_cpu=True, rank0_only=True)
with FSDP.state_dict_type(model, StateDictType.FULL_STATE_DICT, save_policy):
cpu_state_dict = model.state_dict()
if trainer.args.should_save:
trainer._save(output_dir, state_dict=cpu_state_dict) # noqa