59 双向循环神经网络_by《李沐:动手学深度学习v2》pytorch版

news2024/9/29 0:16:59

系列文章目录


文章目录

  • 系列文章目录
  • 双向RNN
    • 推理
  • 总结
  • 以下为理论部分
  • 双向循环神经网络
    • 隐马尔可夫模型中的动态规划
    • 双向模型
      • 定义
      • 模型的计算代价及其应用
    • (**双向循环神经网络的错误应用**)
    • 小结
    • 练习


双向RNN

在这里插入图片描述
这里理解这个图的时候,不要把正向和逆向认为有上下的关系,其实这两者都是各自做各自的运算,最后只在 O t O_t Ot进行了一次运算。

推理

在这里插入图片描述
双向RNN非常不适合做我们之前的那种推理任务,因为后边的数据还没有,这样根本不足以满足双向RNN的要求,因此一般只用做训练来对句子进行特征提取,比如在翻译的时候,给我的句子我用双向来处理,来得到句子的语义信息。

总结

  1. 双向循环神经网络通过反向更新的隐藏层来利用方向时间信息
  2. 通常用来对序列抽取特征、填空,而不是预测未来

以下为理论部分

双向循环神经网络

🏷sec_bi_rnn

在序列学习中,我们以往假设的目标是:
在给定观测的情况下
(例如,在时间序列的上下文中或在语言模型的上下文中),
对下一个输出进行建模。
虽然这是一个典型情景,但不是唯一的。
还可能发生什么其它的情况呢?
我们考虑以下三个在文本序列中填空的任务。

  • ___
  • ___饿了。
  • ___饿了,我可以吃半头猪。

根据可获得的信息量,我们可以用不同的词填空,
如“很高兴”(“happy”)、“不”(“not”)和“非常”(“very”)。
很明显,每个短语的“下文”传达了重要信息(如果有的话),
而这些信息关乎到选择哪个词来填空,
所以无法利用这一点的序列模型将在相关任务上表现不佳。
例如,如果要做好命名实体识别
(例如,识别“Green”指的是“格林先生”还是绿色),
不同长度的上下文范围重要性是相同的。
为了获得一些解决问题的灵感,让我们先迂回到概率图模型。

隐马尔可夫模型中的动态规划

这一小节是用来说明动态规划问题的,
具体的技术细节对于理解深度学习模型并不重要,
但它有助于我们思考为什么要使用深度学习,
以及为什么要选择特定的架构。

如果我们想用概率图模型来解决这个问题,
可以设计一个隐变量模型:
在任意时间步 t t t,假设存在某个隐变量 h t h_t ht
通过概率 P ( x t ∣ h t ) P(x_t \mid h_t) P(xtht)控制我们观测到的 x t x_t xt
此外,任何 h t → h t + 1 h_t \to h_{t+1} htht+1转移
都是由一些状态转移概率 P ( h t + 1 ∣ h t ) P(h_{t+1} \mid h_{t}) P(ht+1ht)给出。
这个概率图模型就是一个隐马尔可夫模型(hidden Markov model,HMM),
如 :numref:fig_hmm所示。

在这里插入图片描述

🏷fig_hmm

因此,对于有 T T T个观测值的序列,
我们在观测状态和隐状态上具有以下联合概率分布:

P ( x 1 , … , x T , h 1 , … , h T ) = ∏ t = 1 T P ( h t ∣ h t − 1 ) P ( x t ∣ h t ) ,  where  P ( h 1 ∣ h 0 ) = P ( h 1 ) . P(x_1, \ldots, x_T, h_1, \ldots, h_T) = \prod_{t=1}^T P(h_t \mid h_{t-1}) P(x_t \mid h_t), \text{ where } P(h_1 \mid h_0) = P(h_1). P(x1,,xT,h1,,hT)=t=1TP(htht1)P(xtht), where P(h1h0)=P(h1).
:eqlabel:eq_hmm_jointP

