大模型基础:

相关知识:

1.大模型训练就是对下一个token进行分类预测,

对最后一个token计算不了loss,标签需要左移一位,形成label。

2.使用交叉熵来计算Loss

3.注意每个大模型有自己的输入模板

4.计算损失

  • completions only 仅针对回答计算损失(一般)

  • NEFTuning 给embedding层加噪声(数据增强,增加模型的泛化性)

基本流程:

  • 加载模型(远程/本地加载,可选:量化加载)+分词器
from transformers import AutoTokenizer, BitsAndBytesConfig, AutoModelForCausalLM
from peft import LoraConfig, get_peft_model, TaskType
import torch

model_path = "Meta-Llama-3.1-8B-Instruct"

tokenizer = AutoTokenizer.from_pretrained(model_path, use_fast=False)
tokenizer.padding_side = "right"
tokenizer.pad_token = tokenizer.eos_token

# 可选:量化加载
bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_use_double_quant=True,
    bnb_4bit_quant_type="nf4",
    bnb_4bit_compute_dtype=torch.float16
)
model = AutoModelForCausalLM.from_pretrained(
    model_path, 
    quantization_config=bnb_config,
    torch_dtype=torch.float16
)

# 使用Lora冻结部分参数微调
peft_config = LoraConfig(
    r=8,
    target_modules=["q_proj",
                    "v_proj",
                    "k_proj",
                    "o_proj",
                    "gate_proj",
                    "down_proj",
                    "up_proj"
                    ],
    task_type=TaskType.CAUSAL_LM,
    lora_alpha=16,
    lora_dropout=0.05
)
model = get_peft_model(model, peft_config)

# 模型放入显卡
model.to("cuda")
  • 加载以及处理数据
# sft部分数据展示   {"query":xxx, "answer":xxx}
"""
{"query": "保持健康的一个提示。 ", "answer": "以下是保持健康的一个提示:\n\n 保持身体活动。每天做适当的身体运动,如散步、跑步或游泳,能促进心血管健康,增强肌肉力量,并有助于减少体重。"}
{"query": "三原色是什么? ", "answer": "三原色通常指的是红色、绿色和蓝色(RGB)。它们是通过加色混合原理创建色彩的三种基础颜色。在以发光为基础的显示设备中(如电视、计算机显示器、智能手机和平板电脑显示屏), 三原色可混合产生大量色彩。其中红色和绿色可以混合生成黄色,红色和蓝色可以混合生成品红色,蓝色和绿色可以混合生成青色。当红色、绿色和蓝色按相等比例混合时,可以产生白色或灰色。"}
"""

from datasets import Dataset
import json

items = []
with open("./data/sft_data.json", "r", encoding="utf8")as f:
    for line in f:
        item = json.loads(line)
        items.append({"prompt": item["query"], "completion": item["answer"]})
dataset = Dataset.from_list(items)
  • 简单训练示例
#把模型放入显卡,定义优化器
model.to("cuda")
optimizer = torch.optim.AdamW(model.parameters())

#输入文本,对文本进行分词,将分词后的input也放入显卡
text = "今天天气不错。"
input = tokenizer(text, return_tensors="pt")
input = {k: v.to("cuda") for k, v in input.items()}    #input是一个字典,包括input_ids, mask

#设置labels和inputs一致
input["labels"] = input["input_ids"].clone()

output = model(**input)

#获取模型的loss
loss = output.loss   #因为传入了label
loss.backward()        #后向传播
optimizer.step()        #更新参数
optimizer.zero_grad()

#保存模型
model.save_pretrained("output_dir")
  • 使用某种框架训练 eg: trl
    """
    这里使用trl库训练
    """
    
    from trl import SFTTrainer, SFTConfig, DataCollatorForCompletionOnlyLM
    
    
    sft_config = SFTConfig(output_dir="/tmp",
                           neftune_noise_alpha=10,
                           per_device_train_batch_size=1,
                           max_seq_length=100,
                           num_train_epochs=10,
                           logging_steps=10,
                           logging_strategy="steps")
    response_template = "<|start_header_id|>assistant<|end_header_id""|>\n\n"
    collator = DataCollatorForCompletionOnlyLM(response_template, tokenizer=tokenizer)
    trainer = SFTTrainer(
        model=model,
        train_dataset=dataset,
        args=sft_config,
        data_collator=collator
    )
    
    trainer.train()


微调(Fine-Tuning)

大模型微调(Fine-tuning)是针对特定任务优化预训练模型的核心技术。

