本文深度拆解LoRA(Low-Rank Adaptation)参数高效微调技术,核心思路是通过冻结大模型基座参数、仅训练小型低秩矩阵,实现低成本的模型定制。文章从大模型微调的核心价值切入,通俗解读LoRA的低秩分解原理与三大核心创新,再结合国产ms-swift框架,完整落地数据准备、模型训练、推理部署全流程,附带可直接运行的实战代码与问题解决方案,帮程序员和大模型小白快速掌握低成本适配特定领域、提升模型性能的关键技能。

1、 为什么需要微调?

用“学生成长”理解预训练与微调

很多小白会疑惑:现成的大模型已经能对话、写代码了,为什么还要微调?其实用“学生成长”的逻辑就能秒懂:

  • 预训练(Pre-training):相当于基础教育阶段,学生在学校系统学习语文、数学、科学等通用知识,构建基本的认知框架。大模型则是在海量文本数据中学习语言规律、世界常识,形成通用的理解与生成能力。
  • 微调(Fine-tuning/SFT):对应职业培训阶段,学生针对特定岗位(如程序员、医生、设计师)学习专业技能。大模型的微调就是用特定领域的“指令-回应”数据,让模型学会适配具体场景的输出逻辑。

没有微调的大模型,就像刚毕业的学生,懂通用知识但不懂行业门道;经过微调的模型,才能真正成为贴合业务的“专业助手”。

微调的核心价值

对开发者和企业来说,微调的价值直接体现在业务落地效果上:

  1. 领域适配:将通用大模型的能力对齐到医疗、金融、法律等专业领域,比如让模型能精准解读病历、生成合规的金融报告。
  2. 任务定制:教会模型遵循固定的指令格式,比如批量生成产品文案、自动抽取合同关键信息、按指定格式输出数据分析结果。
  3. 性能跃迁:在特定任务上,微调后的模型效果远超单纯的提示词工程(比如Prompt Engineering),尤其是复杂的逻辑推理、多轮对话任务。
  4. 风格统一:让模型输出符合品牌调性的语气,比如电商客服的亲切话术、学术助手的严谨表述、游戏NPC的活泼风格。

2、 LoRA原理

全参数微调的痛点

讲LoRA之前,必须要了解全参数微调的痛点。

举个具体的例子,没有LoRA之前,某创业公司想微调70B模型做客服,一算账直接懵了:

  • 存储成本:每个任务存一份完整模型,280GB × 10个任务 = 2.8TB
  • 训练成本:8张A100跑一周,2万美元起步
  • 部署噩梦:切换任务要重新加载模型,用户等30秒
  • 最终选择:放弃微调,直接用通用模型凑合。
什么是LoRA?

LoRA(Low-Rank Adaptation)是一种参数高效的微调方法。它的核心思想很简单:

冻结原模型参数,只训练额外的小参数矩阵来调整模型输出

低秩分解的数学原理

让我用最简单的方式解释:

传统微调的问题

假设模型有一个权重矩阵 W(比如4096×4096),包含1600万个参数。传统微调需要更新所有这些参数:

新权重=原权重W+权重变化△W

LoRA的天才想法

研究发现,权重变化ΔW虽然维度很高(参数很多),但它的"本质维度"很低——用数学语言说,它是低秩的

这意味着我们可以用两个小矩阵的乘积来表示它:

△W=A*B

其中:

  • A 是 4096×8 的矩阵(32,768个参数)
  • B 是 8×4096 的矩阵(32,768个参数)
  • 总参数:65,536个(相比原矩阵1677万参数,压缩约256倍)

再举个最简单的例子(2×2权重):

W0 = [[2, 0],
      [0, 2]]
ΔW  ≈ A · B  (rank=1)
A = [[1], [2]]
B = [[3, 1]]
=> ΔW = [[3, 1],
         [6, 2]]

只学A、B两个小矩阵,就能表达一大块改动ΔW。

为什么微调的变化是低秩的?
  1. 预训练已经打好基础:就像会开车的人学开卡车,只需要掌握几个关键差异,不需要再学油门刹车方向盘和通用的交通规则。
  2. 微调任务的本质是少量调整:比如把通用模型改造成客服助手,本质上只是学会"更礼貌"+“更结构化”,原有的人类通识知识和语言能力不需要从头学习。再比如搭乐高积木,预训练就像把积木都准备好了;微调只是换几块颜色或形状,就能搭出新主题,不必把全部积木都重做。
  3. 实验验证:LoRA论文显示,在许多任务上秩r=8时能达到全量微调90%以上的效果。
LoRA的三大技术创新

创新1:梯度流动的"开关设计"

# 传统微调:所有参数都要计算梯度
原模型权重W → 计算梯度 → 更新 → 占用大量显存

# LoRA:只有小矩阵需要梯度
原模型权重W(冻结)→ 不计算梯度
小矩阵A、B → 计算梯度 → 更新 → 梯度存储降低99%

创新2:缩放因子α

