PyTorch模型INT8量化基础

news2024/11/24 22:48:32

PyTorch模型INT8量化基础

    • 最基础的Tensor量化
    • 校准
    • 两种不同的量化方案
    • 每张量和每通道量化方案
    • 量化后端引擎配置
    • QConfig
    • Tensor量化
    • Post Training Static Quantization (训练后静态量化)
      • fuse_model:融合网络中的一些层
    • 设置qCONFIG
    • prepare: 定标 :scale 和 zero_point
    • 喂数据
    • 转换模型
    • 完整的demo-针对单层的网络
    • 完整的demo-针对Sequential
    • quantize_fx来进行量化操作
    • 保存和加载量化模型
    • 查看参数

 模型的量化指的是使用更少的bit来存储原本以浮点数存储的tensor ,以及使用更少的Bit来完成原本以浮点数完成的计算,好处:

  • 更少的模型体积 四倍的减少
  • 更快的计算 因为更少的内存访问和更快的int8计算
  • 量化之后的模型 部分或者全部的tensor操作使用int类型的计算,而不是使用量化之前的float类型 当然量化还需要底层硬件支持

参考学习笔记:https://www.cnblogs.com/LXP-Never/p/16822727.html

最基础的Tensor量化

在这里插入图片描述

  • 首先缩放因子Scale:首先,计算一个缩放因子(scale),这个因子用来确定如何将浮点数值映射到整数范围。通常,这个因子是一个常数,可以通过以下公式计算得到

  • 其中,max_range和min_range是你希望量化的数值的最大和最小值。

  • [q_min,q_max]是量化输出空间中的范围

  • zero_point充当偏差 来确保输入的0完美映射到量化空间中的0 :zero_point = q_min - min/ scale

  • zero_Point充当偏差 来确保输入的0完美的映射到量化空间中的0:zero_point = q_min - min/scale

校准

  选择输入限幅范围的过程 称之为校准,最简单的技术就是记录运行过程中的最小值和最大值,TensorRT还使用熵最小化(KL散度)、均方误差最小化或者输入范围的百分位数

  • Pytorch中,Observer模块收集输入值的统计信息 并且计算scale和zero_point,不同的校准方案会产生不同的量化输出,最好凭借经验验证哪一种方案最适合您的应用程序和架构
import torch
from torch.quantization.observer import MinMaxObserver, MovingAverageMinMaxObserver, HistogramObserver

# 定义通道数和序列长度
C, L = 3, 4
# 创建一个正太分布随机数生成器
normal = torch.distributions.normal.Normal(0, 1)

# 生成两个随机输入张量
inputs = [normal.sample((C, L)), normal.sample((C, L))]

for x in inputs:
    print(x.shape)

print(inputs)
# [tensor([[-0.0590,  1.1674,  0.7119, -1.1270],
#          [-1.3974,  0.5077, -0.5601,  0.0683],
#          [-0.0929,  0.9473,  0.7159, -0.4574]]]),

# tensor([[-0.0236, -0.7599,  1.0290,  0.8914],
#          [-1.1727, -1.2556, -0.2271,  0.9568],
#          [-0.2500,  1.4579,  1.4707,  0.4043]])]

# 创建观察者对象
observers = [MinMaxObserver(),          # 最小值、最大值 观察者
             MovingAverageMinMaxObserver(),     # 移动平均最小值、最大值 观察者
             HistogramObserver()]

# 遍历观察者对象列表
# 对于每一个观察者 遍历输入数据并且使用观察者对象 观察每一个输入张量
# 打印观察者的类名以及观察之后的计算的量化参数 这些参数包括量化的最小值和最大值


for obs in observers:
    for x in inputs:
        obs(x) # 使用观察者对象 观察输入数据
    print(obs.__class__.__name__, obs.calculate_qparams())
    # MinMaxObserver (tensor([0.0112]), tensor([124], dtype=torch.int32))
    # MovingAverageMinMaxObserver (tensor([0.0101]), tensor([139], dtype=torch.int32))
    # HistogramObserver (tensor([0.0100]), tensor([106], dtype=torch.int32))


  • 对于MinMaxObserver: 计算出的最小值tensor([0.0112]),最大值124,以整数类型32表示
  • 对于MovingAverageMinMaxObserver 计算出的移动平均最小值是0.0101,计算出的移动平均最大值是159,也是使用整数类型表示
  • HistogramObserver 表示通过直方图统计计算出的最小值是0.0188 直方图统计计算出的最大值是94,使用整数类型表示
  • 这些输出是量化神经网络的关键信息,最小值和最大值是量化过程中的关键参数,而不同的观察值可以提供不同的方式来计算这些参数

