大模型学习入门——Day3:注意力机制
本系列笔记的教材:快乐学习大模型-DataWhale团队
注意力机制
注意力机制最先源于计算机视觉领域,其核心思想为当我们关注一张图片,我们往往无需看清楚全部内容而仅将注意力集中在重点部分即可。而在自然语言处理领域,我们往往也可以通过将重点注意力集中在一个或几个 token,从而取得更高效高质的计算效果。
注意力机制有三个核心变量:Query(查询值)、Key(键值)和 Value(真值)。注意力机制就是根据一个查询(Query),来判断一组信息(Key 和 Value)中哪些部分更重要,然后将重要的部分信息加权组合起来,得到一个最终结果。
举个例子
- 我们的数据(键-值对 Key-Value):
Keys = ["apple", "banana", "chair"]
(信息的“标签”)Values = [10, 5, 2]
(信息本身)
- 我们的查询(Query):
Query = "一种水果"
第一步:计算相似度分数 (Query · Key)
我们需要一个方法来衡量我们的查询(Query)和每个键(Key)的相似程度。在真实的神经网络中,这是通过高维向量的点积来完成的。在第一章中我们知道词由词向量表示,语义相近的词他们的词向量也非常接近。
x = Q K T x = QK^T x=QKT
此处的 K 即为将所有 Key 对应的词向量堆叠形成的矩阵。基于矩阵乘法的定义,x 即为 q 与每一个 k 值的点积。现在我们得到的 x 即反映了 Query 和每一个 Key 的相似程度。
第二步:缩放分数 (Scale)
在真实的 Transformer 模型中,为了防止分数过大导致梯度消失,会将其除以一个缩放因子 d k \sqrt{d_k} dk( d k d_k dk 是Key向量的维度)。
第三步:计算注意力权重 (Softmax)
接下来,我们需要将这些原始分数转换成一个总和为 1 的、真正的“注意力权重”分布。这一步通过 Softmax 函数完成。
公式:
Softmax ( x i ) = e x i ∑ j e x j \text{Softmax}(x_i) = \frac{e^{x_i}}{\sum_{j} e^{x_j}} Softmax(xi)=∑jexjexi
权重解读:
当查询是“一种水果”时,模型会把 s o f t m a x ( x 1 ) softmax(x_1) softmax(x1) 的注意力放在 “apple” 上,$ s o f t m a x ( x 2 ) softmax(x_2) softmax(x2) 的注意力放在 “banana” 上,而只有 s o f t m a x ( x 3 ) softmax(x_3) softmax(x3) 的注意力放在 “chair” 上。
第四步:加权求和得到最终输出 (Multiply by Values)
最后一步,我们将上一步得到的注意力权重,与我们最初的 值 (Values) 进行加权求和。
公式:
Output = ∑ i ( weight i ⋅ V i ) \text{Output} = \sum_{i} (\text{weight}_i \cdot V_i) Output=i∑(weighti⋅Vi)
注意力机制核心计算公式
给定查询(Query)、键(Key)、值(Value)矩阵,注意力得分的计算公式为:
A t t e n t i o n ( Q , K , V ) = s o f t m a x ( Q K ⊤ d k ) V \mathrm{Attention}(Q, K, V) = \mathrm{softmax}\left( \frac{Q K^\top}{\sqrt{d_k}} \right) V Attention(Q,K,V)=softmax(dkQK⊤)V
其中:
- Q ∈ R n × d k Q \in \mathbb{R}^{n \times d_k} Q∈Rn×dk 表示查询矩阵
- K ∈ R m × d k K \in \mathbb{R}^{m \times d_k} K∈Rm×dk 表示键矩阵
- V ∈ R m × d v V \in \mathbb{R}^{m \times d_v} V∈Rm×dv 表示值矩阵
- d k d_k dk 为键向量的维度
- s o f t m a x \mathrm{softmax} softmax 用于归一化注意力权重
注意力权重矩阵(score):
s c o r e = Q K ⊤ d k \mathrm{score} = \frac{Q K^\top}{\sqrt{d_k}} score=dkQK⊤
最终加权求和:
o u t p u t = s o f t m a x ( s c o r e ) V \mathrm{output} = \mathrm{softmax}(\mathrm{score}) V output=softmax(score)V
pytorch代码实现
'''注意力计算函数'''
def attention(query, key, value, dropout=None):'''args:query: 查询值矩阵key: 键值矩阵value: 真值矩阵'''# 获取键向量的维度,键向量的维度和值向量的维度相同d_k = query.size(-1) # 计算Q与K的内积并除以根号dk# transpose——相当于转置scores = torch.matmul(query, key.transpose(-2, -1)) / math.sqrt(d_k)# Softmaxp_attn = scores.softmax(dim=-1)if dropout is not None:p_attn = dropout(p_attn)# 采样# 根据计算结果对value进行加权求和return torch.matmul(p_attn, value), p_attn
自注意力机制
在开始计算前,我们先回顾一下注意力机制的三个核心角色:查询(Query)、键(Key)、值(Value)。整个过程就是:用我的 Query (Q) 去和所有元素的 Key (K) 算一个“相关度分数”,然后根据这个分数给所有元素的 Value (V) 分配一个权重,最后把所有加权后的 Value 合并起来,就得到了一个融合了全局上下文的新表示。
所谓自注意力,即是计算本身序列中每个元素对其他元素的注意力分布,即在计算过程中,Q、K、V 都由同一个输入通过不同的参数矩阵计算得到。在 Encoder 中,Q、K、V 分别是输入对参数矩阵 Wq、Wk、Wv 做积得到,从而拟合输入语句中每一个 token 对其他所有 token 的关系。
举个例子
准备工作:我们的迷你 NLP 任务
- 输入句子: “The cat sat” (为了计算简单,我们用3个单词)
- 简化假设:
- 每个单词的词嵌入 (Embedding) 维度
d_model = 4
。 - 注意力头的数量
h = 1
(后文多头注意力部分的时候h=2)。 - 每个头的维度
d_k = d_v = d_model / h = 4 / 1 = 4
。
- 每个单词的词嵌入 (Embedding) 维度
1. 初始输入和权重矩阵
我们假设已经通过词嵌入层得到了每个单词的初始向量,并随机初始化了将它们转换为 Q, K, V 的权重矩阵。
- 输入词嵌入矩阵 X (3个单词, 每个4维):
需要学习的权重矩阵 (我们手动设定一些简单的整数值):X = [[1, 0, 1, 0], # The[0, 1, 1, 0], # cat[1, 1, 0, 1]] # sat
W_Q
(用于生成 Query):[[1,0,1,0], [0,1,1,0], [1,1,0,1], [1,0,0,1]]
W_K
(用于生成 Key):[[0,1,0,1], [1,0,1,1], [0,0,1,0], [1,1,0,0]]
W_V
(用于生成 Value):[[0,0,1,1], [1,1,0,0], [0,1,0,1], [1,0,1,0]]
2. 计算 Q, K, V 矩阵
这是自注意力的第一步:Q = X @ W_Q
, K = X @ W_K
, V = X @ W_V
现在我们有了计算所需的 Q, K, V 矩阵。
Q = X @ W_Q # [[2, 1, 1, 1], [1, 2, 1, 1], [2, 1, 2, 2]]
K = X @ W_K # [[0, 1, 1, 1], [1, 0, 2, 1], [2, 2, 1, 2]]
V = X @ W_V # [[1, 0, 2, 1], [1, 2, 0, 1], [2, 2, 2, 2]]
3. 计算注意力分数 scores = QK^T
Q
矩阵的每一行(代表一个词的Query)都要和 K
矩阵的每一行(代表每个词的Key)做点积。
K T = ( 0 1 2 1 0 2 1 2 1 1 1 2 ) K^T = \begin{pmatrix} 0 & 1 & 2 \\ 1 & 0 & 2 \\ 1 & 2 & 1 \\ 1 & 1 & 2 \end{pmatrix} KT= 011110212212
Q K T = ( 2 1 1 1 1 2 1 1 2 1 2 2 ) ⋅ ( 0 1 2 1 0 2 1 2 1 1 1 2 ) = ( 2 5 9 4 4 9 6 8 12 ) QK^T = \begin{pmatrix} 2 & 1 & 1 & 1 \\ 1 & 2 & 1 & 1 \\ 2 & 1 & 2 & 2 \end{pmatrix} \cdot \begin{pmatrix} 0 & 1 & 2 \\ 1 & 0 & 2 \\ 1 & 2 & 1 \\ 1 & 1 & 2 \end{pmatrix} = \begin{pmatrix} 2 & 5 & 9 \\ 4 & 4 & 9 \\ 6 & 8 & 12 \end{pmatrix} QKT= 212121112112 ⋅ 011110212212 = 2465489912
4. 缩放 (Scale)
将分数除以 d k = 4 = 2 \sqrt{d_k} = \sqrt{4} = 2 dk=4=2。
scaled_scores = scores / 2
= ( 1.0 2.5 4.5 2.0 2.0 4.5 3.0 4.0 6.0 ) = \begin{pmatrix} 1.0 & 2.5 & 4.5 \\ 2.0 & 2.0 & 4.5 \\ 3.0 & 4.0 & 6.0 \end{pmatrix} = 1.02.03.02.52.04.04.54.56.0
5. Softmax 归一化
对 scaled_scores 的每一行独立应用 Softmax。我们以第一行为例:
- e 1.0 ≈ 2.718 e^{1.0} \approx 2.718 e1.0≈2.718, e 2.5 ≈ 12.182 e^{2.5} \approx 12.182 e2.5≈12.182, e 4.5 ≈ 90.017 e^{4.5} \approx 90.017 e4.5≈90.017
Sum = 2.718 + 12.182 + 90.017 = 104.917
weights_row1 = [2.718/104.9, 12.182/104.9, 90.017/104.9] = [0.026, 0.116, 0.858]
计算所有行后,得到注意力权重矩阵 A
:
A = [[0.026, 0.116, 0.858], [0.035, 0.035, 0.930], [0.024, 0.064, 0.912]]
6. 加权求和 Output = A @ V
O u t p u t = ( 0.026 0.116 0.858 . . . ) ⋅ ( 1 0 2 1 1 2 0 1 2 2 2 2 ) Output = \begin{pmatrix} 0.026 & 0.116 & 0.858 \\ ... \end{pmatrix} \cdot \begin{pmatrix} 1 & 0 & 2 & 1 \\ 1 & 2 & 0 & 1 \\ 2 & 2 & 2 & 2 \end{pmatrix} Output=(0.026...0.1160.858)⋅ 112022202112
计算第一行(“The” 的新表示):
output_row1 = 0.026 * [1,0,2,1] + 0.116 * [1,2,0,1] + 0.858 * [2,2,2,2]
≈ [1.858, 1.948, 1.768, 1.858]
这个 [1.858, 1.948, 1.768, 1.858]
就是 “The” 在融合了整个句子上下文之后的新表示。
多头注意力机制
现在我们使用 2个头 (h=2),每个头的维度 d_k=2
。
步骤 1: 分割 Q, K, V 到不同的“头”
在真实实现中,我们会使用不同的 W
矩阵为每个头生成独立的 Q, K, V。为简化,我们直接将之前计算的 Q, K, V 矩阵按列劈开。
Q_head1 = [[2,1], [1,2], [2,1]]
(Q的前2列)Q_head2 = [[1,1], [1,1], [2,2]]
(Q的后2列)K_head1 = [[0,1], [1,0], [2,2]]
(K的前2列)K_head2 = [[1,1], [2,1], [1,2]]
(K的后2列,此处为简化调整值)V_head1 = [[1,0], [1,2], [2,2]]
(V的前2列)V_head2 = [[2,1], [0,1], [2,2]]
(V的后2列)
步骤 2: 并行计算每个头的注意力
现在我们对每个头独立地重复上面的自注意力计算过程。 d k = 2 ≈ 1.414 \sqrt{d_k} = \sqrt{2} \approx 1.414 dk=2≈1.414。
Head 1:
scores_h1 = (Q_head1 @ K_head1.T) / 1.414
weights_h1 = softmax(scores_h1)
Z1 = weights_h1 @ V_head1
(假设计算结果为[[1.8, 1.5], [1.7, 1.6], [1.9, 1.8]]
)
Head 2:
scores_h2 = (Q_head2 @ K_head2.T) / 1.414
weights_h2 = softmax(scores_h2)
Z2 = weights_h2 @ V_head2
(假设计算结果为[[1.5, 1.9], [1.4, 1.8], [1.6, 2.0]]
)
步骤 3: 拼接与最终线性变换
-
拼接 (Concatenate) 所有头的输出:
Z = Concat(Z1, Z2)
= [[1.8, 1.5, 1.5, 1.9], # "The" 的新多头表示
[1.7, 1.6, 1.4, 1.8], # "cat" 的新多头表示
[1.9, 1.8, 1.6, 2.0]] # "sat" 的新多头表示
-
最终线性变换:
将拼接后的矩阵Z
乘以一个最终的输出权重矩阵W_O
(也是需要学习的),得到多头注意力的最终输出。
MultiHead_Output = Z @ W_O
掩码注意力机制
这个机制实现起来非常巧妙。它不是真的删除或隐藏未来的词,而是在计算注意力权重的过程中,通过一个“掩码 (Mask)”来让未来位置的注意力权重在数学上变为零。这个过程发生在计算完注意力分数、但在进行 Softmax 之前。
具体步骤:
- 和标准自注意力一样,我们先计算出注意力分数矩阵
Scores = QK^T / sqrt(d_k)
。
假设这是我们对句子 “The cat sat” 计算出的(已缩放的)注意力分数:
原始分数矩阵 (Scaled Scores):
The cat sat (Keys)
The [[1.0, 2.5, 4.5],
cat [2.0, 2.0, 4.5],
sat [3.0, 4.0, 6.0]]
(Queries)
- 我们创建一个“掩码矩阵 (Mask Matrix)”。这是一个上三角矩阵,其中我们希望保留的位置(当前和过去的位置)值为
0
,希望屏蔽掉的位置(未来的位置)为一个非常大的负数(比如-1e9
或负无穷)。
我们需要一个上三角部分为 − ∞ -\infty −∞ 的掩码(0
代表保留,1
代表屏蔽,这里我们用 − ∞ -\infty −∞ 来表示屏蔽的效果)。
[[ 0, -∞, -∞],[ 0, 0, -∞],[ 0, 0, 0]]
- 我们将这个掩码矩阵加到注意力分数矩阵上。
Masked Scores = Scaled Scores + Mask
= ( 1.0 2.5 4.5 2.0 2.0 4.5 3.0 4.0 6.0 ) + ( 0 − ∞ − ∞ 0 0 − ∞ 0 0 0 ) = ( 1.0 − ∞ − ∞ 2.0 2.0 − ∞ 3.0 4.0 6.0 ) = \begin{pmatrix} 1.0 & 2.5 & 4.5 \\ 2.0 & 2.0 & 4.5 \\ 3.0 & 4.0 & 6.0 \end{pmatrix} + \begin{pmatrix} 0 & -\infty & -\infty \\ 0 & 0 & -\infty \\ 0 & 0 & 0 \end{pmatrix} = \begin{pmatrix} 1.0 & -\infty & -\infty \\ 2.0 & 2.0 & -\infty \\ 3.0 & 4.0 & 6.0 \end{pmatrix} = 1.02.03.02.52.04.04.54.56.0 + 000−∞00−∞−∞0 = 1.02.03.0−∞2.04.0−∞−∞6.0 - 最后,对加了掩码的分数矩阵进行 Softmax 计算。
-
第一行 (处理 “The”):
softmax([1.0, -∞, -∞])
- e 1.0 ≈ 2.718 e^{1.0} \approx 2.718 e1.0≈2.718
- e − ∞ = 0 e^{-\infty} = 0 e−∞=0
- e − ∞ = 0 e^{-\infty} = 0 e−∞=0
Sum = 2.718 + 0 + 0 = 2.718
- 权重:
[2.718/2.718, 0/2.718, 0/2.718] = [1.0, 0.0, 0.0]
- 解读: “The” 只能关注它自己。
-
第二行 (处理 “cat”):
softmax([2.0, 2.0, -∞])
- e 2.0 ≈ 7.389 e^{2.0} \approx 7.389 e2.0≈7.389
- e 2.0 ≈ 7.389 e^{2.0} \approx 7.389 e2.0≈7.389
- e − ∞ = 0 e^{-\infty} = 0 e−∞=0
Sum = 7.389 + 7.389 + 0 = 14.778
- 权重:
[7.389/14.778, 7.389/14.778, 0/14.778] = [0.5, 0.5, 0.0]
- 解读: “cat” 可以关注 “The” 和它自己,但不能关注 “sat”。
-
第三行 (处理 “sat”):
softmax([3.0, 4.0, 6.0])
- 因为没有未来位置,所以这一行的计算与标准自注意力相同。
- 权重结果约为:
[0.024, 0.064, 0.912]
- 解读: “sat” 可以关注所有词。
最终的注意力权重矩阵 (Masked Attention Weights):
A_masked = [[1.0, 0.0, 0.0 ],
[0.5, 0.5, 0.0 ],
[0.024, 0.064, 0.912]]
这个权重矩阵接下来会像标准自注意力一样,与 V (值矩阵) 相乘,得到最终的输出。
公式
Attention ( Q , K , V ) = softmax ( Q K T d k + M ) V \text{Attention}(Q, K, V) = \text{softmax}(\frac{QK^T}{\sqrt{d_k}} + M)V Attention(Q,K,V)=softmax(dkQKT+M)V
- 这里的 M 就是我们的掩码矩阵,其中需要屏蔽的位置是 − ∞ -\infty −∞,其他位置是 0。
代码实现
import torch.nn as nn
import torch'''多头自注意力计算模块'''
class MultiHeadAttention(nn.Module):def __init__(self, args: ModelArgs, is_causal=False):# 构造函数# args: 配置对象super().__init__()# 隐藏层维度必须是头数的整数倍,因为后面我们会将输入拆成头数个矩阵assert args.n_embd % args.n_heads == 0# 模型并行处理大小,默认为1。model_parallel_size = 1# 本地计算头数,等于总头数除以模型并行处理大小。self.n_local_heads = args.n_heads // model_parallel_size# 每个头的维度,等于模型维度除以头的总数。self.head_dim = args.dim // args.n_heads# Wq, Wk, Wv 参数矩阵,每个参数矩阵为 n_embd x n_embd# 这里通过三个组合矩阵来代替了n个参数矩阵的组合,其逻辑在于矩阵内积再拼接其实等同于拼接矩阵再内积,# 不理解的读者可以自行模拟一下,每一个线性层其实相当于n个参数矩阵的拼接self.wq = nn.Linear(args.dim, args.n_heads * self.head_dim, bias=False)self.wk = nn.Linear(args.dim, args.n_heads * self.head_dim, bias=False)self.wv = nn.Linear(args.dim, args.n_heads * self.head_dim, bias=False)# 输出权重矩阵,维度为 n_embd x n_embd(head_dim = n_embeds / n_heads)self.wo = nn.Linear(args.n_heads * self.head_dim, args.dim, bias=False)# 注意力的 dropoutself.attn_dropout = nn.Dropout(args.dropout)# 残差连接的 dropoutself.resid_dropout = nn.Dropout(args.dropout)# 创建一个上三角矩阵,用于遮蔽未来信息# 注意,因为是多头注意力,Mask 矩阵比之前我们定义的多一个维度if is_causal:mask = torch.full((1, 1, args.max_seq_len, args.max_seq_len), float("-inf"))mask = torch.triu(mask, diagonal=1)# 注册为模型的缓冲区self.register_buffer("mask", mask)def forward(self, q: torch.Tensor, k: torch.Tensor, v: torch.Tensor):# 获取批次大小和序列长度,[batch_size, seq_len, dim]bsz, seqlen, _ = q.shape# 计算查询(Q)、键(K)、值(V),输入通过参数矩阵层,维度为 (B, T, n_embed) x (n_embed, n_embed) -> (B, T, n_embed)xq, xk, xv = self.wq(q), self.wk(k), self.wv(v)# 将 Q、K、V 拆分成多头,维度为 (B, T, n_head, C // n_head),然后交换维度,变成 (B, n_head, T, C // n_head)# 因为在注意力计算中我们是取了后两个维度参与计算# 为什么要先按B*T*n_head*C//n_head展开再互换1、2维度而不是直接按注意力输入展开,是因为view的展开方式是直接把输入全部排开,# 然后按要求构造,可以发现只有上述操作能够实现我们将每个头对应部分取出来的目标xq = xq.view(bsz, seqlen, self.n_local_heads, self.head_dim)xk = xk.view(bsz, seqlen, self.n_local_heads, self.head_dim)xv = xv.view(bsz, seqlen, self.n_local_heads, self.head_dim)xq = xq.transpose(1, 2)xk = xk.transpose(1, 2)xv = xv.transpose(1, 2)# 注意力计算# 计算 QK^T / sqrt(d_k),维度为 (B, nh, T, hs) x (B, nh, hs, T) -> (B, nh, T, T)scores = torch.matmul(xq, xk.transpose(2, 3)) / math.sqrt(self.head_dim)# 掩码自注意力必须有注意力掩码if self.is_causal:assert hasattr(self, 'mask')# 这里截取到序列长度,因为有些序列可能比 max_seq_len 短scores = scores + self.mask[:, :, :seqlen, :seqlen]# 计算 softmax,维度为 (B, nh, T, T)scores = F.softmax(scores.float(), dim=-1).type_as(xq)# 做 Dropoutscores = self.attn_dropout(scores)# V * Score,维度为(B, nh, T, T) x (B, nh, T, hs) -> (B, nh, T, hs)output = torch.matmul(scores, xv)# 恢复时间维度并合并头。# 将多头的结果拼接起来, 先交换维度为 (B, T, n_head, C // n_head),再拼接成 (B, T, n_head * C // n_head)# contiguous 函数用于重新开辟一块新内存存储,因为Pytorch设置先transpose再view会报错,# 因为view直接基于底层存储得到,然而transpose并不会改变底层存储,因此需要额外存储output = output.transpose(1, 2).contiguous().view(bsz, seqlen, -1)# 最终投影回残差流。output = self.wo(output)output = self.resid_dropout(output)return output
相关文章:
大模型学习入门——Day3:注意力机制
本系列笔记的教材:快乐学习大模型-DataWhale团队 注意力机制 注意力机制最先源于计算机视觉领域,其核心思想为当我们关注一张图片,我们往往无需看清楚全部内容而仅将注意力集中在重点部分即可。而在自然语言处理领域,我们往往也…...
C++ 学习笔记精要(二)
第一节 特殊类的设计 1. 一个类: 只能在堆上创建对象 关键点:自己控制析构 1.1 方法一: 使用delete禁掉默认析构函数 #include <iostream> using namespace std;class HeapOnly { public:HeapOnly(){_str new char[10];}~HeapOnly() delete;void Destroy(){delete[…...
博士,超28岁,出局!
近日,长沙市望城区《2025年事业引才博士公开引进公告》引发轩然大波——博士岗位年龄要求28周岁及以下,特别优秀者也仅放宽至30周岁。 图源:网络 这份规定让众多"高龄"博士生直呼不合理,并在社交平台掀起激烈讨论。 图源…...
macOS - 根据序列号查看机型、保障信息
文章目录 最近在看 MacBook 二手机,有个咸鱼卖家放个截图 说不清参数,于是想根据 序列号 查看机型。苹果提供了这样的网页: https://checkcoverage.apple.com/ (无需登录) 结果 2025-06-20(五)…...
C/C++ 高频八股文面试题1000题(一)
原作者:Linux教程,原文地址:C/C 高频八股文面试题1000题(一) 在准备技术岗位的求职过程中,C/C始终是绕不开的核心考察点。无论是互联网大厂的笔试面试,还是嵌入式、后台开发、系统编程等方向的岗位,C/C 都…...
C++ map 和 unordered_map 的区别和联系
C map 和 unordered_map 的区别和联系 map 和 unordered_map 都是 C 标准库中关联容器,用于存储键值对。它们的主要区别在于底层实现和性能特性,联系在于它们都提供了键值对的存储和访问功能。 区别: 特性mapunordered_map底层实现红黑树 …...
Sentinel实现原理
Sentinel 是阿里巴巴开源的分布式系统流量控制组件,主要用于服务保护,涵盖流量控制、熔断降级、系统负载保护等功能。 以下是 Sentinel 的实现原理,使用中文简要说明: 1. 总体架构 Sentinel 采用 轻量级 设计,分为 核…...
python打卡day37
疏锦行 知识点回顾: 1. 过拟合的判断:测试集和训练集同步打印指标 2. 模型的保存和加载 a. 仅保存权重 b. 保存权重和模型 c. 保存全部信息checkpoint,还包含训练状态 3. 早停策略 作业:对信贷数据集训练后保存权重…...
MySQL复杂查询优化实战:从多表关联到子查询的性能突破
文章目录 一、复杂查询性能瓶颈分析与优化框架二、多表关联查询的优化策略与实战1. JOIN顺序优化:基于成本估算的表关联策略2. 复合索引与JOIN条件优化3. 大表JOIN的分片处理 三、子查询优化:从嵌套到JOIN的转换艺术1. 标量子查询转换为JOIN2. EXISTS子查…...
LeetCode 680.验证回文串 II
目录 题目: 题目描述: 题目链接: 思路: 核心思路: 思路详解: 代码: C代码: Java代码: 题目: 题目描述: 题目链接: 680. 验证…...
window显示驱动开发—输出合并器阶段
逻辑管道中的最后一步是通过模具或深度确定可见性,以及写入或混合输出以呈现目标,这可以是多种资源类型之一。 这些操作以及输出资源 (呈现目标) 绑定在输出合并阶段定义。 1. 核心功能与管线定位 输出合并是渲染管线的最终固定功能阶段,负…...
单片机开发日志cv MDK-ARM工具链迁移到MAKE
核心经验: STM32H7 多 RAM 区域,外设相关数据段必须放在 AXI SRAM(RAM)区,不能放在 DTCMRAM,否则外设无法访问,程序表面正常但外设全失效。迁移工程时,务必检查链接脚本的内存分布&a…...
大模型与搜索引擎的技术博弈及未来智能范式演进
基于认知革命与技术替代的全景综述 一、大模型对搜索引擎的替代性分析:技术范式与市场重构 (1)技术原理的代际分野 传统搜索引擎遵循 "爬虫抓取 - 索引构建 - 关键词排序" 的三段式架构,其核心是基于 PageRank 算法的…...
Ajax-入门
Ajax: 全称Asynchronous JavaScript And XML,异步的JavaScript和XML。其作用有如下2点: 与服务器进行数据交换:通过Ajax可以给服务器发送请求,并获取服务器响应的数据。 异步交互:可以在不重新加载整个页面的情况下&a…...
FPGA基础 -- Verilog 共享任务(task)和函数(function)
Verilog 中共享任务(task)和函数(function) 的详细专业培训,适合具有一定 RTL 编程经验的工程师深入掌握。 一、任务(task)与函数(function)的基本区别 特性taskfunctio…...
c++set和pair的使用
set是C中的一种关联容器,具有以下特点: 存储唯一元素(不允许重复) 元素自动排序(默认升序) 基于红黑树实现(平衡二叉搜索树) 插入、删除和查找的时间复杂度为O(log n) 前言 在C…...
数据库中间件ShardingSphere5
一、高性能架构模式 数据库集群,第一种方式“读写分离”,第二种方式“数据库分片”。 1.1 读写分离架构 读写分离原理:将数据库读写操作分散到不同的节点上。 读写分离的基本实现: 主库负责处理事务性的增删改操作,…...
window显示驱动开发—使用状态刷新回调函数
用户模式显示驱动程序可以使用 Direct3D 运行时版本 10 State-Refresh回调函数 来实现无状态驱动程序或构建命令缓冲区前导数据。 Direct3D 运行时在调用 CreateDevice (D3D10 ) 函数时,向D3D10DDIARG_CREATEDEVICE结构的 pUMCallbacks 成员指向的D3D10DDI_CORELAY…...
windows11右击恢复为windows10
文章目录 前言一、问题描述二、解决方案 前言 为了解决win11的右击更多选项的问题 一、问题描述 win11的右键更多选项过于繁琐 二、解决方案 在windows11的终端管理员中输入如下代码: reg add "HKCU\Software\Classes\CLSID\{86ca1aa0-34aa-4e8b-a509-50c…...
基于物联网的智能衣柜系统设计
标题:基于物联网的智能衣柜系统设计 内容:1.摘要 随着物联网技术的飞速发展,智能家居领域迎来了新的变革机遇。本研究的目的在于设计一种基于物联网的智能衣柜系统,以提升用户的衣物管理和使用体验。方法上,通过搭建物联网硬件平台ÿ…...
GM DC Monitor v2.0 卸载教程
以下俩种方法任选一种均可 第一种方法:一键自动卸载 进入到软件安装目录 卸载app 进入到app目录,运行一键卸载脚本:sh uninstall.sh 卸载es 进入到es目录,运行一键卸载脚本:sh uninstall.sh 卸载db 进入到db目录&a…...
C#上位机实现报警语音播报
我们在开发C#上位机时,有时候会需要将报警信息通过语音进行播报,今天跟大家分享一下具体的实现过程。 一、组件安装 首先我们创建好一个Windows窗体项目,然后添加System.Speech库引用。 点击引用,右击添加引用,在程…...
python自助棋牌室管理系统
目录 技术栈介绍具体实现截图系统设计研究方法:设计步骤设计流程核心代码部分展示研究方法详细视频演示试验方案论文大纲源码获取/详细视频演示 技术栈介绍 Django-SpringBoot-php-Node.js-flask 本课题的研究方法和研究步骤基本合理,难度适中…...
榕壹云婚恋相亲系统:ThinkPHP+UniApp打造高效婚配平台
引言 在数字化浪潮下,婚恋相亲行业正加速向线上迁移。榕壹云公司基于市场需求与技术积累,开发一款功能完备、技术开源的婚恋相亲小程序系统,为单身人士提供高效、安全的婚恋平台。本文将围绕系统背景、客户定位、核心技术、功能模块及优势场景展开详细解析,助力开发者与技…...
每日leetcode
2890. 重塑数据:融合 - 力扣(LeetCode) 题目 DataFrame report --------------------- | Column Name | Type | --------------------- | product | object | | quarter_1 | int | | quarter_2 | int | | quarter_3 | i…...
深入理解XGBoost(何龙 著)学习笔记(五)
深入理解XGBoost(何龙 著)学习笔记(五) 本文接上一篇,内容为线性回归,介绍三部分,首先介绍了"模型评估”,然后分别提供了线性回归的模型代码:scikit-learn的Linear…...
SelectDB 在 AWS Graviton ARM 架构下相比 x86 实现 36% 性价比提升
在海量数据分析中,追求高性价比已成为各大企业的主流趋势。ARM 架构凭借其高能效和低成本的特点,逐渐在数据中心崛起,成为理想的高性价比选择。基于 ARM 架构的 AWS Graviton 系列处理器,正是这一趋势的典型代表。Graviton 处理器…...
机器学习流量识别(pytorch+NSL-KDD+多分类建模)
本文主要实现以下功能,会提供完整的可运行的代码以及解释为什么这么设计。文章不会收费,若被限制查看,请私信我。 使用 NSL-KDD 数据集的CSV文件进行流量攻击检测,使用机器学习算法实现流量攻击检测,使用pytorch框架…...
三种经典算法无人机三维路径规划对比(SMA、HHO、GWO三种算法),Matlab代码实现
代码功能 该MATLAB代码用于对比三种元启发式优化算法(SMA、HHO、GWO三种算法, SMA黏菌算法、HHO哈里斯鹰优化算法、GWO灰狼优化算法) 在特定优化问题上的性能,运行环境MATLABR2020b或更高 : 初始化问题模型ÿ…...
FTTR+软路由网络拓扑方案
文章目录 网络拓扑软路由配置FTTR光猫路由器TPLink路由器配置WAN设置LAN设置 参考 网络拓扑 软路由配置 配置静态IP地址:192.168.1.100设置网关指向主路由的IP 设置自定义DNS服务器 开启DHCP 这一步很关键,可以让连上wifi的所有设备自动趴强。 FTTR光猫…...
服务器获取外网IP,并发送到钉钉
服务器获取外网IP,并发送到钉钉 import time import hmac import hashlib import base64 import urllib.parse import requests# 请填入你的钉钉机器人配置 access_token XXXX secret XXXX# 获取公网 IP def get_public_ip():try:response requests.get("…...
解决uni-app发布微信小程序主包大小限制为<2M的问题
一 问题说明 我想用uniapp开发多端应用,引入了uview组件库来美化样式,可发布为微信小程序却提示我代码质量不过关,主包代码量太大了: 二 问题分析 2.1 原生微信小程序开发代码质量限制: 1.主包代码大小不得大于2M&…...
魅族“换血”出牌:手机基本盘站不稳,想靠AI和汽车“改命”
撰稿|何威 来源|贝多财经 被吉利收购后,魅族逐渐转向在AI领域躬身耕作。 自2024年2月以“All in AI”正式宣告转型、喊出不再推出传统智能手机的豪言开始,这家曾以设计见长的手机厂商,将下半场押注在AI终端、AR眼镜与智能座舱系统上&#…...
原点安全入选 Gartner®“数据安全平台”中国市场指南代表厂商
2025年1月7日,全球权威咨询与分析机构 Gartner 发布《中国数据安全平台市场指南》(China Context: ‘Market Guide for Data Security Platforms’),北京原点数安科技有限公司(简称“原点安全”,英文名称&q…...
uni-app-配合iOS App项目开发apple watch app
假设你已经用uni-app开发好了一个iOS端的app,现在想要开发一个配套的apple watch app。改怎么去开发呢?是不是一头雾水,这篇文章就会介绍一些apple watch app开发的知识以及如何在uni-app开发的iOS app基础上去开发配套的watch app。 一、ap…...
如何理解Java反射机制
反射机制原理 反射是Java在运行时动态获取类信息、操作类属性和方法的能力。核心原理是JVM在类加载时创建Class对象,该对象包含类的完整结构信息。 关键类: Class:类的元数据入口 Field:类的成员变量 Method:类的方…...
SM3算法C语言实现(无第三方库,带测试)
一、SM3算法介绍 SM3算法是中国国家密码管理局(OSCCA)于2010年发布的商用密码散列函数标准,属于我国自主设计的密码算法体系之一 ,标准文档下载地址为:SM3密码杂凑算法 。SM3算法输出长度为256位(32字节&a…...
King’s LIMS 系统引领汽车检测实验室数字化转型
随着汽车保有量的持续攀升和车龄的增长,消费者对汽车的需求已悄然转变,从最初对外观和性能的追求,逐渐深化为对安全性、可靠性、耐久性、性能与舒适性以及智能化功能的全方位关注。这无疑让汽车检测行业在保障车辆质量、满足市场需求方面肩负…...
CppCon 2017 学习:Mocking Frameworks Considered
当然可以,下面是对 Fowler 的 Whiskey-Store 示例。 Fowler 的 Whiskey-Store 示例(坏设计) 贴出的类图是 Martin Fowler 在《重构》书中使用的一个教学用反面案例(故意设计得不合理),用来说明如何通过重…...
通过事件过滤器拦截QRadioButton点击事件
通过事件过滤器拦截QRadioButton点击事件 一、事件过滤器完整实现 1. 核心代码扩展(含注释) bool MainWindow::eventFilter(QObject* obj, QEvent* ev) {// 拦截所有QRadioButton的鼠标事件(包括点击、释放、双击)if (ev->ty…...
领码 SPARK 融合平台赋能工程建设行业物资管理革新——数智赋能,重塑中国模式新范式
摘要 工程建设行业正加速迈向数字化与精益化转型,物资管理成为项目成败的关键瓶颈。本文深入解析中国工程企业“项目部-物资部-企业项目管理部”三级协同的独特物资管理体系,聚焦集中采购与零星采购的统筹难题。基于领码 SPARK 融合平台,提出…...
“地标界爱马仕”再启:世酒中菜联袂陈汇堂共筑新会陈皮顶奢产业
“地标界爱马仕”再启战略新篇:世酒中菜联袂陈汇堂,共筑新会陈皮顶奢产业生态 ——中世国际与陈汇堂股权合作签约仪式在国际地理标志服务基地举行 江门市新会区,2025年6月20日——被誉为“地标界爱马仕”的全球顶奢品牌运营商世酒中菜 &…...
.Net Framework 4/C# 数据访问技术(ADO.NET)
一、数据库基础 (一) 数据库简介 数据库是按照数据结构来组织、存储和管理数据的仓库,是存储在一起的相关数据的集合。 (二) SQL 语言简介 SQL 是一种数据库查询和程序设计语言,用于存取数据以及查询,更新和管理关系型数据库系统。在编写 SQL 语句时,SQL 语句各关键字要以…...
北京京东,看看难度
最近由于三大外卖平台“打仗”,优惠券多到数不过来,一日三餐每个平台各点一单哈哈哈,正好最近组织内部还有朋友在北京的京东面试过,分享一下她的面经(Java岗): 1. Kafka消息不丢失问题…...
RPGMZ游戏引擎 如何手动控制文字显示速度
直接上代码 const _Window_Base_prototype_initialize Window_Base.prototype.initialize;Window_Base.prototype.initialize function(rect) {_Window_Base_prototype_initialize.call(this, rect);this.文字速度缓冲 0;}; this.文字速度缓冲 0; 进行缓冲 Window_Base…...
linux线程同步
互斥锁 同步与互斥概述** 现代操作系统基本都是多任务操作系统,即同时有大量可调度实体在运行。在多任务操作系统中,同时运行的多个任务可能: 都需要访问/使用同一种资源 多个任务之间有依赖关系,某个任务的运行依赖于另一个任…...
大内存对电脑性能有哪些提升
在科技飞速发展的今天,电脑已经成为我们生活和工作中不可或缺的伙伴。无论是日常办公、追剧娱乐,还是进行复杂的游戏和专业设计,电脑的性能都至关重要。而在影响电脑性能的众多因素中,内存大小常常被人们忽视。 多任务处理更流畅…...
什么是“微博养铁粉”以及如何增加微博铁粉
发了个发微博养铁工具_微博养铁粉的定义 微博养铁粉是指粉丝通过与博主的互动,成为博主的铁粉。铁粉是微博推出的一种反映粉丝与博主之间亲密度的互动产品。成为铁粉后,粉丝的评论权重增加,更容易上前排,点赞和评论的效果也会更好…...
华为和H3C服务器配置远控管理地址
1、华为RH2288_V3服务器 1.1、启动服务器按DEL按键进入服务器bios 1.2、选择Advanced菜单中的 IPMI iBMC Configuration配置项回车进入。 1.3、IPMI iBMC Configuration配置界面中选择IBMC Configuration配置项回车进入。 1.4、IBMC Configuration 配置项中配置IPV4 Configura…...
Git 查询与切换分支的完整指南
Git 查询与切换分支的完整指南 1. 查询分支列表 查看本地分支 git branch当前分支会以绿色显示并带有 * 标记添加 -v 或 -vv 查看更详细的信息(最后一次提交和跟踪关系) git branch -v # 或者 git branch -vv查看所有分支(包括远程分支&a…...