系列篇章💥
No. | 文章 |
---|---|
1 | 【GLM-4部署实战】GLM-4-9B-Chat模型本地部署实践指南 |
2 | 【GLM-4部署实战】GLM-4-9B-Chat模型之对话机器人部署测试 |
3 | 【GLM-4部署实战】GLM-4-9B-Chat模型之vLLM部署推理实践 |
4 | 【GLM-4微调实战】GLM-4-9B-Chat模型之Lora微调实战 |
目录
- 系列篇章💥
- 引言
- 一、环境准备
- 二、安装依赖
- 三、模型下载
- 四、数据集准备
- 五、加载数据
- 六、数据处理
- 七、创建模型
- 八、Lora微调配置
- 九、配置训练参数
- 十、开始Trainer训练
- 十一、保存训练模型
- 十二、模型合并
- 十三、模型推理
- 结语
引言
在人工智能的广阔天地里,深度学习模型的微调技术扮演着至关重要的角色。它使我们能够根据特定任务的需求,对预训练模型进行精细调整,从而获得更优的性能表现。本文将深入探讨如何针对GLM-4-9B-Chat模型实施Lora微调,期待在特定领域中实现更高的准确度和效率。
一、环境准备
在开始我们的微调之旅之前,首先需要搭建一个合适的开发环境。我们推荐在AutoDL平台上租用一台配备24G显存的显卡机器,例如NVIDIA 4090,以满足模型训练的硬件需求。选择镜像时,推荐使用PyTorch-2.1.0-3.10(ubuntu22.04)-12.1,这将为我们提供一个稳定且功能丰富的开发环境。
完成机器租用后,打开JupyterLab并启动终端,开始环境配置。这包括安装必要的软件包和工具,确保我们的开发环境能够支持后续的模型微调工作。
二、安装依赖
在环境配置阶段,我们需要确保所有依赖项都已正确安装。以下是详细的步骤:
- 升级pip:使用以下命令确保pip工具是最新版本:
python -m pip install --upgrade pip
2.更换pypi源:为了加快库的安装速度,我们推荐使用清华大学的TUNA镜像站。通过以下命令更换默认的pypi源:
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
3.安装依赖包:接下来,安装modelscope、transformers、peft等必要的Python库。使用以下命令完成安装:
pip install modelscope==1.9.5
pip install "transformers>=4.40.0"
pip install streamlit==1.24.0
pip install sentencepiece==0.1.99
pip install accelerate==0.29.3
pip install datasets==2.13.0
pip install peft==0.10.0
pip install tiktoken==0.7.0MAX_JOBS=8 pip install flash-attn --no-build-isolation
安装完成如下:
三、模型下载
使用 modelscope 中的 snapshot_download 函数下载模型。第一个参数为模型名称,参数 cache_dir 用于指定模型的下载路径。
import torch
from modelscope import snapshot_download, AutoModel, AutoTokenizer
import os
model_dir = snapshot_download('ZhipuAI/glm-4-9b-chat', cache_dir='/root/autodl-tmp', revision='master')
在 /root/autodl-tmp 路径下新建 download-model.py 文件,并在其中输入以上内容:
运行 python /root/autodl-tmp/download-model.py
执行下载。需注意,模型大小约为 18 GB,下载模型大概需要 10 - 20 分钟,请耐心等待。
四、数据集准备
微调大型语言模型(LLM)通常涉及指令微调,这是一种特定的数据准备和训练过程。在指令微调中,数据集由一系列包含指令、输入和输出的条目组成,例如:
{"instruction": "回答以下用户问题,仅输出答案。","input": "1+1等于几?","output": "2"
}
在这个例子中,instruction
是给予模型的任务指令,明确告知模型需要完成的具体任务;input
是为了完成任务所需的用户提问或相关信息;而 output
则是模型应产生的预期回答。
我们的目标是训练模型,使其能够准确理解并遵循用户的指令。因此,在构建指令集时,必须针对特定的应用目标精心设计。例如,如果我们的目标是创建一个能够模仿特定对话风格的个性化LLM,我们就需要构建与之相应的指令集。
以使用开源的甄嬛传对话数据集为例,如果我们希望模型能够模拟甄嬛的对话风格,我们可以构造如下形式的指令:
[{"instruction": "小姐,别的秀女都在求中选,唯有咱们小姐想被撂牌子,菩萨一定记得真真儿的——","input": "","output": "嘘——都说许愿说破是不灵的。"},{"instruction": "这个温太医啊,也是古怪,谁不知太医不得皇命不能为皇族以外的人请脉诊病,他倒好,十天半月便往咱们府里跑。","input": "","output": "你们俩话太多了,我该和温太医要一剂药,好好治治你们。"},{"instruction": "嬛妹妹,刚刚我去府上请脉,听甄伯母说你来这里进香了。","input": "","output": "出来走走,也是散心。"
},
。。。。。。
]
在此示例中,我们省略了 input
字段,因为模型的回答是基于预设的角色背景知识,而非用户的直接提问。通过这种方式,我们可以训练模型学习并模仿特定角色的语言风格和对话模式,从而在实际应用中提供更加个性化和情景化的交互体验。
五、加载数据
# 导入依赖包
from datasets import Dataset
import pandas as pd
from transformers import AutoTokenizer, AutoModelForCausalLM, DataCollatorForSeq2Seq, TrainingArguments, Trainer, GenerationConfig# 加载数据集 、将JSON文件转换为CSV文件
df = pd.read_json('./huanhuan.json')
ds = Dataset.from_pandas(df)# 查看前三条数据集
ds[:3]
输出:
{'instruction': ['小姐,别的秀女都在求中选,唯有咱们小姐想被撂牌子,菩萨一定记得真真儿的——','这个温太医啊,也是古怪,谁不知太医不得皇命不能为皇族以外的人请脉诊病,他倒好,十天半月便往咱们府里跑。','嬛妹妹,刚刚我去府上请脉,听甄伯母说你来这里进香了。'],'input': ['', '', ''],'output': ['嘘——都说许愿说破是不灵的。', '你们俩话太多了,我该和温太医要一剂药,好好治治你们。', '出来走走,也是散心。']}
六、数据处理
Lora 训练的数据是需要经过格式化、编码之后再输入给模型进行训练的,如果是熟悉 Pytorch 模型训练流程的同学会知道,我们一般需要将输入文本编码为 input_ids,将输出文本编码为 labels,编码之后的结果都是多维的向量。我们首先定义一个预处理函数,这个函数用于对每一个样本,编码其输入、输出文本并返回一个编码后的字典:
1、设置填充符
tokenizer = AutoTokenizer.from_pretrained('/root/autodl-tmp/glm-4-9b-chat/ZhipuAI/glm-4-9b-chat', use_fast=False, trust_remote_code=True)
# 将tokenizer的pad_token设置为eos_token,这样在进行填充时,会使用eos_token作为填充符号
tokenizer.pad_token = tokenizer.eos_token
输出:
2、定义数据集处理函数
def process_func(example):MAX_LENGTH = 384input_ids, attention_mask, labels = [], [], []instruction = tokenizer((f"[gMASK]<sop><|system|>\n假设你是皇帝身边的女人--甄嬛。<|user|>\n"f"{example['instruction']+example['input']}<|assistant|>\n").strip(), add_special_tokens=False)response = tokenizer(f"{example['output']}", add_special_tokens=False)input_ids = instruction["input_ids"] + response["input_ids"] + [tokenizer.pad_token_id]attention_mask = instruction["attention_mask"] + response["attention_mask"] + [1] # 因为eos token咱们也是要关注的所以 补充为1labels = [-100] * len(instruction["input_ids"]) + response["input_ids"] + [tokenizer.pad_token_id] if len(input_ids) > MAX_LENGTH: # 做一个截断input_ids = input_ids[:MAX_LENGTH]attention_mask = attention_mask[:MAX_LENGTH]labels = labels[:MAX_LENGTH]return {"input_ids": input_ids,"attention_mask": attention_mask,"labels": labels
}
GLM4-9B-chat 采用的Prompt Template格式如下:
[gMASK]<sop><|system|>
假设你是皇帝身边的女人--甄嬛。<|user|>
小姐,别的秀女都在求中选,唯有咱们小姐想被撂牌子,菩萨一定记得真真儿的——<|assistant|>
嘘——都说许愿说破是不灵的。<|endoftext|>
3、数据处理
tokenized_id = ds.map(process_func, remove_columns=ds.column_names)
tokenized_id
输出:
4、查看数据结构
print(tokenizer.decode(tokenized_id[0]['input_ids']))
print(tokenized_id[0]['input_ids'])
print(tokenizer.decode([151331, 151333, 151335]))
print(tokenizer.encode('[gMASK]<sop><|system|>', add_special_tokens=False))
输出:
5、查看labels
tokenizer.decode(list(filter(lambda x: x != -100, tokenized_id[1]["labels"])))
输出:
'你们俩话太多了,我该和温太医要一剂药,好好治治你们。 <|endoftext|>'
七、创建模型
import torchmodel = AutoModelForCausalLM.from_pretrained('/root/autodl-tmp/ZhipuAI/glm-4-9b-chat', device_map="auto",torch_dtype=torch.bfloat16, trust_remote_code=True)
model
输出:
model.enable_input_require_grads() # 开启梯度检查点时,要执行该方法
查看精度
model.dtype
输出:
torch.bfloat16
八、Lora微调配置
Lora是一种微调技术,通过在模型中引入可训练的参数来增强模型的表达能力。为了适应特定的微调需求,我们将对Lora进行详细的配置。
在LoraConfig
类中,可以设置多个参数,主要参数如下:
task_type
:指定模型的类型。target_modules
:指定需要训练的模型层的名称,主要是attention部分的层。不同的模型对应的层的名称可能会有所不同,可以接受数组、字符串或正则表达式作为输入。r
:Lora的秩,具体可以参考Lora的原理。lora_alpha
:Lora的alaph参数,具体作用可以参考Lora的原理。
1)关于Lora的缩放,它并不是指r
(秩),而是lora_alpha/r
。在这个LoraConfig
中,缩放被设置为4倍。
from peft import LoraConfig, TaskType, get_peft_modelconfig = LoraConfig(task_type=TaskType.CAUSAL_LM, target_modules=["query_key_value", "dense", "dense_h_to_4h", "dense_4h_to_h"], # 现存问题只微调部分演示即可inference_mode=False, # 训练模式r=8, # Lora 秩lora_alpha=32, # Lora alaph,具体作用参见 Lora 原理lora_dropout=0.1# Dropout 比例
)
config
输出:
LoraConfig(peft_type=<PeftType.LORA: 'LORA'>, auto_mapping=None, base_model_name_or_path=None, revision=None, task_type=<TaskType.CAUSAL_LM: 'CAUSAL_LM'>, inference_mode=False, r=8, target_modules={'dense', 'dense_h_to_4h', 'query_key_value', 'dense_4h_to_h'}, lora_alpha=32, lora_dropout=0.1, fan_in_fan_out=False, bias='none', use_rslora=False, modules_to_save=None, init_lora_weights=True, layers_to_transform=None, layers_pattern=None, rank_pattern={}, alpha_pattern={}, megatron_config=None, megatron_core='megatron.core', loftq_config={}, use_dora=False, layer_replication=None)
2)加载配置
model = get_peft_model(model, config)
config
查看配置如下:
LoraConfig(peft_type=<PeftType.LORA: 'LORA'>, auto_mapping=None, base_model_name_or_path='/root/autodl-tmp/ZhipuAI/glm-4-9b-chat', revision=None, task_type=<TaskType.CAUSAL_LM: 'CAUSAL_LM'>, inference_mode=False, r=8, target_modules={'dense', 'dense_h_to_4h', 'query_key_value', 'dense_4h_to_h'}, lora_alpha=32, lora_dropout=0.1, fan_in_fan_out=False, bias='none', use_rslora=False, modules_to_save=None, init_lora_weights=True, layers_to_transform=None, layers_pattern=None, rank_pattern={}, alpha_pattern={}, megatron_config=None, megatron_core='megatron.core', loftq_config={}, use_dora=False, layer_replication=None)
3)查看可训练的参数
model.print_trainable_parameters()
输出:
trainable params: 21,176,320 || all params: 9,421,127,680 || trainable%: 0.22477479044207158
九、配置训练参数
TrainingArguments
类的源代码详细阐述了每个参数的具体功能,大家有兴趣可以自行查阅。这里,我将简要介绍几个常用的参数:
output_dir
:指定模型训练后的输出目录。per_device_train_batch_size
:设置每个设备的批次大小(即 batch size)。gradient_accumulation_steps
:用于梯度累积的步数。如果显存资源有限,可以减小 batch size 并增加梯度累积的步数。logging_steps
:指定在训练过程中每隔多少步输出一次日志信息。num_train_epochs
:训练的总轮数(即 epoch 数)。gradient_checkpointing
:启用或禁用梯度检查点。一旦启用,必须调用model.enable_input_require_grads()
方法。具体原理可进一步探索,此处不展开详述。
这些参数可以根据实际需求进行调整,以优化训练过程。
# 定义训练参数
args = TrainingArguments(output_dir="./output/GLM4", # 输出目录,用于保存模型和日志per_device_train_batch_size=1, # 每个设备的训练批次大小gradient_accumulation_steps=8, # 梯度累积步数,用于模拟更大的批次大小logging_steps=50, # 每隔多少步记录一次日志num_train_epochs=2, # 训练的总轮数save_steps=100, # 每隔多少步保存一次模型learning_rate=1e-5, # 学习率save_on_each_node=True, # 是否在每个节点上保存模型gradient_checkpointing=True # 是否启用梯度检查点(减少内存占用)
)
十、开始Trainer训练
使用Trainer类来启动模型的训练过程。我们将监控训练进度,并根据需要调整训练策略。
# 创建训练器实例
trainer = Trainer(model=model, # 指定要训练的模型args=args, # 传入训练参数train_dataset=tokenized_id, # 提供训练数据集,这里假设已经进行了分词和编码处理data_collator=DataCollatorForSeq2Seq(tokenizer=tokenizer, padding=True), # 使用适当的数据整理器,这里针对序列到序列任务进行填充
)# 开始训练
trainer.train()
训练结果:
十一、保存训练模型
将训练好的模型和对应的分词器保存到指定的路径
# 设置模型保存路径
peft_model_id = "./GLM4_lora"# 保存训练好的模型到指定路径
trainer.model.save_pretrained(peft_model_id)# 保存对应的分词器到指定路径
tokenizer.save_pretrained(peft_model_id)
输出:
十二、模型合并
在微调完成后,我们需要合并微调后的参数到原始模型中,以便于部署和推理。
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
from peft import PeftModelmode_path = '/root/autodl-tmp/ZhipuAI/glm-4-9b-chat'
lora_path = './GLM4_lora'# 加载tokenizer
tokenizer = AutoTokenizer.from_pretrained(mode_path, trust_remote_code=True)# 加载模型
model = AutoModelForCausalLM.from_pretrained(mode_path, device_map="auto",torch_dtype=torch.bfloat16, trust_remote_code=True).eval()# 加载lora权重
model = PeftModel.from_pretrained(model, model_id=lora_path)
执行结果:
十三、模型推理
最后,我们将使用微调后的模型进行推理,验证微调效果,并根据实际表现进一步优化模型。
prompt = "你是谁?"
inputs = tokenizer.apply_chat_template([{"role": "user", "content": "假设你是皇帝身边的女人--甄嬛。"},{"role": "user", "content": prompt}],add_generation_prompt=True,tokenize=True,return_tensors="pt",return_dict=True).to('cuda')gen_kwargs = {"max_length": 2500, "do_sample": True, "top_k": 1}
with torch.no_grad():outputs = model.generate(**inputs, **gen_kwargs)outputs = outputs[:, inputs['input_ids'].shape[1]:]print(tokenizer.decode(outputs[0], skip_special_tokens=True))
输出:
我是甄嬛,家父是大理寺少卿甄远道。
测试截图如下:
结语
经过本文的实战演练,我们不仅精湛掌握了GLM-4-9B-Chat模型的Lora微调技术,还深入理解了微调过程中的每一个关键步骤。我们期望大家能够将本文的知识应用到自己的项目中,以不断提升模型的性能。随着技术的不断进步,我们期待与大家共同探索更多高效、创新的模型微调方法,以推动人工智能技术的进一步发展。
🎯🔖更多专栏系列文章:AI大模型提示工程完全指南、AI大模型探索之路(零基础入门)、AI大模型预训练微调进阶、AI大模型开源精选实践、AI大模型RAG应用探索实践🔥🔥🔥 其他专栏可以查看博客主页📑
😎 作者介绍:我是寻道AI小兵,资深程序老猿,从业10年+、互联网系统架构师,目前专注于AIGC的探索。
📖 技术交流:欢迎关注【小兵的AI视界】公众号或扫描下方👇二维码,加入技术交流群,开启编程探索之旅。
💘精心准备📚500本编程经典书籍、💎AI专业教程,以及高效AI工具。等你加入,与我们一同成长,共铸辉煌未来。
如果文章内容对您有所触动,别忘了点赞、⭐关注,收藏!加入我,让我们携手同行AI的探索之旅,一起开启智能时代的大门!