大模型微调训练实践

部署运行你感兴趣的模型镜像

文章详细介绍了使用LoRA技术对大模型进行微调的全流程,包括原理、环境配置、数据准备、模型训练及推理部署。LoRA通过训练少量参数实现模型定制,大幅降低微调成本。文章以实际案例展示了数据集创建、参数配置、训练监控和多种部署方式,为开发者提供了完整的大模型微调实践指南。


Start with zero-shot, then few-shot, neither of them worked, then fine-tune.
先不加例子,然后给少量的例子,如果都不好使,模型微调。
—— OpenAI

0x01 为什么需要微调?

预训练与微调的关系

想象一下培养一位医生的过程:

  • 预训练(Pre-training):就像医学院的基础教育,学习解剖、生理、病理等基础知识。大模型在海量文本上学习语言规律和世界常识。
  • 微调(Fine-tuning/SFT):就像临床实习,在具体科室学习专业技能。我们用特定领域的指令-回应对让模型学会遵循指令。

微调的核心价值

  1. 领域适配:将通用知识对齐到特定领域(医疗、金融、法律等)
  2. 任务定制:教会模型遵循特定的指令格式和输出结构
  3. 性能提升:在特定任务上远超单纯提示词工程的效果
  4. 风格控制:让模型输出符合特定语气或品牌形象

0x02 LoRA原理

全参数微调的痛点

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

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

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

什么是LoRA?

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

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

低秩分解的数学原理

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

传统微调的问题

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

新权重原权重权重变化

LoRA的天才想法

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

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

其中:

  • 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

0x03 模型训练框架选择

训练框架的核心价值

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

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

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

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

0x04 模型训练环境

硬件要求

  • 最低配置:单卡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

0x05 训练数据准备

理解数据格式

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

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

defpreprocess(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

defprocess_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→模型输出),再统一转换为内部标准对话样式。

0x06 模型训练

创建训练脚本

创建 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
classTrainingConfig:
"""训练配置类"""
# 模型相关
    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"# 自我认知数据
            ]

defsetup_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

defsetup_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

defload_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

deftrain_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

defmain():
"""主函数"""
# 创建配置
    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分钟,训练完成。

0x07 模型推理与部署

模型推理

创建 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

