Skip to content

Commit 0191e88

Browse files
committed
2025-12-15-svdquant.md refine
1 parent 7164fca commit 0191e88

1 file changed

Lines changed: 122 additions & 3 deletions

File tree

_posts/2025-12-15-svdquant.md

Lines changed: 122 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,19 @@ categories: ["quantization", "SVDQuant"]
55
tags: [diffusion-models, reading-notes, generative-models]
66
math: true
77
---
8-
8+
特别感谢 DeepSeek 和 claude code,没有你们很多东西我都会因为不想手打而放弃。
99

1010
我认真读SVDQuant的代码应该也最少有两次了,想着应该留下一些痕迹。第一次读的时候我还不太搞得懂 `forward` 是怎么一回事,现在再读已经是为了给推理引擎的`feature`做准备了。从还在读大四的实习生大模型小白到开始掌握 ai infra入门的正式员工,都时不时接触这个项目,还挺感慨的。
1111

12+
这里写下的内容是我在接触量化过程中的迷惑,没有很严格的数学推理和很多的公式。
13+
1214
## 前置: 什么是量化?
1315
令人惊讶的是网上居然很少有这方面科普类的文章,比如一文带你读懂什么是模型量化,甚至连 ai 生成的水文也比较少(可能是我搜索的关键词不太对)。总之 Quantization 是一门广泛使用且基础的模型轻量化技术。我不打算写的很详细。
1416