为什么需要这个?假设你用秩8训练效果刚好,现在想试试秩16会不会更好。

这时候问题来了:秩越大矩阵越大,同样学习率下更新力度越强。这样不同秩的结果就没法公平对比了。α除以r就是为了归一化这个力度。

为了让不同秩的配置可以公平对比,LoRA引入了缩放因子:

# 实际的LoRA前向传播
y = W₀x + (α/r) × B × A × x
# 其中:
# - W₀ 是原始权重(冻结)
# - A ∈ R^(d×r), B ∈ R^(r×k)
# - 初始化时B=0,这样训练开始时ΔW=BA=0

这样从r=8切换到r=16时,不需要重新调整学习率。

通俗理解:换了更大排量的车(更大的r),α就像“油门校准器”,保证踩同样的力度,车速可以比对。

创新3:模块化的适配器设计

这是LoRA最具商业价值的特性。以下是一个典型的应用场景:

  • 传统方案:10个类目×280GB模型(70B FP32) = 2.8TB存储,月成本$28,000
  • LoRA方案:1个基座模型(280GB) + 10个50MB适配器 ≈ 280.5GB,月成本$2,800

就像一台手机(基座模型)配很多“手机壳”(适配器),随时换主题,不用每次都买新手机。

再看一个实际的商业案例,多类目电商商品文案生成:

  • 目标:为“服饰/美妆/3C/家居”等10个类目分别定制生成风格与术语
  • 方案:共享一个大模型基座 + 10个LoRA适配器(每个≈50MB)
  • 成本变化:存储 2.8TB → 280.5GB;类目切换延迟 30s → 0.5s
  • 上线速度:需求到上线 2周 → 3天(适配器快速训练+热切换)
  • 质效指标:人工校改率 38% → 12%;品牌风格一致性评分 +16pp

3 、模型训练框架选择

训练框架的核心价值

在大模型微调的工程实践中,训练框架本质是降低技术门槛、提升开发效率的工具集合,核心价值体现在三个维度:

  1. 资源效率:通过显存优化技术(如梯度检查点、混合精度训练、参数高效微调策略),使有限硬件资源(如消费级显卡)也能支持大规模模型训练;
  2. 流程标准化:整合数据预处理、模型加载、训练循环、评估验证、模型导出等环节,避免开发者重复造轮子;
  3. 场景适配性:针对不同任务需求(如基础微调SFT、对齐优化DPO/ORPO、推理加速部署),提供灵活的配置选项与扩展接口。

主流的训练框架有:Transformers( Hugging Face)、LLaMA-Factory、Unsloth、Axolotl、DeepSpeed、ms-swift。

本文我们选择ms-swift框架,支持国产。

4 、模型训练环境

硬件要求
  • 最低配置:单卡24GB显存(如RTX 3090、RTX 4090)
  • 推荐配置:单卡40GB显存(如A100 40G)

以上我都没有,所以使用ModelScope提供的免费GPU资源。

获取免费GPU资源
  1. 访问 ModelScope 魔搭社区:https://modelscope.cn/

  2. 申请免费GPU资源(8核32GB内存,24GB显存,可用36小时)

  1. 启动Notebook,进入Terminal

安装必要的包
# 验证GPU是否正常
nvidia-smi

# 安装modelscope 和 ms-swift
pip install modelscope -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install ms-swift -U -i https://pypi.tuna.tsinghua.edu.cn/simple 

下载模型

# 下载基座模型(Qwen3-4B)
modelscope download --model Qwen/Qwen3-4B --local_dir ./model/Qwen3-4B

5、 训练数据准备

理解数据格式

微调数据有多种格式,这里介绍两种比较常用的:

Alpaca格式

alpaca是基于Meta开源的LLaMA模型构建的一种微调数据集格式,特别适用于 instruction-tuning,即指令微调。其数据格式的特点是提供了一个明确的任务描述(instruction)、输入(input)和输出(output)三部分。system作为系统提示词,可选。

  • instruction:人类指令
  • input:人类输入(可选)
  • output:模型回答
{
  "system": "你是一个擅长数学计算的助手",
  "instruction": "计算这些物品的总费用。",
  "input": "汽车 - $3000,衣服 - $100,书 - $20。",
  "output": "总费用为 $3000 + $100 + $20 = $3120。"
}

在进行指令微调时,instruction对应的内容会与input对应的内容拼接后作为最终的人类输入,即人类输入为instruction \n input。而output对应的内容为模型回答。在上面的例子,人类的最终输入是:

计算这些物品的总费用。
输入:汽车 - $3000,衣服 - $100,书 - $20

也可以加入history,代表历史信息中每轮对话的指令和回答。注意在指令监督微调时,历史信息中的内容也会被用于模型学习。

[
  {
    "system": "系统提示词(选填)",
    "instruction": "人类指令(必填)",
    "input": "人类输入(选填)",
    "output": "模型回答(必填)",
    "history": [
      ["第一轮指令(选填)", "第一轮回答(选填)"],
      ["第二轮指令(选填)", "第二轮回答(选填)"]
    ]
  }
]

