【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  0
  1  1  0  0  0
  1  1  1  0  0
  1  1  1  1  0
  1  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 F

def attention(query, key, value, mask=None, dropout=None):
    """注意力机制的实现, 输入分别是query, key, value, mask: 掩码张量, 
       dropout是nn.Dropout层的实例化对象, 默认为None"""
    # 在函数中, 首先取query的最后一维的大小, 一般情况下就等同于我们的词嵌入维度, 命名为d_k
    d_k = query.size(-1)
    # 按照注意力公式, 将query与key的转置相乘, 这里面key是将最后两个维度进行转置, 再除以缩放系数根号下d_k, 这种计算方法也称为缩放点积注意力计算.
    # 得到注意力得分张量scores
    scores = 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进行随机置0
    if 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.1803
 1.5221 -0.3823  0.8414  0.7836 -0.8481
-0.0345 -0.8643  0.6476 -0.2713  1.5645
 0.8788 -2.2142  0.4022  0.1997  0.1474
 2.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  0
 0  0  0  0  0
 0  0  0  0  0
 0  0  0  0  0
 0  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.3391
   0.0000 -66.1486 -11.5123  ...   20.1519  -4.6823   0.4916

( 1 ,.,.) = 
 -24.8681   7.5495  -5.0765  ...   -7.5992 -26.6630  40.9517
  13.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.2600
   12.4904    0.0000   24.1575  ...     0.0000    2.5838   18.0647
  -32.5959   -4.6252  -29.1050  ...     0.0000  -22.6409  -11.8341
    8.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.7564
    0.0000  -29.9039   -3.0405  ...     0.0000   14.4408   14.8579
   30.7819    0.0000   21.3908  ...   -29.0746    0.0000   -5.8475
[torch.FloatTensor of size 2x4x512]

# 注意力张量:
p_attn: Variable containing:
(0 ,.,.) = 
  1  0  0  0
  0  1  0  0
  0  0  1  0
  0  0  0  1

(1 ,.,.) = 
  1  0  0  0
  0  1  0  0
  0  0  1  0
  0  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.5770
   0.4284  -7.4741   8.8839  ...    1.5618   0.5063   0.5770
   0.4284  -7.4741   8.8839  ...    1.5618   0.5063   0.5770
   0.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.2500
  0.2500  0.2500  0.2500  0.2500
  0.2500  0.2500  0.2500  0.2500
  0.2500  0.2500  0.2500  0.2500

