大模型Prompt-Tuning技术详解:从入门到进阶

一文读懂NLP范式演进、Fine-Tuning与Prompt-Tuning的核心原理

前言

随着ChatGPT、GPT-4等大模型的爆火,Prompt-Tuning技术逐渐成为学术界和工业界关注的焦点。本文将系统介绍NLP任务的四种发展范式,深入剖析Fine-Tuning和Prompt-Tuning的核心原理,并带你了解面向超大规模模型的先进微调技术。

微调方法对比一览表

方法 是否训练 更新什么参数 是否需要标注数据 算力消耗 核心原理 适用场景 一句话总结
传统全参微调 ✅ 是 全部权重W ✅ 需要 极高 直接修改模型所有参数适配任务 任务与预训练差异大、数据充足 动全身,算力爆炸
传统下游微调 ✅ 是 最后几层+分类头 ✅ 需要 中等 只改任务相关层,冻结其他层 资源有限、任务较简单 局部调整,省一点
LoRA ✅ 是 旁路矩阵A、B ✅ 需要 权重更新量低秩分解,原模型冻结 通用首选,资源受限场景 加插件,原模原样
Hard Prompt (PET) ✅ 是 全部权重W ✅ 需要 中等 用[MASK]将任务伪装成MLM 小样本分类任务 加掩码,伪装填空
Soft Prompt ✅ 是 Prompt向量P ✅ 需要 可学习的连续向量作为提示 大模型快速适配 学暗号,向量引导
In-Context Learning ❌ 否 ❌ 不需要 极低(仅推理) 给示例让模型"现学" 超大规模模型(>10B) 给范例,现学现卖

一、NLP任务四种范式

学术界一般将NLP任务的发展分为四个阶段:

范式 特征工程 数据需求 模型类型 典型任务 优点 缺点
第一范式 需要 标注数据 传统机器学习 文本分类、NER 可解释性强 特征工程耗时
第二范式 不需要 标注数据 深度学习 机器翻译 自动学习特征 需要大量标注数据
第三范式 不需要 未标注+标注数据 预训练模型 几乎所有NLP任务 泛化能力强 预训练成本高
第四范式 不需要 少量/无标注数据 预训练模型 分类、生成、推理 少样本/零样本学习 提示设计复杂

样本示例 - 同一任务四种范式的实现差异

python 复制代码
// 任务:判断邮件是否为垃圾邮件

// 第一范式(传统机器学习)
{
  "特征工程": "提取TF-IDF特征、关键词频率",
  "输入": "[0.23, 0.45, 0.12, ...] (特征向量)",
  "模型": "朴素贝叶斯分类器",
  "输出": "spam"
}

// 第二范式(深度学习)
{
  "输入": "恭喜您获得一等奖!点击领取",
  "模型": "Word2Vec + LSTM",
  "输出": "spam",
  "特点": "自动提取特征,无需人工构造"
}

// 第三范式(预训练+微调)
{
  "预训练": "BERT在海量文本上训练",
  "微调数据": "1000条标注邮件",
  "输入": "[CLS] 恭喜您获得一等奖![SEP]",
  "输出": "spam",
  "特点": "少量数据即可训练出好模型"
}

// 第四范式(Prompt)
{
  "预训练": "GPT-3在海量文本上训练",
  "模板": "这条消息是垃圾邮件吗?[文本] 答案:[MASK]",
  "输入": "恭喜您获得一等奖!",
  "输出": "是",
  "特点": "无需标注数据,直接推理"
}

二、Fine-Tuning(传统微调)

2.1 基本原理

Fine-Tuning是一种迁移学习方式,采用已在大量文本上训练好的预训练语言模型,在小规模任务特定数据上继续训练。

预训练模型(大量通用数据)→ 微调(少量任务数据)→ 适配下游任务

2.2 完整样本示例

训练语料 - 情感分类任务

