<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>LLM Inference on 厨师海森堡的厨房</title>
    <link>https://kevinchen1994.github.io/tags/llm-inference/</link>
    <description>Recent content in LLM Inference on 厨师海森堡的厨房</description>
    <image>
      <title>厨师海森堡的厨房</title>
      <url>https://kevinchen1994.github.io/images/profile.jpg</url>
      <link>https://kevinchen1994.github.io/images/profile.jpg</link>
    </image>
    <generator>Hugo -- 0.161.1</generator>
    <language>zh-cn</language>
    <lastBuildDate>Wed, 12 Jun 2024 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://kevinchen1994.github.io/tags/llm-inference/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>KVCache feat. 单向注意力VS双向注意力</title>
      <link>https://kevinchen1994.github.io/posts/kvcache-feat.-%E5%8D%95%E5%90%91%E6%B3%A8%E6%84%8F%E5%8A%9Bvs%E5%8F%8C%E5%90%91%E6%B3%A8%E6%84%8F%E5%8A%9B/</link>
      <pubDate>Wed, 12 Jun 2024 00:00:00 +0000</pubDate>
      <guid>https://kevinchen1994.github.io/posts/kvcache-feat.-%E5%8D%95%E5%90%91%E6%B3%A8%E6%84%8F%E5%8A%9Bvs%E5%8F%8C%E5%90%91%E6%B3%A8%E6%84%8F%E5%8A%9B/</guid>
      <description>&lt;h2 id=&#34;前言&#34;&gt;前言&lt;/h2&gt;
&lt;p&gt;在使用大模型进行推理时我们都会觉得模型的推理速度比较慢，当前也有很多推理加速的方法，像是模型量化、FlashAttention、使用更好的GPU、KVCache等，今天我们就来聊聊KVCache是怎么回事，我会从transformer decoder的推理过程开始，到为什么KVCache可以加速模型推理，以及具体的效果进行介绍。&lt;/p&gt;
&lt;h2 id=&#34;transformer的self-attention机制&#34;&gt;Transformer的self-attention机制&lt;/h2&gt;
&lt;p&gt;目前的大模型都是基于Transformer的decoder架构进行设计的，所以先把著名的self-attention公式抛出来。&lt;/p&gt;
&lt;p&gt;由于大模型使用的是decoder，所以在计算时会mask掉当前token的后边的token，例如当前为token_n，mask矩阵会mask掉n之后的所有token值，目的是防止解码过程中看到答案。&lt;/p&gt;
&lt;p&gt;下图是GPT2的解码过程，给定输入，模型预测下一个token，然后在下一步中使用上一步预测的token作为输入再次进行预测。图来源：&lt;a href=&#34;https://jalammar.github.io/illustrated-gpt2/&#34;&gt;https://jalammar.github.io/illustrated-gpt2/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;https://kevinchen1994.github.io/images/notion_4057612f-0c72-4a90-a7ee-7b28e0d726dc56e50bbd-2997-4f89-bfeb-a27377d1c666.gif&#34;&gt;&lt;/p&gt;
&lt;p&gt;我们来逐步计算一下解码的过程。&lt;/p&gt;
&lt;p&gt;因为decoder中的self-attention是masked self-attention，所以在计算时需要注意进行mask。&lt;/p&gt;
&lt;p&gt;我们将q和k的计算整理成矩阵，可以得到以下公式。&lt;/p&gt;
&lt;p&gt;展开的话就是这样的一个矩阵。&lt;/p&gt;
&lt;p&gt;对于token_1，Attention_1(Q,K,V)=softmax(\frac{Q_1K^T_1}{\sqrt{d_k}})\vec{V_1}&lt;/p&gt;
&lt;p&gt;对于token_2，Attention_2(Q,K,V)=softmax(\frac{Q_2K^T_1}{\sqrt{d_k}})\vec{V_1}+softmax(\frac{Q_2K^T_2}{\sqrt{d_k}})\vec{V_2}&lt;/p&gt;
&lt;p&gt;对于token_3，Attention_3(Q,K,V)=softmax(\frac{Q_3K^T_1}{\sqrt{d_k}})\vec{V_1}+softmax(\frac{Q_3K^T_2}{\sqrt{d_k}})\vec{V_2}+softmax(\frac{Q_3K^T_3}{\sqrt{d_k}})\vec{V_3}&lt;/p&gt;
&lt;p&gt;可以看到在token_2推理的过程中，K_1、V_1是重复使用的；可以看到在token_3推理的过程中，K_1、V_1、K_2、V_2是重复使用的。&lt;/p&gt;
&lt;p&gt;还可以看出来，每次计算attention只需要使用当前的Q即可，并不需要之前的向量。&lt;/p&gt;
&lt;p&gt;所以我们就可以把之前计算过的K、V缓存起来，这就是今天我们要介绍的KVCache。&lt;/p&gt;
&lt;h2 id=&#34;kvcache的作用&#34;&gt;KVCache的作用&lt;/h2&gt;
&lt;p&gt;现在我们就可以给KVCache进行定义了，在decoder-only架构中，通过缓存解码过程中的K、V，来避免重复计算，从而达到推理加速的效果。&lt;/p&gt;
&lt;p&gt;下图清晰了对比了使用KVCache和不使用KVCache的区别，图来源&lt;a href=&#34;https://medium.com/@joaolages/kv-caching-explained-276520203249&#34;&gt;https://medium.com/@joaolages/kv-caching-explained-276520203249&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;https://kevinchen1994.github.io/images/notion_2772e1c2-6780-4c33-a6aa-75a1b091219d9109d308-9ff2-4e32-8ffd-f1fd908c98a1.gif&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;实现细节&#34;&gt;实现细节&lt;/h2&gt;
&lt;p&gt;在huggingface的transformers中，通过modelling_gpt2.py可以看到具体的实现细节。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        query &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_split_heads(query, self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;num_heads, self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;head_dim)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        key &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_split_heads(key, self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;num_heads, self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;head_dim)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        value &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_split_heads(value, self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;num_heads, self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;head_dim)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;				&lt;span style=&#34;color:#75715e&#34;&gt;# 通过layer_past来传递上一次计算的key value&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; layer_past &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            past_key, past_value &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; layer_past
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#75715e&#34;&gt;# 通过拼接来得到最新的key 和 value&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            key &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; torch&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;cat((past_key, key), dim&lt;span style=&#34;color:#f92672&#34;&gt;=-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            value &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; torch&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;cat((past_value, value), dim&lt;span style=&#34;color:#f92672&#34;&gt;=-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; use_cache &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            present &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (key, value)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            present &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;在transformers中使用kvcache对比&#34;&gt;在transformers中使用KVCache对比&lt;/h2&gt;
&lt;p&gt;在Transformers中进行对比开启KVCache后的效果。&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
