【实战教程】使用Unsloth微调Llama3-Chinese-8B-Instruct:中文开源大模型 ...

打印 上一主题 下一主题

主题 869|帖子 869|积分 2607

微调Llama3-Chinese-8B-Instruct

   微调是指在大规模预练习的基础模型上,使用特定领域或使命数据集进行少量迭代练习,以调解模型参数,提拔其在特定使命上的表现。这种方法可以充分利用预练习模型的广泛知识,同时针对特定应用进行优化,达到更精准高效的效果。
  Llama-3-Chinese-8B-Instruct

   Llama-2已经表现的很精彩了,但其仅使用了2万亿Token进行练习。相比之下,Llama-3使用了高达15万亿Token进行练习,这必将大幅提拔实在力,令人高度期待。
    Llama-3-Chinese-Instruct是基于Meta Llama-3的中文开源大模型,其在原版Llama-3的基础上使用了大规模中文数据进行增量预练习,并且使用精选指令数据进行精调,进一步提拔了中文基础语义和指令理解能力,相比二代相关模型获得了显著性能提拔。
  GitHub:https://github.com/ymcui/Chinese-LLaMA-Alpaca-3
Unsloth

   Unsloth是一个开源的大模型练习加快项目,可以显著提拔大模型的练习速度(提高2-5 倍),淘汰显存占用(最大淘汰80%)
  特点如下:
  1. 所有内核均采用OpenAI 的Triton语言编写
  2. 模型训练的精度损失为零
  3. 支持绝大多数主流的GPU设备
  4. 支持对LoRA和QLoRA的训练加速和高效显存管理
  5. 支持Flash Attention加速
  6. 开源训练速度最高达5倍,Unsloth Pro最高达30倍的训练速度
  7. Unsloth与HuggingFace生态兼容,可以很容易将其与transformers、peft、trl等代码库进行结合
复制代码
GitHub:https://github.com/unslothai/unsloth
文档:https://github.com/unslothai/unsloth/wiki

环境设置

创建虚拟环境
  1. conda create --name unsloth_env python=3.10
  2. conda activate unsloth_env
复制代码
安装相关依赖
  1. !pip install "unsloth[colab-new] @ git+https://github.com/unslothai/unsloth.git"
  2. !pip install --no-deps "xformers<0.0.26" trl peft accelerate bitsandbytes
  3. !pip install modelscope
复制代码
下载预练习模型

支持的预置4位量化模型,可实现4倍更快的下载速度和无OOM。更多模型请查看https://huggingface.co/unsloth
  1. fourbit_models = [
  2.     "unsloth/mistral-7b-bnb-4bit",
  3.     "unsloth/mistral-7b-instruct-v0.2-bnb-4bit",
  4.     "unsloth/llama-2-7b-bnb-4bit",
  5.     "unsloth/gemma-7b-bnb-4bit",
  6.     "unsloth/gemma-7b-it-bnb-4bit", # Gemma 7b的Instruct版本
  7.     "unsloth/gemma-2b-bnb-4bit",
  8.     "unsloth/gemma-2b-it-bnb-4bit", # Gemma 2b的Instruct版本
  9.     "unsloth/llama-3-8b-bnb-4bit", # 15万亿令牌的Llama-3
  10. ]
复制代码
这里不使用预置4位量化模型,使用modelscope下载Llama3-Chinese-8B-Instruct中文开源大模型
  1. from modelscope import snapshot_download
  2. model_dir = snapshot_download('FlagAlpha/Llama3-Chinese-8B-Instruct',cache_dir="/root/models")
复制代码
加载model、tokenizer

  1. from unsloth import FastLanguageModel
  2. import torch
  3. model, tokenizer = FastLanguageModel.from_pretrained(
  4.     model_name = "/root/models/Llama3-Chinese-8B-Instruct", # 模型路径
  5.     max_seq_length = 2048, # 可以设置为任何值内部做了自适应处理
  6.     dtype = torch.float16, # 数据类型使用float16
  7.     load_in_4bit = True, # 使用4bit量化来减少内存使用
  8. )
复制代码
设置LoRA练习参数