python 复制代码
# 预训练阶段(BERT)
预训练数据 = [
    "The movie [MASK] really good.",  # 预测: is
    "I [MASK] to the store yesterday.",  # 预测: went
    "She is a [MASK] doctor.",  # 预测: great/good/best
    # ... 海量无标注文本
]

# 下游微调阶段(情感分类)
训练数据 = [
    {"text": "I love this movie!", "label": "positive"},
    {"text": "This film is terrible.", "label": "negative"},
    {"text": "The acting was amazing.", "label": "positive"},
    {"text": "What a waste of time!", "label": "negative"},
    {"text": "Absolutely fantastic!", "label": "positive"},
    # ... 通常需要500-1000条标注数据
]

微调过程示意

python 复制代码
# 传统微调的代码逻辑
class BERTWithClassifier(nn.Module):
    def __init__(self):
        self.bert = BertModel.from_pretrained("bert-base")  # 预训练权重
        self.classifier = nn.Linear(768, 2)  # 新增分类层
    
    def forward(self, input_ids):
        # 1. BERT编码
        cls_vector = self.bert(input_ids)["pooler_output"]  # [batch, 768]
        
        # 2. 分类
        logits = self.classifier(cls_vector)  # [batch, 2]
        return logits

# 训练时更新全部参数
for param in model.parameters():
    param.requires_grad = True  # 1.1亿参数全部更新!

输入输出示例

python 复制代码
{
  "输入": "[CLS] I love this movie! [SEP]",
  "BERT编码": "[0.23, -0.45, 0.67, ...] (768维向量)",
  "分类层输出": "[0.85, 0.15]",
  "最终输出": "positive"
}

2.3 存在的问题

问题 说明 示例
语义偏差 预训练任务(MLM)与下游任务(分类)目标不同 MLM要预测[MASK],分类要判断类别
过拟合风险 小样本场景下容易过拟合 100条数据训练1.1亿参数 → 记住样本而非学习规律
资源消耗大 每个任务需要存储完整模型副本 10个任务 = 10个完整的BERT模型

三、Prompt-Tuning(提示微调)

3.1 什么是Prompt?

Prompt就是你给大型语言模型的所有输入,目的是引导它生成你想要的输出。

样本示例 - Prompt的组成

python 复制代码
{
  "基础Prompt": "请翻译成英文:你好",
  "包含指令": "请将以下中文翻译成英文,只输出翻译结果不要解释:你好",
  "包含上下文": "用户对话历史:... 当前用户说:你好",
  "包含角色": "你是一位专业的翻译官,请翻译:你好",
  "包含格式": "请以JSON格式输出:{\"input\": \"你好\", \"output\": \"hello\"}",
  "包含范例": "示例:再见→goodbye\n你好→",
  "包含约束": "翻译结果不超过10个字母:你好",
  "包含思维链": "让我们一步步思考:1.识别语言 2.查找对应 3.输出结果"
}

3.2 核心理念对比

Fine-Tuning : 模型迁就任务(改变模型本身)
Prompt-Tuning: 任务迁就模型(改变输入方式)

比喻示例

python 复制代码
{
  "Fine-Tuning": "让一个只会中文的人学英语 → 改变人本身",
  "Prompt-Tuning": "让一个人用中文回答问题 → 改变提问方式"
}

3.3 工作原理示例

以情感二分类为例:

python 复制代码
# ========== 传统Fine-Tuning ==========
输入 = "[CLS] I like this film. [SEP]"
       ↓
BERT → [CLS]向量 (768维)
       ↓
新加的分类器 → positive/negative

# ========== Prompt-Tuning ==========
输入 = "[CLS] I like this film. [SEP] It was [MASK]. [SEP]"
       ↓
BERT → 预测[MASK]位置的词
       ↓
Verbalizer映射: great→positive, terrible→negative

四、Prompt-Tuning主要方法

4.1 In-Context Learning(上下文学习)

特点:不训练模型,只给提示

