用Python微调DeepSeek R1
📖阅读时长:15分钟
🕙发布时间:2025-02-05
近日热文:全网最全的神经网络数学原理(代码和公式)直观解释
欢迎关注知乎和公众号的专栏内容
LLM架构专栏
知乎LLM专栏
知乎【柏企】
公众号【柏企科技说】【柏企阅文】
微调前的准备工作
在正式开始微调大语言模型之前,我们先来了解一下技术前提条件和设置要求。
Python库和框架
微调大语言模型需要用到以下Python库和框架:
- unsloth:这个库可太厉害了,它能让像Llama - 3、Mistral、Phi - 4和Gemma 2这些大语言模型的微调速度提高2倍,内存使用减少70%,而且不会降低模型的准确性!
- torch:它是基于PyTorch进行深度学习的基础构建模块。提供了强大的张量库,和NumPy有些类似,但它有个巨大的优势,就是支持GPU加速,这在处理大语言模型时至关重要。
- transformers:这是一个功能强大且广受欢迎的自然语言处理(NLP)开源库。它提供了易于使用的接口,让我们可以轻松访问各种最先进的预训练模型。毕竟,预训练模型是任何微调任务的基础,这个库可帮了大忙。
- trl:Python中的trl包是一个专门用于基于Transformer模型进行强化学习(RL)的库。它建立在Hugging Face transformers库之上,充分利用其优势,让基于Transformer的强化学习变得更易上手、更高效。
计算要求
微调模型是一种让大语言模型的回答更结构化、更具领域针对性的技术,而且不需要对所有参数进行全面训练。不过,对于大多数普通计算机硬件来说,微调大型大语言模型仍然不太现实。因为所有可训练参数以及实际的大语言模型都要存储在GPU的虚拟随机存取存储器(vRAM)中,而大语言模型庞大的规模成了实现这一目标的主要障碍。
所以,在本文中,我们将微调一个相对较小的大语言模型——DeepSeek - R1 - Distill,它有47.4亿个参数。这个模型至少需要8 - 12GB的vRAM
数据准备策略
微调大语言模型需要结构化且特定任务的数据。数据准备策略有很多,比如从社交媒体平台、网站、书籍或研究论文中收集数据。在本文中,我们会使用datasets
库来加载Hugging Face Hub上的数据。具体来说,我们会使用Hugging Face上的yahma/alpaca - cleaned
数据集
Python实现步骤
安装所需包
使用谷歌Colab进行微调任务有个很大的好处,就是大多数包已经预装好了。我们只需要安装一个包,那就是unsloth
。安装包的代码如下:
!pip install unsloth
初始化模型和分词器
我们将使用unsloth
包来加载预训练模型,因为它提供了许多有用的技术,能帮助我们更快地下载和微调大语言模型。加载模型和分词器的代码如下:
from unsloth import FastLanguageModel
model, tokenizer = FastLanguageModel.from_pretrained(
model_name = "unsloth/DeepSeek-R1-Distill-Llama-8B-unsloth-bnb-4bit",
max_seq_length = 2048,
dtype = None,
load_in_4bit = True
)
这里解释一下代码:
- 我们指定了
model_name
为unsloth/DeepSeek - R1 - Distill - Llama - 8B - unsloth - bnb - 4bit
,这是为了访问预训练的DeepSeek - R1 - Distill模型。 - 将
max_seq_length
定义为2048,这设置了模型可以处理的输入序列的最大长度。合理设置这个值,有助于优化内存使用和处理速度。 dtype
设置为None
,这样能让模型根据可用硬件自动适配数据类型,我们就不用手动去检查和指定数据类型啦,unsloth
会帮我们处理好一切。load_in_4bit
这个参数可以增强推理能力并减少内存使用,简单来说,就是将模型量化为4位精度。
添加LoRA适配器
我们要给预训练的大语言模型添加LoRA矩阵,这有助于微调模型的回答。使用unsloth
,整个过程非常简单,只需要几行代码。具体实现如下:
model = FastLanguageModel.get_peft_model(
model,
r = 64,
target_modules = ["q_proj", "k_proj", "v_proj", "o_proj",
"gate_proj", "up_proj", "down_proj",],
lora_alpha = 32,
lora_dropout = 0.05, # Can be set to any, but = 0 is optimized
bias = "none", # Can be set to any, but = "none" is optimized
use_gradient_checkpointing = "unsloth", # True or "unsloth" for very long context
random_state = 3977,
use_rslora = False, # unsloth also supports rank stabilized LoRA
loftq_config = None # And LoftQ
)
代码解释:
- 我们使用
FastLanguageModel
的get_peft_model
方法重新初始化了模型,以便使用PEFT技术。 - 这里需要传入之前获取的预训练模型。
r = 64
这个参数定义了LoRA自适应中低秩矩阵的秩。通常这个秩在8 - 128范围内时能得到较好的效果。lora_dropout = 0.05
这个参数在训练LoRA适配器模型时,给低秩矩阵引入了随机失活(dropout)。它可以防止模型过拟合。target_modules
指定了我们想要应用LoRA自适应的模型中特定类或模块的名称列表。
数据准备
现在,我们已经在预训练的大语言模型上设置好了LoRA适配器,接下来就可以着手构建用于训练模型的数据了。为了构建数据,我们需要以特定的方式指定提示(prompt),使其包含输入、指令和响应。
- 指令(Instructions):表示向大语言模型提出的主要问题。
- 输入(Input):意味着除了指令或问题外,我们还传递了一些数据让模型进行分析。
- 响应(Response):表示大语言模型的输出。它用于说明大语言模型的响应应该如何根据特定指令(问题)进行调整,无论是否传递了输入(数据)。
提示的结构如下:
alpaca_prompt = """Below is an instruction that describes a task, paired with an input that provides further context. Write a response that appropriately completes the request.
### Instruction:
{}
### Input:
{}
### Response:
{}"""
我们创建了一个函数,用于将所有数据按照alpaca_prompt
的格式进行正确构建,代码如下:
EOS_TOKEN = tokenizer.eos_token
def formatting_prompts_func(examples):
instructions = examples["instruction"]
inputs = examples["input"]
outputs = examples["output"]
texts = []
for instruction, input, output in zip(instructions, inputs, outputs):
text = alpaca_prompt.format(instruction, input, output) + EOS_TOKEN
texts.append(text)
return { "text" : texts, }
现在,我们要加载用于微调模型的数据集,在我们的例子中是yahma/alpaca - cleaned
。代码如下:
from datasets import load_dataset
dataset = load_dataset("yahma/alpaca-cleaned", split = "train")
dataset = dataset.map(formatting_prompts_func, batched = True,)
训练模型
现在我们既有了结构化的数据,又有了带有LoRA适配器或矩阵的模型,接下来就可以开始训练模型啦。为了训练模型,我们需要初始化一些超参数,这些超参数不仅有助于训练过程,还会在一定程度上影响模型的准确性。
我们将使用SFTTrainer
和超参数来初始化一个训练器,代码如下:
from trl import SFTTrainer
from transformers import TrainingArguments
from unsloth import is_bfloat16_supported
trainer = SFTTrainer(
model = model,
tokenizer = tokenizer,
train_dataset = dataset,
dataset_text_field = "text",
max_seq_length = max_seq_length,
dataset_num_proc = 2,
packing = False,
args = TrainingArguments(
per_device_train_batch_size = 2,
gradient_accumulation_steps = 4,
warmup_steps = 5,
max_steps = 120,
learning_rate = 2e-4,
fp16 = not is_bfloat16_supported(),
bf16 = is_bfloat16_supported(),
logging_steps = 1,
optim = "adamw_8bit",
weight_decay = 0.01,
lr_scheduler_type = "linear",
seed = 3407,
output_dir = "outputs",
report_to = "none",
)
)
现在,使用这个训练器来启动模型的训练,代码如下:
trainer_stats = trainer.train()
运行这段代码后,模型就会开始训练,并在控制台记录所有步骤以及相应的训练损失。
对微调后的模型进行推理
模型训练完成后,接下来要做的就是对微调后的模型进行推理,评估它的回答。进行推理的代码如下:
FastLanguageModel.for_inference(model)
inputs = tokenizer(
alpaca_prompt.format(
"Continue the fibonnaci sequence.",
"1, 1, 2, 3, 5, 8",
""
), return_tensors = "pt").to("cuda")
outputs = model.generate(**inputs, max_new_tokens = 64, use_cache = True)
tokenizer.batch_decode(outputs)
代码解释:
- 我们使用
unsloth
包中的FastLanguageModel
来加载微调后的模型进行推理,这种方法能得到更快的结果。 - 为了对模型进行推理,我们首先要将查询转换为结构化的提示,然后对提示进行分词。
- 设置
return_tensors = "pt"
是为了让分词器返回一个PyTorch张量,然后使用.to("cuda")
将这个张量加载到GPU上,以提高处理速度。 - 接着调用
model.generate()
来生成查询的响应。 - 在生成响应时,我们设置
max_new_tokens = 64
,这指定了模型应该生成的最大新令牌数。 use_cache = True
也能加快生成速度,特别是对于较长的序列。- 最后,我们将微调后模型输出的张量解码为文本。
保存微调后的模型
这一步完成了模型微调的整个过程,现在我们可以保存微调后的模型,以便将来进行推理或使用。我们还需要将分词器和模型一起保存。下面是将微调后的模型保存到Hugging Face Hub的方法:
model.push_to_hub_merged("<YOUR_HF_ID>/<MODEL_NAME>", tokenizer, save_method = "merged_4bit", token = "<YOUR_HF_TOKEN>")
model.push_to_hub_merged("<YOUR_HF_ID>/<MODEL_NAME>", tokenizer, save_method = "merged_16bit", token = "<YOUR_HF_TOKEN>")
这里需要注意:
- 你需要设置模型的名称,这个名称将用于在Hugging Face Hub上设置模型的ID。
- 可以选择上传4位精度或16位精度的完整合并模型。合并模型意味着将预训练模型和LoRA矩阵一起上传到Hugging Face Hub,当然也有其他选项,比如只上传LoRA矩阵而不上传模型。
总结
本文主要讨论了以下几个要点:
- 大语言模型从最通俗的角度来说,就是深度学习架构(如Transformer)的精妙应用,通过大量的语言文本数据进行训练。
- DeepSeek - R1 - Zero模型通过大规模强化学习(RL)训练,且没有经过监督微调(SFT)作为初步步骤,在推理方面表现出色。
- 微调大语言模型就是为模型提供特定任务的数据,使其回答更符合特定用途,从而提高准确性,让回答更具针对性和领域专业性。
- 我们使用的主要Python库和框架有
unsloth
、torch
、transformers
和trl
。此外,还讨论了微调大语言模型的计算要求。 - 我们构建了用于有效微调模型的数据集,然后使用
SFTTrainer
对模型进行训练。
推荐阅读
1. DeepSeek-R1的顿悟时刻是如何出现的? 背后的数学原理
2. 微调 DeepSeek LLM:使用监督微调(SFT)与 Hugging Face 数据
3. 使用 DeepSeek-R1 等推理模型将 RAG 转换为 RAT
4. DeepSeek R1:了解GRPO和多阶段训练
5. 深度探索:DeepSeek-R1 如何从零开始训练
6. DeepSeek 发布 Janus Pro 7B 多模态模型,免费又强大!
本文由mdnice多平台发布
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。