加速LLM大模型推理,KV缓存技术详解与PyTorch实现

本文涉及的产品
实时数仓Hologres,5000CU*H 100GB 3个月
实时计算 Flink 版,5000CU*H 3个月
智能开放搜索 OpenSearch行业算法版,1GB 20LCU 1个月
简介: 大型语言模型(LLM)的推理效率是AI领域的重要挑战。本文聚焦KV缓存技术,通过存储复用注意力机制中的Key和Value张量,减少冗余计算,显著提升推理效率。文章从理论到实践,详细解析KV缓存原理、实现与性能优势,并提供PyTorch代码示例。实验表明,该技术在长序列生成中可将推理时间降低近60%,为大模型优化提供了有效方案。

随着大型语言模型(LLM)规模和复杂度的指数级增长,推理效率已成为人工智能领域亟待解决的关键挑战。当前,GPT-4、Claude 3和Llama 3等大模型虽然表现出强大的理解与生成能力,但其自回归解码过程中的计算冗余问题依然显著制约着实际应用场景中的响应速度和资源利用效率。

键值(KV)缓存技术作为Transformer架构推理优化的核心策略,通过巧妙地存储和复用注意力机制中的中间计算结果,有效解决了自回归生成过程中的重复计算问题。与传统方法相比,该技术不仅能够在不牺牲模型精度的前提下显著降低延迟,更能实现近线性的计算复杂度优化,为大规模模型部署提供了实用解决方案。

本文将从理论基础出发,系统阐述KV缓存的工作原理、技术实现与性能优势。我们将通过PyTorch实现完整演示代码,详细分析缓存机制如何与Transformer架构的自注意力模块协同工作,并通过定量实验展示不同序列长度下的性能提升。此外,文章还将讨论该技术在实际应用中的局限性及未来优化方向,为读者提供全面而深入的技术洞察。

无论是追求极致推理性能的AI工程师,还是对大模型优化技术感兴趣的研究人员,本文的实践导向方法都将帮助你理解并掌握这一关键性能优化技术。

KV缓存是一种优化技术,用于存储注意力机制中已计算的Key和Value张量,这些张量可在后续自回归生成过程中被重复利用,从而有效减少冗余计算,显著提升推理效率。

键值缓存原理

注意力机制基础

 # 多头注意力
classMultiHeadAttention(nn.Module):
    def__init__(self, head_size, num_head):
        super().__init__()
        self.sa_head=nn.ModuleList([Head(head_size) for_inrange(num_head)])
        self.dropout=nn.Dropout(dropout)
        self.proj=nn.Linear(embed_size, embed_size)

    defforward(self, x):
        x=torch.cat([head(x) forheadinself.sa_head], dim=-1)
        x=self.dropout(self.proj(x))
         returnx
MultiHeadAttention

类的实现遵循标准的多头注意力模块设计。输入张量形状为(B, T, C),其中B代表批次大小,T表示序列长度(在本实现中最大序列长度为

block_size

),C表示嵌入维度。

多头注意力机制的核心思想是将嵌入空间划分为多个头,每个头独立计算注意力权重。对于嵌入维度C=128且头数量为4的情况,每个头的维度为128/4=32。系统将分别计算这4个大小为32的注意力头,然后将结果拼接成形状为(B, T, 128)的输出张量。

KV缓存的必要性

为理解KV缓存的必要性,首先需要分析注意力机制的计算过程:

 classHead(nn.Module):
    def__init__(self, head_size):
        super().__init__()
        self.head_size=head_size
        self.key=nn.Linear(embed_size, head_size, bias=False)
        self.query=nn.Linear(embed_size, head_size, bias=False)
        self.value=nn.Linear(embed_size, head_size, bias=False)
        self.register_buffer('tril', torch.tril(torch.ones(block_size, block_size)))
        self.dropout=nn.Dropout(dropout)

    defforward(self, x):
        B, T, C=x.shape
        k=self.key(x)
        q=self.query(x)
        v=self.value(x)
        wei=q@k.transpose(2, 1)/self.head_size**0.5
        wei=wei.masked_fill(self.tril[:T, :T] ==0, float('-inf'))
        wei=F.softmax(wei, dim=2)    # (B , block_size, block_size)
        wei=self.dropout(wei)
        out=wei@v
         returnout

在注意力头的实现中,系统通过线性变换生成Key、Value和Query,它们的形状均为(B, T, C),其中C为头的大小。