Zero-shot(零样本)
python 复制代码
{
  "输入": "这个任务要求将中文翻译为英文. 销售→",
  "模型输出": "sell",
  "特点": "只给任务描述,不给示例"
}
One-shot(单样本)
python 复制代码
{
  "输入": "这个任务要求将中文翻译为英文. 你好→hello, 销售→",
  "模型输出": "sell",
  "特点": "给1个示例"
}
Few-shot(少样本)
python 复制代码
{
  "输入": "这个任务要求将中文翻译为英文. 你好→hello, 再见→goodbye, 购买→purchase, 销售→",
  "模型输出": "sell",
  "特点": "给10-100个示例"
}

适用条件:模型参数 > 100亿


4.2 Hard Prompt(离散提示/PET)

核心思想:用[MASK]完形填空代替分类任务

完整样本示例
python 复制代码
# ========== 原始数据 ==========
原始数据 = {
  "text": "I love this movie!",
  "label": "positive"
}

# ========== Pattern模板设计 ==========
模板1 = "{text} It was [MASK]."
模板2 = "{text} I felt [MASK]."
模板3 = "The movie is [MASK]. {text}"

# ========== Verbalizer标签映射 ==========
verbalizer = {
    "positive": ["great", "amazing", "wonderful", "good", "fantastic"],
    "negative": ["terrible", "awful", "bad", "poor", "horrible"]
}

# ========== 转换后的训练样本 ==========
训练样本 = {
  "输入": "[CLS] I love this movie! [SEP] It was [MASK]. [SEP]",
  "目标词": "great",  # 而不是直接预测positive
  "标签": "positive"
}

# ========== 推理过程 ==========
测试样本 = "This film is boring."
输入 = "[CLS] This film is boring. [SEP] It was [MASK]. [SEP]"
       ↓ BERT
预测[MASK]: P(great)=0.1, P(amazing)=0.05, P(terrible)=0.6, P(awful)=0.2
       ↓ Verbalizer聚合
P(positive) = 0.1 + 0.05 = 0.15
P(negative) = 0.6 + 0.2 = 0.8
       ↓
输出: negative
PET三步法
python 复制代码
# 步骤1:用少量标注数据微调多个模型
模型1 = 用模板1微调BERT
模型2 = 用模板2微调BERT
模型3 = 用模板3微调BERT

# 步骤2:用微调后的模型标注无标注数据
无标注数据 = ["OK movie", "Not bad", "So so", ...]
软标签 = 模型集成预测(无标注数据)
# 例: "OK movie" → 0.6 positive, 0.4 negative

# 步骤3:用硬标签+软标签训练最终模型
最终模型 = 训练(标注数据 + 无标注数据)

4.3 Soft Prompt(连续提示)

核心思想:将模板转换为可学习的连续向量

Hard Prompt vs Soft Prompt对比
python 复制代码
# ========== Hard Prompt(离散) ==========
hard_prompt = "It was [MASK]."
# 每个词都是固定的真实token
token_ids = tokenizer.encode("It was [MASK].")
# 输出: [1332, 1106, 103]  # 固定的

# ========== Soft Prompt(连续) ==========
soft_prompt = [v1, v2, v3, v4, v5]  # 5个伪标记
# 每个vi是一个可学习的向量(768维)
# 初始化方式1:随机初始化
v1 = torch.randn(768)  # 正态分布随机

# 初始化方式2:从词表复制
v1 = embedding["great"]  # 使用已有词的embedding

# 初始化方式3:使用label word
v1 = embedding["positive_class_token"]

# 训练后,v1成为任务特定的"隐形提示"
Prompt Tuning完整示例
python 复制代码
# ========== 配置 ==========
num_virtual_tokens = 10  # 10个伪标记
hidden_size = 768  # BERT-base维度

# ========== 输入构造 ==========
原始文本 = "I like this film."
文本tokens = tokenizer.encode(原始文本)  # [101, 1045, 2066, 2023, 3231, 1012, 102]