(1 ,.,.) = 
  0.2500  0.2500  0.2500  0.2500
  0.2500  0.2500  0.2500  0.2500
  0.2500  0.2500  0.2500  0.2500
  0.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_k
        self.d_k = embedding_dim // head

        # 传入头数h
        self.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. """

        # 如果存在掩码张量mask
        if 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的注意力表示.

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

FOCShield v2.0.4原理图

1.FOCShield v2.0.4原理图,开源原文件用AD制作。用 AD09可以打开。 主要部分为 1.电机驱动芯片部分 2.电流采样部分

Windows 环境下安装 Anaconda 并适配到 PowerShell 的保姆级教程

Anaconda Anaconda 是一个流行的 Python 数据科学和机器学习平台&#xff0c;它包括了 Conda 包管理器、Python 以及数百个用于科学计算的库和工具。Anaconda 旨在简化包和环境管理&#xff0c;使得安装、更新和管理软件包变得容易&#xff0c;同时也能够轻松创建和切换不同的P…

大数据毕业设计选题推荐-民族服饰数据分析系统-Python数据可视化-Hive-Hadoop-Spark

✨作者主页&#xff1a;IT研究室✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…

【Linux】命令管道

一、命名管道的介绍 之前的管道博客中介绍的是匿名管道&#xff0c;这个管道的应用的一个限制就是只能在具有公共祖先&#xff08;具有亲缘关系&#xff09;的进程间通信。 如果我们不想在不相关的进程之间交换数据&#xff0c;可以使用FIFO文件来做这项工作&#xff0c;他经常…

输入捕获模式测频率PWMI模式测频率占空比

前沿知识&#xff1a;TIM输入捕获-CSDN博客 输入捕获相关函数 // 初始化输入捕获单元 // ICInit是4个通道共用一个函数的&#xff0c;第二个结构体参数&#xff0c;可以用来配置具体是哪个通道。 void TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);/…

【MAUI】CollectionView之 垂直网格

App主页或者导航页面中动态按钮的垂直网格布局 在 XAML 中,CollectionView 可以通过将其 ItemsLayout 属性设置为 VerticalGrid,在垂直网格中显示其项: <CollectionView ItemsSource="{Binding Monkeys}"ItemsLayout

TCP/UDP初识

TCP是面向连接的、可靠的、基于字节流的传输层协议。 面向连接&#xff1a;一定是一对一连接&#xff0c;不能像 UDP 协议可以一个主机同时向多个主机发送消息 可靠的&#xff1a;无论的网络链路中出现了怎样的链路变化&#xff0c;TCP 都可以保证一个报文一定能够到达接收端…

【YOLO学习】YOLOv2详解

文章目录 1. 概述2. Better2.1 Batch Normalization&#xff08;批归一化&#xff09;2.2 High Resolution Classifier&#xff08;高分辨率分类器&#xff09;2.3 Convolutional With Anchor Boxes&#xff08;带有Anchor Boxes的卷积&#xff09;2.4 Dimension Clusters&…

二分查找算法专题(1)

找往期文章包括但不限于本期文章中不懂的知识点&#xff1a; 个人主页&#xff1a;我要学编程(ಥ_ಥ)-CSDN博客 所属专栏&#xff1a; 优选算法专题 目录 二分查找算法的介绍 704. 二分查找 34. 在排序数组中查找元素的第一个和 最后一个位置 35. 搜索插入位置 69. x的平…

力扣题解 983

大家好&#xff0c;欢迎来到无限大的判断&#xff0c;祝大家国庆假期愉快 题目描述&#xff08;中等&#xff09; 最低票价 在一个火车旅行很受欢迎的国度&#xff0c;你提前一年计划了一些火车旅行。在接下来的一年里&#xff0c;你要旅行的日子将以一个名为 days 的数组给出…

Charles(青花瓷)抓取https请求

文章目录 前言Charles&#xff08;青花瓷&#xff09;抓取https请求 前言 如果您觉得有用的话&#xff0c;记得给博主点个赞&#xff0c;评论&#xff0c;收藏一键三连啊&#xff0c;写作不易啊^ _ ^。   而且听说点赞的人每天的运气都不会太差&#xff0c;实在白嫖的话&…

kafka下载配置

下载安装 参开kafka社区 zookeeperkafka消息队列群集部署https://apache.csdn.net/66c958fb10164416336632c3.html 下载 kafka_2.12-3.2.0安装包快速下载地址分享 官网下载链接地址&#xff1a; 官网下载地址&#xff1a;https://kafka.apache.org/downloads 官网呢下载慢…

2024/10/2 408 20题

c d d b b a b c b b a d c d a c

java基础 day1

学习视频链接 人机交互的小故事 微软和乔布斯借鉴了施乐实现了如今的图形化界面 图形化界面对于用户来说&#xff0c;操作更加容易上手&#xff0c;但是也存在一些问题。使用图形化界面需要加载许多图片&#xff0c;所以消耗内存&#xff1b;此外运行的速度没有命令行快 Wi…

【华为HCIP实战课程一】OSPF相关基础介绍及基础配置,网络工程师必修

一、OSPF介绍 开放式最短路径优先协议OSPF(Open Shortest Path First),IPv4使用的OSPFv2,针对IPv6使用OSPFv3协议。 二、为什么需要OSPF OSPF出现之前,网络广泛使用RIP路由协议,RIP由于最大16跳数限制无法适应大型网络,RIP是基于距离矢量算法的路由协议,应用在大型网…

过去8年,编程语言的流行度发生了哪些变化?PHP下降,Objective-C已过时

前天有一个汇总9个不同排名数据的“地表最强”编程语言排行榜&#xff0c;为了更好地理解语言流行度的变化&#xff0c;作者将2016年的类似调查结果与2024年的数据进行了比较。 虽然2016年的调查只包含6个排名&#xff0c;但它仍然提供了宝贵的参考数据。 我们来看看详细的情…

JSON的C实现(上)

JSON的C实现&#xff08;上&#xff09; JSON的C实现&#xff08;上&#xff09;前言JSON简介JSON的C实现思路小结 JSON的C实现&#xff08;上&#xff09; 前言 JSON是众多项目中较为常见的数据交换格式&#xff0c;为不同项目、系统间的信息交换提供了一个规范化标准。JSON…

Unity八股总结

这里写目录标题 OnEnable、Awake、Start运行时的发生顺序&#xff1f;哪些可能在同一个对象周期中反复的发生&#xff1f;动态加载资源的方式?Unity3d脚本从唤醒到销毁有着一套比较完整的生命周期&#xff0c;请列出系统自带的几个重要的方法。物理更新一般放在哪个系统函数里…

查找与排序-插入排序

排序算法可以分为内部排序和外部排序&#xff0c;内部排序是数据记录在内存中进行排序&#xff0c;而外部排序是因排序的数据很大&#xff0c;一次不能容纳全部的排序记录&#xff0c;在排序过程中需要访问外存。常见的内部排序算法有&#xff1a;插入排序、希尔排序、选择排序…

Arduino UNO R3自学笔记15 之 Arduino如何驱动数码管?

注意&#xff1a;学习和写作过程中&#xff0c;部分资料搜集于互联网&#xff0c;如有侵权请联系删除。 前言&#xff1a;学习使用数码管。 1.数码管介绍 数码管的一种是半导体发光器件&#xff0c;数码管可分为七段数码管和八段数码管&#xff0c;区别在于八段数码管比七段数…