CMU 10423 Generative AI:lec4(必读:Sliding Window Attention,RoPE, GQA)

news2024/11/14 19:04:54

lec4有4篇必读文献分别是:Sliding Window Attention,RoPE,GQA,以及花书的CNN第9.1~9.3节。前3个详细研究了一下,如下:

文章目录

  • 1 Sliding Window Attention(2020)
    • 1.1 概览
    • 1.2 个人总结
      • 1.2.1 问题背景
      • 1.2.2 Sliding Window Attention原理图
      • 总结:
      • 1.2.3 编程实现方法和效能
        • 1\. 编程实现方式
        • 2. 性能比较
      • 1.2.4 跟传统transformer模型精度对比
    • 1.3 几个问题
      • 自注意力机制消耗显存的原因是什么?
      • 使用滑窗注意力参数量会降低吗?
  • 2 RoPE(2021)
    • 2.1 概览
    • 2.2 原理详解
      • 2.2.1 RoPE的数据流、原理
        • 1. **输入**
        • 2. **生成Query和Key向量**
        • 3. **二维拆分**
        • 4. **计算𝜃并应用旋转矩阵进行位置编码**
        • 5. **生成带有位置编码的Query/Key**
        • 6. **后续自注意力计算**
        • 总结
      • 2.2.2 RoPE attention相对标准attention的计算过程对比
    • 2.3 一些问题
      • 2.3.1 为什么RoPE融入了绝对位置信息和相对位置信息?
        • 1. **绝对位置信息的融入**
        • 2. **相对位置信息的融入**
        • 3. **结合绝对位置和相对位置**
        • 总结
      • 2.3.2 为什么不直接对Q向量或K向量进行旋转矩阵操作,而是要分成d/2份?
        • 1. **旋转矩阵的维度要求**
        • 2. **分维度进行旋转保留了更多信息**
        • 3. **逐维旋转增强了模型的灵活性**
        • 4. **计算效率**
        • 5. **保持模型的向量结构**
        • 6. **符合自注意力机制的设计原则**
        • 总结:
      • 2.3.3 为什么 v 不需要引入位置信息?
        • 1. **自注意力机制中的角色分工**
        • 2. **为什么只给q和k引入位置信息?**
        • 3. **如果给v向量加入位置信息会怎样?**
        • 4. **总结:q和k注入位置信息的原因**
  • 3 GQA(2023)
    • 3.1 概览
    • 3.2 基本原理
      • 1. **Multi-head Attention(多头注意力)**
      • 2. **Grouped-query Attention(分组查询注意力,GQA)**
      • 3. **Multi-query Attention(多查询注意力,MQA)**
      • 总结:
    • 3.3 从公式和参数角度解释GQA工作原理
    • 3.4 精度和速度情况
      • 1. **精度(Accuracy)**
      • 2. **速度(Speed)**
      • 3. **平衡点**
      • 总结:

1 Sliding Window Attention(2020)

1.1 概览

全称:《Longformer: The Long-Document Transformer》
时间:2020年4月
作者人数:3人,美国西雅图某AI机构
论文地址:https://arxiv.org/pdf/2004.05150
简短总结在确保精度和不降低参数量情况下,通过采用局部注意力而非全局注意力,极大降低transformer模型的计算复杂度和显存消耗,从而能够允许模型接受更长的token输入。

推荐参考博客:

  • 《Longformer 详解》

    • https://wmathor.com/index.php/archives/1509/
    • 总结的很全面,基本源自其他2篇博客的综合+个人理解。
  • 《Longformer: The Long-Document Transformer》

    • https://aistudio.baidu.com/projectdetail/2307544
    • 相当于快速全局介绍,写的不错。

1.2 个人总结

1.2.1 问题背景

回顾transformer中multi head attention内部结构,如下图;

在这里插入图片描述

上图只画了3个输入token。在传统的自注意力机制中,每个 token 都需要与序列中的所有其他 token 进行计算,目的是为了让每个token都能看到全局信息,但这也导致计算复杂度和显存消耗随着序列长度呈平方级增长。因此,这极大地限制了模型的输入长度,例如 BERT 模型的最大可接受序列长度为 512。

1.2.2 Sliding Window Attention原理图

优化思路很好理解,就是不再让每个token都与其它所有token进行计算,而只是让相邻token之间进行注意力计算。具体有3种方法,如下图;

在这里插入图片描述

提前备注:

  • 上图矩形的横纵坐标相当于输入token的长度或token索引号,绿色小格子代表某token与另一个位置的token进行计算。
  • 上图序列长度、w的宽度等只是示意,该论文中跑的模型输入序列长度达到2000 token,w宽度达到512。
  • 在不同层,参数设置可以不一样,就像卷积层,可以是77,55, 3*3的混用。

图中包含四种注意力模式,分别为:

1. (a) Full n 2 n^2 n2 Attention
  • 全局自注意力(Full Self-Attention):这是标准的 Transformer 注意力机制。每个 token 都与所有其他 token 进行计算,计算复杂度为 O ( n 2 ) O(n^2) O(n2)
  • 特点
    • 每个 token 能够看到整个序列中的其他所有 token。
    • 由于其计算和存储需求会随着序列长度呈平方增长,处理长序列的效率低。
    • 优点是可以获得全局的上下文信息,但对于长序列任务(如长文档分类)非常不适合。
2. (b) Sliding Window Attention
  • 滑动窗口注意力:每个 token 只会关注自己前后一定范围的 token(通常是固定大小的窗口,窗口内的 token 之间进行注意力计算)。
  • 特点
    • 只进行局部注意力计算,计算复杂度为 O ( n × w ) O(n \times w) O(n×w) w w w 是窗口大小)。
    • 适用于长序列任务,因为它能显著减少计算量和显存消耗。
    • 缺点是只能捕捉局部上下文,不能直接获取全局信息。
3. © Dilated Sliding Window Attention
  • 扩张滑动窗口注意力:这是滑动窗口注意力的扩展版本。通过引入膨胀(dilation)机制,在滑动窗口内留出间隔,使模型可以关注更远的 token,而不增加窗口的计算量。
  • 特点
    • 膨胀操作增加了感受野,使得模型能够在计算效率保持的同时,获取到更远距离的上下文信息。
    • 计算复杂度仍然为 O ( n × w ) O(n \times w) O(n×w)
    • 这种机制结合了局部信息和较远的上下文,适合需要部分全局信息的任务。
