【大模型理论篇】RoPE旋转位置编码底层数学原理分析

news2024/9/26 3:29:19

1. 位置编码对于NLP模型的作用        

        位置编码(Positional Encoding)在大模型(例如Transformer架构)中起到了非常重要的作用。没有位置编码的信息,模型会丧失序列的顺序信息,导致模型退化成一个简单的“词袋模型”(Bag of Words model)。

        在Transformer中,自注意力(Self-Attention)机制是模型学习序列数据特征的核心部分。自注意力机制允许模型为序列中的每个词计算它与序列中其他词的注意力权重。但是,自注意力机制本身是“位置不敏感”的,因为它并不包含任何关于词语在序列中位置的信息。如果没有位置编码的信息引入,模型将无法区分句子中词语的不同排列。比如,“我养猫”和“猫养我”会被视为相同的输入,因为自注意力机制只关注词语之间的相对相似性,而不考虑它们在序列中的具体位置。

        这种情况下,模型就会退化成一个词袋模型(Bag of Words Model),忽略词语顺序,只关心词语出现频率。在这种模型中,序列中词语的顺序信息被丢失,对于自然语言处理任务(如翻译、情感分析)等是非常不利的,因为这些任务通常对词语顺序极为敏感。

         位置编码的核心作用是为每个词提供其在序列中的位置信息,这使得模型能够区分词语的不同排列方式。由于位置编码的引入,模型能够感知词元与词元之间的相对位置是不同的,从而学习到正确的上下文信息。 通过位置编码,模型不仅能学习到每个词的含义,还能学会词之间的顺序关系。本质上,自然语言处理模型学习的正是词元的词义以及词元的顺序组合关系

2. 常见的位置编码方法

      【1】中整理了两种主流思路的位置编码,包括绝对位置编码以及相对位置编码。

        针对绝对位置编码:绝对位置编码信息会加到输入中,在输入的第k个向量x_k中加入位置向量p_k变为x_k+p_k,其中p_k只依赖于位置编号k。绝对位置编码主要有训练式、三角函数式、递归式等。在Transformer原论文《Attention is all you need》中,提出的三角函数式就是一种绝对位置编码。该论文中位置编码被设计成使用正弦和余弦函数的组合,这种编码方法是固定的,并不随着训练而变化。其公式为:

\text{PE}_{(pos, 2i)} = \sin\left(\frac{pos}{10000^{2i/d_{model}}}\right), \quad \text{PE}_{(pos, 2i+1)} = \cos\left(\frac{pos}{10000^{2i/d_{model}}}\right)

其中,pos 是位置,i 是维度索引,d_{model}是模型的维度。这种编码方式使得不同位置的编码在每个维度上具有不同的周期性,确保每个位置的编码都是唯一的,并能让模型一定程度上推断出相对位置关系。

        针对相对位置编码:自然语言中的语义关系通常更多依赖于词与词之间的相对位置,而不是它们在句子中的绝对位置。例如,在句子“猫追狗”和“公园里猫追狗”中,“追”前后的词(“猫”和“狗”)的相对位置决定了句子的含义,绝对位置不重要。仅仅通过 “猫”“追”“狗” 这三个元素的相对位置就能准确传达意思。在 “公园里猫追狗” 中,增加了 “公园里” 这个表示地点的修饰成分,但 “猫”“追”“狗” 的相对位置依然决定了核心的追逐含义,只是多了地点的限定。无论在句子中的绝对位置如何变化,只要 “猫” 在 “追” 前,“狗” 在 “追” 后,这种追逐关系就不会改变。这种相对位置的稳定性使得能够快速理解句子的主要语义,而不受绝对位置变化的过多影响。【2,3,4,5,6,7】分别对相对位置编码给出了技术实现,有兴趣可以关注。

        其实还存在第三种方式,也是本文主要关注的RoPE旋转位置编码【8】,它是将绝对位置编码与相对位置编码进行了巧妙的结合,既体现了词元在句子序列中的绝对位置信息,又体现了词元与词元之间的相对位置关系。

        RoPE目前已经成为主流认同的大模型位置编码模块,见表1。除此之外,ALiBi(Attention with linear biases)位置编码【11】因为良好的外推性也被广泛应用。

      【9】进行了使用RoPE与其他位置编码方式在效果上的对比。使用 GPT-Neox 代码库对旋转嵌入与 GPT-3 中使用的学习型绝对位置嵌入以及 T5 中使用的学习型相对位置嵌入( RPE)进行了比较。比较是使用具有 1.25 亿参数的模型进行。模型在 OpenWebText2 上进行训练,可以看到RoPE训练和验证曲线的收敛速度更快,总体验证损失更低,而吞吐量仅有轻微下降。还使用 mesh-transformer-jax 代码库和具有 14 亿参数的模型进行了额外的大规模实验,与学习型绝对位置嵌入和 T5 RPE 的基线进行对比。使用了与 GPT3 的 13 亿参数模型类似的超参数,数据集为 The Pile。观察到与学习型绝对位置嵌入相比,RoPE收敛速度有约 30%提高,与 T5 相对位置编码相比有10% - 20%的改进,这表明RoPE可扩展到数十亿参数范围。在 enwiki8 上对 Performer 进行了小规模测试,针对具有 512 维、8 个头的 8 层基于字符的 Transformer。这些测试表明,将旋转嵌入代入 Performer 会导致验证损失急剧下降并快速收敛。从对比结果来看,RoPE在各个方面都带来了积极的增益。

