【6s965-fall2022】量化 Quantization Ⅰ

news2024/11/18 12:36:24

模型的大小不仅和参数量的多少有关,而且也和位宽有关。
M o d e l S i z e = # P a r a m e t e r × B i t W i d t h . ModelSize = \#Parameter × BitWidth. ModelSize=#Parameter×BitWidth.
低位宽的运算操作容易实现、运算速度快、功耗低。

在这里插入图片描述

什么是量化?

从广义上讲,量化是将连续信号变成离散信号的过程;它在信号处理(以离散的时间间隔采样)和图像压缩(减少每个像素的可能颜色空间)中很常见。

在这里插入图片描述

在这门课中,我们将量化定义为将输入从一个连续的、大范围的数值集约束为一个离散的、小范围的数值集的过程。量化的过程包括将每个权重的数据类型改为限制性更强的数据类型(即可以用更少的比特表示)。常用的量化方法有:基于K-Means的权重量化(K-Means Based Weight Quantization)和线性量化(Linear Quantization)。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

数据类型

  • 整数(Integers)
    • 原码(Signed-Magnitude)
    • 反码(Ones’ Complement)
    • 补码(Two’s Complement)
  • 定点数(Fixed Point Numbers)
  • 浮点数(Floating Point Numbers)
    • ( − 1 ) sign × ( 1. mantissa ) + 2 exponent − exponent bias (-1)^{\text{sign}} \times (1.\text{mantissa}) + 2^{\text{exponent} - \text{exponent bias}} (1)sign×(1.mantissa)+2exponentexponent bias
ConventionSign BitsExponent BitsMantissa Bits
IEEE 7541823
IEEE Half-Precision 16-bit float1510
Brain Float (BF16)187
NVIDIA TensorFloat 321810
AMD 24-bit Float (AMD FP24)1716

Brain Float (BF16) 是专门为神经网络而设计的数据类型,对比于IEEE Half-Precision 16-bit float ,更加看重范围,精度不如范围重要。

基于K-Means的权重量化

K-Means-based Weight Quantization [Han et al., ICLR 2016]

正如Brain Float (BF16) 所考虑的那样,神经网络的性能实际上并不太依赖于权重的精度。一般来说,像2.09、2.12、1.92和1.87这样的值都可以被近似为2,那么我们为什么不把权重近似成这样呢?

方法:将权重聚类为 n n n个类别(使用K-Means聚类算法),其中 n n n通常为 2 2 2的某个幂。然后,用每个类别中所有权重的平均值来近似该类别对应的权重。将类别的ID与值的存储在一个编码表(codebook)中,生成一个索引矩阵。

在这里插入图片描述
在存储压缩率方面,设 N 0 N_0 N0为每个权重的原始比特数, n n n为用于聚类的个数( n = 2 N 1 n=2^{N_1} n=2N1), M M M为权重的数量。那么,压缩率约为 M ∗ log ⁡ 2 n + N 0 ∗ n M ∗ N 0 \frac{M*\log_2n + N_0*n}{M*N_0} MN0Mlog2n+N0n。一般来说,随着模型规模的增大(即 M → ∞ M\rightarrow\infty M),压缩率接近 l o g 2 n N 0 \frac{log_2n}{N_0} N0log2n

在推理过程中,要根据编码表(codebook)将索引矩阵中的类别id替换成它们所代表的权重,才可进行正常的浮点运算。

在这里插入图片描述
如果你想对模型进行微调,那么你只需执行梯度下降,参数为编码表条目。一般来说,将每个类别中每个权重的梯度聚合(sum、mean),就可以得到整个类别的梯度,进而可以更新编码表条目。
在这里插入图片描述

另一个需要注意的是,如果你想同时对一个模型进行修剪和量化,一般的做法是先修剪,后量化

挑战极限

超参数选择:唯一需要选择的超参数是编码簿(codebook)条目的数量,一般来说,标准做法是使用16个;AlexNet的实验表明,卷积层需要4位,全连接层需要2位,才会出现明显的准确性下降。奇怪的是,这些阈值与模型是否先被修剪无关。

在这里插入图片描述

SqueezeNet:一般来说,人们会问:为什么我们不一开始就训练压缩模型?SqueezeNet的创造者们决定试一试。再加上剪枝和量化,该模型取得了510倍的可笑的压缩率,同时保持了AlexNet的Top-1和Top-5的准确值。

在这里插入图片描述

