面包屑图标 当前位置: 首页
AI资讯
热点详情

零基础入门DeepSeek微调评测教程

AI热点日报
AI热点日报时间:2026-07-01
热点解读

零基础也能掌握的大模型微调技巧,让AI模型更懂你! 核心内容 大模型微调的直观感受与效果展示 选择DeepSeek-R1-Distill-Qwen-7B模型进行微调 微调教程复现与代码详解 前言:大模型评测是一个系统工程。希望通过比较通俗的方式,直观感受大模型微调后的效果。相关思路旨在抛砖引玉,如果

零基础也能掌握的大模型微调技巧,让AI模型更懂你!

核心内容

  • 大模型微调的直观感受与效果展示
  • 选择DeepSeek-R1-Distill-Qwen-7B模型进行微调
  • 微调教程复现与代码详解

前言:大模型评测是一个系统工程。希望通过比较通俗的方式,直观感受大模型微调后的效果。相关思路旨在抛砖引玉,如果对大模型评测有更深厚的兴趣,可以从不同角度继续探索。

三天前,Datawhale 公众号发布了文章《零基础入门:DeepSeek 微调教程来了!》,反响很好,内容非常接地气,适合学习者体验。

本文尝试在该文章的基础上进行复现,并做了一些延伸,帮助读者更加直观地感受大模型微调带来的变化。

为了方便学习与体验,本文选择的模型是蒸馏后的 DeepSeek-R1-Distill-Qwen-7B 模型,显卡选择 RTX4090 24G。模型及数据集均来源于魔塔社区 medical-o1-reasoning-SFT。

1. 微调教程复现

import torch
import matplotlib.pyplot as plt
from transformers import (
    AutoTokenizer,
    AutoModelForCausalLM,
    TrainingArguments,
    Trainer,
    TrainerCallback
)
from peft import LoraConfig, get_peft_model
from datasets import load_dataset
import os

os.environ["CUDA_VISIBLE_DEVICES"] = "0"  # 指定使用GPU 

# 配置路径(根据实际路径修改)
model_path = "xxxx"  # 模型路径
data_path = "xxxx"  # 数据集路径
output_path = "xxxx"  # 微调后模型保存路径

# 设置设备参数
DEVICE = "cuda"  # 使用CUDA
DEVICE_ID = "0"  # CUDA设备ID,如果未设置则为空
device = f"{DEVICE}:{DEVICE_ID}" if DEVICE_ID else DEVICE  # 组合CUDA设备信息
# 自定义回调记录Loss
class LossCallback(TrainerCallback):
    def __init__(self):
        self.losses = []

    def on_log(self, args, state, control, logs=None, **kwargs):
        if "loss" in logs:
            self.losses.append(logs["loss"])

# 数据预处理函数
def process_data(tokenizer):
    dataset = load_dataset("json", data_files=data_path, split="train[:1500]")

    def format_example(example):
        instruction = f"诊断问题:{example['Question']}n详细分析:{example['Complex_CoT']}"
        inputs = tokenizer(
            f"{instruction}n### 答案:n{example['Response']}<|endoftext|>",
            padding="max_length",
            truncation=True,
            max_length=512,
            return_tensors="pt"
        )
        return {"input_ids": inputs["input_ids"].squeeze(0), "attention_mask": inputs["attention_mask"].squeeze(0)}

    return dataset.map(format_example, remove_columns=dataset.column_names)

# LoRA配置
peft_config = LoraConfig(
    r=16,
    lora_alpha=32,
    target_modules=["q_proj", "v_proj"],
    lora_dropout=0.05,
    bias="none",
    task_type="CAUSAL_LM"
)

# 训练参数配置
training_args = TrainingArguments(
    output_dir=output_path,
    per_device_train_batch_size=2,  # 显存优化设置
    gradient_accumulation_steps=4,  # 累计梯度相当于batch_size=8
    num_train_epochs=3,
    learning_rate=3e-4,
    fp16=True,  # 开启混合精度
    logging_steps=20,
    sa ve_strategy="no",
    report_to="none",
    optim="adamw_torch",
    no_cuda=False,  # 强制使用CUDA
    dataloader_pin_memory=False,  # 加速数据加载
    remove_unused_columns=False,  # 防止删除未使用的列
    device="cuda:0" # 指定使用的GPU设备    
)