3. 旋转位置编码数学原理分析

        虽然在之前的文章《LLaMA3结构关键模块分析》已经对RoPE旋转位置编码做了介绍,但是对于该算法的数学原理讨论不够,因此本文将针对底层的数学原理进一步分析。 接下来的分析会以原论文【8】和参考材料【10】为主来进行分析。

3.1 直觉理解

        旋转位置编码在编码位置信息方面,不同于Transformer原论文模式,没有采用添加一个单独的位置编码向量,而是采用对现有的词嵌入进行旋转。旋转角度是序列中词的位置以及嵌入维度的函数【12】。

  • q_m=f_q(x_m, m)表示在位置 m 处的查询向量。
  • k_n = f_k(x_n, n)表示在位置 n 处的键向量。
  • v_n = f_v(x_n, n)表示在位置 n 处的值向量。

        假设有一个词序列 w₁,w₂,…,wₗ,其中这个序列的长度为 L。每个词在高维空间中被转换为一个向量,称这个空间的维度为 |D|。在 Transformer 模型中,序列中的每个位置(m)都有一个查询向量(qₘ)和一个键向量(kₘ),这些向量是通过函数 f_qf_k 进行创建。在计算注意力矩阵时,主要体现出两个关键信息:

词相似性:具有相似嵌入向量的词有更高的注意力得分。

位置接近性:在序列中位置更接近的词应该有更高的得分。

        为了确定一个词元与另一个词元的注意力得分,需要引入相应的点积计算。对于任何一对位置 m 和 n,查询向量q_m和键向量k_n的点积需要体现出词的相似性和位置信息。在这个点积中:(q・k = ||q|| ||k|| cos (θ))。q 和 k 的幅度相似性(表示为 ||q||・||k||)对应于词嵌入的相似性。q 和 k 的角度(\theta _m\theta _n)有助于位置相似性。可以看到,角度分量仅取决于向量在序列中的位置,与实际的词嵌入无关。RoPE巧妙的利用了该特性,根据序列中的位置旋转查询和键向量。旋转在保持词的相似性的同时,在角度中编码位置信息。在计算旋转向量之间的点积时,向量之间的相对角度捕获位置相近程度。这种思路使得RoPE将词和位置信息无缝地集成到单个操作中。

3.2 复数知识 

        在理解旋转位置编码数学推导之前,还需要具备一些先验知识。

  • 对于二维向量 (x, y),可以用复数来表示:z=x+iy,其中 i 是虚数单位(i^2 = -1)。
  • 使用欧拉公式e^{ix} = \cos x + i\sin x,可以用三角函数来表示复数的实部和虚部。
  • 给定两个二维向量 \mathbf{q} 和 \mathbf{k},假设它们分别表示为复数q = x_q + iy_q​ 和k = x_k + iy_k。其复数共轭的定义:k^* = x_k - iy_k​。
  • 要求两个向量的内积,可以借助复数的乘积来实现: \text{Re}[qk^*] = x_q x_k + y_q y_k。 