两种不同的量化方案

for qscheme in [torch.per_tensor_affine, torch.per_tensor_symmetric]:
    # 计算移动最大平均值 和最小平均值
    obs = MovingAverageMinMaxObserver(qscheme=qscheme)
    for x in inputs:
        obs(x)
    print(f"Qscheme: {qscheme} | {obs.calculate_qparams()}")
    # Qscheme: torch.per_tensor_affine | (tensor([0.0101]), tensor([139], dtype=torch.int32))
    # Qscheme: torch.per_tensor_symmetric | (tensor([0.0109]), tensor([128]))

Qscheme: torch.per_tensor_affine | (tensor([0.0111]), tensor([159], dtype=torch.int32))
Qscheme: torch.per_tensor_symmetric | (tensor([0.0138]), tensor([128]))

  • qscheme 是指量化方案的设置,可以是torch.per_tensor_affine或者是torch.per_tensor_symmetric 这些方案定义了如何量化张量的数据

  • 创建 MovingAverageMinMaxObserver观察者对象 将qscheme设置为不同的方案

  • 打印每一种qscheme设置下的计算的量化参数

  • Qscheme: torch.per_tensor_affine | (tensor([0.0101]), tensor([139], dtype=torch.int32)) 这个表示使用torch.per_tensor_affline 方案进行量化,计算出的移动平均最小值是0.0101,最大值是139

  • 不同的qscheme设置会影响量化的方式,torch.per_tensor_affine适用于任意数据范围,但是torch.per_tensor_symmetric适用于数据范围在对称范围内的情况,选择适当的qscheme取决于输入数据的性质和量化的需求

  • torch.per_tensor_affine

    • Affline量化:这是该方案的关键,Affline量化表示对着每一个张量,我们使用一个线性变换来将浮点值映射到固定点数值,这个线性变换通常由两个参数组成:缩放因子(scale)和偏置(zero_point)
    • 单一缩放因子和偏执:对于 per_tensor_affine 方案,一个缩放因子和一个偏置值适用于整个张量。这意味着对于每个张量,所有元素都被映射到相同的固定点数值范围内
  • torch.per_tensor_symmetric

    • Symmetric量化,采用对称的方式进行量化,这意味零点是张量数据范围的中心点,但是缩放因子scale控制了数据范围的大小,
    • 适用于数据范围在对称范围内,这个方案适用于数据范围在零点周围对称的情况,例如正数和负数的数据范围,这种情况下,采用对称量化可以有效地表示数据

torch.per_tensor_affine 使用单一的缩放因子和偏置来映射整个张量的浮点数据,而 torch.per_tensor_symmetric 使用对称方式映射数据,零点位于数据范围的中心,缩放因子控制范围的大小。选择哪种方案取决于输入数据的性质以及在量化神经网络时的需求。

仿射量化会导致计算量更大的推理,对称量化可能会导致量化分辨率不佳,因为裁剪范围包括从未出现在输入中的值

每张量和每通道量化方案

  • Per-tensor量化:将层的整个权重张量作为一个整体计算量化参数,相同的裁剪范围应用于层中的所有通道,为每一个张量对整个张量使用相同的qparams(scale 和 offse)
  • per-channel量化,将每一个通道单独计算量化参数,为每一个通道使用一组qparams(scale和offse)
  • 对于权重量化,per-Channel 对称量化提供更好的精度;per-tensor 量化表现不佳,可能是由于 BatchNorm 折叠 [3] 跨通道的 Conv 权重差异很大

在这里插入图片描述

  • 每张量量化(Per Tensor Quantization)
    • 每张量量化是指整个张量中的所有数据都使用相同的量化参数进行量化
    • 缩放因子和零点是共享的:对于每个张量,使用一个缩放因子(scale)和一个零点(zero_point)。这意味着整个张量的数据被映射到相同的固定点数值范围内。
    • 适用性:适用于张量中的数据范围大致相同的情况,其中整个张量的数据都可以使用相同的缩放因子和零点来表示。
    • 优点:简单,适用于许多情况,特别是当张量中的数据范围一致时。
  • 每通道量化(Per Channel Quantization):
  • 每通道量化是指对张量中的每一个通道都使用不同的量化参数进行量化
  • 通道级别的缩放因子和零点:对于每个通道,使用独立的缩放因子和零点。这意味着不同通道中的数据可以在不同的固定点数值范围内表示。
  • 适用性:适用于张量中的不同通道具有不同数据范围的情况,通道之间的数据差异比较大
  • 优点:更加灵活,更好适应不同通道之间的数据差异