Messages格式(多数框架的标准格式)

{
  "messages": [
    {"role": "system", "content": "你是一个有帮助的助手"},
    {"role": "user", "content": "你好"},
    {"role": "assistant", "content": "你好!有什么可以帮助你的吗?"}
  ]
}
  • 多轮对话、工具调用、RAG、评测:优先用messages格式,结构直观、与 tokenizer/chat_template 对齐、便于注入系统/历史/工具消息。
  • 单轮任务(如分类、抽取、问答、翻译)或快速实验:用Alpaca更简洁,字段少、易构造与清洗。
数据质量要点

数据质量决定模型的智商,高质量数据是模型精准学习特征和规律的基础。高质量数据的7个标准:

  1. 逻辑清晰:事实准确,无矛盾
  2. 推理链完整:包含思考过程(Chain of Thought)
  3. 多样性高:覆盖各种场景
  4. 数量充足:至少1000条
  5. 长短结合:简答和详答都有
  6. 格式规范:统一的结构
  7. 领域平衡:专业知识:通用知识 = 1:2到1:10(根据具体需求调整)

要想得到好的结果,至少80%的精力应该放在构建高质量数据集这件事情上。

下载训练数据集

我们使用modelsope swift的自我认知数据集,通过将通配符进行替换:{{NAME}}、{{AUTHOER}},来创建属于自己大模型的自我认知数据集。通过微调从而改变模型的自我认知。

# 下载训练数据集
modelscope download --dataset swift/self-cognition --local_dir ./data/self-cognition
modelscope download --dataset AI-ModelScope/alpaca-gpt4-data-en --local_dir ./data/alpaca-gpt4-data-en
modelscope download --dataset AI-ModelScope/alpaca-gpt4-data-zh --local_dir ./data/alpaca-gpt4-data-zh

self-cognition的数据样例:

你可能会有个疑问,下载self-cognition不就够了吗?为什么还要下载 alpaca-gpt4-data-en和alpaca-gpt4-data-zh 数据集?

如果直接全部拿目标领域数据进行微调训练,可能会在模型微调后发现模型"变傻了"?原来那些常识它都不会了,这就是著名的"灾难性遗忘"!

alpaca-gpt4-data-en和alpaca-gpt4-data-zh数据集中包含的是通用知识。

在大模型微调过程中,要将专业知识与通用知识的比例控制在如 1:2 到 1:10 的范围内,主要是为了解决一个核心矛盾:如何在让模型精通特定领域知识的同时,不丢失其原有的通用知识和能力。这本质上是在模型“专精”与“广博”之间寻求一个最佳平衡点。

通用知识配比可以缓解以下几个问题:

要解决的核心问题 问题描述 专业知识:通用知识配比的作用
灾难性遗忘 模型在深入学习专业知识时,像“考前突击”一样,可能忘记之前学会的通用知识和技能(如语言流畅性、逻辑推理)。 充当“复习材料”:在学新知识的同时,不断练习旧知识,防止模型“偏科”和遗忘基础能力。
模式僵化与泛化能力下降 如果只接触专业数据,模型可能会变得“死板”,只会用专业腔调说话,难以适应多样化的用户提问方式,也缺乏创造力。 提供“多样化语境”:通用数据让模型接触到各种语言风格和任务类型,保持其灵活性和泛化能力,使其在面对陌生问题时也能合理应对。
过度拟合 模型可能对训练数据中的细节甚至噪音“死记硬背”,导致在训练集上表现完美,但遇到实际场景中的新样本时效果不佳。 起到“正则化”效果:通用数据作为一种干扰或挑战,迫使模型去学习更本质、更鲁棒的特征和规律,而不是机械记忆,从而提升在实际应用中的稳健性。
准备自定义数据

创建数据处理脚本 data_process.py

import os
import json
from tqdm import tqdm

def preprocess(row, name, author):
    """
    处理自我认知数据,替换占位符

    Args:
        row: 数据行
        name: 模型名称 [中文名, 英文名]
        author: 作者名称 [中文名, 英文名]
    """
    for key, val in [('name', name), ('author', author)]:
        if val isNone:
            continue
        # 根据语言标签选择中文或英文
        val = val[0] if row.get('tag') == 'zh'else val[1]
        if val isNone:
            continue
        # 替换占位符
        placeholder = '{{' + key.upper() + '}}'
        row['query'] = row['query'].replace(placeholder, val)
        row['response'] = row['response'].replace(placeholder, val)

    return row

def process_self_cognition_data(infile, outfile, model_name, model_author):
    """
    处理自我认知数据集
    """
    data = []
    with open(infile, encoding='utf-8') as f:
        for line in f.readlines():
            data.append(json.loads(line))

    out_data = []
    for item in tqdm(data, desc="Processing data"):
        out_data.append(preprocess(item, model_name, model_author))

    # 保存处理后的数据
    os.makedirs(os.path.dirname(outfile), exist_ok=True)
    with open(outfile, "w", encoding="utf-8") as f:
        for item in out_data:
            f.write(json.dumps(item, ensure_ascii=False) + "\n")

    print(f"数据已保存到: {outfile}")
    print(f"共处理 {len(out_data)} 条数据")
    return out_data