方法 数据量 计算资源 性能 适用场景 推荐框架
全参数微调 百万级以上 多 GPU(8×A100) 最优 工业级高要求任务 Transformers
PEFT(LoRA) 数千 - 数万 单 GPU(16GB+) 接近全量 中小规模任务、快速迭代 PEFT、MS-Swift

全参数微调(Full Fine-Tuning)

更新模型所有参数,让模型完全适配下游任务(如特定领域问答、情感分析)。

优缺点

  • 优点:性能理论上最优,能最大程度挖掘模型潜力,适合数据量充足的场景。

  • 缺点:参数量巨大(如 10B 模型需更新 100 亿 + 参数),显存消耗极高(单卡难以支撑),训练速度慢容易过拟合(小数据集场景)

应用场景

  • 大规模标注数据(如百万级以上)。

  • 充足计算资源(多 GPU 集群,如 8×A100)。

  • 任务性能要求极高(如工业级质检、专业领域推理)。

示例:

基于 Transformers+Accelerate

训练参数(全量微调需大batch和低学习率)

from transformers import AutoModelForCausalLM, AutoTokenizer, TrainingArguments, Trainer
from datasets import load_dataset


# 加载模型和分词器(以Llama 3为例)
model_name = "meta-llama/Llama-3-8B-Instruct"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(
    model_name,
    device_map="auto",  # 自动分配到可用GPU
    torch_dtype="auto"
)

# 加载微调数据集(示例:情感分析)
dataset = load_dataset("imdb")
def preprocess_function(examples):
    texts = [f"情感分析:{text}\n答案:{'正面' if label == 1 else '负面'}" 
             for text, label in zip(examples["text"], examples["label"])]
    return tokenizer(texts, truncation=True, max_length=512, padding="max_length")
tokenized_dataset = dataset.map(preprocess_function, batched=True)


# 训练参数(全量微调需大batch和低学习率)
training_args = TrainingArguments(
    output_dir="./full_finetune_llama3",
    per_device_train_batch_size=4,  # 单卡batch
    gradient_accumulation_steps=8,  # 梯度累积(模拟大batch)
    learning_rate=2e-5,  # 全量微调学习率较低
    num_train_epochs=3,
    logging_dir="./logs",
    fp16=True,  # 混合精度训练节省显存
    report_to="none"
)

# 训练
trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=tokenized_dataset["train"],
    eval_dataset=tokenized_dataset["test"]
)
trainer.train()