现在,假设我们观测到所有的 x i x_i xi,除了 x j x_j xj
并且我们的目标是计算 P ( x j ∣ x − j ) P(x_j \mid x_{-j}) P(xjxj)
其中 x − j = ( x 1 , … , x j − 1 , x j + 1 , … , x T ) x_{-j} = (x_1, \ldots, x_{j-1}, x_{j+1}, \ldots, x_{T}) xj=(x1,,xj1,xj+1,,xT)
由于 P ( x j ∣ x − j ) P(x_j \mid x_{-j}) P(xjxj)中没有隐变量,
因此我们考虑对 h 1 , … , h T h_1, \ldots, h_T h1,,hT选择构成的
所有可能的组合进行求和。
如果任何 h i h_i hi可以接受 k k k个不同的值(有限的状态数),
这意味着我们需要对 k T k^T kT个项求和,
这个任务显然难于登天。
幸运的是,有个巧妙的解决方案:动态规划(dynamic programming)。

要了解动态规划的工作方式,
我们考虑对隐变量 h 1 , … , h T h_1, \ldots, h_T h1,,hT的依次求和。
根据 :eqref:eq_hmm_jointP,将得出:

P ( x 1 , … , x T ) = ∑ h 1 , … , h T P ( x 1 , … , x T , h 1 , … , h T ) = ∑ h 1 , … , h T ∏ t = 1 T P ( h t ∣ h t − 1 ) P ( x t ∣ h t ) = ∑ h 2 , … , h T [ ∑ h 1 P ( h 1 ) P ( x 1 ∣ h 1 ) P ( h 2 ∣ h 1 ) ] ⏟ π 2 ( h 2 ) = d e f P ( x 2 ∣ h 2 ) ∏ t = 3 T P ( h t ∣ h t − 1 ) P ( x t ∣ h t ) = ∑ h 3 , … , h T [ ∑ h 2 π 2 ( h 2 ) P ( x 2 ∣ h 2 ) P ( h 3 ∣ h 2 ) ] ⏟ π 3 ( h 3 ) = d e f P ( x 3 ∣ h 3 ) ∏ t = 4 T P ( h t ∣ h t − 1 ) P ( x t ∣ h t ) = … = ∑ h T π T ( h T ) P ( x T ∣ h T ) . \begin{aligned} &P(x_1, \ldots, x_T) \\ =& \sum_{h_1, \ldots, h_T} P(x_1, \ldots, x_T, h_1, \ldots, h_T) \\ =& \sum_{h_1, \ldots, h_T} \prod_{t=1}^T P(h_t \mid h_{t-1}) P(x_t \mid h_t) \\ =& \sum_{h_2, \ldots, h_T} \underbrace{\left[\sum_{h_1} P(h_1) P(x_1 \mid h_1) P(h_2 \mid h_1)\right]}_{\pi_2(h_2) \stackrel{\mathrm{def}}{=}} P(x_2 \mid h_2) \prod_{t=3}^T P(h_t \mid h_{t-1}) P(x_t \mid h_t) \\ =& \sum_{h_3, \ldots, h_T} \underbrace{\left[\sum_{h_2} \pi_2(h_2) P(x_2 \mid h_2) P(h_3 \mid h_2)\right]}_{\pi_3(h_3)\stackrel{\mathrm{def}}{=}} P(x_3 \mid h_3) \prod_{t=4}^T P(h_t \mid h_{t-1}) P(x_t \mid h_t)\\ =& \dots \\ =& \sum_{h_T} \pi_T(h_T) P(x_T \mid h_T). \end{aligned} ======P(x1,,xT)h1,,hTP(x1,,xT,h1,,hT)h1,,hTt=1TP(htht1)P(xtht)h2,,hTπ2(h2)=def [h1P(h1)P(x1h1)P(h2h1)]P(x2h2)t=3TP(htht1)P(xtht)h3,,hTπ3(h3)=def [h2π2(h2)P(x2h2)P(h3h2)]P(x3h3)t=4TP(htht1)P(xtht)hTπT(hT)P(xThT).

