10.3. 注意力评分函数

10.3. 注意力评分函数

注意力评分函数(attention scoring function), 简称评分函数(scoring function), 把这个函数的输出结果输入到softmax函数中进行运算。

由于注意力权重是概率分布, 因此加权和其本质上是加权平均值。

在这里插入图片描述

计算注意力汇聚的输出为值的加权和

import math
import torch
from torch import nn
from d2l import torch as d2l

10.3.1. 掩蔽softmax操作

softmax操作用于输出一个概率分布作为注意力权重。
在某些情况下,并非所有的值都应该被纳入到注意力汇聚中。
在下面的masked_softmax函数中 实现这样的掩蔽softmax操作(masked softmax operation), 其中任何超出有效长度的位置都被掩蔽并置为0。

#@save
def masked_softmax(X, valid_lens):
    """通过在最后一个轴上掩蔽元素来执行softmax操作"""
    # X:3D张量,valid_lens:1D或2D张量
    if valid_lens is None:
        return nn.functional.softmax(X, dim=-1)
    else:
        shape = X.shape
        if valid_lens.dim() == 1:
            valid_lens = torch.repeat_interleave(valid_lens, shape[1])
        else:
            valid_lens = valid_lens.reshape(-1)
        # 最后一轴上被掩蔽的元素使用一个非常大的负值替换,从而其softmax输出为0
        X = d2l.sequence_mask(X.reshape(-1, shape[-1]), valid_lens,
                              value=-1e6)
        return nn.functional.softmax(X.reshape(shape), dim=-1)

# 考虑由两个矩阵表示的样本, 这两个样本的有效长度分别为和。 经过掩蔽softmax操作,超出有效长度的值都被掩蔽为0。
masked_softmax(torch.rand(2, 2, 4), torch.tensor([2, 3]))

# result
tensor([[[0.5423, 0.4577, 0.0000, 0.0000],
         [0.6133, 0.3867, 0.0000, 0.0000]],

        [[0.3324, 0.2348, 0.4329, 0.0000],
         [0.2444, 0.3943, 0.3613, 0.0000]]])
    
# 同样,我们也可以使用二维张量,为矩阵样本中的每一行指定有效长度。
masked_softmax(torch.rand(2, 2, 4), torch.tensor([[1, 3], [2, 4]]))

# result
tensor([[[1.0000, 0.0000, 0.0000, 0.0000],
         [0.4142, 0.3582, 0.2275, 0.0000]],

        [[0.5565, 0.4435, 0.0000, 0.0000],
         [0.3305, 0.2070, 0.2827, 0.1798]]])

10.3.2. 加性注意力

一般来说,当查询和键是不同长度的矢量时, 我们可以使用加性注意力作为评分函数.

#@save
class AdditiveAttention(nn.Module):
    """加性注意力"""
    def __init__(self, key_size, query_size, num_hiddens, dropout, **kwargs):
        super(AdditiveAttention, self).__init__(**kwargs)
        self.W_k = nn.Linear(key_size, num_hiddens, bias=False)
        self.W_q = nn.Linear(query_size, num_hiddens, bias=False)
        self.w_v = nn.Linear(num_hiddens, 1, bias=False)
        self.dropout = nn.Dropout(dropout)

    def forward(self, queries, keys, values, valid_lens):
        queries, keys = self.W_q(queries), self.W_k(keys)
        # 在维度扩展后,
        # queries的形状:(batch_size,查询的个数,1,num_hidden)
        # key的形状:(batch_size,1,“键-值”对的个数,num_hiddens)
        # 使用广播方式进行求和
        features = queries.unsqueeze(2) + keys.unsqueeze(1)
        features = torch.tanh(features)
        # self.w_v仅有一个输出,因此从形状中移除最后那个维度。
        # scores的形状:(batch_size,查询的个数,“键-值”对的个数)
        scores = self.w_v(features).squeeze(-1)
        self.attention_weights = masked_softmax(scores, valid_lens)
        # values的形状:(batch_size,“键-值”对的个数,值的维度)
        return torch.bmm(self.dropout(self.attention_weights), values)

# 我们用一个小例子来演示上面的AdditiveAttention类, 其中查询、键和值的形状为(批量大小,步数或词元序列长度,特征大小), 实际输出为、和。 注意力汇聚输出的形状为(批量大小,查询的步数,值的维度)
queries, keys = torch.normal(0, 1, (2, 1, 20)), torch.ones((2, 10, 2))
# values的小批量,两个值矩阵是相同的
values = torch.arange(40, dtype=torch.float32).reshape(1, 10, 4).repeat(
    2, 1, 1)
valid_lens = torch.tensor([2, 6])

attention = AdditiveAttention(key_size=2, query_size=20, num_hiddens=8,
                              dropout=0.1)
attention.eval()
attention(queries, keys, values, valid_lens)

# result
tensor([[[ 2.0000,  3.0000,  4.0000,  5.0000]],

        [[10.0000, 11.0000, 12.0000, 13.0000]]], grad_fn=<BmmBackward0>)
        