classGraceInference:
"""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已准备就绪!喵~")

defchat(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

defbatch_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]

defmain():
"""测试推理"""
# 初始化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

defmerge_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'
)

defchat_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'
)

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

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

@app.post("/chat", response_model=ChatResponse)
asyncdefchat(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")
asyncdefhealth():
"""健康检查"""
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
     }'

0x08 常见问题与解决方案

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

结语

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

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

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

那么,如何系统的去学习大模型LLM?

如果你也想系统学习AI大模型技术,想通过这项技能真正达到升职加薪,就业或是副业的目的,但是不知道该如何开始学习*_,因为网上的资料太多太杂乱了,如果不能系统的学习就相当于是白学。
为了帮助大家打破壁垒,快速了解大模型核心技术原理,学习相关大模型技术。从原理出发真正入局大模型。在这里我和MoPaaS魔泊云联合梳理打造了系统大模型学习脉络,这份 LLM大模型资料 分享出来:包括LLM大模型书籍、640套大模型行业报告、LLM大模型学习视频、LLM大模型学习路线、开源大模型学习教程等, 😝有需要的小伙伴,可以 扫描下方二维码免费领取🆓**⬇️⬇️⬇️

在这里插入图片描述

【大模型全套视频教程】

教程从当下的市场现状和趋势出发,分析各个岗位人才需求,带你充分了解自身情况,get 到适合自己的 AI 大模型入门学习路线。

从基础的 prompt 工程入手,逐步深入到 Agents,其中更是详细介绍了 LLM 最重要的编程框架 LangChain。最后把微调与预训练进行了对比介绍与分析。

同时课程详细介绍了AI大模型技能图谱知识树,规划属于你自己的大模型学习路线,并且专门提前收集了大家对大模型常见的疑问,集中解答所有疑惑!

在这里插入图片描述

深耕 AI 领域技术专家带你快速入门大模型

跟着行业技术专家免费学习的机会非常难得,相信跟着学习下来能够对大模型有更加深刻的认知和理解,也能真正利用起大模型,从而“弯道超车”,实现职业跃迁!

在这里插入图片描述

【精选AI大模型权威PDF书籍/教程】

精心筛选的经典与前沿并重的电子书和教程合集,包含《深度学习》等一百多本书籍和讲义精要等材料。绝对是深入理解理论、夯实基础的不二之选。

在这里插入图片描述

【AI 大模型面试题 】

除了 AI 入门课程,我还给大家准备了非常全面的**「AI 大模型面试题」,**包括字节、腾讯等一线大厂的 AI 岗面经分享、LLMs、Transformer、RAG 面试真题等,帮你在面试大模型工作中更快一步。

【大厂 AI 岗位面经分享(92份)】

图片

【AI 大模型面试真题(102 道)】

图片

【LLMs 面试真题(97 道)】

图片

【640套 AI 大模型行业研究报告】

在这里插入图片描述

【AI大模型完整版学习路线图(2025版)】

明确学习方向,2025年 AI 要学什么,这一张图就够了!

img

👇👇点击下方卡片链接免费领取全部内容👇👇

在这里插入图片描述

抓住AI浪潮,重塑职业未来!

科技行业正处于深刻变革之中。英特尔等巨头近期进行结构性调整,缩减部分传统岗位,同时AI相关技术岗位(尤其是大模型方向)需求激增,已成为不争的事实。具备相关技能的人才在就业市场上正变得炙手可热。

行业趋势洞察:

  • 转型加速: 传统IT岗位面临转型压力,拥抱AI技术成为关键。
  • 人才争夺战: 拥有3-5年经验、扎实AI技术功底真实项目经验的工程师,在头部大厂及明星AI企业中的薪资竞争力显著提升(部分核心岗位可达较高水平)。
  • 门槛提高: “具备AI项目实操经验”正迅速成为简历筛选的重要标准,预计未来1-2年将成为普遍门槛。

与其观望,不如行动!

面对变革,主动学习、提升技能才是应对之道。掌握AI大模型核心原理、主流应用技术与项目实战经验,是抓住时代机遇、实现职业跃迁的关键一步。

在这里插入图片描述

01 为什么分享这份学习资料?

当前,我国在AI大模型领域的高质量人才供给仍显不足,行业亟需更多有志于此的专业力量加入。

因此,我们决定将这份精心整理的AI大模型学习资料,无偿分享给每一位真心渴望进入这个领域、愿意投入学习的伙伴!

我们希望能为你的学习之路提供一份助力。如果在学习过程中遇到技术问题,也欢迎交流探讨,我们乐于分享所知。

*02 这份资料的价值在哪里?*

专业背书,系统构建:

  • 本资料由我与MoPaaS魔泊云的鲁为民博士共同整理。鲁博士拥有清华大学学士美国加州理工学院博士学位,在人工智能领域造诣深厚:

    • 在IEEE Transactions等顶级学术期刊及国际会议发表论文超过50篇
    • 拥有多项中美发明专利。
    • 荣获吴文俊人工智能科学技术奖(中国人工智能领域重要奖项)。
  • 目前,我有幸与鲁博士共同进行人工智能相关研究。

在这里插入图片描述

内容实用,循序渐进:

  • 资料体系化覆盖了从基础概念入门核心技术进阶的知识点。

  • 包含丰富的视频教程实战项目案例,强调动手实践能力。

  • 无论你是初探AI领域的新手,还是已有一定技术基础希望深入大模型的学习者,这份资料都能为你提供系统性的学习路径和宝贵的实践参考助力你提升技术能力,向大模型相关岗位转型发展

    在这里插入图片描述在这里插入图片描述在这里插入图片描述

抓住机遇,开启你的AI学习之旅!

在这里插入图片描述

您可能感兴趣的与本文相关的镜像

TensorFlow-v2.15

TensorFlow-v2.15

TensorFlow

TensorFlow 是由Google Brain 团队开发的开源机器学习框架,广泛应用于深度学习研究和生产环境。 它提供了一个灵活的平台,用于构建和训练各种机器学习模型

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值