参数高效微调(PEFT,Parameter-Efficient Fine-Tuning

仅更新模型少量参数(如新增适配器层),冻结预训练模型主体参数,在保证性能的同时大幅降低计算成本。

主流方法:LoRA(Low-Rank Adaptation)、Prefix Tuning、IA³、Adapter 等,其中LoRA应用最广泛。

优缺点

  • 优点:显存消耗低(仅需全量微调的 10%-30%),训练速度快,适合小数据集(万级样本),泛化性强(不易过拟合)。

  • 缺点理论性能略低于全量微调(但实际场景中差距极小)推理时需加载适配器权重(部分框架需合并权重)。

应用场景

  • 中小规模数据集(数千至数万样本)。
  • 计算资源有限(单 GPU 或少量 GPU)。
  • 快速迭代的场景(如企业内部工具、垂直领域微调)。

特征 Prompt Tuning Prefix Tuning Adapter LoRA
核心思想 输入层加入可学习的软提示向量 Transformer每一层的Key和Value前添加可训练向量 在Transformer层内插入小型神经网络模块 为原始权重矩阵增加一个低秩的增量矩阵
调参位置 输入嵌入层 所有Transformer层的注意力模块 通常位于FFN层之后或注意力模块后 通常应用于注意力层的Q、V投影矩阵
参数效率 极高 中等
训练速度 很快 较快 中等(存在序列化操作)
性能表现 较好(尤其对大模型) 好,接近全量微调 好,但早期结构有瓶颈 很好,常与全量微调媲美
推理延迟 有(因延长了序列) (因增加了深度) (权重可合并)

LoRA:用“低秩分解”模拟参数更新

  • 原理:LoRA的灵感来源于一个发现:模型在微调过程中的参数增量(ΔW)可能拥有很低的"内在秩"。它并不直接学习完整的增量矩阵,而是用两个更小矩阵的乘积(B·A)来模拟这个增量。训练时,固定原始权重W,只训练A和B。训练结束后,增量部分可以直接合并到原始权重中,因此推理时没有任何额外开销

  • 数学公式:微调后的权重 W' = W + ΔW = W + B * A (其中 B ∈ ℝ^(d×r), A ∈ ℝ^(r×k), r << min(d,k))

  • 示意图:

  • 上下矩阵AB的初始化

  • 上矩阵(A 矩阵)上矩阵 A 通常采用随机初始化的方式。
  • 常见的初始化方法有正态分布初始化(如从均值为 0、标准差为较小值的正态分布中采样)。这样的初始化能让模型在微调开始时,LoRA 模块有一定的初始表达能力,后续通过训练逐步学习到适配任务的参数。
  • 下矩阵(B 矩阵)下矩阵 B 通常初始化为全零矩阵
  • 这是因为在训练初期,希望 LoRA 模块对原始预训练模型的干扰尽可能小,让模型先基于预训练的知识进行学习,后续再通过训练逐步更新 B 矩阵的参数,使 LoRA 模块逐渐发挥作用,实现对预训练模型的高效适配微调。
LoRA 可以加在哪些层?

LoRA 的适配层选择与模型结构和任务相关,核心是加在模型中对任务敏感的 “关键交互层”,以下是常见选择:

1. Transformer 模型的核心应用层(最常用)

Transformer 是 LoRA 应用的典型场景,主要加在注意力机制的权重矩阵中:

  • Q/K/V 投影层(Query/Key/Value Projection):注意力机制中,将输入向量映射到 Q、K、V 的线性层是捕捉任务相关性的核心(如翻译任务中需关注源语言与目标语言的对齐),LoRA 加在此处可高效学习任务特异性的注意力模式。

    • 例:在 GPT 中,对每个 Transformer 块的 Q、K、V 矩阵分别添加 LoRA;在 BERT 中,可选择仅对 Q 矩阵或 Q+V 矩阵添加(实践中 Q 矩阵效果更显著)。

  • 输出投影层(可选):部分场景会在注意力输出的线性投影层(如 Multi-Head Attention 的拼接后投影层)添加 LoRA,但通常优先级低于 Q/K/V 层。

2. 前馈网络(FFN)层(较少用)

Transformer 的前馈网络(Feed-Forward Network)负责对每个 Token 的向量进行非线性变换,部分任务(如文本生成)可在 FFN 的线性层添加 LoRA,但效果通常不如注意力层显著,且可能增加冗余参数。

3. 嵌入层(Embedding Layer,极少用)

词嵌入层(Token Embedding)是模型的输入层,直接影响对输入 Token 的语义编码。但嵌入层参数与词汇表强相关,对新任务的适配需求较低,且添加 LoRA 可能破坏预训练的通用语义,因此极少使用

示例:

(基于 PEFT+LoRA)

训练参数(LoRA可使用更高学习率)

伪代码:

import torch
import torch.nn as nn

# 假设有一个预训练模型的线性层
pretrained_linear_layer = nn.Linear(768, 768).requires_grad_(False) # 冻结

# ---------- LoRA (简化版) ----------
class LoRALayer(nn.Module):
    def __init__(self, original_weight, rank, alpha):
        super().__init__()
        self.original_weight = original_weight # 冻结的原始权重
        d, k = original_weight.shape
        # 低秩矩阵A和B
        self.lora_A = nn.Parameter(torch.zeros(d, rank))
        self.lora_B = nn.Parameter(torch.zeros(rank, k))
        self.scaling = alpha / rank
        nn.init.kaiming_uniform_(self.lora_A, a=math.sqrt(5))
        nn.init.zeros_(self.lora_B)
        
    def forward(self, x):
        # 原始输出 + LoRA适配后的输出
        return x @ (self.original_weight + self.lora_B @ self.lora_A).T * self.scaling

from transformers import AutoModelForCausalLM, AutoTokenizer, TrainingArguments, Trainer
from peft import LoraConfig, get_peft_model
from datasets import load_dataset

# 加载模型和分词器
model_name = "meta-llama/Llama-3-8B-Instruct"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(
    model_name,
    device_map="auto",
    torch_dtype="auto",
    load_in_4bit=True  # 4bit量化进一步节省显存       ------多的操作
)

# 配置LoRA(仅更新注意力层的低秩矩阵)      ------Lora才有的操作
lora_config = LoraConfig(
    r=16,  # 低秩矩阵维度(越大性能越好,显存消耗越高)
    lora_alpha=32,
    target_modules=["q_proj", "v_proj"],  # 目标层(不同模型层名不同)
    lora_dropout=0.05,
    bias="none",
    task_type="CAUSAL_LM"
)
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()  # 打印可训练参数比例(通常<1%)


# 数据集预处理(同全量微调)
dataset = load_dataset("imdb")
def preprocess_function(examples):
    texts = [f"情感分析:{text}\n答案:{'正面' if label == 1 else '负面'}" 
             for text, label in zip(examples["text"], examples["label"])]
    return tokenizer(texts, truncation=True, max_length=512, padding="max_length")
tokenized_dataset = dataset.map(preprocess_function, batched=True)


# 训练参数(LoRA可使用更高学习率)
training_args = TrainingArguments(
    output_dir="./lora_llama3",
    per_device_train_batch_size=8,  # 单卡batch可更大
    gradient_accumulation_steps=4,
    learning_rate=2e-4,  # LoRA学习率更高
    num_train_epochs=3,
    fp16=True,
    report_to="none"
)

trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=tokenized_dataset["train"],
    eval_dataset=tokenized_dataset["test"]
)
trainer.train()