选择哪种方案通常取决于输入数据的性质以及在量化神经网络时的需求。每张量量化适用于数据范围大致相同的情况,而每通道量化适用于不同通道之间具有不同数据范围的情况。根据实际情况,可以选择适当的方案来最大程度地减小内存占用并保持模型性能

from torch.quantization.observer import MovingAveragePerChannelMinMaxObserver

# 通道参数量化
obs =MovingAveragePerChannelMinMaxObserver(ch_axis=0)  # 分别计算所有' C '通道的qparams
for x in inputs:
    obs(x)
print(obs.calculate_qparams())
# (tensor([0.0090, 0.0075, 0.0055]), tensor([125, 187,  82], dtype=torch.int32))

  • 第一个张量 (tensor([0.0090, 0.0075, 0.0055])) 包含每个通道的缩放因子。这表示每个通道都有自己的缩放因子,用于量化数据。
  • 第二个张量 (tensor([125, 187, 82], dtype=torch.int32)) 包含每个通道的零点值。这表示每个通道都有自己的零点值,用于量化数据。

量化后端引擎配置


backend = 'fbgemm' if x86 else 'qnnpack'
qconfig = torch.quantization.get_default_qconfig(backend)  
torch.backends.quantized.engine = backend
  • backend = ‘fbgemm’ if x86 else ‘qnnpack’:在这行代码中,选择了一个量化的后端引擎。这个选择是根据条件 x86 来决定的。如果 x86 为真,那么选择 ‘fbgemm’ 作为量化后端,否则选择 ‘qnnpack’。后端引擎是执行量化操作的库或引擎,不同的后端可以提供不同的性能和功能
  • qconfig = torch.quantization.get_default_qconfig(backend):在这行代码中,通过调用 torch.quantization.get_default_qconfig() 函数获取了一个默认的量化配置(qconfig)。这个配置是根据选择的后端引擎(backend)而生成的,以便为量化操作提供适当的设置。
  • torch.backends.quantized.engine = backend:这一行代码将 PyTorch 的量化引擎设置为选择的后端引擎(‘fbgemm’ 或 ‘qnnpack’)。这将确保后续的量化操作使用正确的引擎来执行,以提供性能和功能上的支持

QConfig

  • QConfig NamedTuple 存储观察者和用于量化激活和权重的量化方案。请务必传递 Observer 类(而非实例)或返回 Observer 实例的可调用对象。用于with_args()覆盖默认参数。

  • activation 字段使用MovingAverageMinMaxObserver观察者 并且设置qscheme为torch.per_tensror_affline 这意味着针对激活值使用每张张量量化,使用每张量的缩放因子和零点

  • weight字段使用MovingAveragePerChannelMinMaxObserver观察者,并且设置qscheme为torch.qint8 这意味着针对权重使用每通道量化,使用每个通道的缩放因子和零点

  • 自定义量化配置允许你精确控制激活值和权重的量化方式,激活值通常使用每张量的量化,而权重通常可以根据实际需求选择每通道或者每张量的量化,这种精细的配置使你可以根据不同的模型需求和硬件特性来优化量化设置


my_qconfig = torch.quantization.QConfig(
  activation=MovingAverageMinMaxObserver.with_args(qscheme=torch.per_tensor_affine),
  weight=MovingAveragePerChannelMinMaxObserver.with_args(qscheme=torch.qint8)
)
# >>>>>
# QConfig(activation=functools.partial(<class 'torch.ao.quantization.observer.MovingAverageMinMaxObserver'>, qscheme=torch.per_tensor_affine){}, weight=functools.partial(<class 'torch.ao.quantization.observer.MovingAveragePerChannelMinMaxObserver'>, qscheme=torch.qint8){})

Tensor量化

  • 为了实现量化,PyTorch 引入了能够表示量化数据的Quantized Tensor,可以存储 int8/uint8/int32类型的数据,并携带有scale、zero_point这些参数。把一个标准的float Tensor转换为量化Tensor的步骤如下:
import torch

x = torch.randn(2, 2, dtype=torch.float32)
# tensor([[ 0.9872, -1.6833],
#         [-0.9345,  0.6531]])

print(x)

# 公式1(量化):xq = round(x / scale + zero_point)
# 使用给定的scale和 zero_point 来把一个float tensor转化为 quantized tensor
xq = torch.quantize_per_tensor(x, scale=0.5, zero_point=8, dtype=torch.quint8)
# tensor([[ 1.0000, -1.5000],
#         [-1.0000,  0.5000]], size=(2, 2), dtype=torch.quint8,
#        quantization_scheme=torch.per_tensor_affine, scale=0.5, zero_point=8)

# print(xq.int_repr())  # 给定一个量化的张量,返回一个以 uint8_t 作为数据类型的张量
# tensor([[10,  5],
#         [ 6,  9]], dtype=torch.uint8)

# 公式2(反量化):xdq = (xq - zero_point) * scale
# 使用给定的scale和 zero_point 来把一个 quantized tensor 转化为 float tensor
xdq = xq.dequantize()
# tensor([[ 1.0000, -1.5000],
#         [-1.0000,  0.5000]])

print(xdq)


tensor([[-0.5235,  0.8499],
        [-3.1486,  0.0299]])
tensor([[-0.5000,  1.0000],
        [-3.0000,  0.0000]])
  • x和xdq的值不一样
  • 量化会有精度损失
  • 随便选取的scale和zp太烂,选择合适的scale和zp可以有效降低精度损失,这两个参数需要前向推理来计算出来

Post Training Static Quantization (训练后静态量化)

 静态量化需要把模型的权重和激活值都进行量化,静态量化需要把训练集或者训练集分布类似的数据喂给模型(没有反向传播),然后通过每一个op输入的分布来计算activation的量化参数(scale和zp),这一步作为定标,因为静态量化的前向推理过程中自始至终都是int计算,activation需要确保一个op的输入符合下一个op的输入。

fuse_model:融合网络中的一些层

合并一些可以合并的layer。这一步的目的是为了提高速度和准确度

fuse_modules(model, modules_to_fuse, inplace=False, fuser_func=fuse_known_modules, fuse_custom_config_dict=None)

比如合并网络中的fc和relu

torch.quantization.fuse_modules(F32Model, [['fc', 'relu']], inplace=True)

一旦成功融合,那么原始网络中的fc就可以替换为新的合并之后的module(因为是list中的第一个元素),而relu(list中剩余的元素)会被替换为nn.Identity(),这个模块是占位符。直接输出输入

import torch
from torch import nn


class F32Model(nn.Module):
    def __init__(self):
        super(F32Model,self).__init__()
        self.fc = nn.Linear(3,2,bias=False)
        self.relu = nn.ReLU(inplace=False)
        
    def forward(self,x):
        x = self.fc(x)
        x = self.relu(x)
        
        return x
    
    
model_fp32 = F32Model()

print(model_fp32)


model_fp32_fused = torch.quantization.fuse_modules(model_fp32,[['fc','relu']])

print(model_fp32_fused)


如果要fuse的model被Sequential封装起来了,请参考下面的代码,但是有顺序安排


torch.quantization.fuse_modules(a_sequential_module, ['0', '1', '2'], inplace=True)
Convolution, BatchNorm
Convolution, BatchNorm, ReLU
Convolution, ReLU
Linear, ReLU
BatchNorm, ReLU
ConvTranspose, BatchNorm

设置qCONFIG

#如果要部署在x86 server上
model_fp32.qconfig = torch.quantization.get_default_qconfig('fbgemm')

#如果要部署在ARM上
model_fp32.qconfig = torch.quantization.get_default_qconfig('qnnpack')

prepare: 定标 :scale 和 zero_point

  • prepare用来给每一个子module插入Observer 用来收集和定标数据
  • 观察输入数据得到四元组的min_val和max_val 至少观察几百个迭代的数据
  • 然后由这个四元组得到scale和zp这两个参数的值
model_fp32_prepared = torch.quantization.prepare(model_fp32_fused)

喂数据

  • 为了获取数据的分布特点 来更好地计算activation的scale和zp,至少需要几百个迭代的数据
#至少观察个几百迭代
for data in data_loader:
    model_fp32_prepared(data)

转换模型

