深度学习中的注意力机制二(Pytorch 16)

news2024/11/20 16:29:31

一 Bahdanau 注意力

通过设计一个 基于两个循环神经网络的编码器‐解码器架构,用于序列到序列学习。具体来说,循环神经网络编码器将长度可变的序列转换为固定形状的上下文变量,然后循环神经网络 解码器根据生成的词元和上下文变量按词元生成输出(目标)序列词元。然而,即使并非所有输入(源)词 元都对解码某个词元都有用,在每个解码步骤中仍使用编码相同的上下文变量。有什么方法能改变上下文变 量呢?

我们试着从 (Graves, 2013)中找到灵感:在为给定文本序列生成手写的挑战中,Graves设计了一种可微注意力模型,将文本字符与更长的笔迹对齐,其中对齐方式仅向一个方向移动。受学习对齐想法的启发,Bahdanau等 人提出了一个没有严格单向对齐限制的可微注意力模型 (Bahdanau et al., 2014)。在预测词元时,如果不是所有输入词元都相关,模型将仅对齐(或参与)输入序列中与当前预测相关的部分。这是通过将上下文变量视 为注意力集中的输出来实现的。

1.1 定义模型

下面描述的Bahdanau注意力模型将遵循上节中的相同符号表达。这个新的基于注意力的模型与 上节节中的模型相同,只不过之前中的上下文变量c 在任何解码时间步t ′都会被ct ′替换。假设输入序列中有T个词元, 解码时间步t ′的上下文变量是注意力集中的输出:

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

#@save
class AttentionDecoder(d2l.Decoder):
    """带有注意力机制解码器的基本接口"""
    def __init__(self, **kwargs):
        super(AttentionDecoder, self).__init__(**kwargs)
        
    @property
    def attention_weights(self):
        raise NotImplementedError

接下来,让我们在接下来的Seq2SeqAttentionDecoder类中实现带有Bahdanau注意力的循环神经网络解码器。 首先,初始化解码器的状态,需要下面的输入:

  1. 编码器在所有时间步的最终层隐状态,将作为注意力的键和值
  2. 上一时间步的 编码器全层隐状态,将作为初始化解码器的隐状态;
  3. 编码器有效长度(排除在注意力池中填充词元)。 在每个解码时间步骤中,解码器上一个时间步的最终层隐状态将用作查询。因此,注意力输出和输入嵌入都 连结为循环神经网络解码器的输入
class Seq2SeqAttentionDecoder(AttentionDecoder):
    def __init__(self, vocab_size, embed_size, num_hiddens, num_layers,
                 dropout=0, **kwargs):
        super(Seq2SeqAttentionDecoder, self).__init__(**kwargs)
        self.attention = d2l.AdditiveAttention(
            num_hiddens, num_hiddens, num_hiddens, dropout)
        self.embedding = nn.Embedding(vocab_size, embed_size)
        self.rnn = nn.GRU(
            embed_size + num_hiddens, num_hiddens, num_layers,
            dropout=dropout)
        self.dense = nn.Linear(num_hiddens, vocab_size)
    
    def init_state(self, enc_outputs, enc_valid_lens, *args):
        # outputs的形状为(batch_size,num_steps,num_hiddens).
        # hidden_state的形状为(num_layers,batch_size,num_hiddens)
        outputs, hidden_state = enc_outputs
        return (outputs.permute(1, 0, 2), hidden_state, enc_valid_lens)

    def forward(self, X, state):
        # enc_outputs的形状为(batch_size,num_steps,num_hiddens).
        # hidden_state的形状为(num_layers,batch_size,
        # num_hiddens)
        enc_outputs, hidden_state, enc_valid_lens = state
        # 输出X的形状为(num_steps,batch_size,embed_size)
        X = self.embedding(X).permute(1, 0, 2)
        outputs, self._attention_weights = [], []
        for x in X:
            # query的形状为(batch_size,1,num_hiddens)
            query = torch.unsqueeze(hidden_state[-1], dim=1)
            # context的形状为(batch_size,1,num_hiddens)
            context = self.attention(
                query, enc_outputs, enc_outputs, enc_valid_lens)
            # 在特征维度上连结
            x = torch.cat((context, torch.unsqueeze(x, dim=1)), dim=-1)
            # 将x变形为(1,batch_size,embed_size+num_hiddens)
            out, hidden_state = self.rnn(x.permute(1, 0, 2), hidden_state)
            outputs.append(out)
            self._attention_weights.append(self.attention.attention_weights)
        # 全连接层变换后,outputs的形状为
        # (num_steps,batch_size,vocab_size)
        outputs = self.dense(torch.cat(outputs, dim=0))
        return outputs.permute(1, 0, 2), [enc_outputs, hidden_state,
                                          enc_valid_lens]
    @property
    def attention_weights(self):
        return self._attention_weights