3.3 公式推导

3.3.1 目标公式

        有了前两部分的铺垫后,接下来我们就可以对原论文中的公式进行详细推导,并证明通过旋转之后的查询向量与键向量的内积计算,可以同时包含绝对位置信息与相对位置信息。也就是证明存在以下关系:

\langle f_q(x_m, m), f_k(x_n, n) \rangle = g(x_m, x_n, m - n)

对公式进行解释:

        该公式表示旋转后的查询向量f_q(x_m, m) 和旋转后的键向量f_k(x_n, n)的内积等于一个函数 g(x_m, x_n, m - n),其中:

  • \langle \cdot, \cdot \rangle表示内积运算。
  • f_q(x_m, m)f_k(x_n, n)是定义在输入词向量 x_mx_n 以及位置参数 m 和 n 上的函数。
  • g(x_m, x_n, m - n)是一个函数,表示输入 x_m​ 和 x_n​ 以及位置之差 m - n 的某种函数关系。

        该公式表达了两个函数在特定位置上的内积与它们位置的差有关,而不是绝对位置本身。计算自注意力时,公式中查询向量与键向量的内积反映了词元之间的相似性。  

3.3.2 寻找满足关系的函数定义

        接下来,就是找到某种函数表达,能够满足上述关系,原论文中给出了如下函数形式定义:

f_q(x_m, m) = (W_q x_m) e^{im\theta}

f_k(x_n, n) = (W_k x_n) e^{in\theta}

g(x_m, x_n, m - n) = \text{Re} \left[ (W_q x_m) (W_k x_n)^* e^{i(m - n)\theta} \right]

公式解释:

        公式中使用了复数表示的旋转编码,通过旋转因子e^{im\theta} 和 e^{in\theta} 引入相对位置信息。        

        f_q(x_m, m) 和f_k(x_n, n) 是经过线性变换后的输入词向量分别乘上一个复数旋转因子 e^{im\theta}e^{in\theta},其中:

        W_qW_k​ 是查询(query)和键(key)的权重矩阵。

        x_m​ 和 x_n​ 分别是输入的词元嵌入向量表示。

        \theta 是一个旋转角度,与位置 m 和 n 的差异相关。

        g(x_m, x_n, m - n)表示函数f_qf_k​ 的内积,它包含了位置差 m - n 的信息。

3.3.3 旋转位置编码的定义

        根据欧拉公式,对复数旋转因子进行变换,得到:

  e^{im\theta} = \cos(m\theta) + i \sin(m\theta)

e^{in\theta} = \cos(n\theta) + i \sin(n\theta)

e^{i(m-n)\theta} = \cos((m-n)\theta) + i \sin((m-n)\theta)

        我们以词嵌入向量维度为2维来进一步说明,后续多维的情况其实就是按照2维字空间进行处理即可。

        首先,将查询向量 q_m 和键向量 k_n​ 转换为复数形式。查询向量 q_m 是一个二维向量,表示为:

q_m = \begin{bmatrix} q_m^{(1)} \\ q_m^{(2)} \end{bmatrix} = W_q x_m

        其中, W_q 是一个 2 \times 2 的矩阵, x_m​ 是一个二维向量。乘积 q_m 也是一个二维向量。

        然后,将 q_m​ 转化为复数形式:

q_m = q_m^{(1)} + i q_m^{(2)}

        旋转位置编码将向量与一个复数的指数形式相乘,用于表示位置信息。查询向量的旋转位置编码表示为:

f_q(x_m, m) = (W_q x_m) e^{im\theta} = q_m e^{im\theta}

q_m e^{im\theta} = (q_m^{(1)} + i q_m^{(2)}) \cdot (\cos(m\theta) + i \sin(m\theta))

根据复数乘法的性质:

(a + ib) \cdot (c + id) = (ac - bd) + i(bc + ad)

对查询向量处理:

q_m e^{im\theta} = (q_m^{(1)} \cos(m\theta) - q_m^{(2)} \sin(m\theta)) + i(q_m^{(2)} \cos(m\theta) + q_m^{(1)} \sin(m\theta))

