突破性技术: 大语言模型LLM量化激活outliers异常值抑制

LLM过去有两种突破性技术大大提升了量化精度,分别是group-wise量化和GPTQ/AWQ量化。前者相比于过去的per-tensor和per-channel/per-axis量化提出了更细粒度的对channel拆分为更小单元的量化方式,后者通过巧妙的算法明显提升了4bit量化的精度。

LLM量化存在一个常识性的挑战是激活部分channel里面存在显著大于其他channel的异常值,这显著提升了激活量化的难度,并且对权重量化精度损失也有很大影响。一种以SliceGpt, QuaRot等为代表的新的方法可能改善这个局面,在近期成为一个研究热点。

LLM激活量化outliers

LLM量化一个常识性的挑战是激活部分channel里面存在显著大于其他channel的异常值。

outlier存在于矩阵乘内积的K维度中。虽然理论上可以通过对不同channel采用不同的量化系数来解决这个问题, 但是模型量化一般对整个K是采用一个统一的量化系数,因为计算实现比较简单,而如果对K维度的每个元素用不同量化系数,那么矩阵乘时要进行非常频繁的反量化使得整个K维度所有元素共用同一套量化系数,结果才能累加在一起,因此对性能的损失肯定是异常显著的。也就是无法对单独的outlier channel跟其他channel使用不同量化系数来解决这个问题。

激活量化常用的的per-tensor或者per-token量化都无法解决这个问题。激活也采用权重的group量化可以部分解决这个问题,但是除CPU/GPU以外的其他硬件并不能很好地支持该量化方式。

过往的部分解决方案

smooth quant通过scale向量,激活除以scale,权重乘以scale从而降低激活outlier幅度,把激活难度迁移到权重。

awq的思路跟smooth quant基本一致,都是韩松团队提出的。也是基于scale向量,激活除以scale,权重乘以scale。权重乘以scale进行放大后,可以降低放大部分的元素量化精度损失。

atom量化,对outlier通道部分的元素通过reorder后,与其他非outlier部分区别对待,outlier部分通道采用高精度计算。

IntactKV: Improving Large Language Model Quantization by Keeping Pivot Tokens Intact,提出了一种非常简单的方法来保护prompt开头部分的公共前缀的kv cache,从而避免特殊token引入的outlier精度损失。

基于Rotation的激活outliers抑制方法原理

SliceGPT首先提出了一种计算不变性(computational invariance),然后被QuaRot和QServe用于抑制激活outliers。相关论文:

SliceGPT: Compress Large Language Models by Deleting Rows and Columns

QuaRot: Outlier-Free 4-Bit Inference in Rotated LLMs

QServe: W4A8KV4 Quantization and System Co-design for Efficient LLM Serving

QuIP: 2-Bit Quantization of Large Language Models With Guarantees

QuIP#: Even Better LLM Quantization with Hadamard Incoherence and Lattice Codebooks

computational invariance

An invariant function is one for which a transformation to the input does not result in a change to the output.

SliceGPT提出了一种Q变换,也就是对LLM的矩阵乘的权重W(部分情况包括bias也要进行变换),一部分变为W*Q,一部分变为QT*W,但是transformer计算结果不变。

Q和其转置QT具有特殊性质:Let Q denote an orthogonal matrix

另外一个关键是RMSNorm具有一个特殊的性质,对输入x进行Q变换得到x*Q,那么输出y变为y*Q,保持了Q变换的传递。

具体原理介绍

1, transformer网络的计算流程:

2, 施加Q变换流程:

3, 具体流程详解:

1. Embedding矩阵W乘以Q矩阵进行Q变换,使得经过embedding的激活由x变成x*Q。
进入transformer block后首先经过rms_norm,这个方法对结果保持了Q变换,也就是原来的输入x输出y,现在是输入x*Q输出y*Q。

2. 在进入attention之前,对Q,K,V的三个矩阵乘的参数变为QT*W,这样Q,K,V矩阵乘的输出原来是X*W,现在是(X*Q)*(QT*W) = X*(Q*QT)*W = X*W,也就是抵消了Q变换,不影响后续attention的计算。因为这个矩阵乘输出抵消了Q变换,所以bias保持不变。