接下来,使用包含7个时间步的4个序列输入的 小批量测试Bahdanau注意力解码器

encoder = d2l.Seq2SeqEncoder(vocab_size=10, embed_size=8, num_hiddens=16,
                             num_layers=2)
encoder.eval()
decoder = Seq2SeqAttentionDecoder(vocab_size=10, embed_size=8, num_hiddens=16,
                                  num_layers=2)
decoder.eval()
X = torch.zeros((4, 7), dtype=torch.long) # (batch_size,num_steps)
state = decoder.init_state(encoder(X), None)
output, state = decoder(X, state)
output.shape, len(state), state[0].shape, len(state[1]), state[1][0].shape
# (torch.Size([4, 7, 10]), 3, torch.Size([4, 7, 16]), 2, torch.Size([4, 16]))

1.2 执行训练

我们在这里指定超参数,实例化一个带有Bahdanau注意力的编码器和解码器,并对这个模 型进行机器翻译训练。由于 新增的注意力机制,训练要比没有注意力机制的 9.7.4节慢得多。

embed_size, num_hiddens, num_layers, dropout = 32, 32, 2, 0.1
batch_size, num_steps = 64, 10
lr, num_epochs, device = 0.005, 250, d2l.try_gpu()

train_iter, src_vocab, tgt_vocab = d2l.load_data_nmt(batch_size, num_steps)
encoder = d2l.Seq2SeqEncoder(
    len(src_vocab), embed_size, num_hiddens, num_layers, dropout)
decoder = Seq2SeqAttentionDecoder(
    len(tgt_vocab), embed_size, num_hiddens, num_layers, dropout)
net = d2l.EncoderDecoder(encoder, decoder)
d2l.train_seq2seq(net, train_iter, lr, num_epochs, tgt_vocab, device)

模型训练后,我们用它将几个英语句子 翻译成法语并计算它们的BLEU分数

engs = ['go .', "i lost .", 'he\'s calm .', 'i\'m home .']
fras = ['va !', 'j\'ai perdu .', 'il est calme .', 'je suis chez moi .']
for eng, fra in zip(engs, fras):
    translation, dec_attention_weight_seq = d2l.predict_seq2seq(
        net, eng, src_vocab, tgt_vocab, num_steps, device, True)
    print(f'{eng} => {translation}, ',
          f'bleu {d2l.bleu(translation, fra, k=2):.3f}')

# go . => va !, bleu 1.000
# i lost . => j'ai perdu ., bleu 1.000
# he's calm . => il est paresseux ., bleu 0.658
# i'm home . => je suis chez moi ., bleu 1.000

训练结束后,下面通过可视化注意力权重会发现,每个查询都会在键值对上分配不同的权重,这说明在每个 解码步中,输入序列的不同部分被选择性地聚集在注意力池中。

# 加上一个包含序列结束词元
d2l.show_heatmaps(
    attention_weights[:, :, :, :len(engs[-1].split()) + 1].cpu(),
    xlabel='Key positions', ylabel='Query positions')

小结:

  • 在预测词元时,如果不是所有输入词元都是相关的,那么具有Bahdanau注意力的循环神经网络编码 器‐解码器会有选择地统计输入序列的不同部分。这是 通过将上下文变量视为加性注意力池化的输出来 实现的
  • 在循环神经网络编码器‐解码器中,Bahdanau注意力 将上一时间步的解码器隐状态视为查询,在所有时 间步的编码器隐状态同时视为键和值

二 多头注意力

