B站大模型指令微调入门实战(完整代码),一键打造你的数字分身

本文主要是介绍B站大模型指令微调入门实战(完整代码),一键打造你的数字分身,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前两天,想导出微信聊天记录,于是搞了个小工具。

感兴趣的小伙伴,可以回看:

  • 微信聊天记录导出为电脑文件实操教程(附代码)

  • 一键获取所有微信聊天记录(附PyQT6入门实战)

拿到这些数据都有什么用?

突发奇想:如果把微信上,所有和我相关的聊天对话提取出来,再结合大语言模型 LLM,是不是就可以打造我的数字分身了?

选择一个基座大模型,通过指令微调的方式,打造个性化AI Bot,不失为一个学习LLM微调的入门级任务。

1. 什么是指令微调

可能有部分小伙伴还不知道什么是指令微调,这里做一些简单科普。如果清楚的话可以跳过。

大模型指令微调(Instruction Tuning)是一种针对大型预训练语言模型的微调技术,其核心目的是增强模型执行特定任务的能力。

常见的微调方式有两种:全量微调 和 增量微调,其中前者需要调整模型全部参数,随着预训练模型规模的不断扩大,全量微调的资源压力将绝大部分开发者和企业拒之门外。

相对而言,增量微调所需的资源压力要少很多,而 LoRA 正是增量微调的典型代表,其优势在于:可以针对不同的下游任务构建小型 LoRA 模块,从而在共享预训练模型参数基础上有效地切换下游任务。

为此,本文将采用 LoRA 对基座大模型进行微调。

2.指令微调实战

参考 & 致谢:https://github.com/datawhalechina/self-llm

2.1 模型选择

下面仅提供单机单卡/单机多卡的运行示例,因此您需要至少一台具有多个 GPU 的机器。

一开始打算微调 GLM4-9B,不过单张16G 显卡无法加载,LoRA 微调需要21G显存,因此至少需要一张 24G 显卡。

为此,只能退而求其次,选择更小的模型,刚好前几天 B 站发布了自研的Index系列模型中的轻量版本,大小只有1.9B,模型参数量更好可以拿来进行入门实战。

模型传送门:https://modelscope.cn/models/IndexTeam/Index-1.9B-Chat

2.2 对话数据准备

指令微调的数据,通常采用 Stanford Alpaca格式,示例如下:

{"instruction" : ...,"input" : ...,"output" : ...},

上一篇: 一键获取所有微信聊天记录(附PyQT6入门实战),自制了一个 微信信息提取 的小工具,可以拿来提取出出所有的聊天记录。

不过,从聊天记录到对话数据,还需要一些逻辑的特殊处理,比如:连续多条对话是否合并,等等。

先看下处理前和处理后的数据格式:
在这里插入图片描述

下面是我这里的处理代码,给到大家做参考:

def message_to_train_data(json_file='messages.json', out_file='messages2.json'):messages = json.loads(open(json_file, 'r', encoding='utf-8').read())# print(len(messages))conversations = []i = 0cur_coveration = []while i < len(messages):while i < len(messages) and messages[i][1] == '我':i += 1if i >= len(messages):breakmessage = messages[i]while i < len(messages) and messages[i][1] != '我' and covert_time2num(messages[i][0]) - covert_time2num(message[0]) <= 60*2:cur_coveration.append(messages[i])i += 1if i >= len(messages):breakif len(cur_coveration) > 0:cur_coveration_len = len(cur_coveration)pre_time = covert_time2num(cur_coveration[-1][0])message = messages[i]cur_time = covert_time2num(message[0])if cur_time - pre_time <= 60*60*6:while i < len(messages) and messages[i][1] == '我' and covert_time2num(messages[i][0]) - covert_time2num(message[0]) <= 60*2:cur_coveration.append(messages[i])i += 1if len(cur_coveration) > cur_coveration_len:conversations.append(cur_coveration)cur_coveration = []# 生成Stanford Alpaca格式对话result = []for coveration in conversations:you_content = '\n'.join([m[2] for m in coveration if m[1] != '我'])me_content = '\n'.join([m[2] for m in coveration if m[1] == '我'])if you_content.strip() and me_content.strip():result.append({"instruction": "你是{猴哥},一个热情、善良的人,后面是来自你朋友的对话,你在理解后认真回答他","input": you_content,"output": me_content, })if len(result) > 0:with open(out_file, 'w', encoding='utf-8') as f:json.dump(result, f, ensure_ascii=False, indent=4)

对于想尽快跑通指令微调流程的小伙伴,也可以采用开源的数据。这里提供 Chat-甄嬛 项目中的数据作为示例。

数据地址:https://github.com/datawhalechina/self-llm/blob/master/dataset/huanhuan.json

[{"instruction": "小姐,别的秀女都在求中选,唯有咱们小姐想被撂牌子,菩萨一定记得真真儿的——","input": "","output": "嘘——都说许愿说破是不灵的。"},
]

2.3 环境准备

在完成数据准备后,你还需要安装一些第三方库,可以使用以下命令:

# 更换 pypi 源加速库的安装
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simplepip 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.19.0
pip install peft==0.10.0
pip install tiktoken==0.7.0
MAX_JOBS=8 pip install flash-attn --no-build-isolation

2.4 模型下载

本次训练采用 B 站自研的Index系列模型中的Index-1.9B-Chat,大小只有1.9B。

模型传送门:https://modelscope.cn/models/IndexTeam/Index-1.9B-Chat

从 model scope 上下载模型有两种方式:

第一种是脚本安装,指定你的本地存放目录cache_dir

#模型下载
from modelscope import snapshot_download
model_dir = snapshot_download('IndexTeam/Index-1.9B-Chat', cache_dir='path/to/Index-1.9B-Chat')

第二种是 git 下载,更方便快捷,不过需要先安装Git LFS(Large File Storage,一个用于Git版本控制的工具,允许管理大型文件):

sudo apt-get install git-lfs
git clone https://www.modelscope.cn/IndexTeam/Index-1.9B-Chat.git

2.3 训练配置

2.3.1 导入必要的包

import os
import torch
import pandas as pd
from datasets import Dataset
from transformers import AutoTokenizer, AutoModelForCausalLM, DataCollatorForSeq2Seq, TrainingArguments, Trainer, GenerationConfig
from peft import LoraConfig, TaskType, get_peft_modelos.environ["CUDA_VISIBLE_DEVICES"] = "0" # 指定使用的GPU ID

2.3.2 训练数据准备

LoRA 训练的数据是需要经过格式化、编码之后再输入给模型进行训练。

为此,需要首先定义一个预处理函数,对每一个样本,编码其输入、输出文本并返回一个编码后的字典:

df_train = pd.read_json('data/train.json')
ds_train = Dataset.from_pandas(df_train)def process_func(example):MAX_LENGTH = 384    # 分词器会将一个中文字切分为多个token,因此需要放开一些最大长度,保证数据的完整性input_ids, attention_mask, labels = [], [], []instruction = tokenizer(f"<unk>system{example['instruction']}reserved_0user{example['input']}reserved_1assistant", add_special_tokens=False)  # add_special_tokens 不在开头加 special_tokensresponse = 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}tokenized_id = ds_train.map(process_func, remove_columns=ds_train.column_names)
print(tokenized_id)

2.3.3 模型准备

指定下载好的模型本地地址,加载 tokenizer 和半精度模型。

model_path = "../models/Index-1.9B-Chat"
tokenizer = AutoTokenizer.from_pretrained(model_path, use_fast=False, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(model_path, \                                           device_map="auto", torch_dtype=torch.bfloat16, trust_remote_code=True)
model.enable_input_require_grads() # 开启梯度检查点时,要执行该方法

2.3.4 LoRA配置

通过 LoraConfig 这个类来配置参数,示例如下:

config = LoraConfig(task_type=TaskType.CAUSAL_LM, target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"], # 需要微调的参数inference_mode=False, # 训练模式r=8, # Lora 秩lora_alpha=32, # Lora alaph,具体作用参见 Lora 原理lora_dropout=0.1# Dropout 比例
)model = get_peft_model(model, config)
print(model.print_trainable_parameters())

2.3.5 训练器配置

通过 TrainingArguments 这个类来完成训练配置,然后调用 Trainer 开始训练。

args = TrainingArguments(output_dir=f"./output/lora-{model_path.split('/')[-1]}",per_device_train_batch_size=4,gradient_accumulation_steps=4,logging_steps=50,num_train_epochs=10,save_steps=1000,learning_rate=1e-4,save_on_each_node=True,gradient_checkpointing=True
)trainer = Trainer(model=model,args=args,train_dataset=tokenized_id,data_collator=DataCollatorForSeq2Seq(tokenizer=tokenizer, padding=True),
)trainer.train()

在 batch_size=4 的情况下,训练只占用了 6G 显存,10个epoch,1700 条数据大概 20min 完成训练。

2.4 推理测试

完成训练后,我们来测试了看看。

加载模型时,只需要指定 LoRA 权重的位置即可。如果要测试原始模型,只需将最后一行代码注释掉即可:

import os
import torch
from transformers import AutoTokenizer, pipeline, AutoModelForCausalLM, AutoTokenizer
from peft import PeftModelos.environ["CUDA_VISIBLE_DEVICES"] = "0" # 指定使用的GPU IDmodel_path = "../models/Index-1.9B-Chat"
lora_path = "output/lora-Index-1.9B-Chat/checkpoint-1000/"tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)# 加载模型
model = AutoModelForCausalLM.from_pretrained(model_path, device_map="auto", torch_dtype=torch.bfloat16, trust_remote_code=True).eval()# 加载lora权重
model = PeftModel.from_pretrained(model, model_id=lora_path)

下面给出一个推理代码的示例:

prompt = "你是谁?"
model_input = [{"role": "system", "content": "假设你是猴哥,请明确这个人设"},{"role": "user", "content": prompt}]
inputs = tokenizer.apply_chat_template(model_input,add_generation_prompt=True,tokenize=True,return_tensors="pt",return_dict=True).to('cuda')# gen_kwargs = {"max_length": 150, "do_sample": True, "top_k": 1, "top_p": 0.9, "temperature": 0.3, "repetition_penalty": 1.1}
gen_kwargs = {"max_new_tokens": 512, "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))

2.5 结果展示

原始模型,推理占用 5203M,加上 LoRA 后占用 5307M,不过发现加载了lora模型后推理速度慢了很多。

因为我发现它会有大量重复的输出,比如下面这个例子,我问他 :最近参加过什么活动么?
在这里插入图片描述
尽管有大量的重复,但是在上面这个回答中,我发现 AI 完全学到了我的聊天风格:文字聊天中,喜欢用空格代替标点符号

不得不说,LoRA 指令微调,还是让模型学到了训练数据中的知识。在下面这个例子中:

在这里插入图片描述
应该说,AI 从我的聊天记录中捕获到的兴趣和关注点还是比较准确的。

写在最后

至此,我们就一起走完了一个大模型指令微调的完整过程。

为了打造一个完美的数字分身,未来可能还需要:

  • 探索更多元的数据,目前只用到了文本对话;
  • 尝试更大的模型和微调参数设置;
  • 结合 RAG 技术,减少幻觉输出。

如果本文对你有帮助,欢迎点赞收藏备用!

我是猴哥,一直在做 AI 领域的研发和探索,会陆续跟大家分享路上的思考和心得。

新朋友欢迎关注 “猴哥的AI知识库” 公众号,下次更新不迷路。

这篇关于B站大模型指令微调入门实战(完整代码),一键打造你的数字分身的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1089200

相关文章

网页解析 lxml 库--实战

lxml库使用流程 lxml 是 Python 的第三方解析库,完全使用 Python 语言编写,它对 XPath表达式提供了良好的支 持,因此能够了高效地解析 HTML/XML 文档。本节讲解如何通过 lxml 库解析 HTML 文档。 pip install lxml lxm| 库提供了一个 etree 模块,该模块专门用来解析 HTML/XML 文档,下面来介绍一下 lxml 库

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

大模型研发全揭秘:客服工单数据标注的完整攻略

在人工智能(AI)领域,数据标注是模型训练过程中至关重要的一步。无论你是新手还是有经验的从业者,掌握数据标注的技术细节和常见问题的解决方案都能为你的AI项目增添不少价值。在电信运营商的客服系统中,工单数据是客户问题和解决方案的重要记录。通过对这些工单数据进行有效标注,不仅能够帮助提升客服自动化系统的智能化水平,还能优化客户服务流程,提高客户满意度。本文将详细介绍如何在电信运营商客服工单的背景下进行

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

从去中心化到智能化:Web3如何与AI共同塑造数字生态

在数字时代的演进中,Web3和人工智能(AI)正成为塑造未来互联网的两大核心力量。Web3的去中心化理念与AI的智能化技术,正相互交织,共同推动数字生态的变革。本文将探讨Web3与AI的融合如何改变数字世界,并展望这一新兴组合如何重塑我们的在线体验。 Web3的去中心化愿景 Web3代表了互联网的第三代发展,它基于去中心化的区块链技术,旨在创建一个开放、透明且用户主导的数字生态。不同于传统

AI一键生成 PPT

AI一键生成 PPT 操作步骤 作为一名打工人,是不是经常需要制作各种PPT来分享我的生活和想法。但是,你们知道,有时候灵感来了,时间却不够用了!😩直到我发现了Kimi AI——一个能够自动生成PPT的神奇助手!🌟 什么是Kimi? 一款月之暗面科技有限公司开发的AI办公工具,帮助用户快速生成高质量的演示文稿。 无论你是职场人士、学生还是教师,Kimi都能够为你的办公文

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

Andrej Karpathy最新采访:认知核心模型10亿参数就够了,AI会打破教育不公的僵局

夕小瑶科技说 原创  作者 | 海野 AI圈子的红人,AI大神Andrej Karpathy,曾是OpenAI联合创始人之一,特斯拉AI总监。上一次的动态是官宣创办一家名为 Eureka Labs 的人工智能+教育公司 ,宣布将长期致力于AI原生教育。 近日,Andrej Karpathy接受了No Priors(投资博客)的采访,与硅谷知名投资人 Sara Guo 和 Elad G

活用c4d官方开发文档查询代码

当你问AI助手比如豆包,如何用python禁止掉xpresso标签时候,它会提示到 这时候要用到两个东西。https://developers.maxon.net/论坛搜索和开发文档 比如这里我就在官方找到正确的id描述 然后我就把参数标签换过来

usaco 1.2 Name That Number(数字字母转化)

巧妙的利用code[b[0]-'A'] 将字符ABC...Z转换为数字 需要注意的是重新开一个数组 c [ ] 存储字符串 应人为的在末尾附上 ‘ \ 0 ’ 详见代码: /*ID: who jayLANG: C++TASK: namenum*/#include<stdio.h>#include<string.h>int main(){FILE *fin = fopen (