1. In-Context Learning 原理

In-Context Learning(ICL)是大语言模型的核心涌现能力之一:无需梯度更新,仅通过上下文中的示例就能学会新任务

1.1 ICL 的工作机制

当你在 Prompt 中提供示例时,模型的注意力机制会将示例的模式映射到查询上:

# Zero-shot
任务:将以下句子分类为正面/负面/中性
输入:这家餐厅的服务态度很好
输出:

# Few-shot(1-shot)
任务:将以下句子分类为正面/负面/中性
输入:这道菜太难吃了 → 输出:负面
输入:这家餐厅的服务态度很好 → 输出:

模型通过示例"理解"了任务的输入输出映射规则,而非死记硬背。

1.2 ICL vs Fine-tuning

维度ICL (Few-shot)Fine-tuning
参数更新
数据需求1-20 个示例数百到数万
部署成本低(同一模型)高(需部署多版本)
灵活性高(随时改示例)低(需重新训练)
性能上限中等
延迟较高(长 Prompt)较低

2. 示例数量的影响

2.1 边际收益递减曲线

示例数量与效果的关系遵循对数增长曲线:

准确率
  |        ___________  ← 8-shot
  |      /
  |    /  ← 4-shot  
  |  /
  |/  ← 1-shot
  |________
  |  ← 0-shot
  +--------- 示例数量
     0  1  4  8  16

经验法则:

  • 1-shot:适合简单分类任务,定义输出格式
  • 2-3 shot:大多数 NLP 任务的甜蜜点
  • 4-8 shot:复杂任务或需要展示多样性的场景
  • >8 shot:边际收益极低,浪费 Token

2.2 实验数据

示例数SST-2 准确率GSM8K 准确率
088.2%14.6%
191.5%23.4%
493.1%30.2%
893.4%33.1%
1693.6%34.8%

分类任务 4-shot 后基本饱和,数学任务持续受益但增益递减。

3. 示例选择策略

3.1 随机选择(Random)

最简单的策略:从示例池中随机抽取。

import random

def random_select(examples, k=3):
    return random.sample(examples, k)

优点:简单、无偏见 缺点:可能选到不具代表性的示例,效果波动大

3.2 相似度选择(KNN-based)

用嵌入向量找到与查询最相似的示例:

from sklearn.metrics.pairwise import cosine_similarity
import numpy as np

def similarity_select(examples, query, embedder, k=3):
    """基于语义相似度选择示例"""
    # 编码所有示例和查询
    ex_embeddings = np.array([embedder.embed(e["input"]) for e in examples])
    query_embedding = np.array([embedder.embed(query)])
    
    # 计算余弦相似度
    sims = cosine_similarity(query_embedding, ex_embeddings)[0]
    
    # 取 top-k 最相似
    top_indices = np.argsort(sims)[-k:][::-1]
    return [examples[i] for i in top_indices]

适用场景:分类、信息提取等输入分布敏感的任务 注意:过于相似的示例可能导致模型"复制"而非"推理"

3.3 多样性选择(Diversity-based)

选择互相之间差异大的示例,覆盖更大的模式空间:

def diversity_select(examples, embedder, k=3):
    """最大边际相关性(MMR)选择"""
    embeddings = np.array([embedder.embed(e["input"]) for e in examples])
    selected = []
    selected_indices = []
    
    # 第一个随机选
    first = random.randint(0, len(examples) - 1)
    selected.append(first)
    selected_indices.append(first)
    
    while len(selected) < k:
        best_score = -1
        best_idx = -1
        for i in range(len(examples)):
            if i in selected:
                continue
            # 与已选示例的最小距离(越大越好 = 越不同)
            min_sim = min(
                cosine_similarity(
                    embeddings[i].reshape(1, -1),
                    embeddings[j].reshape(1, -1)
                )[0][0]
                for j in selected
            )
            if min_sim > best_score:
                best_score = min_sim
                best_idx = i
        selected.append(best_idx)
    
    return [examples[i] for i in selected]

3.4 三种策略对比

策略分类任务生成任务数学任务计算开销
随机★★★★★★★极低
相似度★★★★★★★★★★
多样性★★★★★★★★★★★
相似+多样混合★★★★★★★★★★★★★★中高

4. 示例顺序的影响

4.1 近因效应

LLM 存在显著的近因偏差:最后一个示例对输出影响最大。

# 顺序 A:正面 → 负面 → 中性
示例1: "好看" → 正面
示例2: "难吃" → 负面
示例3: "还行" → 中性
查询: "服务一般"
→ 倾向输出"中性"(受最后一个示例影响)

