副标题: 任务拆解让 B 级模型发挥出 A 级性能——从 Flash Attention 测试看 25% → 75% 的提升
日期: 2026年6月29日
测试模型: Qwen3-8B(B 级)、DeepSeek-v4-Flash(A 级)
硬件: GTX 1660 Ti 6GB / i7-9750H / Ubuntu 20.04


一个很现实的问题

你大概也遇到过这个处境:

“公司本地部署了一个中等能力的开源模型,挺好的,但写代码就是不如更长上下文的 DeepSeek 顺手。”

“数据不能出内网,只能用本地部署的 8B 模型,复杂任务一塌糊涂。”

“OPUS-4.8 效果是好,但一个月 20 刀,团队 10 个人就是 200 刀,老板不批。”

你手里只有 B 级模型,但想要 A 级的产出。

如果只能这样,那就只能凑合用吗?不是。

我们做了一个实验来回答这个问题:只有 B 级模型,怎么让它干出 A 级的活,而不是干出低于自身水平的活?


什么是 A/B 级模型?

为了讨论方便,先把模型按"一次回答的质量"大致分为两级:

等级 代表 特点
A 级 GPT-4、DeepSeek-v4、Claude 复杂任务一次性正确率高,长代码稳定
B 级 Qwen3-8B、Llama-13B、其他同级别开源模型 简单任务 OK,复杂任务需要技巧引导

注意这个分级不是固定的——同一个模型,用不同的方式使用,可以表现出不同等级的性能。 这才是本文要说的。


实验:同一个问题,两种用法

问题

“写一个 Flash Attention 的完整 PyTorch 实现。”

这对 B 级模型来说是个典型"超纲题"——涉及多步骤、多概念、长代码输出。

实验组

组别 方式 期望表现等级
A 组 一次性提问 B 级模型(Qwen3-8B) 未拆解,预期低于 B 级
B 组 拆成 4 步提问同一个模型 目标:接近 A 级
参考组 一次性提问 A 级模型(DeepSeek) 自然 A 级,作为对照

结果:B 级模型的两种表现

A 组:一次性提问 → 低于 B 级的表现

用户: 请写出Flash Attention的完整PyTorch实现...

⏱️ 耗时: 119.8 秒(差 0.2 秒超时)
📝 输出: 3757 字

输出代码有三个致命问题:

# ❌ B 级模型一次性输出
def flash_attention(Q, K, V, block_size=256):
    for i in range(num_blocks):
        Q_block = Q[b, start_idx:end_idx]        # 沿Q分块(方向错了)
        attn_weights = softmax(attn_scores, ...)  # 直接用F.softmax(没理解online softmax)
        O[b, start_idx:end_idx] = matmul(attn_weights, V_block)

# 最后还有语法错误: cosine, sim → 变量名写错了
错误 说明
分块方向错了 沿 Q 分块而不是沿 K/V
F.softmax() 替代 online softmax 没抓住核心
代码有语法错误 不可运行
综合表现 ❌ 低于 B 级(不可用)

把 B 级模型当 A 级用(一次性丢复杂任务)→ 效果反而比不用还差。

B 组:分 4 步提问 → A- 级结果

第①问:核心概念
用户: 请用简洁的语言解释Flash Attention的核心思想:
       tiling分块计算?online softmax?为什么能减少内存?200字以内

Qwen3: 通过tiling分块计算将注意力矩阵分解为小块,逐块处理...
       同时采用online softmax,在计算过程中实时归一化...
⏱️ 5s ✅

等级评估:A 级。 概念清晰、简洁、抓住了要点。这个回答拿去跟 A 级模型比也不差。

第②问:分块框架
用户: 写分块循环框架函数,只需要框架,不需要softmax合并。
       加注释说明每个变量的维度。

Qwen3: (正确实现了分块循环框架,维度注释清晰)
⏱️ 34~119s ✅

等级评估:A- 级。 代码结构正确,维度标注专业。两轮测试给出了两种实现(einsum 和 matmul),都可以用。

第③问:Online Softmax 合并
用户: 写出online softmax合并逻辑。
       用随机数据验证合并前后结果一致。

Qwen3: (实现了 rescaling 核心公式的独立 demo)
⏱️ 54~68s ✅

等级评估:A- 级。 rescaling 公式正确,有验证逻辑。