LoRA (Low-Rank Adaptation)是一种大语言模型的低阶适配器技术,可在模型微调过程中,只更新整个模型参数的1%到10%左右,而不是全部参数。通过这种方式实现有效的模型微调和优化,提高了模型在特定使命上的性能。
  1. model = FastLanguageModel.get_peft_model(
  2.     model,
  3.     r = 16, # 选择任何大于0的数字!建议使用8、16、32、64、128
  4.     target_modules = ["q_proj", "k_proj", "v_proj", "o_proj",
  5.                       "gate_proj", "up_proj", "down_proj",],
  6.     lora_alpha = 16,
  7.     lora_dropout = 0,  # 支持任何值,但等于0时经过优化
  8.     bias = "none",    # 支持任何值,但等于"none"时经过优化
  9.     # [NEW] "unsloth" 使用的VRAM减少30%,适用于2倍更大的批处理大小!
  10.     use_gradient_checkpointing = "unsloth", # True或"unsloth"适用于非常长的上下文
  11.     random_state = 3407,
  12.     use_rslora = False,  # 支持排名稳定的LoRA
  13.     loftq_config = None, # 和LoftQ
  14. )
复制代码
准备数据集

   准备数据集实在就是指令集构建,LLM的微调一般指指令微调过程。所谓指令微调,就是使用指定的微调数据格式、情势。
    练习目标是让模型具有理解并遵循用户指令的能力。因此在指令集构建时,应该针对目标使命,针对性的构建使命指令集。
    这里使用alpaca格式的数据集,格式情势如下:
  1. [
  2.         {
  3.           "instruction": "用户指令(必填)",
  4.           "input": "用户输入(选填)",
  5.           "output": "模型回答(必填)",
  6.         },
  7.     "system": "系统提示词(选填)",
  8.     "history": [
  9.       ["第一轮指令(选填)", "第一轮回答(选填)"],
  10.       ["第二轮指令(选填)", "第二轮回答(选填)"]
  11.     ]
  12. ]
复制代码
  1. instruction:用户指令,要求AI执行的任务或问题
  2. input:用户输入,是完成用户指令所必须的输入内容,就是执行指令所需的具体信息或上下文
  3. output:模型回答,根据给定的指令和输入生成答案
复制代码
这里根据企业私有文档数据,生成相关格式的练习数据集,大概格式如下:
  1. [
  2.   {
  3.     "instruction": "内退条件是什么?",
  4.     "input": "",
  5.     "output": "内退条件包括与公司签订正式劳动合同并连续工作满20年及以上,以及距离法定退休年龄不足5年。特殊工种符合国家相关规定可提前退休的也可在退休前5年内提出内退申请。"
  6.   },
  7. ]
复制代码
数据处理

定义对数据处理的函数方法
  1. alpaca_prompt = """下面是一项描述任务的说明,配有提供进一步背景信息的输入。写出一个适当完成请求的回应。
  2. ### Instruction:
  3. {}
  4. ### Input:
  5. {}
  6. ### Response:
  7. {}"""
  8. EOS_TOKEN = tokenizer.eos_token # Must add EOS_TOKEN
  9. def formatting_prompts_func(examples):
  10.     instructions = examples["instruction"]
  11.     inputs       = examples["input"]
  12.     outputs      = examples["output"]
  13.     texts = []
  14.     for instruction, input, output in zip(instructions, inputs, outputs):
  15.         # Must add EOS_TOKEN, otherwise your generation will go on forever!
  16.         text = alpaca_prompt.format(instruction, input, output) + EOS_TOKEN
  17.         texts.append(text)
  18.     return { "text" : texts, }
  19. pass
复制代码
加载数据集并进行映射处理操纵
  1. from datasets import load_dataset
  2. dataset = load_dataset("json", data_files="./train.json", split = "train")
  3. dataset = dataset.map(formatting_prompts_func, batched = True,)
复制代码
  1. print(dataset[0])
复制代码
经处理后的一条数据格式如下:
  1. {'output': '输出内容',
  2. 'input': '',
  3. 'instruction': '指令内容',
  4. 'text': '下面是一项描述任务的说明,配有提供进一步背景信息的输入。写出一个适当完成请求的回应。\n\n### Instruction:\n指令内容?\n\n### Input:\n\n\n### Response:\n输出内容。<|end_of_text|>'
  5. }
复制代码
练习超参数配置

  1. from transformers import TrainingArguments
  2. training_args  = TrainingArguments(
  3.         output_dir = "models/lora/llama", # 输出目录
  4.         per_device_train_batch_size = 2, # 每个设备的训练批量大小
  5.         gradient_accumulation_steps = 4, # 梯度累积步数
  6.         warmup_steps = 5,
  7.         max_steps = 60, # 最大训练步数,测试时设置
  8.         # num_train_epochs= 5, # 训练轮数   
  9.         logging_steps = 10,  # 日志记录频率
  10.         save_strategy = "steps", # 模型保存策略
  11.         save_steps = 100, # 模型保存步数
  12.         learning_rate = 2e-4, # 学习率
  13.         fp16 = not torch.cuda.is_bf16_supported(), # 是否使用float16训练
  14.         bf16 = torch.cuda.is_bf16_supported(), # 是否使用bfloat16训练
  15.         optim = "adamw_8bit",  # 优化器
  16.         weight_decay = 0.01,  # 正则化技术,通过在损失函数中添加一个正则化项来减小权重的大小
  17.         lr_scheduler_type = "linear",  # 学习率衰减策略
  18.         seed = 3407, # 随机种子
  19.     )