def main():
    # 创建输出目录
    os.makedirs(output_path, exist_ok=True)

    # 加载tokenizer
    tokenizer = AutoTokenizer.from_pretrained(model_path)
    tokenizer.pad_token = tokenizer.eos_token

    # 加载模型到GPU
    model = AutoModelForCausalLM.from_pretrained(
        model_path,
        torch_dtype=torch.float16,
        device_map=device
    )
    model = get_peft_model(model, peft_config)
    model.print_trainable_parameters()

    # 准备数据
    dataset = process_data(tokenizer)

    # 训练回调
    loss_callback = LossCallback()

    # 数据加载器
    def data_collator(data):
        batch = {
            "input_ids": torch.stack([torch.tensor(d["input_ids"]) for d in data]).to(device),
            "attention_mask": torch.stack([torch.tensor(d["attention_mask"]) for d in data]).to(device),
            "labels": torch.stack([torch.tensor(d["input_ids"]) for d in data]).to(device)  # 使用input_ids作为labels
        }
        return batch

    # 创建Trainer
    trainer = Trainer(
        model=model,
        args=training_args,
        train_dataset=dataset,
        data_collator=data_collator,
        callbacks=[loss_callback]
    )

    # 开始训练
    print("开始训练...")
    trainer.train()

    # 保存最终模型
    trainer.model.sa ve_pretrained(output_path)
    print(f"模型已保存至:{output_path}")

    # 绘制训练集损失Loss曲线
    plt.figure(figsize=(10, 6))
    plt.plot(loss_callback.losses)
    plt.title("Training Loss Curve")
    plt.xlabel("Steps")
    plt.ylabel("Loss")
    plt.sa vefig(os.path.join(output_path, "loss_curve.png"))
    print("Loss曲线已保存")

if __name__ == "__main__":
    main()

微调的具体讲解可以参考上一篇公众号的内容,这里直接看 LOSS 曲线。

可以看到,经过简单的微调,模型的 LOSS 值有所下降,说明 Deepseek 模型对训练集的数据产生了拟合。

2. 直观比较模型生成

微调完成后,生成的内容效果如何?怎么进行比较?最直接的办法就是拿「微调模型」和「原始模型」对同一个问题的回答做对比。

统一提示词和问题,然后比较答案。具体代码如下:

import torch
from transformers import AutoTokenizer, AutoModelForCausalLM
from peft import PeftModel
import os
import json
from bert_score import score
from tqdm import tqdm
# 设置可见GPU设备(根据实际GPU情况调整)
os.environ["CUDA_VISIBLE_DEVICES"] = "0"  # 指定仅使用GPU 

# 路径配置 ------------------------------------------------------------------------
base_model_path = "xxxxx"  # 原始预训练模型路径
peft_model_path = "xxxxx"  # LoRA微调后保存的适配器路径

# 模型加载 ------------------------------------------------------------------------
# 初始化分词器(使用与训练时相同的tokenizer)
tokenizer = AutoTokenizer.from_pretrained(base_model_path)

# 加载基础模型(半精度加载节省显存)
base_model = AutoModelForCausalLM.from_pretrained(
    base_model_path,
    torch_dtype=torch.float16,  # 使用float16精度
    device_map="auto"           # 自动分配设备(CPU/GPU)
)

# 加载LoRA适配器(在基础模型上加载微调参数)
lora_model = PeftModel.from_pretrained(
    base_model, 
    peft_model_path,
    torch_dtype=torch.float16,
    device_map="auto"
)
# 合并LoRA权重到基础模型(提升推理速度,但会失去再次训练的能力)
lora_model = lora_model.merge_and_unload()  
lora_model.eval()  # 设置为评估模式

