社区供稿 | Firefly-13B开源,QLoRA+百万数据,单卡可训百亿大模型

07/11 18:00
阅读数 410

前言:欢迎关注我们的中文大语言模型开源项目Firefly(流萤)。目前我们的项目支持对baichuan、ziya、bloom、llama等主流大模型进行指令微调,同时支持全量微调和QLoRA高效微调。我们整理并开源了多个主流的高质量的中英文指令数据集,读者可以快速上手微调自己的大模型。


项目地址:https://github.com/yangjianxin1/Firefly


此前,Firefly项目依次开源了firefly-bloom-1b4、firefly-bloom-2b6、firefly-bloom-7b1和firefly-baichuan-7b等中文大模型,这些模型也见证了我们项目的迭代历程。


此次,我们将开源Firefly项目的第一个百亿参数规模的中英文大模型firefly-ziya-13b,该模型基于ziya-13b的预训练权重,使用百万中英文指令数据进行微调。


接下来的章节主要介绍firefly-ziya-13b的基座模型、训练策略、模型效果等。模型权重和训练数据等,详见文末链接。


01

Ziya模型简介

此次我们开源的firefly-ziya-13b模型,正如其名称所示,我们选择了IDEA团队的Ziya-LLaMA-13B-Pretrain-v1作为基座模型。本章节主要对该基座模型进行介绍。


Ziya-LLaMA-13B-Pretrain-v1 是基于LLaMA的130亿参数大规模预训练模型,针对中文分词优化,并完成了中英文 110B tokens 的增量预训练,进一步提升了中文生成和理解能力。


原始数据包含英文和中文,其中英文数据来自openwebtext、Books、Wikipedia和Code,中文数据来自清洗后的悟道数据集、IDEA自建的中文数据集。在对原始数据进行去重、模型打分、数据分桶、规则过滤、敏感主题过滤和数据评估后,最终得到125B tokens的有效数据。


为了解决LLaMA原生分词对中文编解码效率低下的问题,IDEA在LLaMA词表的基础上增加了7k+个常见中文字,通过和LLaMA原生的词表去重,最终得到一个39410大小的词表,并通过复用Transformers里LlamaTokenizer来实现了这一效果。


以下是Ziya-LLaMA-13B-Pertrain-v1 和原始的LLaMA 模型分别在中英文评测集上的表现。

可以看到,相比于LLaMA模型,Ziya-LLaMA-13B-Pertrain-v1在中文评测集上的0-shot与5-shot效果都更优秀,证明了其在中文能力上有显著的提升。且两者在英文评测集HELM上的效果基本上持平,在部分子任务上互有胜负,表明其英文能力没有受到较大的损失。


除此之外,我们关注到Ziya官方的sft模型在SuperCLUE中文大模型榜单上的表现也不错。

Ziya-LLaMA-13B-Pertrain-v1在增量预训练时,采用的是全量参数训练,而不是LoRA等轻量级微调的方式,理论上来说会比LoRA增量预训练的模型效果更好。


基于上述分析,我们项目选择了Ziya-LLaMA-13B-Pertrain-v1作为13b的基座模型。


02

训练策略

QLoRA是一种可以使用较低成本对大模型进行微调的技术,该方法具有非常不错的效果。QLoRA官方的Guanaco-65b模型在LLM Leaderboard中目前排名第二(注:该模型仅使用了9千多条OASST1的数据进行微调)。

Firefly项目中集成了QLoRA训练流程,并且我们已经多次使用QLoRA训练模型,开源了firefly-bloom-7b1和firefly-baichuan-7b模型,获得了不错的效果。基于上述原因,我们依旧采用QLoRA技术训练firefly-ziya-13b模型。


对于QLoRA的原理和训练流程尚不熟悉的同学,可参考我们的往期文章:

  1. 【QLoRA实战】使用单卡高效微调bloom-7b1,效果惊艳

  2. Firefly | QLoRA+百万数据,多卡高效微调bloom-7b1模型

  3. Firefly|百川baichuan-7B实测,QLoRA+百万指令数据微调


训练数据方面,我们依旧使用moss-003-sft-data数据,并且从BELLE项目的数学数据中,随机采样了5000条数据。合并之后数据量为100万+,训练一个epoch。