# 尽管加性注意力包含了可学习的参数,但由于本例子中每个键都是相同的, 所以注意力权重是均匀的,由指定的有效长度决定。
d2l.show_heatmaps(attention.attention_weights.reshape((1, 1, 2, 10)),
                  xlabel='Keys', ylabel='Queries')

在这里插入图片描述

10.3.3. 缩放点积注意力

缩放点积注意力(scaled dot-product attention)评分函数.

在下面的缩放点积注意力的实现中,我们使用了暂退法进行模型正则化。

#@save
class DotProductAttention(nn.Module):
    """缩放点积注意力"""
    def __init__(self, dropout, **kwargs):
        super(DotProductAttention, self).__init__(**kwargs)
        self.dropout = nn.Dropout(dropout)

    # queries的形状:(batch_size,查询的个数,d)
    # keys的形状:(batch_size,“键-值”对的个数,d)
    # values的形状:(batch_size,“键-值”对的个数,值的维度)
    # valid_lens的形状:(batch_size,)或者(batch_size,查询的个数)
    def forward(self, queries, keys, values, valid_lens=None):
        d = queries.shape[-1]
        # 设置transpose_b=True为了交换keys的最后两个维度
        scores = torch.bmm(queries, keys.transpose(1,2)) / math.sqrt(d)
        self.attention_weights = masked_softmax(scores, valid_lens)
        return torch.bmm(self.dropout(self.attention_weights), values)

# 为了演示上述的DotProductAttention类, 我们使用与先前加性注意力例子中相同的键、值和有效长度。 对于点积操作,我们令查询的特征维度与键的特征维度大小相同。
queries = torch.normal(0, 1, (2, 1, 2))
attention = DotProductAttention(dropout=0.5)
attention.eval()
attention(queries, keys, values, valid_lens)

# result
tensor([[[ 2.0000,  3.0000,  4.0000,  5.0000]],

        [[10.0000, 11.0000, 12.0000, 13.0000]]])
        
# 与加性注意力演示相同,由于键包含的是相同的元素, 而这些元素无法通过任何查询进行区分,因此获得了均匀的注意力权重。
d2l.show_heatmaps(attention.attention_weights.reshape((1, 1, 2, 10)),
                  xlabel='Keys', ylabel='Queries')

在这里插入图片描述

10.3.4. 小结

  • 将注意力汇聚的输出计算可以作为值的加权平均,选择不同的注意力评分函数会带来不同的注意力汇聚操作。
  • 当查询和键是不同长度的矢量时,可以使用可加性注意力评分函数。当它们的长度相同时,使用缩放的“点-积”注意力评分函数的计算效率更高。