复制代码
开始练习

  1. trainer = SFTTrainer(
  2.         model=model, # 模型
  3.         tokenizer=tokenizer, # 分词器
  4.         args=training_args, # 训练参数
  5.         train_dataset=dataset, # 训练数据集
  6.         dataset_text_field="text", # 数据集文本字段名称
  7.         max_seq_length=2048, # 最大序列长度
  8.         dataset_num_proc=2, # 数据集处理进程数
  9.         packing=False, # 可以让短序列的训练速度提高5倍
  10. )
复制代码
显示当前内存状态
  1. # 当前GPU信息
  2. gpu_stats = torch.cuda.get_device_properties(0)
  3. # 当前模型内存占用
  4. start_gpu_memory = round(torch.cuda.max_memory_reserved() / 1024 / 1024 / 1024, 3)
  5. # GPU最大内存
  6. max_memory = round(gpu_stats.total_memory / 1024 / 1024 / 1024, 3)
  7. print(f"GPU = {gpu_stats.name}. Max memory = {max_memory} GB.")
  8. print(f"{start_gpu_memory} GB of memory reserved.")
复制代码
可以看出当前模型占用5.633G显存

执行练习
  1. trainer_stats = trainer.train()
复制代码

显示最终内存和时间统计数据
  1. # 计算总的GPU使用内存(单位:GB)
  2. used_memory = round(torch.cuda.max_memory_reserved() / 1024 / 1024 / 1024, 3)
  3. # 计算LoRA模型使用的GPU内存(单位:GB)
  4. used_memory_for_lora = round(used_memory - start_gpu_memory, 3)
  5. # 计算总的GPU内存使用百分比
  6. used_percentage = round(used_memory / max_memory * 100, 3)
  7. # 计算LoRA模型的GPU内存使用百分比
  8. lora_percentage = round(used_memory_for_lora / max_memory * 100, 3)
  9. print(f"{trainer_stats.metrics['train_runtime']} seconds used for training.")
  10. print(f"{round(trainer_stats.metrics['train_runtime'] / 60, 2)} minutes used for training.")
  11. print(f"Peak reserved memory = {used_memory} GB.")
  12. print(f"Peak reserved memory for training = {used_memory_for_lora} GB.")
  13. print(f"Peak reserved memory % of max memory = {used_percentage} %.")
  14. print(f"Peak reserved memory for training % of max memory = {lora_percentage} %.")
复制代码
可以看出模型练习时显存增加了0.732G

模型推理

  1. FastLanguageModel.for_inference(model) # 启用原生推理速度快2倍
  2. inputs = tokenizer(
  3. [
  4.     alpaca_prompt.format(
  5.         "内退条件是什么?", # instruction
  6.         "", # input
  7.         "", # output
  8.     )
  9. ], return_tensors = "pt").to("cuda")
  10. outputs = model.generate(**inputs, max_new_tokens = 64, use_cache = True)
  11. tokenizer.batch_decode(outputs)
复制代码
可以看出模型回答跟练习数据集中的数据意思根本一致。

生存LoRA模型

留意:这仅生存 LoRA 适配器,而不是完整模型
  1. lora_model="models/llama_lora"
  2. model.save_pretrained(lora_model)
  3. tokenizer.save_pretrained(lora_model)
  4. # 保存到huggingface
  5. # model.push_to_hub("your_name/lora_model", token = "...")
  6. # tokenizer.push_to_hub("your_name/lora_model", token = "...")
复制代码

加载模型

   留意:从新加载模型将额外占用显存,若GPU显存不足,需关闭、清除先前加载、练习模型的内存占用
  加载刚生存的LoRA适配器用于推断,他将主动加载整个模型及LoRA适配器。adapter_config.json定义了完整模型的路径。
  1.     import torch
  2.     from unsloth import FastLanguageModel
  3.     model, tokenizer = FastLanguageModel.from_pretrained(
  4.         model_name = "models/llama_lora",
  5.         max_seq_length = 2048,
  6.         dtype = torch.float16,
  7.         load_in_4bit = True,
  8.     )
  9.     FastLanguageModel.for_inference(model)