# 顺序 B:中性 → 负面 → 正面
示例1: "还行" → 中性
示例2: "难吃" → 负面
示例3: "好看" → 正面
查询: "服务一般"
→ 倾向输出"正面"(近因效应)

4.2 顺序最佳实践

def order_examples(examples):
    """推荐的示例排列顺序"""
    # 按难度递增排列,最复杂的放最后
    examples_sorted = sorted(examples, key=lambda x: x.get("difficulty", 0))
    
    # 如果有标签分布不均,交错排列避免聚类
    return interleave_by_label(examples_sorted)

def interleave_by_label(examples):
    """按标签交错排列"""
    from collections import defaultdict
    by_label = defaultdict(list)
    for e in examples:
        by_label[e["label"]].append(e)
    
    result = []
    max_len = max(len(v) for v in by_label.values())
    for i in range(max_len):
        for label in by_label:
            if i < len(by_label[label]):
                result.append(by_label[label][i])
    return result

4.3 顺序敏感度实验

排列方式准确率标准差
最优排列94.2%-
随机排列91.8%±2.1%
最差排列87.5%-

最差与最优排列之间差距可达 7%,这纯粹由顺序导致。

5. 示例格式的影响

5.1 格式一致性

示例的格式必须与期望的输出格式完全一致:

# ❌ 不一致:示例用句子,期望 JSON
示例: "好看" → "正面"
查询: "一般" → 
输出: "中性"  ← 不是 JSON

# ✅ 一致:示例和查询都用 JSON
示例: {"text": "好看", "label": "正面"}
示例: {"text": "难吃", "label": "负面"}
查询: {"text": "一般", "label": 

5.2 格式模板化

FEW_SHOT_TEMPLATE = """
任务:情感分析,将输入文本分类为 positive/negative/neutral

示例:
输入: {ex1_input}
输出: {ex1_output}

输入: {ex2_input}
输出: {ex2_output}

输入: {ex3_input}
输出: {ex3_output}

输入: {query}
输出:
"""

# 确保格式严格一致
prompt = FEW_SHOT_TEMPLATE.format(
    ex1_input="这部电影太精彩了",
    ex1_output="positive",
    ex2_input="浪费时间",
    ex2_output="negative",
    ex3_input="还行吧,不功不过",
    ex3_output="neutral",
    query="值得二刷"
)

5.3 Chain-of-Thought 与 Few-shot 结合

在示例中加入推理过程,效果显著优于直接给答案:

# Few-shot CoT
输入: 123 × 45 = ?
推理: 123 × 45 = 123 × 40 + 123 × 5 = 4920 + 615 = 5535
输出: 5535

输入: 789 × 12 = ?
推理:

6. 高级技巧

6.1 动态示例选择

class DynamicFewShotSelector:
    """运行时动态选择示例"""
    
    def __init__(self, example_pool, embedder, k=3):
        self.pool = example_pool
        self.embedder = embedder
        self.k = k
        self.cache = {}  # 缓存嵌入
        
        # 预计算示例嵌入
        for ex in self.pool:
            self.cache[ex["input"]] = self.embedder.embed(ex["input"])
    
    def select(self, query):
        query_emb = self.embedder.embed(query)
        scores = []
        for ex in self.pool:
            sim = cosine_similarity(
                query_emb.reshape(1, -1),
                self.cache[ex["input"]].reshape(1, -1)
            )[0][0]
            scores.append((sim, ex))
        
        scores.sort(reverse=True)
        return [ex for _, ex in scores[:self.k]]

6.2 示例去重与质量过滤

def filter_examples(examples, min_quality=0.7):
    """过滤低质量示例"""
    filtered = []
    seen_embeddings = []
    
    for ex in examples:
        emb = embed(ex["input"])
        
        # 质量检查
        if len(ex["output"].strip()) < 2:
            continue
        if ex.get("quality_score", 1.0) < min_quality:
            continue
        
        # 去重:与已选示例相似度 < 0.9
        if seen_embeddings:
            max_sim = max(
                cosine_similarity(emb.reshape(1,-1), 
                                  prev.reshape(1,-1))[0][0]
                for prev in seen_embeddings
            )
            if max_sim > 0.9:
                continue
        
        filtered.append(ex)
        seen_embeddings.append(emb)
    
    return filtered

7. 总结

Few-shot Prompting 的效果取决于三个关键决策:

  1. 选多少:2-4 个示例覆盖大多数场景,超过 8 个收益极低
  2. 选哪些:相似度+多样性混合策略效果最佳
  3. 怎么排:按难度递增、标签交错、格式严格一致

核心原则:示例是任务的"定义",不是"训练数据"。每个示例都应在教模型一个关于任务的明确信号。

加入讨论

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

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