【AI大模型】深入Transformer架构:编码器部分的实现与解析(上)
目录
🍔 编码器介绍
🍔 掩码张量
2.1 掩码张量介绍
2.2 掩码张量的作用
2.3 生成掩码张量的代码分析
2.4 掩码张量的可视化
2.5 掩码张量总结
🍔 注意力机制
3.1 注意力计算规则的代码分析
3.2 带有mask的输入参数:
3.3 注意力机制总结
🍔 多头注意力机制
4.1 多头注意力机制概念
4.2 多头注意力机制结构图
4.3 多头注意力机制的作用
4.4 多头注意力机制的代码实现
4.5 多头注意力机制总结
学习目标
🍀 了解编码器中各个组成部分的作用.
🍀 掌握编码器中各个组成部分的实现过程.
🍔 编码器介绍
编码器部分: * 由N个编码器层堆叠而成 * 每个编码器层由两个子层连接结构组成 * 第一个子层连接结构包括一个多头自注意力子层和规范化层以及一个残差连接 * 第二个子层连接结构包括一个前馈全连接子层和规范化层以及一个残差连接
🍔 掩码张量
2.1 掩码张量介绍
- 掩代表遮掩,码就是我们张量中的数值,它的尺寸不定,里面一般只有1和0的元素,代表位置被遮掩或者不被遮掩,至于是0位置被遮掩还是1位置被遮掩可以自定义,因此它的作用就是让另外一个张量中的一些数值被遮掩,也可以说被替换, 它的表现形式是一个张量.
2.2 掩码张量的作用
- 在transformer中, 掩码张量的主要作用在应用attention(将在下一小节讲解)时,有一些生成的attention张量中的值计算有可能已知了未来信息而得到的,未来信息被看到是因为训练时会把整个输出结果都一次性进行Embedding,但是理论上解码器的的输出却不是一次就能产生最终结果的,而是一次次通过上一次结果综合得出的,因此,未来的信息可能被提前利用. 所以,我们会进行遮掩. 关于解码器的有关知识将在后面的章节中讲解.
2.3 生成掩码张量的代码分析
def subsequent_mask(size):"""生成向后遮掩的掩码张量, 参数size是掩码张量最后两个维度的大小, 它的最后两维形成一个方阵"""# 在函数中, 首先定义掩码张量的形状attn_shape = (1, size, size)# 然后使用np.ones方法向这个形状中添加1元素,形成上三角阵, 最后为了节约空间, # 再使其中的数据类型变为无符号8位整形unit8 subsequent_mask = np.triu(np.ones(attn_shape), k=1).astype('uint8')# 最后将numpy类型转化为torch中的tensor, 内部做一个1 - 的操作, # 在这个其实是做了一个三角阵的反转, subsequent_mask中的每个元素都会被1减, # 如果是0, subsequent_mask中的该位置由0变成1# 如果是1, subsequent_mask中的该位置由1变成0 return torch.from_numpy(1 - subsequent_mask)
- np.triu演示:
>>> np.triu([[1,2,3],[4,5,6],[7,8,9],[10,11,12]], k=-1)
array([[ 1, 2, 3],[ 4, 5, 6],[ 0, 8, 9],[ 0, 0, 12]])>>> np.triu([[1,2,3],[4,5,6],[7,8,9],[10,11,12]], k=0)
array([[ 1, 2, 3],[ 0, 5, 6],[ 0, 0, 9],[ 0, 0, 0]])>>> np.triu([[1,2,3],[4,5,6],[7,8,9],[10,11,12]], k=1)
array([[ 0, 2, 3],[ 0, 0, 6],[ 0, 0, 0],[ 0, 0, 0]])
- 输入实例:
# 生成的掩码张量的最后两维的大小
size = 5
- 调用:
sm = subsequent_mask(size)
print("sm:", sm)
- 输出效果:
# 最后两维形成一个下三角阵
sm: (0 ,.,.) = 1 0 0 0 01 1 0 0 01 1 1 0 01 1 1 1 01 1 1 1 1
[torch.ByteTensor of size 1x5x5]
2.4 掩码张量的可视化
plt.figure(figsize=(5,5))
plt.imshow(subsequent_mask(20)[0])
- 输出效果:
- 效果分析:
- 通过观察可视化方阵, 黄色是1的部分, 这里代表被遮掩, 紫色代表没有被遮掩的信息, 横坐标代表目标词汇的位置, 纵坐标代表可查看的位置;
- 我们看到, 在0的位置我们一看望过去都是黄色的, 都被遮住了,1的位置一眼望过去还是黄色, 说明第一次词还没有产生, 从第二个位置看过去, 就能看到位置1的词, 其他位置看不到, 以此类推.
2.5 掩码张量总结
-
学习了什么是掩码张量:
- 掩代表遮掩,码就是我们张量中的数值,它的尺寸不定,里面一般只有1和0的元素,代表位置被遮掩或者不被遮掩,至于是0位置被遮掩还是1位置被遮掩可以自定义,因此它的作用就是让另外一个张量中的一些数值被遮掩, 也可以说被替换, 它的表现形式是一个张量.
-
学习了掩码张量的作用:
- 在transformer中, 掩码张量的主要作用在应用attention(将在下一小节讲解)时,有一些生成的attetion张量中的值计算有可能已知量未来信息而得到的,未来信息被看到是因为训练时会把整个输出结果都一次性进行Embedding,但是理论上解码器的的输出却不是一次就能产生最终结果的,而是一次次通过上一次结果综合得出的,因此,未来的信息可能被提前利用. 所以,我们会进行遮掩. 关于解码器的有关知识将在后面的章节中讲解.
-
学习并实现了生成向后遮掩的掩码张量函数: subsequent_mask
- 它的输入是size, 代表掩码张量的大小.
- 它的输出是一个最后两维形成1方阵的下三角阵.
- 最后对生成的掩码张量进行了可视化分析, 更深一步理解了它的用途.
🍔 注意力机制
我们这里使用的注意力的计算规则:
3.1 注意力计算规则的代码分析
import torch.nn.functional as Fdef attention(query, key, value, mask=None, dropout=None):"""注意力机制的实现, 输入分别是query, key, value, mask: 掩码张量, dropout是nn.Dropout层的实例化对象, 默认为None"""# 在函数中, 首先取query的最后一维的大小, 一般情况下就等同于我们的词嵌入维度, 命名为d_kd_k = query.size(-1)# 按照注意力公式, 将query与key的转置相乘, 这里面key是将最后两个维度进行转置, 再除以缩放系数根号下d_k, 这种计算方法也称为缩放点积注意力计算.# 得到注意力得分张量scoresscores = torch.matmul(query, key.transpose(-2, -1)) / math.sqrt(d_k)# 接着判断是否使用掩码张量if mask is not None:# 使用tensor的masked_fill方法, 将掩码张量和scores张量每个位置一一比较, 如果掩码张量处为0# 则对应的scores张量用-1e9这个值来替换, 如下演示scores = scores.masked_fill(mask == 0, -1e9)# 对scores的最后一维进行softmax操作, 使用F.softmax方法, 第一个参数是softmax对象, 第二个是目标维度.# 这样获得最终的注意力张量p_attn = F.softmax(scores, dim = -1)# 之后判断是否使用dropout进行随机置0if dropout is not None:# 将p_attn传入dropout对象中进行'丢弃'处理p_attn = dropout(p_attn)# 最后, 根据公式将p_attn与value张量相乘获得最终的query注意力表示, 同时返回注意力张量return torch.matmul(p_attn, value), p_attn
- tensor.masked_fill演示:
>>> input = Variable(torch.randn(5, 5))
>>> input
Variable containing:2.0344 -0.5450 0.3365 -0.1888 -2.18031.5221 -0.3823 0.8414 0.7836 -0.8481
-0.0345 -0.8643 0.6476 -0.2713 1.56450.8788 -2.2142 0.4022 0.1997 0.14742.9109 0.6006 -0.6745 -1.7262 0.6977
[torch.FloatTensor of size 5x5]>>> mask = Variable(torch.zeros(5, 5))
>>> mask
Variable containing:0 0 0 0 00 0 0 0 00 0 0 0 00 0 0 0 00 0 0 0 0
[torch.FloatTensor of size 5x5]>>> input.masked_fill(mask == 0, -1e9)
Variable containing:
-1.0000e+09 -1.0000e+09 -1.0000e+09 -1.0000e+09 -1.0000e+09
-1.0000e+09 -1.0000e+09 -1.0000e+09 -1.0000e+09 -1.0000e+09
-1.0000e+09 -1.0000e+09 -1.0000e+09 -1.0000e+09 -1.0000e+09
-1.0000e+09 -1.0000e+09 -1.0000e+09 -1.0000e+09 -1.0000e+09
-1.0000e+09 -1.0000e+09 -1.0000e+09 -1.0000e+09 -1.0000e+09
[torch.FloatTensor of size 5x5]
- 输入参数:
# 我们令输入的query, key, value都相同, 位置编码的输出
query = key = value = pe_result
Variable containing:
( 0 ,.,.) = 46.5196 16.2057 -41.5581 ... -16.0242 -17.8929 -43.0405-32.6040 16.1096 -29.5228 ... 4.2721 20.6034 -1.2747-18.6235 14.5076 -2.0105 ... 15.6462 -24.6081 -30.33910.0000 -66.1486 -11.5123 ... 20.1519 -4.6823 0.4916( 1 ,.,.) = -24.8681 7.5495 -5.0765 ... -7.5992 -26.6630 40.951713.1581 -3.1918 -30.9001 ... 25.1187 -26.4621 2.9542-49.7690 -42.5019 8.0198 ... -5.4809 25.9403 -27.4931-52.2775 10.4006 0.0000 ... -1.9985 7.0106 -0.5189
[torch.FloatTensor of size 2x4x512]
- 调用:
attn, p_attn = attention(query, key, value)
print("attn:", attn)
print("p_attn:", p_attn)
- 输出效果:
# 将得到两个结果
# query的注意力表示:
attn: Variable containing:
( 0 ,.,.) = 12.8269 7.7403 41.2225 ... 1.4603 27.8559 -12.260012.4904 0.0000 24.1575 ... 0.0000 2.5838 18.0647-32.5959 -4.6252 -29.1050 ... 0.0000 -22.6409 -11.83418.9921 -33.0114 -0.7393 ... 4.7871 -5.7735 8.3374( 1 ,.,.) = -25.6705 -4.0860 -36.8226 ... 37.2346 -27.3576 2.5497-16.6674 73.9788 -33.3296 ... 28.5028 -5.5488 -13.75640.0000 -29.9039 -3.0405 ... 0.0000 14.4408 14.857930.7819 0.0000 21.3908 ... -29.0746 0.0000 -5.8475
[torch.FloatTensor of size 2x4x512]# 注意力张量:
p_attn: Variable containing:
(0 ,.,.) = 1 0 0 00 1 0 00 0 1 00 0 0 1(1 ,.,.) = 1 0 0 00 1 0 00 0 1 00 0 0 1
[torch.FloatTensor of size 2x4x4]
3.2 带有mask的输入参数:
query = key = value = pe_result# 令mask为一个2x4x4的零张量
mask = Variable(torch.zeros(2, 4, 4))
- 调用:
attn, p_attn = attention(query, key, value, mask=mask)
print("attn:", attn)
print("p_attn:", p_attn)
- 带有mask的输出效果:
# query的注意力表示:
attn: Variable containing:
( 0 ,.,.) = 0.4284 -7.4741 8.8839 ... 1.5618 0.5063 0.57700.4284 -7.4741 8.8839 ... 1.5618 0.5063 0.57700.4284 -7.4741 8.8839 ... 1.5618 0.5063 0.57700.4284 -7.4741 8.8839 ... 1.5618 0.5063 0.5770( 1 ,.,.) = -2.8890 9.9972 -12.9505 ... 9.1657 -4.6164 -0.5491-2.8890 9.9972 -12.9505 ... 9.1657 -4.6164 -0.5491-2.8890 9.9972 -12.9505 ... 9.1657 -4.6164 -0.5491-2.8890 9.9972 -12.9505 ... 9.1657 -4.6164 -0.5491
[torch.FloatTensor of size 2x4x512]# 注意力张量:
p_attn: Variable containing:
(0 ,.,.) = 0.2500 0.2500 0.2500 0.25000.2500 0.2500 0.2500 0.25000.2500 0.2500 0.2500 0.25000.2500 0.2500 0.2500 0.2500(1 ,.,.) = 0.2500 0.2500 0.2500 0.25000.2500 0.2500 0.2500 0.25000.2500 0.2500 0.2500 0.25000.2500 0.2500 0.2500 0.2500
[torch.FloatTensor of size 2x4x4]
3.3 注意力机制总结
- 学习并实现了注意力计算规则的函数: attention
- 它的输入就是Q,K,V以及mask和dropout, mask用于掩码, dropout用于随机置0.
- 它的输出有两个, query的注意力表示以及注意力张量.
🍔 多头注意力机制
4.1 多头注意力机制概念
- 从多头注意力的结构图中,貌似这个所谓的多个头就是指多组线性变换层,其实并不是,我只有使用了一组线性变化层,即三个变换张量对Q,K,V分别进行线性变换,这些变换不会改变原有张量的尺寸,因此每个变换矩阵都是方阵,得到输出结果后,多头的作用才开始显现,每个头开始从词义层面分割输出的张量,也就是每个头都想获得一组Q,K,V进行注意力机制的计算,但是句子中的每个词的表示只获得一部分,也就是只分割了最后一维的词嵌入向量. 这就是所谓的多头,将每个头的获得的输入送到注意力机制中, 就形成多头注意力机制.
4.2 多头注意力机制结构图
4.3 多头注意力机制的作用
- 这种结构设计能让每个注意力机制去优化每个词汇的不同特征部分,从而均衡同一种注意力机制可能产生的偏差,让词义拥有来自更多元的表达,实验表明可以从而提升模型效果.
4.4 多头注意力机制的代码实现
# 用于深度拷贝的copy工具包
import copy# 首先需要定义克隆函数, 因为在多头注意力机制的实现中, 用到多个结构相同的线性层.
# 我们将使用clone函数将他们一同初始化在一个网络层列表对象中. 之后的结构中也会用到该函数.
def clones(module, N):"""用于生成相同网络层的克隆函数, 它的参数module表示要克隆的目标网络层, N代表需要克隆的数量"""# 在函数中, 我们通过for循环对module进行N次深度拷贝, 使其每个module成为独立的层,# 然后将其放在nn.ModuleList类型的列表中存放.return nn.ModuleList([copy.deepcopy(module) for _ in range(N)])# 我们使用一个类来实现多头注意力机制的处理
class MultiHeadedAttention(nn.Module):def __init__(self, head, embedding_dim, dropout=0.1):"""在类的初始化时, 会传入三个参数,head代表头数,embedding_dim代表词嵌入的维度, dropout代表进行dropout操作时置0比率,默认是0.1."""super(MultiHeadedAttention, self).__init__()# 在函数中,首先使用了一个测试中常用的assert语句,判断h是否能被d_model整除,# 这是因为我们之后要给每个头分配等量的词特征.也就是embedding_dim/head个.assert embedding_dim % head == 0# 得到每个头获得的分割词向量维度d_kself.d_k = embedding_dim // head# 传入头数hself.head = head# 然后获得线性层对象,通过nn的Linear实例化,它的内部变换矩阵是embedding_dim x embedding_dim,然后使用clones函数克隆四个,# 为什么是四个呢,这是因为在多头注意力中,Q,K,V各需要一个,最后拼接的矩阵还需要一个,因此一共是四个.self.linears = clones(nn.Linear(embedding_dim, embedding_dim), 4)# self.attn为None,它代表最后得到的注意力张量,现在还没有结果所以为None.self.attn = None# 最后就是一个self.dropout对象,它通过nn中的Dropout实例化而来,置0比率为传进来的参数dropout.self.dropout = nn.Dropout(p=dropout)def forward(self, query, key, value, mask=None):"""前向逻辑函数, 它的输入参数有四个,前三个就是注意力机制需要的Q, K, V,最后一个是注意力机制中可能需要的mask掩码张量,默认是None. """# 如果存在掩码张量maskif mask is not None:# 使用unsqueeze拓展维度mask = mask.unsqueeze(0)# 接着,我们获得一个batch_size的变量,他是query尺寸的第1个数字,代表有多少条样本.batch_size = query.size(0)# 之后就进入多头处理环节# 首先利用zip将输入QKV与三个线性层组到一起,然后使用for循环,将输入QKV分别传到线性层中,# 做完线性变换后,开始为每个头分割输入,这里使用view方法对线性变换的结果进行维度重塑,多加了一个维度h,代表头数,# 这样就意味着每个头可以获得一部分词特征组成的句子,其中的-1代表自适应维度,# 计算机会根据这种变换自动计算这里的值.然后对第二维和第三维进行转置操作,# 为了让代表句子长度维度和词向量维度能够相邻,这样注意力机制才能找到词义与句子位置的关系,# 从attention函数中可以看到,利用的是原始输入的倒数第一和第二维.这样我们就得到了每个头的输入.query, key, value = \[model(x).view(batch_size, -1, self.head, self.d_k).transpose(1, 2)for model, x in zip(self.linears, (query, key, value))]# 得到每个头的输入后,接下来就是将他们传入到attention中,# 这里直接调用我们之前实现的attention函数.同时也将mask和dropout传入其中.x, self.attn = attention(query, key, value, mask=mask, dropout=self.dropout)# 通过多头注意力计算后,我们就得到了每个头计算结果组成的4维张量,我们需要将其转换为输入的形状以方便后续的计算,# 因此这里开始进行第一步处理环节的逆操作,先对第二和第三维进行转置,然后使用contiguous方法,# 这个方法的作用就是能够让转置后的张量应用view方法,否则将无法直接使用,# 所以,下一步就是使用view重塑形状,变成和输入形状相同.x = x.transpose(1, 2).contiguous().view(batch_size, -1, self.head * self.d_k)# 最后使用线性层列表中的最后一个线性层对输入进行线性变换得到最终的多头注意力结构的输出.return self.linears[-1](x)
- tensor.view演示:
>>> x = torch.randn(4, 4)
>>> x.size()
torch.Size([4, 4])
>>> y = x.view(16)
>>> y.size()
torch.Size([16])
>>> z = x.view(-1, 8) # the size -1 is inferred from other dimensions
>>> z.size()
torch.Size([2, 8])>>> a = torch.randn(1, 2, 3, 4)
>>> a.size()
torch.Size([1, 2, 3, 4])
>>> b = a.transpose(1, 2) # Swaps 2nd and 3rd dimension
>>> b.size()
torch.Size([1, 3, 2, 4])
>>> c = a.view(1, 3, 2, 4) # Does not change tensor layout in memory
>>> c.size()
torch.Size([1, 3, 2, 4])
>>> torch.equal(b, c)
False
- torch.transpose演示:
>>> x = torch.randn(2, 3)
>>> x
tensor([[ 1.0028, -0.9893, 0.5809],[-0.1669, 0.7299, 0.4942]])
>>> torch.transpose(x, 0, 1)
tensor([[ 1.0028, -0.1669],[-0.9893, 0.7299],[ 0.5809, 0.4942]])
- 实例化参数:
# 头数head
head = 8# 词嵌入维度embedding_dim
embedding_dim = 512# 置零比率dropout
dropout = 0.2
- 输入参数:
# 假设输入的Q,K,V仍然相等
query = value = key = pe_result# 输入的掩码张量mask
mask = Variable(torch.zeros(8, 4, 4))
- 调用:
mha = MultiHeadedAttention(head, embedding_dim, dropout)
mha_result = mha(query, key, value, mask)
print(mha_result)
- 输出效果:
tensor([[[-0.3075, 1.5687, -2.5693, ..., -1.1098, 0.0878, -3.3609],[ 3.8065, -2.4538, -0.3708, ..., -1.5205, -1.1488, -1.3984],[ 2.4190, 0.5376, -2.8475, ..., 1.4218, -0.4488, -0.2984],[ 2.9356, 0.3620, -3.8722, ..., -0.7996, 0.1468, 1.0345]],[[ 1.1423, 0.6038, 0.0954, ..., 2.2679, -5.7749, 1.4132],[ 2.4066, -0.2777, 2.8102, ..., 0.1137, -3.9517, -2.9246],[ 5.8201, 1.1534, -1.9191, ..., 0.1410, -7.6110, 1.0046],[ 3.1209, 1.0008, -0.5317, ..., 2.8619, -6.3204, -1.3435]]],grad_fn=<AddBackward0>)
torch.Size([2, 4, 512])
4.5 多头注意力机制总结
-
学习了什么是多头注意力机制:
- 每个头开始从词义层面分割输出的张量,也就是每个头都想获得一组Q,K,V进行注意力机制的计算,但是句子中的每个词的表示只获得一部分,也就是只分割了最后一维的词嵌入向量. 这就是所谓的多头.将每个头的获得的输入送到注意力机制中, 就形成了多头注意力机制.
-
学习了多头注意力机制的作用:
- 这种结构设计能让每个注意力机制去优化每个词汇的不同特征部分,从而均衡同一种注意力机制可能产生的偏差,让词义拥有来自更多元的表达,实验表明可以从而提升模型效果.
-
学习并实现了多头注意力机制的类: MultiHeadedAttention
- 因为多头注意力机制中需要使用多个相同的线性层, 首先实现了克隆函数clones.
- clones函数的输入是module,N,分别代表克隆的目标层,和克隆个数.
- clones函数的输出是装有N个克隆层的Module列表.
- 接着实现MultiHeadedAttention类, 它的初始化函数输入是h, d_model, dropout分别代表头数,词嵌入维度和置零比率.
- 它的实例化对象输入是Q, K, V以及掩码张量mask.
- 它的实例化对象输出是通过多头注意力机制处理的Q的注意力表示.
相关文章:

【AI大模型】深入Transformer架构:编码器部分的实现与解析(上)
目录 🍔 编码器介绍 🍔 掩码张量 2.1 掩码张量介绍 2.2 掩码张量的作用 2.3 生成掩码张量的代码分析 2.4 掩码张量的可视化 2.5 掩码张量总结 🍔 注意力机制 3.1 注意力计算规则的代码分析 3.2 带有mask的输入参数: 3.…...

spring学习日记-day7-整合mybatis
一、学习目标 spring整合MyBatis的原理主要涉及到将MyBatis的Mapper映射文件交由Spring容器管理,并将其注入到MyBatis的SqlSessionFactory中,从而实现两者的整合。 二、整合mybatis 1.写一个mybatis测试案例 项目结构: 1.数据库 CREATE DA…...

【YOLO目标检测行人与车数据集】共5607张、已标注txt格式、有训练好的yolov5的模型
目录 说明图片示例 说明 数据集格式:YOLO格式 图片数量:5607 标注数量(txt文件个数):5607 标注类别数:2 标注类别名称:person、car 数据集下载:行人与车数据集 图片示例 数据集图片: …...
JMeter中线程组、HTTP请求的常见参数解释
在JMeter中,线程组和HTTP请求是进行性能测试的两个核心组件。以下是它们的一些常见相关参数的解释: 线程组参数 线程数 指定模拟的用户数,即并发执行的线程数。 Ramp-Up时间(秒) 指定所有线程启动的时间间隔。在这…...