哈夫曼编码:我们做了一个隐含的假设,即所有的类别索引id都必须由相同数量的比特来表示。但是,很明显,有些类别索引id会比其他的使用更加频繁。为什么我们不对使用更频繁的类别索引id使用较少的比特呢?使用哈夫曼编码就可以做到这一点,并且可以进一步提高压缩率。

在这里插入图片描述
深度压缩 (Deep Compression)
Deep Compression: Compressing Deep Neural Networks With Pruning, Trained Quantization And Huffman Coding
在这里插入图片描述
在这里插入图片描述

实现

安装需要的库

!pip install torchprofile
!pip install fast-pytorch-kmeans

一个 n n n位的k-means量化将把权重分成 2 n 2^n 2n个类别,同一类别中的权重将共享相同的权重值。因此,k-means量化将创建一个codebook,其中包括

  • centroidsc 2 n 2^n 2n fp32聚类中心。
  • labels:一个 n n n位的整数张量,与原始fp32权重张量的元素个数相同。每个整数表示它属于哪个簇。

在推理过程中,根据codebook生成一个fp32张量。

quantized_weight = codebook.centroids[codebook.labels].view_as(weight)

from collections import namedtuple
from fast_pytorch_kmeans import KMeans

Codebook = namedtuple('Codebook', ['centroids', 'labels'])


def k_means_quantize(fp32_tensor: torch.Tensor, bitwidth=4, codebook=None):
    """
    quantize tensor using k-means clustering
    :param fp32_tensor:
    :param bitwidth: [int] quantization bit width, default=4
    :param codebook: [Codebook] (the cluster centroids, the cluster label tensor)
    :return:
        [Codebook = (centroids, labels)]
            centroids: [torch.(cuda.)FloatTensor] the cluster centroids
            labels: [torch.(cuda.)LongTensor] cluster label tensor
    """
    if codebook is None:
        # get number of clusters based on the quantization precision
        n_clusters = 1 << bitwidth
        # use k-means to get the quantization centroids
        kmeans = KMeans(n_clusters=n_clusters, mode='euclidean', verbose=0)
        labels = kmeans.fit_predict(fp32_tensor.view(-1, 1)).to(torch.long)
        centroids = kmeans.centroids.to(torch.float).view(-1)
        codebook = Codebook(centroids, labels)
    # decode the codebook into k-means quantized tensor for inference
    quantized_tensor = codebook.centroids[codebook.labels]
    fp32_tensor.set_(quantized_tensor.view_as(fp32_tensor))
    return codebook

在这里插入图片描述
现在将k-means量化函数包装成一个用于量化整个模型的类。在 "KMeansQuantizer "类中,我们必须保持codebook(即 "中心点 "和 “标签”)的记录,这样我们就可以在模型权重改变时应用或更新codebook。

from torch.nn import parameter
class KMeansQuantizer:
    def __init__(self, model : nn.Module, bitwidth=4):
        self.codebook = KMeansQuantizer.quantize(model, bitwidth)
    
    @torch.no_grad()
    def apply(self, model, update_centroids):
        for name, param in model.named_parameters():
            if name in self.codebook:
                if update_centroids:
                    update_codebook(param, codebook=self.codebook[name])
                self.codebook[name] = k_means_quantize(
                    param, codebook=self.codebook[name])

    @staticmethod
    @torch.no_grad()
    def quantize(model: nn.Module, bitwidth=4):
        codebook = dict()
        if isinstance(bitwidth, dict):
            for name, param in model.named_parameters():
                if name in bitwidth:
                    codebook[name] = k_means_quantize(param, bitwidth=bitwidth[name])
        else:
            for name, param in model.named_parameters():
                if param.dim() > 1:
                    codebook[name] = k_means_quantize(param, bitwidth=bitwidth)
        return codebook

对模型进行量化

bitwidth = 8
quantizer = KMeansQuantizer(model, bitwidth)

当模型量化到较低的比特时,准确率明显下降。因此,我们必须进行训练以恢复准确性。在k-means量化感知训练期间,中心点也会被更新,中心点的梯度计算如下。

∂ L ∂ C k = ∑ j ∂ L ∂ W j ∂ W j ∂ C k = ∑ j ∂ L ∂ W j 1 ( I j = k ) \frac{\partial \mathcal{L} }{\partial C_k} = \sum_{j} \frac{\partial \mathcal{L} }{\partial W_{j}} \frac{\partial W_{j} }{\partial C_k} = \sum_{j} \frac{\partial \mathcal{L} }{\partial W_{j}} \mathbf{1}(I_{j}=k) CkL=jWjLCkWj=jWjL1(Ij=k)

