大模型历险记-微调

部署完了接下来尝试微调的工作

一.准备训练的语料数据

BelleGroup 是一个由中国深圳大学(SZU)的研究团队发起的开源项目,里面有不少中文的语料数据集,这次选用的就是其中一个有50w条中文数据的数据集

数据集的结构

1.下载

用wget下载这个json文件,记得cd到你需要保存的目录下面,或者在命令行里加上你的目标路径

wget https://huggingface.co/datasets/BelleGroup/train_0.5M_CN/main/Belle_open_source_0.5M.json

2.拆分测试集

新建一个文件:split_json.py,然后把下面的代码粘贴进去 

import random,json
 
def write_txt(file_path,datas):
    with open(file_path,"w",encoding="utf8") as f:
        for d in datas:
            f.write(json.dumps(d,ensure_ascii=False)+"\n")
        f.close()
 
with open("Belle_open_source_0.5M.json","r",encoding="utf8") as f:
    lines=f.readlines()
    #拼接数据
    changed_data=[]
    for l in lines:
        l=json.loads(l)
        changed_data.append({"text":"### Human: "+l["instruction"]+" ### Assistant: "+l["output"]})
 
    #从拼好后的数据中,随机选出1000条,作为训练数据
    #为了演示使用,我们只用1000条,生产环境至少要使用全部50w条
    r_changed_data=random.sample(changed_data, 1000)
 
    #写到json中
    write_txt("Belle_open_source_0.5M_changed_test.json",r_changed_data)

我这里直接把数据和split_json.py存放在同一个目录下了,记得修改自己的路径

然后运行这个文件,生成Belle_open_source_0.5M_changed_test.json

二.进行微调训练

1.在服务器上创建jupyter notebook

这一步如果是租的服务器应该挺简单,可以直接用提供的接口

如果是像我这样用实验室服务器的小穷鬼

先在远程服务器启动jupyter notebook,没装的先install一下

这里很幽默的一点是实验室的服务器直接运行jupyter notebook是无法运行的,必须要从python -m notebook的jupyter notebook接口进入才可以

原因是我的虚拟环境里有,但是整个账户的环境变量里没设定好,多人多个不同的虚拟环境,比较混乱吧。尝试想修改一下虚拟环境,但是进去一片屎山,算了~

启动以后会显示jupyter notebook的前端端口号,比如我这里是8890

然后启动ssh隧道

ssh -L notebook的端口:localhost:notebook的端口 用户名@服务器ip -p 服务器端口

接下来打开网址

又遇到问题了

不知道谁给服务器的notebook设定了password,且没有token

参考了csdn上很多解决的办法都不太能成功

包括修改密码、取消密码登录、删除token等

最后用重新生成config的办法简单粗暴的完成了。。。。

好了现在终于进入服务器的notebook了

接下来把我的虚拟环境加入到ipykernel里面

python -m ipykernel install --user --name=xx --display-name="Python (xx)"

能在右上角看见自己的虚拟环境就是kernel搞成功了

2.微调的代码

这里的代码参考了其他大佬的csdn

①装库

!pip install -q huggingface_hub
!pip install -q -U trl transformers accelerate peft
!pip install -q -U datasets bitsandbytes einops wandb

②登陆hugging face的notebook

from huggingface_hub import notebook_login
notebook_login()

https://huggingface.co/settings/tokens的token加进入

(需要服务器能上hugging face)

然后我们实验室的就不能上哈哈。。

方法一,可以尝试启动学术加速

参考博客:学术资源加速-CSDN博客

*账户没有管理员权限,写不了etc下的内容,pass

方法二,在本地重新创一个进行训练部分

*配了半天环境,本地配的乱七八糟,pass

方法三,回到第一行,租一个云服务器,

*舍不得花钱,pass

眼看我要彻底失败了,

然后我就在想,为什么要连huggingface,这段代码的作用是什么

wandb的作用是实时可视化记录loss数据

huggingface呢?查看源码好像也没用到这部分的东西

这个可能主要是方便访问huggingface hub上的私有模型或者其他受保护的一些资源

但是我这个模型,就在本地啊?数据也在本地,包括transformers accelerate peft,这些我都install上了

理论上来说我并不需要这个login吧?