上一步完成之后,得到各个op的权重的四元组(min_val,max_val,qmin,qmax)中的(min_val,max_val)都已经有了,各个op的activation中的四元组(min_val,max_val,qmin,qmax)中的(min_val,max_val)也都已经观察出来了,然后调用convert_API

model_prepared_int8 = torch.quantization.convert(model_fp32_prepared)

完整的demo-针对单层的网络


import torch
from torch import nn


class F32Model(torch.nn.Module):
    def __init__(self):
        super(F32Model,self).__init__()
        
        self.quant = torch.quantization.QuantStub()  # 转换张量从浮点到量化
        
        
        self.conv =  nn.Conv2d(1,1,1)
        self.fc = nn.Linear(2,2,bias = False)
        self.relu = nn.ReLU()
        
        # 将量化张量  转换为浮点
        self.dequant = torch.quantization.DeQuantStub()
        
    def forward(self,x):
        x = self.quant(x)
        x = self.conv(x)
        x = self.fc(x)
        x = self.relu(x)
        x = self.dequant(x)
        
        return x
    
model_fp32 = F32Model()

# 量化需要开启验证模式
model_fp32.eval()

# 将模型部署在arm
model_fp32.qconfig = torch.quantization.get_default_qconfig('qnnpack')

#  将网络的一些层进行融合

model_fp32_fused = torch.quantization.fuse_modules(model_fp32,[['fc','relu']])

#  准备模型  插入观察对象    观察activation 和weight
model_fp32_prepared = torch.quantization.prepare(model_fp32_fused)


# 代表性数据集 获取与数据的分布特点  来更好的计算 及或者的scale 和zp

# batch x channel x h x w
input_fp32 = torch.randn(1,1,2,2)

#  喂数据  计算参数
model_fp32_prepared(input_fp32)


# 量化模型

model_int8 = torch.quantization.convert(model_fp32_prepared)

# 运行模型  计算都以int8来计算

import time

# 测量float32模型的执行时间
start_time_fp32 = time.time()
output_fp32 = model_fp32(input_fp32)
# end_time_fp32 = 
execution_time_fp32 = time.time() - start_time_fp32

# 测量int8模型的执行时间
start_time_int8 = time.time()
output_int8 = model_int8(input_fp32)
# end_time_int8 = 
execution_time_int8 = time.time() - start_time_int8

print("Execution time (float32): {:.8f} seconds".format(execution_time_fp32))
print("Execution time (int8): {:.8f} seconds".format(execution_time_int8))
Execution time (float32): 0.00096154 seconds
Execution time (int8): 0.00000000 seconds

完整的demo-针对Sequential

import torch

from torch import nn
import copy

# 部署的后端计算引擎  运行在x86 芯片
backend = "fbgemm"


model = nn.Sequential(
    nn.Conv2d(2,64,3),
    nn.ReLU(),
    nn.Conv2d(64,128,3),
    nn.ReLU()
)

m = copy.deepcopy(model)

m.eval()

#  然后 开始融合模型
torch.quantization.fuse_modules(m,['0','1'],inplace=True)
torch.quantization.fuse_modules(m,['2','3'],inplace=True)

# 插入Stub

m = nn.Sequential(
    torch.quantization.QuantStub(),
    m,
    torch.quantization.DeQuantStub()
)

# 设置后端
m.qconfig = torch.quantization.get_default_qconfig(backend)

#  插入观察对象
torch.quantization.prepare(m,inplace = True)


# 喂数据  计算scale和zero_point

#  推理模式  没有反向传播计算
with torch.inference_mode():
    for _ in range(10):
        x = torch.rand(1,2,28,28)
        m(x)
    
# 转换为int8量化模型 
torch.quantization.convert(m,inplace=True)

# 检查一下 权重参数是不是Int8 

# print(m[[1]].weight().element_size())


"""Check"""
print(m[1][0].weight().element_size()) # 1 byte instead of 4 bytes for FP32

from torch.quantization import quantize_fx


m = copy.deepcopy(model)
m.eval()



nn.Conv2d(2, 64, 3):这是一个卷积层,包括以下参数:

输入通道数 2:表示输入数据具有2个通道(通常对应于彩色图像的R和G通道)。
输出通道数 64:表示卷积层将生成64个输出通道,每个通道对应一个卷积核。
卷积核大小 3:表示卷积核的大小是3x3