其中 L \mathcal{L} L是损失, C k C_k Ckk个中心点, I j I_{j} Ij是权重 W j W_{j} Wj的标签。 1 ( ) \mathbf{1}() 1()是指标函数, 1 ( I j = k ) \mathbf{1}(I_{j}=k) 1(Ij=k)意味着 1    i f    I j = k    e l s e    0 1\;\mathrm{if}\;I_{j}=k\;\mathrm{else}\;0 1ifIj=kelse0,即, I j = = k I_{j}==k Ij==k

为了简单起见,我们直接根据最新的权重更新中心点

C k = ∑ j W j 1 ( I j = k ) ∑ j 1 ( I j = k ) C_k = \frac{\sum_{j}W_{j}\mathbf{1}(I_{j}=k)}{\sum_{j}\mathbf{1}(I_{j}=k)} Ck=j1(Ij=k)jWj1(Ij=k)

def update_codebook(fp32_tensor: torch.Tensor, codebook: Codebook):
    """
    update the centroids in the codebook using updated fp32_tensor
    :param fp32_tensor: [torch.(cuda.)Tensor] 
    :param codebook: [Codebook] (the cluster centroids, the cluster label tensor)
    """
    n_clusters = codebook.centroids.numel()
    fp32_tensor = fp32_tensor.view(-1)
    for k in range(n_clusters):
        codebook.centroids[k] = fp32_tensor[codebook.labels == k].mean()

训练

accuracy_drop_threshold = 0.5
quantizers_before_finetune = copy.deepcopy(quantizers)
quantizers_after_finetune = quantizers

for bitwidth in [8, 4, 2]:
    recover_model()
    quantizer = quantizers[bitwidth]
    print(f'k-means quantizing model into {bitwidth} bits')
    quantizer.apply(model, update_centroids=False)
    quantized_model_size = get_model_size(model, bitwidth)
    print(f"    {bitwidth}-bit k-means quantized model has size={quantized_model_size/MiB:.2f} MiB")
    quantized_model_accuracy = evaluate(model, dataloader['test'])
    print(f"    {bitwidth}-bit k-means quantized model has accuracy={quantized_model_accuracy:.2f}% before quantization-aware training ")
    accuracy_drop = fp32_model_accuracy - quantized_model_accuracy
    if accuracy_drop > accuracy_drop_threshold:
        print(f"        Quantization-aware training due to accuracy drop={accuracy_drop:.2f}% is larger than threshold={accuracy_drop_threshold:.2f}%")
        num_finetune_epochs = 5
        optimizer = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.9)
        scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, num_finetune_epochs)
        criterion = nn.CrossEntropyLoss()
        best_accuracy = 0
        epoch = num_finetune_epochs
        while accuracy_drop > accuracy_drop_threshold and epoch > 0:
            train(model, dataloader['train'], criterion, optimizer, scheduler,
                  callbacks=[lambda: quantizer.apply(model, update_centroids=True)])
            model_accuracy = evaluate(model, dataloader['test'])
            is_best = model_accuracy > best_accuracy
            best_accuracy = max(model_accuracy, best_accuracy)
            print(f'        Epoch {num_finetune_epochs-epoch} Accuracy {model_accuracy:.2f}% / Best Accuracy: {best_accuracy:.2f}%')
            accuracy_drop = fp32_model_accuracy - best_accuracy
            epoch -= 1
    else:
        print(f"        No need for quantization-aware training since accuracy drop={accuracy_drop:.2f}% is smaller than threshold={accuracy_drop_threshold:.2f}%")

结果

k-means quantizing model into 8 bits
    8-bit k-means quantized model has size=8.80 MiB
    8-bit k-means quantized model has accuracy=92.75% before quantization-aware training 
        No need for quantization-aware training since accuracy drop=0.20% is smaller than threshold=0.50%
k-means quantizing model into 4 bits
    4-bit k-means quantized model has size=4.40 MiB
    4-bit k-means quantized model has accuracy=84.01% before quantization-aware training 
        Quantization-aware training due to accuracy drop=8.94% is larger than threshold=0.50%
        Epoch 0 Accuracy 92.29% / Best Accuracy: 92.29%
        Epoch 1 Accuracy 92.47% / Best Accuracy: 92.47%