4. (d) Global + Sliding Window Attention
  • 全局 + 滑动窗口注意力:结合了滑动窗口和全局注意力的机制。在某些关键 token 上(如问题 token 或 [CLS] token),引入全局注意力,使它们可以看到整个序列中的所有 token,而其他 token 仍然使用滑动窗口机制。
  • 特点
    • 通过全局注意力的引入,关键 token 能够获取全局上下文信息,同时保持滑动窗口的计算效率。
    • 适用于需要结合局部和全局信息的任务,如问答系统、文本分类等。

总结:

  • 全局自注意力 提供了全面的上下文信息,但计算开销高,不适合处理长文档。

  • 滑动窗口注意力 通过局部计算显著减少了开销,但无法获得全局信息。

    • 【备注:尽管单层模型无法获取全局信息,但通过堆叠多层 encoder,越高层的注意力机制能够覆盖更大的感受野,类似于卷积神经网络中的 3x3 卷积,随着层数的增加,模型的感受野逐层扩大,逐步捕获全局上下文信息。】
  • 扩张滑动窗口 使得模型在局部计算的基础上能够捕捉到更远的上下文,扩展了感受野。

  • 全局+滑动窗口注意力 则结合了两者的优点,在效率与全局信息捕捉之间取得平衡。

1.2.3 编程实现方法和效能

1. 编程实现方式

Longformer 的注意力实现基于三种不同的技术来优化计算和内存使用:

  • Longformer-loop(循环实现)

    • 这种方式使用了循环来计算每个对角线的矩阵乘法。
    • 虽然这种方法内存效率很高,因为它只计算非零值,但其速度非常慢,几乎无法实际使用,仅用于测试目的。
  • Longformer-chunks(分块实现)

    • 通过将查询(Q)和键(K)矩阵分割成大小为 w × w w \times w w×w 的重叠块,然后进行分块的矩阵乘法,并对非对角元素进行掩码处理。
    • 这种方法在计算效率上非常高,因为它利用了 PyTorch 中的单一矩阵乘法操作,但由于它仍然会计算一些零值,所以其内存占用是完全优化实现的两倍。
    • 适合在预训练/微调阶段使用,内存消耗虽然增加,但在实际应用中不是大问题。
  • Longformer-cuda(CUDA 实现)

    • 使用 TVM(Tensor Virtual Machine)库开发的自定义 CUDA 内核,实现了最优化的稀疏矩阵乘法。
    • 这种实现方式既支持膨胀滑动窗口注意力,又是最为节省内存的,同时与标准的全自注意力机制一样快,适合自回归语言建模等需要处理长序列的任务。
2. 性能比较

在这里插入图片描述

  • 如图所示,Longformer 的内存使用量随着序列长度线性增长,而标准的全自注意力机制在较长序列下会出现内存耗尽的情况。
  • Longformer-chunks 的分块实现是最快的,而 Longformer-loop 由于其内存效率高,但速度较慢,仅用于测试。

1.2.4 跟传统transformer模型精度对比

  1. 任务级别的性能比较
  • 在多个长文档自然语言处理任务上(如文本分类、问答和共指消解),经过预训练和微调的 Longformer 模型表现优于 RoBERTa 等基于 Transformer 的标准模型。Longformer 能够处理的序列长度达到了 4096,比 RoBERTa 支持的 512 长度大幅增加。

  • 在一些数据集(如 WikiHop 和 TriviaQA)上,Longformer 还达到了新的 SOTA(State-of-the-Art)性能。

  • 模型参数与性能

    • 在对比模型参数和性能时,Longformer 在类似的参数量下,能够达到甚至超过常规 Transformer 模型的表现。例如,在文本8(text8)和百科8(enwik8)数据集上,Longformer 取得了新的 SOTA 成绩。

这些对比显示了 Longformer 在处理长序列任务时,在性能和效率上都具有显著的优势。

1.3 几个问题

自注意力机制消耗显存的原因是什么?

消耗显存的主要原因与 **自注意力机制 **的计算方式和存储需求有关,具体原因如下:

  1. 注意力矩阵的大小

在自注意力机制中,每个 token 都需要与序列中的所有其他 token 进行计算。这意味着对于一个长度为 n n n​ 的序列,模型需要计算一个 n × n n \times n n×n​ 的注意力矩阵。这个矩阵会占用大量的显存,因为随着序列长度的增加,注意力矩阵的大小呈平方级增长 O ( n 2 ) O(n^2) O(n2)​。例如,长度为512的序列产生的注意力矩阵是 512 × 512 512 \times 512 512×512​,但长度为2048的序列则需要存储一个更大的 2048 × 2048 2048 \times 2048 2048×2048​ 矩阵,显存消耗大幅增加。

  1. 梯度存储

在反向传播时,模型不仅需要存储前向传播计算的注意力矩阵,还需要存储计算梯度所需的中间结果。随着序列长度的增加,模型需要保存更多的中间计算状态和梯度信息,从而占用更多的显存。

  1. 多头注意力机制

现代 Transformer 模型通常使用多头注意力机制(Multi-head Attention),其中每个注意力头都需要单独计算一个 n × n n \times n n×n​ 的注意力矩阵。这进一步增加了显存的消耗,因为每个头的计算都需要独立的存储空间。

  1. 激活值与参数的存储

除了注意力矩阵外,模型的每一层还会产生大量的激活值(activation)和参数,这些都需要占用显存。而随着序列长度增加,激活值的存储需求也会呈线性增加。

总结

显存的消耗主要源自于自注意力机制中需要存储大量的 n × n n \times n n×n​ 注意力矩阵、多头注意力的独立计算、以及反向传播时中间计算结果和梯度信息的存储。

使用滑窗注意力参数量会降低吗?

滑动窗口注意力机制(Sliding Window Attention)本身不会减少模型的参数量,因为它的参数与常规 Transformer 的参数是相同的,主要由查询矩阵 Q Q Q、键矩阵 K K K、值矩阵 V V V 以及多头注意力机制中的线性变换层所决定。滑窗法的主要优势体现在计算复杂度和内存使用的降低,而非参数量的减少。

具体原因:

  1. 参数量来源

Transformer 模型的参数量主要由多头注意力中的矩阵 Q Q Q​、 K K K​、 V V V​ 以及 feed-forward 全连接层决定,这些部分在滑动窗口注意力机制和常规自注意力机制中保持不变。因此,滑窗法并不会减少这些线性变换的参数量。

  1. 计算复杂度和内存