quantize_fx来进行量化操作

  • quantize_fx(也称为 FX Graph Mode Quantization)是 Facebook 提供的量化库,建立在 PyTorch FX(PyTorch JIT 编译器)之上。它使用 FX 图来表示模型,然后通过 PyTorch JIT 动态图来执行量化操作。因此,quantize_fx 采用了一种不同的方法来处理模型的量化。
  • quantize_fx 提供了更多的细粒度控制和自定义选项,使用户能够更灵活地调整量化过程,包括选择每通道量化、选择量化后端引擎等。
from torch.quantization import quantize_fx
# 用于创建模型的深拷贝,以便不影响原始模型。
m = copy.deepcopy(model)
m.eval()

# 配置引擎
qconfig = {"":torch.quantization.get_default_qconfig(backend)}

# 插入观察对象
model_prepared = quantize_fx.prepare_fx(m,qconfig)

with torch.inference_mode():
  for _ in range(10):
    x = torch.rand(1,2,28, 28)
    model_prepared(x)
# quantize
model_quantized = quantize_fx.convert_fx(model_prepared)

保存和加载量化模型

import torch
from torch import nn


class F32Model(torch.nn.Module):
    def __init__(self):
        super(F32Model,self).__init__()
        
        self.quant = torch.quantization.QuantStub()  # 转换张量从浮点到量化
        
        
        self.conv =  nn.Conv2d(1,1,1)
        self.fc = nn.Linear(2,2,bias = False)
        self.relu = nn.ReLU()
        
        # 将量化张量  转换为浮点
        self.dequant = torch.quantization.DeQuantStub()
        
    def forward(self,x):
        x = self.quant(x)
        x = self.conv(x)
        x = self.fc(x)
        x = self.relu(x)
        x = self.dequant(x)
        
        return x
    
model_fp32 = F32Model()

# 量化需要开启验证模式
model_fp32.eval()

# 将模型部署在arm
model_fp32.qconfig = torch.quantization.get_default_qconfig('qnnpack')

#  将网络的一些层进行融合

model_fp32_fused = torch.quantization.fuse_modules(model_fp32,[['fc','relu']])

#  准备模型  插入观察对象    观察activation 和weight
model_fp32_prepared = torch.quantization.prepare(model_fp32_fused)


# 代表性数据集 获取与数据的分布特点  来更好的计算 及或者的scale 和zp

# batch x channel x h x w
input_fp32 = torch.randn(1,1,2,2)

#  喂数据  计算参数
model_fp32_prepared(input_fp32)


# 量化模型

model_int8 = torch.quantization.convert(model_fp32_prepared)

# 运行模型  计算都以int8来计算

import time

# 测量float32模型的执行时间
start_time_fp32 = time.time()
output_fp32 = model_fp32(input_fp32)
# end_time_fp32 = 
execution_time_fp32 = time.time() - start_time_fp32

# 测量int8模型的执行时间
start_time_int8 = time.time()
output_int8 = model_int8(input_fp32)
# end_time_int8 = 
execution_time_int8 = time.time() - start_time_int8

print("Execution time (float32): {:.8f} seconds".format(execution_time_fp32))
print("Execution time (int8): {:.8f} seconds".format(execution_time_int8))

torch.save(model_int8.state_dict(), "./state_dict.pth")
model_int8.load_state_dict(torch.load("./state_dict.pth"))
print(model_int8)

  • QuantizedConv2d:这是一个量化卷积层。它接受量化输入,执行卷积操作,并生成量化输出。它还包括了缩放因子(scale)和零点值(zero_point),用于量化输出
  • qscheme 设置为 torch.per_tensor_affine,表示每张量使用单独的缩放和零点。
  • Identity:这是一个标识操作,它不进行任何处理,直接将输入传递给输出。在这个模型中,它用作 ReLU 激活函数的标识操作,因此没有引入额外的量化操作。
  • DeQuantize:这是一个反量化操作,用于将量化输出从整数转换回浮点数。这个操作是从量化输出到浮点输出的转换。
  • QuantizedConv2d 和 QuantizedLinearReLU 分别是卷积层和线性层,它们处理不同的输入数据和进行不同的操作。因此,它们的量化参数可能会不同,以便更好地适应其操作。这种差异是正常的,量化参数的选择旨在最大程度地减小量化误差并保持模型性能。