# 生成函数 ------------------------------------------------------------------------
def generate_response(model, prompt):
    """统一的生成函数
    参数:
        model : 要使用的模型实例
        prompt : 符合格式要求的输入文本
    返回:
        清洗后的回答文本
    """
    # 输入编码(保持与训练时相同的处理方式)
    inputs = tokenizer(
        prompt,
        return_tensors="pt",          # 返回PyTorch张量
        max_length=1024,               # 最大输入长度(与训练时一致)
        truncation=True,              # 启用截断
        padding="max_length"          # 填充到最大长度(保证batch一致性)
    ).to(model.device)               # 确保输入与模型在同一设备

    # 文本生成(关闭梯度计算以节省内存)
    with torch.no_grad():
        outputs = model.generate(
            input_ids=inputs.input_ids,
            attention_mask=inputs.attention_mask,
            max_new_tokens=1024,       # 生成内容的最大token数(控制回答长度)
            temperature=0.7,         # 温度参数(0.0-1.0,值越大随机性越强)
            top_p=0.9,               # 核采样参数(保留累积概率前90%的token)
            repetition_penalty=1.1,  # 重复惩罚系数(>1.0时抑制重复内容)
            eos_token_id=tokenizer.eos_token_id,  # 结束符ID
            pad_token_id=tokenizer.pad_token_id,  # 填充符ID 
        )
    
    # 解码与清洗输出
    full_text = tokenizer.decode(outputs[0], skip_special_tokens=True)  # 跳过特殊token
    answer = full_text.split("### 答案:n")[-1].strip()  # 提取答案部分
    return answer

# 对比测试函数 --------------------------------------------------------------------
def compare_models(question):
    """模型对比函数
    参数:
        question : 自然语言形式的医疗问题
    """
    # 构建符合训练格式的prompt(注意与训练时格式完全一致)
    prompt = f"诊断问题:{question}n详细分析:n### 答案:n"
    
    # 双模型生成
    base_answer = generate_response(base_model, prompt)  # 原始模型
    lora_answer = generate_response(lora_model, prompt)  # 微调模型
    
    # 终端彩色打印对比结果
    print("n" + "="*50)  # 分隔线
    print(f"问题:{question}")
    print("-"*50)
    print(f"33[1;34m[原始模型]33[0mn{base_answer}")  # 蓝色显示原始模型结果
    print("-"*50)
    print(f"33[1;32m[LoRA模型]33[0mn{lora_answer}")  # 绿色显示微调模型结果
    print("="*50 + "n")

# 主程序 ------------------------------------------------------------------------
if __name__ == "__main__":
        # 测试问题集(可自由扩展)
    test_questions = [
        "根据描述,一个1岁的孩子在夏季头皮出现多处小结节,长期不愈合,且现在疮大如梅,溃破流脓,口不收敛,头皮下有空洞,患处皮肤增厚。这种病症在中医中诊断为什么病?"
    ]
    
    # 遍历测试问题
    for q in test_questions:
        compare_models(q)

来看看模型对同一个问题输出结果的差异。这里为了凸显微调后与原始模型的不同,选择了训练集中的一条数据进行测试(读者可根据自己的情况随机测试)。以下是生成的内容:

从生成的内容来看,LoRA 微调后的模型与原始模型确实存在差异。但到底差异有多大,单凭肉眼很难判断——尤其对医疗领域不熟悉的学习者而言,更是如此。有没有更直观的方法来体现这种差异呢?

这时,文本相似性评估派上了用场。可以考虑使用 BERTScore 来对比。BERTScore 是什么?简单说,就是衡量语义相似度的一种指标。通过它来比较训练集的参考答案和模型生成的答案,可以直观看到微调前后的变化。

为了方便学习,以下代码使用的 BERT 模型是最基础的 bert-base-chinese(同样可在魔塔社区下载)。考虑到部分学习者可能无法访问 Hugging Face 官网,这里采用离线模型加载。

温馨提示:模型评估非常消耗资源,建议只调用 10 条数据集即可。

代码如下:

import torch
from transformers import AutoTokenizer, AutoModelForCausalLM
from peft import PeftModel
import os
import json
from bert_score import score
from tqdm import tqdm
# 设置可见GPU设备(根据实际GPU情况调整)
os.environ["CUDA_VISIBLE_DEVICES"] = "0"  # 指定仅使用GPU 

# 路径配置 ------------------------------------------------------------------------
base_model_path = "xxxxxx/DeepSeek-R1-Distill-Qwen-7B"  # 原始预训练模型路径
peft_model_path = "xxxxxx/output"  # LoRA微调后保存的适配器路径