优化Mysql
目录 Mysql优化就四种:定位慢查询/sql执行计划/索引/Sql优化经验... 2 1Mysql如何定位慢查询?... 2 2Sql语句执行很慢,如何分析呢?... 3 2.1那这个SQL语句执行很慢,如何分析呢?. 3 3.了解过索引吗?(什么是索引)…...

如何使用MethodChannel通信
文章目录 1 概念介绍2 实现方法3 经验总结我们在上一章回中介绍了Visibility组件相关的内容,本章回中将介绍Flutter与原生平台通信相关的内容.闲话休提,让我们一起Talk Flutter吧。 1 概念介绍 在移动开发领域以Android和IOS SDK开发出的应用程序叫原生开发,开发同一个程序…...
【JavaWeb】JavaWeb笔记 HTTP
文章目录 简介HTTP1.0和HTTP1.1的区别 请求和响应报文报文的格式请求报文form表单发送GET请求特点GET请求行,请求头,请求体form表单发送post请求特点post的请求行 请求头 请求体 响应报文响应状态码更多的响应状态码 简介 HTTP 超文本传输协议 (HTTP-Hyper Text transfer proto…...

Java项目实战II基于Java+Spring Boot+MySQL的甘肃非物质文化网站设计与实现(源码+数据库+文档)
目录 一、前言 二、技术介绍 三、系统实现 四、文档参考 五、核心代码 六、源码获取 全栈码农以及毕业设计实战开发,CSDN平台Java领域新星创作者 一、前言 甘肃省作为中国历史文化名省,拥有丰富的非物质文化遗产资源,涵盖表演艺术、手…...

数据结构--包装类简单认识泛型
目录 1 包装类 1.1 基本数据类型和对应的包装类 1.2 装箱和拆箱,自动装箱和自动拆箱 2 什么是泛型 3 引出泛型 3.1 语法 4 泛型类的使用 4.1 语法 4.2 示例 5 泛型的上界 5.1 语法 5.2 示例 5.3 复杂示例 8 泛型方法 8.1 定义语法 8.2 示例 总结 1 …...

c#使用winscp库实现FTP/SFTP/SCP的获取列表、上传和下载功能
网上写c#调用winscp实现的资料很少,且写的不够详细。本人查了下winscp的libraries说明,写了个小工具,供大家参考。 winscp的接口说明地址如下: WinSCP .NET Assembly and COM Library :: WinSCP 一、先展示一下小工具的界面 1、…...

【Android 13源码分析】Activity生命周期之onCreate,onStart,onResume-1
忽然有一天,我想要做一件事:去代码中去验证那些曾经被“灌输”的理论。 – 服装…...

达梦数据库开启归档模式
目录 一、什么是归档模式? 二、开启归档模式的步骤 1、创建归档目录 2、进入dm数据库bin目录 3、登录数据库 4、关闭数据库 5、启动数据库到Mount状态 6、增加本地归档日志文件 7、开启归档 8、启动数据库 9、验证是否开启成功 三、开启归档模式的优…...
C++ 语言特性07 - 静态成员的初始化
一:概述 1. 静态成员变量通常在类定义内部声明,并在类定义外部定义和初始化。 class MyClass { public:static int staticVar; // 声明 };int MyClass::staticVar 42; // 定义和初始化 2. 从C11开始,可以在类内直接初始化静态数据成员&am…...

【数据结构】图论基础
文章目录 图的概念图的基本概念图的类型图的表示方法 图的相关基本概念1. 路径(Path)2. 连通性(Connectivity)3. 图的度(Degree)4. 子图(Subgraph)5. 生成树(Spanning Tr…...

HTML5实现好看的唐朝服饰网站模板源码2
文章目录 1.设计来源1.1 网站首页1.2 唐装演变1.3 唐装配色1.4 唐装花纹1.5 唐装文化 2.效果和源码2.1 动态效果2.2 源代码 源码下载万套模板,程序开发,在线开发,在线沟通 作者:xcLeigh 文章地址:https://blog.csdn.ne…...

golang web笔记-2.请求request
什么是request http消息分为request(请求) 和 response(响应) request:在go中是一个struct,代表了客户段发送的http请求,已可以通过request 的方法访问请求中的cookie、URL、User Agent…...
docker的安装与启动——配置国内Docker源
移除旧版本docker sudo yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine 配置docker yum源。 sudo yum install -y yum-utils sudo yum-config-manager –add-repo ht…...

httpsok-v1.17.0-SSL通配符证书自动续签
🔥httpsok-v1.17.0-SSL通配符证书自动续签 介绍 httpsok 是一个便捷的 HTTPS 证书自动续签工具,基于全新的设计理念,专为 Nginx 、OpenResty 服务器设计。已服务众多中小企业,稳定、安全、可靠。 一行命令,一分钟轻…...
相机、镜头参数详解以及相关计算公式
一、工业相机参数 1、分辨率 相机每次采集图像的像素点数,也是指这个相机总共有多少个感光晶片。在采集图像时,相机的分辨率对检测精度有很大的影响,在对同样打的视场成像时,分辨率越高,对细节的展示越明显。 相机像素…...

【微服务】组件、基础工程构建(day2)
组件 服务注册和发现 微服务模块中,一般是以集群的方式进行部署的,如果我们调用的时候以硬编码的方式,那么当服务出现问题、服务扩缩容等就需要对代码进行修改,这是非常不好的。所以微服务模块中就出现了服务注册和发现组件&…...

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造,完美适配AGV和无人叉车。同时,集成以太网与语音合成技术,为各类高级系统(如MES、调度系统、库位管理、立库等)提供高效便捷的语音交互体验。 L…...
应用升级/灾备测试时使用guarantee 闪回点迅速回退
1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间, 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点,不需要开启数据库闪回。…...

K8S认证|CKS题库+答案| 11. AppArmor
目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、切换节点 3)、切换到 apparmor 的目录 4)、执行 apparmor 策略模块 5)、修改 pod 文件 6)、…...

华为OD机试-食堂供餐-二分法
import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...

学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...

视频字幕质量评估的大规模细粒度基准
大家读完觉得有帮助记得关注和点赞!!! 摘要 视频字幕在文本到视频生成任务中起着至关重要的作用,因为它们的质量直接影响所生成视频的语义连贯性和视觉保真度。尽管大型视觉-语言模型(VLMs)在字幕生成方面…...
CMake控制VS2022项目文件分组
我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...
#Uniapp篇:chrome调试unapp适配
chrome调试设备----使用Android模拟机开发调试移动端页面 Chrome://inspect/#devices MuMu模拟器Edge浏览器:Android原生APP嵌入的H5页面元素定位 chrome://inspect/#devices uniapp单位适配 根路径下 postcss.config.js 需要装这些插件 “postcss”: “^8.5.…...

七、数据库的完整性
七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...

Python Ovito统计金刚石结构数量
大家好,我是小马老师。 本文介绍python ovito方法统计金刚石结构的方法。 Ovito Identify diamond structure命令可以识别和统计金刚石结构,但是无法直接输出结构的变化情况。 本文使用python调用ovito包的方法,可以持续统计各步的金刚石结构,具体代码如下: from ovito…...