3. attention输出后经过o_proj矩阵乘,o_proj的权重由W乘以Q矩阵进行Q变换,使得的激活由x1变成x1*Q。注意o_proj如果具有bias,也需要对bias变为bias*Q,才能正常对o_proj的输入进行完整的Q变换。

4. o_proj后是残差连接x+x1,因为残差另一个输入不像Q,K,V那样对Q进行了抵消,所以残差相加的激活x0和x1都经过Q变换,残差输出保持Q变换。

5. 然后到了FFN,首先是up_proj和gate_proj,这两个权重由W变为QT*W,跟上面Q,K,V矩阵乘一样,激活与变换后的权重相乘后抵消了Q变换。

6. 然后FFN的up_proj和gate_proj的结果经过激活函数到down_proj,down_proj的权重由W乘以Q,使得其输出进行Q变换。Bias跟o_proj一样需要进行变换。

7. FFN结果经过残差连接后进入下一个transformer block,循环进行上述2-6过程。

8. 最终lm_head的输入为x*Q,因此对lm_head的W变为QT*W,使得lm_head结果保持不变。

变换矩阵Q如何选择

理论上任何正交矩阵Q(满足Q*QT=I)都可以使用。

如果对整个模型所有transformer层应用一个统一的Q变换矩阵,那么,这两个变换可以完全融入矩阵乘的参数,跟AWQ一样,不引入任何额外的模型结构变化和计算量。

SliceGPT的方法

SliceGPT的目的主要是为了权重的剪枝,而不是激活outlier的抑制。因此其针对剪枝的需要并结合激活校准数据为每一层选择了特定的Q矩阵。

SliceGPT对每一层应用不同的Q变换矩阵,虽然可以通过Q和QT抵消一部分变换导致不引入额外的计算,但是残差连接处的两个输入激活具有不同的变换,这时就需要对其中一个输入变换到跟另一个输入一样的变换,两个结果才能相加。因此SliceGPT需要对残差的长程连接插入额外的矩阵乘计算:

QuaRot的方法

we apply randomized Hadamard transformations to the weight matrices without changing the model. 特别之处在于采用了一个随机化的Hadamard矩阵。

We also apply online Hadamard transformations to the attention module to remove outlier features in keys and values, enabling the KV cache to be quantized.

如果采用非随机化的Hadamard矩阵,或者所有曾使用一个固定的随机性的Hadamard矩阵,所有transformer层共用一个变换矩阵,不需要插入额外的矩阵乘从而引入任何额外计算量和模型结构变化。但如果每一层用不同的Hadamard矩阵,则会导致额外矩阵乘的插入实现Hadamard变换。

Hadamard matrix介绍

https://en.wikipedia.org/wiki/Hadamard_matrix

https://mathworld.wolfram.com/HadamardMatrix.html

据猜测,对于所有能被 4 整除的数,Hn都存在。

QServe的选择

跟QuaRot类似但没有采用随机化的Hadamard矩阵:We simply choose the scaled Hadamard matrix as the rotation matrix. since rotation is a unitary transformation, the rotation matrix Q can be absorbed by the weights of the output module in the previous block.

看上去对整个模型采用统一的Q变换矩阵(scaled Hadamard matrix)因此无需改变模型结构和引入任何额外计算量。

因为QServe并没有像QuaRot那样采用激进的4bit激活量化,而是8bit激活量化,可能这样就足够而无需采用更复杂的方法,或者说虽然这样次优但是不会引入任何模型结构变化从而有利于部署。当然整个模型采用一个固定的scaled Hadamard或者随机的Hadamard矩阵哪个更好需要进一步评测。

实践

如何得到变换的Q矩阵

QuaRot开源的代码库中fake_quant/hadamard_utils.py或者quarot/functional/hadamard.py中定义了random_hadamard_matrix可以获取随机化和scale后的hadamard矩阵,使得其行列式为1或者-1,满足正交性:

https://github.com/spcl/QuaRot