Query与Key进行点积运算,生成形状为(B, T, T)的权重矩阵,表示各token之间的相关性。权重矩阵经过掩码处理转换为下三角矩阵,确保在点积运算过程中每个token仅考虑前面的token(即从1到n),这强制实现了因果关系,使得自回归模型中的token仅使用历史信息预测下一个token。

下图展示了自回归生成在注意力机制中的实现过程:

在自回归生成的每个步骤中,系统均需重新计算已经计算过的Key和Value。例如,在第2步中,K1与第1步生成的K1相同。由于在推理阶段模型参数已固定,相同输入将产生相同输出,因此将这些Key和Value存储在缓存中并在后续步骤中复用是更高效的方法。

下图直观展示了KV缓存的工作机制:

实现KV缓存的主要区别在于:

  1. 推理时每次仅传入一个新token,而非增量传递所有token
  2. 由于Key和Value已缓存,无需重复计算历史token的表示
  3. 无需对权重进行掩码处理,因为每次只处理单个Query token,权重矩阵(QK^T)的维度为(B, 1, T)而非(B, T, T)

缓存机制实现

KV缓存的实现基于形状为(B, T, C)的零张量初始化,其中T为最大处理的token数量(即block_size):

 classHead(nn.Module):
    def__init__(self, head_size):
        super().__init__()
        self.head_size=head_size
        self.key=nn.Linear(embed_size, head_size, bias=False)
        self.query=nn.Linear(embed_size, head_size, bias=False)
        self.value=nn.Linear(embed_size, head_size, bias=False)
        self.dropout=nn.Dropout(dropout)

        self.k_cache=None
        self.v_cache=None
        self.cache_index=0

    defforward(self, x):
        B, T, C=x.shape# 形状: B, 1, C
        k=self.key(x)
        q=self.query(x)
        v=self.value(x)

        # 如果缓存为空则初始化
        ifself.k_cacheisNoneorself.v_cacheisNone:
            # 使用固定大小初始化缓存
            self.k_cache=torch.zeros(B, block_size, self.head_size, device=x.device)
            self.v_cache=torch.zeros(B, block_size, self.head_size, device=x.device)
            self.cache_index=0

         returnout

自回归模型在训练时使用固定的上下文长度,即当前token预测下一个token时可回溯的最大token数量。在本实现中,这个上下文长度由

block_size

参数确定,表示缓存的最大token数量,通过缓存索引进行跟踪:

 defforward(self, x):
        B, T, C=x.shape# B, 1, C
        k=self.key(x)
        q=self.query(x)
        v=self.value(x)

        # 如果缓存为空则初始化
        ifself.k_cacheisNoneorself.v_cacheisNone:
            # 使用固定大小初始化缓存
            self.k_cache=torch.zeros(B, block_size, self.head_size, device=x.device)
            self.v_cache=torch.zeros(B, block_size, self.head_size, device=x.device)
            self.cache_index=0

        # 原地更新缓存
        ifself.cache_index+T<=block_size:
            self.k_cache[:, self.cache_index:self.cache_index+T, :] =k
            self.v_cache[:, self.cache_index:self.cache_index+T, :] =v

        # 注意:鉴于我们一次只传递一个token,T将始终为1,因此上面的操作
        # 等效于直接执行self.k_cache[:, self.cache_index, :] = k

        # 更新缓存索引
        self.cache_index=min(self.cache_index+T, block_size)

        # 注意力点积
        wei=q@self.k_cache.transpose(2, 1)/self.head_size**0.5
        wei=F.softmax(wei, dim=2)    # (B, block_size, block_size)
        wei=self.dropout(wei)
        out=wei@self.v_cache

         returnout

从第一个token开始,系统将Key-Value对存入对应的缓存位置,并递增缓存索引直到达到设定的上限:

 defforward(self, x):
        B, T, C=x.shape# B, 1 (T), C
        k=self.key(x)
        q=self.query(x)
        v=self.value(x)

       ifself.k_cacheisNoneorself.v_cacheisNone:
            # 使用固定大小初始化缓存
            self.k_cache=torch.zeros(B, block_size, self.head_size, device=x.device)
            self.v_cache=torch.zeros(B, block_size, self.head_size, device=x.device)
            self.cache_index=0

        # 原地更新缓存
        ifself.cache_index+T<=block_size:
            self.k_cache[:, self.cache_index:self.cache_index+T, :] =k
            self.v_cache[:, self.cache_index:self.cache_index+T, :] =v
        else:
            # 将token向后移动一步
            shift=self.cache_index+T-block_size# Shift将始终为1
            self.k_cache[:, :-shift, :] =self.k_cache[:, shift:, :].clone()
            self.v_cache[:, :-shift, :] =self.v_cache[:, shift:, :].clone()
            self.k_cache[:, -T:, :] =k
            self.v_cache[:, -T:, :] =v

        # 更新缓存索引
        self.cache_index=min(self.cache_index+T, block_size)

        wei=q@self.k_cache.transpose(2, 1)/self.head_size**0.5
        wei=wei.masked_fill(self.tril[:T, :T] ==0, float('-inf'))
        wei=F.softmax(wei, dim=2)    # (B, block_size, block_size)
        wei=self.dropout(wei)
        out=wei@self.v_cache

         returnout