复制代码
执行推理
  1. alpaca_prompt = """
  2. 下面是一项描述任务的说明,配有提供进一步背景信息的输入。写出一个适当完成请求的回应。
  3. ### Instruction:
  4. {}
  5. ### Input:
  6. {}
  7. ### Response:
  8. {}
  9. """
  10. inputs = tokenizer(
  11. [
  12.     alpaca_prompt.format(
  13.         "内退条件是什么?", # instruction
  14.         "", # input
  15.         "", # output
  16.     )
  17. ], return_tensors = "pt").to("cuda")
  18. outputs = model.generate(**inputs, max_new_tokens = 64, use_cache = True)
  19. tokenizer.batch_decode(outputs)
复制代码
生存完整模型

  1. # 合并到16bit 保存到本地 OR huggingface
  2. model.save_pretrained_merged("models/Llama3", tokenizer, save_method = "merged_16bit",)
  3. model.push_to_hub_merged("hf/model", tokenizer, save_method = "merged_16bit", token = "")
  4. # 合并到4bit 保存到本地 OR huggingface
  5. model.save_pretrained_merged("models/Llama3", tokenizer, save_method = "merged_4bit",)
  6. model.push_to_hub_merged("hf/model", tokenizer, save_method = "merged_4bit", token = "")
复制代码
这里合并到16bit

生存为GGUF格式

将模型生存为GGUF格式
  1. # 保存到 16bit GGUF 体积大
  2. model.save_pretrained_gguf("model", tokenizer, quantization_method = "f16")
  3. model.push_to_hub_gguf("hf/model", tokenizer, quantization_method = "f16", token = "")
  4. # 保存到 8bit Q8_0 体积适中
  5. model.save_pretrained_gguf("model", tokenizer,)
  6. model.push_to_hub_gguf("hf/model", tokenizer, token = "")
  7. # 保存到 q4_k_m GGUF 体积小
  8. model.save_pretrained_gguf("model", tokenizer, quantization_method = "q4_k_m")
  9. model.push_to_hub_gguf("hf/model", tokenizer, quantization_method = "q4_k_m", token = "")
复制代码
在执行转换过程中遇到如下问题
  1. RuntimeError: Unsloth: Quantization failed! You might have to compile llama.cpp yourself, then run this again.
  2. You do not need to close this Python program. Run the following commands in a new terminal:
  3. You must run this in the same folder as you're saving your model.
  4. git clone --recursive https://github.com/ggerganov/llama.cpp
  5. cd llama.cpp && make clean && make all -j
  6. Once that's done, redo the quantization.
复制代码
安装提示编译llama.cpp
  1. git clone --recursive https://github.com/ggerganov/llama.cpp
  2. cd llama.cpp && make clean && make all -j
复制代码
发现任然出现上述错误,截止现在,经查证,官方项目确实存在该Bug。因此使用手动方式进行转换操纵。
参考:llama.cpp 转换、量化和合并
  1. !python ./models/llama.cpp/convert.py ./models/Llama3 --outtype f16   --vocab-type bpe --outfile ./models/Llama3-FP16.gguf
复制代码

AI大模型学习福利

作为一名热心肠的互联网老兵,我决定把宝贵的AI知识分享给大家。 至于能学习到多少就看你的学习毅力和能力了 。我已将紧张的AI大模型资料包罗AI大模型入门学习头脑导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。

大模型&AI产物经理怎样学习

求大家的点赞和收藏,我花2万买的大模型学习资料免费共享给你们,来看看有哪些东西。
1.学习路线图


第一阶段: 从大模型体系计划入手,教学大模型的主要方法;
第二阶段: 在通过大模型提示词工程从Prompts角度入手更好发挥模型的作用;
第三阶段: 大模型平台应用开辟借助阿里云PAI平台构建电商领域虚拟试衣体系;
第四阶段: 大模型知识库应用开辟以LangChain框架为例,构建物盛行业咨询智能问答体系;
第五阶段: 大模型微调开辟借助以大康健、新零售、新媒体领域构建适合当前领域大模型;
第六阶段: 以SD多模态大模型为主,搭建了文生图小程序案例;
第七阶段: 以大模型平台应用与开辟为主,通过星火大模型,文心大模型等成熟大模型构建大模型行业应用。
2.视频教程

网上虽然也有很多的学习资源,但根本上都残破不全的,这是我自己整理的大模型视频教程,上面路线图的每一个知识点,我都有配套的视频教学。


(都打包成一块的了,不能逐一展开,统共300多集)
因篇幅有限,仅展示部分资料,必要点击下方图片前往获取
3.技术文档和电子书

这里主要整理了大模型相关PDF书籍、行业陈诉、文档,有几百本,都是现在行业最新的。

4.LLM面试题和面经合集

这里主要整理了行业现在最新的大模型面试题和各种大厂offer面经合集。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

天空闲话

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表