没有数据、没有GPU的情况下怎么训练DeepSeek

春节期间,AI 界热闹非凡,到处都是关于 DeepSeek 的报道。大家都知道,训练好的模型通常需要昂贵的专用 GPU,这对很多想试试微调技术的人来说,真是一道门槛。

好消息来了:你完全可以用免费的 Google Colab Notebook 来实现微调。借助 Colab 提供的免费 GPU,你可以把通用的 DeepSeek R1 模型"定制"为专门针对某个领域的模型,让它回答问题时既专业又贴合实际需求,而且操作简单、成本低。

如今,不仅是开发者,很多创业者也在关注 DeepSeek R1 模型,并尝试将它集成到自己的产品中。通过微调,你可以让模型用更符合特定领域特点的方式回答问题,充分利用 DeepSeek 强大的推理能力,让回答既清晰又有逻辑。

接下来给大家如何利用 Google Colab Notebook (可参考教程申请《Google Colab免费GPU使用教程》)这一免费、易用的资源,通过 Python 对 DeepSeek-R1 模型进行微调。你会学到如何用任意数据集训练模型,使其能更精准地回答专业领域的问题。

在开始微调之前,我们先看看需要准备哪些东西。

需要安装的 Python 库

微调 LLM 时,我们需要以下几个 Python 库:

  • unsloth:这个库能让你微调 Llama-3、Mistral、Phi-4、Gemma 2x 这类大模型时更快,占用内存也少 70%,而且效果不会打折扣。
  • torch:这是 PyTorch 的基础库。它可以进行高效的张量运算,类似于 NumPy,但还支持 GPU 加速,对处理大模型非常重要。
  • transformers:这是一个很流行的自然语言处理库,里面有很多预训练模型。因为微调需要在预训练模型上做文章,这个库能帮你轻松调用各种模型。
  • trl:这个库专门用来做基于 Transformer 的强化学习,是建立在 Hugging Face 的 transformers 库基础上的,让用强化学习训练模型变得简单高效。
计算资源要求

微调模型其实就是让模型回答问题时更加符合特定领域的要求,不需要从零开始训练所有参数。但是,大模型微调时所有需要训练的参数都要加载到 GPU 的内存(vRAM)里,所以对硬件要求很高。

为了让大家更容易体验,我们这次选用的模型是DeepSeek-R1-Distill(47.4 亿参数) 。这个模型大概需要 8~12GB 的 vRAM。好消息是,我们可以使用免费的 Google Colab 上的 T4 GPU,它有 16GB 的 vRAM,完全能满足我们的需求。

数据准备

微调模型时,需要有结构化、针对任务的数据。数据可以来自社交媒体、网站、书籍或者论文等多种渠道。

这篇文章中,我们将使用datasets库,从Hugging Face Hub 上下载数据。具体来说,我们用的是yahma/alpaca-cleaned数据集。

代码实现

安装所需的包

使用 Google Colab 进行微调有个很大的好处:大部分包已经预装好了,我们只需要额外安装一个包 ------unsloth。 安装方法非常简单,在 Colab 中运行下面的命令即可:

diff 复制代码
!pip install unsloth

初始化模型和分词器

我们使用unsloth这个包来加载预训练模型,因为它提供了很多方便的技术,能让我们更快地下载和微调大型语言模型。 下面这段代码用于加载模型和分词器:

ini 复制代码
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,
    # token = "hf_...", # 如果使用 gated 模型,比如 meta-llama/Llama-2-7b-hf,可以传入 token
)

这里解释下各个参数的意义:

  • model_name :指定我们要加载的预训练模型名称,这里是unsloth/DeepSeek-R1-Distill-Llama-8B-unsloth-bnb-4bit,表示我们使用的是 DeepSeek-R1-Distill 模型。
  • max_seq_length:设置模型能处理的最大输入序列长度,这里设为 2048,这样既能满足大部分需求,又能合理利用内存和提升速度。
  • dtype :设置为None,这样可以自动适配当前硬件的数据类型,无需我们额外操心。
  • load_in_4bit:将模型量化为 4bit 精度,这样可以在保证效果的同时,大幅减少内存占用,加快推理速度。