训练时,我们将多轮对话拼接成如下格式,然后进行tokenize。

<s>input1</s>target1</s>input2</s>target2</s>...


在计算loss时,我们通过mask的方式,input部分的loss不参与参数更新,只有“target</s>”部分的loss参与参数更新。这种方式充分利用了模型并行计算的优势,训练更加高效,且多轮对话中的每个target部分都参与了训练,训练更充分。否则,就需要把一个n轮对话,拆分成n条数据,且只计算最后一个target的loss,大大降低了训练效率。


loss计算的实现方式可参考以下代码:

https://github.com/yangjianxin1/Firefly/blob/master/component/loss.py#L3


对于QLoRA,除了embedding和lm_head外,我们在所有全连接层都插入adapter,其中lora_rank为64,lora_alpha为16,lora_dropout为0.05。最终参与训练的参数量约为2.5亿

训练超参数如下所示:

max length 1024
lr_scheduler_type constant_with_warmup
batch size 64
lr 1e-4
warmup step 3000
optimizer paged_adamw_32bit
training step 15k


在单张V100上,使用QLoRA技术并且开启gradient_checkpointing后,可以将batch size设为8,长度设为1024,对13b的模型进行训练,所以读者可以低资源复现我们的模型效果。


模型的训练损失的变化趋势如下图所示,训练损失的下降比较平滑,且比我们之前训练baichun-7b和bloom-7b1时的loss下降得更快更低。训完一个epoch之后,loss尚未有收敛的趋势。


03

使用方式

模型的用法非常简单,直接运行下面的脚本即可,我们提供了单轮对话和多轮对话的脚本。该脚本兼容多模型,只需要修改model_name,即可使用我们之前开源的firefly-bloom-7b1和firefly-baichuan-7b模型。


使用firefly-ziya-13b进行单轮对话的方式如下:

from transformers import AutoModelForCausalLM, AutoTokenizerimport torch"""单轮对话,不具有对话历史的记忆功能"""