通常,我们将前向递归(forward recursion)写为:

π t + 1 ( h t + 1 ) = ∑ h t π t ( h t ) P ( x t ∣ h t ) P ( h t + 1 ∣ h t ) . \pi_{t+1}(h_{t+1}) = \sum_{h_t} \pi_t(h_t) P(x_t \mid h_t) P(h_{t+1} \mid h_t). πt+1(ht+1)=htπt(ht)P(xtht)P(ht+1ht).

递归被初始化为 π 1 ( h 1 ) = P ( h 1 ) \pi_1(h_1) = P(h_1) π1(h1)=P(h1)
符号简化,也可以写成 π t + 1 = f ( π t , x t ) \pi_{t+1} = f(\pi_t, x_t) πt+1=f(πt,xt)
其中 f f f是一些可学习的函数。
这看起来就像我们在循环神经网络中讨论的隐变量模型中的更新方程。

与前向递归一样,我们也可以使用后向递归对同一组隐变量求和。这将得到:

P ( x 1 , … , x T ) = ∑ h 1 , … , h T P ( x 1 , … , x T , h 1 , … , h T ) = ∑ h 1 , … , h T ∏ t = 1 T − 1 P ( h t ∣ h t − 1 ) P ( x t ∣ h t ) ⋅ P ( h T ∣ h T − 1 ) P ( x T ∣ h T ) = ∑ h 1 , … , h T − 1 ∏ t = 1 T − 1 P ( h t ∣ h t − 1 ) P ( x t ∣ h t ) ⋅ [ ∑ h T P ( h T ∣ h T − 1 ) P ( x T ∣ h T ) ] ⏟ ρ T − 1 ( h T − 1 ) = d e f = ∑ h 1 , … , h T − 2 ∏ t = 1 T − 2 P ( h t ∣ h t − 1 ) P ( x t ∣ h t ) ⋅ [ ∑ h T − 1 P ( h T − 1 ∣ h T − 2 ) P ( x T − 1 ∣ h T − 1 ) ρ T − 1 ( h T − 1 ) ] ⏟ ρ T − 2 ( h T − 2 ) = d e f = … = ∑ h 1 P ( h 1 ) P ( x 1 ∣ h 1 ) ρ 1 ( h 1 ) . \begin{aligned} & P(x_1, \ldots, x_T) \\ =& \sum_{h_1, \ldots, h_T} P(x_1, \ldots, x_T, h_1, \ldots, h_T) \\ =& \sum_{h_1, \ldots, h_T} \prod_{t=1}^{T-1} P(h_t \mid h_{t-1}) P(x_t \mid h_t) \cdot P(h_T \mid h_{T-1}) P(x_T \mid h_T) \\ =& \sum_{h_1, \ldots, h_{T-1}} \prod_{t=1}^{T-1} P(h_t \mid h_{t-1}) P(x_t \mid h_t) \cdot \underbrace{\left[\sum_{h_T} P(h_T \mid h_{T-1}) P(x_T \mid h_T)\right]}_{\rho_{T-1}(h_{T-1})\stackrel{\mathrm{def}}{=}} \\ =& \sum_{h_1, \ldots, h_{T-2}} \prod_{t=1}^{T-2} P(h_t \mid h_{t-1}) P(x_t \mid h_t) \cdot \underbrace{\left[\sum_{h_{T-1}} P(h_{T-1} \mid h_{T-2}) P(x_{T-1} \mid h_{T-1}) \rho_{T-1}(h_{T-1}) \right]}_{\rho_{T-2}(h_{T-2})\stackrel{\mathrm{def}}{=}} \\ =& \ldots \\ =& \sum_{h_1} P(h_1) P(x_1 \mid h_1)\rho_{1}(h_{1}). \end{aligned} ======P(x1,,xT)h1,,hTP(x1,,xT,h1,,hT)h1,,hTt=1T1P(htht1)P(xtht)P(hThT1)P(xThT)h1,,hT1t=1T1P(htht1)P(xtht)ρT1(hT1)=def [hTP(hThT1)P(xThT)]h1,,hT2t=1T2P(htht1)P(xtht)ρT2(hT2)=def hT1P(hT1hT2)P(xT1hT1)ρT1(hT1) h1P(h1)P(x1h1)ρ1(h1).

