# mypy: 允许未类型化定义
导入
警告
来自
打字
导入
可选
导入
火炬
导入 torch.jit
# 需要避免循环导入
导入 torch.nn.functional
作为 F
来自
火炬
导入 nn,
张量
__all__ = ["MultiheadAttention"]
[文档]
类
多头注意力(nn.
多头注意力):
浮点模块 = nn.
多头注意力
r可量化的 MultiheadAttention 实现。
注意::
请参阅::class:`~torch.nn.MultiheadAttention` 了解更多
信息
允许模型联合关注来自不同表示子空间的信息。
代表子空间。
参考文献:《Attention Is All You Need》
原始的 MHA 模块是不可量化的。
通过显式实例化线性层来实现它。
.. math::
\text{MultiHead}(Q, K, V) = \text{Concat}(head_1,\dots,head_h)W^O
\text{其中} head_i = \text{Attention}(QW_i^Q, KW_i^K, VW_i^V)
参数:
embed_dim: 模型的总维度。
num_heads: 并行注意力头。
dropout: 在 attn_output_weights 上添加 Dropout 层。默认:0.0。
bias: 将偏置作为模块参数添加。默认:True。
add_bias_kv: 在 dim=0 维度上向 key 和 value 序列添加偏置。
add_zero_attn: 向 key 添加一个新的全零批次。
dim=1 的值序列。
kdim:键中特征的总数。默认:None。
vdim:值中特征的总数。默认:None。
batch_first:如果为 ``True``,则输入和输出张量以(batch,seq,feature)的形式提供。
默认:``False``(seq,batch,feature)。
注意,如果:attr:`kdim` 和:attr:`vdim` 为 None,它们将被设置为
attr:`embed_dim`,以便查询、键和值具有相同的
特征数量。
示例:
>>> 导入 torch.ao.nn.quantizable 作为 nnqa
>>> multihead_attn = nnqa.MultiheadAttention(embed_dim, num_heads)
>>> attn_output, attn_output_weights = multihead_attn(query, key, value)
注意::
请遵循量化流程以转换可量化的 MHA。
```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)
```
常量 = ["batch_first"]
def 初始化(
自身,
嵌入维度: int,
头数: int,
dropout: 浮点数 = 0.0,
偏差:
布尔类型 = True,
添加偏置 kv:
布尔类型 =
错误,
添加零注意力:
布尔类型 =
错误,
kdim: 可选[int] =
无,
vdim: 可选[int] =
无,
批量优先:
布尔类型 =
错误,
设备=
无,
数据类型=
无,
) -> 无:
工厂参数 = {
"设备":
设备, "dtype":
数据类型}
超级().
初始化(
嵌入维度,
头数,
dropout,
偏差,
添加偏置 kv,
添加零注意力,
kdim,
vdim,
批量优先,
**工厂参数,
)
自身.
线性_Q = nn.
线性(
自身.
嵌入维度,
自身.
嵌入维度,
偏差=
偏差, **
工厂参数
)
自身.
线性_K = nn.
线性(
自身.kdim,
自身.
嵌入维度,
偏差=
偏差, **
工厂参数
)
自身.
线性_V = nn.
线性(
自身.vdim,
自身.
嵌入维度,
偏差=
偏差, **
工厂参数
)
# 对于类型:忽略,请参阅 https://github.com/pytorch/pytorch/issues/58969
自身.out_proj = nn.
线性(
自身.
嵌入维度,
自身.
嵌入维度,
偏差=
偏差, **
工厂参数)
# 类型:忽略[赋值]
功能性
自身.q_scaling_product =
火炬.ao.nn.
量化.
浮点功能()
# 注意:在顶部导入 torch.ao.nn.quantized 会导致循环导入
# 量化/反量化
自身.
量化注意力输出 =
火炬.ao.
量化.
量化占位符()
自身.
量化注意力输出权重 =
火炬.ao.
量化.
量化占位符()
自身.dequant_q =
火炬.ao.
量化.DeQuantStub()
自身.dequant_k =
火炬.ao.
量化.DeQuantStub()
自身.dequant_v =
火炬.ao.
量化.DeQuantStub()
def _获取名称(
自身):
返回
可量化的多头注意力
@classmethod
def 从浮点数(
类,
其他):
断言
类型(
其他) ==
类.
浮点模块
断言
有属性(
其他,
qconfig),
"浮点模块必须包含 'qconfig'"
# 将 dropout 设置为 0.0!
观察到 =
类(
其他.
嵌入维度,
其他.
头数,
其他.dropout,
(其他.in_proj_bias
是 not
无),
(其他.
偏差_k
是 not
无),
其他.
添加零注意力,
其他.kdim,
其他.vdim,
其他.
批量优先,
)
观察到.
偏差_k =
其他.
偏差_k
观察.
偏置_v =
其他.
偏置_v
观察.qconfig =
其他.qconfig
设置线性权重
关于类型:忽略,请参阅 https://github.com/pytorch/pytorch/issues/58969
观察.out_proj.
权重 =
其他.out_proj.
权重
观察.out_proj.bias =
其他.out_proj.bias
如果
其他._qkv_same_embed_dim:
# 使用单独的参数
bias = 其他.in_proj_bias
_start = 0
_end = _start + 其他.
嵌入维度
权重 =
其他.
输入投影权重[_start:
结束,
]
如果 bias
是 not
无:
bias = 火炬.nn.
参数(
偏差[_start:
结束
]
偏差.
需要梯度)
观察.
线性_Q.
权重 =
火炬.nn.
参数(
重量,
重量.
需要梯度)
观察.
线性_Q.bias = bias
bias = 其他.in_proj_bias
_start = _end
_end = _start + 其他.
嵌入维度
权重 =
其他.
输入投影权重[_start:
结束,
]
如果 bias
是 not
无:
bias = 火炬.nn.
参数(
偏差[_start:
结束
]
偏差.
需要梯度)
观察.
线性_K.
权重 =
火炬.nn.
参数(
重量,
重量.
需要梯度)
观察.
线性_K.bias = bias
bias = 其他.in_proj_bias
_start = _end
权重 =
其他.
输入投影权重[_start:,
]
如果 bias
是 not
无:
bias = 火炬.nn.
参数(
偏差[_start
,
偏差.
需要梯度)
观察.
线性_V.
权重 =
火炬.nn.
参数(
重量,
重量.
需要梯度)
观察.
线性_V.bias = bias
否则:
观察.
线性_Q.
权重 = nn.
参数(
其他.q_proj_weight)
观察.
线性_K.
权重 = nn.
参数(
其他.k_proj_weight)
观察.
线性_V.
权重 = nn.
参数(
其他.
v_项目权重)
如果
其他.in_proj_bias
是
无:
观察.
线性_Q.bias = None
观察.
线性_K.bias = None
观察.
线性_V.bias = None
否则:
观察.
线性_Q.bias = nn.
参数(
其他.
输入投影偏置[0 :
其他.
嵌入维度]
)
观察.
线性_K.bias = nn.
参数(
其他.
输入投影偏置[
其他.
嵌入维度 : (
其他.
嵌入维度 * 2
]
)
观察.
线性_V.bias = nn.
参数(
其他.
输入投影偏置
[
其他.
嵌入维度 * 2)
]
)
观察.
评估()
明确准备
观察 =
火炬.ao.
量化.prepare(
观察,
内置=True)
返回
观察
[文档] @torch.
算子.
未使用
def 反量化(
自身):
r将量化 MHA 转换回浮点数的工具。
做这件事的动机在于,将量化版本中使用的格式转换回权重并不简单。
将量化版本中使用的格式转换回权重的操作并不简单。
浮点数.
```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)
```
文件指针 =
自身.
浮点模块(
自身.
嵌入维度,
自身.
头数,
自身.dropout,
(自身.
线性_Q.
_权重偏置()[1]
是 not
无), # type: ignore[operator]
(自身.
偏差_k
是 not
无),
自身.
添加零注意力,
自身.kdim,
自身.vdim,
自身.
批量优先,
)
断言 fp._qkv_same_embed_dim ==
自身._qkv_same_embed_dim
如果
自身.
偏差_k
是 not
无:
fp.偏差_k = nn.
参数(
自身.
偏置_k.
反量化())
如果
自身.
偏置_v
是 not
无:
fp.偏置_v = nn.
参数(
自身.
偏置_v.
反量化())
# 设置线性权重
因为线性层被量化,mypy 不知道如何处理它们——可能需要忽略类型检查。
可能需要忽略类型检查。
关于 type: ignore[has-type],请参阅 https://github.com/pytorch/pytorch/issues/58969。
w, b = 自身.out_proj._weight_bias() # type: ignore[operator, has-type]
fp.out_proj.权重 = nn.
参数(w.
反量化())
如果 b
是 not
无:
fp.out_proj.bias = nn.参数(b)
wQ, bQ = 自身.
线性_Q.
权重偏置() # type: ignore[operator]
wQ = wQ.反量化()
wK, bK = 自身.
线性_K.
_权重偏置() # type: ignore[operator]
wK = wK.反量化()
wV, bV = 自身.
线性_V.
权重偏置() # type: ignore[operator]
wV = wV.反量化()
如果 fp._qkv_same_embed_dim:
使用单独的参数
_开始 = 0
_结束 =
_开始 + fp.
嵌入维度
fp.输入投影权重[_start:
结束,
] = wQ
如果 fp.in_proj_bias
是 not
无:
断言
所有(bQ == 0)
fp.输入投影偏置[_start:
结束] = bQ
_start = _end
_end = _开始 + fp.
嵌入维度
fp.输入投影权重[_start:
_结束,
] = wK
如果 fp.in_proj_bias
是 not
无:
断言
所有(bK == 0)
fp.输入投影偏置[_start:
结束] = bK
开始 =
结束
fp.输入投影权重[_start:,
] = wV
如果 fp.in_proj_bias
是 not
无:
断言
所有(bV == 0)
fp.输入投影偏置[_start
] = bV
否则:
fp.q_proj_weight = nn.参数(wQ)
fp.k_proj_weight = nn.参数(wK)
fp.v_proj_weight = nn.参数(wV)
如果 fp.in_proj_bias
是
无:
自身.
线性_Q.bias = None
自身.
线性_K.bias = None
自身.
线性_V.bias = None
否则:
fp.输入投影偏置[0 : fp.
嵌入维度] = bQ
fp.输入投影偏置[fp.
嵌入维度 : (fp.
嵌入维度 * 2
] = bK
fp.输入投影偏置
[fp.
嵌入维度 * 2)
] = bV
返回 fp
@classmethod
def 来自观察(
类,
其他):
整个流程是浮点数 -> 观察到 -> 量化
这个类只做浮点数 -> 观察到
请参阅 nn.quantized.MultiheadAttention
抛出异常
不支持的操作异常(
看起来您正在尝试准备一个
"MHA 模块。请参阅"
"可量化的 MHA 示例。"
)
[文档] def
前向(
自身,
查询:
张量,
键:
张量,
值:
张量,
key_padding_mask: 可选[
张量] =
无,
需要的权重:
布尔类型 = True,
attn_mask: 可选[
张量] =
无,
平均注意力权重:
布尔类型 = True,
因果:
布尔类型 =
错误,
) -> 元组[
张量,
可选[
张量
]]
r""
注意::
请参阅 :func:`~torch.nn.MultiheadAttention.forward` 获取更多信息
信息
参数:
查询、键、值:将查询和一组键值对映射到输出。
请参阅“Attention Is All You Need”以获取更多详细信息。
key_padding_mask:如果提供,指定键中的填充元素
当给定一个二进制掩码且值为 True 时,将被注意力忽略。
对应的注意力层上的值将被忽略。
需要权重:输出注意力输出权重。
attn_mask:2D 或 3D 掩码,用于阻止对某些位置的注意力。2D 掩码将广播到所有
批处理时,3D 掩码允许为每个批次的条目指定不同的掩码。
形状:
- 输入:
- 查询::math:`(L, N, E)` 其中 L 是目标序列长度,N 是批大小,E 是
嵌入维度。:math:`(N, L, E)` 如果 ``batch_first`` 为 ``True``。
- key: (S, N, E),其中 S 是源序列长度,N 是批量大小,E 是嵌入维度。如果 batch_first 为 True,则为(N, S, E)。
嵌入维度。如果 batch_first 为 True,则为(N, S, E)。
- value: (S, N, E)其中 S 是源序列长度,N 是批量大小,E 是嵌入维度。如果 batch_first 为 True,则为(N, S, E)。
嵌入维度。如果 batch_first 为 True,则为(N, S, E)。
key_padding_mask: :math:`(N, S)` 其中 N 是批大小,S 是源序列长度。
如果提供一个 BoolTensor,则值为 ``True`` 的位置将被忽略,而值为 ``False`` 的位置将保持不变。
值为 ``True`` 的位置将被忽略,而值为 ``False`` 的位置将保持不变。
- attn_mask:2D 掩码:math:`(L, S)`,其中 L 为目标序列长度,S 为源序列长度。
3D 掩码:math:`(N*num_heads, L, S)`,其中 N 为批大小,L 为目标序列长度,
S 是源序列长度。attn_mask 确保位置 i 可以关注未掩码的
如果提供 BoolTensor,则位置与``True``相关。
不允许关注,而 ``False`` 值将保持不变。如果是一个 FloatTensor
它将被添加到注意力权重中。
- is_causal: 如果指定,则应用因果掩码作为注意力掩码。与提供 attn_mask 互斥。
默认:``False``。
- average_attn_weights: 如果为真,表示返回的 `attn_weights` 应该在所有注意力权重上进行平均
heads. 否则,每个 heads 分别提供 ``attn_weights``。请注意,此标志仅具有
当 `need_weights=True` 时的效果。默认:True(即平均头权重)
输出:
- attn_output: :math:`(L, N, E)` 其中 L 是目标序列长度,N 是批大小,
E 是嵌入维度。:math:`(N, L, E)` 如果 `batch_first` 为 `True`。
- attn_output_weights: 如果 ``average_attn_weights=True``,则返回平均的注意力权重
形状的头部::math:`(N, L, S)`,其中 N 是批量大小,L 是目标序列长度
S 是源序列长度。如果 `average_attn_weights=False`,则返回每个注意力权重。
形状的头部::math:`(N, num_heads, L, S)`。
```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)
```
返回
自身._forward_impl(
查询,
键,
值,
key_padding_mask,
需要的权重,
attn_mask,
平均注意力权重,
因果,
)
def _forward_impl(
自身,
查询:
张量,
键:
张量,
值:
张量,
key_padding_mask: 可选[
张量] =
无,
需要的权重:
布尔类型 = True,
attn_mask: 可选[
张量] =
无,
平均注意力权重:
布尔类型 = True,
因果:
布尔类型 =
错误,
) -> 元组[
张量,
可选[
张量
]]
此版本将不会处理静态键/值对。
保留此处以供将来更改。
#
TODO:此方法有一些重复的行,
`torch.nn.functional.multi_head_attention`。需要重构。
静态键 = None
static_v = None
如果
注意力掩码
是 not None
和
因果:
抛出异常
断言错误(
仅允许因果掩码或 attn_mask)
如果
因果:
抛出异常
断言错误(
"AO MHA 模块不支持因果掩码")
如果
自身.
批量优先:
查询,
键, value = (x.
转置(0, 1)
为 x
在 (
查询,
键,
值))
tgt_len, bsz, 嵌入维度检查 =
查询.
尺寸()
断言
自身.
嵌入维度 ==
嵌入维度检查
允许 MHA 具有不同大小的特征维度
断言
键.
尺寸(0) ==
值.
尺寸(0)
和
键.
尺寸(1) ==
值.
尺寸(1)
头维度 =
自身.
嵌入维度 //
自身.
头数
断言 (
头维度 *
自身.
头数 ==
自身.
嵌入维度
), "embed_dim 必须能被 num_heads 整除"
缩放 = float(
头维度) ** -0.5
q = 自身.
线性_Q(
查询)
k = 自身.
线性_K(
键)
v = 自身.
线性_V(
值)
q = 自身.
q 缩放乘积.
多标量(q,
扩展)
如果
注意力掩码
是 not
无:
如果 attn_mask.dtype ==
火炬.uint8:
warnings.警告(
"在 `nn.MultiheadAttention` 中的 `attn_mask` 的 `Byte tensor` 已弃用。"
"请使用布尔 tensor 代替。",
栈级别=3,
)
注意力掩码 = attn_mask.
到(
火炬.
布尔)
断言 (
attn_mask.is_floating_point() 或 attn_mask.dtype ==
火炬.
布尔类型
), f仅支持 float 和 bool 类型用于 attn_mask,不支持{attn_mask.
数据类型}"
如果 attn_mask.
暗淡() == 2:
注意力掩码 = attn_mask.
展平(0)
如果
列表(attn_mask.
尺寸()) != [1,
查询.
尺寸(0),
键.
尺寸(0
)
抛出异常
运行时错误(
"2D 注意力掩码的大小不正确。")
elif attn_mask.暗淡() == 3:
如果
列表(attn_mask.
尺寸()) != [
批大小 *
自身.
头数,
查询.
尺寸(0),
键.
尺寸(0),
]:
抛出异常
运行时错误(
"3D 注意力掩码的大小不正确。")
否则:
抛出异常
运行时错误(
f注意力掩码的维度{attn_mask.
暗淡()}
不受支持"
)
# 现在注意力掩码的维度是 3。
将 ByteTensor 的 key_padding_mask 转换为布尔值
如果 key_padding_mask
是 not None
和 key_padding_mask.dtype ==
火炬.uint8:
warnings.警告(
"在`nn.MultiheadAttention`中,`key_padding_mask`的 Byte tensor 已被弃用。"
"请使用布尔 tensor 代替。",
栈级别=3,
)
key_padding_mask = key_padding_mask.到(
火炬.
布尔)
如果
自身.
偏差_k
是 not None
和
自身.
偏置_v
是 not
无:
如果
静态键
是 None
和 static_v
是
无:
明确断言 bias_k 和 bias_v 不为 None
以 TorchScript 可以理解的方式
偏差_k =
自身.
偏差_k
断言
偏差_k
是 not None
偏置_v =
自身.
偏置_v
断言
偏置_v
是 not None
k = 火炬.
猫([k,
偏置_k.
重复(1, bsz, 1
)]])
v = 火炬.
猫([v,
偏置_v.
重复(1, bsz, 1
)]])
如果
注意力掩码
是 not
无:
注意力掩码 = F.
填充(attn_mask, (0, 1))
如果 key_padding_mask
是 not
无:
key_padding_mask = F.填充(key_padding_mask, (0, 1))
否则:
断言
静态键
是
无,
"偏差不能添加到静态键。"
断言 static_v
是
无,
偏差不能添加到静态值。
否则:
断言
自身.
偏差_k
是 None
断言
自身.
偏置_v
是 None
q = q.连续().
视图(tgt_len,
批大小 *
自身.
头数,
头维度).
转置(0, 1)
如果 k
是 not
无:
k = k.连续().
视图(-1,
批大小 *
自身.
头数,
头维度).
转置(0, 1)
如果 v
是 not
无:
v = v.连续().
视图(-1,
批大小 *
自身.
头数,
头维度).
转置(0, 1)
如果
静态键
是 not
无:
断言
静态_k.
尺寸(0) ==
批大小 *
自身.
头数
断言
静态_k.
尺寸(2) ==
头维度
k = 静态键
如果 static_v
是 not
无:
断言
静态_v.
尺寸(0) ==
批大小 *
自身.
头数
断言
静态_v.
尺寸(2) ==
头维度
v = static_v
源长度 = k.
尺寸(1)
如果 key_padding_mask
是 not
无:
断言 key_padding_mask.
尺寸(0) ==
批大小
断言 key_padding_mask.
尺寸(1) ==
源长度
如果
自身.
添加零注意力:
源长度 += 1
零 =
火炬.
零((k.
尺寸(0), 1) + k.
尺寸()[2
])
如果 k.
是否量化:
零 =
火炬.
按张量量化(
零, k.q_scale(), k.q_zero_point(), k.dtype
)
k = 火炬.
猫([k,
零的个数
]
暗淡=1)
零的个数 =
火炬.
零((v.
尺寸(0), 1) + k.
尺寸()[2
])
如果 v.
是否量化:
零的个数 =
火炬.
按张量量化(
零的个数, v.q_scale(), v.q_zero_point(), v.dtype
)
v = 火炬.
猫([v, v_zeros
]
暗淡=1)
如果
注意力掩码
是 not
无:
注意力掩码 = F.
填充(attn_mask, (0, 1))
如果 key_padding_mask
是 not
无:
key_padding_mask = F.填充(key_padding_mask, (0, 1))
这里留下量化区域
q = 自身.
解量化_q(q)
k = 自身.
解量化_k(k)
v = 自身.dequant_v(v)
attn_output_weights = 火炬.bmm(q, k.
转置(1, 2))
断言
列表(
注意力输出权重.
尺寸()) == [
批大小 *
自身.
头数,
tgt_len,
源长度,
]
如果
注意力掩码
是 not
无:
如果 attn_mask.dtype ==
火炬.
布尔:
注意力输出权重.
遮蔽填充(attn_mask, float(
-无穷大))
否则:
attn_output_weights += 注意力掩码
如果 key_padding_mask
是 not
无:
attn_output_weights = 注意力输出权重.
视图(
bsz, 自身.
头数, tgt_len,
源长度
)
attn_output_weights = 注意力输出权重.
遮蔽填充(
key_padding_mask.展平(1).
展平(2),
float(-无穷大),
)
attn_output_weights = 注意力输出权重.
视图(
批大小 *
自身.
头数, tgt_len,
源长度
)
attn_output_weights = F.软式最大化(
注意力输出权重,
暗淡=-1)
attn_output_weights = F.dropout(
注意力输出权重, p=
自身.dropout,
训练=
自身.
训练
)
attn_output = 火炬.bmm(
注意力输出权重, v)
断言
列表(attn_output.
尺寸()) == [
批大小 *
自身.
头数, tgt_len,
头维度]
如果
自身.
批量优先:
attn_output = attn_output.视图(bsz, tgt_len,
自身.
嵌入维度)
否则:
attn_output = (
attn_output.转置(0, 1)
.连续()
.视图(tgt_len, bsz,
自身.
嵌入维度)
)
重新进入量化区域
attn_output = 自身.quant_attn_output(attn_output)
关于类型:忽略[has-type],请参阅 https://github.com/pytorch/pytorch/issues/58969
attn_output = 自身.out_proj(attn_output)
# 类型:忽略[has-type]
attn_output_weights = 自身.
量化注意力输出权重(
注意力输出权重)
如果
需要的权重:
头部平均注意力权重
attn_output_weights = 注意力输出权重.
视图(
bsz, 自身.
头数, tgt_len,
源长度
)
如果
平均注意力权重:
attn_output_weights = 注意力输出权重.
均值(
暗淡=1)
返回 attn_output, attn_output_weights
否则:
返回 attn_output,
无