从LoRA到OFT:Qwen2.5-VL在昇腾910B与4090上的多模态微调实践与踩坑记录

本文系统记录了在华为昇腾910B英伟达4090两种硬件平台上,针对两个不同规模数据集(其中一个为从头构建的签字识别数据集)基于LLamafactory进行Qwen2.5-VL-3B多模态大模型微调的完整实验过程。实验主要探索了LoRA、Full Fine-tuning、Freeze Fine-tuning和OFT四种主流微调方法,并对学习率、训练轮数、LoRA秩、批处理大小、精度类型等数十个超参数进行了系统性对比测试,并在此过程中解决Oft无法正常推理和测试等问题。

实验采用"数据预处理和构建 → 模型微调 → 效果验证"的三阶段技术路线,核心思想是利用多模态大模型的视觉-语言理解能力,通过有针对性的微调使其适应特定领域任务需求。

本文聚焦于实验流程梳理、数据构建、环境配置、问题排查和结果总览,旨在为多模态微调实践者提供完整的参考路径。关于具体超参数的深入分析和调优建议,将在后续文章中详细展开。

文章目录


🎉进入大模型应用与实战专栏 | 🚀查看更多专栏内容


实验环境说明

硬件与软件环境

实验在两套服务器上并行开展,环境配置如下:

华为昇腾910B服务器:

  • 核心库版本: llamafactory 0.9.4.dev0peft 0.17.1transformers 4.57.1
  • 主要用于: sign数据集(小样本审批文档识别任务)的全面测试,由于显卡问题只在单卡上做了测试
  • 特点: 适配国产AI芯片,部分功能存在兼容性差异

英伟达4090服务器:

  • 核心库版本: llamafactory 0.9.3.dev0peft 0.15.2transformers 4.52.0
  • 主要用于: llava数据集(大样本通用视觉问答任务)的对比实验,在四卡上做了测试
  • 特点: 生态成熟,推理性能更优

由于硬件架构和框架版本差异,部分微调方式仅在特定平台测试:

  • 英伟达平台: 完整测试了LoRA、Full、Freeze三种方法
  • 华为平台: 由于LLamafactory版本更新,所以额外补充了OFT微调方法的验证,Full由于多卡微调会出问题不测试

微调方法简介

本实验涉及四种主流微调策略:

1. LoRA (Low-Rank Adaptation)

  • 原理: 在预训练权重旁注入低秩分解矩阵,仅训练轻量级适配器
  • 优势: 参数效率高(典型适配器仅58MB),显存占用低,训练快速
  • 适用场景: 资源受限环境下的快速迭代

2. Full Fine-tuning (全量微调)

  • 原理: 更新模型全部参数
  • 优势: 理论性能上限最高,适合大规模数据场景
  • 劣势: 需要DeepSpeed ZeRO-3等分布式策略,显存需求大(22GB+)(约7GB模型文件)

3. Freeze Fine-tuning (冻结微调)

  • 原理: 冻结大部分层,仅训练顶层若干Transformer块
  • 优势: 平衡性能与效率,适合微调最后几层语义理解,生成的模型文件大小介于LoRA和Full之间(7.28-8.14GB)
  • 配置: 通过freeze_trainable_layers控制可训练层数

4. OFT (Orthogonal Fine-Tuning)

  • 原理: 通过正交变换矩阵进行参数高效微调
  • 特点: 适配器体积极小(50MB),但框架支持尚不完善(由于LlamaFactory版本更新,OFT功能仅在华为平台的新版本中可用)

数据集说明

llava-en-zh-2k数据集(通用场景)

  • 样本规模: 约2000条样本,按8:2比例划分训练集和测试集
  • 任务特性: 通用的图像理解和描述任务,同一图像可有多种合理描述
  • 评估重点: 生成文本的流畅性和覆盖度,ROUGE-L作为主要指标
  • 基线性能: 预训练模型在该数据集上ROUGE-L为22.12,BLEU-4为7.39

sign数据集(专业场景)

  • 样本规模: 小样本数据集(共60条数据),按7:3比例划分训练集和测试集
  • 任务特性: 识别审批文档中的意见和签字信息,要求精确提取
  • 评估重点: 信息提取的准确性,BLEU-4作为重要参考
  • 基线性能: 预训练模型ROUGE-L为79.11,BLEU-4为67.05
  • 数据处理: 经历标签分离、文档图像化、问答对构建三个步骤

两个数据集的核心区别在于:llava数据集侧重生成能力 的泛化评估,而sign审批文档数据集侧重信息抽取的精准度评估。前者关注模型是否能用自然语言描述图像内容,后者关注模型能否准确定位并提取结构化信息。

数据预处理流程

sign数据集(专业场景)

上述为一个原始数据样例,为转换为可微调的数据集,需经历以下步骤。

第一步:标签分离与文本化

  • 从原始标注文档中提取所有标注标签(包括审批意见区域标注、签字位置标注等)
  • 将这些标签信息转换为自然语言描述,形成结构化的文本输出
  • 确保输出格式统一规范,便于模型学习一致的输出模式

第二步:文档图像化处理

  • 将去除标注后的文档转换为高质量图像格式
  • 保持图像清晰度,确保文字和签名等细节信息不丢失
  • 统一图像尺寸规格,便于批量处理

第三步:问答对构建

  • 设计通用的提示词模板,明确指导模型完成审批意见和签字的识别任务
  • 将文档图像与提示词组合作为模型的输入上下文(input)
  • 将标签转换后的结构化文本作为模型的期望输出(output)
  • 按照ShareGPT规范整理成多轮对话格式,每个文档形成一组完整的问答对

llava-en-zh-2k数据集(通用场景)

由于sign数据集样本量过少,为了获得更加通用的效果对比,特意基于llava-en-zh-2k数据集同步做对照试验。

huggingface官网上下载llava-en-zh-2k数据集,下载并上传到data路径下。

data/dataset_info.json文件中做如下修改

json 复制代码
  "llava_zh": {
    "file_name": "train-00000-of-00001.parquet",
    "subset": "zh",
    "formatting": "sharegpt",
    "columns": {
      "messages": "messages",
      "images": "images"
    },
    "tags": {
      "role_tag": "role",
      "content_tag": "content",
      "user_tag": "user",
      "assistant_tag": "assistant"
    }
  },

在LLamafactory中选择该数据集,点击预览没反应,后台报错如下:

bash 复制代码
Traceback (most recent call last):
  File "/home/df1500/anaconda3/envs/llamafactory/lib/python3.10/site-packages/gradio/routes.py", line 1283, in predict
    output = await route_utils.call_process_api(
  File "/home/df1500/anaconda3/envs/llamafactory/lib/python3.10/site-packages/gradio/route_utils.py", line 349, in call_process_api
    output = await app.get_blocks().process_api(
  File "/home/df1500/anaconda3/envs/llamafactory/lib/python3.10/site-packages/gradio/blocks.py", line 2123, in process_api
    result = await self.call_function(
  File "/home/df1500/anaconda3/envs/llamafactory/lib/python3.10/site-packages/gradio/blocks.py", line 1630, in call_function
    prediction = await anyio.to_thread.run_sync(  # type: ignore
  File "/home/df1500/anaconda3/envs/llamafactory/lib/python3.10/site-packages/anyio/to_thread.py", line 56, in run_sync
    return await get_async_backend().run_sync_in_worker_thread(
  File "/home/df1500/anaconda3/envs/llamafactory/lib/python3.10/site-packages/anyio/_backends/_asyncio.py", line 2485, in run_sync_in_worker_thread
    return await future
  File "/home/df1500/anaconda3/envs/llamafactory/lib/python3.10/site-packages/anyio/_backends/_asyncio.py", line 976, in run
    result = context.run(func, *args)
  File "/home/df1500/anaconda3/envs/llamafactory/lib/python3.10/site-packages/gradio/utils.py", line 915, in wrapper
    response = f(*args, **kwargs)
  File "/home/df1500/wangjn/LLaMA-Factory/src/llamafactory/webui/components/data.py", line 77, in get_preview
    data = _load_data_file(data_path)
  File "/home/df1500/wangjn/LLaMA-Factory/src/llamafactory/webui/components/data.py", line 67, in _load_data_file
    return list(f)
  File "/home/df1500/anaconda3/envs/llamafactory/lib/python3.10/codecs.py", line 322, in decode
    (result, consumed) = self._buffer_decode(data, self.errors, final)
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xa0 in position 44: invalid start byte

如果你此时按照它的提示,将文件转换为utf-8编码,预览结果会是一个矩阵,不管是训练还是推理,都会遇到下面的提示:

bash 复制代码
Factory/src/llamafactory/data/loader.py", line 131, in _load_single_dataset
    dataset = load_dataset(
  File "/home/df1500/anaconda3/envs/llamafactory/lib/python3.10/site-packages/datasets/load.py", line 1412, in load_dataset
    builder_instance.download_and_prepare(
  File "/home/df1500/anaconda3/envs/llamafactory/lib/python3.10/site-packages/datasets/builder.py", line 894, in download_and_prepare
    self._download_and_prepare(
  File "/home/df1500/anaconda3/envs/llamafactory/lib/python3.10/site-packages/datasets/builder.py", line 948, in _download_and_prepare
    split_generators = self._split_generators(dl_manager, **split_generators_kwargs)
  File "/home/df1500/anaconda3/envs/llamafactory/lib/python3.10/site-packages/datasets/packaged_modules/parquet/parquet.py", line 60, in _split_generators
    self.info.features = datasets.Features.from_arrow_schema(pq.read_schema(f))
  File "/home/df1500/anaconda3/envs/llamafactory/lib/python3.10/site-packages/pyarrow/parquet/core.py", line 2393, in read_schema
    file = ParquetFile(
  File "/home/df1500/anaconda3/envs/llamafactory/lib/python3.10/site-packages/pyarrow/parquet/core.py", line 328, in __init__
    self.reader.open(
  File "pyarrow/_parquet.pyx", line 1656, in pyarrow._parquet.ParquetReader.open
  File "pyarrow/error.pxi", line 92, in pyarrow.lib.check_status
pyarrow.lib.ArrowInvalid: Parquet magic bytes not found in footer. Either the file is corrupted or this is not a parquet file.

这个错误表明 Parquet 文件已损坏或不是有效的 Parquet 文件,因此直接保留原来的编码格式即可。之前的错误是因为 LLaMAFactory 的 WebUI 预览功能试图用文本模式读取 Parquet 二进制文件导致的,不预览数据而是直接训练或推理即可解决此问题

若固定使用70%数据作为训练集,30%数据作为测试集。数据集分割代码如下:

py 复制代码
import pyarrow.parquet as pq
import pyarrow as pa
import numpy as np

# 读取原始parquet文件
input_file = 'LLaMA-Factory/data/train-00000-of-00001.parquet'
train_file = 'LLaMA-Factory/data/llava_train.parquet'
test_file = 'LLaMA-Factory/data/llava_test.parquet'

try:
    # 读取数据
    table = pq.read_table(input_file)
    print("文件读取成功")
    print(f"总行数: {table.num_rows}")
    print(f"Schema: {table.schema}")
    
    # 获取总行数
    total_rows = table.num_rows
    
    # 生成随机索引并打乱
    indices = np.arange(total_rows)
    np.random.seed(42)  # 设置随机种子以保证可复现性
    np.random.shuffle(indices)
    
    # 计算分割点
    train_size = int(total_rows * 0.8)
    
    # 分割索引
    train_indices = indices[:train_size]
    test_indices = indices[train_size:]
    
    # 按索引分割数据
    train_table = table.take(train_indices)
    test_table = table.take(test_indices)
    
    # 保存训练集
    pq.write_table(train_table, train_file)
    print(f"\n训练集保存成功: {train_file}")
    print(f"训练集行数: {train_table.num_rows} ({train_table.num_rows/total_rows*100:.1f}%)")
    
    # 保存测试集
    pq.write_table(test_table, test_file)
    print(f"\n测试集保存成功: {test_file}")
    print(f"测试集行数: {test_table.num_rows} ({test_table.num_rows/total_rows*100:.1f}%)")
    
except Exception as e:
    print(f"处理失败: {e}")
    import traceback
    traceback.print_exc()

评估指标分析与选择

预训练模型直接推理llava-en-zh-2k数据集的效果如下:

py 复制代码
{
    "predict_bleu-4": 7.1718031,
    "predict_model_preparation_time": 0.0356,
    "predict_rouge-1": 31.2310681,
    "predict_rouge-2": 8.1065009,
    "predict_rouge-l": 21.9558203,
    "predict_runtime": 9877.2846,
    "predict_samples_per_second": 0.101,
    "predict_steps_per_second": 0.101
}
指标 导向 计算逻辑 适用场景
ROUGE-L 召回率 最长公共子序列/参考长度 衡量内容覆盖度,对词序重组不敏感
ROUGE-1 召回率 单词重叠数/参考词数 反映词汇召回率
ROUGE-2 召回率 二元组重叠/参考二元组数 反映局部流畅度
BLEU-4 精确率 4-gram精确匹配+长度惩罚 衡量翻译准确性,严格匹配

ROUGE 是召回导向 的指标,关注生成文本覆盖了多少参考文本。BLEU 是精确导向的指标,最初为机器翻译设计,关注生成文本的精确度。下面是对不同指标的解释:

  • ROUGE-L:最长公共子序列匹配,反映整体语义连贯性,最全面反映生成质量,对句子重组不敏感

    python 复制代码
    参考答案: "这 张 图片 展示 了 一只 猫 坐在 沙发 上"
    生成答案: "图片 中 有 一只 猫 坐在 沙发 上"
    
    # 步骤1: 找最长公共子序列 (不要求连续)
    LCS = ["图片", "一只", "猫", "坐在", "沙发", "上"]  # 长度 = 6
    
    # 步骤2: 计算 ROUGE-L
    参考答案长度 = 10
    ROUGE-L = 6 / 10 = 0.60 (60%)
  • ROUGE-1:单词级别重叠,反映词汇召回率

    python 复制代码
    参考答案 (Reference): "猫坐在沙发上"
    生成答案 (Candidate): "一只猫在沙发"
    
    # 步骤1: 分词
    Reference tokens: ["猫", "坐", "在", "沙发", "上"]  # 5个词
    Candidate tokens: ["一只", "猫", "在", "沙发"]      # 4个词
    
    # 步骤2: 计算匹配
    匹配的词: ["猫", "在", "沙发"]  # 3个词
    
    # 步骤3: 计算 ROUGE-1
    ROUGE-1 = 匹配数 / 参考答案总词数
            = 3 / 5 
            = 0.60 (60%)
  • ROUGE-2:二元组重叠,反映流畅度

    python 复制代码
    参考答案: "猫坐在沙发上"
    生成答案: "一只猫在沙发"
    
    # 步骤1: 提取 2-gram (二元组)
    Reference 2-grams: ["猫坐", "坐在", "在沙发", "沙发上"]  # 4个
    Candidate 2-grams: ["一只猫", "猫在", "在沙发"]         # 3个
    
    # 步骤2: 计算匹配
    匹配的 2-gram: ["在沙发"]  # 只有1个
    
    # 步骤3: 计算 ROUGE-2
    ROUGE-2 = 1 / 4 = 0.25 (25%)
  • BLEU-4:4-gram 精确匹配,反映翻译/生成准确性

    python 复制代码
    参考答案: "这 只 猫 坐 在 沙发 上"  # 长度 r=7
    生成答案: "猫 在 沙发 上"          # 长度 c=4
    
    # 步骤1: 计算各 n-gram 精确度
    
    # 1-gram (unigram)
    Candidate 1-grams: ["猫", "在", "沙发", "上"]
    匹配: ["猫", "在", "沙发", "上"] 全匹配
    p_1 = 4/4 = 1.0
    
    # 2-gram (bigram)
    Candidate 2-grams: ["猫在", "在沙发", "沙发上"]
    Reference 2-grams: ["这只", "只猫", "猫坐", "坐在", "在沙发", "沙发上"]
    匹配: ["在沙发", "沙发上"]
    p_2 = 2/3 = 0.667
    
    # 3-gram
    Candidate 3-grams: ["猫在沙发", "在沙发上"]
    Reference 3-grams: ["这只猫", "只猫坐", "猫坐在", "坐在沙发", "在沙发上"]
    匹配: ["在沙发上"]
    p_3 = 1/2 = 0.5
    
    # 4-gram
    Candidate 4-grams: ["猫在沙发上"]
    Reference 4-grams: ["这只猫坐", "只猫坐在", "猫坐在沙发", "坐在沙发上"]
    匹配: 无
    p_4 = 0/1 = 0
    
    # 步骤2: 计算简短惩罚,输出长度c越小,BP越小
    c = 4, r = 7
    BP = exp(1 - 7/4) = exp(-0.75) = 0.472
    
    # 步骤3: 计算 BLEU-4
    BLEU-4 = 0.472 × (1.0 × 0.667 × 0.5 × 0)^(1/4)
           = 0.472 × 0  # 因为 p_4 = 0
           = 0

    重要特性:修正计数 (Clipped Count)

    防止重复词获得过高分数:

    python 复制代码
    参考答案: "猫 在 沙发 上"
    生成答案: "猫 猫 猫 猫"  # 恶意重复
    
    # 不修正的情况:
    p_1 = 4/4 = 1.0  # 错误地得到满分!
    
    # 修正计数:
    "猫" 在参考答案中只出现 1 次
    Count_clip("猫") = min(4, 1) = 1
    p_1 = 1/4 = 0.25  # 正确反映了质量

因此当ROUGE 高但 BLEU 低,说明生成流畅但准确性不足;BLEU 高但 ROUGE 低,说明过于保守缺乏泛化,下面是一个简单的例子

参考答案: "这张图片展示了一只猫坐在沙发上"
生成答案: "图片中有一只猫坐在沙发上"

ROUGE-L: 高 ✓ (捕捉到核心语义)

ROUGE-1: 高 ✓ (词汇重叠多)

ROUGE-2: 低 ✗ (二元组不完全匹配)

BLEU-4: 低 ✗ (4-gram几乎无匹配)

考虑到ROUGE-L是召回导向,衡量覆盖度;BLEU-4是精确导向,衡量准确性,最终同时比较这两个指标,覆盖"全面性"和"精确性"。针对多模态任务,考虑到同一图像可以有多种合理描述,可将ROUGE-L作为主指标,BLEU-4作为辅助指标。

评估策略:

  • llava数据集:ROUGE-L为主指标(反映描述全面性),BLEU-4为辅(控制准确性)
  • sign数据集: 两指标并重,高ROUGE-L(>90)和高BLEU-4(>85)共同确保结构化输出的准确性

实验问题记录与解决方案

在多平台、多版本、多模态的复杂实验环境中,我们遇到了诸多技术挑战。以下按问题来源分类整理:

一、LLamaFactory框架使用问题

1. 图像Token数量不匹配

错误信息:

bash 复制代码
ValueError: The number of images does not match the number of <image> tokens

根本原因:

Qwen2.5-VL在处理图像时,会根据image_max_pixels配置动态生成<image> token。若数据预处理时图像数量与消息中的token占位符不一致,会导致加载失败。

解决方案:

确保每条数据的images列表长度与消息文本中<image>标记数量严格对应:

python 复制代码
# 正确示例
{
  "images": ["img1.jpg"],  # 1张图
  "messages": [
    {"role": "user", "content": "<image>请描述"}  # 1个token
  ]
}
2. 数据集配置错误

错误信息:

bash 复制代码
RuntimeError: Cannot find valid samples, check `data/README.md` for the data format.

根本原因:

dataset_info.json中的formattingcolumnstags字段配置与实际数据格式不符,导致LLamaFactory无法解析样本。

关键配置项:

json 复制代码
"program_test": {
    "file_name": "program_test.json",
    "formatting": "sharegpt",
    "columns": {
      "messages": "messages",
      "images": "images"
    },
    "tags": {
      "role_tag": "role",
      "content_tag": "content",
      "user_tag": "user",
      "assistant_tag": "assistant",
      "system_tag": "system"
    }
  },
3. 上下文长度过短

模型加载完成后,训练时出现下面的报错:

bash 复制代码
Image features and image tokens do not match: tokens: 1222, features 1280

根本原因:

多模态输入(图像+文本)转换为token后超过cutoff_len默认值2048,导致截断时破坏了图像特征的完整性。

解决方案:

根据任务调整截断长度:

yaml 复制代码
cutoff_len: 4096  # llava数据集推荐
cutoff_len: 8192  # 复杂文档识别任务

实验验证:

llava数据集在cutoff_len=2048时ROUGE-L仅22.12,调整为4096后恢复正常。

二、硬件平台特异性问题

4. 华为平台多卡训练/推理异常

异常现象:

使用多卡训练后,推理输出乱码:

python 复制代码
#includeHumanHuman以下以下HumanHumanimport以下#includefromHumanHumanHuman#include#defdefimport以下#voiddefHumanHumanHumandefimportTheHuman以下voidimportimport以下Human以下以下以下#Human以下Human以下#includeHuman#includeHumanimport以下以下defimportHumanHuman以下以下#Human以下#HumanGivenvoiddef#HumanThe以下#importdef以下HumanHuman以下importThe以下**#includeHumanfromimportHumanGivenHuman以下以下importdefimportHumanHumanHumanimportimportdefimportThe以下**importHuman#include以下**#includeThedef#Humandef以下import#voiddef##includeHumanHumanvoidHuman#以下import#includevoidimportdef以下#include以下Human#import以下Human以下Human以下以下Human#Human以下Human#import以下voidHuman#The以下**HumanHumanGiven****HumanHuman以下以下import**Humandef#include以下**Humanvoid#HumanHuman以下import#includeimportimport以下importvoidimport以下#defHumanfromfrom#import以下Human以下import以下from#include#include以下#以下void#Humanimportimportimport**#以下from#The以下

根本原因:

华为昇腾NPU在多卡并行时的通信机制与CUDA不完全一致,可能导致权重同步异常。

临时解决方案:

强制使用单卡:

bash 复制代码
ASCEND_RT_VISIBLE_DEVICES=4 DISABLE_VERSION_CHECK=1 GRADIO_SERVER_PORT=7868 llamafactory-cli webui
5. 多卡情况下DeepSpeed ZeRO-3与Pure BF16冲突

错误信息:

bash 复制代码
ValueError: pure_bf16 is incompatible with DeepSpeed ZeRO-3

根本原因:

这源于 DeepSpeed 的架构设计与 Hugging Face Trainer 精度管理机制之间的冲突

  • ZeRO-3 的分片机制 :DeepSpeed ZeRO-3 的核心在于将模型的所有参数(Parameters)、梯度和优化器状态都分片存储在不同的 GPU 上。
  • "Pure BF16" 的定义 :所谓的 "Pure BF16"(纯 BF16)是指在训练过程中,模型权重始终 以 BF16 格式存储,不保存 FP32 副本
  • DeepSpeed 的设计 :DeepSpeed 默认采用混合精度训练。在 ZeRO-3 模式下,它通常需要保留一份 FP32 的"主权重"(Master Weights)来确保数值更新的精度。
  • 代码层面的强制校验 :Hugging Face 的 Trainer 或底层 accelerate 库在检测到开启了 pure_bf16=True 且同时配置了 ZeRO-3 时,会主动抛出此错误。这是因为在 ZeRO-3 下,由于参数被高度分片,DeepSpeed 的内部缓冲区管理目前不支持这种完全抛弃 FP32 主权重的模式。

三、模型架构特殊性问题

6. 无法冻结语言模型层

针对 Qwen2.5-VL 在微调时开启 freeze_language_model=True 出现的错误:

ValueError: Target modules set() not found in the base model. Please check the target modules and try again.

这个问题基本原因是 框架找不到需要冻结/微调的语言部分模块名称 ,导致最终匹配出的 target modules 为空 ,从而抛出这个错误。错误不是模型本身损坏,而是冻结语言层功能无法自动识别 Qwen2.5-VL 这种复杂多模态模型的内部语言模块,因此 target_modules 最终是空集。

经典的 PEFT/LoRA 框架 (如 Hugging Face PEFT)在查找需要应用 LoRA 或冻结的模块时,默认是基于标准语言模型结构的模块名(如 q_proj, k_proj, v_proj, o_proj, mlp, 等等)。

对于标准语言模型这些名称匹配正常;但在 Qwen2.5-VL 这种多模态结构里:

  • 语言模型部分往往被封装为更复杂的子模块(比如嵌套在 language_model.model.layers);
  • 有时它的层名称、嵌套结构可能不完全匹配 PEFT 默认假设的层模式。
7. Freeze微调时的dtype冲突

在 Freeze 微调 QwenVL(Qwen-VL / Qwen2.5-VL)模型时,设置 dtype=fp32 出现错误:

RuntimeError: Expected attn_mask dtype to be bool or float or to match query dtype, but got attn_mask.dtype: c10::BFloat16 and query.dtype: float instead.

这是因为QwenVL / Qwen2.5-VL 系列模型在加载时,为了节省显存和加速训练(官方设计就是 BF16 优先):

  • 默认权重 dtype = BF16
  • 默认 attention mask dtype = BF16
  • vision encoder / projector / embedding 默认 BF16

当使用 Freeze 微调(冻结语言模型)时:

text 复制代码
语言模型参数 frozen
视觉模块 / projector / adapter 可训练

很多训练框架实现 Freeze 时会做:

python 复制代码
model = model.float()   # ❗只对可训练模块转 FP32

结果是:

组件 dtype
query (来自 trainable module) FP32
key/value (来自 frozen BF16 权重) BF16
attn_mask(预处理阶段生成) BF16

这个错误的本质是:模型的某些组件(通常是视觉编码器或特定的注意力层)内部强制生成了 BF16 格式的掩码,而你全局定义的查询向量(Query)却是 FP32 格式。

为什么LoRA不会出现这样的报错呢?因为Freeze 微调本质上是全量微调代码逻辑的子集 。当冻结了 LLM 但微调 Projector 时,模型走的是原生 forward 路径。当使用 LoRA 时,实际上是在原模型上"挂载"了额外的层。PEFT 库在处理多模态模型时做了以下几件事:

  • 适配器精度对齐 :LoRA 层(A 矩阵和 B 矩阵)通常默认以 FP32 初始化,并在训练时自动匹配当前激活值的 dtype。在 LoRA 微调过程中,基础模型权重仍然保留原来的 dtype(通常是 FP16/BF16) ,只有 LoRA 适配器本身有可训练权重(通常是 FP32 或者自动适配的 dtype)。但 注意力核心计算依然是以基础模型的 dtype(FP16/BF16)执行,因此 attn_mask 和 query/k/v 都是统一 dtype,不会触发 dtype mismatch 错误。
  • 模块自动转换 :PEFT 的 get_peft_model 内部包含了一些针对 vision_towerprojector 的精度补丁(Patch)。它会确保在 LoRA 计算路径上的 Tensor 精度是统一的。
  • 计算链路独立 :LoRA 是一种加法逻辑。即使存在一些微小的精度不匹配,PEFT 往往会在输入层进入注意力机制前,通过框架层面强制执行一次 .to(model.dtype),从而规避了 Mask 冲突。

四、版本兼容性问题

8. Neat Packing功能失效

错误信息:

bash 复制代码
ValueError: Neat packing is incompatible with transformers>=4.53.0.

背景:

Neat Packing是LLamaFactory的序列打包优化功能,可避免不同样本间的交叉注意力污染。但在transformers 4.53.0+版本中,底层Attention实现变更导致此功能失效。

影响范围:

华为平台(transformers 4.57.1)受影响,英伟达平台(4.52.0)正常。

临时方案:

yaml 复制代码
# 禁用neat_packing,仅使用基础packing
packing: true
neat_packing: false  # 或直接删除此行

根本解决:

等待LlamaFactory适配新版transformers,或降级到4.52.x。

9. OFT适配器推理失败

错误信息:

bash 复制代码
ValueError: Adapter is only valid for the LoRA method.

问题根源:

LlamaFactory在加载适配器时,错误地将OFT适配器误判为非LoRA方法,导致推理阶段拒绝加载。

代码缺陷位置:

python 复制代码
# llamafactory/hparams/parser.py:119
if model_args.adapter_name_or_path is not None and \
   finetuning_args.finetuning_type != "lora":  # ❌ 未包含OFT
    raise ValueError("Adapter is only valid for the LoRA method.")

修复方案:

python 复制代码
# 修改判断条件
if model_args.adapter_name_or_path is not None and \
   finetuning_args.finetuning_type not in ["lora", "oft"]:  # ✓ 添加OFT
    raise ValueError(...)

现状评估:

OFT作为新增功能,框架测试覆盖不足,建议优先使用成熟的LoRA方法。

默认参数设置

以下是基于LLamaFactory做多模态微调时的默认参数,详细的微调参数介绍参见:LLaMA-Factory 支持调优算法说明以及LLaMA-Factory 支持参数说明

  • 训练参数配置
参数项 默认值 说明
量化等级 none 是否量化(QLORA)
量化方法 bnb 使用的量化方法
对话模板 qwen2_vl 检验提示词使用的模板
RoPE缩放方法 none RoPE编码的使用方法
加速方式 auto 使用的加速方式
  • 训练核心参数
参数项 默认值 说明
训练阶段 Supervised Fine-Tuning 目前采用的训练方式
数据路径 data 数据文件夹的路径
数据集 llava_zh 预览的数据集
学习率 5e-5 AdamW优化器的学习率
训练轮数 3.0 需要执行的训练总轮数
最大梯度范数 1.0 用于梯度裁剪的范数
最大样本数 100000 每个数据集的最大样本数
计算类型 bf16 是否使用混合精度训练
  • 高级训练参数
参数项 默认值 说明
截断长度 4096 输入序列分词后的最大长度
批处理大小 1 每个GPU处理样本数量
梯度累积 8 梯度累积的步数
学习率调节器 cosine 学习率调度器的名称
  • 部分参数微调设置
参数项 默认值 说明
可训练层数 2 最末尾(+)/最前端(-)可训练层数的数量
可训练模块 all 可训练模块的名称,使用英文逗号分隔多个名称
额外模块 - 除LoRA层以外的可训练模块名称
  • LoRA 参数设置
参数项 默认值 说明
LoRA秩 8 LoRA矩阵的秩大小
LoRA缩放系数 16 LoRA缩放系数大小
LoRA随机丢弃 0 LoRA权重随机丢弃的概率
LoRA+学习率比例 0 LoRA+中B矩阵的学习率倍数
使用 rslora 对LoRA层使用秩稳定缩放方法
使用 DoRA 使用权重分解的LoRA
使用 PiSSA 使用PiSSA方法
新建适配器 在现有的适配器上创建一个新的初始化的适配器
  • 多模态参数设置
参数项 默认值 说明
冻结视觉编码器 冻结模型中的视觉编码器
冻结多模态投影器 冻结模型中的多模态投影器
冻结语言模型 冻结模型中的语言模型
图像最大像素 768*768 输入图像的最大像素数
图像最小像素 32*32 输入图像的最小像素数
  • 其他参数设置
参数项 默认值 说明
序列打包 将序列打包为等长样本。
使用无污染打包 避免打包后的序列产生交叉注意力。
学习提示词 不在提示词的部分添加掩码(仅适用于 SFT)。
不学习历史对话 仅学习最后一轮对话(仅适用于 SFT)。
更改词表大小 更改分词器词表和嵌入层的大小。
使用 LLaMA Pro 仅训练块扩展后的参数。

不同参数下的实验时间/显存/效果记录

llava数据集

LoRA微调
超参数 训练时间 占用显存 ROUGE-L BLEU-4
不微调 - - 22.12 7.39
默认 0:05:07 9912MiB 23.46 9.45
epoch=6 0:10:37 9912MiB 23.74 10.06
epoch=30 0:56:28 9912MiB 22.47 9.75
epoch=6,lr=1e-4 0:10:29 9912MiB 24.30 11.22
epoch=6,lr=2e-4 0:11:05 9912MiB 24.01 11.05
epoch=6,lr=1e-4,clip_norm=2 0:10:29 9912MiB 24.09 10.77
epoch=6,lr=1e-4,clip_norm=0.5 0:10:29 9912MiB 24.16 10.81
epoch=6,lr=1e-4,dtype=fp16 0:10:01 9912MiB 24.55 11.22
epoch=6,lr=1e-4,dtype=fp32 0:10:08 9878MiB 24.21 11.18
epoch=6,lr=1e-4,dtype=pure_bf16 0:10:29 9878MiB 24.36 11.00
epoch=6,lr=1e-4,cutoff_len=2048 0:11:12 9912MiB 22.12 7.39
epoch=6,lr=1e-4,cutoff_len=8192 0:11:09 9912MiB 24.26 10.87
epoch=6,lr=1e-4,batch=2 0:06:02 11656MiB 23.68 10.60
epoch=6,lr=1e-4,batch=4 0:04:06 15174MiB 23.41 9.90
epoch=6,lr=1e-4,grad_accum_steps=4 0:10:30 9912MiB 24.14 11.14
epoch=6,lr=1e-4,grad_accum_steps=16 0:10:32 9916MiB 23.59 10.24
epoch=6,lr=1e-4,lora_rank=4 0:11:02 9748MiB 24.08 10.65
epoch=6,lr=1e-4,lora_rank=16 0:10:48 10250MiB 24.37 10.82
epoch=6,lr=1e-4,lora_rank=32 0:11:23 10886MiB 24.13 10.72
epoch=6,lr=1e-4, lora_alpha=8 0:10:55 9912MiB 23.97 10.63
epoch=6,lr=1e-4, lora_alpha=32 0:10:54 9912MiB 24.42 11.36
epoch=6,lr=1e-4, lora_alpha=64 0:11:05 9912MiB 24.07 10.94
epoch=6,lr=1e-4, lora_dropout=0.2 0:11:13 9912MiB 24.02 10.64
epoch=6,lr=1e-4, lora_dropout=0.5 0:11:32 9912MiB 24.63 10.89
epoch=6,lr=1e-4, loraplus_lr_ratio=1 0:10:42 9912MiB 24.15 10.86
epoch=6,lr=1e-4, loraplus_lr_ratio=8 0:10:40 9912MiB 23.65 10.93
epoch=6,lr=1e-4, create_new_adapter=true 0:10:34 9912MiB 24.46 10.99
epoch=6,lr=1e-4, use_rslora=true 0:10:31 9912MiB 24.04 11.05
epoch=6,lr=1e-4, use_dora=true 0:15:58 9934MiB 24.08 10.90
epoch=6,lr=1e-4, pissa_convert=true 0:10:34 9912MiB 24.15 11.18
epoch=6,lr=1e-4, freeze_vision_tower=false 0:15:51 10118MiB 24.06 10.86
epoch=6,lr=1e-4, freeze_multi_modal_projector=false 0:10:19 9912MiB 24.38 10.86
epoch=6,lr=1e-4, freeze_language_model=true error【ValueError: Target modules set() not found in the base model. Please check the target modules and try again.】 - - -
epoch=6,lr=1e-4, image_max_pixels=1048576 0:10:31 9912MiB 24.10 11.01
epoch=6,lr=1e-4, image_max_pixels: 4096 0:10:03 9614MiB 23.77 10.42
epoch=6,lr=1e-4,packing=true序列打包(Converting format of dataset) 0:03:09 17296MiB 22.23 8.04
epoch=6,lr=1e-4,packing=true,neat_packing=true序列打包+无污染打包 0:03:47 17616MiB 23.44 9.63
epoch=6,lr=1e-4,train_on_prompt=true,学习提示词 0:10:40 9912MiB 23.37 9.87
epoch=6,lr=1e-4,mask_history=true,不学习历史对话 0:11:07 9912MiB 23.95 11.00
epoch=6,lr=1e-4,mask_history=true,resize_vocab=true,更改词表大小 0:34:09. 23382MiB 23.92 10.84
epoch=6,lr=1e-4,mask_history=true,use_llama_pro=true,仅训练块扩展后的参数 0:05:54 12144MiB 22.71 8.74
QLoRA
epoch=6,lr=1e-4,quantization_bit=8(推理显存7928MiB) 0:23:17 6530MiB 23.90 10.89
epoch=6,lr=1e-4,quantization_bit=4 (推理显存7928MiB) 0:16:02 5096MiB 24.09 10.76
Full微调
超参数 训练时间 占用显存 ROUGE-L BLEU-4
不微调 - - 22.12 7.39
默认 0:19:47 22086MiB 23.43 10.79
epoch=6 0:38:58 22086MiB 23.06 10.24
dtype=fp16 0:18:58 17902MiB 23.75 10.66
dtype=fp32 OOM - - -
dtype=pure_bf16 error【ValueError: pure_bf16 is incompatible with DeepSpeed ZeRO-3.】 - - -
freeze_vision_tower=false 0:34:58 19766MiB 23.28 10.33
freeze_multi_modal_projector=false 0:42:35 22290MiB 23.28 10.44
image_max_pixels: 4096 0:18:26 21920MiB 23.36 10.41
Freeze微调
超参数 训练时间 占用显存 ROUGE-L BLEU-4
不微调 - - 22.12 7.39
默认 0:03:24 12446MiB 21.79 7.57
epoch=10 0:13:01 12446MiB 21.41 7.32
lr=1e-4 0:03:28 12446MiB 21.61 7.32
freeze_trainable_layers=8 0:05:31 20888MiB 22.31 8.30
freeze_trainable_layers=8,dtype=fp16 0:05:34 20888MiB 23.18 8.91
freeze_trainable_layers=8,dtype=fp32 error【RuntimeError: Expected attn_mask dtype to be bool or float or to match query dtype, but got attn_mask.dtype: c10::BFloat16 and query.dtype: float instead.】 - - -
freeze_trainable_layers=8,dtype=pure_bf16 0:04:20 14242MiB 22.26 7.86
freeze_trainable_layers=2,dtype=pure_bf16 0:03:24 10698MiB 21.18 6.66

sign数据集

LoRA微调
超参数 训练时间 占用显存 ROUGE-L BLEU-4
不微调 - - 79.11 67.05
默认(推理显存8453MiB) 0:04:45 17554MiB 79.72 67.89
epoch=6 0:09:37 17554MiB 80.95 69.06
epoch=8 0:11:28 17554MiB 83.39 72.80
epoch=10 0:15:17 17554MiB 83.91 73.32
epoch=15 0:22:21 17554MiB 94.33 90.27
epoch=20 0:31:09 17554MiB 92.37 87.01
epoch=15,clip_norm=10 0:23:35 17554MiB 88.13 80.44
epoch=15,clip_norm=2 0:22:39 17554MiB 94.36 90.39
epoch=15,clip_norm=0.5 0:25:44 17554MiB 94.39 90.27
epoch=15,clip_norm=0.1 0:23:33 17554MiB 94.36 90.39
epoch=15,clip_norm=1e-5 0:23:07 17554MiB 80.06 68.21
epoch=15,dtype=fp16 0:25:13 17606MiB 94.36 90.39
epoch=15,dtype=fp32 0:25:52 17696MiB 90.48 83.75
epoch=15,dtype=pure_bf16 0:25:08 17696MiB 94.33 90.27
epoch=15,cutoff_len=2048 error - - -
epoch=15,cutoff_len=8192 0:25:56 17554MiB 94.36 90.39
epoch=15,batch=2 0:28:13 27597MiB 80.95 69.06
epoch=15,batch=4 oom - - -
epoch=15,grad_accum_steps=4 0:23:50 17526MiB 83.91 73.32
epoch=15,grad_accum_steps=2 0:25:35 17500MiB 93.16 87.79
epoch=15,grad_accum_steps=1 0:24:35 17478MiB 93.08 87.54
epoch=15,grad_accum_steps=16 0:23:20 17666MiB 83.86 73.28
epoch=15,lora_rank=1 0:24:04 17314MiB 94.45 90.35
epoch=15,lora_rank=2 0:24:01 17344MiB 94.45 90.43
epoch=15,lora_rank=4 0:25:01 17408MiB 94.45 90.43
epoch=15,lora_rank=16 0:23:13 17848MiB 94.36 90.39
epoch=15,lora_rank=4, lora_alpha=8 0:10:55 17408MiB 83.71 73.14
epoch=15,lora_rank=4, lora_alpha=32 0:10:54 17408MiB 93.11 87.33
epoch=15,lora_rank=4, lora_dropout=0.2 0:24:25 17796MiB 94.39 90.35
epoch=15,lora_rank=4, lora_dropout=0.8 0:23:42 17796MiB 94.33 90.27
epoch=15,lora_rank=4, freeze_vision_tower=false 0:44:05 17864MiB 92.91 88.00
epoch=15,lora_rank=4, freeze_multi_modal_projector=false 0:24:26 17409MiB 94.39 90.35
epoch=15,lora_rank=4, image_max_pixels=262144 0:10:12 16994→25844MiB 83.77 73.11
epoch=15,lora_rank=4, image_max_pixels: 4096 0:10:03 16027MiB 81.44 69.79
epoch=15,lora_rank=4,packing=true序列打包(Converting format of dataset) 0:30:49 17664MiB 93.11 87.70
epoch=15,lora_rank=4,neat_packing=true无污染打包 error【ValueError: Neat packing is incompatible with transformers>=4.53.0.】 - - -
epoch=15,lora_rank=4,train_on_prompt=true,学习提示词 0:23:58 17408MiB 80.76 68.67
epoch=15,lora_rank=4,mask_history=true,不学习历史对话 0:23:40 17408MiB 94.45 90.35
epoch=15,lora_rank=4,resize_vocab=true,更改词表大小(推理占用11129MB显存) 0:24:39 26854MiB 93.11 87.70
epoch=15,lora_rank=4,use_llama_pro=true,仅训练块扩展后的参数 oom - - -
QLoRA
epoch=15,lora_rank=4,quantization_bit=8(推理时不量化,显存8421MiB) 1:24:45 13834MiB 94.36 90.39
epoch=15,lora_rank=4,quantization_bit=8(推理时量化8,显存8221MiB) - - 94.09 89.93
epoch=15,lora_rank=4,quantization_bit=8(推理时量化4,显存7112MiB) - - 88.58 81.08
epoch=15,lora_rank=4,quantization_bit=4 (推理时不量化,显存8423MiB) 3:03:27 22553MiB 94.36 90.39
epoch=15,lora_rank=4,quantization_bit=4 (推理时量化8,显存8225MiB) - - 90.51 83.92
epoch=15,lora_rank=4,quantization_bit=4 (推理时量化4,显存7113MiB) - - 95.06 91.53
Freeze微调
超参数 训练时间 占用显存 ROUGE-L BLEU-4
不微调 - - 79.11 67.05
默认 0:03:53 18780MiB 82.15 70.77
epoch=15 0:16:25 18780MiB 93.10 87.79
epoch=15,freeze_trainable_layers=4 0:18:40 20649MiB 92.99 87.47
epoch=15,freeze_trainable_layers=8 0:18:54 25918MiB 92.99 87.50
Oft微调
超参数 训练时间 占用显存 ROUGE-L BLEU-4
不微调 - - 79.11 67.05
默认(推理显存8593MiB) 0:06:32 17660MiB 85.89 76.67
epoch=10 0:20:46 17660MiB 93.11 87.33
epoch=15 0:30:47 17660MiB 92.98 87.33
epoch=10,lora_rank=4 0:20:10 17660MiB 92.99 87.16
epoch=10,lora_rank=16 0:20:21 17660MiB 92.99 87.16

不同微调方式下的模型文件大小分析

不同微调方式生成的模型文件大小差异显著:

微调方式 默认大小 参数调整后大小范围
LoRA 58MB 8MB(rank=1)~ 229MB(rank=32)
LoRA+词表扩展 2.4GB -
Full 6.99GB 固定
Freeze 7.28GB 7.28GB ~ 8.14GB(trainable_layers=2~8)
OFT 50MB 固定

关键发现:

  • LoRA的rank参数对文件大小影响巨大:rank=1时仅8MB,rank=32时达229MB,呈线性关系
  • 启用DoRA使LoRA文件从58MB增至62MB,扩展词表(resize_vocab)会导致文件膨胀40倍以上,需谨慎使用
  • 冻结视觉编码器使LoRA文件从58MB增至79MB
  • Freeze微调中增加可训练层数显著增大模型文件
  • OFT方法文件最小,但框架支持尚不完善

不同微调方式下的训练时间与显存占用分析

以llava数据集在英伟达4090上的epoch=6实验为例:

微调方式 训练时间 显存占用 推理显存
LoRA(默认) 10:29 9.9GB 未统计
LoRA(batch=2) 6:02 11.7GB 未统计
LoRA(DoRA) 15:58 9.9GB 未统计
Full(4卡) 38:58 22GB×4 未统计
Freeze(默认) 3:24 12.4GB 未统计

sign数据集在华为910B上的对比:

微调方式 训练时间(epoch=15) 显存占用 推理显存
LoRA(默认) 22:21 17.6GB 8.5GB
LoRA(rank=4) 23:40 17.4GB 8.5GB
LoRA(8bit量化) 1:24:45 13.8GB 8.2GB(8bit)/ 7.1GB(4bit)
LoRA(4bit量化) 3:03:27 22.6GB 8.4GB(不量化)/ 7.1GB(4bit)
Freeze(默认) 16:25 18.8GB 未统计
OFT(epoch=10) 20:46 17.7GB 8.6GB

关键洞察:

  1. 时间效率: Freeze > LoRA > OFT > Full
  2. 显存效率: 量化LoRA > LoRA > Freeze ≈ OFT > Full
  3. 量化悖论: 4bit量化训练时显存反而高于8bit(22.6GB vs 13.8GB),但推理显存最低;Q4/Q8可节省50%显存,但训练时长增加2-3倍(推理不受影响)

不同微调方式的整体表现分析

llava数据集(通用场景)

基线性能: ROUGE-L: 22.12, BLEU-4: 7.39

微调方式 ROUGE-L BLEU-4 相对提升 综合评价
LoRA(epoch=6, lr=1e-4) 24.30 11.22 ★★★★☆ 最佳性价比
Full(epoch=3) 23.43 10.79 ★★★☆☆ 提升有限,成本高
Full(epoch=6) 23.06 10.24 ★★☆☆☆ 过拟合迹象
Freeze(默认) 21.79 7.57 ★☆☆☆☆ 效果不佳

分析要点:

  • LoRA效果最优: 在通用任务上,LoRA以最低的成本(训练时间10分钟,显存10GB)实现了最好的性能提升(ROUGE-L +9.8%, BLEU-4 +51.8%)
  • Full微调反常: 全量微调效果反而不如LoRA,epoch增加至6时性能下降,说明小数据集上容易过拟合
  • Freeze效果差: 仅微调少数层无法充分适应新任务,ROUGE-L甚至低于基线,且微调难度大,冻结层数的选择空间过大

sign数据集(专业场景)

基线性能: ROUGE-L: 79.11, BLEU-4: 67.05

微调方式 ROUGE-L BLEU-4 相对提升 综合评价
LoRA(epoch=15) 94.33 90.27 ★★★★★ 卓越
LoRA(rank=4, epoch=15) 94.45 90.43 ★★★★★ 最佳配置
Freeze(epoch=15) 93.10 87.79 ★★★★☆ 良好
OFT(epoch=10) 93.11 87.33 ★★★★☆ 良好

分析要点:

  • 小样本精准任务: 所有微调方式均取得显著提升(ROUGE-L +15%+, BLEU-4 +30%+)
  • LoRA最佳: 即使降低rank至4,性能几乎不受影响(90.43 vs 90.27)
  • Freeze与OFT相当: 在这个任务上表现接近,都达到87%+的BLEU-4
  • 训练轮数关键: epoch=15时达到峰值,继续增加至20反而下降(87.01)

实验总结与方法论

微调方法选择决策框架

第一步:资源约束筛选
资源维度 约束条件 可选方法 关键考量
显存 <12GB 仅QLoRA 硬约束,无其他选择
12-20GB LoRA、Freeze Full不可行
20GB+多卡 全部可选 权衡成本收益
时间 <1小时 Freeze 最少层数快速验证
数小时 LoRA 主力方法
充裕 Full 警惕小数据过拟合
存储 需频繁分发 LoRA 几十MB vs 数GB
仅内部使用 任意 非主要考量
第二步:数据特征匹配
数据特征 判断标准 策略方向
超小样本 <100条 降rank防过拟合 + 高epoch(10-20轮) + 数据增强
小样本 100-1000条 LoRA标准配置 + 验证集调优epoch
中大样本 1000+条 LoRA性价比最优 + epoch较少即可
精确匹配任务 结构化抽取/OCR 大cutoff_len + 关注BLEU + 增加rank
语义理解任务 描述/问答 标准cutoff_len + 关注ROUGE + 保持多样性
复杂输入 高分辨率/长文档 必须提高cutoff_len + 降batch或梯度累积
简单输入 低分辨率/短文本 标准cutoff_len + 可增大batch

超参数调优优先级

三级参数体系
优先级 参数 调整信号 调整方向
P0-必调 epoch 验证集loss曲线 小样本↑ / 过拟合↓
cutoff_len 数据截断警告 宁大勿小,检查token分布
学习率lr 训练曲线形态 震荡→过大 / 平缓→过小
P1-瓶颈时调 LoRA rank 性能不足 小数据↓防过拟合 / 大数据↑增能力
batch_size 训练稳定性 优先增大,不足用梯度累积
梯度累积 显存受限 保持有效批量大小一致
clip_norm 训练不稳定 出现时调小,否则默认
P2-禁区 dtype - ❌ 除非硬件兼容问题
多参数同调 - ❌ 避免相互影响难定位
实验性功能 - ❌ 框架未标注稳定的功能

标准实验流程

复制代码
阶段一:基线建立 (1-2次)
├─ 目标:验证pipeline + 获取未微调基线
├─ 方法:最小配置(LoRA默认或Freeze最少层)
└─ 关注:数据加载、推理正常、基线指标

阶段二:方法选择 (2-3次)  
├─ 目标:确定最适合的微调方法
├─ 方法:相同epoch对比LoRA/Full/Freeze
└─ 关注:性能、时间、显存的trade-off

阶段三:参数调优 (5-10次)
├─ 目标:优化核心超参数
├─ 方法:单变量法调epoch、lr、cutoff_len
└─ 关注:验证集指标变化趋势

阶段四:细节优化 (按需)
├─ 目标:榨取最后性能
├─ 方法:调rank、梯度策略、数据增强
└─ 关注:收益是否值得复杂度

通用决策树

复制代码
开始任务
│
├→ 显存 < 12GB?─────是→ QLoRA
│   └─否
├→ 需快速验证?──────是→ Freeze
│   └─否
├→ 数据量?
│   ├─ <100 ────→ LoRA (低rank + 高epoch + 防过拟合)
│   ├─ 100-1000 ─→ LoRA (标准配置 + 调epoch/lr)
│   └─ >1000 ────→ LoRA优先 (Full需多卡)
│
├→ 任务类型?
│   ├─ 精确抽取 ─→ 大cutoff_len + 关注BLEU
│   └─ 语义理解 ─→ 标准配置 + 关注ROUGE
│
└→ 达标?─是→ 完成
    └─否→ 按P0→P1→P2顺序调优
相关推荐
孙琦Ray2 小时前
GitHub开源项目月报 · 2026年1月 · 开源AI代理热榜解读
开源·软件开发·多模态·rag·知识管理·ai代理·终端桌面
诸神缄默不语2 小时前
如何用Python调用智谱清言GLM系API实现智能问答
python·ai·大模型·nlp·chatglm·glm·智谱清言
AI扶我青云志3 小时前
深度介绍:Moltbot(原名Clawdbot)
人工智能·大模型·agent
猿小羽3 小时前
深入解析与实践:Prompt Engineering
人工智能·深度学习·ai·大模型·nlp·实践·prompt engineering
特立独行的猫a4 小时前
2026国内外主流大模型全景对比:技术演进与场景适配深度解析
ai·大模型·llm·openai
Haooog18 小时前
AI应用代码生成平台
java·学习·大模型·langchain4j
千桐科技1 天前
qKnow 知识平台商业版 v2.6.1 正式发布:移除对第三方 LLM 应用框架的依赖,一次真正走向自主可控的里程碑升级
大模型·知识图谱·图数据库·知识库·rag·qknow·知识平台
CoderJia程序员甲1 天前
GitHub 热榜项目 - 日榜(2026-01-28)
人工智能·ai·大模型·github·ai教程
世优科技虚拟人1 天前
从AI数字人讲解到MR数字人导览,数字人厂商革新文旅新服务
人工智能·大模型·数字人·智能交互