在实践中,当给定相同的查询、键和值的集合时,我们希望模型可以基于相同的注意力机制学习到不同的行为,然后将不同的行为作为知识组合起来,捕获序列内各种范围的依赖关系(例如,短距离依赖和长距离依 赖关系)。因此,允许注意力机制组合使用查询、键和值的不同子空间表示(representation subspaces)可能是有益的。

为此,与其只使用单独一个注意力汇聚,我们可以 用独立学习得到的h组不同的线性投影(linear projections) 来变换查询、键和值。然后,这h组变换后的查询、键和值将并行地送到注意力汇聚中。最后,将这h个注意 力汇聚的输出拼接在一起,并且通过另一个可以学习的线性投影进行变换,以产生最终输出。这种设计被称为 多头注意力(multihead attention)(Vaswani et al., 2017)。对于h个注意力汇聚输出,每一个注意力汇聚都 被称作一个头(head)。下图展示了使用全连接层来实现可学习的线性变换的多头注意力。

2.1 执行建模

基于这种设计,每个头都可能会关注输入的不同部分,可以表示比简单加权平均值更复杂的函数

在实现过程中通常 选择缩放点积注意力作为每一个注意力头。为了避免计算代价和参数代价的大幅增长,我 们设定pq = pk = pv = po/h。值得注意的是,如果将查询、键和值的线性变换的输出数量设置为 pqh = pkh = pvh = po,则可以并行计算h个头。在下面的实现中,po是通过参数num_hiddens指定的。

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

#@save
class MultiHeadAttention(nn.Module):
    """多头注意力"""
    def __init__(self, key_size, query_size, value_size, num_hiddens,
                 num_heads, dropout, bias=False, **kwargs):
        super(MultiHeadAttention, self).__init__(**kwargs)
        self.num_heads = num_heads
        self.attention = d2l.DotProductAttention(dropout)
        self.W_q = nn.Linear(query_size, num_hiddens, bias=bias)
        self.W_k = nn.Linear(key_size, num_hiddens, bias=bias)
        self.W_v = nn.Linear(value_size, num_hiddens, bias=bias)
        self.W_o = nn.Linear(num_hiddens, num_hiddens, bias=bias)
    
    def forward(self, queries, keys, values, valid_lens):
        # queries,keys,values的形状:
        # (batch_size,查询或者“键-值”对的个数,num_hiddens)
        # valid_lens 的形状:
        # (batch_size,)或(batch_size,查询的个数)
        # 经过变换后,输出的queries,keys,values 的形状:
        # (batch_size*num_heads,查询或者“键-值”对的个数,
        # num_hiddens/num_heads)
        queries = transpose_qkv(self.W_q(queries), self.num_heads)
        keys = transpose_qkv(self.W_k(keys), self.num_heads)
        values = transpose_qkv(self.W_v(values), self.num_heads)

        if valid_lens is not None:
            # 在轴0,将第一项(标量或者矢量)复制num_heads次,
            # 然后如此复制第二项,然后诸如此类。
            valid_lens = torch.repeat_interleave(
                valid_lens, repeats=self.num_heads, dim=0)
        # output的形状:(batch_size*num_heads,查询的个数,
        # num_hiddens/num_heads)
        output = self.attention(queries, keys, values, valid_lens)
        # output_concat的形状:(batch_size,查询的个数,num_hiddens)
        output_concat = transpose_output(output, self.num_heads)
        return self.W_o(output_concat)

为了能够使多个头并行计算,上面的MultiHeadAttention类将使用下面定义的 两个转置函数。具体来说,transpose_output函数反转了transpose_qkv函数的操作。

#@save
def transpose_qkv(X, num_heads):
    """为了多注意力头的并行计算而变换形状"""
    # 输入X的形状:(batch_size,查询或者“键-值”对的个数,num_hiddens)
    # 输出X的形状:(batch_size,查询或者“键-值”对的个数,num_heads,
    # num_hiddens/num_heads)
    X = X.reshape(X.shape[0], X.shape[1], num_heads, -1)
    # 输出X的形状:(batch_size,num_heads,查询或者“键-值”对的个数,
    # num_hiddens/num_heads)
    X = X.permute(0, 2, 1, 3)
    # 最终输出的形状:(batch_size*num_heads,查询或者“键-值”对的个数,
    # num_hiddens/num_heads)
    return X.reshape(-1, X.shape[2], X.shape[3])