F32Model(
  (quant): Quantize(scale=tensor([0.0068]), zero_point=tensor([45]), dtype=torch.quint8)
  (conv): QuantizedConv2d(1, 1, kernel_size=(1, 1), stride=(1, 1), scale=0.006129027809947729, zero_point=0)
  (fc): QuantizedLinearReLU(in_features=2, out_features=2, scale=0.004328194074332714, zero_point=0, qscheme=torch.per_tensor_affine)
  (relu): Identity()
  (dequant): DeQuantize()
)

查看参数

其实,只有权重被量化为整形,偏置如果有的话 还是浮点型

print(model_int8.fc.weight().int_repr())
print(model_int8.fc.bias())

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

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

相关文章

ASEMI整流桥KBL410需要散热片吗?

编辑-Z 在决定电子设备或半导体组件的配置时&#xff0c;了解每个部件的性能和需求至关重要。那么&#xff0c;对于KBL410这款整流桥&#xff0c;它是否需要散热片呢&#xff1f;在本文中&#xff0c;我们将详细解析KBL410的工作原理&#xff0c;以及是否需要散热片。 首先&am…

数字电路中触发器/锁存器的简单理解,与电路结构

1&#xff0c;为什么有触发器? 数字逻辑电路系统包含组合逻辑和时序逻辑。组合逻辑用来实现与状态无关的门电路&#xff0c;比如算法的实现函数&#xff0c;无反馈&#xff0c;无记忆&#xff1b;时序逻辑则主要用来同步电路的各个状态&#xff0c;有反馈&#xff0c;有记忆&a…

Java多线程间的通信:生产者消费者问题

逻辑分析 代码实现 package ThreadCommunction;import sun.security.krb5.internal.crypto.Des;import java.util.Date;//目标&#xff1a;了解线程通信 public class ThreadTest {public static void main(String[] args) {//需求&#xff1a;3个生产者线程&#xff0c;负责产…

STM32F4X OLED使用

STM32F4X OLED使用 OLED简介OLED使用OLED驱动芯片SSD1309引脚SSD1309通信协议4线SPI模式3线SPI模式I2C模式OLED地址确认 8080接口OLED 8080写模式OLED 8080读模式 6800接口 SSD1309显示原理SSD1309显存大小SSD1309显存分布SSD1039数据显示MCU操作SSD1309显存方法 OLED取模字符取…

C++类总结

参考&#xff1a; C中的private, public, protected_c private-CSDN博客https://www.cnblogs.com/corineru/p/11001242.html C 中 Private、Public 和 Protected 的区别 Private Public Protected 声明为private类成员只能由基类内部的函数访问。 可以从任何地方访问声明…

Linux系统编程详解

Linux 多线程编程 什么是线程&#xff1f; 与线程类似&#xff0c;线程是允许应用程序并发执行多个任务的一种机制 线程是轻量级的进程&#xff08;LWP&#xff1a;Light Weight Process&#xff09;&#xff0c;在 Linux 环境下线程的本 质仍是进程。 一个进程可以包含多个线…

使用dumuz工具实现淘宝收藏的宝贝批量下载(批量导出)

淘宝买家在实际应用中经常会将关心的淘宝宝贝添加到淘宝的收藏夹里&#xff0c;方便稍后查看和购买。如果你希望将淘宝收藏夹中的内容导出来&#xff0c;以便自己进行归类整理&#xff0c;可以按照以下教程进行操作&#xff1a; 应用功能描述 模拟人工操作淘宝"收藏夹-&…

消息称苹果或在明年推出搭载M3芯片的MacBook产品

近日据 DigiTimes 发布的博文&#xff0c;苹果公司计划在 2024 年推出搭载 M3 芯片的 MacBook 产品。然而&#xff0c;关于这款新产品的发布日期仍存在争议。虽然一些爆料认为苹果可能会在今年发布这款产品&#xff0c;但也有一些爆料认为发布时间会推迟到 2024 年。根据各项报…

Vue3引入腾讯地图,点击坐标后实时获取经纬度

本文将介绍如何在Vue 引入腾讯地图组件&#xff0c;引入后可以直接在页面中渲染腾讯地图&#xff0c;实现 经纬度 与 地图锚点位置的双向绑定&#xff0c;如&#xff1a; 1&#xff0c;输入经纬度后&#xff0c;地图自动定位到指定位置&#xff1b;2&#xff0c;鼠标在地图点击…

[论文笔记]SimCSE