if __name__ == "__main__":
    # 设置模型名称和作者信息
    model_name = ['喵星人Grace', 'Grace']  # [中文名, 英文名]
    model_author = ['唐银', 'Tang Yin']  # [中文名, 英文名]

    # 处理自我认知数据
    infile = "data/self-cognition/self_cognition.jsonl"
    outfile = "data/self-cognition/self_cognition_processed.jsonl"

    process_self_cognition_data(infile, outfile, model_name, model_author)

写代码的时候,可以点右上角的“WebIDE”图标,把Notebook切换成WebIDE,比较方便。

运行数据处理:

查看self_cognition_processed.jsonl文件,可以看到训练数据中的关键词已经被成功替换。

看到这里你可能又会疑惑,这里既不是前面介绍的 Alpaca 格式,也不是 Messages 格式。它是“query-response”类格式。

如果查看 alpaca-gpt4-data-en和alpaca-gpt4-data-zh 数据集内容的话,也会发现甚至不是json,而是csv文件。

实际上ms-swift 框架在加载数据时会用 AutoPreprocessor 将不同常见字段名自动映射为标准字段(query→用户输入,response→模型输出),再统一转换为内部标准对话样式。

6、 模型训练

创建训练脚本

创建 train.py,这是我们的核心训练代码:

import os
from dataclasses import dataclass
from typing import List

# 设置环境变量
os.environ['CUDA_VISIBLE_DEVICES'] = '0'# 使用第一块GPU

# 导入必要的库
from swift.llm import (
    get_model_tokenizer,
    load_dataset,
    get_template,
    EncodePreprocessor
)
from swift.utils import (
    get_logger,
    find_all_linears,
    get_model_parameter_info,
    seed_everything
)
from swift.tuners import Swift, LoraConfig
from swift.trainers import Seq2SeqTrainer, Seq2SeqTrainingArguments

# 初始化日志和随机种子
logger = get_logger()
seed_everything(42)  # 固定随机种子,保证结果可复现

@dataclass
class TrainingConfig:
    """训练配置类"""
    # 模型相关
    model_path: str = "model/Qwen3-4B"# 基座模型路径
    system_prompt: str = "你是Grace,一只聪明可爱的喵星助手。"# 系统提示词
    output_dir: str = 'output'# 输出目录

    # 数据集相关
    datasets: List[str] = None# 数据集列表
    data_seed: int = 42# 数据随机种子
    max_length: int = 2048# 最大序列长度
    split_ratio: float = 0.1# 验证集划分比例
    num_proc: int = 8# 数据预处理进程数

    # LoRA相关
    lora_rank: int = 8# LoRA秩(越大越接近全量微调,但参数也越多)
    lora_alpha: int = 32# LoRA缩放因子(论文建议设置为rank的2倍,但实践中可根据效果调整)

    # 训练超参数
    learning_rate: float = 1e-4# 学习率(LoRA通常用1e-4)
    train_batch_size: int = 2# 训练批次大小,根据显存大小进行调节
    eval_batch_size: int = 2# 评估批次大小,根据显存大小进行调节
    gradient_accumulation_steps: int = 16# 梯度累积步数(有效batch_size = train_batch_size × gradient_accumulation_steps = 2 × 16 = 32)
    num_train_epochs: int = 3# 训练轮数,如果你的数据量过小,可以多训练几个epoch
    warmup_ratio: float = 0.05# 学习率预热比例
    weight_decay: float = 0.1# 权重衰减,防止过拟合
    save_steps: int = 50# 保存间隔
    eval_steps: int = 50# 评估间隔
    logging_steps: int = 5# 日志输出间隔

    def __post_init__(self):
        """初始化后处理"""
        if self.datasets isNone:
            self.datasets = [
                "data/alpaca-gpt4-data-en/train.csv#2000",  # 英文数据,取2000条
                "data/alpaca-gpt4-data-zh/train.csv#2000",  # 中文数据,取2000条
                "data/self-cognition/self_cognition_processed.jsonl"# 自我认知数据
            ]

def setup_model_and_tokenizer(config: TrainingConfig):
    """设置模型和分词器"""
    logger.info(f"加载模型: {config.model_path}")

    # 加载模型和分词器
    model, tokenizer = get_model_tokenizer(config.model_path)
    logger.info(f"模型加载成功: {model.model_info}")

    # 获取对话模板
    template = get_template(
        model.model_meta.template,
        tokenizer,
        default_system=config.system_prompt,
        max_length=config.max_length
    )
    template.set_mode('train')  # 设置为训练模式,会同时计算input和target的loss

    return model, tokenizer, template