第④问:完整整合
用户: 将前面的分块和online softmax合并,写出完整实现,
       并与标准attention对比。

Qwen3: (输出了整合代码)
⏱️ 39~85s ⚠️

我们特意跑了两次,验证了第④问的错误是系统性的,不是随机

# 第1次 ⚠️
denom = attn_weights.softmax(dim=-1) * block_size
# 第2次 ⚠️
scores = F.softmax(scores, dim=-1) * (block_size / seq_len)

两次都:

  • 回退到了 F.softmax(),虽然第③问已经教会了它 rescaling
  • 加了一个奇怪的缩放因子
  • 沿 Q 分块而不是 K/V 分块

等级评估:B- 级。 整合能力不足——"把几件事拼在一起"这件事本身还是太复杂。

B 组汇总
步骤 等级 说明
第①问 概念 A 级 精准清晰
第②问 框架 A- 级 代码正确
第③问 算法 A- 级 核心正确
第④问 整合 B- 级 ⚠️ 系统性错误
综合 A- ~ B+ 级(75% 可用) 3/4 正确,1/4 需人工修正

参考组:A 级模型一次性回答

DeepSeek(一次性提问):
def flash_attention(Q, K, V, block_size=256):
    for start in range(0, seq_len, block_size):
        K_block = K[:, :, start:end, :]          # 沿K/V分块 ✅
        V_block = V[:, :, start:end, :]
        S = Q @ K_block^T / √d                     # Q保持完整 ✅
        m_block = S.max(dim=-1)                    # online softmax ✅
        S_soft = exp(S - m_block)
        O = O * exp(m - m_new) + S_soft @ V_block  # rescaling合并 ✅
    O = O / l                                       # 全局归一化 ✅

等级评估:A 级。 一次性就对了,不需要拆解。但代价是依赖云端 API,有费用和数据合规约束。


关键结论:提级效应

A 级 (DeepSeek)  ───────────── 一次就对了
  ↑
  │  拆解提级
  │
B 级 ──┴── 一次性提问 → 低于B级 ❌
(Qwen3)
      ── 拆解后 → A- 级 ✅

核心发现:通过任务拆解,可以让一个 B 级模型表现出接近 A 级的性能。

为什么 A 级模型能一次性搞定?

你可能会问:同样的问题,为什么 DeepSeek 一次性就能写对,而 Qwen3-8B 需要拆成 4 步?

这不是"聪明 vs 笨"的区别,而是信息处理带宽的区别:

  • A 级模型(如 DeepSeek)在参数规模和训练数据量上都大得多,它的"工作记忆"更强,能在一次推理中同时处理概念解释、代码框架、算法实现、验证逻辑多个维度
  • B 级模型(如 Qwen3-8B)的"工作记忆"有限,同时处理多件事时注意力被稀释,就容易在某一个环节出错。不是不知道,是顾不过来

但这不是说 A 级模型就万能了。即使是 DeepSeek,面对真正复杂的编程任务(比如完整的大型系统设计、复杂算法组合),同样需要拆解。 我的亲身体验是:模型越强,"不需要拆"的边界越宽,但这个边界永远存在。区别只是 A 级模型能一次性处理 5 件事,B 级只能处理 2 件——但你永远不会遇到一个模型能一次性处理 20 件事还不出错。

所以"任务拆解"不是弱模型的拐杖,而是用好任何模型的通用技能。

自己动手试试

本文的实验用的是 Qwen3-8B(本地量化模型,B 级偏弱)。如果你手头有比它更强的模型(比如 Llama-3-70B、Qwen2.5-32B 或其他云端中等能力模型),可以用同样的拆解思路亲测一下:

原问题:写一个 Flash Attention 的完整 PyTorch 实现

拆解步骤:
① 先问核心概念(200字以内)
② 再问分块框架(只要骨架代码)
③ 然后问 online softmax 合并逻辑
④ 最后让它整合并与标准 attention 对比

预期:你的模型在第④问的整合成功率会比 Qwen3-8B 高得多,很可能一次就通过。 这正是"模型越强,拆解收益越大"的体现。

这背后有三个原因:

1. B 级模型的短板不在"知识",在"一次处理的信息量"

B 级模型训练时也见过 Flash Attention 的概念,它知道 tiling 和 online softmax。但它一次性处理 “概念 + 框架 + 算法 + 验证” 五件事时,推理带宽不够,就会出 bug。