当缓存索引达到

block_size

时,系统会将所有token向前移动一个位置,为新token腾出空间,并将新token分配到最后一个位置:

 # 缓存移动示例
k_cache=torch.zeros(1, 3, 3)
v_cache=torch.zeros(1, 3, 3)

steps=3
foriinrange(steps):
  k_cache[:, i, :] =torch.randint(10, (1, 3))
print("k_cache Before:\n", k_cache)

shift=1
k_cache[:, :-shift, :] =k_cache[:, shift:, :].clone()
v_cache[:, :-shift, :] =v_cache[:, shift:, :].clone()
print("k_cache After:\n", k_cache)

# 输出 :-
k_cacheBefore:
tensor([[[2., 2., 9.],
         [3., 6., 4.],
         [3., 9., 5.]]])

k_cacheAfter:
tensor([[[3., 6., 4.],
         [3., 9., 5.],
          [3., 9., 5.]]]) # 这个最后的token随后被新的Key Token k_cache[:, -T:, :] = k替换

以上即为KV缓存在注意力机制中的完整实现。接下来我们将分析KV缓存对推理性能的具体影响。

推理性能比较

本节将展示KV缓存优化技术对推理性能的实际影响。以下是实现了KV缓存的GPT模型代码:

 # 导入库
importtorch
importtorch.nnasnn
fromtorch.nnimportfunctionalasF
# 读取txt文件(编码解码)/* 从以下地址下载:https://raw.githubusercontent.com/karpathy/char-rnn/master/data/tinyshakespeare/input.txt */
text=open('input.txt', 'r',).read()
vocab=sorted(list(set(text)))
encode=lambdas: [vocab.index(c) forcins]
decode=lambdal: [vocab[c] forcinl]
# 划分训练集和验证集
x=int(0.9*len(text))
text=torch.tensor(encode(text), dtype=torch.long)
train, val=text[:x], text[x:]
# 创建一个get_batch函数以(batch_size, vocab_size(8))的形状从文本中随机加载数据
device='cuda'iftorch.cuda.is_available() else'cpu'
torch.manual_seed(1337)
batch_size=8# 我们将并行处理多少个独立序列?
block_size=1024# 预测的最大上下文长度?
embed_size=256
dropout=0
num_head=4
num_layers=4
defget_batch(split):
    # 生成一小批输入x和目标y的数据
    data=trainifsplit=='train'elseval
    ix=torch.randint(len(data) -block_size, (batch_size,))
    x=torch.stack([data[i:i+block_size] foriinix])
    y=torch.stack([data[i+1:i+block_size+1] foriinix])
    returnx.to(device), y.to(device)
xb, yb=get_batch('train')
# 注意力头
classHead(nn.Module):
    def__init__(self, head_size):
        super().__init__()
        self.head_size=head_size
        self.key=nn.Linear(embed_size, head_size, bias=False)
        self.query=nn.Linear(embed_size, head_size, bias=False)
        self.value=nn.Linear(embed_size, head_size, bias=False)
        self.register_buffer('tril', torch.tril(torch.ones(block_size, block_size)))
        self.dropout=nn.Dropout(dropout)

    defforward(self, x):
            B, T, C=x.shape
            k=self.key(x)
            q=self.query(x)
            v=self.value(x)
            wei=q@k.transpose(2, 1)/self.head_size**0.5
            wei=wei.masked_fill(self.tril[:T, :T] ==0, float('-inf'))
            wei=F.softmax(wei, dim=2)    # (B , block_size, block_size)
            wei=self.dropout(wei)
            out=wei@v
            returnout