import numpy as np
from hadamard_utils import random_hadamard_matrixN = 4096had_mat = random_hadamard_matrix(N, 'cpu')
had_mat_np = had_mat.numpy()# result is I
mat_result = np.matmul(had_mat_np, np.transpose(had_mat_np))
# result is 1 or -1
det = np.linalg.det(had_mat_np)

RMSNorm/LayerNorm处理

LayerNorm不满足Q变换的传递性,需要转换为LayerNorm,QuaRot提出了一种把LayerNorm转换为RMSNorm的方法。另外只有标准的RMSNorm即x/||x||才满足Q变换传递性,但是实际llama2, baichuan, qwen等模型用的RMSNorm还具有一个scale向量。需要融合到后面矩阵乘的参数里面才能实现计算等价。

有QuaRot还需不需要GPTQ/AWQ?

有待评测

总的来说,如果只是想简单的抑制激活的outliers从而提升激活量化精度,当然对权重量化应该也大有益处,QServe的方法应该是个不错的尝试起点。

本人使用Qwen1.5 5b chat验证,通过打印出矩阵乘激活通道间的绝对平均值,发现确实能够显著抑制激活的outlier:

如果本文对您有所帮助,欢迎赞助(^_−)☆

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/22099.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LeetCode25_K个一组翻转链表

. - 力扣(LeetCode) 一、题目描述 二、过程模拟 1. 第一步 2. 第二步:子链表分组 3. 第三步:断开前后两组 4. 第四步:翻转start到end的部分 5. 第五步:连接翻转好的前半部分和未翻转的后半部分&#xff…

怎么把照片转成jpg

将照片转换成JPG格式是一个相对简单的过程,适用于大多数设备和操作系统。以下是一些常见的方法: 一、在Windows系统中转换照片格式 使用画图工具 打开你想要转换的照片,右击选择“打开方式”里面的“画图”工具。在画图工具的界面上&#xf…

《世界很喧嚣,做自己就好》有感

仅仅只用了两个中午一个晚上就看完了《世界很喧嚣,做自己就好》,不能说看完,应该说浏览完。决定要看这本书,仅仅只是因为它的书名。前几章还认真看,后面越来越快,再后来一目十行,只重点关注黑色…

找回以前的视频:技术与实践3个指南

你们有没有发现现在视频已经成为我们生活中不可或缺的一部分了?不管是在工作场合做演示、在学习时看教学视频,还是在休闲娱乐时追剧看电影,视频都扮演着超级重要的角色。 然而误删或手机故障的发生很可能将以前的视频清除。本文将深入探讨手…

「实战应用」如何用图表控件LightningChart JS创建SQL仪表板应用(一)

LightningChart JS是Web上性能特高的图表库,具有出色的执行性能 - 使用高数据速率同时监控数十个数据源。 GPU加速和WebGL渲染确保您的设备的图形处理器得到有效利用,从而实现高刷新率和流畅的动画,常用于贸易,工程,航…

【递归、搜索与回溯】递归、搜索与回溯准备+递归主题

递归、搜索与回溯准备递归主题 1.递归2.搜索3.回溯与剪枝4.汉诺塔问题5.合并两个有序链表6.反转链表7.两两交换链表中的节点8.Pow(x, n)-快速幂(medium) 点赞👍👍收藏🌟🌟关注💖💖 你…

Docker基础篇之本地镜像发布到阿里云

文章目录 1. 本地镜像发布到阿里云的流程2. 阿里云开发平台3. 将自己的本地镜像推送到阿里云 1. 本地镜像发布到阿里云的流程 阿里云ECS Docker生态如下图所示: 2. 阿里云开发平台 在控制台找到容器和镜像服务: 然后创建一个个人实例: 下面…

深圳垣象科技golang期望19K一面挂

垣象科技是一个初创公司,我们看好制造业数字化这个大方向,希望搭建一个云平台,通过创新的产品和服务,提高电子及高科技行业产品创新的能力和研发的效率 一面(挂) 总体来说项目答得不好,h5 拖拽…

适用于 Windows 10 的 5 个最佳数据恢复工具

很多时候,由于存储设备故障或意外删除数据,有时您需要在Windows 10操作系统中恢复数据。大多数人不熟悉在Windows 10下恢复数据的过程。由于这个原因,他们经常认为这是不可能的。事实是,有很多工具可用于Windows 10数据恢复。今天…