滑动窗口注意力通过限制每个 token 仅与窗口内的 token 进行交互,避免了与整个序列的所有 token 进行全局交互,从而减少了计算复杂度和内存开销。传统的全局自注意力计算量是 O ( n 2 ) O(n^2) O(n2)​,而滑窗法降低到了 O ( n × w ) O(n \times w) O(n×w)​(其中 w w w​ 是窗口大小),但这并不影响参数量。

  1. 多层模型的感受野

滑动窗口方法的主要贡献在于通过多层堆叠来扩大感受野,而不增加计算和内存消耗。这使得它可以在计算开销更低的情况下处理更长的序列,但模型参数量仍保持相同。

总结:

滑动窗口注意力不会减少模型的参数量,它的优势在于减少了计算复杂度和内存消耗,使得模型能够高效处理长序列任务。

2 RoPE(2021)

2.1 概览

全称:《ROFORMER: ENHANCED TRANSFORMER WITH ROTARY POSITION EMBEDDING》
时间:2021年4月
作者人数:6人,深圳追一科技公司
论文地址:https://arxiv.org/pdf/2004.05150
简短总结RoPE本质相当于去除transformer里传统的位置编码模块,直接在attention内部分别对Q和K进行某种旋转矩阵操作,将【绝对位置信息】嵌入Q和K中,并且后续Q和K进行点积运算时,还能自然地捕捉到【相对位置信息】。
注意:RoPE是融入到attention模块内部的,所以模型中会出现很多次。

个人感觉最好的参考博客:

  • https://fancyerii.github.io/2023/09/15/rope/

2.2 原理详解

2.2.1 RoPE的数据流、原理

回顾transformer结构,输入数据经过embedding编码后,会与位置编码数据进行融合,再输入到attention中,如下图:

在这里插入图片描述

而RoPE编码方式去掉了上图的位置编码,转而直接对attention中得到的Q向量和K向量进行位置编码操作。具体数据流和原理如下图:

在这里插入图片描述

1. 输入
  • 输入的是经过word embedding后生成的词向量。这些向量表示序列中的每个词,但最初并不包含位置信息。
2. 生成Query和Key向量
  • 通过线性变换矩阵 W q W_q Wq W k W_k Wk,输入的词向量分别被转化为querykey向量。它们的维度均为 d d d(通常是较高的维度,比如768或1024)。
3. 二维拆分
  • 对高维的query和key向量进行维度拆分,将它们分成若干对二维子向量(如图中的每对彩色块)。这些子向量将分别进行独立处理。
4. 计算𝜃并应用旋转矩阵进行位置编码
  • 每一对二维子向量通过一个旋转矩阵来注入位置信息。旋转角度 θ i \theta_i θi 根据以下公式计算:

θ i = 1000 0 − 2 ( i − 1 ) / d \theta_i = 10000^{-2(i-1)/d} θi=100002(i1)/d

  • i i i 是当前二维子向量的索引, d d d 是向量的总维度。每个维度对都有一个不同的旋转角度 θ i \theta_i θi
  • 旋转矩阵 R θ m R_{\theta_m} Rθm 的公式为:

R θ m = [ cos ⁡ ( m θ i ) − sin ⁡ ( m θ i ) sin ⁡ ( m θ i ) cos ⁡ ( m θ i ) ] R_{\theta_m} = \begin{bmatrix} \cos(m\theta_i) & -\sin(m\theta_i) \\ \sin(m\theta_i) & \cos(m\theta_i) \end{bmatrix} Rθm=[cos(mθi)sin(mθi)sin(mθi)cos(mθi)]

其中, m m m 是token在序列中的位置, θ i \theta_i θi 是根据维度计算出来的旋转角度。

  • 这个旋转矩阵会应用于query和key的每一对二维子向量,从而将token的位置信息注入到query和key向量中。
5. 生成带有位置编码的Query/Key
  • 经过旋转矩阵处理之后,query和key向量已经包含了词语的位置信息。这些向量不仅保留了原始语义信息,还能够反映词语之间的相对位置信息。
6. 后续自注意力计算
  • 带有位置编码的query和key向量接下来会进入自注意力机制,进行qk点积计算以获得注意力权重,并结合value向量进行加权求和,完成整个自注意力计算。
总结
  • RoPE方法中,输入的高维query和key向量首先被拆分成二维子向量,随后通过基于token位置的旋转矩阵注入位置信息,生成带有位置编码的query和key向量。这些向量最终进入自注意力机制,完成Transformer的标准计算流程。

2.2.2 RoPE attention相对标准attention的计算过程对比

在10423课程lec4中,有4张PPT在讲RoPE,第一张是2.2.1中那个原理图,后面3张是在讲具体怎么计算的。我逐一贴出来介绍下:

第一张图

在这里插入图片描述

1. 标准注意力机制(左侧黄色框内)
  • 传统的自注意力机制中:
    • q j = W q T x j q_j = W_q^T x_j qj=WqTxj​:Query向量由输入词向量 x j x_j xj​ 通过线性变换矩阵 W q W_q Wq​ 得到。
    • k j = W k T x j k_j = W_k^T x_j kj=WkTxj​:Key向量同样由输入词向量 x j x_j xj​ 通过线性变换矩阵 W k W_k Wk​ 得到。
    • s t , j = k j T q t / ∣ k ∣ s_{t,j} = k_j^T q_t / \sqrt{|k|} st,j=kjTqt/k ​:注意力分数由Query和Key的点积计算得到,并且会被归一化。
    • 最后,经过Softmax,得到注意力权重 a t = softmax ( s t ) a_t = \text{softmax}(s_t) at=softmax(st)​。
2. RoPE机制中的注意力计算(右侧绿色框内)
  • Query和Key的初步生成与标准机制一致

    • q j = W q T x j q_j = W_q^T x_j qj=WqTxj k j = W k T x j k_j = W_k^T x_j kj=WkTxj:依然是通过线性变换从输入词向量中得到。
  • 通过旋转矩阵编码位置信息

    • 这里,RoPE对Query和Key向量进行了基于位置的旋转操作。通过旋转矩阵 R θ , m R_{\theta,m} Rθ,m 对Query和Key向量进行旋转:
      • q ~ j = R θ , j q j \tilde{q}_j = R_{\theta,j} q_j q~j=Rθ,jqj:Query向量被旋转。
      • k ~ j = R θ , j k j \tilde{k}_j = R_{\theta,j} k_j k~j=Rθ,jkj:Key向量同样被旋转。
    • R θ , j R_{\theta,j} Rθ,j 是一个旋转矩阵,具体表示在公式中,对应不同的绝对位置 m m m 和不同维度的旋转角度 θ i \theta_i θi
  • 旋转后的注意力计算

    • s t , j = k ~ j T q ~ t / d k s_{t,j} = \tilde{k}_j^T \tilde{q}_t / \sqrt{d_k} st,j=k~jTq~t/dk :带有位置编码的Query和Key向量参与点积计算,从而得到带有位置信息的注意力分数。