# 多头注意力
classMultiHeadAttention(nn.Module):
    def__init__(self, head_size, num_head):
        super().__init__()
        self.sa_head=nn.ModuleList([Head(head_size) for_inrange(num_head)])
        self.dropout=nn.Dropout(dropout)
        self.proj=nn.Linear(embed_size, embed_size)
    defforward(self, x):
        x=torch.cat([head(x) forheadinself.sa_head], dim=-1)
        x=self.dropout(self.proj(x))
        returnx

classFeedForward(nn.Module):
    def__init__(self, embed_size):
        super().__init__()

        self.ff=nn.Sequential(
              nn.Linear(embed_size, 4*embed_size),
              nn.ReLU(),
              nn.Linear(4*embed_size, embed_size),
              nn.Dropout(dropout)
        )
    defforward(self, x):
        returnself.ff(x)

classBlock(nn.Module):
    def__init__(self, embed_size, num_head):
        super().__init__()
        head_size=embed_size//num_head
        self.multihead=MultiHeadAttention(head_size, num_head)
        self.ff=FeedForward(embed_size)
        self.ll1=nn.LayerNorm(embed_size)
        self.ll2=nn.LayerNorm(embed_size)
    defforward(self, x):
        x=x+self.multihead(self.ll1(x))
        x=x+self.ff(self.ll2(x))
        returnx

# 超简单的bigram模型
classBigramLanguageModelWithCache(nn.Module):
    def__init__(self, vocab_size):
        super().__init__()
        # 每个token直接从查找表中读取下一个token的logits
        self.token_embedding_table=nn.Embedding(vocab_size, embed_size)
        self.possitional_embedding=nn.Embedding(block_size, embed_size)
        self.linear=nn.Linear(embed_size, vocab_size)
        self.block=nn.Sequential(*[Block(embed_size, num_head) for_inrange(num_layers)])
        self.layer_norm=nn.LayerNorm(embed_size)
    defforward(self, idx, targets=None):
        B, T=idx.shape
        # idx和targets都是(B,T)整数张量
        logits=self.token_embedding_table(idx) # (B,T,C)
        ps=self.possitional_embedding(torch.arange(T, device=device))
        x=logits+ps    #(B, T, C)
        logits=self.block(x)     #(B, T, c)
        logits=self.linear(self.layer_norm(logits)) # 这应该在head_size和Vocab_size之间进行映射
        iftargetsisNone:
            loss=None
        else:
            B, T, C=logits.shape
            logits=logits.view(B*T, C)
            targets=targets.view(B*T)
            loss=F.cross_entropy(logits, targets)
        returnlogits, loss
    defgenerate(self, idx, max_new_tokens):
        # idx是当前上下文中索引的(B, T)数组
        for_inrange(max_new_tokens):
            # 获取预测结果
            logits, loss=self(idx) # logits形状:B, 1, C
            logits=logits[:, -1, :] # 变为(B, C)
            # 应用softmax获取概率
            probs=F.softmax(logits, dim=-1) # (B, C)
            # 从分布中采样
            idx_next=torch.multinomial(probs, num_samples=1).to(device) # (B, 1)
            # 无需拼接,因为我们一次只传递一个token
            idx=idx_next
         returnidx

generate函数在推理阶段被显式调用,不参与训练过程:

 m_kv_cache=BigramLanguageModelWithCache(65).to(device)
 m_kv_cache.load_state_dict(torch.load("bigram.pth"))

通过执行generate命令可以测量KV缓存对推理性能的影响:

 m_kv_cache=BigramLanguageModelWithCache(65).to(device)
 steps=10000
 print("".join(decode(m_kv_cache.generate(torch.zeros([1,1], dtype=torch.long).to(device) , max_new_tokens=steps)[0].tolist())))

下图展示了标准模型与KV缓存模型在不同生成步数下的推理时间对比,测试模型的序列长度(block_size)为1024,嵌入维度为256:

 # 数据点
 steps = [100, 500, 1000, 5000, 10000]
 kv-cache = [0.2, 1.3, 2.85, 16.5, 31]  # 红线
 w/0 kv-cache = [0.3, 1.8, 5.01, 40.4, 85]  # 蓝线

实验结果表明,KV缓存模型在推理性能上总体优于标准模型,但其效率取决于浮点运算次数(FLOPs),而FLOPs会随着序列长度(block_size)和嵌入维度(embed_size)的增加而增加。对于较小的模型配置(如block_size=8, embed_size=64),标准模型可能更高效。由于计算复杂度随模型大小增加,KV缓存的优势在大型模型中更为明显。有关标准Transformer与KV缓存模型的FLOPs计算详情,可参考Rajan的技术文章[2]。