因此,我们可以将后向递归(backward recursion)写为:

ρ t − 1 ( h t − 1 ) = ∑ h t P ( h t ∣ h t − 1 ) P ( x t ∣ h t ) ρ t ( h t ) , \rho_{t-1}(h_{t-1})= \sum_{h_{t}} P(h_{t} \mid h_{t-1}) P(x_{t} \mid h_{t}) \rho_{t}(h_{t}), ρt1(ht1)=htP(htht1)P(xtht)ρt(ht),

初始化 ρ T ( h T ) = 1 \rho_T(h_T) = 1 ρT(hT)=1
前向和后向递归都允许我们对 T T T个隐变量在 O ( k T ) \mathcal{O}(kT) O(kT)
(线性而不是指数)时间内对 ( h 1 , … , h T ) (h_1, \ldots, h_T) (h1,,hT)的所有值求和。
这是使用图模型进行概率推理的巨大好处之一。
它也是通用消息传递算法 :cite:Aji.McEliece.2000的一个非常特殊的例子。
结合前向和后向递归,我们能够计算

P ( x j ∣ x − j ) ∝ ∑ h j π j ( h j ) ρ j ( h j ) P ( x j ∣ h j ) . P(x_j \mid x_{-j}) \propto \sum_{h_j} \pi_j(h_j) \rho_j(h_j) P(x_j \mid h_j). P(xjxj)hjπj(hj)ρj(hj)P(xjhj).

因为符号简化的需要,后向递归也可以写为 ρ t − 1 = g ( ρ t , x t ) \rho_{t-1} = g(\rho_t, x_t) ρt1=g(ρt,xt)
其中 g g g是一个可以学习的函数。
同样,这看起来非常像一个更新方程,
只是不像我们在循环神经网络中看到的那样前向运算,而是后向计算。
事实上,知道未来数据何时可用对隐马尔可夫模型是有益的。
信号处理学家将是否知道未来观测这两种情况区分为内插和外推,
有关更多详细信息,请参阅 :cite:Doucet.De-Freitas.Gordon.2001

双向模型

如果我们希望在循环神经网络中拥有一种机制,
使之能够提供与隐马尔可夫模型类似的前瞻能力,
我们就需要修改循环神经网络的设计。
幸运的是,这在概念上很容易,
只需要增加一个“从最后一个词元开始从后向前运行”的循环神经网络,
而不是只有一个在前向模式下“从第一个词元开始运行”的循环神经网络。
双向循环神经网络(bidirectional RNNs)
添加了反向传递信息的隐藏层,以便更灵活地处理此类信息。
:numref:fig_birnn描述了具有单个隐藏层的双向循环神经网络的架构。

在这里插入图片描述

🏷fig_birnn

事实上,这与隐马尔可夫模型中的动态规划的前向和后向递归没有太大区别。
其主要区别是,在隐马尔可夫模型中的方程具有特定的统计意义。
双向循环神经网络没有这样容易理解的解释,
我们只能把它们当作通用的、可学习的函数。
这种转变集中体现了现代深度网络的设计原则:
首先使用经典统计模型的函数依赖类型,然后将其参数化为通用形式。

定义

双向循环神经网络是由 :cite:Schuster.Paliwal.1997提出的,
关于各种架构的详细讨论请参阅 :cite:Graves.Schmidhuber.2005
让我们看看这样一个网络的细节。