第二张图

在这里插入图片描述

1. RoPE旋转矩阵的高效计算
  • 旋转矩阵的稀疏模式

    • R θ , m R_{\theta,m} Rθ,m 具有稀疏的“块结构”,每对相邻维度使用独立的旋转矩阵。这个稀疏块结构允许更加高效的矩阵向量乘法运算。
  • 高效矩阵乘法展示

    • 右侧公式展示了如何对任意向量 y y y 应用旋转矩阵 R θ , m R_{\theta,m} Rθ,m。例如,假设 y y y 是长度为 d k d_k dk 的向量:
      • 第一部分: y 1 , y 2 , . . . y d / 2 y_1, y_2, ... y_{d/2} y1,y2,...yd/2 会通过与 cos ⁡ ( m θ ) \cos(m\theta) cos(mθ) 相乘,并且通过交换顺序项实现计算。
      • 第二部分:对应维度的项会与 sin ⁡ ( m θ ) \sin(m\theta) sin(mθ) 相乘,同样是通过交换维度顺序计算。
    • 这种方式通过分解矩阵乘法,显著减少了计算复杂度。
第3张图

在这里插入图片描述

这张图展示了Rotary Position Embedding (RoPE) 的矩阵版本,进一步解释了如何高效计算RoPE,并将其与标准的自注意力机制进行对比。

1. 上半部分(绿色区域)解释:RoPE机制与标准注意力机制的矩阵版本
  • RoPE注意力机制

    • 与前面的RoPE定义一致,RoPE通过旋转矩阵 R θ , j R_{\theta, j} Rθ,j​ 对Query ( q j q_j qj​) 和 Key ( k j k_j kj​) 向量进行旋转,表示为:

    q ~ j = R θ , j q j , k ~ j = R θ , j k j \tilde{q}_j = R_{\theta, j} q_j, \quad \tilde{k}_j = R_{\theta, j} k_j q~j=Rθ,jqj,k~j=Rθ,jkj

    • 然后,使用带有旋转位置信息的 Query 和 Key 进行点积计算,得到注意力分数。
  • 矩阵版本

    • 在矩阵版本中,将 Query 和 Key 的计算表示为:

    Q = X W q , K = X W k Q = X W_q, \quad K = X W_k Q=XWq,K=XWk

    其中 X X X​ 是输入词嵌入, W q W_q Wq​ 和 W k W_k Wk​ 是线性变换矩阵。这里 Q Q Q​ 和 K K K​ 表示所有词的 Query 和 Key 向量矩阵。

    • 之后,RoPE对整个 Query 矩阵 Q Q Q​ 和 Key 矩阵 K K K​ 进行旋转操作:

    Q ~ = g ( Q ; Θ ) , K ~ = g ( K ; Θ ) \tilde{Q} = g(Q; \Theta), \quad \tilde{K} = g(K; \Theta) Q~=g(Q;Θ),K~=g(K;Θ)

    其中 g ( ⋅ ; Θ ) g(\cdot; \Theta) g(;Θ)​ 是位置编码函数,它根据旋转角度 Θ \Theta Θ​ 对向量进行旋转操作。

    • 最终,计算注意力分数矩阵 S S S​:

    S = Q ~ K ~ T / d k S = \tilde{Q} \tilde{K}^T / \sqrt{d_k} S=Q~K~T/dk

    通过Softmax函数得到注意力权重矩阵 A A A​:

    A = softmax ( S ) A = \text{softmax}(S) A=softmax(S)

2. 下半部分(蓝色区域)解释:构建新的矩阵 Y ~ \tilde{Y} Y~ 以便进行旋转
  • 目标:在蓝色区域中,目标是构造一个新的矩阵 Y ~ \tilde{Y} Y~​,其每一行表示经过旋转矩阵 R θ , m R_{\theta,m} Rθ,m​ 作用后的向量:

Y ~ m , . = R θ , m Y m \tilde{Y}_{m,.} = R_{\theta,m} Y_m Y~m,.=Rθ,mYm

这意味着我们要对矩阵 Y Y Y​ 中的每一行向量 Y m Y_m Ym​ 进行旋转矩阵操作,从而将位置信息编码到 Y Y Y​ 中。

  • 旋转矩阵的分块计算

    • 为了更高效地实现这种旋转操作,使用了矩阵的分块结构。
    • 在蓝色部分公式中,矩阵 C C C​ 中存储了旋转角度的余弦和正弦函数:

    C = [ 1 θ 1 … 1 θ d / 2 … … … N θ 1 … N θ d / 2 ] C = \begin{bmatrix} 1 \theta_1 & \dots & 1 \theta_{d/2} \\ \dots & \dots & \dots \\ N \theta_1 & \dots & N \theta_{d/2} \end{bmatrix} C= 1θ1Nθ11θd/2Nθd/2

    这个矩阵定义了不同位置和维度的旋转角度。

    • 最终旋转操作公式:矩阵 Y ~ \tilde{Y} Y~​ 可以通过如下方式计算:

    Y ~ = [ Y : , 1 : d / 2    Y : , d / 2 + 1 : d ] ⊗ cos ⁡ ( C ) + [ − Y : , d / 2 + 1 : d    Y : , 1 : d / 2 ] ⊗ sin ⁡ ( C ) \tilde{Y} = \left[ Y_{:,1:d/2} \, \, Y_{:,d/2+1:d} \right] \otimes \cos(C) + \left[ -Y_{:,d/2+1:d} \, \, Y_{:,1:d/2} \right] \otimes \sin(C) Y~=[Y:,1:d/2Y:,d/2+1:d]cos(C)+[Y:,d/2+1:dY:,1:d/2]sin(C)

    这意味着,矩阵 Y Y Y​ 的前一半维度 Y : , 1 : d / 2 Y_{:,1:d/2} Y:,1:d/2​ 和后一半维度 Y : , d / 2 + 1 : d Y_{:,d/2+1:d} Y:,d/2+1:d​会分别通过余弦和正弦函数进行旋转计算,并通过拼接操作得到新的矩阵 Y ~ \tilde{Y} Y~​。