重新表达为向量形式:

q_m e^{im\theta} = \begin{bmatrix} q_m^{(1)} \cos(m\theta) - q_m^{(2)} \sin(m\theta) \\ q_m^{(2)} \cos(m\theta) + q_m^{(1)} \sin(m\theta) \end{bmatrix}

上述公式,说明查询向量乘以了一个旋转矩阵,形式如下:

\begin{bmatrix} \cos(m\theta) & -\sin(m\theta) \\ \sin(m\theta) & \cos(m\theta) \end{bmatrix} \cdot \begin{bmatrix} q_m^{(1)} \\ q_m^{(2)} \end{bmatrix}

同样的,对键向量 k_n的旋转位置编码表示为:

f_k(x_n, n) = (W_k x_n) e^{in\theta} = k_n e^{in\theta}

其中:

k_n e^{in\theta} = \begin{bmatrix} k_n^{(1)} \cos(n\theta) - k_n^{(2)} \sin(n\theta) \\ k_n^{(2)} \cos(n\theta) + k_n^{(1)} \sin(n\theta) \end{bmatrix}

同样表示为一个旋转矩阵乘以键向量:

\begin{bmatrix} \cos(n\theta) & -\sin(n\theta) \\ \sin(n\theta) & \cos(n\theta) \end{bmatrix} \cdot \begin{bmatrix} k_n^{(1)} \\ k_n^{(2)} \end{bmatrix}

函数g(x_m, x_n, m - n)的定义为:

g(x_m, x_n, m - n) = \text{Re}\left[(W_q x_m)(W_k x_n)^* e^{i(m-n)\theta}\right]

其中,\text{Re}表示取实部,这个函数就是用于计算查询向量和键向量在旋转位置编码下的内积。

3.3.4 函数g形式的成立证明

接下来就是需要计算该函数g,首先,我们有查询向量和键向量的复数表示:

W_q x_m = q_m = q_m^{(1)} + i q_m^{(2)}, \quad W_k x_n = k_n = k_n^{(1)} + i k_n^{(2)}

以及它们的复共轭:

(W_k x_n)^* = k_n^* = k_n^{(1)} - i k_n^{(2)}

和复数 e^{i(m - n)\theta}

e^{i(m - n)\theta} = \cos((m - n)\theta) + i \sin((m - n)\theta)

继续计算函数 g(x_m, x_n, m - n)

g(x_m, x_n, m - n) = \text{Re}\left[(W_q x_m)(W_k x_n)^* e^{i(m - n)\theta}\right]

将复数形式代入:

= \text{Re} \left[ (q_m^{(1)} + i q_m^{(2)}) (k_n^{(1)} - i k_n^{(2)}) (\cos((m - n)\theta) + i \sin((m - n)\theta)) \right]

首先计算两个复数的乘积:

(q_m^{(1)} + i q_m^{(2)}) (k_n^{(1)} - i k_n^{(2)}) = (q_m^{(1)} k_n^{(1)} + q_m^{(2)} k_n^{(2)}) + i (q_m^{(2)} k_n^{(1)} - q_m^{(1)} k_n^{(2)})

接着,与 e^{i(m - n)\theta} 相乘:

\left( (q_m^{(1)} k_n^{(1)} + q_m^{(2)} k_n^{(2)}) + i (q_m^{(2)} k_n^{(1)} - q_m^{(1)} k_n^{(2)}) \right) \cdot (\cos((m - n)\theta) + i \sin((m - n)\theta)) \\ = (q_m^{(1)} k_n^{(1)} + q_m^{(2)} k_n^{(2)}) \cos((m - n)\theta) - (q_m^{(2)} k_n^{(1)} - q_m^{(1)} k_n^{(2)}) \sin((m - n)\theta) + i \left[ (q_m^{(1)} k_n^{(1)} + q_m^{(2)} k_n^{(2)}) \sin((m - n)\theta) + (q_m^{(2)} k_n^{(1)} - q_m^{(1)} k_n^{(2)}) \cos((m - n)\theta) \right]

取上式的实部:

\text{Re} \left[ (q_m^{(1)} k_n^{(1)} + q_m^{(2)} k_n^{(2)}) \cos((m - n)\theta) - (q_m^{(2)} k_n^{(1)} - q_m^{(1)} k_n^{(2)}) \sin((m - n)\theta) \right]