添加 LoRA 适配器

接下来,我们要给预训练模型添加 LoRA 矩阵,这样可以帮助模型在微调时更好地适应特定任务。使用unsloth进行这一过程非常简单,只需几行代码:

ini 复制代码
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, # 设置 dropout 防止过拟合,0.05 是个不错的选择
    bias = "none",    # bias 参数这里选择 "none" 是最优设置
    use_gradient_checkpointing = "unsloth", # 对于长序列,使用这个可以节省内存
    random_state = 3977,
    use_rslora = False,  # unsloth 也支持 rank stabilized LoRA
    loftq_config = None, # LoftQ 配置,如果没有特殊需求可以设置为 None
)

这里简单说明一下关键参数的作用:

  • r:设置 LoRA 中低秩矩阵的秩,这里取 64,一般 8 到 128 之间都可以选择,64 常常能取得不错的效果。
  • lora_dropout:在训练 LoRA 适配器时加入 dropout,有助于防止模型过拟合。
  • target_modules:指定模型中需要应用 LoRA 的模块列表,这里列出了多个常见的投影模块。

数据准备

当我们完成了模型的适配设置之后,就需要准备数据来进行微调。数据需要按照一定的格式组织好,其中包含输入、指令和期望的输出。

我们通常将数据分为三部分:

  • Instruction(指令) :这是向模型提出的问题或任务描述。
  • Input(输入) :如果有额外数据需要提供,这里可以传入补充信息。
  • Response(回答) :这就是我们期望模型输出的内容,它需要与指令(和输入)相匹配。

我们先定义一个模板,用来组织这些信息:

ini 复制代码
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:
{}"""

接着,我们写一个函数,利用这个模板对数据进行格式化。注意,我们在每条文本后面都要加上 EOS(结束符),否则生成的内容可能会无限延长:

ini 复制代码
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):
        # 这里一定要加上 EOS_TOKEN,否则生成内容可能不会停止
        text = alpaca_prompt.format(instruction, input, output) + EOS_TOKEN
        texts.append(text)
    return { "text": texts, }

最后,我们加载用于微调的数据集,这里我们选用的是 Hugging Face Hub 上的 "yahma/alpaca-cleaned"。运行下面的代码即可:

ini 复制代码
from datasets import load_dataset
dataset = load_dataset("yahma/alpaca-cleaned", split="train")
dataset = dataset.map(formatting_prompts_func, batched=True)

模型训练

既然我们已经准备好了格式化后的数据,也把 LoRA 适配器加载到模型上了,那接下来就可以开始训练了。

训练前,我们需要先设置一些超参数,这些参数会影响训练的过程和模型的准确度。

我们使用SFTTrainer来初始化训练器,并传入相关的超参数。下面这段代码展示了如何完成初始化:

ini 复制代码
from trl import SFTTrainer
from transformers import TrainingArguments
from unsloth import is_bfloat16_supported

trainer = SFTTrainer(
    model = model,             # 已经添加了 LoRA 适配器的模型
    tokenizer = tokenizer,     # 模型对应的分词器
    train_dataset = dataset,   # 用于训练的数据集
    dataset_text_field = "text",   # 数据集中包含结构化数据的字段名称
    max_seq_length = max_seq_length,   # 模型能处理的最大输入序列长度
    dataset_num_proc = 2,      # 用于加载和处理数据的进程数
    packing = False,           # 对于短序列,开启这个选项能让训练快5倍(这里暂不开启)
    args = TrainingArguments(
        per_device_train_batch_size = 2,   # 每块 GPU 上的训练批次大小
        gradient_accumulation_steps = 4,     # 梯度累积的步数
        warmup_steps = 5,
        # num_train_epochs = 1,  # 如果需要完整训练1个周期,可以设置这个参数
        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",      # 如需使用 WandB 等工具可修改此项
    ),
)

初始化完成后,我们只需调用训练命令即可开始训练:

ini 复制代码
trainer_stats = trainer.train()

这样训练过程就会开始运行,训练过程中在 Colab 的日志中会显示每一步的训练损失。

模型推理

当训练结束后,我们可以利用微调后的模型进行推理,看看它的回答效果如何。下面这段代码演示了如何使用微调后的模型进行推理:

ini 复制代码
FastLanguageModel.for_inference(model)  # 开启原生 2 倍加速推理
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,目的是加载已经微调好的模型用于推理。这种方式可以让推理速度更快。
  • 在进行推理前,首先需要将查询内容(也就是问题)按照预先设定的格式组织成一个结构化的文本(prompt),然后使用分词器将这个 prompt 转换为模型可以识别的格式。
  • 为了让分词器输出 PyTorch 的张量,我们设置了参数return_tensors="pt"。接着,用.to("cuda")把张量加载到 GPU 上,这样处理速度会大大提高。
  • 接下来,我们调用model.generate()方法生成回答。
  • 在生成回答时,我们设置了max_new_tokens=64,这表示模型最多生成 64 个新 token。
  • 同时,参数use_cache=True可以加速生成过程,特别是在生成长序列时效果更明显。
  • 最后,我们使用分词器的batch_decode()方法将生成的张量结果解码成我们能看懂的文本。

保存微调模型

训练完毕后,最后一步就是保存我们的微调模型,以便日后使用或部署。记得同时保存模型和分词器。下面提供了两种保存方式,分别对应 4bit 和 16bit 精度:

ini 复制代码
# 以 4bit 精度保存
model.push_to_hub_merged("<YOUR_HF_ID>/<MODEL_NAME>", tokenizer, save_method="merged_4bit", token="<YOUR_HF_TOKEN>")

# 以 16bit 精度保存
model.push_to_hub_merged("<YOUR_HF_ID>/<MODEL_NAME>", tokenizer, save_method="merged_16bit", token="<YOUR_HF_TOKEN>")

这里只需将<YOUR_HF_ID><MODEL_NAME><YOUR_HF_TOKEN>替换成你在 Hugging Face 的相关信息即可。

相关推荐
python算法(魔法师版)15 分钟前
深度学习深度解析:从基础到前沿
人工智能·深度学习
kakaZhui39 分钟前
【llm对话系统】大模型源码分析之 LLaMA 位置编码 RoPE
人工智能·深度学习·chatgpt·aigc·llama
struggle20252 小时前
一个开源 GenBI AI 本地代理(确保本地数据安全),使数据驱动型团队能够与其数据进行互动,生成文本到 SQL、图表、电子表格、报告和 BI
人工智能·深度学习·目标检测·语言模型·自然语言处理·数据挖掘·集成学习
佛州小李哥2 小时前
通过亚马逊云科技Bedrock打造自定义AI智能体Agent(上)
人工智能·科技·ai·语言模型·云计算·aws·亚马逊云科技
云空3 小时前
《DeepSeek 网页/API 性能异常(DeepSeek Web/API Degraded Performance):网络安全日志》
运维·人工智能·web安全·网络安全·开源·网络攻击模型·安全威胁分析
AIGC大时代3 小时前
对比DeepSeek、ChatGPT和Kimi的学术写作关键词提取能力
论文阅读·人工智能·chatgpt·数据分析·prompt
山晨啊84 小时前
2025年美赛B题-结合Logistic阻滞增长模型和SIR传染病模型研究旅游可持续性-成品论文
人工智能·机器学习
一水鉴天4 小时前
为AI聊天工具添加一个知识系统 之77 详细设计之18 正则表达式 之5
人工智能·正则表达式
davenian5 小时前
DeepSeek-R1 论文. Reinforcement Learning 通过强化学习激励大型语言模型的推理能力
人工智能·深度学习·语言模型·deepseek
X.AI6665 小时前
【大模型LLM面试合集】大语言模型架构_llama系列模型
人工智能·语言模型·llama