赞
踩
国内众多开源模型中,现在比较有影响力的除了ChatGLM,就是baichuan了(当然,还有其他,后续我再关注)
而baichuan也在不断迭代,23年7月份出了第一代,23年9月份出了第二代,分别对应本文的第一部分、第二部分(即本文第一部分更新于7月份,第二部分更新于9月份)
baichuan-7B 是由百川智能(CEO为原搜狗创始人王小川)开发的一个开源可商用的大规模预训练语言模型
虽然baichuan-7B采用了和LLaMA一样的模型设计,但他们在原本的 LLaMA 框架上进行诸多修改
比如为提升模型的效果以及解码效率,做了
比如为提升训练时的吞吐,做了以下优化
基于上述的几个优化技术,使得在千卡 A800 显卡上达到了 7B 模型 182 TFLOPS 的吞吐,GPU 峰值算力利用率高达 58.3%
本次微调参考项目:https://github.com/wp931120/baichuan_sft_lora
由于baichuan没有 supervised finetune 这一步,没有和人类意图进行对齐,经常听不懂你下达的指令。该项目遂利用belle 0.5M 指令微调数据,采用qlora的量化微调的方式对百川大模型进行人类意图对齐训练
如何训练呢?简单而言
更具体而言,本次微调baichuan-7B的步骤如下
- git clone https://github.com/wp931120/baichuan_sft_lora.git
- cd baichuan_sft_lora
配置环境 - conda create -n baichuan-7b python=3.9
- conda activate baichuan-7b
- pip install -r requirements.txt
数据集下载- import os # 导入os模块,这个模块提供了一种方便的使用操作系统依赖功能的方式
- os.environ['CUDA_VISIBLE_DEVICES'] = '0' # 设置CUDA可见设备,'0'表示仅使用第一块GPU
-
- from datasets import load_dataset # 导入load_dataset函数,用于加载数据集
- import transformers # 导入transformers库,这是一个常用的NLP库
-
- # 导入Trainer和TrainingArguments,分别用于模型的训练和训练参数的设置
- from transformers import Trainer, TrainingArguments
- # 导入AutoTokenizer和AutoModelForCausalLM,分别用于自动化地从预训练模型中获取Tokenizer和模型
- from transformers import AutoTokenizer, AutoModelForCausalLM
- # 导入BitsAndBytesConfig,用于设置模型的量化配置
- from transformers import BitsAndBytesConfig
-
- # 导入一些特定的函数和配置类
- from peft import (
- LoraConfig,
- get_peft_model,
- prepare_model_for_kbit_training,
- set_peft_model_state_dict,
- )
- import torch # 导入PyTorch库,这是一个常用的深度学习库
-
-
- # 定义一些配置信息
- CUTOFF_LEN = 1024
- VAL_SET_SIZE = 2000
- DATA_PATH = "./dataset/Belle_open_source_0.5M.json"
- OUTPUT_DIR = "baichuansft"
- resume_from_checkpoint = "baichuansft"
-
- # 设置设备映射,""表示默认设备,0表示设备编号
- device_map = {"": 0}
- # 使用AutoTokenizer从预训练模型中获取Tokenizer
- tokenizer = AutoTokenizer.from_pretrained("./baichuan-7B",trust_remote_code=True)
- # 使用AutoModelForCausalLM从预训练模型中获取模型,并设置量化配置
- model = AutoModelForCausalLM.from_pretrained("./baichuan-7B",
- trust_remote_code=True,
- quantization_config=BitsAndBytesConfig(
- load_in_4bit=True,
- bnb_4bit_compute_dtype=torch.bfloat16,
- bnb_4bit_use_double_quant=True,
- bnb_4bit_quant_type='nf4'
- ),
- device_map=device_map)
-
- model = prepare_model_for_kbit_training(model) # 准备模型进行kbit训练
-
- # 导入bitsandbytes模块
- import bitsandbytes as bnb
-
- # 定义一个函数,用于找到模型中所有的线性层的名称
- def find_all_linear_names(model):
- cls = bnb.nn.Linear4bit
- lora_module_names = set()
- for name, module in model.named_modules(): # 遍历模型中的所有模块
- if isinstance(module, cls): # 如果模块是线性层
- names = name.split('.')
- lora_module_names.add(names[0] if len(names) == 1 else names[-1]) # 添加到线性层名称集合中
-
- if 'lm_head' in lora_module_names: # 如果'lm_head'在名称集合中,需要移除
- lora_module_names.remove('lm_head')
- return list(lora_module_names) # 返回线性层名称列表
-
- # 获取所有的线性层的名称
- modules = find_all_linear_names(model)
-
- # 设置LoRA配置
- config = LoraConfig(
- r=8,
- lora_alpha=16,
- lora_dropout=0.05,
- bias="none",
- target_modules=modules,
- task_type="CAUSAL_LM",
- )
-
- # 获取用于训练的模型
- model = get_peft_model(model, config)
- tokenizer.pad_token_id = 0 # 设置tokenizer的pad_token_id为0
-
- # 如果有设置从检查点恢复
- if resume_from_checkpoint:
- # 检查可用的权重并加载
- checkpoint_name = os.path.join(
- resume_from_checkpoint, "pytorch_model.bin"
- ) # 完整的检查点
- # 如果完整的检查点不存在,则加载LoRA模型的检查点
- if not os.path.exists(checkpoint_name):
- checkpoint_name = os.path.join(
- resume_from_checkpoint, "adapter_model.bin"
- ) # 仅LoRA模型 - 上面的LoRA配置必须匹配
- resume_from_checkpoint = (
- False # 所以训练器不会尝试加载状态
- )
- if os.path.exists(checkpoint_name):
- print(f"Restarting from {checkpoint_name}")
- adapters_weights = torch.load(checkpoint_name)
- set_peft_model_state_dict(model, adapters_weights) # 设置模型的状态字典
- else:
- print(f"Checkpoint {checkpoint_name} not found")
-
- # 加载数据集
- data = load_dataset("json", data_files=DATA_PATH)
-
- # 定义tokenize函数,用于将输入进行tokenize
- def tokenize(prompt, add_eos_token=True):
- # 这里是tokenize的具体操作
- result = tokenizer(
- prompt,
- truncation=True,
- max_length=CUTOFF_LEN,
- padding=False,
- return_tensors=None,
- )
- # 添加EOS token
- if (
- result["input_ids"][-1] != tokenizer.eos_token_id
- and len(result["input_ids"]) < CUTOFF_LEN
- and add_eos_token
- ):
- result["input_ids"].append(tokenizer.eos_token_id)
- result["attention_mask"].append(1)
-
- if add_eos_token and len(result["input_ids"]) >= CUTOFF_LEN:
- result["input_ids"][CUTOFF_LEN - 1] = tokenizer.eos_token_id
- result["attention_mask"][CUTOFF_LEN - 1] = 1
-
- # 输入和标签都是input_ids
- result["labels"] = result["input_ids"].copy()
-
- return result
-
- # 定义generate_and_tokenize_prompt函数,用于生成并tokenize输入
- def generate_and_tokenize_prompt(data_point):
- instruction = data_point['instruction']
- input_text = data_point["input"]
- input_text = "Human: " + instruction + input_text + "\n\nAssistant: "
- input_text = tokenizer.bos_token + input_text if tokenizer.bos_token != None else input_text
- target_text = data_point["output"] + tokenizer.eos_token
- full_prompt = input_text + target_text
- tokenized_full_prompt = tokenize(full_prompt)
- return tokenized_full_prompt
-
- # 划分训练集和验证集,并进行shuffle和map操作
- if VAL_SET_SIZE > 0:
- train_val = data["train"].train_test_split(
- test_size=VAL_SET_SIZE, shuffle=True, seed=42
- )
- train_data = train_val["train"].shuffle().map(generate_and_tokenize_prompt)
- val_data = train_val["test"].shuffle().map(generate_and_tokenize_prompt)
- else:
- train_data = data['train'].shuffle().map(generate_and_tokenize_prompt)
- val_data = None
-
- # 创建Trainer对象,用于进行训练
- trainer = Trainer(
- model=model,
- train_dataset=train_data,
- eval_dataset=val_data,
- args=TrainingArguments(
- num_train_epochs=1,
- per_device_train_batch_size=1,
- per_device_eval_batch_size=1,
- learning_rate=3e-4,
- gradient_accumulation_steps=4,
- evaluation_strategy="steps" if VAL_SET_SIZE > 0 else "no",
- save_strategy="steps",
- eval_steps=2000 if VAL_SET_SIZE > 0 else None,
- save_steps=2000,
- output_dir=OUTPUT_DIR,
- report_to = "tensorboard",
- save_total_limit=3,
- load_best_model_at_end=True if VAL_SET_SIZE > 0 else False,
- optim="adamw_torch"
- ),
- data_collator=transformers.DataCollatorForSeq2Seq(tokenizer,
- pad_to_multiple_of=8,
- return_tensors="pt",
- padding=True),
- )
-
- # 进行训练
- trainer.train(resume_from_checkpoint=False)
- # 保存预训练模型
- model.save_pretrained(OUTPUT_DIR)
最终,显存占用为7G左右2023年7月11日,百川智能发布Baichuan-13B(这是其GitHub地址)
Baichuan-13B 是继 Baichuan-7B 之后开发的包含 130 亿参数的开源可商用的大规模语言模型,本次发布包含以下两个版本
Baichuan-13B 有如下几个特点:
模型名称 | 隐藏层维度 | 层数 | 注意力头数 | 词表大小 | 总参数量 | 训练数据(tokens) | 位置编码 | 最大长度 |
Baichuan-7B | 4,096 | 32 | 32 | 64,000 | 7,000,559,616 | 1.2 万亿 | RoPE | 4,096 |
Baichuan-13B | 5,120 | 40 | 40 | 64,000 | 13,264,901,120 | 1.4 万亿 | ALiBi | 4,096 |
由于Baichuan-13B-Base、Baichuan-13B-Chat 推理所需的模型权重、源码、配置已发布在 Hugging Face,故可以使用下面命令将模型下载到本地,方便使用时直接加载(/data/sim_chatgpt/)
- git clone https://huggingface.co/baichuan-inc/Baichuan-13B-Base
- git clone https://huggingface.co/baichuan-inc/Baichuan-13B-Chat
至于模型推理,可以直接用 LLaMA-Efficient-Tuning 仓库中的环境
conda activate baichuan-7b
- import torch
- from transformers import AutoModelForCausalLM, AutoTokenizer
- from transformers.generation.utils import GenerationConfig
- tokenizer = AutoTokenizer.from_pretrained("/data/sim_chatgpt/Baichuan-13B-Chat", use_fast=False, trust_remote_code=True)
- model = AutoModelForCausalLM.from_pretrained("/data/sim_chatgpt/Baichuan-13B-Chat", torch_dtype=torch.float16, trust_remote_code=True)
- model = model.quantize(4).cuda()
- model.generation_config = GenerationConfig.from_pretrained("/data/sim_chatgpt/Baichuan-13B-Chat")
- messages = []
- messages.append({"role": "user", "content": "世界上第二高的山峰是哪座"})
- response = model.chat(tokenizer, messages)
- print(response)
本次微调参考:LLaMA-Efficient-Tuning
git clone https://github.com/hiyouga/LLaMA-Efficient-Tuning.git
- # 这里使用和 baichuan-7b 相同的环境
- conda create -n baichuan-7b python=3.10
- conda activate baichuan-7b
- cd LLaMA-Efficient-Tuning
- pip install -r requirements.txt
- CUDA_VISIBLE_DEVICES=0 # 设定CUDA设备ID为0,只使用第一块GPU进行训练
- nohup python src/train_bash.py \ # 使用nohup命令在后台运行训练脚本train_bash.py,保证终端关闭后任务仍在运行
- --do_train \ # 指定执行训练操作
- --model_name_or_path /data/sim_chatgpt/Baichuan-13B-Chat \ # 指定模型的名称或者模型文件的路径
- --template baichuan \ # 模型模板为"baichuan"
- --dataset alpaca_gpt4_zh \ # 使用名为"alpaca_gpt4_zh"的数据集进行训练
- --output_dir baichuan_lora_checkpoint \ # 训练过程中的输出目录
- --max_source_length 256 \ # 输入文本的最大长度
- --max_target_length 512 \ # 输出文本的最大长度
- --per_device_train_batch_size 1 \ # 每个设备的训练批次大小
- --gradient_accumulation_steps 1 \ # 梯度累计步骤,用于模拟更大的批次大小
- --lr_scheduler_type cosine \ # 使用余弦退火策略进行学习率调整
- --logging_steps 10 \ # 每10步记录一次日志
- --save_steps 10000 \ # 每10000步保存一次模型
- --learning_rate 5e-5 \ # 设置学习率
- --num_train_epochs 1.0 \ # 训练1个epoch
- --plot_loss \ # 绘制损失图
- --fp16 \ # 使用半精度浮点数进行训练,可以加速训练并减少内存使用
- --lora_target W_pack \ # LoRA的目标设置
- --lora_rank 8 \ # LoRA的秩设置
- --padding_side right \ # 在序列的右侧进行填充
- --quantization_bit 4 \ # 量化位数设置为4
- >> qlora_log.out 2>&1 & # 将标准输出和错误输出都重定向到qlora_log.out文件中,并在后台运行
23年九月,百川智能发布了Baichuan 2(其GitHub地址、其技术报告地址)的基础模型:baichuan-7B、Baichuan 2-13B,这两个模型都在2.6万亿tokens上进行了训练,下表对比了Baichuan 2与Baichuan 1的各个参数
模型名称 | 隐藏层维度 | 层数 | 注意力头数 | 词表大小 | 训练数据(tokens) | 位置编码 | 最大长度 |
Baichuan-7B | 4,096 | 32 | 32 | 64,000 | 1.2 万亿 | RoPE | 4,096 |
Baichuan-13B | 5,120 | 40 | 40 | 64,000 | 1.4 万亿 | ALiBi | 4,096 |
Baichuan 2-7B | 4096 | 32 | 32 | 125,696 | 2.6万亿 | RoPE | 4096 |
Baichuan 2-13B | 5120 | 40 | 40 | 125,696 | 2.6万亿 | ALiBi | 4096 |
baichuan的训练数据包括一般的互联网网页、书籍、研究论文、代码库等,以构建一个广泛的通用知识体系,其训练语料库的组成如下图所示
接下来
Baichuan 2的模型架构基于流行的Transformer 。但是,作者对其进行了若干修改。
分词器需要平衡两个关键因素:高压缩率以实现高效的推断,以及适当大小的词汇表以确保每个词嵌入的充分训练。
Baichuan 2在其7B版本中采用了旋转位置嵌入 (Rotary Positional Embedding, RoPE)。而在其13B版本中,采用了ALiBi (由Press等人于2021年提出),这与Baichuan第一代的两个不同尺寸的模型保持一致
百川2还引入了对齐程序,产生了两种专门针对对话的模型:baichuan2 7b chat和baichuan2 13b chat。baichuan2的对齐过程和ChatGPT或deep speed chat一样,还是我们熟悉且“热爱”的三阶段训练方式:监督微调(SFT)、训练奖励模型RM、基于人类反馈的强化学习(RLHF)迭代策略
在监督微调阶段,使用人类标注者为从各种数据源收集的prompt进行标注。每个prompt都根据与Claude类似的关键原则被标记为有帮助或无害
为了验证数据质量,我们使用交叉验证(cross-validation)——权威标注者检查特定众包工作组注释的样本批次的质量,过滤低质量数据
至于数据上,SFT阶段总共使用了1万条SFT数据
在获得奖励模型后,使用PPO算法来进一步训练模型。和deepspeed chat一样,使用四个模型:
具体如下图所示
我本想再解析下Baichuan 2关于RLHF的代码实现,但巧的是,和ChatGLM类似,我在Baichuan 2公布的代码中,也没有看到这个RLHF实现的代码(没有像DSC对ChatGPT三阶段训练方式的代码实现,给的一目了然)..
此外,23年9.25日,百川发布了Baichuan2 53B的模型,但因暂未有对应的论文或技术报告发布,故暂不对其做技术解析
至于baichuan2的微调会放在:大模型项目开发线上营
// 待更..
MOSS是复旦大学邱锡鹏团队推出的一个支持中英双语和多种插件的开源对话语言模型,moss-moon系列模型具有160亿参数,在FP16精度下可在单张A100/A800或两张3090显卡运行,在INT4/8精度下可在单张3090显卡运行
其基座语言模型在约七千亿中英文以及代码单词上预训练得到,后续经过对话指令微调、插件增强学习和人类偏好训练具备多轮对话能力及使用多种插件的能力
moss-moon-003-sft
基础上还具备使用搜索引擎、文生图、计算器、解方程等四种插件的能力。moss-moon-003-sft
模型,约占用12GB显存即可进行推理。moss-moon-003-sft
模型,约占用24GB显存即可进行推理。moss-moon-003-sft-plugin
模型,约占用12GB显存即可进行推理。moss-moon-003-sft-plugin
模型,约占用24GB显存即可进行推理。moss-moon-003-sft
收集到的偏好反馈数据上训练得到的偏好模型,将在近期开源。moss-moon-003-sft
基础上经过偏好模型moss-moon-003-pm
训练得到的最终模型,具备更好的事实性和安全性以及更稳定的回复质量,将在近期开源。moss-moon-003-sft-plugin
基础上经过偏好模型moss-moon-003-pm
训练得到的最终模型,具备更强的意图理解能力和插件使用能力,将在近期开源。text-davinci-003
生成的约57万条英文对话和59万条中文对话moss-moon-003-sft
所使用的多轮对话数据,基于MOSS-002内测阶段采集的约10万用户输入数据和gpt-3.5-turbo
构造而成,相比moss-002-sft-data
,moss-003-sft-data
更加符合真实用户意图分布,包含更细粒度的有用性类别标记、更广泛的无害性数据和更长对话轮数,约含110万条对话数据。目前仅开源少量示例数据,完整数据将在近期开源moss-moon-003-sft-plugin
所使用的插件增强的多轮对话数据,包含支持搜索引擎、文生图、计算器、解方程等四个插件在内的约30万条多轮对话数据。目前仅开源少量示例数据,完整数据将在近期开源moss-moon-003-pm
所使用的偏好数据,包含在约18万额外对话上下文数据及使用moss-moon-003-sft
所产生的回复数据上构造得到的偏好对比数据,将在近期开源我司七月杜助教写了一篇部署MOSS的教程,详情请点击:MOSS模型量化版部署过程
// 待更
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。