Clickhouse 的分布式架构说明——Clickhouse 架构篇(五)

文章目录 前言架构特点及对比ClickHouse分布式架构的优点ClickHouse分布式架构的缺点ClickHouse与Hadoop的分布式架构对比 基本概念集群副本分片 ClickHouse的复制表引擎创建复制表复制表复制复制表查询 ClickHouse分布式表引擎分布式表查询原理分布式表的数据写入方案分布式表…

Plotting World Map in Python

1. 方法一 pygal Plotting World Map Using Pygal in Python import pygal # create a world map worldmap pygal.maps.world.SupranationalWorld() # set the title of map worldmap.title Continents# adding the continents worldmap.add(Africa, [(africa)]) worl…

PyTorch深度学习实战(44)——基于 DETR 实现目标检测

PyTorch深度学习实战(44)——基于 DETR 实现目标检测 0. 前言1. Transformer1.1 Transformer 基础1.2 Transformer 架构 2. DETR2.1 DETR 架构2.2 实现 DETR 模型 3. 基于 DETR 实现目标检测3.1 数据加载与模型构建3.2 模型训练与测试 小结系列链接 0. 前…

自动驾驶---Control之LQR控制

1 前言 在前面的系列博客文章中为读者阐述了很多规划相关的知识(可参考下面专栏),本篇博客带领读者朋友们了解控制相关的知识,后续仍会撰写规控相关文档。 在控制理论的发展过程中,人们逐渐认识到对于线性动态系统的控…

视频封面:如何用前端实现视频帧截图

在这样一个图像化极其重要的时代,从视频中提取精彩瞬间,即视频帧截图的技术,已成为前端开发中的一个亮点。JavaScript作为网页动态效果和交互的主力军,其在视频处理领域能力逐渐被挖掘和重视,尤其是视频帧截图技术的应…

一键实现文件重命名:巧妙运用随机大写字母命名,复制新文件名,轻松管理文件库!

我们的电脑里总是堆积着各种各样的文件。无论是工作文档、生活照片还是学习资料,这些文件都承载着我们的回忆和辛勤努力。然而,随着时间的推移,文件名的混乱和重复逐渐成为我们管理文件的难题。为了解决这一困扰,我们推出了一款创…

09.爬虫---正则解析爬取数据

09.正则解析爬取数据 1.目标网站2.具体实现3.正则表达式分析4.完整代码并存入表格 1.目标网站 直达目标网站 https://movie.douban.com/chart 2.具体实现 我们来拿取一下上面网页的代码如下: from urllib import requesturl https://movie.douban.com/chart headers {Us…

解决 DBeaver 查询时不刷新数据,需要重新连接才会刷新,有缓存一样

DBeaver 查询时总是第一次有数据,再次执行查询数据不会刷新,像是有缓存一样,需要重新连接再查询才会刷新,知道肯定是哪里设置的不对,但是一直没找到,实在是重连太烦了,多次尝试终于找到了设置。…

【数据结构】从前序与中序遍历,或中序与后序遍历序列,构造二叉树

欢迎浏览高耳机的博客 希望我们彼此都有更好的收获 感谢三连支持! 首先,根据先序遍历可以确定根节点E,再在中序遍历中通过E确定左树和右数 ; 设立inBegin和inEnd,通过这两个参数的游走,来进行子树的创建&a…

ApiJson简单使用

前言 最近在正式迭代中插入了一个大屏演示项目,因为后端开发人员任务都安排满了,而演示项目逻辑比较简单,大多是直接查表就能搞定,所以只能想办法让前端直接和数据库交互,增加开发速度。在找工具时发现了ApiJson。尝试…

详解redis单线程设计思路

写在文章开头 我们都知道redis是一个基于单线程实现高效网络IO和键值对读写操作的内存数据库,本文将从源码的角度剖析一下redis高效的单线程设计。 Hi,我是 sharkChili ,是个不断在硬核技术上作死的 java coder ,是 CSDN的博客专家 ,也是开源项目 Java Guide 的维护者之一…