3张图总结:
  • 第一张图解释了RoPE如何在自注意力机制中对Query和Key向量进行旋转操作,从而注入位置信息,使得模型可以同时考虑绝对和相对位置。
  • 第二张图展示了RoPE旋转矩阵的稀疏结构如何帮助提升计算效率。通过将高维向量分解为多个二维子向量进行旋转,模型能够在不增加过多计算开销的情况下完成矩阵乘法。
  • 第三张图:这张图主要展示了RoPE在矩阵形式下的高效计算过程。首先对整个Query和Key矩阵进行线性变换,生成 Q Q Q​ 和 K K K​。接着,RoPE通过旋转矩阵 R θ , m R_{\theta,m} Rθ,m​ 对它们进行旋转编码,最终使用带有位置信息的向量矩阵进行点积计算。在下半部分,通过矩阵的块结构与三角函数的组合,使得RoPE可以更加高效地对每一行向量进行旋转,增强了计算效率,同时保持了位置信息的编码效果。

2.3 一些问题

2.3.1 为什么RoPE融入了绝对位置信息和相对位置信息?

让我用更加通俗的方式解释RoPE如何同时融入绝对位置相对位置的信息,结合原图来帮助理解:

1. 绝对位置信息的融入
  • 在图中,每个词的向量(如 ( x 1 , x 2 ) (x_1, x_2) (x1,x2)​)本来只是反映这个词的语义,没有包含任何位置信息。
  • 绝对位置表示的是这个词在序列中的具体位置(例如,这个词是第1个词还是第5个词)。为了让模型知道这个词在哪个位置,我们给每个词加上位置信息
  • RoPE使用的是旋转矩阵,它会根据词在序列中的位置 m m m​(图中红色的 m m m​)对词的向量进行旋转,旋转角度与位置 m m m​ 和维度相关的角度 θ \theta θ​ 有关。这个旋转过程就是对每个词的位置进行编码。
    • 例子:假设有两个词,分别在序列中的第1个位置和第3个位置,它们会根据各自的位置进行不同的旋转。图中的 x 1 x_1 x1​ 和 x 2 x_2 x2​ 被旋转成了新的 ( x 1 ′ , x 2 ′ ) (x'_1, x'_2) (x1,x2)​,这个旋转是基于它们的绝对位置
    • 通过这种旋转操作,词语的向量就包含了这个词的绝对位置信息(词语是第几个词)。
2. 相对位置信息的融入
  • 相对位置表示的是两个词之间的距离,或者说它们在序列中彼此之间的相对关系。RoPE的编码方式可以同时捕捉这种相对位置信息,这是它的一个关键优势。
  • 当我们计算query和key向量之间的点积(这个点积决定了两个词之间的相关性),因为每个词的向量已经经过了基于绝对位置的旋转,所以两个词的位置差异就反映在了它们的旋转角度上。
    • 例子:假设我们有两个词,位置分别是第1个词和第3个词,它们经过不同的旋转之后,旋转的角度差异会影响它们的点积结果。如果这两个词位置接近(例如第1个和第2个词),它们的旋转角度差异很小,点积结果会更大,表示它们相关性较高。如果这两个词位置很远(例如第1个和第10个词),旋转角度差异大,点积结果会更小,表示它们的相关性较弱。
    • 这种通过旋转角度来编码的方式,能够让模型捕捉到词与词之间的相对距离,即词语之间的相对位置
3. 结合绝对位置和相对位置
  • 绝对位置是通过旋转角度直接编码进每个词的向量的,这样模型知道每个词在序列中的具体位置。
  • 相对位置则是在计算两个词的相似度时,通过它们旋转角度的差异自然反映出来。两个词旋转得越接近,它们的位置也就越接近,相似度就越高;旋转得越不同,位置差距越大,相似度越低。
总结
  1. 绝对位置信息:每个词通过旋转矩阵,根据它在序列中的位置 m m m​ 进行旋转,旋转角度决定了这个词的绝对位置。
  2. 相对位置信息:当两个词计算相似度(点积)时,旋转角度的差异反映了它们的相对距离,因此模型可以感知词语之间的相对位置关系。

RoPE的优势就在于,它通过一种简单的旋转操作,能够同时让模型感知到词语的绝对位置和相对位置,并且与自注意力机制完美结合。

2.3.2 为什么不直接对Q向量或K向量进行旋转矩阵操作,而是要分成d/2份?

Q Q Q K K K 向量分成 d / 2 d/2 d/2(每对相邻维度形成一个二维子向量)进行旋转,而不是直接对整个向量进行旋转,是因为旋转矩阵的数学性质以及实际实现中的高效性和模型性能优化的考虑。以下是几个主要原因:

1. 旋转矩阵的维度要求
  • 旋转矩阵在数学上通常是用于二维三维空间的操作。一个标准的二维旋转矩阵表示为:

在这里插入图片描述

  • 这种二维旋转矩阵只能用于两个维度的向量。如果直接应用于高维向量(例如768维的 Q Q Q​ 或 K K K​ 向量),则需要构建更复杂的高维旋转矩阵,但这在计算上是非常复杂且不直观的。
  • 因此,将高维向量拆分为多个二维子向量,对每个二维子向量进行旋转,是一个有效且可行的方案。这使得旋转操作更简单、更具计算效率。
2. 分维度进行旋转保留了更多信息
  • 如果直接对整个高维向量进行旋转,意味着所有维度都将按照同一个旋转角度进行全局变化,这可能会导致部分信息的丢失,尤其是在高维空间中,不同维度往往具有不同的语义。
  • 通过将高维向量分成多个二维子向量,并对每对子向量应用独立的旋转矩阵,每个维度对可以根据各自的特点进行独立旋转,从而更好地保留了向量的结构和信息。这种分维度的旋转使得RoPE能够对不同的维度分别进行处理,增强了模型对语义和位置信息的建模能力。
3. 逐维旋转增强了模型的灵活性
  • RoPE采用每对相邻维度之间进行独立的旋转操作,能够引入不同维度间的位置信息。这种设计让模型可以更加灵活地捕捉序列中的信息,并且不同维度对的旋转角度 θ \theta θ​随着维度变化而不同,确保了模型可以处理复杂的序列依赖。
  • 换句话说,这种按维度对旋转可以让模型在不同层面上捕捉词语间的相对位置关系,而不是一刀切地对所有维度施加相同的位置信息。