#@save
def transpose_output(X, num_heads):
    """逆转transpose_qkv函数的操作"""
    X = X.reshape(-1, num_heads, X.shape[1], X.shape[2])
    X = X.permute(0, 2, 1, 3)
    return X.reshape(X.shape[0], X.shape[1], -1)

下面使用键和值相同的小例子来测试我们编写的MultiHeadAttention类。多头注意力输出的形状是(batch_size,num_queries,num_hiddens)。

num_hiddens, num_heads = 100, 5
attention = MultiHeadAttention(num_hiddens, num_hiddens, num_hiddens,
                               num_hiddens, num_heads, 0.5)
attention.eval()

# MultiHeadAttention(
#   (attention): DotProductAttention(
#     (dropout): Dropout(p=0.5, inplace=False)
#   )
#   (W_q): Linear(in_features=100, out_features=100, bias=False)
#   (W_k): Linear(in_features=100, out_features=100, bias=False)
#   (W_v): Linear(in_features=100, out_features=100, bias=False)
#   (W_o): Linear(in_features=100, out_features=100, bias=False)
# )
batch_size, num_queries = 2, 4
num_kvpairs, valid_lens = 6, torch.tensor([3, 2])
X = torch.ones((batch_size, num_queries, num_hiddens))
Y = torch.ones((batch_size, num_kvpairs, num_hiddens))
attention(X, Y, Y, valid_lens).shape  # torch.Size([2, 4, 100])

小结:

  • 多头注意力融合了来自于多个注意力汇聚的不同知识,这些知识的不同来源于相同的查询、键和值的 不同的子空间表示。
  • 基于 适当的张量操作,可以 实现多头注意力 的 并行计算

三 自注意力和位置编码

在深度学习中,经常使用 卷积神经网络(CNN)循环神经网络(RNN)对序列进行编码。想象一下,有了注意力机制之后,我们 将词元序列输入注意力池化中,以便同一组词元同时充当查询、键和值。具体来说,每个查询都会关注所有的键-值对并生成一个注意力输出。由于查询、键和值来自同一组输入,因此被称为 自注意力(self‐attention)(Lin et al., 2017, Vaswani et al., 2017),也被称为 内部注意力(intra‐attention)(Cheng et al., 2016, Parikh et al., 2016, Paulus et al., 2017)。本节将 使用自注意力进行序列编码,以及如何 使用序列的顺序 作为 补充信息

3.1 自注意力

根据之前定义的注意力汇聚函数f。下面的代码片段是 基于多头注意力对一个张量完成自注意力的计算,张量的形状为(批量大小,时间步的数目或词元序列的长度,d)。输出与输入的张量形状相同。

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

num_hiddens, num_heads = 100, 5
attention = d2l.MultiHeadAttention(num_hiddens, num_hiddens, num_hiddens,
                                   num_hiddens, num_heads, 0.5)
attention.eval()

# MultiHeadAttention(
#   (attention): DotProductAttention(
#     (dropout): Dropout(p=0.5, inplace=False)
#   )
#   (W_q): Linear(in_features=100, out_features=100, bias=False)
#   (W_k): Linear(in_features=100, out_features=100, bias=False)
#   (W_v): Linear(in_features=100, out_features=100, bias=False)
#   (W_o): Linear(in_features=100, out_features=100, bias=False)
# )
batch_size, num_queries, valid_lens = 2, 4, torch.tensor([3, 2])
X = torch.ones((batch_size, num_queries, num_hiddens))
attention(X, X, X, valid_lens).shape
# torch.Size([2, 4, 100])

3.2 比较卷积神经网络、循环神经网络和自注意力

接下来比较下面几个架构,目标都是 将由n个词元组成的序列映射到另一个长度相等的序列,其中的每个输 入词元或输出词元都由d维向量表示。具体来说,将比较的是 卷积神经网络循环神经网络 自注意力 这几个架构的 计算复杂性、顺序操作和最大路径长度。请注意,顺序操作会妨碍并行计算,而 任意的序列位置组合之间的路径越短,则能 更轻松地学习序列中的远距离依赖关系 (Hochreiter et al., 2001)。