这正是我们希望拿到的结果:

g(x_m, x_n, m - n) = (q_m^{(1)} k_n^{(1)} + q_m^{(2)} k_n^{(2)}) \cos((m - n)\theta) - (q_m^{(2)} k_n^{(1)} - q_m^{(1)} k_n^{(2)}) \sin((m - n)\theta)

接下来,我们继续通过内积的方式来证明函数g公式的成立性:

我们有位置 m 的 query 向量和位置 n 的 key 向量,分别为:

f_q(x_m, m) = [q^{(1)}_m \cos(m \theta) - q^{(2)}_m \sin(m \theta), q^{(2)}_m \cos(m \theta) + q^{(1)}_m \sin(m \theta)]

f_k(x_n, n) = [k^{(1)}_n \cos(n \theta) - k^{(2)}_n \sin(n \theta), k^{(2)}_n \cos(n \theta) + k^{(1)}_n \sin(n \theta)]

内积计算:

\langle f_q(x_m, m), f_k(x_n, n) \rangle

展开内积操作得到:

\langle f_q(x_m, m), f_k(x_n, n) \rangle = (q^{(1)}_m \cos(m \theta) - q^{(2)}_m \sin(m \theta))(k^{(1)}_n \cos(n \theta) - k^{(2)}_n \sin(n \theta))+ (q^{(2)}_m \cos(m \theta) + q^{(1)}_m \sin(m \theta))(k^{(2)}_n \cos(n \theta) + k^{(1)}_n \sin(n \theta))

整理得:

= q^{(1)}_m k^{(1)}_n (\cos(m \theta) \cos(n \theta) + \sin(m \theta) \sin(n \theta))- q^{(1)}_m k^{(2)}_n (\cos(m \theta) \sin(n \theta) - \sin(m \theta) \cos(n \theta))- q^{(2)}_m k^{(1)}_n (\sin(m \theta) \cos(n \theta) - \cos(m \theta) \sin(n \theta))+ q^{(2)}_m k^{(2)}_n (\sin(m \theta) \sin(n \theta) + \cos(m \theta) \cos(n \theta))

应用三角函数恒等式:

\cos(a + b) = \cos a \cos b - \sin a \sin b

 \cos(a - b) = \cos a \cos b + \sin a \sin b

\sin(a + b) = \sin a \cos b + \cos a \sin b

\sin(a - b) = \sin a \cos b - \cos a \sin b

我们可以将内积表达式整理为:

\langle f_q(x_m, m), f_k(x_n, n) \rangle = (q^{(1)}_m k^{(1)}_n + q^{(2)}_m k^{(2)}_n) \cos((m - n) \theta)+ (q^{(1)}_m k^{(2)}_n - q^{(2)}_m k^{(1)}_n) \sin((m - n) \theta)= (q^{(1)}_m k^{(1)}_n + q^{(2)}_m k^{(2)}_n) \cos((m - n) \theta)- (q^{(2)}_m k^{(1)}_n - q^{(1)}_m k^{(2)}_n) \sin((m - n) \theta)

而之前我们已经得到函数 g的形式为:

g(x_m, x_n, m - n) = (q_m^{(1)} k_n^{(1)} + q_m^{(2)} k_n^{(2)}) \cos((m - n)\theta) - (q_m^{(2)} k_n^{(1)} - q_m^{(1)} k_n^{(2)}) \sin((m - n)\theta)

所以因此可以证明:

\langle f_q(x_m, m), f_k(x_n, n) \rangle = g(x_m, x_n, m - n)

3.3.5 结论

        因此,函数 g(x_m, x_n, m - n)的计算公式成立,这个公式通过使用旋转编码捕捉了查询和键向量之间的相对位置关系,并在实际应用中能够帮助模型更好地理解序列数据的结构。

