社区供稿 | QLoRA + 百万数据,多卡高效微调 BLOOM-7b1 模型

06/30 21:00
阅读数 1.7K
前言 :欢迎关注我们的中文大语言模型开源项目 Firefly(流萤)。正如我们的项目名称一样,希望本项目能够像流萤一般发出淡淡微光,为中文大语言模型社区尽绵薄之力 ,促进中文大语言模型社区的发展。

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

在文章【QLoRA实战】使用单卡高效微调bloom-7b1,效果惊艳中,我们介绍了QLoRA高效微调大模型的基本原理。并且使用Firefly(流萤)项目的训练代码,在单张GPU上,使用29万条多轮指令数据,对bloom-7b1进行了初步实验,取得了不错的效果,验证了QLoRA方法的有效性。


为了进一步探索QLoRA方法的有效性,在本文中,我们使用百万级别多轮指令数据对bloom-7b1进行指令微调,对模型进行更充分的训练。最终我们得到firefly-bloom-7b1-qlora-sft模型,从我们的测评case来看,具有非常优秀的表现。我们也开源了模型权重,欢迎大家使用。


由于精力有限,目前尚未在开源测试集或LLM榜单上进行测评。非常欢迎有兴趣的同学进行测评。


本文主要内容如下:

  1. 优化QLoRA训练流程,支持多卡NF4并行训练,提升训练速度。

  2. 整理并开源三份百万级别的中英文多轮指令数据集。

  3. 开源基于QLoRA和百万数据微调的firefly-bloom-7b1-qlora-sft模型权重。

  4. 测试firefly-bloom-7b1-qlora-sft模型的效果。


论文地址:

https://arxiv.org/pdf/2305.14314.pdf

项目地址:

https://github.com/yangjianxin1/Firefly

模型权重:

https://hf.co/YeungNLP/firefly-bloom-7b1-qlora-sft


01

数据分享

我们整理并开源了三份中英文指令微调数据集,并且统一了数据格式,以适应Firefly项目的训练代码。


数据集信息如下表所示:

数据集
介绍

firefly-train-1.1M

我们收集了23种常见的中文NLP任务的数据,并且构造了许多与中华文化相关的数据,如对联、作诗、文言文翻译、散文、金庸小说等。对于每个任务,由人工书写若干种指令模板,保证数据的高质量与丰富度,数据量为115万。


数据集链接:

https://huggingface.co/datasets/YeungNLP/firefly-train-1.1M

moss-003-sft-data

由复旦大学MOSS团队开源的中英文多轮对话数据,包含100+数据。


数据集接:

https://huggingface.co/datasets/YeungNLP/moss-003-sft-data

ultrachat

由清华大学开源的英文多轮对话数据,包含140+数据。


数据集接:

https://huggingface.co/datasets/YeungNLP/ultrachat


数据为jsonl格式,每一行的数据格式如下,其中conversation字段是必需的。