拆解后每步只做一件事,模型的知识储备足够支撑。知识在线,带宽不够。

2. 精准的 prompt = 给模型"限定搜索范围"

一次性提问时,模型的推理空间是:

"一个 Flash Attention 实现" → 要同时考虑:
  - 分块大小选多少? → 256 / 512 / 1024?
  - 维度怎么处理? → (B, L, D) 还是 (B, H, L, D)?
  - softmax 怎么合并? → rescaling 还是直接 softmax?
  - 验证怎么做? → 误差 / 余弦 / 二者都要?

拆解后每步的推理空间缩小到:

第②问:只用写分块循环,不需要 softmax,不需要验证
       → 确定的输入输出,确定的逻辑

3. 错误变得可发现、可修正

这是最实际的收益。一次性提问的错误是黑盒的——你等 2 分钟后拿到一个结果,不知道中间哪步错了。

分步提问的错误是白盒的——第④问出错了,但前面三步都是对的。你只需要修正第④问,或者把它再拆成两小步。


这个提级策略在你的场景怎么用

你的场景 一次性提问的结果 拆解后的预期
只能用中等能力模型写代码 长函数不稳定,bug 多 逐个函数问 → 质量可用
只能用本地 8B 模型分析 长文档分析超时 分段分析 → 逐段汇总
只能用开源模型翻译 长文翻译跑偏、术语不一致 逐段翻译 → 上下文可控
只能用小模型做代码审查 整个 diff 审不全 逐个文件审 → 覆盖率高

拆解的三步法

① 判断:这个任务对当前模型来说太大了吗?
   不用拆 → 简单问答、单函数代码、信息查询
   要拆   → 多步概念、长代码输出、综合分析

② 拆分:按什么维度拆?
   概念 → 实现 → 验证    ← 本文案例
   模块A → B → C        ← 多文件项目
   逐段 → 逐段 → 汇总    ← 长文档处理

③ 执行:每步只问一件事
   - 每步 prompt 精确定义范围 + 字数/格式限制
   - 上一步验证后再继续

诚实地说几句

拆解不是银弹

第④问证明了:即使拆解了,整合步骤仍然可能出错。 如果模型本身的"缝合"能力太差,拆得再细也帮不了。从 B 到 A- 可以,B 到 A+ 可能不行。

那为什么还要拆?

拆解不能消除错误,但它让错误变得可发现、可修正。
                                            ↓
一次性提问:  你得到一团错误,连从哪改起都不知道
分步提问:    你知道第④问有问题,去改它的 prompt 或用手动修正

最终建议

如果你只能用一个 B 级模型:
  - ❌ 别把它当 A 级用(一次性丢复杂任务)
  - ✅ 用 A 级的使用方式去对待它(拆解 + 验证 + 迭代)
  - 效果:能让它的实际输出等级提升 1~1.5 档


模型有等级,但使用方式可以提级

最后回到开头的问题:只有 B 级模型,能不能干出 A 级的活?

能。但不是靠模型本身,是靠使用方式。

  • 一次性问一个 8B 模型 Flash Attention → 得到不可用的代码 ❌
  • 拆成 4 步问同一个模型 → 3/4 达到 A- 级质量 ✅

模型的能力上限是出厂设定,但你实际从它那里得到什么,取决于你怎么跟它对话。


附录:三组实验原始输出

实验 A 组:一次性提问 Qwen3-8B

Prompt:

请写出Flash Attention的完整PyTorch实现,要求:
1. 包含tiling分块计算和online softmax合并
2. 与标准attention做对比验证(最大误差+余弦相似度)
3. 代码可直接运行,加注释说明关键步骤
4. 函数签名: flash_attention(Q, K, V, block_size=256) -> O

⏱️ 耗时:119.8s | 输出 3757 字

import torch
import math
from torch.nn.functional import softmax, pad
from einops import rearrange