3.3.6 扩展到多维向量场景 

        根据旋转矩阵的定义,RoPE 在处理查询和键向量的时候,将每对连续出现的两个元素视为一个子空间 。因此,对于一个长度为H 的向量来说,将会形成 H / 2 个这样的子空间。在这些子空间中,每一个子空间 i  ∈ { 1 , . . . , H / 2} 所对应的两个元素t \cdot \theta_i都会根据一个特定的旋转角度 进行旋转,其中t 代表位置索引,而\theta_i 表示该子空间中的基。有了上述的知识背景,再回过头来看旋转编码,就一清二楚了。

4. 参考材料

【1】让研究人员绞尽脑汁的Transformer位置编码

【2】《Self-Attention with Relative Position Representations》

【3】《Transformer-XL: Attentive Language Models Beyond a Fixed-Length Context》

【4】《Exploring the Limits of Transfer Learning with a Unified Text-to-Text Transformer》

【5】《DeBERTa: Decoding-enhanced BERT with Disentangled Attention》

【6】《Convolutional Sequence to Sequence Learning》

【7】《Encoding word order in complex embeddings》

【8】《RoFormer: Enhanced Transformer with Rotary Position Embedding》

【9】Rotary Embeddings: A Relative Revolution

【10】LLaMA 中的旋转式位置编码(Rotary Position Embedding)

【11】《Train Short, Test Long: Attention with Linear Biases Enables Input Length Extrapolation》

【12】Positional Encoding Explained: A Deep Dive into Transformer PE

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

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

相关文章

物品租赁​​​​​​​|基于SprinBoot+vue的物品租赁​​​​​​​系统(源码+数据库+文档)

物品租赁系统 基于SprinBootvue的物品租赁系统 一、前言 二、系统设计 三、系统功能设计 5.1 系统功能模块 5.2 管理员功能模块实现 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取: 博主介绍:✌️大…

【单片机原理及应用】实验:数码管的中断控制

目录 一、实验目的 二、实验内容 三、实验步骤 四、记录与处理 五、思考 六、成果文件提取链接 一、实验目的 掌握外部中断的工作原理,学习中断编程与程序调试方法。 二、实验内容 【参照图表】 (1)创建一个包含80C51固件,采用…

汽车乘客热舒适度大挑战,如何利用仿真技术提高汽车环境舒适度

舒适性在人们选择汽车的决定性方面占比越来越重,而汽车乘员舱环境的舒适性是指为乘员提供舒适愉快便利的乘坐环境与条件,包括良好的平顺性、车内的低噪声、适宜的空气环境以及良好的驾驶操作性能。 舒适性 经济性 安全性、动力性 典型的乘员舱热舒适性模…

git常见命令行及分支规范

文章目录 GIT常见命令行原理图基本设置初始化和克隆仓库文件管理提交更改查看状态和历史分支管理远程仓库交互高级功能GIT常见分支风格1. 单一主干分支(Single Main Branch)//极少使用优点:缺点:2. 多主干分支(Multiple Main Branches)//个人小型项目采用优点:缺点:3. …

OpenCV Jet颜色映射和HSV颜色空间对比

目录 一、概述 二、Jet颜色空间映射 2.1优势 2.2颜色变化范围 2.3应用场景 三、HSV 颜色空间 3.1优势 3.2颜色分布 3.3应用场景 四、Jet与HSV区别 4.1对比总结 4.2选择建议 OpenCV图像处理与应用实战算法汇总地址: OpenCV 图像处理应用实战算法列表汇总…

H264编码原理(二)帧内预测

假设你去了一家餐厅吃饭,这家餐厅提供了一个有趣的点餐方式。服务员会根据餐厅最近最受欢迎的菜品组合,推荐九个套餐给你。你的任务是从这九个套餐中找到一个最接近你心中想要的菜品组合的套餐,然后告诉服务员你想替换哪些菜,以得…

PS快速如何抠图章?

1、选择--色彩范围--颜色选择(反相)--确定 2、选中范围后--按delete删除非选中内容--就能得到图章 3、图层叠加来加深颜色和补全缺失点

合宙低功耗4G模组Air780EQ——开发板使用说明

CORE-AIR780E 开发板是合宙通信推出的基于 Air780E 模组所开发的, 包含电源,SIM 卡,USB,天线,音频等必要功能的最小硬件系统。 以方便用户在设计前期对Air780E模块 进行性能评估,功能调试,软件…

Python__面向对象__多态学习