考虑一个 卷积核大小为k的卷积层。在后面的章节将提供关于使用卷积神经网络处理序列的更多详细信息。目 前只需要知道的是,由于序列长度是n,输入和输出的通道数量都是d,所以 卷积层的计算复杂度为O(knd^2 )。 如上图所示,卷积神经网络是分层的,因此为有O(1)个顺序操作,最大路径长度为O(n/k)。例如,x1和x5处于上图中 卷积核大小为3的双层卷积神经网络的感受野内

更新循环神经网络的隐状态 时,d × d权重矩阵和d维隐状态的乘法计算复杂度为O(d^2 )。由于序列长度为n, 因此 循环神经网络层的计算复杂度为O(nd^2 )。根据 图10.6.1,有O(n)个顺序操作无法并行化,最大路径长度 也是O(n)。

在 自注意力 中,查询、键和值都是n × d矩阵。缩放的”点-积“注意力,其中n × d矩阵乘 以d×n矩阵。之后输出的n×n矩阵乘以n×d矩阵。因此,自注意力具有O(n^2d)计算复杂性。正如在上图中所讲,每个词元都通过自注意力直接连接到任何其他词元。因此,有O(1)个顺序操作可以并行计算,最大路径长度也是O(1)。

总而言之,卷积神经网络和自注意力都拥有并行计算的优势,而且 自注意力的最大路径长度最短。但是因为 其计算复杂度是关于序列长度的二次方,所以在很长的序列中计算会非常慢。

3.3 位置编码

在处理词元序列时,循环神经网络是逐个的重复地处理词元的,而 自注意力则因为并行计算而放弃了顺序操作。为了使用序列的顺序信息,通过在输入表示中添加 位置编码(positional encoding)来注入绝对的或相对的位置信息。位置编码可以通过学习得到也可以直接固定得到。接下来描述的是基于正弦函数和余弦函数 的固定位置编码 (Vaswani et al., 2017)。

假设输入表示X ∈ R n×d 包含一个序列中n个词元的d维嵌入表示。位置编码使用相同形状的位置嵌入矩阵 P ∈ R n×d输出X + P,矩阵第i行、第2j列和2j + 1列上的元素为:

乍 一 看, 这 种 基 于 三 角 函 数 的 设 计 看 起 来 很 奇 怪。 在 解 释 这 个 设 计 之 前, 让 我 们先在下面的PositionalEncoding类中实现它。

#@save
class PositionalEncoding(nn.Module):
    """位置编码"""
    def __init__(self, num_hiddens, dropout, max_len=1000):
        super(PositionalEncoding, self).__init__()
        self.dropout = nn.Dropout(dropout)
        # 创建一个足够长的P
        self.P = torch.zeros((1, max_len, num_hiddens))
        X = torch.arange(max_len, dtype=torch.float32).reshape(
            -1, 1) / torch.pow(10000, torch.arange(
            0, num_hiddens, 2, dtype=torch.float32) / num_hiddens)
        self.P[:, :, 0::2] = torch.sin(X)
        self.P[:, :, 1::2] = torch.cos(X)

    def forward(self, X):
        X = X + self.P[:, :X.shape[1], :].to(X.device)
        return self.dropout(X)

在位置嵌入矩阵P中,行代表词元在序列中的位置,列代表位置编码的不同维度。从下面的例子中可以看到位 置嵌入矩阵的第6列和第7列的频率高于第8列和第9列。第6列和第7列之间的偏移量(第8列和第9列相同)是 由于正弦函数和余弦函数的交替

encoding_dim, num_steps = 32, 60
pos_encoding = PositionalEncoding(encoding_dim, 0)
pos_encoding.eval()
X = pos_encoding(torch.zeros((1, num_steps, encoding_dim)))
P = pos_encoding.P[:, :X.shape[1], :]
d2l.plot(torch.arange(num_steps), P[0, :, 6:10].T, xlabel='Row (position)',
         figsize=(6, 2.5), legend=["Col %d" % d for d in torch.arange(6, 10)])

绝对位置信息,为了明白沿着编码维度单调降低的频率与绝对位置信息的关系,让我们打印出0, 1, . . . , 7的二进制表示形式。 正如所看到的,每个数字、每两个数字和每四个数字上的比特值在第一个最低位、第二个最低位和第三个最 低位上分别交替。