def setup_lora(model, config: TrainingConfig):
    """配置LoRA"""
    logger.info("配置LoRA参数...")

    # 自动找到所有线性层(通常是q_proj, k_proj, v_proj, o_proj, gate_proj, up_proj, down_proj等)
    target_modules = find_all_linears(model)
    logger.info(f"找到 {len(target_modules)} 个可训练的线性层")

    # 创建LoRA配置
    lora_config = LoraConfig(
        task_type='CAUSAL_LM',  # 因果语言模型任务
        r=config.lora_rank,  # 秩
        lora_alpha=config.lora_alpha,  # 缩放因子
        target_modules=target_modules,  # 目标模块
    )

    # 应用LoRA到模型
    model = Swift.prepare_model(model, lora_config)

    # 打印参数信息
    param_info = get_model_parameter_info(model)
    logger.info(f"模型参数信息:{ param_info }")

    return model, lora_config

def load_and_process_dataset(config: TrainingConfig, template):
    """加载并处理数据集"""
    logger.info("加载数据集...")

    # 加载数据集
    train_dataset, val_dataset = load_dataset(
        config.datasets,  # 数据列表 [datasetA,datasetB,xxxx],支持json,jsonl,csv
        split_dataset_ratio=config.split_ratio,
        num_proc=config.num_proc,
        seed=config.data_seed,
        shuffle=True
    )

    logger.info(f"数据集加载成功:")
    logger.info(f"  - 训练集: {len(train_dataset)} 条")
    logger.info(f"  - 验证集: {len(val_dataset)} 条")

    # 数据预处理(tokenization)
    logger.info("开始数据预处理...")
    encode_func = EncodePreprocessor(template=template)
    train_dataset = encode_func(train_dataset, num_proc=config.num_proc)
    val_dataset = encode_func(val_dataset, num_proc=config.num_proc)

    # 打印样例
    logger.info("数据样例:")
    template.print_inputs(train_dataset[0])

    return train_dataset, val_dataset

def train_model(model, train_dataset, val_dataset, template, config: TrainingConfig):
    """训练模型"""
    logger.info("开始训练...")

    # 设置训练参数
    training_args = Seq2SeqTrainingArguments(
        output_dir=config.output_dir,
        learning_rate=config.learning_rate,
        per_device_train_batch_size=config.train_batch_size,
        per_device_eval_batch_size=config.eval_batch_size,
        gradient_accumulation_steps=config.gradient_accumulation_steps,
        num_train_epochs=config.num_train_epochs,
        weight_decay=config.weight_decay,
        lr_scheduler_type='cosine',  # 余弦学习率调度
        warmup_ratio=config.warmup_ratio,
        logging_steps=config.logging_steps,
        save_strategy='steps',
        save_steps=config.save_steps,
        eval_strategy='steps',
        eval_steps=config.eval_steps,
        save_total_limit=2,  # 最多保存2个checkpoint
        metric_for_best_model='loss',
        greater_is_better=False,
        load_best_model_at_end=True,
        gradient_checkpointing=True,  # 通过重计算来节省显存,会增加计算时间
        report_to=['tensorboard'],  # 使用tensorboard记录
        logging_first_step=True,   # 第一个step输出一下log
        dataloader_num_workers=4,  # 数据加载进程数
        data_seed=config.data_seed,
    )

    # 启用梯度计算
    model.enable_input_require_grads()  # 启用输入梯度,这是gradient checkpointing的前置要求

    # 创建训练器
    trainer = Seq2SeqTrainer(
        model=model,
        args=training_args,
        data_collator=template.data_collator,
        train_dataset=train_dataset,
        eval_dataset=val_dataset,
        template=template,
    )

    # 开始训练
    train_result = trainer.train()

    # 保存最终模型
    trainer.save_model()
    trainer.save_state()

    logger.info("训练完成!")
    logger.info(f"模型保存在: {trainer.state.last_model_checkpoint}")

    return trainer

def main():
    """主函数"""
    # 创建配置
    config = TrainingConfig()

    # 创建输出目录
    os.makedirs(config.output_dir, exist_ok=True)

    # 设置模型和分词器
    model, tokenizer, template = setup_model_and_tokenizer(config)

    # 配置LoRA
    model, lora_config = setup_lora(model, config)

    # 加载数据集
    train_dataset, val_dataset = load_and_process_dataset(config, template)

    # 训练模型
    trainer = train_model(model, train_dataset, val_dataset, template, config)

    # 打印总结
    logger.info("\\n" + "="*50)
    logger.info("训练总结:")
    logger.info(f"  - 最终checkpoint: {trainer.state.last_model_checkpoint}")
    logger.info(f"  - 训练损失: {trainer.state.best_metric:.4f}")
    logger.info(f"  - 总训练步数: {trainer.state.global_step}")
    logger.info("="*50)

if __name__ == "__main__":
    main()

开跑前核对两件事:

  • model_path 是否指向已下载的基座模型目录?
  • datasets 路径是否真实存在、样本格式正确?