目录 一、多态 1.多态定义理解 2.实例属性和类属性 3.类相关的函数 (1) 实例方法 (2)类方法 (3)静态方法 一、多态 1.多态定义理解 在Python中,多态是一种特性,类似于一个接口,允许在一个对象中的一个操作可作用在不同类型的对象上…

OpenGL3.3_C++_Windows(36)

PBR_IBL镜面部分 镜面部分并不能像漫反射部分一样将BRDF部分像常量一样提取出来,因为它整个积分上不是常数,因为它受到wi和w0的影响,就比如一个x的等式,不能把x部分提取出来一样,他是随着等式变化的如果试图解算所有入…

uni-app开发日志:将schema2code生成的新增页和修改页整合成一页

有没有想过,add和edit页面其实没多大差别? 我之前自研的系统add和edit都是一个页面,只要判断一下当前有没有id传递来,为空来就是新增。 这样如果页面修改时,才能尽量少改动代码,少出错。 对比add.vue和edit…

企业海外新闻稿发布的转型之路:从纸媒到数字化

在全球化进程加速的今天,企业的传播渠道和方式也在经历着深刻的变革。曾经占据主导地位的纸质媒体,逐渐被灵活高效的海外媒体网站所取代。企业新闻稿发布形式的转变,不仅体现了技术进步和媒体环境的变化,也标志着企业全球传播战略…

日结兼职零工平台小程序系统开发制作方案

目前在大学生和自由职业者群体中,对短期兼职零工的需求日益增长。同时,企业与个人雇主也面临着季节性和临时性用工的需求。 日结兼职零工小程序系统为寻求日结工作的求职者和需要短期劳动力的企业提供一个快速匹配的平台。实现快速发布日结兼职工作信息…

5年经验社招后端面试经历分享

这是一位读者去年的面经,他在 2023 年在百度毕业,后面拿到了快手、滴滴和京东的 offer,最终选择了京东,薪资总包是 51w。 下面是正文。 背景介绍 大家好,本人 2018 年毕业于一所普通 211 学校,专业是软件…

【Redis】Redis 的消息队列 List、Streams—(六)

目录 一、消息队列二、List 方案三、Streams 方案 一、消息队列 我们一般把消息队列中发送消息的组件称为生产者,把接收消息的组件称为消费者,下图是一个通用的消息队列的架构模型: 消息队列在存取消息时,必须要满足三个需求&…

基于yolov10的PCB检测算法研究

内容:项目将YOLOV10创新后的PCB检测算法成功部署到GD32H757上,实现PCB缺陷的工业产线实时检测。 项目主要支持开源代码:HomiKetalys/gd32ai-modelzoo: Provide deployable deep learning models on gd32 (github.com) (想了解将…

群晖7.2.1 半洗白后安装AME

1. 群晖打开SSH 2. Xshell登录群晖 用管理员账户登录,然后使用sudo -i 获取root权限,sudo -i是要再次验证管理员密码 sudo -iSA6400还需要运行这个命令 /usr/syno/etc/rc.sysv/apparmor.sh stop #DSM7.2 AME版本3.1.0-3005强制解锁激活命令 curl -sk…

最常用集合 - ArrayList详解

ArrayList介绍 ArrayList实现了List接口,是顺序容器,即元素存放的数据与放进去的顺序相同,允许放入null元素,底层通过数组实现。除该类未实现同步外,其余跟Vector大致相同。每个ArrayList都有一个容量(capacity)&…

MeshAnything V2来了!30秒生成建模师级Mesh!最大可生成面数提升至1600.

GitHub已揽星1.9k的MeshAnything项目上新了V2版本,由来自南洋理工大学、清华大学、帝国理工学院、西湖大学等研究人员完成。 MeshAnything V2相比V1,使用了最新提出的Adjacent Mesh Tokenization(AMT)算法,将最大可生…

mysql学习教程,从入门到精通,MySQL介绍(1)

1、MySQL 教程 本教程是为初学者准备的,以帮助他们理解与MySQL语言相关的基础知识和高级概念。 mysql MySQL 是最流行的关系型数据库管理系统,在 WEB 应用方面 MySQL 是最好的 RDBMS(Relational Database Management System:关系数据库管理系…