# 拼接伪标记
输入 = [v1, v2, ..., v10] + [101, 1045, 2066, 2023, 3231, 1012, 102]
#      └──可学习参数──┘   └────────固定不变────────┘

# ========== 训练 ==========
训练配置 = {
    "可训练参数": "只有10个伪标记的embedding(10×768=7680个)",
    "冻结参数": "BERT全部1.1亿参数",
    "训练数据": "100条标注样本",
    "优化器": "Adam",
    "学习率": "1e-4"
}

# ========== 推理 ==========
# 训练好的伪标记变成该任务的"通关密码"
输入 = 训练好的伪标记 + "This movie is great!"
输出 = model(输入)  # 预测[MASK]
三种初始化方式对比
python 复制代码
# 方式1:随机初始化
P_random = torch.randn(10, 768)  # 从零开始学

# 方式2:从词表复制
P_copy = embedding[["the", "a", "an", "this", "that"]]  # 用常见词初始化

# 方式3:从label word复制
P_label = embedding[["great", "good", "fantastic"]]  # 用标签词初始化

五、各方法完整对比表

方法 训练方式 更新参数 标注数据需求 算力 样本示例
传统微调 全参数更新 全部W 1000+条 极高 输入:"好电影"+标签→训练
PET 全参数更新 全部W 100+条 "好电影 It was [MASK]"
Prompt Tuning 只训练提示 提示向量P 100+条 [v1][v2]好电影[MASK]
P-Tuning 只训练提示 提示向量P 100+条 任意位置插入[v_i]
In-Context 不训练 0条 极低 给例子→现学→推理

六、进阶PEFT方法

6.1 LoRA(Low-Rank Adaptation)

核心思想:权重更新量ΔW是低秩的,可分解为B×A

python 复制代码
# ========== 传统微调 =========#
W_output = W_input @ W_original  # 更新全部W

# ========== LoRA微调 ==========
W_output = W_input @ (W_original + B @ A)
#          └─冻结不动─┘   └─只训练B和A─┘

# 参数对比示例(d=4096, k=4096, r=8)
W_original参数 = 4096 × 4096 = 16,777,216
A矩阵参数 = 8 × 4096 = 32,768
B矩阵参数 = 4096 × 8 = 32,768
LoRA总参数 = 65,536  # 减少99.6%!

完整代码示例

python 复制代码
from peft import LoraConfig, get_peft_model

# 配置LoRA
lora_config = LoraConfig(
    r=8,                    # 低秩维度
    lora_alpha=16,          # 缩放因子
    target_modules=["q_proj", "v_proj"],  # 在Q和V层加LoRA
    lora_dropout=0.1,
    task_type="SEQ_CLS"
)

# 应用LoRA
model = AutoModelForSequenceClassification.from_pretrained("bert-base")
lora_model = get_peft_model(model, lora_config)

# 查看可训练参数
lora_model.print_trainable_parameters()
# 输出: trainable params: 294,912 || all params: 110,000,000 || trainable%: 0.27%

6.2 QLoRA

核心思想:4-bit量化 + LoRA

python 复制代码
from transformers import BitsAndBytesConfig

# 4-bit量化配置
bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_quant_type="nf4",
    bnb_4bit_use_double_quant=True,
)

# 加载4-bit模型
model = AutoModelForCausalLM.from_pretrained(
    "meta-llama/Llama-2-7b",
    quantization_config=bnb_config  # 70B模型只需48GB显存!
)

七、完整任务示例对比

任务:情感分类(判断评论是积极/消极)

python 复制代码
// ========== 方法1:传统微调 ==========
{
  "训练数据": [
    {"text": "This movie is fantastic!", "label": "positive"},
    {"text": "I hate this film", "label": "negative"},
    {"text": "The acting was amazing", "label": "positive"},
    {"text": "What a waste of time", "label": "negative"},
    "... (500-1000条)"
  ],
  "模型": "BERT + 分类头",
  "训练方式": "更新全部1.1亿参数",
  "推理输入": "This movie is great!",
  "推理输出": "positive"
}