def main(): # model_name = 'YeungNLP/firefly-baichuan-7b' model_name = 'YeungNLP/firefly-ziya-13b'    # model_name = 'YeungNLP/firefly-bloom-7b1'
max_new_tokens = 500 top_p = 0.9 temperature = 0.35 repetition_penalty = 1.0 device = 'cuda' input_pattern = '<s>{}</s>' model = AutoModelForCausalLM.from_pretrained( model_name, trust_remote_code=True, low_cpu_mem_usage=True, torch_dtype=torch.float16, device_map='auto' ).to(device).eval() tokenizer = AutoTokenizer.from_pretrained( model_name, trust_remote_code=True, # llama不支持fast use_fast=False if model.config.model_type == 'llama' else True ) text = input('User:') while True: text = text.strip() text = input_pattern.format(text) input_ids = tokenizer(text, return_tensors="pt", add_special_tokens=False).input_ids.to(device) with torch.no_grad(): outputs = model.generate( input_ids=input_ids, max_new_tokens=max_new_tokens, do_sample=True, top_p=top_p, temperature=temperature, repetition_penalty=repetition_penalty, eos_token_id=tokenizer.eos_token_id ) outputs = outputs.tolist()[0][len(input_ids[0]):] response = tokenizer.decode(outputs) response = response.strip().replace(text, "").replace('</s>', "").replace('<s>', "").strip() print("Firefly:{}".format(response)) text = input('User:')

if __name__ == '__main__':    main()

使用firefly-ziya-13b进行多轮对话的方式如下:

from transformers import AutoModelForCausalLM, AutoTokenizerimport torch

def main(): # model_name = 'YeungNLP/firefly-baichuan-7b' model_name = 'YeungNLP/firefly-ziya-13b' # model_name = 'YeungNLP/firefly-bloom-7b1'
device = 'cuda' max_new_tokens = 500 # 每轮对话最多生成多少个token history_max_len = 1000 # 模型记忆的最大token长度 top_p = 0.9 temperature = 0.35 repetition_penalty = 1.0
# 加载模型 model = AutoModelForCausalLM.from_pretrained( model_name, trust_remote_code=True, low_cpu_mem_usage=True, torch_dtype=torch.float16, device_map='auto' ).to(device).eval() tokenizer = AutoTokenizer.from_pretrained( model_name, trust_remote_code=True, # llama不支持fast use_fast=False if model.config.model_type == 'llama' else True ) # 记录所有历史记录 history_token_ids = tokenizer('<s>', return_tensors="pt").input_ids
# 开始对话 user_input = input('User:') while True: user_input = '{}</s>'.format(user_input) user_input_ids = tokenizer(user_input, return_tensors="pt", add_special_tokens=False).input_ids history_token_ids = torch.concat((history_token_ids, user_input_ids), dim=1) model_input_ids = history_token_ids[:, -history_max_len:].to(device) with torch.no_grad(): outputs = model.generate( input_ids=model_input_ids, max_new_tokens=max_new_tokens, do_sample=True, top_p=top_p, temperature=temperature, repetition_penalty=repetition_penalty, eos_token_id=tokenizer.eos_token_id ) model_input_ids_len = model_input_ids.size(1) response_ids = outputs[:, model_input_ids_len:] history_token_ids = torch.concat((history_token_ids, response_ids.cpu()), dim=1) response = tokenizer.batch_decode(response_ids) print("Firefly:" + response[0].strip().replace('</s>', "")) user_input = input('User:')

if __name__ == '__main__':    main()


04

模型效果

下面的样例均为firefly-ziya-13b模型所生成,未经修改,可能存在事实性错误,仅供参考。由于图片中的字体过小,建议放大图片后进行阅读。


更好的阅读效果,以及更丰富的生成样例,请查看文末的共享文档链接。


多轮对话

我们测试了模型的多轮对话能力,效果远超出了我们的预期,非常惊艳。模型在上下文理解、指代消歧方面的能力都非常优秀。即使是非常长的对话历史,模型也能正确地理解用户当前对话意图。

对话示例1:


对话示例2:


数学题

尽管我们只在训练语料中加入了5000条数学题的数据,但是模型依然给了我们很大的惊喜。这5000条数据,激活了模型的数学推理能力,在做一些常见的加减乘除数学题时,大多数情况下,模型能够清晰地给出解题步骤,并给出正确的答案。


仅用5000条数学数据激活模型的数学推理能力,这与LIMA和Guanaco,以及近来的研究结论相契合:知识来源于预训练,指令微调更多是与人类指令对齐,少数高质量的指令数据,即可很大程度激活模型的能力


由于topp解码方式中带有一定的随机性,有时可能会采样到错误的解题思路,从而得到错误的结果。在解数学题的时候,可以尝试beam search或者贪婪解码等确定性解码策略。


为了避免数学题在训练集中出现过,下面的测试题目均为我们大开脑洞,随机书写的。


知识库问答

我们从网上找了一些近期的新闻片段,来测试模型的知识库问答能力。模型的表现也相当不错,基本上都能够根据给定的文章内容,生成合适的答案,这在知识库问答场景中有非常大的实用价值。


医疗问答


中英翻译

我们测试了一些常规的中英翻译case,以及将古诗词翻译成英文的能力,表现也不错,翻译结果与古诗词表达的意境相近。


邮件生成


商品文案生成


其他示例


更多生成示例,请扫描下面的文档二维码,查看共享文档的内容:


05

结语

从有限的评测case来看,firefly-ziya-13b具有十分优秀的指令遵从能力,且Ziya-LLaMA-13B-Pretrain-v1+QLoRA的方式,值得在中英文下游任务中进行尝试。


由于尚未在开源测试集或LLM榜单上进行评测,客观的评测结果尚不得知。后续若有时间精力,将会对评测部分的工作进行补充。


后续我们将会在模型量化、推理加速、模型评测等方面对项目进行优化,欢迎大家持续关注Firefly项目:

https://github.com/yangjianxin1/Firefly



本文转载自社区供稿内容,不代表官方立场。了解更多,请关注微信公众号"YeungNLP"了解更多:
如果你有好的文章希望通过我们的平台分享给更多人,请通过这个链接与我们联系: 

https://hf.link/tougao

本文分享自微信公众号 - Hugging Face(gh_504339124f0f)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部