于是我就头贴直接往下运行了

事实证明,确实能继续训练昂。

继续,

③配置wandb

注册,并在Weights & Biases复制出自己的key

import wandb
wandb.init()

输入进去

④导入相关包和数据集

from datasets import load_dataset
import torch,einops
from transformers import AutoModelForCausalLM, BitsAndBytesConfig, AutoTokenizer, TrainingArguments
from peft import LoraConfig
from trl import SFTTrainer

dataset = load_dataset("json",data_files="Belle_open_source_0.5M_changed_test.json",split="train")

⑤配置模型

base_model_name ="/model/FlagAlpha_Llama2-Chinese-7b-Chat"
bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,#在4bit上,进行量化
    bnb_4bit_use_double_quant=True,# 嵌套量化,每个参数可以多节省0.4位
    bnb_4bit_quant_type="nf4",#NF4(normalized float)
    bnb_4bit_compute_dtype=torch.float16,
)

⑥配置GPU

device_map = {"": 0}
#有多个gpu时,为:device_map = {"": [0,1,2,3……]}

⑦加载模型

base_model = AutoModelForCausalLM.from_pretrained(
    base_model_name,#本地模型名称
    quantization_config=bnb_config,#上面本地模型的配置
    device_map=device_map,#使用GPU的编号
    trust_remote_code=True,
    use_auth_token=True
)
base_model.config.use_cache = False
base_model.config.pretraining_tp = 1

⑧配置QLoRA

peft_config = LoraConfig(
    lora_alpha=16,
    lora_dropout=0.1,
    r=64,
    bias="none",
    task_type="CAUSAL_LM",
)

⑨token分词器

tokenizer = AutoTokenizer.from_pretrained(base_model_name, trust_remote_code=True)
tokenizer.pad_token = tokenizer.eos_token

⑩配置训练参数

output_dir = "./results"
training_args = TrainingArguments(
    report_to="wandb",
    output_dir=output_dir,#训练后输出目录
    per_device_train_batch_size=4,#每个GPU的批处理数据量
    gradient_accumulation_steps=4,#在执行反向传播/更新过程之前,要累积其梯度的更新步骤数
    learning_rate=2e-4,#超参、初始学习率。太大模型不稳定,太小则模型不能收敛
    logging_steps=10,#两个日志记录之间的更新步骤数
    max_steps=100#要执行的训练步骤总数
)
max_seq_length = 512
#TrainingArguments 的参数详解:https://blog.csdn.net/qq_33293040/article/details/117376382
 
trainer = SFTTrainer(
    model=base_model,
    train_dataset=dataset,
    peft_config=peft_config,
    dataset_text_field="text",
    max_seq_length=max_seq_length,
    tokenizer=tokenizer,
    args=training_args,
)

11、开始训练

trainer.train()

顺利的话,可以用wandb看见训练的loss,前面的max_step是训练的总论次,logging_step是能看见的loss的间隔

不顺利的话。。

你就会像我一样,获得两种错误

错误一:OutOfMemory

这个报错很好理解,GPU部署在0上,内存不够,因为服务器还有其他同学在用。

遇到这个问题,可以等别人不做实验了再尝试,或者调整一下LoRA的参数,batch和反馈深度等

具体参数的含义,后面再学吧。

尝试参数修改,还是内存不够,又不想等别人,怎么办呢,我有另一个想法

查看nvidia-smi,GPU2、3、4其实没什么人用,我想部署到2上

device_map = {"": 2}

于是我尝试修改⑥,

但是这样我以为是在GPU:2上部署,但是并不是呢,是部署在0,1,2上的意思

一时之间我居然想不到怎么单独部署在GPU2上

ps,注释的这种写多个GPU的方式语法错误

接着我遇到了错误二:ValueError: You can't train a model that has been loaded in 8-bit precision on multiple devices.

尝试部署多GPU居然也会报错

我把这段报错搜索栏一下发现,仓库里也有人提交了这个问题:

https://github.com/huggingface/accelerate/issues/1515

peft遇到在最新版本的 transformers 库中,无法将以 8-bit 精度加载的模型在多 GPU 上分布