以下是生成的输出示例:

 TRUCEMIM-y vit s.

PETRO: histe feRil ass:
Whit Cimovecest isthen iduche neesoxlg anouther ankes aswerclin
'swal k s with selon more stoflld noncr id, mcis heis,
A?
TIOink
bupt venonn, d Ce?tey
Ke thiston tiund thorn fethe sutan kiportanou wuth thas tthers, steiellellke, on s hyou trefit.

Bwat dotive wother, foru;

Anke; ineees ronon irun: heals, I it Heno; gedad n thouc e,on pind ttanof anontoay:

Isher!

 Ase, mesev minds

需要注意的是,生成的文本看似无意义,但这主要是由于计算资源和训练数据的限制,而非KV缓存技术本身的问题。尽管标准模型能生成更接近真实英语的单词,但KV缓存模型仍保留了基本的结构特征,只是输出质量有所降低。

最后,我们使用Hugging Face的预训练GPT-2模型进行了对比测试:

 fromtransformersimportAutoModelForCausalLM, AutoTokenizer
importtime

# 加载模型和分词器
model_name="gpt2"
model=AutoModelForCausalLM.from_pretrained(model_name)
tokenizer=AutoTokenizer.from_pretrained(model_name)

# 文本提示
prompt="Once upon a time"
input_ids=tokenizer(prompt, return_tensors="pt").input_ids

# 生成文本并测量时间的函数
defgenerate_text(use_cache):
    # 生成文本
    start_time=time.time()
    output_ids=model.generate(input_ids.to(device), use_cache=use_cache, max_new_tokens=1000)
    elapsed_time=time.time() -start_time

    # 解码输出
    output_text=tokenizer.decode(output_ids[0], skip_special_tokens=True)

    returnoutput_text, elapsed_time

# 不使用KV缓存生成
output_without_cache, time_without_cache=generate_text(use_cache=False)
print("Without KV Cache:")
print(output_without_cache)
print(f"Time taken: {time_without_cache:.2f} seconds\n")

# 使用KV缓存生成
output_with_cache, time_with_cache=generate_text(use_cache=True)
print("With KV Cache:")
print(output_with_cache)
 print(f"Time taken: {time_with_cache:.2f} seconds")

测试结果:

 Without KV Cache:
 Once upon a time, the world was a place of great beauty and great danger. The world was a place of great danger, and the world was a place of great danger. The world was a place of great danger, and the world was a place of great danger. The world was a place of great danger, and the world was a place of great danger.
 Time taken: 76.70 seconds

 With KV Cache:
 Once upon a time, the world was a place of great beauty and great danger. The world was a place of great danger, and the world was a place of great danger. The world was a place of great danger, and the world was a place of great danger. The world was a place of great danger, and the world was a place of great danger.
 Time taken: 62.46 seconds

总结

本文详细阐述了KV缓存的工作原理及其在大型语言模型推理优化中的应用,文章不仅从理论层面阐释了KV缓存的工作原理,还提供了完整的PyTorch实现代码,展示了缓存机制与Transformer自注意力模块的协同工作方式。实验结果表明,随着序列长度增加,KV缓存技术的优势愈发明显,在长文本生成场景中能将推理时间降低近60%。这一技术为优化大模型部署提供了一种无需牺牲精度的实用解决方案,为构建更高效的AI应用奠定了基础。

https://avoid.overfit.cn/post/3e49427b9e42440aa0c8d834c1906f2f

作者:Shubh Mishra