引言 今天带来一篇当时引起轰动的论文SimCSE笔记,论文题目是 语句嵌入的简单对比学习。 SimCSE是一个简单的对比学习框架,它可以通过无监督和有监督的方式来训练。 对于无监督方式,输入一个句子然后在一个对比目标中预测它自己,仅需要标准的Dropout作为噪声。这种简单的…

XXL-Job分布式任务调度框架-知识点汇总5

一 XXLlob 1.1 xxl-job作用 XXL-JOB是一个轻量级分布式任务调度平台&#xff0c;XXL-JOB主要提供了任务的动态配置管理、任务监控和统计报表以及调度日志几大功能模块&#xff0c;支持多种运行模式和路由策略&#xff0c;可基于对应执行器机器集群数量进行简单分片数据处理。…

ftp靶机_获取shell

ftp靶机_获取shell 文章目录 ftp靶机_获取shellftp概念实验环境信息探测 发现漏洞优化shell ftp概念 FTP 是File Transfer Protocol(文件传输协议)的英文简称&#xff0c;而中文简称为“文传协议”。用于Internet上的控制文件的双向传输。同时&#xff0c;它也是一个应用程序(…

macbook磁盘清理免费教程分享

笔记本电脑在是我们工作和生活中重要组成部分&#xff0c;磁盘清理是常有的事&#xff0c;而macbook作为其中的代表之一&#xff0c;也越来越受到人们的青睐。然而&#xff0c;如何进行macbook磁盘清理&#xff0c;也事许多人都会遇到的问题&#xff0c;特别是被提示“磁盘已满…

[python] pytest

在写一个项目前, 可以先编写测试模块 测试模块中包含了一个个最小的功能 当每一个功能都完善正确时 再将这些功能转换成项目运行的功能 多个项目运行的功能就组成了一个模块 多个模块就组成了一个项目服务 pytest 是一个 Python 测试框架&#xff0c;它提供了简单易用的语…

[补题记录] Atcoder Beginner Contest 294(E)

URL&#xff1a;https://atcoder.jp/contests/abc294 目录 E Problem/题意 Thought/思路 Code/代码 E Problem/题意 我们将其当作一个铺路的过程。 给总长度 L&#xff0c;计划 1 有 N 步&#xff0c;计划 2 有 M 步&#xff0c;每一步给出&#xff08;v&#xff0c;l&a…

Python 列表切片陷阱:引用、复制与深复制

大家早好、午好、晚好吖 ❤ ~欢迎光临本文章 如果有什么疑惑/资料需要的可以点击文章末尾名片领取源码 Python 列表的切片和赋值操作很基础&#xff0c;之前也遇到过一些坑&#xff0c; 但今天刷 Codewars 时发现了一个更大的坑&#xff0c;故在此记录。 Python 列表赋值&am…

【重拾C语言】十二、C语言程序开发(穷举与试探——八皇后问题)

目录 前言 十二、C语言程序开发 12.1~3 自顶向下、逐步求精&#xff1b;结构化程序设计原则&#xff1b;程序风格 12.4 八皇后——穷举与试探 12.4.1 穷举法 示例&#xff1a;寻找一个整数的平方根 12.4.2 试探法 示例&#xff1a;计算给定数字的阶乘 12.4.3 穷举与试…

python教程:selenium WebDriver 中的几种等待

嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 强制等待:sleep() import time sleep(5) #等待5秒设置固定休眠时间&#xff0c;单位为秒。 由python的time包提供, 导入 time 包后就可以使用。 缺点&#xff1a; 不智能&#xff0c;使用太多的sleep会影响脚本运行速度。…

网络安全(黑客)—自学笔记

目录 一、自学网络安全学习的误区和陷阱 二、学习网络安全的一些前期准备 三、网络安全学习路线 四、学习资料的推荐 想自学网络安全&#xff08;黑客技术&#xff09;首先你得了解什么是网络安全&#xff01;什么是黑客&#xff01; 网络安全可以基于攻击和防御视角来分类…

Jmeter组件执行顺序与作用域

一、Jmeter重要组件&#xff1a; 1&#xff09;配置元件---Config Element&#xff1a; 用于初始化默认值和变量&#xff0c;以便后续采样器使用。配置元件大其作用域的初始阶段处理&#xff0c;配置元件仅对其所在的测试树分支有效&#xff0c;如&#xff0c;在同一个作用域的…