k-means quantizing model into 2 bits
    2-bit k-means quantized model has size=2.20 MiB
    2-bit k-means quantized model has accuracy=12.26% before quantization-aware training 
        Quantization-aware training due to accuracy drop=80.69% is larger than threshold=0.50%
        Epoch 0 Accuracy 90.29% / Best Accuracy: 90.29%
        Epoch 1 Accuracy 91.06% / Best Accuracy: 91.06%
        Epoch 2 Accuracy 91.22% / Best Accuracy: 91.22%
        Epoch 3 Accuracy 91.44% / Best Accuracy: 91.44%
        Epoch 4 Accuracy 91.33% / Best Accuracy: 91.44%

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

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

相关文章

龙蜥开发者说:6 年前打开的开源“潘多拉盲盒”,如今都解了哪些未知数 | 第 15 期

「龙蜥开发者说」第 15 期来了&#xff01;开发者与开源社区相辅相成&#xff0c;相互成就&#xff0c;这些个人在龙蜥社区的使用心得、实践总结和技术成长经历都是宝贵的&#xff0c;我们希望在这里让更多人看见技术的力量。本期故事&#xff0c;我们邀请了龙蜥社区开发者陈成…

PTA L1-020 帅到没朋友(详解)

前言&#xff1a;本期是关于帅到没朋友的详解&#xff0c;内容包括四大模块&#xff1a;题目&#xff0c;代码实现&#xff0c;大致思路&#xff0c;代码解读 今天你c了吗&#xff1f; 题目&#xff1a; 当芸芸众生忙着在朋友圈中发照片的时候&#xff0c;总有一些人因为太帅…

如何将视频制作成高清gif?试试这一招快速完成

随着现在短视频平台的兴起&#xff0c;越来越多的小伙伴在刷到好玩有趣的视频时都会下载下来&#xff0c;进行分享。但是&#xff0c;视频的体积往往比较大&#xff0c;传输起来非常的不方便。这时候就可以将视频转换成gif动图来使用。接下来&#xff0c;小编给大家分享几个视频…

CobaltStrike之宏钓鱼及shellcode 实验详细

文章目录预备知识实验目的实验地址实验环境实验步骤一启动cs服务器客户端连入服务器生成office宏病毒利用宏病毒上线CS生成宏文件利用成功实验步骤二实验步骤三预备知识 CobaltStrike是一款渗透测试神器&#xff0c;常被业界人称为CS神器。CobaltStrike已经不再使用MSF而是作为…

css图片铺满浏览器窗口且不变形

今天项目中提到一个需求&#xff0c;需要把背景图片铺满浏览器全屏&#xff0c;且图片不变形。 也就是说显示的宽高是不确定的&#xff0c;我第一反应就是background-size: 100% 100%;但是这样做有一个弊端。 下面结合代码图具体讨论一下&#xff1a; 首先&#xff0c;设计一个…

请求上下文头信息:User-Agent ,Referer,Form,Server,Allow,Accept-Range

User-Agent User-Agent 的组成 由1个product和后面零个或多个product信息组成 product包含两个部分&#xff0c;token和token指向的软件的版本号 举例中 Mozilla 5.0 表示浏览器兼容Mozilla 5.0版本的&#xff0c;后面的window 信息&#xff0c;就属于conment部分 RWS由一个…

36. 实战:基于上一节的全面升级——实现某音批量下载功能

目录 前言 目的 思路 代码实现 1. 先将下载单个视频的功能封装成函数 2. 获取下载列表 3. 创建线程池调用下载函数 完整源码 运行效果 总结 前言 上一节我们实现了某短视频平台的去水印下载功能&#xff0c;本节我们实现批量抓取&#xff1a;给定某一个用户主页&…

Cache的PLRU替换策略