{    "conversation_id":44,    "category":"Brainstorming",    "conversation":[        {            "human":"怎么能够有效地提升个人能力?",            "assistant":"提升个人能力可以通过以下几个方面实现:\n\n1. 学习新知识和技能:阅读相关书籍、参加课程和工作坊、观看教育视频等学习方式,可以帮助你掌握新的知识和技能,从而提升个人能力。\n\n2. 持续练习和实践:不断地练习和实践所学的知识和技能,可以帮助你更好地掌握和应用这些技能,提高你的表现和效率。\n\n3. 寻求反馈和改进:寻求他人的反馈和建议可以帮助你发现自己的不足之处,进一步完善和改进自己的能力。\n\n4. 建立良好的工作和生活习惯:建立良好的工作和生活习惯,如合理安排时间、注意健康、保持积极态度等,可以提高你的工作效率和生活品质,从而提升个人能力。\n\n5. 社交和交流:与他人交流和合作,可以帮助你更好地了解他人的观点和经验,从而提升自己的能力和见识。"        },        {            "human":"对于初学者来说,如何快速掌握编程技能?",            "assistant":"以下是几个建议,可以帮助初学者快速掌握编程技能:\n\n1. 学习基础知识:建议先学习编程语言的基础知识和语法,例如变量、数据类型、控制结构、函数等。可以通过在线课程、编程书籍、视频教程等方式学习。\n\n2. 实践编程:进行实际的编程练习是非常重要的,可以通过参加编程挑战、编写小项目等方式来进行实践。\n\n3. 寻求反馈:可以寻求他人的反馈和建议,例如在线编程社区、编程导师等,他们可以帮助你发现问题并及时纠正。\n\n4. 阅读优秀代码:阅读和分析优秀的代码可以帮助你学习其他程序员的编程思路和技巧,从而提高自己的编程能力。\n\n5. 不断学习和更新:编程技术和工具在不断更新和发展,因此需要不断学习新的知识和技能,例如学习新的编程语言、框架、工具等,以跟上技术的发展和变化。\n\n总之,学习编程需要耐心和坚持,并且需要不断地实践和学习。通过以上建议,可以帮助初学者快速掌握编程技能。"        },        {            "human":"除了参加编程挑战和阅读代码,还有哪些方式可以提升自己的编程能力呢?",            "assistant":"除了参加编程挑战和阅读代码,还有以下几种方式可以提升自己的编程能力:\n\n1. 参与开源项目:加入一个开源项目,可以让你了解到实际的软件开发流程和团队合作方式,并且可以与其他开发人员合作,学习和分享编程技巧和经验。\n\n2. 进行代码重构:对已经编写的代码进行重构,可以帮助你了解更好的编程实践和设计模式,从而提高代码的可读性和可维护性。\n\n3. 练习算法和数据结构:算法和数据结构是编程中的重要基础,练习算法和数据结构可以帮助你了解不同的数据结构和算法,从而更好地解决实际问题。\n\n4. 学习软件工程和架构:学习软件工程和架构的知识,可以帮助你了解如何设计和构建可靠、可扩展和可维护的软件系统。\n\n5. 参加编程社区和论坛:参加编程社区和论坛可以让你与其他开发人员分享和讨论编程经验和技巧,了解最新的技术趋势和解决方案。\n\n总之,提升编程能力需要不断地学习和实践,并与其他开发人员和编程专家进行交流和合作,以获取更多的经验和知识。"        },        {            "human":"你有没有推荐的编程挑战网站?",            "assistant":"当然,以下是一些值得推荐的编程挑战网站:\n\n1. HackerRank:HackerRank 是一个全球领先的技术招聘平台,提供数百种编程挑战和比赛,包括算法、数据结构、数学、SQL、AI 等多个技术领域。\n\n2. LeetCode:LeetCode 是一个在线编程学习和实践平台,提供数千道编程题目,主要涵盖算法和数据结构。\n\n3. Codeforces:Codeforces 是一个全球知名的编程竞赛网站,提供数百个比赛和练习题目,主要涵盖算法和数据结构。\n\n4. Codewars:Codewars 是一个在线编程学习和实践平台,提供数千道编程题目,主要涵盖算法、数据结构、面向对象编程等多个技术领域。\n\n5. Project Euler:Project Euler 是一个非营利性的在线编程挑战平台,提供一系列数学和计算机科学问题,需要用编程语言进行解答。\n\n以上这些编程挑战网站可以帮助你提高编程能力,并且可以与其他编程爱好者进行交流和竞争。"        },        {            "human":"在学习编程的过程中,如何避免陷入困境和放弃呢?",            "assistant":"学习编程是一个需要长期坚持和不断学习的过程,以下是一些避免陷入困境和放弃的方法:\n\n1. 制定合理的学习计划:制定合理的学习计划,包括学习时间、学习内容、目标等,可以帮助你更好地管理时间和精力,避免学习过程中的松懈和分心。\n\n2. 寻找合适的学习资源:选择适合自己的学习资源,例如在线课程、编程书籍、视频教程等,可以让你更好地了解和掌握编程知识和技能。\n\n3. 寻求帮助和支持:在学习过程中,遇到问题和困难是很正常的,可以寻求他人的帮助和支持,例如参加编程社区、找到编程导师等。\n\n4. 进行实践和项目:实践和项目是学习编程的重要组成部分,可以帮助你更好地了解和掌握编程技能,同时也可以提高学习的兴趣和动力。\n\n5. 坚持并保持兴趣:坚持学习和保持兴趣是学习编程的关键。可以通过参加编程社区、参加编程竞赛、与其他编程爱好者交流等方式来保持兴趣和动力。\n\n总之,学习编程需要耐心和坚持,并需要不断学习和实践。通过以上方法可以帮助你避免陷入困境和放弃。"        }    ],}