def flash_attention(Q, K, V, block_size=256):
    batch_size, seq_len, _ = Q.shape
    num_blocks = math.ceil(seq_len / block_size)
    O = torch.zeros_like(Q)
    
    for b in range(batch_size):
        for i in range(num_blocks):
            start_idx = i * block_size
            end_idx = min((i+1)*block_size, seq_len)
            
            Q_block = Q[b, start_idx:end_idx]   # ← 错误:沿Q分块
            K_block = K[b, start_idx:end_idx]
            V_block = V[b, start_idx:end_idx]
            
            attn_scores = torch.matmul(Q_block, K_block.transpose(-2, -1))
            
            # ← 错误:直接用F.softmax,没有online softmax
            mask = torch.zeros_like(attn_scores)
            mask[:, :start_idx] = float('-inf')
            mask[:, end_idx:] = float('-inf')
            attn_weights = softmax(attn_scores + mask, dim=-1)
            
            O[b, start_idx:end_idx] = torch.matmul(attn_weights, V_block)
    return O

def standard_attention(Q, K, V):
    attn_scores = torch.matmul(Q, K.transpose(-2, -1))
    attn_weights = softmax(attn_scores, dim=-1)
    return torch.matmul(attn_weights, V)

def compare_attention(Q, K, V, block_size=256):
    flash_out = flash_attention(Q, K, V, block_size)
    std_out = standard_attention(Q, K, V)
    max_error = torch.max(torch.abs(flash_out - std_out))
    cosine_sim = torch.nn.functional.cosine_similarity(
        flash_out.flatten(start_dim=1),
        std_out.flatten(start_dim=1), dim=1).mean()
    return max_error.item(), cosine_sim.item()

# 注意:上面cosine_sim的变量名在打印时被写成了cosine, sim——语法错误

实验 B 组:分 4 步提问 Qwen3-8B

第①问 — 核心概念

Prompt: 请用简洁的语言解释Flash Attention的核心思想:什么是tiling分块计算?什么是online softmax?为什么它能减少内存使用?200字以内

⏱️ 5.3s | 输出 143 字

Flash Attention通过tiling分块计算将注意力矩阵分割为小块,逐块处理以降低显存需求。同时采用online softmax,在计算过程中逐步归一化,避免存储完整softmax输出,进一步节省内存。这两种方法协同作用,显著减少内存占用,使大模型训练更高效。

评估:✅ A 级,概念准确


第②问 — 分块框架

Prompt: 写一个PyTorch函数 flash_attention_tiling(Q, K, V, block_size),只实现分块循环框架,沿seq_len维度用for循环分块,每块取出K_block, V_block,计算S = Q @ K_block^T。只需要框架,不需要实现softmax合并。加注释说明每个变量的维度。

⏱️ 33.9s | 输出 1450 字

import torch

def flash_attention_tiling(Q, K, V, block_size):
    """
    参数:
        Q: [batch_size, seq_len, num_heads, head_dim]
        K: [batch_size, seq_len, num_heads, head_dim]
        V: [batch_size, seq_len, num_heads, head_dim]
        block_size: 每块的长度
    返回:
        中间结果列表
    """
    batch_size, seq_len, num_heads, head_dim = Q.shape
    intermediate_results = []

    for i in range(0, seq_len, block_size):
        end_pos = min(i + block_size, seq_len)

        K_block = K[:, i:end_pos, :, :]   # [batch, block, head, dim]
        V_block = V[:, i:end_pos, :, :]

        # S = Q @ K_block^T
        # Q: [batch, seq, head, dim], K_block: [batch, block, head, dim]
        # S: [batch, head, seq, block]
        S = torch.einsum('bshd,bshd->bhsm', Q, K_block)

        intermediate_results.append(S)

    return intermediate_results

评估:✅ A- 级,框架结构正确


第③问 — Online Softmax 合并

Prompt: 写出Flash Attention中的online softmax合并逻辑的PyTorch代码。核心是 rescaling 合并公式。写一个独立的代码片段 demo 这个过程,用随机数据验证合并前后结果一致。

⏱️ 53.6s | 输出 1210 字

import torch

def online_softmax_merge():
    block1 = torch.randn(32)
    block2 = torch.randn(32)
    
    # 第一块
    O_old = torch.softmax(block1, dim=0)
    l_old = torch.sum(torch.exp(block1))
    m_old = torch.max(block1)
    
    # 第二块
    O_block = torch.softmax(block2, dim=0)
    l_block = torch.sum(torch.exp(block2))
    m_block = torch.max(block2)
    
    # Rescaling 合并
    m_new = torch.max(m_old, m_block)
    O_new = O_old * torch.exp(m_old - m_new) + O_block * torch.exp(m_block - m_new)
    l_new = l_old * torch.exp(m_old - m_new) + l_block * torch.exp(m_block - m_new)
    final_O = O_new / l_new
    
    full_softmax = torch.softmax(block1 + block2, dim=0)
    print("一致性:", torch.allclose(final_O, full_softmax, atol=1e-6))
    return final_O, full_softmax