<think>嗯,用户给了一个INSERT INTO的SQL语句,问有没有语法问题。首先,我需要仔细看一下这个语句的结构。 首先,表名是implas.tbl_implog_datacenter,看起来没问题。然后是列名列表,包括id, name, display_name, query_port, backup_port, restore_port, clusters。这些列的数量和VALUES后面的值数量是否一致呢?数一下,列有7个,VALUES后面跟着的值也是7个,所以数量是对的。 接下来看看各个值的情况。id是&#39;dc-xxx&#39;,字符串没问题。name是&#39;main&#39;,也没问题。display_name是&#39;vm数据中心&#39;,正确。query_port那一长串的IP和端口,用逗号分隔,但整个被单引号包裹,作为字符串应该是可以的,不过要注意是否有特殊字符需要转义,但这里好像没有,所以没问题。backup_port是&#39;local&#39;,restore_port是&#39;cluster1&#39;吗?这里可能需要确认,因为VALUES部分中的backup_port对应的值是&#39;local,cluster1&#39;,而restore_port的值好像没看到。哦,不对,原句中的VALUES部分最后是&#39;local,cluster1&#39;,可能用户写的时候有误,因为列是backup_port和restore_port,但这里VALUES里面可能少了一个值? 让我再仔细看看。原语句的VALUES部分: VALUES(&#39;dc-xxx&#39;, &#39;main&#39;, &#39;vm数据中心&#39;, &#39;10.3.5.70:8200,...&#39;, &#39;local,cluster1&#39;); 这里,列是7个,但VALUES里的值数量对吗?原句中的VALUES结构可能有误。原用户提供的语句中,VALUES部分在clusters列后面的值应该是&#39;local,cluster1&#39;,但前面的列包括backup_port和restore_port,所以可能这里的分隔有问题。 原句中的VALUES部分: VALUES(&#39;dc-xxx&#39;, &#39;main&#39;, &#39;vm数据中心&#39;, &#39;query_port的值&#39;, &#39;backup_port的值&#39;, &#39;restore_port的值&#39;, &#39;clusters的值&#39;); 但用户实际写的是: VALUES(..., &#39;10.3.5.70:8200,...&#39;, &#39;local,cluster1&#39;); 这里可能把backup_port和restore_port的值合并到同一个位置了,导致列数不匹配。因为原本应该有7个值,但用户只提供了5个?或者可能原句中的备份端口和恢复端口的值被错误地放在一起了? 哦,原语句中的VALUES部分是这样的: VALUES(&#39;dc-xxx&#39;, &#39;main&#39;, &#39;vm数据中心&#39;, &#39;10.3.5.70:8200,...&#39;, &#39;local,cluster1&#39;); 其中,query_port对应的是那串很长的IP和端口,backup_port应该是接下来的值,但用户写的是&#39;local,cluster1&#39;,然后restore_port的值呢?可能用户在这里漏掉了,导致列数不匹配。例如,原本应该backup_port和restore_port各有一个值,但现在VALUES里在第四个位置是query_port的长字符串,第五个位置是&#39;local,cluster1&#39;,这样backup_port会被赋值为&#39;local&#39;,而restore_port可能被赋值为&#39;cluster1&#39;吗?但是这样的话,clusters列的值就没有了,或者可能用户把clusters的值也合并到这里了? 不对,原句中的列包括clusters,所以VALUES应该有7个值。用户提供的VALUES部分可能结构有误。让我再仔细数一下: 原句中的VALUES部分: (id, name, display_name, query_port, backup_port, restore_port, clusters) VALUES( &#39;dc-xxx&#39;, &#39;main&#39;, &#39;vm数据中心&#39;, &#39;10.3.5.70:8200,10.3.5.70:8201,...&#39;, &#39;local,cluster1&#39; ); 这里明显VALUES只有5个值,而列有7个,所以这里存在列数不匹配的问题。所以语法错误是因为VALUES中的值数量与列数不一致。用户可能在输入时遗漏了restore_port和clusters的值,或者将多个值合并到了同一个位置。 因此,正确的应该是每个列对应一个值,所以backup_port和restore_port需要各自的值,最后是clusters的值。例如,正确的VALUES部分应该是: VALUES( &#39;dc-xxx&#39;, &#39;main&#39;, &#39;vm数据中心&#39;, &#39;10.3.5.70:8200,...&#39;, &#39;backup_port_value&#39;, &#39;restore_port_value&#39;, &#39;clusters_value&#39; ); 而用户提供的VALUES中,第五个值是&#39;local,cluster1&#39;,但后面缺少restore_port和clusters的值,导致列数不足,因此语法错误。 所以主要问题在于VALUES子句中的值数量与列数不匹配,缺少了restore_port和clusters的值,或者可能用户误将多个列的值合并到同一个位置了。例如,可能用户本意是将backup_port设为&#39;local&#39;,restore_port设为&#39;cluster1&#39;,而clusters设为其他值,但实际写成了将&#39;local,cluster1&#39;作为一个值赋给了backup_port,导致后面的列缺少值。 此外,检查是否有其他语法问题,比如逗号是否正确,字符串是否用单引号闭合,没有多余或缺少的引号。在用户的语句中,query_port的值很长,但看起来引号是闭合的,没有问题。最后的&#39;local,cluster1&#39;作为backup_port的值,可能用户想用逗号分隔,但实际作为字符串是允许的,不过如果数据库设计上backup_port是单个值,这里可能逻辑有问题,但语法上是正确的。 总结:主要问题在于列数和值的数量不匹配,导致语法错误。</think>该SQL语句存在以下语法和结构问题: $$ \begin{aligned} 1.\ & \text{列数与值数量不匹配:} \\ & \quad \text{• 指定7列(id, name, display_name, query_port, backup_port, restore_port, clusters)} \\ & \quad \text{• VALUES子句只提供5个值} \\ 2.\ & \text{字段映射错误:} \\ & \quad \text{• 第五个值&#39;local,cluster1&#39;被错误地赋给backup_port字段} \\ & \quad \text{• restore_port和clusters字段缺少对应值} \end{aligned} $$ **完整修正建议:** ```sql INSERT INTO implas.tbl_implog_datacenter (id, name, display_name, query_port, backup_port, restore_port, clusters) VALUES( &#39;dc-xxx&#39;, &#39;main&#39;, &#39;vm数据中心&#39;, &#39;10.3.5.70:8200,10.3.5.70:8201,...,10.3.5.72:9501&#39;, -- query_port &#39;local&#39;, -- backup_port &#39;cluster1&#39;, -- restore_port &#39;集群信息&#39; -- clusters ); ``` **验证步骤:** 1. 检查字段顺序:用$SHOW\ CREATE\ TABLE\ implas.tbl_implog_datacenter$确认表结构 2. 端口列表规范:确认query_port字段是否允许存储逗号分隔的复合值 3. IP格式验证:确保所有地址符合$IPv4:Port$格式 4. 去重优化:如10.3.5.70:9500等重复项可简写为$10.3.5.{70-72}:{8200-8201,9500-9501}$ **扩展建议:** - 对于集群信息,建议使用JSON格式存储更规范: ```sql clusters = &#39;{"cluster1": {"nodes": ["10.3.5.70", "10.3.5.71"]}}&#39; ``` - 考虑建立专门的端口映射表,避免在单个字段存储复杂数据
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

nsq_ai

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值