for i in range(8):
    print(f'{i}的二进制是:{i:>03b}')
    
# 0的二进制是:000
# 1的二进制是:001
# 2的二进制是:010
# 3的二进制是:011
# 4的二进制是:100
# 5的二进制是:101
# 6的二进制是:110
# 7的二进制是:111

在二进制表示中,较高比特位的交替频率低于较低比特位,与下面的热图所示相似,只是位置编码通过使用三角函数在编码维度上降低频率。由于输出是浮点数,因此此类连续表示比二进制表示法更节省空间。

P = P[0, :, :].unsqueeze(0).unsqueeze(0)
d2l.show_heatmaps(P, xlabel='Column (encoding dimension)',
                  ylabel='Row (position)', figsize=(3.5, 4), cmap='Blues')

小结:

  • 自注意力 中,查询、键和值都来自同一组输入
  • 卷积神经网络和自注意力都拥有并行计算的优势,而且 自注意力的最大路径长度最短。但是因为其计算复杂度是关于序列长度的二次方,所以在很长的序列中计算会非常慢。
  • 为了 使用序列的顺序信息,可以通过 在输入表示中添加位置编码来注入绝对的或相对的位置信息

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

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

相关文章

转转小程序数据处理

声明 本文章中所有内容仅供学习交流使用,不用于其他任何目的,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!wx a15018601872,x30184483x…

今天遇到一个GPT解决不了的问题

问题描述 你好,postman的一个post请求,编辑器里面放了一个很长的json数据,报Tokenization is skipped for long lines for performance reasons. This can be configured via editor.maxTokenizationLineLength.,但是同样的数据&a…

YOLOv9全网最新改进系列:YOLOv9完美融合标准化的注意力模块NAM,高效且轻量级的归一化注意力机制,助力目标检测再上新台阶!

YOLOv9全网最新改进系列:YOLOv9完美融合标准化的注意力模块NAM,高效且轻量级的归一化注意力机制,助力目标检测再上新台阶!!! YOLOv9原文链接戳这里,原文全文翻译请关注B站Ai学术叫叫首er B站全…

基于模糊控制的AMT自动变速汽车换档智能控制系统simulink建模与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 基于模糊控制的AMT自动变速汽车换档智能控制系统simulink建模与仿真。 2.系统仿真结果 输入的V,Ac,a 输出的档位: 3.核心程序与模型 版…

基于MPPT最大功率跟踪和SVPWM的光伏三相并网逆变器simulink建模与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 基于MPPT最大功率跟踪和SVPWM的光伏三相并网逆变器simulink建模与仿真。包括PV模块,MPPT模块,SVPWM模块,电网模块等。 2.系统仿真结果 1不…

Pytorch常用的函数(九)torch.gather()用法