4. 计算效率
  • 对每对二维子向量使用旋转矩阵操作,在计算上是非常高效的。二维旋转矩阵的计算复杂度是固定的 O ( 1 ) O(1) O(1)​,并且可以轻松并行化处理多个维度对的旋转。
  • 如果直接对整个高维 Q Q Q​ 或 K K K​ 向量进行旋转,需要构造一个更大的旋转矩阵,并且该操作的复杂度会随着维度数急剧增加。这样会大幅增加计算量,影响模型的训练效率和推理速度。
5. 保持模型的向量结构
  • Transformer中的 Q Q Q​ 和 K K K​ 向量在自注意力机制中,通常保持着一定的结构,分维度旋转可以在不破坏原始向量语义的前提下进行位置信息编码。
  • 通过将高维向量分解为多个二维子向量,我们确保了每对相邻维度在旋转操作后仍然能够保持彼此之间的关系,而不会引入全局的混乱。
6. 符合自注意力机制的设计原则
  • Transformer的自注意力机制设计初衷是通过 Q Q Q​ 和 K K K​ 的点积来计算词之间的相关性。这种点积运算在分维度旋转后,仍然能有效地捕捉到不同位置之间的相对距离关系,且维持原有的计算模式不变。
  • 分维度旋转的结果是:位置信息不仅仅被加到某个特定维度上,而是分散地嵌入到每对维度中。这符合Transformer中分维度进行计算的设计逻辑,使得RoPE与自注意力机制完美结合。
总结:

将高维 Q Q Q K K K 向量分成 d / 2 d/2 d/2 对二维子向量并分别应用旋转矩阵,是为了:

  1. 适应旋转矩阵的二维性质
  2. 保留更多信息,允许不同维度对独立旋转;
  3. 提高灵活性,增强模型在不同维度上对相对位置信息的捕捉;
  4. 提高计算效率,避免直接对高维向量进行旋转的复杂性;
  5. 保持向量结构,确保不会破坏向量的原有语义;
  6. 符合自注意力机制的设计,确保计算点积时可以有效捕捉词与词之间的相对位置关系。

通过这种方式,RoPE可以在不增加计算复杂度的情况下,高效地为模型注入位置信息,同时保持高维语义结构的完整性。

2.3.3 为什么 v 不需要引入位置信息?

这是一个很好的问题。value向量( v v v在自注意力机制中承担的是不同的角色,它与query q q q)和key k k k)向量的作用有所不同,因此在大多数实现中并没有注入位置信息。接下来我将详细解释这个问题。

1. 自注意力机制中的角色分工

