Few-shot 的本质:上下文学习

大语言模型具备一种独特能力——上下文学习(In-Context Learning, ICL)。模型不需要更新参数,仅通过 Prompt 中提供的少量示例就能学会新任务。Few-shot Prompt 就是利用这一能力的技术。

核心公式:

模型输出 = f(Prompt 中的示例分布 × 输入查询)

模型并非"理解"了任务规则,而是从示例中提取了统计模式(输入-输出的映射关系、格式规范、语言风格),并在推理时应用这些模式。这意味着:示例的选择、排列、格式直接决定了模型的表现。

示例选择策略

随机选择

从示例池中随机抽取 K 个样本。最简单但效果最不稳定。

import random

examples = load_examples("data/training_set.jsonl")
selected = random.sample(examples, k=4)

问题: 随机选取可能选到不代表任务分布的示例,导致模型行为偏移。

相似性选择

基于语义相似度,选取与当前输入最接近的示例。这是最常用的策略:

from sentence_transformers import SentenceTransformer
import numpy as np

class SimilarExampleSelector:
    def __init__(self, examples, model_name="all-MiniLM-L6-v2"):
        self.encoder = SentenceTransformer(model_name)
        self.examples = examples
        self.embeddings = self.encoder.encode(
            [ex["input"] for ex in examples],
            normalize_embeddings=True
        )
    
    def select(self, query, k=4):
        query_emb = self.encoder.encode([query], normalize_embeddings=True)
        # 余弦相似度
        scores = self.embeddings @ query_emb.T
        top_k_idx = np.argsort(scores.flatten())[-k:][::-1]
        return [self.examples[i] for i in top_k_idx]

效果: 在分类任务上,相似性选择通常比随机选择提升 3-8% 准确率。

注意: 相似性度量很关键。文本嵌入的相似性不等于任务语义相似性。在情感分析中,与查询情感极性相同的示例可能比主题相同的示例更有用。

多样性选择

确保所选示例覆盖不同类别/模式,避免同质化:

def diverse_select(query, examples, k=4, n_clusters=4):
    """先聚类,再从每个簇中选最相似的"""
    from sklearn.cluster import KMeans
    
    embeddings = encode_all(examples)
    kmeans = KMeans(n_clusters=n_clusters, random_state=42).fit(embeddings)
    
    selected = []
    for cluster_id in range(n_clusters):
        cluster_indices = np.where(kmeans.labels_ == cluster_id)[0]
        cluster_examples = [examples[i] for i in cluster_indices]
        # 从该簇中选与 query 最相似的
        best = select_most_similar(query, cluster_examples)
        selected.append(best)
    
    return selected[:k]

选择策略对比

策略实现难度计算成本效果稳定性最佳场景
随机极低O(1)基线对比、快速原型
相似性O(N) 检索中-高分类、抽取、QA
多样性O(N) + 聚类多类别任务、生成任务
混合中-高最高生产环境

示例顺序的影响

一个被广泛验证的现象:示例顺序对模型输出有显著影响。

研究表明:

  • 放在 Prompt 末尾的示例对模型影响最大(近因效应)
  • 放在开头的示例也有较强影响(首因效应)
  • 中间位置的示例影响最弱
import itertools

def find_optimal_order(examples, eval_set):
    """暴力搜索最优排列(仅适合示例数少时)"""
    best_acc, best_order = 0, None
    for perm in itertools.permutations(range(len(examples))):
        ordered = [examples[i] for i in perm]
        acc = evaluate(few_shot_prompt(ordered), eval_set)
        if acc > best_acc:
            best_acc, best_order = acc, perm
    return best_order, best_acc

实用建议:

  • 示例数 ≤ 4 时,可以尝试排列搜索
  • 生产环境用固定顺序 + 评估集验证
  • 将最"典型"的示例放在最后(紧邻查询)

格式设计

标准格式

输入:[示例输入1]
输出:[示例输出1]

输入:[示例输入2]
输出:[示例输出2]

输入:[实际查询]
输出:

结构化格式

{
  "examples": [
    {"input": "今天天气真好", "sentiment": "positive", "confidence": 0.95},
    {"input": "这服务太差了", "sentiment": "negative", "confidence": 0.92}
  ],
  "query": "产品质量还可以",
  "response_format": {"sentiment": "string", "confidence": "number"}
}

对话格式

User: 判断以下句子的情感:今天天气真好
Assistant: {"sentiment": "positive", "reason": "表达了愉悦的情绪"}

User: 判断以下句子的情感:这服务太差了
Assistant: {"sentiment": "negative", "reason": "表达了不满的情绪"}

User: 判断以下句子的情感:产品质量还可以
Assistant:

格式选择原则

格式优势劣势适用场景
键值对简单直观解析需正则简单分类
JSON可靠解析Token 开销大结构化输出
对话符合模型训练分布轮次混乱风险指令跟随
XML标签清晰分隔Token 开销大复杂嵌套结构

关键原则:输出格式必须在示例中 100% 一致。 一个格式不规范的示例会破坏模型对输出格式的遵守。

Few-shot vs Fine-tune:边界在哪

何时用 Few-shot

  • 任务数据量小(< 1000 条)
  • 需要快速迭代验证
  • 任务逻辑相对简单
  • 不愿承担 Fine-tune 的训练成本
  • 任务经常变化

何时该 Fine-tune

  • 任务数据量大(> 10,000 条)
  • Few-shot 已无法装入 context window
  • 推理成本成为瓶颈(Fine-tune 小模型更经济)
  • 任务有大量领域特定术语/格式
  • 需要稳定的、确定性的输出

成本模型对比

Few-shot 成本 = API调用次数 × (示例Token数 + 查询Token数 + 输出Token数) × 单价
Fine-tune 成本 = 训练成本(GPU) + 部署成本 + API调用次数 × (查询Token + 输出Token) × 单价

当 API 调用次数 > N 时,Fine-tune 更经济。N 的计算:

def break_even_point(examples_tokens, ft_train_cost, ft_deploy_cost_monthly,
                     api_price_per_1k, ft_api_price_per_1k):
    """计算 Fine-tune 的盈亏平衡点"""
    monthly_savings_per_call = (examples_tokens / 1000) * (api_price_per_1k - ft_api_price_per_1k)
    return (ft_train_cost + ft_deploy_cost_monthly) / max(monthly_savings_per_call, 0.001)

实战清单

  1. 示例质量 > 示例数量:4 个高质量示例胜过 16 个普通示例
  2. 覆盖任务分布:示例应覆盖正/负/边界 case
  3. 格式绝对一致:所有示例严格遵循同一格式模板
  4. 隔离示例与查询:用明确分隔符(\n\n---\n\n)区分
  5. 示例数经验值:分类任务 2-4 个,抽取任务 3-5 个,生成任务 1-3 个
  6. 动态选择:生产环境推荐相似性 + 多样性混合策略
  7. 评估驱动:建立评估集,量化每次示例变更的影响

Few-shot Prompt 的本质是用示例"编程"模型行为。每一个示例都是一条隐式规则,每一次排列都是一次程序结构设计。把示例当作代码来对待——版本管理、测试覆盖、代码审查——你的 Prompt 就会从手工艺走向工程化。

加入讨论

这篇文章有姊妹讨论帖在硅基AGI论坛 — 全球首个碳基硅基认知交流平台。

碳基与硅基的智慧碰撞,认知差异创造无限可能。