// ========== 方法2:PET ==========
{
  "训练数据": [
    {
      "输入": "This movie is fantastic! It was [MASK].",
      "目标": "great",
      "标签": "positive"
    },
    "... (100-200条)"
  ],
  "模型": "BERT (复用MLM head)",
  "训练方式": "更新全部参数,但任务形式接近预训练",
  "推理输入": "This movie is great! It was [MASK].",
  "推理过程": "预测[MASK]=great → 映射到positive",
  "推理输出": "positive"
}

// ========== 方法3:Prompt Tuning ==========
{
  "训练数据": [
    {
      "输入": "[v1][v2][v3][v4][v5] This movie is fantastic! [MASK]",
      "目标": "great",
      "标签": "positive"
    },
    "... (100-200条)"
  ],
  "可训练参数": "只有[v1-v5]的embedding",
  "推理输入": "[v1][v2][v3][v4][v5] This movie is great! [MASK]",
  "推理输出": "positive"
}

// ========== 方法4:In-Context Learning ==========
{
  "推理输入": "判断情感:\n'This is great' → 积极\n'I hate this' → 消极\n'This movie is fantastic!' →",
  "推理输出": "积极",
  "训练数据": "不需要!"
}

八、选择决策树

九、技术演进时间线

python 复制代码
2018: BERT + Fine-tuning (第三范式)
       ↓
2020: GPT-3 + In-Context Learning (第四范式开端)
       ↓
2021: PET (Hard Prompt) + Prompt Tuning (Soft Prompt)
       ↓
2022: P-Tuning v1/v2 + Instruction-Tuning
       ↓
2023: LoRA + QLoRA (参数高效微调)
       ↓
现在: RAG + Agent (应用落地)

十、总结

维度 核心要点
NLP四范式 传统ML → 深度学习 → 预训练微调 → 提示学习
传统微调 模型迁就任务,更新全部参数,算力大
Prompt-Tuning 任务迁就模型,冻结模型参数,只调输入
Hard Prompt 用[MASK]伪装成完形填空
Soft Prompt 学连续向量作为隐形提示
In-Context 给例子让模型现学,不训练
LoRA 低秩分解,99.6%参数减少
QLoRA 4-bit量化,消费级显卡跑大模型
最佳实践 大模型用LoRA,小模型考虑全微调

📌 记忆口诀

传统微调动全身,每个任务都分身;

LoRA旁路加插件,原模原样不费神;

Hard Prompt加[MASK],伪装填空训模型;

Soft Prompt学暗号,向量引导最聪明;

In-Context给范例,现学现卖是推理。

本文为技术学习笔记,欢迎交流讨论!

相关推荐
FrontAI1 小时前
深入浅出 LangGraph —— 第8章:人机交互:中断与审批流程
人工智能·langchain·人机交互·ai agent·langgraph
Wanderer X1 小时前
【VLM】diffusion
人工智能
mahtengdbb11 小时前
三阶段压缩瓶颈改进YOLOv26特征提取效率与通道自适应能力提升
人工智能·yolo·目标跟踪
嵌入式小企鹅1 小时前
CPU需求变化、RISC-V安全方案、DeepSeek V4适配、太空算力动态
人工智能·驱动开发·华为·开源·算力·risc-v
HyperAI超神经1 小时前
利用堆叠集成学习,英国研究团队实现251颗盾牌座δ型星星震学指数高精度预测
人工智能·机器学习·集成学习
AI刀刀1 小时前
手机deepseek怎么导出pdf
人工智能·ai·pdf·豆包·deepseek·ds随心转
专注&突破1 小时前
用AI学习graphify
人工智能·学习
wayz111 小时前
Day 16 编程实战:PCA主成分分析与技术指标降维
人工智能·算法·机器学习
风雨中的小七1 小时前
和AI一起搞事情#4. 小白用claude code做游戏究竟能踩多少坑
prompt