# 保存LoRA权重(仅几百MB)
model.save_pretrained("lora_weights")

提示微调(Prompt Tuning):给模型一个可学习的“提示”

  • 原理:传统的Prompt是人工设计的文本提示(Hard Prompt)。Prompt Tuning则将其转化为可学习的连续向量(Soft Prompt),直接拼接到输入序列的嵌入表示中,模型通过训练来学习这些向量的最佳值。在训练时,只有这些提示向量会被更新,预训练模型的所有参数都被冻结

  • 示意图:(更容易进行多任务学习,一次训练得到)

示例:(伪代码)

# ---------- Prompt Tuning (简化版) ----------
class PromptTuning(nn.Module):
    def __init__(self, prompt_length, hidden_size):
        super().__init__()
        self.prompts = nn.Parameter(torch.randn(prompt_length, hidden_size))
        
    def forward(self, input_embeddings):
        # 将提示符拼接到输入序列前
        batch_size = input_embeddings.size(0)
        prompts = self.prompts.unsqueeze(0).expand(batch_size, -1, -1)
        return torch.cat([prompts, input_embeddings], dim=1)

Prefix Tuning:在模型每一层“前置”知识

  • 原理:Prefix Tuning认为,仅仅在输入层加提示不够深入。它在每一个Transformer层的自注意力模块中,在处理序列本身的Key和Value向量之前,拼接上一组可训练的向量(即Prefix)。这使得引导信息能够贯穿模型的整个前向传播过程,影响力更强。

  • 自注意力计算: Attn(Query, Concat(Prefix_Key, Original_Key), Concat(Prefix_Value, Original_Value))

  • 示意图:(只对K,V层+前缀知识)

Adapter:在模块之间插入“转换插头”

  • 原理:Adapter借鉴了计算机科学中的适配器设计模式。它在Transformer架构的特定位置(例如,每个FFN层之后)插入一个小型的前馈神经网络。这个网络通常采用"降维-非线性-升维"的结构(瓶颈结构),并配有一个残差连接。训练时,冻结主模型参数,只训练这些Adapter模块

  • 示意图

示例:(伪代码)

# ---------- Adapter (简化版) ----------
class Adapter(nn.Module):
    def __init__(self, hidden_size, adapter_size):
        super().__init__()
        # 降维
        self.down_proj = nn.Linear(hidden_size, adapter_size)
        # 升维
        self.up_proj = nn.Linear(adapter_size, hidden_size)
        self.activation = nn.ReLU()
        
    def forward(self, x):
        # 残差连接
        return x + self.up_proj(self.activation(self.down_proj(x)))

不同框架以及使用方法

1. PEFT(参数高效微调首选)
  • 特点:Hugging Face 官方库,支持 LoRAPrefix Tuning 等多种 PEFT 方法,无缝对接 Transformers 和 Trainer。
  • 安装pip install peft
  • 核心优势:简单易用,可与量化(BitsAndBytes)、加速(Accelerate工具结合。
2. MS-Swift(阿里一站式微调框架)
  • 特点:集成数据处理、微调、部署全流程,支持全量微调、LoRA、Prompt Tuning,自动优化训练参数。
  • 安装pip install ms-swift
  • 使用示例(LoRA 微调):
    # 命令行一键微调(无需写代码)
    swift sft \
      --model_type llama3 \
      --model_name_or_path meta-llama/Llama-3-8B-Instruct \
      --dataset imdb \
      --peft_type lora \
      --output_dir ./swift_lora_results
    
3. FastChat(大模型微调 + 部署一体化)
  • 特点:专注于对话模型,支持全量微调、LoRA,内置训练脚本和评估工具,适合聊天机器人场景。
  • 安装pip install "fschat[model_worker,webui]"
  • 使用示例
    # 微调命令(LoRA)
    python -m fastchat.train.train_lora \
      --model_name_or_path meta-llama/Llama-3-8B-Instruct \
      --dataset path/to/your/data \
      --output_dir ./fastchat_lora
Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