# mypy: 允许未类型化定义
导入
复制
导入
警告
from 集合
导入
命名元组
from 打字
导入
任何,
可选,
联合
from typing_extensions 导入
已弃用
导入
火炬
导入 torch.nn
是
神经网络
from torch.ao.quantization.fake_quantize 导入 (
默认动态 fake_quant,
默认嵌入伪量化,
默认嵌入伪量化 4 位,
默认伪量化,
默认融合激活伪量化,
默认融合通道权重假量化,
默认融合权重假量化,
默认通道权重假量化,
默认权重假量化,
伪量化,
模拟量化基类,
熔合通道权重伪量化范围-127 到 127,
熔合权重伪量化范围-127 到 127,
熔合移动平均观测伪量化,
)
from .观察者
导入 (
部分包装器,
默认调试观察者,
默认动态量化观察者,
默认浮点 Q 参数观察者,
默认浮点 Q 参数观察者 4 位,
默认观察者,
默认通道权重观察者,
默认占位符观察者,
默认重用输入观察者,
默认权重观察者,
直方图观察者,
最小最大观察者,
移动平均最小最大观察者,
空操作观察者,
观察者基类,
每通道权重观察者范围-127 到 127,
占位符观察者,
ReuseInputObserver,
weight_observer_range_neg_127_to_127,
)
全部 = [
"Q 配置",
# TODO: 已弃用,请删除
"Q 配置动态",
"default_qconfig",
"默认调试 qconfig",
"默认按通道 qconfig",
"默认动态 qconfig",
"float16 动态 qconfig",
"float16 静态 qconfig",
"按通道动态 qconfig",
"浮点 qparams 仅权重 qconfig",
"浮点 qparams 仅权重 qconfig_4bit",
"默认 quint8 权重 qconfig",
"默认 qat_qconfig",
"默认动态 qat_qconfig",
"默认仅权重 qconfig",
"默认激活仅 Qconfig",
"默认 QAT Qconfig v2",
"默认重用输入 Qconfig",
"默认对称 Qnnpack Qconfig",
"默认_per_channel 对称_qnnpack_qconfig",
"默认对称_qnnpack_qat_qconfig",
"默认_per_channel 对称_qnnpack_qat_qconfig",
"默认嵌入_qat_qconfig",
默认嵌入量化 QAT 配置 4 位,
"获取默认 Qconfig",
"获取默认 QAT Qconfig",
"获取默认 Qconfig 字典",
"获取默认 QAT Qconfig 字典",
"Q 配置任意",
"qconfig 等于",
]
[文档]class QConfig(namedtuple("QConfig", ["activation", "weight"])):
```python
# 假设输入文本为:
input_text = '"""'
# 翻译函数(此处仅为示例,实际翻译功能需要调用真实的翻译 API)
def translate_to_simplified_chinese(text):
# 这里应该调用真实的翻译 API 进行翻译
# 由于示例中不使用真实的 API,以下为模拟翻译结果
return text
# 输出翻译结果
translated_text = translate_to_simplified_chinese(input_text)
print(translated_text)
```
描述了如何通过提供来量化一个层或网络的一部分
激活和权重分别的设置(观察器类)
注意,QConfig 需要包含观察器 **类**(如 MinMaxObserver)或一个在调用时返回实例的可调用对象。
量化准备函数将为每一层的每个观察器实例化多次。
量化准备函数将为每一层的每个观察器实例化多次。
观察者类通常具有合理的默认参数,但可以使用 `with_args` 进行覆盖
方法(行为类似于 functools.partial)::
my_qconfig = QConfig(
激活=MinMaxObserver.with_args(dtype=torch.qint8),
weight=default_observer.with_args(dtype=torch.qint8))
"""
__slots__ = ()
def __new__(cls, activation, weight):
捕捉常见错误
如果 isinstance(activation, nn.Module) 或 isinstance(weight, nn.Module):
raise ValueError(
"QConfig 接收了观察者实例,请传递观察者类。"
使用 `MyObserver.with_args(x=1)` 来覆盖构造函数的参数(如有需要)
)
返回 super().__new__(cls, activation, weight)
@deprecated(
`QConfigDynamic` 将在 PyTorch 1.12 中被弃用,请使用 `QConfig` 代替,
分类=
未来警告,
)
类 QConfigDynamic(
命名元组(
"Q 配置动态", [
激活,
权重])):
""
描述如何通过提供信息动态量化层或网络的一部分
权重设置(观察者类)
它类似于 QConfig,但用于动态量化。
注意,QConfigDynamic 需要包含观察者**类**(如 MinMaxObserver)或一个在调用时返回实例的可调用对象,而不是具体的观察者实例本身。
注意,QConfigDynamic 需要包含观察者**类**(如 MinMaxObserver)或一个在调用时返回实例的可调用对象,而不是具体的观察者实例本身。
量化函数将为每一层实例化观察者多次。
观察者类通常有合理的默认参数,但可以使用 `with_args` 方法(类似于 functools.partial)进行覆盖
方法(行为类似于 functools.partial)::
my_qconfig = QConfigDynamic(weight=default_observer.with_args(dtype=torch.qint8))
"文档"
__slots__ = ()
def __new__(类,
激活=
火把.
神经网络.
身份,
重量=
火把.
神经网络.
身份):
# 捕捉常见错误
如果 isinstance(
重量,
神经网络.
模块):
raise ValueError(
"QConfigDynamic 接收到观察者实例,请传递观察者类。"
+ "如需覆盖构造函数的参数,请使用 MyObserver.with_args(x=1)。"
)
返回
超级().__new__(
类,
激活,
重量)
默认_qconfig = QConfig(
激活=
默认观察者,
重量=
默认权重观察者)
""
默认 qconfig 配置。
""
默认调试 qconfig =
Q 配置(
重量=
默认权重观察者,
激活=
默认调试观察者
)
""
默认的调试 qconfig 配置。
""
默认每通道 qconfig =
Q 配置(
激活=
默认观察者,
重量=
默认每通道权重观察者
)
""
每通道权重量化的默认 qconfig 配置。
""
default_dynamic_qconfig = Q 配置(
激活=
默认动态量化观察者,
重量=
默认权重观察者
)
""
默认动态 qconfig。
""
float16 动态 qconfig =
Q 配置(
激活=
占位符观察者.
带参数(
数据类型=
火把.float16,
动态的=
是),
重量=
占位符观察者.
带参数(
数据类型=
火把.float16),
)
""
动态 qconfig,权重量化为`torch.float16`。
""
float16 静态 qconfig =
Q 配置(
激活=
占位符观察者.
带参数(
数据类型=
火把.float16),
重量=
占位符观察者.
带参数(
数据类型=
火把.float16),
)
""
动态 qconfig,激活和权重均量化为`torch.float16`。
""
每通道动态量化配置 = QConfig(
激活=
默认动态量化观察者,
重量=
默认通道权重观察者,
)
""
每通道量化权重动态 qconfig。
""
float_qparams_weight_only_qconfig = Q 配置(
激活=
默认占位符观察者,
重量=
默认浮点零点观察者
)
""
使用浮点数 zero_point 量化的权重动态 qconfig
""
仅权重浮点 Q 参数_4 位 Q 配置 =
Q 配置(
激活=
默认占位符观察者,
重量=
默认浮点 Q 参数观察者_4 位
)
默认 QAT 配置 =
Q 配置(
激活=
默认伪量化,
重量=
默认权重伪量化
)
""
QAT 的默认 qconfig。
""
默认动态 QAT 配置 =
Q 配置(
激活=
默认动态伪量化,
重量=
默认权重伪量化
)
""
动态 QAT 的默认 qconfig。
""
默认仅权重权重配置 = QConfig(
激活=
火把.
神经网络.
身份,
重量=
默认权重伪量化
)
""
仅对权重进行量化的默认 qconfig。
""
默认激活量化配置 =
Q 配置(
激活=
默认假量化,
重量=
火把.
神经网络.
身份
)
""
仅对激活进行量化的默认 qconfig。
""
使用融合观察器和模拟量化模块以优化训练性能的 QAT 配置。
要修改激活/权重观察器,可以修改 fake_quantize.py 中的默认条目。
默认_qat_qconfig_v2 =
Q 配置(
激活=
默认融合激活假量化,
重量=
默认融合权重伪量化
)
""
`default_qat_config`的融合版本,具有性能优势。
""
默认重用输入 QConfig = QConfig(
激活=
默认重用输入观察者,
重量=NoopObserver
)
""
默认 qconfig 配置,用于重用输入张量中的观察者,例如 reshape
""
def 获取默认 qconfig(
后端="x86",
版本=0):
""
返回指定后端的默认 PTQ qconfig。
参数:
* `backend` (str): 表示目标后端的字符串。目前支持
`x86`(默认),`fbgemm`,`qnnpack` 和 `onednn`。
返回:
qconfig
"文档"
支持的后端 = [
fbgemm,
x86,
qnnpack,
onednn]
如果
后端
不
在
支持的后端:
raise 断言错误(
后端:
+ 字符串(
后端)
+ f"不支持。后端必须是以下之一"{
支持的后端}"
)
如果
版本 == 0:
如果
后端 == "fbgemm":
qconfig = Q 配置(
激活=
历史图观察者.
带参数(
范围缩减=
是),
重量=
默认通道权重观察者,
)
elif 后端 ==
qnnpack:
# TODO: 使其与 xnnpack 约束兼容
qconfig = Q 配置(
激活=
历史图观察者.
带参数(
范围缩减=
错误),
重量=
默认权重观察者,
)
elif 后端 ==
onednn:
如果
不
火把.cpu.
支持 VNNI 吗():
警告.
警告(
"默认的 oneDNN 后端 qconfig,reduce_range 设置为 false 时可能存在精度问题"
"在未支持向量神经网络指令的 CPU 上。"
)
qconfig = QConfig(
激活=
历史观测器.with_args(
范围缩减=
错误),
重量=
默认每通道权重观察者,
)
elif 后端 ==
x86:
qconfig = Q 配置(
激活=
历史图观察者.
带参数(
范围缩减=
是),
重量=
默认通道权重观察者,
)
否则:
# 不会到达
qconfig = 默认_qconfig
否则:
raise 断言错误(
"版本号: "
+ 字符串(
版本)
+ 在 get_default_qconfig 中不支持。版本号必须是 0
)
返回 qconfig
""
默认,针对指定后端的对称 PTQ qconfig。以及相同的每个通道的变体。
同样,这里指的是具有零点=0 的符号权重,以及额外的
对称性适用于零点=0 的符号权重,以及额外的
值限制。激活也是使用此签名的 8 位整数
qconfig。
*一旦此更改合并[截至 2022 年 3 月 17 日],如果后端或 qengine 等于
'qnnpack',则可能使用此对称 qconfig 的一些量化算子
xnnpack 库中的算子。
** 支持使用由`qnnpack`支持的 xnnpack 算子进行非对称 qconfig(由 get_default_qconfig()返回)。
qconfig(由 get_default_qconfig()返回)尚不可用。
* 此 qconfig 使用有符号激活和权重。权重已添加。
限制,例如零点被强制设为 0,使得权重
对称,因此得名。8 位量化值
限制为[-127, +127],排除-128。
xnnpack 有一个量化和缩放值限制,0x1p-32 <=
requantization_scale < 256.0 其中,`requantization_scale = (input_scale * kernel_scale) / (output_scale)`。使用此 eps(假设最大值为 256)是为了防止 requantization_scale 低于 xnnpack 的阈值。
* kernel_scale) / (output_scale)`. 使用此 eps(w/ assumed max value
of 256)是为了防止 requantization_scale 低于 xnnpack 的阈值。
。
""
默认对称 qnnpack_qconfig =
Q 配置(
激活=
直方图观察者.
带参数(
数据类型=
火把.qint8,
范围缩减=
错误, eps=2**-12
),
重量=
范围观察者_neg_127_to_127,
)
默认_per_channel_symmetric_qnnpack_qconfig =
Q 配置(
激活=
直方图观察者.
带参数(
数据类型=
火把.qint8,
范围缩减=
错误, eps=2**-12
),
重量=
每通道权重观察者范围-127 到 127,
)
默认嵌入量化 Q 配置 =
Q 配置(
激活=
Noop 观察者.
带参数(
数据类型=
火把.float32),
重量=
默认嵌入伪量化,
)
默认嵌入 QAT 配置 4 位 =
Q 配置(
激活=
Noop 观察者.
带参数(
数据类型=
火把.float32),
重量=
默认嵌入伪量化 4 位,
)
默认量化权重配置 quint8 = QConfig(
激活=
直方图观察者,
重量=MinMaxObserver
)
def 获取默认 QAT QConfig(
后端="x86",
版本=1):
""
返回指定后端的默认 QAT qconfig。
参数:
* `backend` (str): 表示目标后端的字符串。目前支持
`x86`(默认),`fbgemm`,`qnnpack` 和 `onednn`。
`版本`: 版本,用于向后兼容。可以是 `None` 或 `1`。
返回:
qconfig
"文档"
支持的后端 = [
fbgemm,
x86,
qnnpack,
onednn]
如果
后端
不
在
支持的后端:
raise 断言错误(
"后端:"
+ 字符串(
后端)
+ f" 不支持。后端必须是以下之一"{
支持的后端}"
)
# 直方图观察者在量化感知训练中速度太慢
如果
版本 == 0:
如果
后端 ==
fbgemm:
qconfig = QConfig(
激活=
伪量化.
带参数(
观察者=
移动平均最小最大观察者,
量化最小值=0,
量化最大值=255,
范围缩减=
是,
),
重量=
默认通道权重假量化,
)
elif 后端 ==
qnnpack:
qconfig = QConfig(
激活=
伪量化.
带参数(
观察者=
移动平均最小最大观察者,
量化最小值=0,
量化最大值=255,
范围缩减=
错误,
),
重量=
默认权重伪量化,
)
elif 后端 ==
onednn:
qconfig = Q 配置(
激活=
模拟量化.
带参数(
观察者=
移动平均最小最大观察者,
量化最小值=0,
量化最大值=255
),
重量=
默认通道权重伪量化,
)
elif 后端 ==
x86:
qconfig = QConfig(
激活=
伪量化.
带参数(
观察者=
移动平均最小最大观察者,
量化最小值=0,
量化最大值=255,
范围缩减=
是,
),
重量=
默认每通道权重假量化,
)
否则:
qconfig = 默认 QAT 配置
使用融合的观察+假量化模块进行量化加速训练(QAT)。
elif 版本 == 1:
如果
后端 ==
fbgemm:
qconfig = QConfig(
激活=
混合移动平均观测器伪量化.
带参数(
观测器=
移动平均最小最大观测器,
量化最小值=0,
量化最大值=255,
范围缩减=
是,
),
重量=
默认融合每通道权重假量化,
)
elif 后端 ==
qnnpack:
# TODO: 使其符合 xnnpack 约束
qconfig = QConfig(
激活=
混合移动平均观测伪量化.
带参数(
观察者=
移动平均最小最大观察者,
量化最小值=0,
量化最大值=255,
范围缩减=
错误,
),
重量=
默认融合_wt_假量化,
)
elif 后端 ==
onednn:
qconfig = Q 配置(
激活=
混合移动平均观测伪量化.
带参数(
观察者=
移动平均最小最大观察者,
量化最小值=0,
量化最大值=255
),
重量=
默认融合每通道权重假量化,
)
elif 后端 ==
x86:
qconfig = QConfig(
激活=
混合移动平均观测伪量化.
带参数(
观察者=
移动平均最小最大观察者,
量化最小值=0,
量化最大值=255,
范围缩减=
是,
),
重量=
默认融合通道权重假量化,
)
否则:
配置文件 =
默认_qat_qconfig_v2
否则:
raise 断言错误(
"版本号:"
+ 字符串(
版本)
+ "在 get_default_qat_qconfig 中不支持。版本号必须是 0 或 1"
)
返回 qconfig
""
qnnpack 的默认对称 QAT qconfig 及其通道权重变体。
""
default_symmetric_qnnpack_qat_qconfig = QConfig(
激活=
混合移动平均观测伪量化.
带参数(
观察者=
移动平均最小最大观察者,
量化最小值=-128,
量化最大值=127,
数据类型=
火把.qint8,
范围缩减=
错误,
eps=2**-12,
),
重量=
融合_wt_假量化范围_neg_127_to_127,
)
默认通道对称 qnnpack 量化 qconfig =
Q 配置(
激活=
混合移动平均观察伪量化.
带参数(
观察者=
移动平均最小最大观察者,
量化最小值=-128,
量化最大值=127,
数据类型=
火把.qint8,
范围缩减=
错误,
eps=2**-12,
),
重量=
混合通道加权伪量化范围-127 到 127,
)
默认_fp32 占位符_qconfig =
Q 配置(
激活=
占位符观察者.with_args(
数据类型=
火把.float32),
重量=
占位符观察者.with_args(
数据类型=
火把.float32),
)
_默认_quint8 占位符_qconfig =
Q 配置(
激活=
占位符观察者.
带参数(
数据类型=
火把.quint8),
使用此 qconfig 的运算符没有权重
重量=
无,
)
@deprecated(
`torch.ao.quantization.get_default_qconfig_dict` 已弃用,将在未来的版本中删除
请使用 `torch.ao.quantization.get_default_qconfig_mapping` 代替。,
分类=
未来警告,
)
def get_default_qconfig_dict(后端=
x86,
版本=0):
返回
火把.ao.
量化.
获取默认量化配置映射(
后端,
版本).to_dict()
@deprecated(
"`torch.ao.quantization.get_default_qat_qconfig_dict` 已弃用,并将在未来版本中删除。请使用 `torch.ao.quantization.get_default_qat_qconfig_mapping` 代替。"
"在未来版本中。请使用 `torch.ao.quantization.get_default_qat_qconfig_mapping` 代替。",
分类=
未来警告,
)
def get_default_qat_qconfig_dict(后端=
x86,
版本=1):
返回
火把.ao.
量化.
获取默认 QAT_QCONFIG 映射(
后端,
版本
).to_dict()
def _assert_valid_qconfig(qconfig: 可选[QConfig
],
模块:
火把.
神经网络.
模块) ->
无:
""
验证此 `qconfig` 是否有效。
"文档"
如果 qconfig
是
无:
返回
是否是卷积转置模块 = isinstance(
模块,
(火把.
神经网络.
卷积转置 1d,
火把.
神经网络.
卷积转置 2d,
火把.
神经网络.ConvTranspose3d),
)
如果
是卷积转置模块:
如果 qconfig.
权重
是
无:
目前假设任何没有权重的 ConvTranspose 的 qconfig 都是有效的
返回
example 观察者 =
qconfig 配置.
重量()
每通道 = isinstance(
example 观察者,
(
火把.ao.
量化.
PerChannelMinMax 观察者,
火把.ao.
量化.
移动平均通道最大最小观察者,
),
)
断言 (
不
每通道
), "通道权重观察者尚未支持 ConvTranspose"{n}d."
QConfigAny = 可选[QConfig]
QConfigAny.__module__ = "torch.ao.quantization.qconfig"
def 添加模块到 qconfig_obs_ctr(
qconfig: QConfigAny, 模块:
可选[
神经网络.
模块]
) -> 任何:
r这是一个用于量化准备的帮助函数,用于更新 qconfig,以便存储在 qconfig 中的构造函数将在'module'所在的同一设备上创建观察者。
这是为了在 qconfig 传播到每个模块时使用,以避免潜在的设备对齐问题。
这是为了在 qconfig 传播到每个模块时使用,以避免潜在的设备对齐问题。
这是为了在 qconfig 传播到每个模块时使用,以避免潜在的设备对齐问题。
参数:
qconfig:存储在激活和权重中的 QConfig,具有 obs 构造函数
模块:与 qconfig 相关的模块
返回:
qconfig:配置为将 obs 构造函数设置在模块相同的设备上构建
"文档"
如果
模块
是
无
或者 qconfig
是
无
或者 qconfig.
字段列表 != (
激活,
重量):
返回 qconfig
def 根据模块和设备获取工厂参数的键值对():
断言 isinstance(
模块,
火把.
神经网络.
模块)
设备 = {p.
设备 for p
在
模块.
参数()} | {
p.设备 for p
在
模块.
缓冲区()
}
设备 =
下一(
迭代(
设备))
如果
长度(
设备) > 0
否则
无
返回
无
如果
设备
是
无
否则 {
"设备":
设备}
def 配置构造函数以将观察者放在模块设备上(
原始构造函数):
尝试:
# 检查构造函数是否可以接受 factory_kwargs
检查 =
原始构造函数.
带参数(
工厂参数=
无)
检查()
返回
原始构造函数.
带可调用参数(
工厂参数=
获取基于模块和设备的工厂参数
)
除了
属性错误:
# qconfig 没有激活或权重
返回
原始构造函数
除了
类型错误:
# 该类不接受 factory_kwargs 参数
返回
原始构造函数
激活 =
配置构造函数以将观察者放在模块设备上(
q 配置.
激活)
权重 =
配置构造函数以将观察者放在模块设备上(qconfig.
重量)
返回 QConfig(
激活,
重量)
_观察者或伪量化构造函数 =
联盟[
_部分包装器,
类型[
观察者基类
],
类型[
模拟量化基类]
]
def _观测或全频率控制等于(
obs_or_fq1: _观察者或伪量化构造函数,
obs_or_fq2: _观察者或伪量化构造函数,
):
如果 isinstance(obs_or_fq1,
_部分包装器)
并且 isinstance(
obs_or_fq2, _部分包装器
):
返回
_部分包装器等于(
观测或_fq1,
观测或_fq2)
返回
观测或_fq1 ==
观测或_fq2
def _部分包装等于(
观测或_fq1:
_部分包装,
观测或_fq2:
部分包装):
""
返回两个部分包装器是否相等
"文档"
functools.partial 没有定义__eq__运算符,所以'=='默认为'is'
obs_or_fq1_keywords = 复制.
复制(obs_or_fq1.p.
关键词)
观测或 FQ2 关键词 =
复制.
复制(
观测或 FQ2.p.
关键词)
关键词等于 =
真实
将观察者构造函数与_obs_or_fq_ctr_equals 进行比较,因为直接比较会失败
如果
观察者
在 obs_or_fq1_keywords
并且
观察者
在
观测或 FQ2 关键词:
关键词相等 =
关键词相等
并且
观测或全称控制等于(
观测或全称 1 关键词[
观测者
],
观测或全称 2 关键词[
观察者]
)
obs_or_fq1_关键词.
流行(
观察者)
obs_or_fq2_关键词.
流行(
观察者)
关键词相等 =
关键词等于
并且
观测或 FQ1 关键词 ==
观测或 FQ2 关键词
返回 (
观测或_fq1.p.
函数 ==
观测或_fq2.p.
函数
并且
观测或_fq1.p.args ==
观测或_fq2.p.args
并且
关键词相等
)
def qconfig_equals(q1: QConfigAny, q2: QConfigAny):
""
如果 `q1` 等于 `q2`,则返回 `True`,否则返回 `False`。
"文档"
如果 q1
是
无
或者 q2
是
无:
返回 q1 == q2
否则:
断言 q1
是
不
无
并且 q2
是
不
无
尝试:
# Qconfig 权重和激活可以是部分包装,
# 或者观察者类。需要特殊处理(上面所述)。
比较部分包装
激活相同 =
_观察或全频段计数器相等(q1.
激活, q2.
激活)
重量相同 =
观测或全频段计数器等于(q1.
重量, q2.
重量)
返回
激活相同
并且
重量相同
除了
属性错误:
返回
问题 1 ==
问题 2
def 激活函数是无记忆的(qconfig: QConfig):
""
返回是否在给定的 QConfig 中定义的激活观察者是记忆的。
这意味着平均常数为 1 的移动平均观察者。
"文档"
def _is_memoryless(观察者):
返回 (
有属性(
观察者,
平均常数)
并且
观察者.
平均常数 == 1
)
动作 =
配置.
激活()
如果 isinstance(
动作,
模拟量化基类)
并且
有属性(
动作,
激活后处理):
返回
_无记忆(
动作.
激活后处理)
否则:
返回
_无记忆(
动作)
def _重用输入 q 配置(qconfig:
可选[QConfig
)]
返回 (
qconfig 是
不
无
并且 isinstance(qconfig.activation(), ReuseInputObserver)
并且 isinstance(qconfig.
重量(), NoopObserver)
)