轻松微调大模型:利用 Colab 和 Unsloth 实现高效训练

文章目录

  • [1. 引言](#1. 引言)
  • [2. 准备工作](#2. 准备工作)
  • [3. 基础概念](#3. 基础概念)
  • [4. 完整微调流程](#4. 完整微调流程)
    • [4.1 创建Colab环境](#4.1 创建Colab环境)
    • [4.2 安装依赖](#4.2 安装依赖)
    • [4.3 加载预训练模型](#4.3 加载预训练模型)
    • [4.4 微调前测试](#4.4 微调前测试)
    • [4.5 加载与格式化数据集](#4.5 加载与格式化数据集)
    • [4.6 执行微调训练](#4.6 执行微调训练)
    • [4.7 微调后测试](#4.7 微调后测试)
    • [4.8 将微调后的模型保存为 GGUF 格式](#4.8 将微调后的模型保存为 GGUF 格式)
      • [4.8.1 配置HUGGINGFACE_TOKEN的环境变量](#4.8.1 配置HUGGINGFACE_TOKEN的环境变量)
    • [4.9 将微调后的模型上传到 HuggingFace](#4.9 将微调后的模型上传到 HuggingFace)
    • [4.10 使用Ollama运行微调后的模型](#4.10 使用Ollama运行微调后的模型)

1. 引言

为什么要用 Colab 和 Unsloth 微调大模型?

大型语言模型(LLM)如 Llama、Mistral 等在通用任务上表现惊艳,但要让它们适配特定场景(比如医疗问答、算命预测),就需要微调。Google Colab 提供免费的 GPU 资源,而 Unsloth 是一个高效的微调工具,能大幅降低显存需求,让普通用户也能在云端完成训练。这篇文章将带你一步步完成从零到部署的全过程。

本文的目标与适用人群

目标是帮助你在 Colab 上微调一个大模型,并将结果部署到本地运行。适合人群包括 AI 爱好者、开发者,以及想将 LLM 应用到特定领域的从业者。无论你是新手还是有经验的用户,这里都有清晰的指引。

2. 准备工作

硬件与软件需求

  • Colab: 只需要一个 Google 账户,免费版提供 T4 GPU(约 15GB 显存),足够跑 7B 参数模型。
  • 本地环境: 用于部署的电脑建议至少 8GB 内存(运行 7B 模型),若有 NVIDIA GPU 可加速。
  • 网络: 稳定的互联网连接,用于下载模型和数据集(🪜)

安装 Colab 和本地安装Ollama

Colab: 直接访问colab,无需安装。
本地安装Ollama: 安装 Ollama(后文会用到)。

进入官网后点击Download for Windows进行下载安装

安装完毕后查看是否生效,在DOS窗口执行如下命令
检查版本: ollama --version

3. 基础概念

什么是微调(Fine-Tuning)?

微调是用特定领域的数据调整预训练模型的过程。相比从头训练,微调更快、更省资源,能让模型"学会"新任务,比如回答医疗问题。

Unsloth 的优势与 LoRA 技术简介

Unsloth 是一个优化工具,能让微调速度提升 2-5 倍,显存占用降低 60% 以上。它基于 LoRA(Low-Rank Adaptation),只更新模型的部分参数,而不是全部,既高效又保持性能。简单来说,LoRA 就像给模型加了个"补丁",轻量又灵活。

4. 完整微调流程

使用Colab微调大模型代码地址:Colab微调大模型代码(针对医疗数据集进行微调)

4.1 创建Colab环境

首先我们需要再云端硬盘中新建笔记本

随后我们需要更改运行时类型

选择T4 GPT后进行保存

4.2 安装依赖

在 Colab 新建笔记本,运行以下代码安装所需库:

这些库包括 Unsloth 主程序、bitsandbytes(用于量化模型)和 unsloth_zoo(预训练模型支持)。

python 复制代码
%%capture
# 这是一个 Jupyter Notebook 的魔法命令,用于隐藏命令的输出。
# 通过捕获输出,可以让 Colab 的界面更整洁,避免显示冗长的安装日志。

# 安装 unsloth 包。
# unsloth 是一个高效的工具,用于微调大型语言模型(LLM),能显著减少显存需求并加速训练。
!pip install unsloth

# 卸载当前已安装的 unsloth 包(如果存在),然后从 GitHub 安装最新版本。
# "-y" 表示自动确认卸载,"--upgrade" 确保获取最新版,"--no-cache-dir" 避免使用缓存,
# "--no-deps" 跳过依赖安装(因为我们只关心 unsloth 本身),
# 通过 GitHub 源安装可以获得最新的功能和修复。
!pip uninstall unsloth -y && pip install --upgrade --no-cache-dir --no-deps git+https://github.com/unslothai/unsloth.git

# 安装 bitsandbytes 和 unsloth_zoo 两个依赖包。
# bitsandbytes 是一个用于模型量化的库,支持 4 位和 8 位精度,能大幅降低内存占用。
# unsloth_zoo 提供了一些预训练模型或相关工具,方便用户快速上手。
!pip install bitsandbytes unsloth_zoo

4.3 加载预训练模型

选择一个基础模型,这里用 unsloth/DeepSeek-R1-Distill-Llama-8B

python 复制代码
# 导入 Unsloth 提供的 FastLanguageModel 类,用于加载和操作高效的大型语言模型。
from unsloth import FastLanguageModel

# 导入 PyTorch 库,它是深度学习的基础框架,用于处理模型的张量计算和 GPU 加速。
import torch

# 设置模型处理文本的最大序列长度(单位:token),这里设为 2048。
# 这决定了模型一次能处理的文本长度,越大越能捕捉长上下文,但也增加显存需求。
max_seq_length = 2048

# 设置模型的数据类型(dtype),这里设为 None 表示让 Unsloth 自动选择最优类型。
# 通常会根据硬件支持选择 float16 或 bfloat16,以平衡精度和性能。
dtype = None

# 启用 4 位量化加载模型,值为 True。
# 4 位量化可以将模型大小和显存需求减少约 75%,非常适合在资源有限的环境(如 Colab 免费版)运行。
load_in_4bit = True

# 从预训练模型库中加载指定的模型和对应的 tokenizer(分词器)。
# 返回两个对象:model(模型本身)和 tokenizer(用于将文本转为数字输入的工具)。
model, tokenizer = FastLanguageModel.from_pretrained(
    model_name="unsloth/DeepSeek-R1-Distill-Llama-8B",  # 指定模型名称,这里使用 Unsloth 优化的 DeepSeek-R1-Distill-Llama-8B。
    max_seq_length=max_seq_length,                      # 使用上面定义的最大序列长度。
    dtype=dtype,                                       # 使用上面定义的数据类型(自动选择)。
    load_in_4bit=load_in_4bit,                         # 启用 4 位量化加载。
    # token="hf_...",                                  # 如果需要访问私有模型,可以取消注释并填入 Hugging Face 的 API 令牌。
)

4.4 微调前测试

先看看模型未经训练的表现:

这里我们调用推理模型进行推理,输出可能是泛泛而谈,微调后会更精准。

python 复制代码
# 定义提示模板(prompt_style),这是一个多行字符串,用于格式化输入和输出。
# 模板包含指令、问题和回答部分,设计目的是引导模型生成结构化的回答。
prompt_style = """以下是描述任务的指令,以及提供进一步上下文的输入。
请写出一个适当完成请求的回答。
在回答之前,请仔细思考问题,并创建一个逻辑连贯的思考过程,以确保回答准确无误。

### 指令:
你是一位精通医学知识的医生,能够回答关于疾病、治疗方案和健康建议的问题。
请回答以下医疗问题。

### 问题:
{}

### 回答:
<think>{}"""

# 定义一个测试问题,用于在微调前检查模型的初始能力。
# 这里选择了一个常见的医疗问题,方便观察模型的表现。
question = "我最近总是感到疲劳,可能是什么原因?"

# 将模型切换到推理模式。
# FastLanguageModel.for_inference 是 Unsloth 提供的方法,优化模型以进行生成任务,避免训练时的额外开销。
FastLanguageModel.for_inference(model)

# 使用 tokenizer 将格式化的提示转换为模型可处理的数字输入。
# prompt_style.format(question, "") 将问题插入模板,思考部分暂时为空(留给模型生成)。
# return_tensors="pt" 表示返回 PyTorch 张量格式,to("cuda") 将数据移到 GPU 上加速处理。
inputs = tokenizer([prompt_style.format(question, "")], return_tensors="pt").to("cuda")

# 调用模型生成回答。
# input_ids 是编码后的输入序列,attention_mask 指示哪些部分需要关注,
# max_new_tokens=1200 限制生成最多 1200 个新 token,use_cache=True 启用缓存以加速生成。
outputs = model.generate(
    input_ids=inputs.input_ids,
    attention_mask=inputs.attention_mask,
    max_new_tokens=1200,
    use_cache=True,
)

# 将模型生成的数字输出解码为人类可读的文本。
# batch_decode 处理批量输出,这里取第一个(也是唯一一个)结果。
response = tokenizer.batch_decode(outputs)

# 打印生成的回答,展示模型在微调前的能力。
# response[0] 是解码后的完整文本,可能包含提示部分和生成的回答。
print(response[0])

4.5 加载与格式化数据集

使用 shibing624/medical 中文医疗数据集:

我在这里定义了一个叫做 train_prompt_style 的字符串模板,它就像是我给模型的一份"任务说明书"。我的目的是告诉模型,它现在是一位精通医学知识的医生,需要回答医疗相关的问题。这个模板分为几个部分:首先,我写了一个总体的任务描述,提醒模型要认真思考并给出准确的回答;接着,我明确了指令,设定模型的角色和任务;然后,我用 {} 留了三个占位符,分别用来填入具体的问题、思考过程和最终的回答。这样,我就能确保模型在训练时按照这个结构生成内容,比如先分析问题,再给出专业建议。这样设计既清晰又有逻辑,方便模型学习如何像医生一样思考和回应。

python 复制代码
train_prompt_style = """以下是描述任务的指令,以及提供进一步上下文的输入。
请写出一个适当完成请求的回答。
在回答之前,请仔细思考问题,并创建一个逻辑连贯的思考过程,以确保回答准确无误。

### 指令:
你是一位精通医学知识的医生,能够回答关于疾病、治疗方案和健康建议的问题。
请回答以下医疗问题。

### 问题:
{}

### 回答:
<思考>
{}
</思考>
{}"""

我在这段代码里主要是为了准备训练数据,让模型能理解和生成医疗相关的回答。首先,我定义了一个结束标记EOS_TOKEN,这是从分词器里拿来的,用来告诉模型每段文本到哪里结束。接着,我从 datasets 库里导入了 load_dataset 函数,用它加载了一个医疗数据集 shibing624/medical,选了finetune 配置里的前 200 条训练数据。为了搞清楚数据长什么样,我打印了它的字段名,结果是 instructioninputoutput。然后,我写了一个函数 formatting_prompts_func,用来把这些数据格式化成我想要的样子:我从数据里抽出instruction 作为问题,input 作为思考过程,output 作为回答,再用之前定义的 train_prompt_style 模板把它们组合起来,加上结束标记,最后存进一个列表里。通过 dataset.map 方法,我批量处理了所有数据,最后输出了第一条格式化后的文本,看看是不是按我的预期工作。这一步的目的是让数据变成模型能直接学习的格式,确保训练顺利进行。

python 复制代码
# 定义结束标记(EOS_TOKEN),用于指示文本的结束
EOS_TOKEN = tokenizer.eos_token  # 必须添加结束标记

# 导入数据集加载函数
from datasets import load_dataset
# 加载指定的数据集,选择中文语言和训练集的前500条记录
dataset = load_dataset("shibing624/medical", 'finetune', split = "train[0:200]", trust_remote_code=True)
# 打印数据集的列名,查看数据集中有哪些字段
print(dataset.column_names)
python 复制代码
# 定义一个函数,用于格式化数据集中的每条记录
def formatting_prompts_func(examples):
    # 从数据集中提取问题、复杂思考过程和回答
    inputs = examples["instruction"]
    cots = examples["input"]
    outputs = examples["output"]
    texts = []  # 用于存储格式化后的文本
    # 遍历每个问题、思考过程和回答,进行格式化
    for input, cot, output in zip(inputs, cots, outputs):
        # 使用字符串模板插入数据,并加上结束标记
        text = train_prompt_style.format(input, cot, output) + EOS_TOKEN
        texts.append(text)  # 将格式化后的文本添加到列表中
    return {
        "text": texts,  # 返回包含所有格式化文本的字典
    }

dataset = dataset.map(formatting_prompts_func, batched = True)
dataset["text"][0]

4.6 执行微调训练

配置 LoRA 并开始训练:

训练约需 20-30 分钟,视数据量而定。

python 复制代码
# 将模型切换到训练模式。
# FastLanguageModel.for_training 是 Unsloth 提供的方法,确保模型准备好进行参数更新,而不是仅用于推理。
FastLanguageModel.for_training(model)

# 配置并返回一个支持参数高效微调(PEFT)的模型。
# get_peft_model 使用 LoRA 技术,只更新模型的部分参数,从而减少显存需求和计算开销。
model = FastLanguageModel.get_peft_model(
    model,  # 传入之前加载的预训练模型,作为微调的基础。
    r=16,   # 设置 LoRA 的秩(rank),控制新增可训练参数的规模。值越大,模型调整能力越强,但显存需求也增加。
    target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"],  
            # 指定需要应用 LoRA 的模型模块,这些是 Transformer 架构中的关键部分(如注意力机制和前馈网络)。
    lora_alpha=16,  
            # LoRA 的缩放因子,影响新增参数对模型的贡献程度。通常与 r 成比例设置,这里为 16。
    lora_dropout=0, 
            # 设置 LoRA 层的 dropout 比率,用于防止过拟合。这里设为 0,表示不丢弃任何参数。
    bias="none",    
            # 指定是否为 LoRA 参数添加偏置项。"none" 表示不添加,保持轻量化。
    use_gradient_checkpointing="unsloth",  
            # 启用梯度检查点技术,Unsloth 优化版本能节省显存,支持更大的批量大小或模型。
    random_state=3407,  
            # 设置随机种子,确保每次运行时模型初始化的随机性一致,便于结果复现。
    use_rslora=False,   
            # 是否使用 Rank-Stabilized LoRA(一种改进的 LoRA 变体)。这里设为 False,使用标准 LoRA。
    loftq_config=None,  
            # 设置是否使用 LoftQ(一种量化技术)。这里设为 None,表示不启用。
)
python 复制代码
# 导入 SFTTrainer 类,用于监督微调训练。
# SFTTrainer 是 TRL(Transformers Reinforcement Learning)库提供的工具,适合基于指令的数据集微调模型。
from trl import SFTTrainer

# 导入 TrainingArguments 类,用于定义训练的超参数。
# 这是 Hugging Face Transformers 库的核心类,允许灵活配置训练过程。
from transformers import TrainingArguments

# 导入 Unsloth 提供的函数,用于检查硬件是否支持 bfloat16 数据类型。
# bfloat16 是一种高效的半精度格式,能在支持的硬件上加速训练。
from unsloth import is_bfloat16_supported

# 创建 SFTTrainer 实例,配置训练所需的模型、数据和参数。
trainer = SFTTrainer(
    model=model,                    # 传入之前配置好的模型(已启用 LoRA)。
    tokenizer=tokenizer,            # 传入与模型配套的分词器,用于处理文本数据。
    train_dataset=dataset,          # 传入训练数据集(已格式化为包含 "text" 字段)。
    dataset_text_field="text",      # 指定数据集中包含训练文本的字段名,这里是 "text"。
    max_seq_length=max_seq_length,  # 设置最大序列长度,与模型加载时保持一致(如 2048)。
    dataset_num_proc=2,             # 设置数据处理的并行进程数,加速数据预处理。
    packing=False,                  # 是否启用序列打包。False 表示不打包,每条数据独立处理。
    args=TrainingArguments(         # 定义训练超参数的配置对象。
        per_device_train_batch_size=2,  # 每个设备(GPU)的批次大小,设为 2 以适应显存限制。
        gradient_accumulation_steps=4,  # 梯度累积步数,累积 4 次小批次后更新参数,模拟大批量训练。
        warmup_steps=5,                 # 预热步数,学习率在前 5 步逐渐增加,稳定训练。
        max_steps=75,                   # 最大训练步数,控制训练时长(步数 = 数据量 / 批次大小)。
        learning_rate=2e-4,             # 学习率,设为 0.0002,控制参数更新速度。
        fp16=not is_bfloat16_supported(),  # 如果不支持 bfloat16,则使用 fp16(16 位浮点数)加速训练。
        bf16=is_bfloat16_supported(),      # 如果硬件支持 bfloat16,则启用它,兼顾精度和速度。
        logging_steps=1,                   # 每 1 步记录一次训练日志,方便监控损失变化。
        optim="adamw_8bit",                # 使用 8 位 AdamW 优化器,节省显存并保持性能。
        weight_decay=0.01,                 # 权重衰减系数,设为 0.01,防止模型过拟合。
        lr_scheduler_type="linear",        # 学习率调度器类型,"linear" 表示线性衰减。
        seed=3407,                         # 随机种子,确保训练结果可复现。
        output_dir="outputs",              # 训练结果(如检查点)保存的目录。
        report_to="none",                  # 不将训练日志报告到外部工具(如 WandB),仅本地记录。
    ),
)

# 开始训练模型。
# trainer.train() 执行微调过程,根据配置更新模型参数,完成后保存到 output_dir。
trainer.train()

4.7 微调后测试

用相同问题测试效果:

回答更贴近医疗专业知识。

python 复制代码
print(question) # 打印前面的问题
# 将模型切换到推理模式,准备回答问题
FastLanguageModel.for_inference(model)

# 将问题转换成模型能理解的格式,并发送到 GPU 上
inputs = tokenizer([prompt_style.format(question, "")], return_tensors="pt").to("cuda")

# 让模型根据问题生成回答,最多生成 4000 个新词
outputs = model.generate(
    input_ids=inputs.input_ids,  # 输入的数字序列
    attention_mask=inputs.attention_mask,  # 注意力遮罩,帮助模型理解哪些部分重要
    max_new_tokens=4000,  # 最多生成 4000 个新词
    use_cache=True,  # 使用缓存加速生成
)

# 将生成的回答从数字转换回文字
response = tokenizer.batch_decode(outputs)

# 打印回答
print(response[0])

4.8 将微调后的模型保存为 GGUF 格式

GGUF,全称是 GPT-Generated Unified Format(GPT 生成的统一格式),是一种专门为存储和部署大型语言模型(LLM)设计的文件格式。简单来说,它就像一个"打包盒",把模型的所有必要信息都装在一起,比如模型的权重(参数)、分词器(tokenizer)信息、超参数(hyperparameters)和元数据(metadata),全都压缩成一个二进制文件。这样做的好处是方便高效地加载和运行模型,尤其是在本地设备上。

执行前我们需要先在Colab中配置token的环境变量

python 复制代码
# 导入 Google Colab 的 userdata 模块,用于访问用户数据
from google.colab import userdata

# 从 Google Colab 用户数据中获取 Hugging Face 的 API 令牌
HUGGINGFACE_TOKEN = userdata.get('HUGGINGFACE_TOKEN')

# 将模型保存为 8 位量化格式(Q8_0)
# 这种格式文件小且运行快,适合部署到资源受限的设备
if True: model.save_pretrained_gguf("model", tokenizer,)

# 将模型保存为 16 位量化格式(f16)
# 16 位量化精度更高,但文件稍大
if False: model.save_pretrained_gguf("model_f16", tokenizer, quantization_method = "f16")

# 将模型保存为 4 位量化格式(q4_k_m)
# 4 位量化文件最小,但精度可能稍低
if False: model.save_pretrained_gguf("model", tokenizer, quantization_method = "q4_k_m")

4.8.1 配置HUGGINGFACE_TOKEN的环境变量

Huggingface官网:huggingface

首先我们先获取到Huggingface的token




4.9 将微调后的模型上传到 HuggingFace

python 复制代码
# 导入 Hugging Face Hub 的 create_repo 函数,用于创建一个新的模型仓库
from huggingface_hub import create_repo

# 在 Hugging Face Hub 上创建一个新的模型仓库
create_repo("xiongwenhao/medical_finetuned", token=HUGGINGFACE_TOKEN, exist_ok=True)

# 将模型和分词器上传到 Hugging Face Hub 上的仓库
model.push_to_hub_gguf("xiongwenhao/medical_finetuned", tokenizer, token=HUGGINGFACE_TOKEN)

4.10 使用Ollama运行微调后的模型

bash 复制代码
ollama run hf.co/{用户名}/{上传到HuggingFace的模型名称}
示例:ollama run hf.co/xiongwenhao/medical_finetuned
相关推荐
AI大模型10 小时前
基于 Ollama 本地 LLM 大语言模型实现 ChatGPT AI 聊天系统
程序员·llm·ollama
李大腾腾3 天前
3、JSON处理( n8n 节点用于交换信息的语言)
openai·workflow·ollama
陈佬昔没带相机3 天前
ollama 终于有UI了,但我却想弃坑了
人工智能·llm·ollama
李大腾腾3 天前
2、n8n 构建你的第一个 AI Agent
openai·agent·ollama
一包烟电脑面前做一天5 天前
RAG实现:.Net + Ollama + Qdrant 实现文本向量化,实现简单RAG
.net·向量数据库·ai大模型·rag·ollama·qdrant·文本分块
一包烟电脑面前做一天5 天前
MCP实现:.Net实现MCP服务端 + Ollama ,MCP服务端工具调用
.net·ai大模型·ollama·mcp·mcp服务端
简放7 天前
智能机票助手-接入Ollama本地模型-Spring-AI-Alibaba
spring boot·langchain·ollama
爱分享的飘哥8 天前
第五十五章:AI模型的“专属定制”:LoRA微调原理与高效合并技巧
人工智能·lora·微调·ai训练·peft·代码实战·模型定制
高克莱8 天前
【macOS操作系统部署开源DeepSeek大模型,搭建Agent平台,构建私有化RAG知识库完整流程】
macos·llm·agent·知识库·anythingllm·ollama·deepseek
GitLqr13 天前
AI洞察 | Manus 与 GPT-5 等最新动向
chatgpt·claude·ollama