对于任意时间步 t t t,给定一个小批量的输入数据
X t ∈ R n × d \mathbf{X}_t \in \mathbb{R}^{n \times d} XtRn×d
(样本数 n n n,每个示例中的输入数 d d d),
并且令隐藏层激活函数为 ϕ \phi ϕ
在双向架构中,我们设该时间步的前向和反向隐状态分别为
H → t ∈ R n × h \overrightarrow{\mathbf{H}}_t \in \mathbb{R}^{n \times h} H tRn×h
H ← t ∈ R n × h \overleftarrow{\mathbf{H}}_t \in \mathbb{R}^{n \times h} H tRn×h
其中 h h h是隐藏单元的数目。
前向和反向隐状态的更新如下:

H → t = ϕ ( X t W x h ( f ) + H → t − 1 W h h ( f ) + b h ( f ) ) , H ← t = ϕ ( X t W x h ( b ) + H ← t + 1 W h h ( b ) + b h ( b ) ) , \begin{aligned} \overrightarrow{\mathbf{H}}_t &= \phi(\mathbf{X}_t \mathbf{W}_{xh}^{(f)} + \overrightarrow{\mathbf{H}}_{t-1} \mathbf{W}_{hh}^{(f)} + \mathbf{b}_h^{(f)}),\\ \overleftarrow{\mathbf{H}}_t &= \phi(\mathbf{X}_t \mathbf{W}_{xh}^{(b)} + \overleftarrow{\mathbf{H}}_{t+1} \mathbf{W}_{hh}^{(b)} + \mathbf{b}_h^{(b)}), \end{aligned} H tH t=ϕ(XtWxh(f)+H t1Whh(f)+bh(f)),=ϕ(XtWxh(b)+H t+1Whh(b)+bh(b)),

其中,权重 W x h ( f ) ∈ R d × h , W h h ( f ) ∈ R h × h , W x h ( b ) ∈ R d × h , W h h ( b ) ∈ R h × h \mathbf{W}_{xh}^{(f)} \in \mathbb{R}^{d \times h}, \mathbf{W}_{hh}^{(f)} \in \mathbb{R}^{h \times h}, \mathbf{W}_{xh}^{(b)} \in \mathbb{R}^{d \times h}, \mathbf{W}_{hh}^{(b)} \in \mathbb{R}^{h \times h} Wxh(f)Rd×h,Whh(f)Rh×h,Wxh(b)Rd×h,Whh(b)Rh×h
和偏置 b h ( f ) ∈ R 1 × h , b h ( b ) ∈ R 1 × h \mathbf{b}_h^{(f)} \in \mathbb{R}^{1 \times h}, \mathbf{b}_h^{(b)} \in \mathbb{R}^{1 \times h} bh(f)R1×h,bh(b)R1×h都是模型参数。

接下来,将前向隐状态 H → t \overrightarrow{\mathbf{H}}_t H t
和反向隐状态 H ← t \overleftarrow{\mathbf{H}}_t H t连接起来,
获得需要送入输出层的隐状态 H t ∈ R n × 2 h \mathbf{H}_t \in \mathbb{R}^{n \times 2h} HtRn×2h
在具有多个隐藏层的深度双向循环神经网络中,
该信息作为输入传递到下一个双向层。
最后,输出层计算得到的输出为
O t ∈ R n × q \mathbf{O}_t \in \mathbb{R}^{n \times q} OtRn×q q q q是输出单元的数目):

O t = H t W h q + b q . \mathbf{O}_t = \mathbf{H}_t \mathbf{W}_{hq} + \mathbf{b}_q. Ot=HtWhq+bq.

这里,权重矩阵 W h q ∈ R 2 h × q \mathbf{W}_{hq} \in \mathbb{R}^{2h \times q} WhqR2h×q
和偏置 b q ∈ R 1 × q \mathbf{b}_q \in \mathbb{R}^{1 \times q} bqR1×q
是输出层的模型参数。
事实上,这两个方向可以拥有不同数量的隐藏单元。