使用命令行工具训练(可选)

如果你想使用更简单的命令行方式,可以创建 train.sh

#!/bin/bash
# 使用ms-swift命令行工具进行训练

export CUDA_VISIBLE_DEVICES=0  # 指定GPU

swift sft \\
    --model model/Qwen3-4B \\
    --train_type lora \\
    --dataset 'data/alpaca-gpt4-data-zh/train.csv#2000' \\
              'data/alpaca-gpt4-data-en/train.csv#2000' \\
              'data/self-cognition/self_cognition_processed.jsonl' \\
    --torch_dtype bfloat16 \\
    --num_train_epochs 3 \\
    --per_device_train_batch_size 2 \\
    --per_device_eval_batch_size 2 \\
    --learning_rate 1e-4 \\
    --lora_rank 8 \\
    --lora_alpha 32 \\
    --target_modules all-linear \\
    --gradient_accumulation_steps 16 \\
    --eval_steps 50 \\
    --save_steps 50 \\
    --save_total_limit 2 \\
    --logging_steps 5 \\
    --max_length 2048 \\
    --output_dir output \\
    --system '你是Grace,一只聪明可爱的喵星助手。' \\
    --warmup_ratio 0.05 \\
    --dataloader_num_workers 4
    --dataset_num_proc 4 \
    --model_name '喵星人Grace''Grace' \
    --model_author '唐银''TangYin'
开始训练

运行训练脚本:

# 使用Python脚本训练
python train.py

# 或使用Shell脚本
chmod +x train.sh
./train.sh

训练过程中,你会看到类似这样的输出:

监控训练过程

使用TensorBoard监控训练:

# 启动TensorBoard
tensorboard --logdir output --port 6006

ctrl+单击“http://localhost:6006”即可访问代理出来的链接:

用时大概50分钟,训练完成。

7 、模型推理与部署

模型推理

创建 inference.py

import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0'

from swift.llm import (
    PtEngine, RequestConfig, get_model_tokenizer,
    get_template, InferRequest
)
from swift.tuners import Swift

class GraceInference:
    """Grace模型推理类"""

    def __init__(self, model_path: str, lora_path: str):
        """
        初始化推理引擎

        Args:
            model_path: 基座模型路径
            lora_path: LoRA权重路径
        """
        print(f"正在唤醒Grace...")

        # 加载模型和分词器
        self.model, self.tokenizer = get_model_tokenizer(model_path)

        # 加载LoRA权重
        self.model = Swift.from_pretrained(self.model, lora_path)

        # 设置模板
        self.template = get_template(
            self.model.model_meta.template,
            self.tokenizer,
            default_system="你是Grace,一只聪明可爱的喵星助手。"
        )

        # 创建推理引擎
        self.engine = PtEngine.from_model_template(
            self.model,
            self.template,
            max_batch_size=2
        )

        # 推理配置
        self.request_config = RequestConfig(
            max_tokens=512,
            temperature=0.7,
            top_p=0.9
        )

        print("Grace已准备就绪!喵~")

    def chat(self, query: str, history: list = None) -> str:
        """
        与Grace对话

        Args:
            query: 用户输入
            history: 对话历史

        Returns:
            Grace的回复
        """
        messages = history or []
        messages.append({'role': 'user', 'content': query})

        # 创建推理请求
        infer_request = InferRequest(messages=messages)

        # 执行推理
        response = self.engine.infer([infer_request], self.request_config)[0]

        return response.choices[0].message.content

    def batch_infer(self, queries: list) -> list:
        """批量推理"""
        requests = [
            InferRequest(messages=[{'role': 'user', 'content': q}])
            for q in queries
        ]

        responses = self.engine.infer(requests, self.request_config)

        return [r.choices[0].message.content for r in responses]

def main():
    """测试推理"""
    # 初始化Grace
    grace = GraceInference(
        model_path='model/Qwen3-4B',
        lora_path='output/checkpoint-348'# 请根据实际路径修改
    )

    # 单轮对话测试
    print("\\n=== 单轮对话测试 ===")
    test_queries = [
        "你是谁?",
        "给我讲个笑话吧",
        "Python和Java哪个更好?"
    ]

    for query in test_queries:
        print(f"\\n用户: {query}")
        response = grace.chat(query)
        print(f"Grace: {response}")

    # 多轮对话测试
    print("\\n=== 多轮对话测试 ===")
    history = []

    conversation = [
        "你好,Grace!",
        "你会Python吗?",
        "能教我写个Hello World吗?"
    ]

    for query in conversation:
        print(f"\\n 用户: {query}")
        response = grace.chat(query, history)
        print(f"Grace: {response}")

        # 更新历史
        history.append({'role': 'user', 'content': query})
        history.append({'role': 'assistant', 'content': response})

    # 批量推理测试
    print("\\n=== 批量推理测试 ===")
    batch_queries = [
        "早上好!",
        "你喜欢看电影吗?",
        "推荐一些好看的电影"
    ]

    responses = grace.batch_infer(batch_queries)
    for q, r in zip(batch_queries, responses):
        print(f"\\n {q}\\n {r}")