LRU(Least Recently Used)替换策略是cache的经典替换策略之一,然而,LRU替换策略的硬件开销较大。因此,一些现代处理器,例如Intel 486和PowerPC,它们使用的cache替换策略是PLRU(pseudo-LRU)。 PLRU是LRU的一种近似方法,本文介绍PLRU中的tree-PLRU(tree-based pseudo-…

VSCode 配置 C/C++ 开发环境( MSVC )

0.绿色版本下载地址&#xff1a;https://www.aliyundrive.com/s/DMK13owZSrC绿色版本采用 VSCode User 1.7.4.2 X64 版本。绿色便携版本采用的生成工具来自 Visual C 2010 版本提供的 32 位工具链。Win 7 Win10 Win 11 可以正常运行。使用方法&#xff0c;下载文件后&#xff0…

微信小程序021理发店美容预约系统java nodejs php

美容预约小程序微信端要求在系统的手机上可以运行&#xff0c;主要实现了管理端&#xff1b;首页、个人中心、用户管理、服务项目管理、美容师管理、预约管理、类型管理、系统管理&#xff0c;微信端&#xff1b;首页、预约、我的等主要功能模块的操作和管理。 小程序前端框架&…

ML LightGBM详细原理讲解+面试必考知识点

&#x1f604; 三大竞赛杀器&#xff1a;XGBoost、LightGBM、CatBoost。之前我已更新完XGBoost的讲解&#xff0c;这次来讲讲LightGBM。我也是看网上看了多篇文章做总结的(我是遇到不会问题的就去搜&#xff0c;实在记不起来看过哪些&#xff0c;如果有侵权问题&#xff0c;可私…

Spark 读取、写入时序数据库TDengine以及TDengine概述

一、TDengine是什么TDengine 是一款高性能、分布式、支持 SQL 的时序数据库&#xff0c;其核心代码&#xff0c;包括集群功能全部开源&#xff08;开源协议&#xff0c;AGPL v3.0&#xff09;。TDengine 能被广泛运用于物联网、工业互联网、车联网、IT 运维、金融等领域。除核心…

Postman form-data、x-www-form-urlencoded的区别

我们在平时的postman请求调试&#xff0c;以及web的http请求开发时&#xff0c;常常能看到以下几种传参的形式 form-data、x-www-form-urlencoded、raw、binary&#xff0c;那么这些请求方式的区别是什么呢&#xff1f; 1、form-data: 就是http请求中的multipart/form-data,它…

一文读懂!2023量子计算行业发展方向

&#xff08;图片来源&#xff1a;网络&#xff09;量子计算是一个引人入胜的主题&#xff0c;放眼全球&#xff0c;很多文章报道了商业量子计算的项目和应用&#xff0c;关于量子计算行业在2023年的可能发展方向&#xff0c;本文从大量文章中筛选罗列了一些主要发展方向。McKi…

postman生成测试报告

一、newman插件生成测试报告安装nodejs官网下载适合自己设备的包&#xff0c;下载后直接傻瓜式安装&#xff1b;安装完成验证&#xff1a;打开终端&#xff0c;输入 node -v&#xff0c;即可查看安装版本&#xff1b;安装newman安装好nodejs后&#xff0c;通过npm来安装newman&…

VueJS 基础之组件

文章目录参考描述组件化开发组成templatescriptstylemain.js使用嵌套关系组件的使用LeftRight举个栗子全局组件及私有组件私有组件全局组件举个栗子main.jsApp.vue参考 项目描述VueJS官方文档搜索引擎Bing哔哩哔哩黑马程序员 描述 项目描述Edge109.0.1518.70 (正式版本) (64 …

计算机网路6-详述网路层:数据交换、路由算法和协议、网络协议

一、网络层基本概念 1、主要任务 把分组从源端传到目的端&#xff0c;为分组交换网上的不同主机提供通信服务 2、网络层传输单位--数据报 数据报与分组之间关系&#xff1a; 分组是数据报通过切割划分出来的一个片段&#xff0c;多个分组组成数据报 3、网络层的几大功能 路…

LeetCode-122. 买卖股票的最佳时机 II

目录暴力递归动态规划贪心题目来源 122. 买卖股票的最佳时机 II 暴力递归 根据题意&#xff1a;由于不限制交易次数&#xff0c;在每一天&#xff0c;就可以根据当前是否持有股票选择相应的操作。「暴力搜索」在树形问题里也叫「回溯搜索」、「回溯法」。 首先画出树形图&…

如何实现报表集成?(二)——用户同步和单点登录

在上一篇&#xff0c;我们对报表集成进行了一个整体的介绍&#xff0c;从报表集成的背景、痛点、需求、集成架构等几个方面进行了阐述。 这一篇&#xff0c;我们来聊一下用户同步和单点登录。行文过程中得到了来自报表软件厂商 Smartbi 的报表产品&#xff1a;电子表格软件的协…

c++11 标准模板(STL)(std::multiset)(三)

定义于头文件 <set> template< class Key, class Compare std::less<Key>, class Allocator std::allocator<Key> > class multiset;(1)namespace pmr { template <class Key, class Compare std::less<Key>> usi…