模型的计算代价及其应用

双向循环神经网络的一个关键特性是:使用来自序列两端的信息来估计输出。
也就是说,我们使用来自过去和未来的观测信息来预测当前的观测。
但是在对下一个词元进行预测的情况中,这样的模型并不是我们所需的。
因为在预测下一个词元时,我们终究无法知道下一个词元的下文是什么,
所以将不会得到很好的精度。
具体地说,在训练期间,我们能够利用过去和未来的数据来估计现在空缺的词;
而在测试期间,我们只有过去的数据,因此精度将会很差。
下面的实验将说明这一点。

另一个严重问题是,双向循环神经网络的计算速度非常慢。
其主要原因是网络的前向传播需要在双向层中进行前向和后向递归,
并且网络的反向传播还依赖于前向传播的结果。
因此,梯度求解将有一个非常长的链。

双向层的使用在实践中非常少,并且仅仅应用于部分场合。
例如,填充缺失的单词、词元注释(例如,用于命名实体识别)
以及作为序列处理流水线中的一个步骤对序列进行编码(例如,用于机器翻译)。
在 :numref:sec_bert和 :numref:sec_sentiment_rnn中,
我们将介绍如何使用双向循环神经网络编码文本序列。

(双向循环神经网络的错误应用)

由于双向循环神经网络使用了过去的和未来的数据,
所以我们不能盲目地将这一语言模型应用于任何预测任务。
尽管模型产出的困惑度是合理的,
该模型预测未来词元的能力却可能存在严重缺陷。
我们用下面的示例代码引以为戒,以防在错误的环境中使用它们。

import torch
from torch import nn
from d2l import torch as d2l

# 加载数据
batch_size, num_steps, device = 32, 35, d2l.try_gpu()
train_iter, vocab = d2l.load_data_time_machine(batch_size, num_steps)
# 通过设置“bidirective=True”来定义双向LSTM模型
vocab_size, num_hiddens, num_layers = len(vocab), 256, 2
num_inputs = vocab_size
lstm_layer = nn.LSTM(num_inputs, num_hiddens, num_layers, bidirectional=True)
model = d2l.RNNModel(lstm_layer, len(vocab))
model = model.to(device)
# 训练模型
num_epochs, lr = 500, 1
d2l.train_ch8(model, train_iter, vocab, lr, num_epochs, device)
perplexity 1.1, 109857.9 tokens/sec on cuda:0
time travellerererererererererererererererererererererererererer

travellerererererererererererererererererererererererererer

<Figure size 350x250 with 1 Axes>

在这里插入图片描述
上述结果显然令人瞠目结舌。
关于如何更有效地使用双向循环神经网络的讨论,
请参阅 :numref:sec_sentiment_rnn中的情感分类应用。

小结

  • 在双向循环神经网络中,每个时间步的隐状态由当前时间步的前后数据同时决定。
  • 双向循环神经网络与概率图模型中的“前向-后向”算法具有相似性。
  • 双向循环神经网络主要用于序列编码和给定双向上下文的观测估计。
  • 由于梯度链更长,因此双向循环神经网络的训练代价非常高。

练习

  1. 如果不同方向使用不同数量的隐藏单位, H t \mathbf{H_t} Ht的形状会发生怎样的变化?
  2. 设计一个具有多个隐藏层的双向循环神经网络。
  3. 在自然语言中一词多义很常见。例如,“bank”一词在不同的上下文“i went to the bank to deposit cash”和“i went to the bank to sit down”中有不同的含义。如何设计一个神经网络模型,使其在给定上下文序列和单词的情况下,返回该单词在此上下文中的向量表示?哪种类型的神经网络架构更适合处理一词多义?

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

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

相关文章

计算机毕业设计 基于Python的音乐平台的设计与实现 Python+Django+Vue 前后端分离 附源码 讲解 文档

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