if __name__ == "__main__":
    main()

运行结果:

合并LoRA权重(可选)

如果你想将LoRA权重合并到基座模型中,创建 merge_lora.py

import os
from swift.llm import get_model_tokenizer
from swift.tuners import Swift

def merge_lora_weights(
    model_path: str,
    lora_path: str,
    output_path: str
):
    """
    将LoRA权重合并到基座模型

    Args:
        model_path: 基座模型路径
        lora_path: LoRA权重路径
        output_path: 输出路径
    """
    print(f" 正在合并LoRA权重...")
    print(f" 基座模型: {model_path}")
    print(f" LoRA权重: {lora_path}")

    # 加载模型
    model, tokenizer = get_model_tokenizer(model_path)

    # 加载LoRA权重
    model = Swift.from_pretrained(model, lora_path)

    # 合并权重
    model = model.merge_and_unload()  # 将LoRA权重合并到原始模型并卸载LoRA模块

    # 保存合并后的模型
    model.save_pretrained(output_path)
    tokenizer.save_pretrained(output_path)

    print(f" 合并完成!")
    print(f" 合并后的模型保存在: {output_path}")

if __name__ == "__main__":
    merge_lora_weights(
        model_path='model/Qwen3-4B',
        lora_path='output/checkpoint-348',
        output_path='output/grace-merged'
    )

运行结果:

也可以使用shell脚本:

#!/bin/bash
# 指定 GPU
export CUDA_VISIBLE_DEVICES=0

swift export \
    --model model/Qwen3-4B \
    --adapters output/checkpoint-348 \
    --merge_lora true \
    --max_model_len 2048 \
    --test_convert_precision true \
    --output_dir output/checkpoint-348-merge

什么时候需要合并?

  • 想在不支持LoRA的推理框架里部署;
  • 想把“外挂”永久焊接到“本体”里,方便单文件分发。
启动Web界面

创建 app.py ,提供友好的聊天界面:

import gradio as gr
from inference import GraceInference

# 初始化Grace
grace = GraceInference(
    model_path='model/Qwen3-4B',
    lora_path='output/checkpoint-348'
)

def chat_with_grace(message, history):
    """Gradio聊天函数"""
    # 转换历史格式
    messages = []
    for h in history:
        messages.append({'role': 'user', 'content': h[0]})
        messages.append({'role': 'assistant', 'content': h[1]})

    # 获取回复
    response = grace.chat(message, messages)

    return response

# 创建Gradio界面
demo = gr.ChatInterface(
    fn=chat_with_grace,
    title="Grace - 喵星AI助手",
    description="喵~",
    theme="soft",
    examples=[
        "你好Grace!",
        "给我讲个有趣的故事",
        "如何学好Python编程?",
        "今天心情不太好,能安慰我一下吗?"
    ],
    css="""
    .gradio-container {
        font-family: 'Microsoft YaHei', sans-serif;
    }
    """
)

if __name__ == "__main__":
    demo.launch(share=True, server_port=7860)

运行Web界面:

ctrl+单击链接即可访问代理出来的链接:

部署为API服务

创建 deploy.py ,部署为REST API:

from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import List, Optional
import uvicorn

from inference import GraceInference

app = FastAPI(title="Grace API", version="1.0")

# 初始化模型
grace = GraceInference(
    model_path='model/Qwen3-4B',
    lora_path='output/checkpoint-348'
)

class ChatRequest(BaseModel):
    """聊天请求模型"""
    message: str
    history: Optional[List[dict]] = []
    max_tokens: Optional[int] = 512
    temperature: Optional[float] = 0.7

class ChatResponse(BaseModel):
    """聊天响应模型"""
    response: str
    model: str = "grace-4b"

@app.post("/chat", response_model=ChatResponse)
asyncdef chat(request: ChatRequest):
    """聊天接口"""
    try:
        # 更新推理配置
        grace.request_config.max_tokens = request.max_tokens
        grace.request_config.temperature = request.temperature

        # 获取回复
        response = grace.chat(request.message, request.history)

        return ChatResponse(response=response)

    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@app.get("/health")
asyncdef health():
    """健康检查"""
    return {"status": "healthy", "model": "grace-4b"}

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8000)

启动API服务:

测试API:

curl -X POST "http://localhost:8000/chat" \
     -H "Content-Type: application/json" \
     -d '{
       "message": "你是谁?",
       "temperature": 0.7
     }'

8 、常见问题与解决方案

问题 原因 解决方案
显存不足 Batch size太大 减小batch size,增加梯度累积
Loss不下降 学习率不当 调整学习率,检查数据质量
过拟合 数据量太少 增加数据,使用dropout,早停
推理速度慢 模型太大 使用量化,减小max_length
效果不理想 数据质量差 重新清洗数据,增加多样性