在自注意力机制中,querykeyvalue 向量的角色如下:

  • query( q q q:用于计算当前词与其他词之间的相似度。
  • key( k k k:用于与query计算点积,得到注意力分数(即相似度)。
  • value( v v v:在根据计算出的注意力分数后,最终用于计算输出。这表示的是词本身的“信息内容”。

因此,qk 主要用于计算注意力权重,也就是说,它们决定了哪些词应该获得更多的注意力。而 v 是将这些注意力权重应用到的内容,也就是最终要输出的信息。

2. 为什么只给q和k引入位置信息?

qk 在自注意力机制中计算相似度时需要考虑位置关系,因为:

  • q 和 k 决定哪些词之间应该有更多的相似性,而词语之间的相对位置在自然语言中通常是非常重要的。比如在一句话中,距离较近的词语往往具有更强的相关性,RoPE等方法通过引入位置信息来帮助模型捕捉这种相关性。
  • 引入位置信息使模型能够更好地理解“哪两个词更相关”,因为语言的语义结构不仅依赖词汇本身,还依赖于词汇之间的相对顺序。

相反,v向量只是代表词语本身的内容信息。自注意力机制使用的权重是基于q和k之间的相似度计算得来的,v 只是携带这些信息并进行加权求和。换句话说,v向量本身的作用是“传递”信息,而不是决定哪些词有更强的相关性

3. 如果给v向量加入位置信息会怎样?

如果对v向量也注入位置信息,可能会引入一些不必要的复杂性:

  • 冗余信息:在计算最终输出时,注意力权重(来自q和k的计算)已经决定了相对位置信息。如果v也引入位置信息,模型可能会过度关注词的位置,导致信息的重复编码,并且可能会使模型变得难以训练。
  • 信息混淆:v向量的主要作用是传递语义信息,而不是反映词语的位置。将位置信息注入v可能会导致语义信息和位置信息混淆,减弱模型对实际内容的关注。
4. 总结:q和k注入位置信息的原因
  • q和k决定注意力权重:q和k是自注意力机制中的关键,它们决定了哪些词之间的相似度高,因此必须考虑相对位置关系。
  • v只负责传递信息:v向量携带的是词本身的语义内容,因此不需要加入位置信息,避免引入不必要的复杂性。

通过这种设计,Transformer模型在捕捉词语之间的相对位置关系的同时,也能保持语义信息的完整性。这就是为什么我们只对qk进行位置编码,而不对v进行位置编码的原因。

3 GQA(2023)

3.1 概览

全称:《GQA: Training Generalized Multi-Query Transformer Models from Multi-Head Checkpoints》
时间:2023年5月
作者人数:6人,谷歌
论文地址:https://arxiv.org/pdf/2004.05150
简短总结通过采用局部注意力而非全局注意力,极大降低了transformer模型的计算复杂度和显存消耗,从而能够允许模型接受更多的token输入。

3.2 基本原理

先回顾transformer中单头和多头注意力内部结构:

在这里插入图片描述

论文中有个图解释了3种注意力机制区别:

在这里插入图片描述

1. Multi-head Attention(多头注意力)

  • 机制:在传统的多头注意力机制中,每个查询头(Query)都会有独立的键(Key)和值(Value)头。

  • 工作流程

    • 例如,图中有8个查询头,每个查询头都拥有对应的独立的Key和Value。
    • 每个查询头都与它各自的Key和Value进行计算,这使得模型可以在不同的子空间里处理不同的信息。
  • 优点:这种机制能够很好地捕捉序列中不同位置之间的复杂关系,因为每个头都有独立的Key和Value。

  • 缺点:计算量非常大,尤其是在处理大规模模型时,Key和Value头的数量成倍增加,导致计算效率低。

2. Grouped-query Attention(分组查询注意力,GQA)

  • 机制:GQA是一种折中方案,它通过让一组查询头共享同一个Key和Value头来减少计算量。

  • 工作流程

    • 图中展示了8个查询头,但它们被分成了4组。每组2个查询头共享一个Key和Value头,而不是每个查询头都有自己的Key和Value。
    • 这样,每个Key和Value被多个查询头复用,减少了Key和Value的数量,但仍保留了部分多头注意力的优势。
  • 优点:相比完全独立的多头注意力,GQA大大减少了Key和Value的数量,因此能够提高计算效率和节省内存。同时,查询头仍然保留一定的独立性,可以捕捉不同的模式。

  • 缺点:因为多个查询头共享Key和Value,所以相比完全独立的多头注意力,模型可能在一些复杂的依赖关系上表现得稍弱,但这种影响通常很小。

3. Multi-query Attention(多查询注意力,MQA)

  • 机制:在MQA中,所有查询头都共享同一个Key和Value头

  • 工作流程

    • 图中展示了8个查询头,但它们全部共享同一个Key和Value。
    • 这种方式大幅减少了Key和Value的数量,将其降至极少数。
  • 优点:计算效率最高,因为Key和Value的数量被大大压缩,内存带宽使用也非常低。这种结构特别适合需要高效推理的大模型。

  • 缺点:因为所有查询头都共享同一个Key和Value,模型捕捉不同位置之间复杂关系的能力会受到一定限制,可能导致模型性能有所下降。

总结:

  • Multi-head Attention:每个查询头都有独立的Key和Value,捕捉序列间复杂关系能力最强,但计算量大。
  • Grouped-query Attention:通过让一组查询头共享Key和Value,减少了计算量和内存需求,同时保留了一部分多头注意力的独立性,是计算效率和模型性能的一个折中方案。
  • Multi-query Attention:所有查询头共享同一个Key和Value,计算量最小,推理速度最快,但可能损失一定的模型性能。

Grouped-query Attention(GQA)在计算效率和模型性能之间找到了一个平衡点,适用于需要提升推理效率的场景,而Multi-query Attention(MQA)则进一步提高了效率,但可能在某些复杂任务上表现得不如GQA。

3.3 从公式和参数角度解释GQA工作原理

lec4中就2页介绍GQA,第一页是上面贴出的那图,第二页就是下图:

  1. 关键概念:
  • GQA的核心思想是为多个查在这里插入图片描述
    询头共享相同的Key和Value头,而不是为每个查询头分配独立的Key和Value。这减少了Key和Value的数量,从而降低了计算成本和内存占用。

  • 查询头的数量( h q h_q hq:表示查询头的数量,在图中的例子里为8个。

  • Key/Value头的数量( h k v h_{kv} hkv:表示Key和Value头的数量,在图中的例子里为4个。注意到,这比查询头的数量少。

  • 分组大小( g g g:每组查询头共享一个Key和Value。在图中,查询头被分为4组,每组包含2个查询头( g = 2 g = 2 g=2)。

  • 公式解释:

    • 输入向量 X X X:表示输入的词嵌入向量。
    • Value和Key的计算

    V ( i ) = X W v ( i ) , K ( i ) = X W k ( i ) V^{(i)} = X W_v^{(i)}, \quad K^{(i)} = X W_k^{(i)} V(i)=XWv(i),K(i)=XWk(i)

    这里, W v ( i ) W_v^{(i)} Wv(i) W k ( i ) W_k^{(i)} Wk(i) 分别是为第 i i i 个Key和Value头的权重矩阵。GQA减少了Key和Value的数量,因此参数矩阵也减少。

    • Query的计算

    Q ( i , j ) = X W q ( i , j ) Q^{(i,j)} = X W_q^{(i,j)} Q(i,j)=XWq(i,j)

    Query向量仍然为每个查询头计算,但在GQA中,多个查询头共享同一个Key和Value头。公式中, W q ( i , j ) W_q^{(i,j)} Wq(i,j) 是第 i i i 组中的第 j j j 个查询头的权重矩阵。

  • 注意力计算

    • 共享Key和Value头后,计算注意力时可以减少计算量:

    X ( i ) = softmax ( ∑ j = 1 g Q ( i , j ) K ( i ) ) V ( i ) X^{(i)} = \text{softmax} \left( \sum_{j=1}^g Q^{(i,j)} K^{(i)} \right) V^{(i)} X(i)=softmax(j=1gQ(i,j)K(i))V(i)

    每组的查询头 Q ( i , j ) Q^{(i,j)} Q(i,j) 与共享的Key K ( i ) K^{(i)} K(i) 进行点积计算,得到注意力分数,最后与共享的Value V ( i ) V^{(i)} V(i) 进行加权求和。

3.4 精度和速度情况

1. 精度(Accuracy)

  • 接近MHA:根据论文,GQA 的精度与传统的 **Multi-head Attention (MHA) **几乎相当。具体而言,使用 GQA 的模型在多个自然语言处理任务(如文本摘要、翻译、问答等)上的精度仅下降 0.1-0.3%,而 **Multi-query Attention (MQA) **则可能导致精度下降超过 1%
  • 比 MQA 更高:在复杂依赖任务(如长文本翻译)中,GQA 保留了更多的查询独立性,相比 MQA 精度要高得多。实验表明,GQA 保证了查询头之间足够的独立性,避免了 MQA 由于共享头而导致的性能下降。

2. 速度(Speed)

  • 推理速度提升显著:GQA 在推理速度上比 MHA 提升约 1.5倍,主要原因是 GQA 大幅减少了需要计算的 Key 和 Value 头的数量。
  • 接近 MQA:虽然 GQA 比 MQA 慢约 10-15%,但其速度仍然接近 MQA。在大规模模型(如 T5-XXL)上,GQA 的推理速度与 MQA 几乎相当,比 MHA 明显更快。

3. 平衡点

  • 更好的折中方案:GQA 在精度和速度之间取得了平衡。相比 MHA,GQA 减少了计算量,同时保持了高精度;相比 MQA,它牺牲了部分推理速度,但显著提升了模型精度。

总结:

  • 精度:GQA 精度几乎与 MHA 持平,比 MQA 高 0.7-1%
  • 速度:GQA 推理速度比 MHA 快 1.5倍,接近 MQA 的速度(仅慢 10-15%)。

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

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

相关文章

基于Springboot+vue实现的雪具销售系统

作者主页:Java码库 主营内容:SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app等设计与开发。 收藏点赞不迷路 关注作者有好处 文末获取源码 项目描述 根据日常实际需要,一方面需要在系统中实现基…

基于ESP-IDF的ESP32开发记录——添加多个自己编写的组件

在第一篇的基础上,经过一段时间的学习,看其他人的视频。 ESPIDF的CMAKE其实还有这个特性: 主目录下的CMAKElists文件中,箭头的这句include其实就是帮我们包含了idf目录下的cmake文件,包含了有什么用呢? 其…

Java数组的定义及遍历

数组的声明 长度不能超过定义的长度。超过则会报错通过下标来访问 数组的遍历 最常用最简单的方法是增强for循环。

掌握IC电子元器件海外市场:独立站建设的关键作用

在IC电子元器件领域,由于产品具有较高的特殊性和技术含量,传统的线下交易模式已难以满足市场需求。因此,建立一个专业的IC电子元器件独立站成为了企业进入这一市场的趋势。在IC电子元器件行业中,建设独立站不仅能提供更加个性化的…

Linux日志-日志小结

作者介绍:简历上没有一个精通的运维工程师。希望大家多多关注作者,下面的思维导图也是预计更新的内容和当前进度(不定时更新)。 Linux 系统中的日志是记录系统活动和事件的重要工具,它们可以帮助管理员监视系统状态、调查问题以及了解系统运行…

告别繁琐,固乔快递批量查询助手:让物流追踪变得简单

探索固乔科技,您会发现一个强大的工具宝库,其中尤为亮眼的是其快递查询助手软件。这款软件专为高效处理快递信息设计,集成了众多快递公司的接口,实现了一站式查询体验。用户只需简单下载并安装,即可享受其带来的便捷与…

小红书商品详情API:引领电商新时代,精细把控商品呈现革新

一、功能、特点与优势 功能: 小红书商品详情API是一种为开发者提供获取小红书平台上商品详细信息的接口。通过该接口,开发者可以实时获取商品的名称、价格、描述、图片、规格、评论等多维度信息,满足商品展示、信息更新、数据分析等多种需求…

WebAssembly js 调用c++ 高性能传参

WebAssembly js 调用c 高性能传参 通过 Emscripten,你可以直接使用 JavaScript 的 TypedArray 与 C 共享内存, 从而避免频繁的数据拷贝操作。TypedArray 是一种高效的二进制数据表示形式,非常适合处理大规模数值数据。 js 调用: …

二手书回收小程序搭建,体会阅读的魅力

在全民阅读的市场下,书籍的更新速度非常快,也产生了大量闲置书籍,为减少浪费,旧书回收成为了一个两全其美的方式。并且与新书对比,二手书的性价比非常高,大众可以低价购买到喜欢的书。因此,二手…

redis的基础数据结构-list列表

文章目录 1. redis的list数据结构1.1. list结构的特性1.2. 常用命令 2. 常见业务场景2.1 消息队列案例讲解背景优势解决方案代码实现 2.2 排行榜案例讲解背景优势解决方案代码实现 3. 注意事项: 1. redis的list数据结构 参考链接:https://mp.weixin.qq.…

SprinBoot+Vue小区车辆管理系统的设计与实现

目录 1 项目介绍2 项目截图3 核心代码3.1 Controller3.2 Service3.3 Dao3.4 application.yml3.5 SpringbootApplication3.5 Vue 4 数据库表设计5 文档参考6 计算机毕设选题推荐7 源码获取 1 项目介绍 博主个人介绍:CSDN认证博客专家,CSDN平台Java领域优质…

国内智能运维厂商月度动态 202408

作为市场人员,虽然也添加了各类行业媒体、同行厂商的关注,但被同事问起业内动向时,常常也是记忆模糊、拍破脑袋也说不完整一件事。 所以找机会翻看了一下各大厂商的公号,先做个简单的8月汇总。 格式暂时是这样的: 整…

SpringBoot2:请求处理原理分析-利用内容协商功能实现接口的两种数据格式(JSON、XML)

文章目录 一、功能说明二、案例实现1、基于请求头实现2、基于请求参数实现 一、功能说明 我们知道,用ResponseBody注解标注的接口,默认返回给页面的是json数据。 其实,也可以返回xml结构的数据给页面。 这一篇就来实现一下这个小功能。 二、…

【GoMate框架案例】讯飞大模型RAG智能问答挑战赛top10 Baseline

【RAG框架】GoMate:RAG Framework within Reliable input,Trusted output 【项目链接】:https://github.com/gomate-community/GoMate 一、赛题背景 RAG(检索增强生成)是一种结合了检索模型和生成模型的技术,它通过检…

电竞社交平台

#计算机项目设计#vue项目实战 #java项目 #计算机项目 #电竞 #支付宝沙箱支付 #邮件发送 电竞社交平台 技术栈 开发工具:IDEA , VsCod 程序框架:SpringBoot、Mybatis、Vue 运行环境:Windows 数 据 库:MySQL 8.0.32 支付宝沙箱and邮…

2024最新FL Studio24.1.1.4285破解版中文安装包百度云网盘下载地址

大家好,今天我要给大家介绍一款音乐制作神器——FL Studio 24.1.1.4285中文版。这款软件可是音乐制作界的翘楚,无论是专业人士还是音乐爱好者,都会为它的强大功能和易用性所折服。 我们来看看FL Studio的特点。 这是一款全能型的音乐工作站&…

你的个人敏感信息是怎么泄露的?

你肯定经历过各种骚扰电话,房产中介、信用卡中心、贷款等等,在你不胜其烦挂断这些电话的同时,你有没有想过一个问题:这些骚扰电话都是怎么来的 ? 举个例子,著名的万豪酒店,在 2018 年发生了一次…

Leetcode 只出现一次的元素

题目要求我们找到数组中只出现了一次的元素,而其他元素都出现了两次。 解题思路: 我们可以使用位运算中的异或操作(XOR)。异或操作有以下两个特性: 相同的两个数字异或结果为0,例如:a ^ a 0…

使用Flask框架构建RESTful API:从基础到实践

随着移动设备和Web应用的普及,API(应用程序接口)的重要性日益凸显。RESTful API因其简洁的设计和广泛的支持成为构建现代Web服务的标准。Flask是一个轻量级且灵活的Python Web框架,非常适合用来快速搭建RESTful API。本文将详细介…

亿发:信息化智能化需求大幅提升,企业信息化建设又迈出关键一步

在全球数字经济蓬勃发展的背景下,企业信息化建设正成为推动生产力提升、增强市场竞争力的关键引擎。随着企业对信息化和智能化的需求大幅提升,越来越多的企业加速了数字化转型的步伐,通过信息技术优化业务流程、提高运营效率、增强客户体验。…