IDE 使用技巧与插件推荐(含例说明)

在使用集成开发环境&#xff08;IDE&#xff09;进行编程时&#xff0c;掌握一些技巧和使用高效的插件可以显著提高开发效率。以下是一些通用的IDE使用技巧和插件推荐&#xff0c;适用于多种流行的IDE&#xff0c;如IntelliJ IDEA、Visual Studio Code、PyCharm等。每个技巧和插…

泳池异常检测系统源码分享

泳池异常检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer Vis…

Pencils Protocol 成市场新宠,生态通证$DAPP价值几何

Pencils Protocol 是 Scroll 生态综合性收益平台&#xff0c;其仅在 Scroll 生态单链 TVL 就已经突破了 3.5 亿美元&#xff0c;同时在上线短短几个月的时间里就积累了超 50 万活跃社区用户。现阶段 Pencils Protocol 已经完成了 DAPP 通证的 TGE &#xff0c;分别在 Tokensoft…

[SAP ABAP] 锁对象

在SAP中使用锁对象&#xff0c;用于避免在数据库中插入或更改数据时出现不一致的情况 1.创建锁对象 数据准备 学校表(ZDBT_SCH_437) 使用事务码SE11创建锁对象 点击"锁对象"单选按钮&#xff0c;输入以E开头的锁定对象的名称&#xff0c;然后点击创建按钮 锁对象名…

关于宝塔PHP getenv无法获取环境变量问题解决办法

今天有用ThinkPHP8接入阿里云OSS时&#xff0c;需要用的用到getenv()来读取环境变量&#xff0c;因为新版OSS SDK是用环境变更来设置AK的。 现象 正常执行PHP文件&#xff0c;可以取到环境变量&#xff1b;但是通过nginxphp-fpm调用脚本取到不到环境变量 原因 php-fpm为了防止…

【软考】高速缓存的组成

目录 1. 说明2. 组成 1. 说明 1.高速缓存用来存放当前最活跃的程序和数据。2.高速缓存位于CPU 与主存之间。3.容量般在几千字节到几兆字节之间。4.速度一般比主存快 5~10 倍&#xff0c;由快速半导体存储器构成。5.其内容是主存局部域的副本&#xff0c;对程序员来说是透明的。…

【C++】C++中如何处理多返回值、C++中的模板

十四、C中如何处理多返回值 本部分也是碎碎念&#xff0c;因为这些点都是很小的点&#xff0c;构不成一篇文章&#xff0c;所以本篇就是想到哪个点就写哪个点。 1、C中如何处理多个返回值 写过python的同学都知道&#xff0c;当你写一个函数的返回时&#xff0c;那是你想返回…

STM32 F1移植FATFS文件系统 USMART组件测试相关函数功能

STM32 F1移植FATFS文件系统 使用USMART调试组件测试相关函数功能 文章目录 STM32 F1移植FATFS文件系统 使用USMART调试组件测试相关函数功能前言部分主要相关代码# USMART介绍1. mf_scan_files 扫描磁盘文件2. mf_mount 挂载磁盘3. mf_open 打开文件4. mf_read 读数据内容5. mf…

软件测试学习路线图

软件测试工程师是专门从事软件、系统或产品测试和评估的技术专业人士&#xff0c;确保它们符合既定标准并无任何缺陷。通过精心设计和执行测试计划&#xff0c;软件测试工程师发现 Bug、故障和需要改进的领域&#xff0c;从而提高最终产品的可靠性和性能。 软件测试工程师在软…

干货|CNAS-CL01设备部分解读,透彻掌握软件测试实验室设备关键点

CNAS-CL01《检测和校准实验室能力认可准则》是软件测试实验室建立符合CNAS标准的质量管理体系必须要贯彻的一部准则&#xff0c;分为五大核心部分&#xff1a;通用要求、结构要求、资源要求、过程要求和管理体系要求。前面的文章中我们为大家分享了通用要求部分、结构要求部分以…