# 模型加载 ------------------------------------------------------------------------
# 初始化分词器(使用与训练时相同的tokenizer)
tokenizer = AutoTokenizer.from_pretrained(base_model_path)

# 加载基础模型(半精度加载节省显存)
base_model = AutoModelForCausalLM.from_pretrained(
    base_model_path,
    torch_dtype=torch.float16,  # 使用float16精度
    device_map="auto"           # 自动分配设备(CPU/GPU)
)

# 加载LoRA适配器(在基础模型上加载微调参数)
lora_model = PeftModel.from_pretrained(
    base_model, 
    peft_model_path,
    torch_dtype=torch.float16,
    device_map="auto"
)
# 合并LoRA权重到基础模型(提升推理速度,但会失去再次训练的能力)
lora_model = lora_model.merge_and_unload()  
lora_model.eval()  # 设置为评估模式

# 生成函数 ------------------------------------------------------------------------
def generate_response(model, prompt):
    """统一的生成函数
    参数:
        model : 要使用的模型实例
        prompt : 符合格式要求的输入文本
    返回:
        清洗后的回答文本
    """
    # 输入编码(保持与训练时相同的处理方式)
    inputs = tokenizer(
        prompt,
        return_tensors="pt",          # 返回PyTorch张量
        max_length=1024,               # 最大输入长度(与训练时一致)
        truncation=True,              # 启用截断
        padding="max_length"          # 填充到最大长度(保证batch一致性)
    ).to(model.device)               # 确保输入与模型在同一设备

    # 文本生成(关闭梯度计算以节省内存)
    with torch.no_grad():
        outputs = model.generate(
            input_ids=inputs.input_ids,
            attention_mask=inputs.attention_mask,
            max_new_tokens=1024,       # 生成内容的最大token数(控制回答长度)
            temperature=0.7,         # 温度参数(0.0-1.0,值越大随机性越强)
            top_p=0.9,               # 核采样参数(保留累积概率前90%的token)
            repetition_penalty=1.1,  # 重复惩罚系数(>1.0时抑制重复内容)
            eos_token_id=tokenizer.eos_token_id,  # 结束符ID
            pad_token_id=tokenizer.pad_token_id,  # 填充符ID 
        )
    
    # 解码与清洗输出
    full_text = tokenizer.decode(outputs[0], skip_special_tokens=True)  # 跳过特殊token
    answer = full_text.split("### 答案:n")[-1].strip()  # 提取答案部分
    return answer

# 对比测试函数 --------------------------------------------------------------------
def compare_models(question):
    """模型对比函数
    参数:
        question : 自然语言形式的医疗问题(如"小孩感冒怎么办?")
    """
    # 构建符合训练格式的prompt(注意与训练时格式完全一致)
    prompt = f"诊断问题:{question}n详细分析:n### 答案:n"
    
    # 双模型生成
    base_answer = generate_response(base_model, prompt)  # 原始模型
    lora_answer = generate_response(lora_model, prompt)  # 微调模型
    
    # 终端彩色打印对比结果
    print("n" + "="*50)  # 分隔线
    print(f"问题:{question}")
    print("-"*50)
    print(f"33[1;34m[原始模型]33[0mn{base_answer}")  # 蓝色显示原始模型结果
    print("-"*50)
    print(f"33[1;32m[LoRA模型]33[0mn{lora_answer}")  # 绿色显示微调模型结果
    print("="*50 + "n")