02

训练策略

我们将moss-003-sft-data和ultrachat两份数据进行混合,得到约240万条训练数据。依然使用我们的老朋友bloom-7b1作为基座模型

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

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

除了embedding和lm_head,我们在所有全连接层处都插入adapter,其中lora_rank为64,lora_alpha为16,lora_dropout为0.05。最终参与训练的参数量约为1.2亿,超过一个bert-base模型的参数量。


对于一条多轮指令数据,训练时只计算target部分的损失函


训练超参数如下所示:

max length
1024
lr_scheduler_type constant
batch size
64
lr
2e-4
warmup step
3000
optimizer
paged_adamw_32bit
training step
16k

使用firefly-bloom-7b1-qlora-sft进行单轮对话的方式如下:

from peft import PeftModelfrom transformers import AutoModelForCausalLM, AutoTokenizerimport torch
model_name = 'bigscience/bloom-7b1'adapter_name = 'YeungNLP/firefly-bloom-7b1-qlora-sft'max_new_tokens = 300top_p = 0.9temperature = 0.35repetition_penalty = 1.0device = 'cuda'input_pattern = '<s>{}</s>'
model = AutoModelForCausalLM.from_pretrained( model_name, low_cpu_mem_usage=True, torch_dtype=torch.float16, device_map='auto')model = PeftModel.from_pretrained(model, adapter_name)model.eval()model = model.to(device)tokenizer = AutoTokenizer.from_pretrained(model_name)
text = input('User:')while True: text = input_pattern.format(text) input_ids = tokenizer(text, return_tensors="pt").input_ids input_ids = input_ids.to(device)    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 ) rets = tokenizer.batch_decode(outputs) output = rets[0].strip().replace(text, "").replace('</s>', "") print("Firefly:{}".format(output)) text = input('User:')

使用firefly-bloom-7b1-qlora-sft进行多轮对话的方式如下:

from transformers import AutoModelForCausalLM, AutoTokenizerfrom peft import PeftModelimport torch
device = 'cuda'model_name = 'bigscience/bloom-7b1'adapter_name = 'YeungNLP/firefly-bloom-7b1-qlora-sft'max_new_tokens = 500top_p = 0.9temperature = 0.35repetition_penalty = 1.0
tokenizer = AutoTokenizer.from_pretrained(model_name)model = AutoModelForCausalLM.from_pretrained( model_name, low_cpu_mem_usage=True, torch_dtype=torch.float16, device_map='auto')model = PeftModel.from_pretrained(model, adapter_name)model.eval()model = model.to(device)
# 记录所有历史记录history_token_ids = tokenizer('<s>', return_tensors="pt").input_ids# 输入模型的最大长度history_max_len = 1000
user_input = input('User:')while True: user_input = '{}</s>'.format(user_input) user_input_ids = tokenizer(user_input, return_tensors="pt").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) 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:')

03

模型效果

下面的样例均为firefly-bloom-7b1-qlora-sft模型所生成,未经修改,仅供参考。由于图片中的字体过小,建议放大图片后进行阅读。


知识库问答

我们从网上随机复制了一些文本片段,测试模型的知识库问答能力,模型的表现可谓相当惊艳。模型基本上都能够根据给定的文章内容,生成合适的答案,这在知识库问答场景中有非常大的实用价值。


古诗词

我们也测试了模型在古诗词方面的能力,模型基本能够把古诗词正确背诵出来。


中英翻译

我们首先测试了一些常规的中英翻译的case。然后突发奇想,测试模型将古诗词翻译成英文的能力,表现也不错,翻译结果与古诗词表达的意境相近。


其他示例


04

结语

在本文中,为了进一步探索QLoRA的方法效果,我们使用QLoRA+百万级别的指令数据对bloom-7b1模型进行指令微调。并且添加了对QLoRA多卡并行训练的支持,提高了训练效率。


从一些评测case来看,QLoRA具有非常优秀的效果,是一种非常值得尝试的高效和低成本的大模型微调手段。


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

 

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

https://huggingface.link/tougao

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

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