开发者 younesbelkada 回应,他解释称原生管道并行(Naive Pipeline Parallelism, NPP)不应该在分布式设置下使用,因为 NPP 本质上是顺序的,理应仅通过单一 Python 进程运行

接着我又用我蹩脚的英文一直看几个人的对话

诶给我找到一个,device_map="auto".这个好啊,自动帮我找剩余内存最大的GPU

重启尝试了一下,果然可以运行

*作者的回复里应该是有更好的用多GPU的办法的,有看懂的小伙伴可以分享一下

Anyway,开始运行了

训练结束

(前面参数写的是step=100,在减少内存消耗的时候我把这里改成10了,效果肯定没100的好,但是我的目标只是把整个部署的流程顺一遍,所以这样做了

12、保存训练模型

import os
output_dir = os.path.join(output_dir, "final_checkpoint")
trainer.model.save_pretrained(output_dir)

save完成

*safetensors 是一种新的张量存储格式,旨在安全、快速地存储和加载大规模的机器学习模型参数。与传统的 PyTorch .pt.bin 文件格式相比,safetensors 提供了一些显著的优势:

主要特点

  1. 安全性safetensors 格式通过设计避免了执行任意代码的风险,确保了文件的安全性。这对于共享和分发模型文件尤其重要。
  2. 速度:这种格式经过优化,可以快速地存储和加载张量数据。这对于大规模模型来说,加载时间的减少非常显著。
  3. 兼容性safetensors 与现有的 PyTorch 等框架兼容,支持直接加载到这些框架中。

这个就是存储下来的最优模型

嗯,一开始以为没有存下来呢,之前的项目都是存xxx.pt的,时代果然在进化,学习永远在路上昂

三、模型合并

 恭喜恭喜,很快就能over了

1、新建一个merge_model.py的文件,把下面的代码粘贴进去:

from peft import PeftModel
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
import os

# 设置原来本地模型的地址
model_name_or_path = '/home/camp/wsqllm/model/Llama2-Chinese-7b-Chat'
# 设置微调获得模型的地址
adapter_name_or_path = '/home/camp/wsqllm/results/final_checkpoint'
# 设置合并后模型的导出地址
save_path = '/home/camp/wsqllm/model/newmodel'

# 确保保存路径存在
os.makedirs(save_path, exist_ok=True)

tokenizer = AutoTokenizer.from_pretrained(
    model_name_or_path,
    trust_remote_code=True
)
model = AutoModelForCausalLM.from_pretrained(
    model_name_or_path,
    trust_remote_code=True,
    low_cpu_mem_usage=True,
    torch_dtype=torch.float16,
    device_map='auto'
)
print("Load model success")

model = PeftModel.from_pretrained(model, adapter_name_or_path)
print("Load adapter success")

model = model.merge_and_unload()
print("Merge success")

# 更新生成配置
if model.generation_config.do_sample is False:
    if hasattr(model.generation_config, 'temperature'):
        model.generation_config.temperature = None
    if hasattr(model.generation_config, 'top_p'):
        model.generation_config.top_p = None

tokenizer.save_pretrained(save_path)
model.save_pretrained(save_path)

print(f"Model and tokenizer saved to {save_path}")

这个文件在把之前原始模型参数和微调后的参数进行合并

四、gradio可视化

 和上一篇一样,打开gradio的可视化前端

python gradio_demo.py --base_model /home/camp/wsqllm/model/newmodel --tokenizer_path /home/camp/wsqllm/model/newmodel --gpus 4

部署在几请根据自己的来,本人一开始默认复制了大佬的指令,gpu0喜提。。

建立ssh隧道,这里不再次重复了,看gradio的端口号是什么然后修改之前的那个就可以

当然,这里的微调,其实可视化的时候也看不太出来是不是真的是微调成功的,还是说原模型语料库里面可能就已经调教完成了,选了一个test里面的语句尝试了一下

好消息,和测试集的语料回答的一样,坏消息,英文回答的

可能是因为prompt里面有英文二字吧

比较好能观察的方法是自己设定一个训练的语料库,比如写一些特定领域的数据集之类的,之后也可以在网上搜索一下有无此类的公开数据集

maybe

明天继续langchain

赶紧实操完然后看面经然后找实习啊啊啊

  • 31
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值