WebAssembly进阶,vue3 使用 WebAssembly,及 WebAssembly vs JavaScript 的性能对比

目录 核心使用步骤 .c文件.cpp文件编译 使用 Emscripten 转译文件 页面中引入.wasm文件中的函数 WebAssembly vs JavaScript 的性能对比 性能对比关键点: 具体场景 实际案例分析 如果对WebAssembly不熟悉可以前往:WebAssembly最详教程,进行WebAssembly基础学习 Web…

一篇文章弄懂数据结构中的各种排序_插入排序_冒泡排序_快速排序_堆排序_归并排序_基数排序

文章目录 一篇文章弄懂数据结构中的各种排序1.排序的概念2. 插入排序2.1 直接插入排序2.2 折半插入排序2.3 希尔排序 3.冒泡排序3.1 算法原理3.2 性能分析 4.快速排序4.1 算法原理4.2 性能分析 5. 选择排序5.1 简单选择排序5.2 堆排序5.1 算法流程5.2 算法效率分析5.3 堆排序的…

2024CSCO 芦康沙妥珠单抗创造晚期TNBC二线治疗新高度

前言 “魔法子弹”的概念从上世纪初提出&#xff0c;经过一百多年的不断探索&#xff0c;抗体药物偶联物&#xff08;ADC&#xff09;从理想照进现实&#xff0c;达到今天百舸争流的盛况&#xff0c;被认为是极具前景的创新疗法&#xff0c;全球范围内已有十余款产品被批准用于…

使用Mendeley生成APA格式参考文献

Mendeley 是一款文献管理工具&#xff0c;可以在Word中方便的插入引用文献。 效果对比&#xff1a; 注&#xff1a;小绿鲸有三种导出格式&#xff0c;分别为复制、导出为Bibtex和导出为Endnote三种。 Mendeley 下载与安装 Download Mendeley Reference Manager For Desktop m…

报道|解读INFORMS期刊影响因子的下降及运筹与管理科学出版的未来

编者按 David Simchi-Levi和Tinglong Dai老师近期在ORMS Today上发表了一篇名为拥抱影响力的变化&#xff1a;解读INFORMS期刊影响因子的下降及运筹与管理科学出版的未来的文章&#xff0c;探讨了近几年INFORMS的大多数期刊影响因子下降的原因以及带给我们的启示。 2023年7月&a…

Qt 首次配置 Qt Creator 14.01 for Python

前言&#xff1a; 如何用QT实现Python的配置的交互界面。本文从0开始&#xff0c;进行实践的介绍。 在上一节里面&#xff0c;我们做了社区版本的配置&#xff1a; https://blog.csdn.net/yellow_hill/article/details/142597007?spm1001.2014.3001.5501 这一节&#xff0…

Linux —— udp实现群聊代码

一、介绍 前面我们一步步模拟实现了一个简单的udp服务器和客户端&#xff0c;通过这个服务器&#xff0c;我们简单实现一个群聊的功能&#xff0c;本篇是专门用来记录代码的&#xff0c;详细的实现思路可以去参考我其他两篇&#xff0c;Socket编程&#xff08;一&#xff09;和…

Android性能优化相关的10个经典面试题

本文首发于公众号“AntDream”&#xff0c;欢迎微信搜索“AntDream”或扫描文章底部二维码关注&#xff0c;和我一起每天进步一点点 以下是一些Android性能优化面试问题&#xff0c;包括问题和参考解答&#xff1a; 1. 如何优化Android应用的启动速度&#xff1f; 答案&#…

零基础教你如何开发webman应用插件

0X07 发布插件应用 插件应用发布地址 https://www.workerman.net/app/create。填写好发布相关信息 0X08 上传源码zip文件 提交完成之后等待官方审核就可以啦&#xff01; 0X09 安装插件 应用插件安装有两种方式 在插件市场安装 进入官方管理后台webman-admin 的应用插件页点击…