目录
相关文章
|
20天前
|
机器学习/深度学习 人工智能 算法
小米7B参数推理大模型首次开源!Xiaomi MiMo:数学代码双杀,超越32B巨头
小米开源的MiMo推理大模型通过联动预训练与强化学习算法,在7B参数规模下实现数学推理与代码生成能力的突破性提升,技术报告显示其性能超越部分32B级模型。
373 74
小米7B参数推理大模型首次开源!Xiaomi MiMo:数学代码双杀,超越32B巨头
|
20天前
|
机器学习/深度学习 人工智能 API
阿里通义开源新一代混合推理模型 Qwen3:创新双模式推理,支持"思考模式"和"非思考模式"
Qwen3是阿里巴巴推出的新一代大型语言模型,支持119种语言和两种推理模式,采用四阶段训练流程和Apache 2.0协议开源,提供从0.6B到235B的多种模型配置。
264 19
阿里通义开源新一代混合推理模型 Qwen3:创新双模式推理,支持"思考模式"和"非思考模式"
|
21天前
|
人工智能 自然语言处理 DataWorks
DataWorks Copilot 集成Qwen3-235B-A22B混合推理模型,数据开发与分析效率再升级!
阿里云DataWorks平台正式接入Qwen3模型,支持最大235B参数量。用户可通过DataWorks Copilot智能助手调用该模型,以自然语言交互实现代码生成、优化、解释及纠错等功能,大幅提升数据开发与分析效率。Qwen3作为最新一代大语言模型,具备混合专家(MoE)和稠密(Dense)架构,适应多种应用场景,并支持MCP协议优化复杂任务处理。目前,用户可通过DataWorks Data Studio新版本体验此功能。
155 20
DataWorks Copilot 集成Qwen3-235B-A22B混合推理模型,数据开发与分析效率再升级!
|
15天前
|
人工智能 并行计算 监控
在AMD GPU上部署AI大模型:从ROCm环境搭建到Ollama本地推理实战指南
本文详细介绍了在AMD硬件上构建大型语言模型(LLM)推理环境的全流程。以RX 7900XT为例,通过配置ROCm平台、部署Ollama及Open WebUI,实现高效本地化AI推理。尽管面临技术挑战,但凭借高性价比(如700欧元的RX 7900XT性能接近2200欧元的RTX 5090),AMD方案成为经济实用的选择。测试显示,不同规模模型的推理速度从9到74 tokens/秒不等,满足交互需求。随着ROCm不断完善,AMD生态将推动AI硬件多元化发展,为个人与小型组织提供低成本、低依赖的AI实践路径。
159 1
在AMD GPU上部署AI大模型:从ROCm环境搭建到Ollama本地推理实战指南
|
6天前
|
编解码 人工智能
通义万相Wan2.1-VACE开源!业内首个视频编辑统一模型!附推理教程
通义万相Wan2.1-VACE开源!业内首个视频编辑统一模型!附推理教程
173 7
|
25天前
|
机器学习/深度学习 人工智能 算法
RAGEN:RL训练LLM推理新范式!开源强化学习框架让Agent学会多轮决策
RAGEN是一个基于StarPO框架的开源强化学习系统,通过马尔可夫决策过程形式化Agent与环境的交互,支持PPO、GRPO等多种优化算法,显著提升多轮推理训练的稳定性。
134 5
RAGEN:RL训练LLM推理新范式!开源强化学习框架让Agent学会多轮决策
|
22天前
|
自然语言处理 测试技术 Serverless
Qwen3开源发布:Think Deeper, Act Faster!社区推理、部署、微调、MCP调用实战教程来啦!
Qwen3开源发布:Think Deeper, Act Faster!社区推理、部署、微调、MCP调用实战教程来啦!
406 22
|
18天前
|
缓存 并行计算 测试技术
阿里云PAI-全模态模型Qwen2.5-Omni-7B推理浅试
阿里云PAI-全模态模型Qwen2.5-Omni-7B推理浅试
152 11
|
24天前
|
测试技术 语音技术 索引
GenPRM:思维链+代码验证,通过生成式推理的过程奖励让大模型推理准确率显著提升
本文提出GenPRM,一种生成式过程奖励模型,通过显式Chain-of-Thought推理与代码验证提升大型语言模型性能。针对传统PRMs的局限,GenPRM结合相对进展估计和监督微调,优化推理评估精度。实验表明,GenPRM在ProcessBench及数学任务中显著优于现有方法,且可通过测试时扩展进一步增强性能。然而,该方法在计算开销和跨领域应用上仍存在局限性。
44 0
GenPRM:思维链+代码验证,通过生成式推理的过程奖励让大模型推理准确率显著提升
|
11天前
|
机器学习/深度学习 PyTorch API
PyTorch量化感知训练技术:模型压缩与高精度边缘部署实践
本文深入探讨神经网络模型量化技术,重点讲解训练后量化(PTQ)与量化感知训练(QAT)两种主流方法。PTQ通过校准数据集确定量化参数,快速实现模型压缩,但精度损失较大;QAT在训练中引入伪量化操作,使模型适应低精度环境,显著提升量化后性能。文章结合PyTorch实现细节,介绍Eager模式、FX图模式及PyTorch 2导出量化等工具,并分享大语言模型Int4/Int8混合精度实践。最后总结量化最佳策略,包括逐通道量化、混合精度设置及目标硬件适配,助力高效部署深度学习模型。
97 21
PyTorch量化感知训练技术:模型压缩与高精度边缘部署实践