15-
详细的可以看看["Working with Quantized Types"](https://docs.nvidia.com/deeplearning/tensorrt/latest/inference-library/work-quantized-types.html#)
17+
详细的可以看看
18+
["Working with Quantized Types"](https://docs.nvidia.com/deeplearning/tensorrt/latest/inference-library/work-quantized-types.html#)
19+
20+
[目前针对大模型进行量化的方法有哪些? - 吃果冻不吐果冻皮的回答 - 知乎](https://www.zhihu.com/question/627484732/answer/3261671478)
1621

1722
### RTN量化
1823
我觉得最能体现量化核心思想的是RTN量化(Round-To-Nearest):
@@ -49,7 +54,7 @@ $$
4954

5055
对 X 来说,如果我们选择只对权重 W 量化,就是所谓的`weight-only quantization`,如果我们对激活A 和权重 W 都量化,就是所谓的`weight-activation quantization`。例如我们只将权重量化为 4bit,我们就把它叫做`W4A16`,如果权重和激活都量化成 4bit,就叫做`W4A4`
5156

52-
对于步骤 1 来说,我们如何选择最值,是选择整个 tensor(per tensor)的最值,还是选择矩阵中每一行的最值(per channel),就是所谓的**量化粒度**,从直觉上来说粒度当然是越细越好。
57+
对于步骤 1 来说,我们如何选择最值,是选择整个 tensor(per-tensor)的最值,还是选择矩阵中每一行的最值(per-channel),就是所谓的**量化粒度**,从直觉上来说粒度当然是越细越好。除了 per-tensor 和 per-channel 的粒度选择外,还有一种折中方案是 **分组量化(per-group quantization)**。在分组量化中,我们将 tensor 分成多个小组,在每个小组内部独立计算量化的缩放因子(scale)和零点(zero_point)。这个分组的大小由 `group size` 参数控制。例如,对于一个有 4096 个通道的权重矩阵,我们可以选择每 128 个通道为一组(group_size=128),这样既比 per-tensor(粒度最粗)更精细,又比 per-channel(粒度最细)计算开销更小。分组量化在精度和效率之间提供了一个灵活的可调节点。我在学习过程中所见使用最多的应该就是per-group,group_size往往能够被作为参数传入,所以量化后衡量模型好坏再反过来调整量化参数也很重要,当然 group_size在某些情况下不能为某些值,以后遇到报错会知道的
5358

5459
对于步骤 2 来说,我们选择的量化精度实际上是需要根据推理运行的硬件来选择的,例如英伟达 4090 显卡支持 fp8 计算,而 3090 `Tensor Core` 不支持fp8, 推理速度可能和 bf16 差不多。
5560

@@ -87,3 +92,117 @@ $$
8792

8893
因此 scale 越大,Err 越大。当量化粒度较小时,受到 `outlier` 影响的权重越少,量化精度相对粗粒度量化更高。
8994

95+
#### 为什么量化粒度不是越小越好
96+
最开始我以为这个问题是站在memory 角度解决的,毕竟如果是 per weight量化,实际上得到的 X_q( low bits) + scale (hight bits) > X (high bits)。但仔细一想根本没有 per weight 量化,即使是对于粒度最细的per-channel 来说,额外的 scale 和 zero_point开销也是很小的,这一点说不通。
97+
98+
直觉上我感觉这和CUDA 等硬件优化有关,例如`grouped_gemm`,如果group_size太小,会影响到批处理 gemm 的效率。但我没有具体地查证与验证过,再此先记录一下吧。
99+
100+
### 如何决定量化精度?
101+
1. 硬件是否支持对应精度,例如 Tensor Core
102+
2. 精度损失容忍度
103+
3. 内存限制:假如我想用 5090 跑 qwen image(20B),至少得把它量化到 8bit 才能在 32g的显存内塞下它。
104+
4. 量化算法:有的`W4A4`算法比`FP8`精度还好,有现成的优秀算法当然是越低 bit 越好。
105+
106+
### 如何去量化激活?
107+
我觉得讲解这个问题前,应该先知道激活是什么。我想读到这里的人应该至少对模型推理的过程至少有一个模糊的认识了。
108+
109+
以FLUX 为例,它的模型结构如下:
110+
<details>
111+
<summary>点击展开/收起FLUX模型架构</summary>
112+
<pre><code class="language-python">
113+
Flux(
114+
(pe_embedder): EmbedND()
115+
(img_in): Linear(in_features=64, out_features=3072, bias=True)
116+
(time_in): MLPEmbedder(
117+
(in_layer): Linear(in_features=256, out_features=3072, bias=True)
118+
(silu): SiLU()
119+
(out_layer): Linear(in_features=3072, out_features=3072, bias=True)
120+
)
121+
(vector_in): MLPEmbedder(
122+
(in_layer): Linear(in_features=768, out_features=3072, bias=True)
123+
(silu): SiLU()
124+
(out_layer): Linear(in_features=3072, out_features=3072, bias=True)
125+
)
126+
(guidance_in): MLPEmbedder(
127+
(in_layer): Linear(in_features=256, out_features=3072, bias=True)
128+
(silu): SiLU()
129+
(out_layer): Linear(in_features=3072, out_features=3072, bias=True)
130+
)
131+
(txt_in): Linear(in_features=4096, out_features=3072, bias=True)
132+
(double_blocks): ModuleList(
133+
(0-18): 19 x DoubleStreamBlock(
134+
(img_mod): Modulation(
135+
(lin): Linear(in_features=3072, out_features=18432, bias=True)
136+
)
137+
(img_norm1): LayerNorm((3072,), eps=1e-06, elementwise_affine=False)
138+
(img_attn): SelfAttention(
139+
(qkv): Linear(in_features=3072, out_features=9216, bias=True)
140+
(norm): QKNorm(
141+
(query_norm): RMSNorm()
142+
(key_norm): RMSNorm()
143+
)
144+
(proj): Linear(in_features=3072, out_features=3072, bias=True)
145+
)
146+
(img_norm2): LayerNorm((3072,), eps=1e-06, elementwise_affine=False)
147+
(img_mlp): Sequential(
148+
(0): Linear(in_features=3072, out_features=12288, bias=True)
149+
(1): GELU(approximate='tanh')
150+
(2): Linear(in_features=12288, out_features=3072, bias=True)
151+
)
152+
(txt_mod): Modulation(
153+
(lin): Linear(in_features=3072, out_features=18432, bias=True)
154+
)
155+
(txt_norm1): LayerNorm((3072,), eps=1e-06, elementwise_affine=False)
156+
(txt_attn): SelfAttention(
157+
(qkv): Linear(in_features=3072, out_features=9216, bias=True)
158+
(norm): QKNorm(
159+
(query_norm): RMSNorm()
160+
(key_norm): RMSNorm()
161+
)
162+
(proj): Linear(in_features=3072, out_features=3072, bias=True)
163+
)
164+
(txt_norm2): LayerNorm((3072,), eps=1e-06, elementwise_affine=False)
165+
(txt_mlp): Sequential(
166+
(0): Linear(in_features=3072, out_features=12288, bias=True)
167+
(1): GELU(approximate='tanh')
168+
(2): Linear(in_features=12288, out_features=3072, bias=True)
169+
)
170+
)
171+
)
172+
(single_blocks): ModuleList(
173+
(0-37): 38 x SingleStreamBlock(
174+
(linear1): Linear(in_features=3072, out_features=21504, bias=True)
175+
(linear2): Linear(in_features=15360, out_features=3072, bias=True)
176+
(norm): QKNorm(
177+
(query_norm): RMSNorm()
178+
(key_norm): RMSNorm()
179+
)
180+
(pre_norm): LayerNorm((3072,), eps=1e-06, elementwise_affine=False)
181+
(mlp_act): GELU(approximate='tanh')
182+
(modulation): Modulation(
183+
(lin): Linear(in_features=3072, out_features=9216, bias=True)
184+
)
185+
)
186+
)
187+
(final_layer): LastLayer(
188+
(norm_final): LayerNorm((3072,), eps=1e-06, elementwise_affine=False)
189+
(linear): Linear(in_features=3072, out_features=64, bias=True)
190+
(adaLN_modulation): Sequential(
191+
(0): SiLU()
192+
(1): Linear(in_features=3072, out_features=6144, bias=True)
193+
)
194+
)
195+
)
196+
</code></pre>
197+
</details>
198+
199+
200+
## 前置:Smoothquant
201+
关联阅读:["From SmoothQuant to SVDQuant"](https://declk.github.io/blog/LLMs/From%20SmoothQuant%20to%20SVDQuant.html)
202+
203+
这一篇博客和我不同,他主要是论文的概述,推论的很清晰且不细节。
204+
205+
206+
## SVDquant
207+
svdquant 是在 smoothquant 后续的工作。顺带一提 smoothquant 后续有非常多有名的量化工作,例如 AWQ。
208+
在svdquant作者的实现

0 commit comments

Comments
 (0)