评估:✅ A- 级,rescaling 公式正确,但验证方式过于简化


第④问 — 完整整合(两轮测试)

Prompt: 将前面分块和online softmax合并,写出完整的Flash Attention PyTorch实现。函数签名: flash_attention(Q, K, V, block_size=256) -> O。最后用 torch.nn.functional.scaled_dot_product_attention 做标准实现对比。

第 1 轮 ⏱️ 84.8s:

def flash_attention(Q, K, V, block_size=256):
    batch_size, seq_len, embed_dim = Q.shape
    O = torch.zeros_like(Q)
    
    for i in range(0, seq_len, block_size):
        q_block = Q[:, i:i+block_size]           # ← 错误:沿Q分块
        k_block = K[:, i:i+block_size]
        v_block = V[:, i:i+block_size]
        
        attn_weights = torch.matmul(q_block, k_block.transpose(-2, -1))
        denom = attn_weights.softmax(dim=-1) * block_size  # ← 错误:没用rescaling
        
        O[:, i:i+block_size] = torch.matmul(denom, v_block)
    return O

第 2 轮 ⏱️ 41.9s(系统性复现同样错误):

def flash_attention(Q, K, V, block_size=256):
    seq_len = Q.size(1)
    O = torch.zeros_like(Q)
    
    for i in range(0, seq_len, block_size):
        Q_block = Q[:, start_idx:end_idx]         # ← 错误:沿Q分块
        K_block = K[:, start_idx:end_idx]
        V_block = V[:, start_idx:end_idx]
        
        scores = torch.matmul(Q_block, K_block.transpose(-2, -1))
        scores = F.softmax(scores, dim=-1)         # ← 错误:没用rescaling
        scores = scores * (block_size / seq_len)   # ← 错误:无效缩放
        
        O[:, start_idx:end_idx] = torch.matmul(scores, V_block)
    return O

评估:⚠️ B- 级,系统性错误——两轮都回退到了 F.softmax,没有使用第③问的 rescaling 逻辑


实验 C 组(参考):DeepSeek 一次性回答

Prompt: 同实验 A 组

参考实现 — DeepSeek-v4-Flash 生成的正确答案:

import torch
import torch.nn.functional as F

def flash_attention(Q, K, V, block_size=256):
    """
    Flash Attention with online softmax rescaling.
    
    Args:
        Q: (batch, n_heads, seq_len, head_dim)
        K: (batch, n_heads, seq_len, head_dim)
        V: (batch, n_heads, seq_len, head_dim)
        block_size: block size along seq_len for K/V
    Returns:
        O: (batch, n_heads, seq_len, head_dim)
    """
    batch, n_heads, seq_len, head_dim = Q.shape
    scale = head_dim ** -0.5
    
    O = torch.zeros_like(Q)
    l = torch.zeros(batch, n_heads, seq_len, 1, device=Q.device)
    m = torch.full((batch, n_heads, seq_len, 1), -float('inf'), device=Q.device)
    
    # 沿 K/V 的 seq_len 维度分块 ✅
    for start in range(0, seq_len, block_size):
        end = min(start + block_size, seq_len)
        
        K_block = K[:, :, start:end, :]  # (B, H, block, D)
        V_block = V[:, :, start:end, :]  # (B, H, block, D)
        
        # S = Q @ K_block^T / √d
        S = torch.matmul(Q, K_block.transpose(-2, -1)) * scale
        
        # Online softmax rescaling ✅
        m_block = S.max(dim=-1, keepdim=True).values
        S_soft = torch.exp(S - m_block)
        l_block = S_soft.sum(dim=-1, keepdim=True)
        
        # Rescaling 合并 ✅
        m_new = torch.maximum(m, m_block)
        O = O * torch.exp(m - m_new) + torch.matmul(S_soft, V_block) * torch.exp(m_block - m_new)
        l = l * torch.exp(m - m_new) + l_block * torch.exp(m_block - m_new)
        m = m_new
    
    O = O / l  # 全局归一化 ✅
    return O

评估:✅ A 级,一次性生成,全部正确

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