前言
随着『GPT4多模态/Microsoft 365 Copilot/Github Copilot X/ChatGPT插件』的推出,绝大部分公司的技术 产品 服务,以及绝大部分人的工作都将被革新一遍
- 类似iPhone的诞生 大家面向iOS编程 有了App Store
- 现在有了ChatGPT插件/GPT应用商店,以后很多公司 很多人面向GPT编程(很快技术人员分两种,一种懂GPT,一种不懂GPT)
然ChatGPT/GPT4基本不可能开源了,而通过上篇文章《类ChatGPT项目的部署与微调(上):从LLaMA、Alpaca/Vicuna/BELLE、中文版》可知,国内外各大公司、研究者推出了很多类ChatGPT开源项目,比如LLaMA、BLOOM
本文则侧重
- ChatGLM
- 垂直领域的ChatGPT等,比如ChatDoctor 毕竟,虽然LLaMA这些模型的通用能力很强,然应用在垂直领域的话,还得再加上各个垂直方向的预料加以训练,由此便诞生了以LLaMA为底层模型的比如ChatDoctor,且可以预见的是,垂直领域的ChatGPT,今年会诞生一大批
第四部分 国内的GLM框架与类ChatGPT项目ChatGLM-6B
4.1 GLM: General Language Model Pretraining with Autoregressive Blank Infilling
4.1.1 GLM结构:微改transformer block且通过自定义attention mask兼容GPT BERT T5三种结构
在2022年上半年,当时主流的预训练框架可以分为三种:
- autoregressive,自回归模型的代表是单向的GPT,本质上是一个从左到右的语言模型,常用于无条件生成任务(unconditional generation),缺点是无法利用到下文的信息
- autoencoding,自编码模型是通过某个降噪目标(如掩码语言模型,简单理解就是通过挖洞,训练模型做完形填空的能力)训练的语言编码器,如双向的BERT、ALBERT、RoBERTa、DeBERTa 自编码模型擅长自然语言理解任务(natural language understanding tasks),常被用来生成句子的上下文表示,缺点是不适合生成任务
- encoder-decoder,则是一个完整的Transformer结构,包含一个编码器和一个解码器,以T5、BART为代表,常用于有条件的生成任务 (conditional generation) 细致来说,T5的编码器中的注意力是双向,解码器中的注意力是单向的,因此可同时应用于自然语言理解任务和生成任务。但T5为了达到和RoBERTa和DeBERTa相似的性能,往往需要更多的参数量
这三种预训练模型各自称霸一方,那么问题来了,可否结合三种预训练模型,以成天下之一统?这便是2022年5月发表的这篇论文《GLM: General Language Model Pretraining with Autoregressive Blank Infilling》的出发点,它提出了GLM架构(这是张义策关于GLM论文的解读之一,下面三小节的内容主要参考自该篇解读)
首先,GLM框架在整体基于Transformer基础上,做了以下三点微小改动
- 重新排列了层归一化和残差连接的顺序
- 针对token的输出预测使用单一线性层
- 用GeLU替换ReLU激活函数
考虑到我讲的ChatGPT技术原理解析课群内,有同学对这块有疑问,所以再重点说下
- 本质上,一个GLMblock其实就是在一个transformer block的基础上做了下结构上的微小改动而已 至于实际模型时,这个block的数量或层数可以独立设置,比如设置24层(具体见下述代码第48行) GLM/arguments.py at 4b65bdb165ad323e28f91129a0ec053228d10566 · THUDM/GLM · GitHub
group.add_argument('--num-layers', type=int, default=24,
- 比如,基于GLM框架的类ChatGPT开源项目「ChatGLM」便用了28个GLMBlock,类似gpt2 用的12-48个decoder-transformer block,BERT用的12-24个encoder-transformer block
- 有些文章 包括我那篇transformer笔记,为举例,便用的N=6的示例,相当于编码器模块 用的6个encoder-transformer block,解码器模块 也用的6个decoder-transformer block
其次,考虑到三类预训练模型的训练目标
- GPT的训练目标是从左到右的文本生成
- BERT的训练目标是对文本进行随机掩码,然后预测被掩码的词
- T5则是接受一段文本,从左到右的生成另一段文本
为了大一统,我们必须在结构和训练目标上兼容这三种预训练模型。如何实现呢?文章给出的解决方法是结构上,只需要GLM中同时存在单向注意力和双向注意力即可
因为在原本的Transformer模型中,这两种注意力机制是通过修改attention mask实现的
- 当attention_mask是全1矩阵的时候,这时注意力是双向的
- 当attention_mask是三角矩阵的时候(如下图),注意力就是单向
类似地,GLM可以在只使用Transformer编码器的情况下,自定义attention mask来兼容三种模型结构
具体怎么个兼容法呢?假设原始的文本序列为,采样的两个文本片段为 和 ,那么掩码后的文本序列为 (以下简称Part A),如上图所示,拆解图中的三块分别可得
- 我们要根据第一个 解码出 ,根据第二个依次解码出 ,那怎么从 处解码出变长的序列吗?这就需要用到开始标记 和结束标记 了
- 我们从开始标记 开始依次解码出被掩码的文本片段,直至结束标记 。通过本博客内的Transformer笔记可知,Transformer中的位置信息是通过位置向量来记录的 在GLM中,位置向量有两个,一个 用来记录Part A中的相对顺序,一个 用来记录被掩码的文本片段(简称为Part B)中的相对顺序
- 此外,还需要通过自定义自注意掩码(attention mask)来达到以下目的: 双向编码器Part A中的词彼此可见,即图(d)中蓝色框中的区域 单向解码器Part B中的词单向可见,即图(d)黄色框的区域 Part B可见Part A 其余不可见,即图(d)中灰色的区域
需要说明的是,Part B包含所有被掩码的文本片段,但是文本片段的相对顺序是随机打乱的
4.1.2 GLM的预训练和微调
训练目标上,GLM论文提出一个自回归空格填充的任务(Autoregressive Blank Infifilling),来兼容三种预训练目标
自回归填充有些类似掩码语言模型,首先采样输入文本中部分片段,将其替换为[MASK]标记,然后预测[MASK]所对应的文本片段,与掩码语言模型不同的是,预测的过程是采用自回归的方式
具体来说
- 当被掩码的片段长度为1的时候,空格填充任务等价于掩码语言建模,类似BERT
- 当将文本1和文本2拼接在一起,然后将文本2整体掩码掉,空格填充任务就等价于条件语言生成任务,类似T5/BART
- 当全部的文本都被掩码时,空格填充任务就等价于无条件语言生成任务,类似GPT
最终,作者使用了两个预训练目标来优化GLM,两个目标交替进行:
- 文档级别的预测/生成:从文档中随机采样一个文本片段进行掩码,片段的长度为文档长度的50%-100%
- 句子级别的预测/生成:从文档中随机掩码若干文本片段,每个文本片段必须为完整的句子,被掩码的词数量为整个文档长度的15%
尽管GLM是BERT、GPT、T5三者的结合,但是在预训练时,为了适应预训练的目标,作者还是选择掩码较长的文本片段,以确保GLM的文本生成能力,并在微调的时候将自然语言理解任务也转化为生成任务,如情感分类任务转化为填充空白的任务
输入:{Sentence},prompt:It is really ,对应的标签为good和bad
4.2 GLM-130B:国内为数不多的可比肩GPT3的大模型之一
2022年8月,清华背景的智谱AI基于GLM框架,正式推出拥有1300亿参数的中英双语稠密模型 GLM-130B(论文地址、代码地址,论文解读之一,GLM-130B is trained on a cluster of 96 DGX-A100 GPU (8×40G) servers with a 60-day,可以较好的支持2048个token的上下文窗口)
其在一些任务上的表现优于GPT3-175B,是国内与2020年5月的GPT3在综合能力上差不多的模型之一(即便放到23年年初也并不多),这是它的一些重要特点
4.3 类ChatGPT开源项目ChatGLM-6B的训练框架与部署步骤
4.3.1 ChatGLM-6B的训练框架
ChatGLM-6B(介绍页面、代码地址),是智谱 AI 开源、支持中英双语的对话语言模型,其
- 基于General Language Model(GLM)架构,具有62亿参数,无量化下占用显存13G INT8量化级别下支持在单张11G显存的 2080Ti 上进行推理使用(因为INT8下占用显存8G) 而INT4量化级别下部署的话最低只需 6GB显存(另基于 P-Tuning v2 的高效参数微调方法的话,在INT4 下最低只需 7GB 显存即可启动微调) 量化等级****最低 GPU 显存(部署/推理)最低 GPU 显存(高效参数微调)FP16(无量化)13 GB14 GBINT88 GB9 GBINT46 GB7 GB 这里需要解释下的是,INT8量化是一种将深度学习模型中的权重和激活值从32位浮点数(FP32)减少到8位整数(INT8)的技术。这种技术可以降低模型的内存占用和计算复杂度,从而减少计算资源需求,提高推理速度,同时降低能耗 量化的过程通常包括以下几个步骤: 1 量化范围选择:确定权重和激活值的最小值和最大值 2 量化映射:根据范围将32位浮点数映射到8位整数 3 反量化:将8位整数转换回浮点数,用于计算
- ChatGLM-6B参考了 ChatGPT 的训练思路,在千亿基座模型GLM-130B中注入了代码预训练,通过监督微调(Supervised Fine-Tuning)、反馈自助(Feedback Bootstrap)、人类反馈强化学习(Reinforcement Learning from Human Feedback)等方式等技术实现人类意图对齐,并针对中文问答和对话进行优化
- 最终经过约 1T 标识符的中英双语训练,生成符合人类偏好的回答
虽尚有很多不足(比如因为6B的大小限制,导致模型的记忆能力、编码、推理能力皆有限),但在6B这个参数量级下不错了,部署也非常简单,我七月在线的同事朝阳花了一两个小时即部署好了(主要时间花在模型下载上,实际的部署操作很快)
4.3.2 ChatGLM-6B的部署步骤
以下是具体的部署过程
- 硬件配置 本次实验用的七月的GPU服务器(专门为七月集/高/论文/VIP学员配置的),显存大小为16G的P100,具体配置如下: CPU&内存:28核(vCPU)112 GB 操作系统:Ubuntu_64 GPU:NVIDIA Tesla P100 显存:16G
- 配置环境 建议最好自己新建一个conda环境 pip install -r requirements.txt 特别注意torch版本不低于1.10(这里安装的1.10),transformers为4.27.1 torch的安装命令可以参考pytorch官网:https://pytorch.org/ 这里使用的pip命令安装的,命令如下 pip install torch==1.10.0+cu102 torchvision==0.11.0+cu102 torchaudio==0
- 下载项目仓库 git clone https://github.com/THUDM/ChatGLM-6B cd ChatGLM-6B
- 下载ChatGLM-6B模型文件 具体而言,较大的8个模型文件可以从这里下载(下载速度快):清华大学云盘 其他的小文件可以从这里下载(点击红框的下载按钮即可):THUDM/chatglm-6b · Hugging Face 注意这里都下载在了/data/chatglm-6b下,在后面执行代码的时候需要将文件中的模型文件路径改为自己的
- 推理与部署 可运行的方式有多种 如果在本地运行,可以直接执行代码,或者使用命令行方式运行 如果想让别人公网访问,可以用下面两种方式:一种是基于Gradio,一种是基于streamlit 特别注意:运行代码前请检查模型文件路径是否正确,这里均改为了/data/chatglm-6b代码运行demo
from transformers import AutoTokenizer, AutoModeltokenizer = AutoTokenizer.from_pretrained("THUDM/chatglm-6b", trust_remote_code=True)model = AutoModel.from_pretrained("/data/chatglm-6b", trust_remote_code=True).half().cuda()model = model.eval()response, history = model.chat(tokenizer, "你好", history=[])print(response)response, history = model.chat(tokenizer, "晚上睡不着应该怎么办", history=history)print(response)
运行之后 如下截图所示命令行 Demo 运行仓库中 cli_demo.py: python cli_demo.py 程序会在命令行中进行交互式的对话,在命令行中输入指示并回车即可生成回复,输入 clear 可以清空对话历史,输入 stop 终止程序基于Gradio的网页版demo 运行web_demo.py即可(注意可以设置share=True,便于公网访问):python web_demo.py(注意运行前确认下模型文件路径)基于streamlit网页版 Demo pip install streamlit pip install streamlit-chat streamlit run web_demo2.py --server.port 6006(可以将6006端口放出,便于公网访问) 默认情况下,模型以 FP16 精度加载,运行上述代码需要大概 13GB 显存。如果显存有限,还可以考虑模型量化,目前支持4/8 bit 量化
此外,据介绍,GLM团队正在内测130B参数的ChatGLM,相信从6B到130B,效果应该能提升很多
4.4 微调ChatGLM-6B:针对各种数据集通过LoRA或P-Tuning v2
4.4.1 通过Stanford Alpaca的52K数据集基于LoRA(PEFT库)微调ChatGLM-6B
从上文可知,Stanford Alpaca的52K数据集是通过Self Instruct方式提示GPT3对应的API产生的指令数据,然后通过这批指令数据微调Meta的LLaMA 7B
而GitHub上的这个微调ChatGLM-6B项目(作者:mymusise),则基于Stanford Alpaca的52K数据集通过LoRA(low-rank adaptation)的方式微调ChatGLM-6B
如上一篇文章所说,Huggingface公司推出的PEFT(Parameter-Efficient Fine-Tuning)库便封装了LoRA这个方法,具体而言,通过PEFT-LoRA微调ChatGLM-6B的具体步骤如下
- 第一步,配置环境与准备 先下载项目仓库 git clone https://github.com/mymusise/ChatGLM-Tuning.git 创建一个python3.8的环境 conda create -n torch1.13 python==3.8 conda activate torch1.13 根据requirements.txt配置环境 pip install bitsandbytes==0.37.1 安装1.13,cuda11.6(torch官网命令) pip install torch==1.13.1+cu116 torchvision==0.14.1+cu116 torchaudio==0.13.1 --extra-index-url https://download.pytorch.org/whl/cu116 安装其他的包
pip install accelerate==0.17.1pip install tensorboard==2.10pip install protobuf==3.19.5pip install transformers==4.27.1pip install icetkpip install cpm_kernels==1.0.11pip install datasets==2.10.1pip install git+https://github.com/huggingface/peft.git # 最新版本 >=0.3.0.dev0
遇到冲突问题:icetk 0.0.5 has requirement protobuf<3.19, but you have protobuf 3.19.5. 最后装了3.18.3的protobuf,发现没有问题 模型文件准备 模型文件在前面基于ChatGLM-6B的部署中已经准备好了,注意路径修改正确即可 - 第二步,数据准备 项目中提供了数据,数据来源为 Stanford Alpaca 项目的用于微调模型的52K数据,数据生成过程可详见:https://github.com/tatsu-lab/stanford_alpaca#data-release alpaca_data.json,包含用于微调羊驼模型的 52K 指令数据,这个 JSON 文件是一个字典列表,每个字典包含以下字段: instruction: str,描述了模型应该执行的任务,52K 条指令中的每一条都是唯一的 input: str,任务的可选上下文或输入。例如,当指令是“总结以下文章”时,输入就是文章,大约 40% 的示例有输入 output: str,由 *text-davinci-003 *生成的指令的答案 示例如下:
[ { "instruction": "Give three tips for staying healthy.", "input": "", "output": "1.Eat a balanced diet and make sure to include plenty of fruits and vegetables. \n2. Exercise regularly to keep your body active and strong. \n3. Get enough sleep and maintain a consistent sleep schedule." }, { "instruction": "What are the three primary colors?", "input": "", "output": "The three primary colors are red, blue, and yellow." }, ...]
- 第三步,数据处理 运行 cover_alpaca2jsonl.py 文件 python cover_alpaca2jsonl.py \ --data_path data/alpaca_data.json \ --save_path data/alpaca_data.jsonl \ 处理后的文件示例如下:
{"text": "### Instruction:\nGive three tips for staying healthy.\n\n### Response:\n1.Eat a balanced diet and make sure to include plenty of fruits and vegetables. \n2. Exercise regularly to keep your body active and strong. \n3. Get enough sleep and maintain a consistent sleep schedule.\nEND\n"}{"text": "### Instruction:\nWhat are the three primary colors?\n\n### Response:\nThe three primary colors are red, blue, and yellow.\nEND\n"}
运行 tokenize_dataset_rows.py 文件,注意:修改tokenize_dataset_rows中的model_name为自己的文件路径 :/data/chatglm-6bpython tokenize_dataset_rows.py \ --jsonl_path data/alpaca_data.jsonl \ --save_path data/alpaca \ --max_seq_length 200 \ --skip_overlength \
- 第四步,微调过程 注意:运行前修改下finetune.py 文件中模型路径:/data/chatglm-6b
python finetune.py \ --dataset_path data/alpaca \ --lora_rank 8 \ --per_device_train_batch_size 6 \ --gradient_accumulation_steps 1 \ --max_steps 52000 \ --save_steps 1000 \ --save_total_limit 2 \ --learning_rate 1e-4 \ --fp16 \ --remove_unused_columns false \ --logging_steps 50 \ --output_dir output;
Nvidia驱动报错(如没有可忽略) 遇到问题,说明Nvidia驱动太老,需要更新驱动 UserWarning: CUDA initialization: The NVIDIA driver on your system is too old (found version 10020). Please update your GPU driver by downloading and installing a new version from the URL: http://www.nvidia.com/Download/index.aspx Alternatively, go to: https://pytorch.org to install a PyTorch version that has been compiled with your version of the CUDA driver. (Triggered internally at ../c10/cuda/CUDAFunctions.cpp:109.) 解决:更新驱动即可,参考:Ubuntu 18.04 安装 NVIDIA 显卡驱动 - 知乎 BUG REPORT报错 参考:因为peft原因,cuda10.2报错 · Issue #108 · mymusise/ChatGLM-Tuning · GitHub CUDA SETUP: CUDA version lower than 11 are currently not supported for LLM.int8() 考虑安装11以上的cudatooklit,参考下面链接,安装cudatooklit11.3(因为Ubuntu系统版本的原因,不能装11.6的)Ubuntu16.04 安装cuda11.3+cudnn8.2.1 - 知乎 cudatooklit下载地址:CUDA Toolkit 11.3 Downloads | NVIDIA 开发者 运行代码前先执行下面命令:export LD_LIBRARY_PATH=/usr/local/cuda-11.3/lib64:$LD_LIBRARY_PATH export CUDA_HOME=/usr/local/cuda-11.3:$CUDA_HOME export PATH=/usr/local/cuda-11.3/bin:$PATH
内存不够,考虑将per_device_train_batch_size设为1python finetune.py \ --dataset_path data/alpaca \ --lora_rank 8 \ --per_device_train_batch_size 1 \ --gradient_accumulation_steps 1 \ --max_steps 52000 \ --save_steps 1000 \ --save_total_limit 2 \ --learning_rate 1e-4 \ --fp16 \ --remove_unused_columns false \ --logging_steps 50 \ --output_dir output;
报错:RuntimeError: expected scalar type Half but found Float https://github.com/mymusise/ChatGLM-Tuning/issues?q=is%3Aissue+is%3Aopen+RuntimeError%3A+expected+scalar+type+Half+but+found+Float 解决方法: 一种是,不启用fp16, load_in_8bit设为True,可以运行,但loss为0; 一种是,启用fp16, load_in_8bit设为False,不行,应该还是显存不够的问题,至少需要24G左右的显存
4.4.2 ChatGLM团队:通过ADGEN数据集基于P-Tuning v2微调ChatGLM-6B
此外,ChatGLM团队自身也出了一个基于P-Tuning v2的方式微调ChatGLM-6B的项目:ChatGLM-6B 模型基于 P-Tuning v2 的微调
P-Tuning v2(代码地址,论文地址)意义在于:将需要微调的参数量减少到原来的 0.1%,再通过模型量化、Gradient Checkpoint 等方法,最低只需要 7GB 显存即可运行
那具体怎么通过P-Tuning v2微调ChatGLM-6B呢,具体步骤如下:
- 第一步,配置环境与准备 地址:ChatGLM-6B/ptuning at main · THUDM/ChatGLM-6B · GitHub 安装以下包即可,这里直接在torch1.13的conda环境下安装的
pip install rouge_chinese nltk jieba datasets
- 第二步,模型文件准备 模型文件在前面基于ChatGLM-6B的部署中已经准备好了,注意路径修改正确即可 特别注意:如果你是之前下载的可能会报错,下面有详细的错误及说明
- 第三步,数据准备 ADGEN数据集的任务为根据输入(content)生成一段广告词(summary)*{ "content": "类型#上衣版型#宽松版型#显瘦图案#线条衣样式#衬衫衣袖型#泡泡袖衣款式#抽绳", "summary": "这件衬衫的款式非常的宽松,利落的线条可以很好的隐藏身材上的小缺点,穿在身上有着很好的显瘦效果。领口装饰了一个可爱的抽绳,漂亮的绳结展现出了十足的个性,配合时尚的泡泡袖型,尽显女性甜美可爱的气息。" }* 从Google Drive 或者 Tsinghua Cloud 下载处理好的 ADGEN数据集,将解压后的AdvertiseGen目录放到本 ptuning 目录下即可
- 第四步,微调过程 修改train.sh文件 去掉最后的 --quantization_bit 4 注意修改模型路径,THUDM/chatglm-6b修改为/data/chatglm-6b 如果你也是在云服务器上运行,建议可以加上nohup后台命令,以免断网引起训练中断的情况修改后train.sh文件如下:
PRE_SEQ_LEN=8LR=1e-2CUDA_VISIBLE_DEVICES=0 nohup python -u main.py \ --do_train \ --train_file AdvertiseGen/train.json \ --validation_file AdvertiseGen/dev.json \ --prompt_column content \ --response_column summary \ --overwrite_cache \ --model_name_or_path /data/chatglm-6b \ --output_dir output/adgen-chatglm-6b-pt-$PRE_SEQ_LEN-$LR \ --overwrite_output_dir \ --max_source_length 64 \ --max_target_length 64 \ --per_device_train_batch_size 1 \ --per_device_eval_batch_size 1 \ --gradient_accumulation_steps 16 \ --predict_with_generate \ --max_steps 3000 \ --logging_steps 10 \ --save_steps 1000 \ --learning_rate $LR \ --pre_seq_len $PRE_SEQ_LEN \ >> log.out 2>&1 &
执行命令,开始微调 bash train.sh 如果报错:'ChatGLMModel' object has no attribute 'prefix_encoder'(如没有可忽略) 解决方案:需要更新 THUDM/chatglm-6b at main 里面的几个py文件(重新下载下这几个文件就可以了) 微调过程占用大约13G的显存 微调过程loss变化情况 微调完成后,output/adgen-chatglm-6b-pt-8-1e-2路径下会生成对应的模型文件,如下(这里生成了3个): - 第五步,推理过程 只需要在加载模型的位置修改成微调后的路径即可 将 evaluate.sh 中的 CHECKPOINT 更改为训练时保存的 checkpoint 名称,运行以下指令进行模型推理和评测: 改这一行即可:--model_name_or_path ./output/$CHECKPOINT/checkpoint-3000 bash evaluate.sh 评测指标为中文 Rouge score 和 BLEU-4,生成的结果保存在 ./output/adgen-chatglm-6b-pt-8-1e-2/generated_predictions.txt 我们可以对比下微调前后的效果 以命令行 Demo为例,只需修改cli_demo.py中的模型路径为:ptuning/out/adgen-chatglm-6b-pt-8-1e-2/checkpoint-3000,运行 cli_demo.py即可: python cli_demo.py 用以下数据为例:Input: 类型#上衣材质#牛仔布颜色#白色风格#简约图案#刺绣衣样式#外套衣款式#破洞 Label: 简约而不简单的牛仔外套,白色的衣身十分百搭。衣身多处有做旧破洞设计,打破单调乏味,增加一丝造型看点。衣身后背处有趣味刺绣装饰,丰富层次感,彰显别样时尚。 这件上衣的材质是牛仔布,颜色是白色,风格是简约,图案是刺绣,衣样式是外套,衣款式是破洞。 用户:根据输入生成一段广告词,输入为:类型#上衣材质#牛仔布颜色#白色风格#简约图案#刺绣衣样式#外套衣款式#破洞。 Output[微调前]: Output[微调后]:
总结:建议使用官方提供的基于P-Tuning v2微调ChatGLM-6B的方式对自己的数据进行微调
4.5 通过Cursor生成微调ChatGLM的示例代码
可能有读者会有疑问,到底怎么微调ChatGLM-6B呢,接下来我们通过Cursor一步一步生成一份示例代码(且参考此文),如下所示
- 导入所需的库:PyTorch、TensorDataset 和 DataLoader(用于处理数据集)、transformers(加载预训练模型和分词器)、Lora(此处指Layer-wise Optimized Rate Adaptation,非另一个简称为LoRA的微调LLM的Low-Rank Adaptation)和 SummaryWriter(用于将训练和验证损失记录到 TensorBoard)
import torchfrom torch.utils.data import TensorDataset, DataLoaderfrom transformers import AutoTokenizer, AutoModelForCausalLMfrom lora import Lorafrom torch.utils.tensorboard import SummaryWriter
- 下载并加载预训练的 ChatGLM-6B 模型和相应的分词器
# Download and load the ChatGLM-6B modelmodel_name = "TsinghuaAI/ChatGLM-6B"tokenizer = AutoTokenizer.from_pretrained(model_name)model = AutoModelForCausalLM.from_pretrained(model_name)
- 初始化Lora优化器,并设置学习率和秩(rank)
# Initialize the Lora optimizeroptimizer = Lora(model.parameters(), lr=1e-5, rank=16)
值得一提的是,关于学习率应该怎么设置更好 实际上,学习率的选择取决于你的模型和数据集 如果你的模型和数据集很大,那么你可能需要使用较小的学习率,以避免模型过拟合或者没有收敛 如果你的模型和数据集很小,那么你可能需要使用较大的学习率,以加快模型的训练速度 通常来说,学习率的初始值可以设置为1e-5或1e-4,然后根据模型在训练集和验证集上的表现来调整学习率 如果模型在训练集上的表现很好,但在验证集上的表现很差,那么你可能需要降低学习率; 如果模型在训练集和验证集上的表现都很差,那么你可能需要增加学习率 - 定义输入文本input_text,和输出文本output_text 并使用分词器将输入文本和输出文本转换为张量(tensor)
# Define your input textinput_text = "Hello, how are you?"# Tokenize the input textinput_ids = torch.tensor(tokenizer.encode(input_text)).unsqueeze(0)attention_mask = torch.ones_like(input_ids)# Define your output textoutput_text = "I'm doing well, thank you for asking."# Tokenize the output textlabels = torch.tensor(tokenizer.encode(output_text)).unsqueeze(0)
- 将训练数据转换为 TensorDataset,并使用 DataLoader 创建批处理数据加载器
# Convert your training data to a TensorDatasettrain_dataset = TensorDataset(input_ids, attention_mask, labels)# Create a DataLoader to load# the data in batchesbatch_size = 32train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
其中,batch_size参数指定了每个batch的大小,shuffle参数指定了是否打乱数据的顺序,当然,可以根据实际需要调整这些参数 - 定义验证数据,并将其转换为 TensorDataset 和 DataLoader
# Define your validation dataval_input_text = "How are you doing?"val_output_text = "I'm doing well, thank you for asking."val_input_ids = torch.tensor(tokenizer.encode(val_input_text)).unsqueeze(0)val_attention_mask = torch.ones_like(val_input_ids)val_labels = torch.tensor(tokenizer.encode(val_output_text)).unsqueeze(0)val_dataset = TensorDataset(val_input_ids, val_attention_mask, val_labels)val_loader = DataLoader(val_dataset, batch_size=batch_size, shuffle=True)
其中,nput_ids是一个包含输入文本的token ID的tensor attention_mask是一个指示哪些token是padding token的tensor labels是一个包含输出文本的token ID的tensor 在这个示例中,我们首先加载了ChatGLM-6B模型的tokenizer 然后,我们定义了输入文本和输出文本,并使用tokenizer将它们转换成了input_ids和labels 其中,attention_mask被设置为一个与input_ids相同大小的tensor,其中所有的值都是1,表示所有的token都是有效的 当然,你可以根据自己的训练数据和tokenizer来调整这些代码 另外,要说明的是要判断模型是否过拟合或者没有收敛,你可以观察模型在训练集和验证集上的表现 如果模型在训练集上的表现很好,但在验证集上的表现很差,那么就说明模型过拟合了 如果模型在训练集和验证集上的表现都很差,那么就说明模型没有收敛 如果模型在训练集和验证集上的表现都很好,那么就说明模型训练得很好 也可以使用PyTorch的DataLoader和TensorDataset来加载训练集和验证集,并在每个epoch结束时计算模型在训练集和验证集上的损失值 - 定义训练损失和验证损失的列表
# Define your training and validation lossestrain_losses = []val_losses = []
- 初始化 SummaryWriter,用于将损失写入 TensorBoard
# Initialize the SummaryWriterwriter = SummaryWriter()
- 对模型进行微调: a. 在 10 个epoch内进行迭代
for epoch in range(10): total_train_loss = 0 total_val_loss = 0 model.train()
b. 在每个时期中,对训练数据进行遍历,并计算损失。使用优化器进行梯度更新for batch in train_loader: optimizer.zero_grad() input_ids, attention_mask, labels = batch outputs = model(input_ids=input_ids, attention_mask=attention_mask, labels=labels) loss = outputs.loss loss.backward() optimizer.step() # 每次迭代的loss可以帮助我们更好地了解模型的训练过程,以及每个batch的训练效果 total_train_loss += loss.item() # 每轮的平均loss则可以帮助我们更好地了解模型的整体训练效果 # 以及模型是否出现了过拟合或欠拟合的情况 avg_train_loss = total_train_loss / len(train_loader) train_losses.append(avg_train_loss)
其中,optimizer.zero_grad()这行代码是用来清空模型的梯度的,为何要做这个操作呢? 因为在每次迭代中,我们需要计算模型的梯度,并使用优化器来更新模型的参数。但是,在计算新的梯度之前,我们需要先清空之前的梯度。否则,新的梯度会与之前的梯度相加,导致模型的参数更新不正确 因此,我们需要在每次迭代之前调用optimizer.zero_grad()来清空模型的梯度 ———————————————— 在每次迭代中,我们需要计算模型的梯度,并使用优化器来更新模型的参数 loss.backward()会自动计算模型的梯度,并将梯度保存在模型的参数中。这样,我们就可以使用优化器来更新模型的参数了 ———————————————— optimizer.step()是用来更新模型的参数的。在每次迭代中,我们需要使用优化器来更新模型的参数,以使模型的输出更接近于真实输出。optimizer.step()会根据模型的梯度和优化器的参数来更新模型的参数。具体来说,它会根据模型的梯度和学习率来计算参数的更新量,并将更新量加到模型的参数中。这样,模型的参数就会被更新了 如果没有调用optimizer.step(),那么模型的参数就不会被更新,每次迭代都是无用功。在每次迭代中,我们需要计算模型的梯度,并使用优化器来更新模型的参数。loss.backward()会自动计算模型的梯度,并将梯度保存在模型的参数中。但是,如果没有调用optimizer.step(),那么模型的参数就不会被更新,模型的输出也不会得到改善。因此,我们必须在每次迭代中调用optimizer.step(),以更新模型的参数 c. 在每个时期结束时,评估模型在验证数据上的表现,并计算损失total_val_loss = 0 model.eval() with torch.no_grad(): for batch in val_loader: input_ids, attention_mask, labels = batch outputs = model(input_ids=input_ids, attention_mask=attention_mask, labels=labels) loss = outputs.loss total_val_loss += loss.item() avg_val_loss = total_val_loss / len(val_loader) val_losses.append(avg_val_loss)
其中,model.eval()是用来将模型设置为评估模式的。在评估模式下,模型的行为会有所不同。具体来说,评估模式下,模型不会计算梯度,也不会更新模型的参数。这是因为,在评估模式下,我们只需要使用模型来进行预测,而不需要对模型进行训练 因此,为了提高模型的预测性能,我们需要将模型设置为评估模式。在这个示例中,我们在计算验证集上的损失值时,将模型设置为评估模式,以确保模型的参数不会被更新 ———————————————— with torch.no_grad():是一个上下文管理器,它可以临时禁用PyTorch的自动求导功能。在这个上下文中,所有的计算都不会被记录在计算图中,也不会影响模型的梯度。这个上下文通常用于评估模型或者进行推理时,因为在这些情况下,我们不需要计算梯度,也不需要更新模型的参数。禁用自动求导功能可以提高计算效率,并减少内存消耗。在这个示例中,我们在计算验证集上的损失值时,使用了with torch.no_grad():上下文,以提高计算效率 ———————————————— outputs.loss是模型在当前batch上的损失值。在这个示例中,我们使用了AutoModelForCausalLM模型,它是一个自回归语言模型,可以根据输入文本生成输出文本。在每个batch中,我们将输入文本和输出文本传递给模型,模型会根据输入文本生成输出文本,并计算输出文本与真实输出文本之间的交叉熵损失。这个交叉熵损失就是outputs.loss,我们可以使用这个损失来更新模型的参数,以使模型的输出更接近于真实输出 d. 打印训练损失和验证损失print(f"Epoch {epoch+1} train loss: {avg_train_loss:.4f} val loss: {avg_val_loss:.4f}")
e. 将训练损失和验证损失写入 TensorBoard# Write the training and validation losses to TensorBoard writer.add_scalar("Training loss", avg_train_loss, epoch) writer.add_scalar("Validation loss", avg_val_loss, epoch)
- 关闭 SummaryWriter
# Close the SummaryWriterwriter.close()
第五部分 复旦MOSS
MOSS是复旦大学邱锡鹏团队推出的一个支持中英双语和多种插件的开源对话语言模型,moss-moon系列模型具有160亿参数,在FP16精度下可在单张A100/A800或两张3090显卡运行,在INT4/8精度下可在单张3090显卡运行。MOSS基座语言模型在约七千亿中英文以及代码单词上预训练得到,后续经过对话指令微调、插件增强学习和人类偏好训练具备多轮对话能力及使用多种插件的能力
5.1 已开源的模型/数据
5.1.1 已开源的模型
- moss-moon-003-base: MOSS-003基座模型,在高质量中英文语料上自监督预训练得到,预训练语料包含约700B单词,计算量约6.67x1022次浮点数运算。
- moss-moon-003-sft: 基座模型在约110万多轮对话数据上微调得到,具有指令遵循能力、多轮对话能力、规避有害请求能力。
- moss-moon-003-sft-plugin: 基座模型在约110万多轮对话数据和约30万插件增强的多轮对话数据上微调得到,在
moss-moon-003-sft
基础上还具备使用搜索引擎、文生图、计算器、解方程等四种插件的能力。 - moss-moon-003-sft-int4: 4bit量化版本的
moss-moon-003-sft
模型,约占用12GB显存即可进行推理。 - moss-moon-003-sft-int8: 8bit量化版本的
moss-moon-003-sft
模型,约占用24GB显存即可进行推理。 - moss-moon-003-sft-plugin-int4: 4bit量化版本的
moss-moon-003-sft-plugin
模型,约占用12GB显存即可进行推理。 - moss-moon-003-sft-plugin-int8: 8bit量化版本的
moss-moon-003-sft-plugin
模型,约占用24GB显存即可进行推理。 - moss-moon-003-pm: 在基于
moss-moon-003-sft
收集到的偏好反馈数据上训练得到的偏好模型,将在近期开源。 - moss-moon-003: 在
moss-moon-003-sft
基础上经过偏好模型moss-moon-003-pm
训练得到的最终模型,具备更好的事实性和安全性以及更稳定的回复质量,将在近期开源。 - moss-moon-003-plugin: 在
moss-moon-003-sft-plugin
基础上经过偏好模型moss-moon-003-pm
训练得到的最终模型,具备更强的意图理解能力和插件使用能力,将在近期开源。
5.1.2 已开源的数据
- moss-002-sft-data: MOSS-002所使用的多轮对话数据,覆盖有用性、忠实性、无害性三个层面,包含由
text-davinci-003
生成的约57万条英文对话和59万条中文对话 - moss-003-sft-data:
moss-moon-003-sft
所使用的多轮对话数据,基于MOSS-002内测阶段采集的约10万用户输入数据和gpt-3.5-turbo
构造而成,相比moss-002-sft-data
,moss-003-sft-data
更加符合真实用户意图分布,包含更细粒度的有用性类别标记、更广泛的无害性数据和更长对话轮数,约含110万条对话数据。目前仅开源少量示例数据,完整数据将在近期开源 - moss-003-sft-plugin-data:
moss-moon-003-sft-plugin
所使用的插件增强的多轮对话数据,包含支持搜索引擎、文生图、计算器、解方程等四个插件在内的约30万条多轮对话数据。目前仅开源少量示例数据,完整数据将在近期开源 - moss-003-pm-data:
moss-moon-003-pm
所使用的偏好数据,包含在约18万额外对话上下文数据及使用moss-moon-003-sft
所产生的回复数据上构造得到的偏好对比数据,将在近期开源
// 待更..
第六部分 ChatDoctor:基于LLaMA或BART做增强
6.1 英文版ChatDoctor:通过self-instruct技术提示API的数据和医患对话数据集微调LLaMA
Github上有一个ChatDoctor项目(ChatDoctor: A Medical Chat Model Fine-tuned on LLaMA Model using Medical Domain Knowledge)
核心思想就是基于Meta 的LLaMA进行各种微调,具体而言主要是以下4个步骤
- 首先使用 Stanford Alpaca 提供的 52K instruction-following 数据训练了一个通用的对话模型 对于数据集构建这块,用的instruction, input, output的结构 instruction 可以是类似'你现在的身份是医生,请以这个身份跟我对话',input,output 就是问答对 实际微调时,使用的 6 个 A*100 GPU 进行,持续时间为 30 分钟 训练过程中使用的超参数如下:总的batch size 192,学习率(learning rate)设为2e-5,总共3个epoch,最大序列长度512个token,warmup ratio 0.03,无权重衰减 ———————————————— 至于斯坦福团队微调LLaMA 7B所用的52K指令数据咋来的呢, 说来也有趣,它是通过Self-Instruct『Self-Instruct是来自华盛顿大学Yizhong Wang等22年12月通过这篇论文《SELF-INSTRUCT: Aligning Language Model with Self Generated Instructions》提出的』提示GPT3的API拿到的 具体可以看下上一篇文章的3.1节:类ChatGPT开源项目的部署与微调
- 通过ChatGPT GenMedGPT-5k(将疾病数据库中的每条消息提示 ChatGPT API 以自动生成指令数据,且对ChatGPT API的提示包含了疾病和症状、药物的标准),和疾病数据库(包含大约 700 种疾病及其相关症状、医学检查和推荐药物)生成的『患者和医生之间的5K对话数据集』,再次微调模型 数据格式继续用的instruction, input, output的结构,比如
{ "instruction": "If you are a doctor, please answer the medical questions based on the patient's description.", "input": "Doctor, I have been experiencing sudden and frequent panic attacks. I don't know what to do.", "output": "Well, based on what you're telling me, it sounds like you may be suffering from panic disorder. The best course of action is to start with psychotherapy and mental health counseling. Additionally, we should conduct an electrocardiogram to make sure that there are no physical issues causing your panic attacks. We will also need to perform a depression screen and a toxicology screen to rule out any other underlying causes. Finally, I would recommend a comprehensive psychological and psychiatric evaluation and therapy to help manage your symptoms." }
- 通过HealthCareMagic-100k(来自在线问答的医疗咨询服务网站HealthCareMagic.com)的患者和医生之间的真实对话进行第三轮微调
- 通过icliniq-15k的患者和医生之间的真实对话进行第四轮微调
当然,也有业内研究者基于BART-base,且利用他们自己公司的数据,微调出他们版本的ChatDoctor(截取自邓老师朋友圈)
6.2 中文版ChatDoctor:哈工大华驼/本草——通过中文医学数据指令微调LLaMA
华驼模型仓库,是经过中文医学指令精调/指令微调(Instruct-tuning) 的LLaMA-7B模型(Llama-7B tuned with Chinese medical knowledge),这是其GitHub地址
该项目通过医学知识图谱和GPT3.5 API构建了中文医学指令数据集,并在此基础上对LLaMA进行了指令微调,提高了LLaMA在医疗领域的问答效果,且基于相同的数据,还训练了医疗版本的ChatGLM模型: ChatGLM-6B-Med
此外,我们还尝试利用GPT3.5 API将医学文献中的“结论”作为外部信息融入多轮对话中,在此基础上对LLaMA进行了指令微调。目前。我们只开放针对"肝癌"单个疾病训练的模型参数。在未来,我们计划发布融入文献结论的医学对话数据集,并且会针对“肝胆胰”相关16种疾病训练模型。
在数据集的构建上,
- 我们采用了公开和自建的中文医学知识库,主要参考了cMeKG 医学知识库围绕疾病、药物、检查指标等构建,字段包括并发症,高危因素,组织学检查,临床症状,药物治疗,辅助治疗等。知识库示例如下:
{"中心词": "偏头痛", "相关疾病": ["妊娠合并偏头痛", "恶寒发热"], "相关症状": ["皮肤变硬", "头部及眼后部疼痛并能听到连续不断的隆隆声", "晨起头痛加重"], "所属科室": ["中西医结合科", "内科"], "发病部位": ["头部"]}
- 我们利用GPT3.5接口围绕医学知识库构建问答数据,设置了多种Prompt形式来充分利用知识 指令微调的训练集数据示例如下:
"问题:一位年轻男性长期使用可卡因,突然出现胸痛、呕吐、出汗等症状,经检查发现心电图反映心肌急性损伤,请问可能患的是什么疾病?治疗方式是什么?"回答: 可能患的是心肌梗塞,需要进行维拉帕米、依普利酮、硝酸甘油、ß阻滞剂、吗啡等药物治疗,并进行溶栓治疗、低分子量肝素、钙通道阻滞剂等辅助治疗。此外需要及时停用可卡因等药物,以防止病情加重。"
我们提供了模型的训练数据集,共计8000余条,需要注意的是,虽然训练集的构建融入了知识,但是仍存在错误和不完善的地方,后续我们会利用更好的策略迭代更新数据集 当然,指令微调数据集质量仍有限,后续将进行不断迭代,同时医学知识库和数据集构建代码还在整理中,整理完成将会发布 - 此外,我们收集了2023年关于肝癌疾病的中文医学文献,利用GPT3.5接口围绕医学文献多轮问答数据。在·
./data_literature/liver_cancer.json
中我们提供了其中的1k条训练样例。目前,训练样本的质量仍然有限,在后续我们会进一步迭代数据,会以公开数据集
的形式对外进行发布。训练样本的示例如下:
6.3 PMC-LLaMA:上海交大用480万篇生物医学论文微调LLaMA模型
上海交大用480万篇生物医学论文微调LLaMA模型,在QA上取得了很好的效果
- 论文地址
- 代码地址
第七部分 可商用的数据集与可商用的模型
有的朋友可能已经注意到了,我们已经部署/微调了不少模型,包括且不限于LLaMA、以及基于LLaMA做各种微调的Alpaca、Vicuna、BELLE、Chinese-LLaMA/Chinese-Alpaca,以及LLaMA的RLHF版:ChatLLaMA(英文版)、ColossalChat,甚至包括国内的ChatGLM等模型
但感到遗憾的是,目前这些模型 都不能商用,当然 对于其中有些模型不允许商用也能理解,比如Alpaca扩展数据集的方式毕竟是通过self-instruct技术提示OpenAI的API生成数据,如果去商用,则和OpenAI本身产生不可避免的直接商业冲突
很多模型不允许商用还只是一方面,另一方面,数据集也是很大的一个问题,既然通过self-instruct技术提示OpenAI的API生成数据没法商用,那什么样的数据集允许商用呢,本部分便为大家探讨可以商用的数据集和可以商用的模型
7.1 4.12发布的Dolly 2.0:数据集由数千名 Databricks 员工生成的超过 1.5 万条记录的语料库
2023年4 月 12 日,Databricks 发布了Dolly 2.0 ,表示这是业内第一个开源、遵循指令的 LLM,它在透明且免费提供的数据集上进行了微调,该数据集也是开源的,可用于商业目的。这意味着 Dolly 2.0 可用于构建商业应用程序,无需支付 API 访问费用或与第三方共享数据。
项目链接:https://huggingface.co/databricks/dolly-v2-12b
数据集:https://github.com/databrickslabs/dolly/tree/master/data
以下是它的一些特点
- Dolly 2.0 是一个 120 亿参数的语言模型,它基于开源 EleutherAI pythia 模型系列
- Databricks 还发布了 Dolly 2.0 在其上进行微调的数据集,称为 databricks-dolly-15k。这是由数千名 Databricks 员工生成的超过 1.5 万条记录的语料库。 数据集包含7 项非常具体的任务: 1 公开问答:例如「为什么人们喜欢喜剧电影?」或「法国的首都是什么?」在某些情况下,没有一个正确的答案,而在其他情况下,需要借助于整个世界的知识; 2 封闭式问答:这些问题只用一段参考文献中的信息就可以回答。例如,给定维基百科中关于原子的一段,人们可能会问:「原子核中质子和中子的比例是多少?」; 3 从维基百科中提取信息:在这里,标注者会从维基百科上复制一个段落,并从该段落中提取实体或其他事实信息,如重量或测量; 4 总结维基百科上的信息:对于这一点,注释者从维基百科上提供了一段话,并被要求将其提炼为一个简短的摘要; 5 集思广益:这项任务要求进行开放式的构思,并列出相关的可能选项。例如「这个周末我可以和我的朋友做哪些有趣的活动?」; 6 分类:在这项任务中,标注者被要求对类别成员进行判断(例如,列表中的项目是动物、矿物还是蔬菜),或者判断一段短文的属性,例如电影评论的情绪; 7 创意写作:这项任务将包括写一首诗或一封情书等内容。
- 模型权重及微调数据集均可下载,要下载 Dolly 2.0 模型的权重,只需访问 Databricks Hugging Face 页面,并访问 databricks-labs 的 Dolly repo,下载 databricks-dolly-15k 数据集。
7.2 4.17发布的RedPajama开源数据集:1.2万亿token 容量5个T
2023年4月17日,RedPajama开源1.2万亿token数据集,帮助开发者训练类ChatGPT大语言模型。这也是目前类ChatGPT领域,全球最大的开源训练数据集(地址:https://huggingface.co/datasets/togethercomputer/RedPajama-Data-1T)
RedPajama完美复制了LLaMA模型上的1.2万亿训练数据集,由维基百科、GitHub、普通抓取、C4、图书、ArXiv(知名论文网站)、Stack Exchange七部分组成。完整数据集容量约5T,根据数据使用条例已经允许商业化
7.3 4.17发布的COIG:首个大规模、可商用的中文开源指令数据
https://hub.baai.ac.cn/view/25750
2023年4月17日,北京智源人工智能研究院第一期总共发布了 5 个子数据集,包括翻译指令、考试指令、人类价值观对齐指令、反事实修正多轮聊天、Leetcode指令,总计 191k 数据,聚焦中文语料、数据类型多样、经过了人工质检与修正、数据质量可靠,而且可以商用。
- 对应的论文:Chinese Open Instruction Generalist: a Preliminary Release https://arxiv.org/pdf/2304.07987.pdf 对应的数据链接: https://huggingface.co/datasets/BAAI/COIG 包括翻译指令、考试指令、人类价值观对齐指令、反事实修正多轮聊天、Leetcode指令,总计 191k 数据,聚焦中文语料、数据类型多样、经过了人工质检与修正、数据质量可靠,而且可以商用
7.4 4.19 发布的StableLM
2023年4月19日,Stability AI发布了一款名为StableLM的开源语言模型,旨在让基础AI技术更加透明、易于访问和支持。该模型可以生成文本和代码,并将支持一系列下游应用。它们展示了如何通过适当的训练,小型且高效的模型可以实现高性能
github链接:https://github.com/Stability-AI/StableLM
huggingface链接:https://huggingface.co/stabilityai/stablelm-base-alpha-7b
- 参数规模上 StableLM 模型的alpha版本有3B和7B参数,接着会推出14B和65B的模型。在CC BY-SA-4.0许可条款的约束下,开发者可以自由查看、使用并调整我们的StableLM基础模型,用于商业或研究目的。
- 数据集上 StableLM是在一个基于The Pile的新实验数据集上进行训练的,但其规模是The Pile的三倍,包含1.5万亿个内容标记。The Pile这个数据集包含的数据来源就有维基百科、Stack Exchange 和 PubMed。不过,Stability AI 在 the Pile 的基础上进行了扩展,所使用的数据集大小是标准 the Pile 的 3 倍。
- 模型架构上 StableLM使用了一些常用的模型架构,比如LSTM、Transformer等,还使用了一些先进的技术,比如自注意力机制、残差连接等,以提高模型的效果和精度
- 应用上 StableLM已经被广泛应用于自然语言处理任务,比如机器翻译、情感分析、文本分类和问答系统等,但StableLM 会说中文,但只会一点点(回复内容对应不上,语句也不通顺)。
7.5 其他可商用的模型
部分可商用模型的汇总链接
- github.com/eugeneyan/open-llms
版权归原作者 v_JULY_v 所有, 如有侵权,请联系我们删除。