结语

看到这里,有人可能会觉得大模型微调就像用美颜APP——选个滤镜、调个参数,就能出片。但真相是:这活儿更像老中医开方子,不是简单抓药,而是讲究“辨证论治”。

比如,学习率(learning rate)调大了,模型可能“学飘了”,上蹿下跳不收敛;调小了又成了“树懒”,半天学不进去。LoRA里的秩(rank)设置,低了学不透,高了又过拟合。如果只当它们是旋钮瞎拧,模型分分钟摆烂给你看。

扎实的理论基础才是驾驭这项技术的核心基石。微调并非简单的参数调节操作,每一个超参数——从学习率、权重衰减到LoRA配置中的秩(r)和缩放因子(alpha)——都蕴含着深刻的数学原理与优化思想。真正有效的微调,是理论指导下的精密实践,是深刻理解每个参数意义后的审慎决策。

如何学习大模型 AI ?

由于新岗位的生产效率,要优于被取代岗位的生产效率,所以实际上整个社会的生产效率是提升的。

但是具体到个人,只能说是:

“最先掌握AI的人,将会比较晚掌握AI的人有竞争优势”。

这句话,放在计算机、互联网、移动互联网的开局时期,都是一样的道理。

我在一线科技企业深耕十二载,见证过太多因技术卡位而跃迁的案例。那些率先拥抱 AI 的同事,早已在效率与薪资上形成代际优势,我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在大模型的学习中的很多困惑。我们整理出这套 AI 大模型突围资料包

  • ✅ 从零到一的 AI 学习路径图
  • ✅ 大模型调优实战手册(附医疗/金融等大厂真实案例)
  • ✅ 百度/阿里专家闭门录播课
  • ✅ 大模型当下最新行业报告
  • ✅ 真实大厂面试真题
  • ✅ 2025 最新岗位需求图谱

所有资料 ⚡️ ,朋友们如果有需要 《AI大模型入门+进阶学习资源包》下方扫码获取~
在这里插入图片描述

① 全套AI大模型应用开发视频教程

(包含提示工程、RAG、LangChain、Agent、模型微调与部署、DeepSeek等技术点)
在这里插入图片描述

② 大模型系统化学习路线

作为学习AI大模型技术的新手,方向至关重要。 正确的学习路线可以为你节省时间,少走弯路;方向不对,努力白费。这里我给大家准备了一份最科学最系统的学习成长路线图和学习规划,带你从零基础入门到精通!
在这里插入图片描述

③ 大模型学习书籍&文档

学习AI大模型离不开书籍文档,我精选了一系列大模型技术的书籍和学习文档(电子版),它们由领域内的顶尖专家撰写,内容全面、深入、详尽,为你学习大模型提供坚实的理论基础。
在这里插入图片描述

④ AI大模型最新行业报告

2025最新行业报告,针对不同行业的现状、趋势、问题、机会等进行系统地调研和评估,以了解哪些行业更适合引入大模型的技术和应用,以及在哪些方面可以发挥大模型的优势。
在这里插入图片描述

⑤ 大模型项目实战&配套源码

学以致用,在项目实战中检验和巩固你所学到的知识,同时为你找工作就业和职业发展打下坚实的基础。
在这里插入图片描述

⑥ 大模型大厂面试真题

面试不仅是技术的较量,更需要充分的准备。在你已经掌握了大模型技术之后,就需要开始准备面试,我精心整理了一份大模型面试题库,涵盖当前面试中可能遇到的各种技术问题,让你在面试中游刃有余

图片

以上资料如何领取?

在这里插入图片描述

为什么大家都在学大模型?

最近科技巨头英特尔宣布裁员2万人,传统岗位不断缩减,但AI相关技术岗疯狂扩招,有3-5年经验,大厂薪资就能给到50K*20薪!

图片

不出1年,“有AI项目经验”将成为投递简历的门槛。

风口之下,与其像“温水煮青蛙”一样坐等被行业淘汰,不如先人一步,掌握AI大模型原理+应用技术+项目实操经验,“顺风”翻盘!
在这里插入图片描述
在这里插入图片描述

这些资料真的有用吗?

这份资料由我和鲁为民博士(北京清华大学学士和美国加州理工学院博士)共同整理,现任上海殷泊信息科技CEO,其创立的MoPaaS云平台获Forrester全球’强劲表现者’认证,服务航天科工、国家电网等1000+企业,以第一作者在IEEE Transactions发表论文50+篇,获NASA JPL火星探测系统强化学习专利等35项中美专利。本套AI大模型课程由清华大学-加州理工双料博士、吴文俊人工智能奖得主鲁为民教授领衔研发。

资料内容涵盖了从入门到进阶的各类视频教程和实战项目,无论你是小白还是有些技术基础的技术人员,这份资料都绝对能帮助你提升薪资待遇,转行大模型岗位。
在这里插入图片描述
在这里插入图片描述

以上全套大模型资料如何领取?

在这里插入图片描述

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