Pytorch常用的函数(九)torch.gather()用法 torch.gather() 就是在指定维度上收集value。 torch.gather() 的必填也是最常用的参数有三个,下面引用官方解释: input (Tensor) – the source tensordim (int) – the axis along which to indexindex (Lo…

Linux中gitlab-runner部署使用备忘

环境: 操作系统::CentOS8 gitlab版本:13.11.4 查看gitlab-runner版本 可以从https://packages.gitlab.com/app/runner/gitlab-runner/search找到与安装的gitlab版本相近的gitlab-runner版本以及安装命令等信息,我找到与13.11.4相…

【离散数学】集合上二元关系性质判定的实现(c语言实现)

实验要求 关系矩阵的初始化和打印 我们将关系矩阵存入一个二维数组中,因为集合元素个数不会超过5个所以就用一个5行5列二维数组来表示。 在我们得到了集合元素个数之后我们就可以对数组进行0,1随机赋值 //初始关系矩阵 void init_matrix(int array[][5], int n) {…

【数据结构|C语言版】栈和队列

前言1. 栈1.1 栈的概念和性质1.2 顺序栈1.3 链栈1.4 共享栈 2. 队列2.1 队列的概念和性质2.2 循环队列2.3 链式队列2.4 双端队列 3. 例题精选3.1 有效的括号3.2 用队列实现栈2.4 用栈实现队列3.4 设计循环队列3.5 参考答案 结语 #include<GUIQU.h> int main { 上期回顾: …

centos8.5 安装 redis 7.2.4 详细步骤

1 下载Index of /releases/ (redis.io) 通过xftp等方式上传到服务器&#xff0c;安装依赖包 yum install gcc gcc-c make tcl -y [rootlocalhost software]# ll total 3308 -rw-r--r--. 1 root root 3386861 May 3 21:56 redis-7.2.4.tar.gz [rootlocalhost software]# ll…

SlowFast报错:ValueError: too many values to unpack (expected 4)

SlowFast报错&#xff1a;ValueError: too many values to unpack (expected 4) 报错细节 File "/home/user/yuanjinmin/SlowFast/tools/visualization.py", line 81, in run_visualizationfor inputs, labels, _, meta in tqdm.tqdm(vis_loader): ValueError: too …

软件测试之测试用例详细解读

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 一、通用测试用例八要素   1、用例编号&#xff1b;    2、测试项目&#xff1b;   3、测…

CSS---复合选择器和元素显示模式(三)

一、CSS的复合选择器 1.1 什么是复合选择器 在CSS中&#xff0c;可以根据选择器的类型把选择器分为基础选择器和复合选择器&#xff0c;复合选择器是建立在基础选择器之上&#xff0c;对基本选择器进行组合形成的。 复合选择器是由两个或多个基础选择器连写组成&#xff0c;它…

算法学习Day2——单调栈习题

第一题&#xff0c;合并球 题解&#xff1a;一开始写了一次暴力双循环&#xff0c;直接O(n^2)严重超时&#xff0c;后面于是又想到了O(n)时间复杂度的链表&#xff0c;但是还是卡在 最后一个数据会TLE&#xff0c;我也是高兴的拍起来安塞腰鼓和华氏护肤水&#xff0c;后面学长给…

信息系统项目管理师0094:项目管理过程组(6项目管理概论—6.4价值驱动的项目管理知识体系—6.4.3项目管理过程组)

点击查看专栏目录 文章目录 6.4.3项目管理过程组1.适应型项目中的过程组2.适应型项目中过程组之间的关系6.4.3项目管理过程组 项目管理过程组是为了达成项目的特定目标,对项目管理过程进行的逻辑上的分组。项目管理过程组不同于项目阶段:①项目管理过程组是为了管理项目,针对…

typescript 模块化

模块的概念&#xff1a; 把一些公共的功能单独抽离成一个文件作为一个模块。 模块里面的变量、函数、类等默认是私有的&#xff0c;如果我们要在外部访问模块里面的数据&#xff08;变量、函数、类&#xff09;&#xff0c;需要通过export暴露模块里面的数据&#xff08;&#…

C++面向对象程序设计 - 多态性

多态性是面向对象程序设计的一个重要特征&#xff0c;多态的意思是一个事物的多种形态。在C程序设计中&#xff0c;多态性是指具有不同功能的函数可以用同一个函数名&#xff0c;这样就可以用一个函数名调用不同内容的函数。面向对象方法中的多态性&#xff0c;比如说向不同的对…

Colibri for Mac v2.2.0 原生无损音频播放器 激活版

Colibri支持所有流行的无损和有损音频格式的完美清晰的比特完美播放&#xff0c;仅使用微小的计算能力&#xff0c;并提供干净和直观的用户体验。 Colibri在播放音乐时使用极少的计算能力。该应用程序使用最先进的Swift 3编程语言构建&#xff0c;BASS音频引擎作为机器代码捆绑…

计算机毕业设计 | springboot+vue凌云在线阅读平台 线上读书系统(附源码)

1&#xff0c;绪论 随着社会和网络技术的发展&#xff0c;网络小说成为人们茶钱饭后的休闲方式&#xff0c;但是现在很多网络小说的网站都是收费的&#xff0c;高额的收费制度是很多人接受不了的&#xff0c;另外就是很多小说网站都会有大量的弹窗和广告&#xff0c;这极大的影…

【文献解析】3D高斯抛雪球是个什么玩意

论文地址&#xff1a;https://arxiv.org/abs/2308.04079 项目&#xff1a;3D Gaussian Splatting for Real-Time Radiance Field Rendering 代码&#xff1a;git clone https://github.com/graphdeco-inria/gaussian-splatting --recursive 一、文章概述 1.1问题导向 辐射…