# 主程序 ------------------------------------------------------------------------
if __name__ == "__main__":
    # 测试问题集(可自由扩展)
    # test_questions = [
    #     "根据描述,一个1岁的孩子在夏季头皮出现多处小结节,长期不愈合,且现在疮大如梅,溃破流脓,口不收敛,头皮下有空洞,患处皮肤增厚。这种病症在中医中诊断为什么病?"
    # ]
    
    # # 遍历测试问题
    # for q in test_questions:
    #     compare_models(q)
    # 加载测试数据
    ####-----------批量测试---------------#
    with open("xxxxxx/data/medical_o1_sft_Chinese.json") as f:
        test_data = json.load(f) 

    # 数据量比较大,我们只选择10条数据进行测试
    test_data=test_data[:10]
    # 批量生成回答
    def batch_generate(model, questions):
        answers = []
        for q in tqdm(questions):
            prompt = f"诊断问题:{q}n详细分析:n### 答案:n"
            ans = generate_response(model, prompt)
            answers.append(ans)
        return answers

    # 生成结果
    base_answers = batch_generate(base_model, [d["Question"] for d in test_data])
    lora_answers = batch_generate(lora_model, [d["Question"] for d in test_data])
    ref_answers = [d["Response"] for d in test_data]

    bert_model_path="xxxxx/model/bert-base-chinese"
    # 计算BERTScore
    _, _, base_bert = score(base_answers, ref_answers, lang="zh",model_type=bert_model_path,num_layers=12,device="cuda")
    _, _, lora_bert = score(lora_answers, ref_answers, lang="zh",model_type=bert_model_path,num_layers=12,device="cuda")
    print(f"BERTScore | 原始模型: {base_bert.mean().item():.3f} | LoRA模型: {lora_bert.mean().item():.3f}")

来看看结果:

从 BERTScore 的比较来看,LoRA 微调后的结果与原始模型相比确实有细微差异。随着训练轮次加深,甚至故意让大模型产生“过拟合”后,这种相似性差异会进一步加大。这从一个相对定性的角度,为学习者提供了一个新的观察视角。

3. 后记

大模型的评测是一个相对复杂且体系化的领域,尤其是金融与医疗领域涉及较强的专业性。在实际的企业部署中,会有更多样化的方法来评估模型生成的质量。

本文尽可能从初学者的角度切入,让学习者能够简单直接地了解模型微调后与原始模型的差异。

热点追踪提示词
你是一名 AI 行业编辑,请围绕下面这条热点输出一份资讯解读:
热点:零基础入门DeepSeek微调评测教程要求:
1. 先用一句话解释这条热点在讲什么
2. 再总结它为什么重要
3. 说明会影响哪些 AI 产品或内容方向
4. 最后给出 3 个适合资讯站使用的标题
来源:https://www.53ai.com/news/finetuning/2025030126493.html
ai 人工智能

游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。

相关热点
AI热点2026-07-01 19:33
EcomStat集中式分析平台一站式数据洞察与可视化

做电商的朋友都知道,利润往往藏在细节里。很多时候,光看平台表面数据是远远不够的——成本结构不清晰、各渠道数据分散、利润计算滞后,这些问题一叠加,赚钱就变成了“糊涂账”。EcomStat 正是针对这些痛点推出的一个集中式分析平台。它的核心逻辑很简单:把零散的数据拉到一个仪表盘上,让成本、利润、费用和客

AI热点2026-07-01 19:33
AI智能财务管家:GPT驱动的个人支出管理应用

说到日常记账,你是不是也试过不少App,结果往往坚持不了几天就放弃了?别急,这位AI理财助手可能不太一样——它更像身边一个默默帮你盯紧钱&包的朋友。 什么是AI Money Manager – GPT Expense? AI理财管家 – GPT消费记录,这是一款运行在Android平台上的财经应用。

AI热点2026-07-01 19:33
TaxBuzz AI税务会计专业人士人工智能浏览器扩展

财税行业的朋友们,最近发现一款非常实用的AI工具——TaxBuzz Ai,这是一款专为税务和会计专业人士设计的Chrome浏览器扩展。它的核心亮点在于:能够实时提供AI辅助与专业见解,帮助简化工作流程、提升准确性,同时确保符合IRS(美国国税局)规定。最便捷的是,它可以无缝嵌入您正在浏览的任何网页,

AI热点2026-07-01 19:33
Pump 人工智能团购云成本优化工具

想象一下,将人工智能与团购模式相结合,专门用于云成本优化——这正是Pump在做的事情。它能让初创企业在AWS、GCP或Azure上的云支出,通过自动选购性价比最高的承诺服务实现智能省钱,整个过程无需工程团队介入。而且,Pump本身完全免费,目标就是让初创公司拥有与大厂同等级别的云成本控制能力。 什么

延伸阅读