深度学习-2.9梯度不稳定和Glorot条件

梯度不稳定和Glorot条件

一、梯度消失和梯度爆炸

  对于神经网络这个复杂系统来说,在模型训练过程中,一个最基础、同时也最常见的问题,就是梯度消失和梯度爆炸。

  我们知道,神经网络在进行反向传播的过程中,各参数层的梯度计算会涉及到激活函数导函数取值,具体来说,假设现在有一个三层的神经网络,其中两个隐藏层的激活函数为 F ( x ) F(x) F(x),对应的导函数为 f ( x ) f(x) f(x),设X为输入训练的数据特征,y为标签, y ^ \hat{y} y^为模型向前传播输出结果,$ w_1 为第一层参数、 为第一层参数、 为第一层参数、w_2 为第二层参数、 为第二层参数、 为第二层参数、w_3$为第三层参数,loss为损失函数,则有如下计算公式:

  每一次正向传播计算结果:
y ^ = F ( F ( X ∗ w 1 ) ∗ w 2 ) ∗ w 3 \hat y = F(F(X * w_1) * w_2) * w_3 y^=F(F(Xw1)w2)w3
  而loss是一个关于y和 y ^ \hat{y} y^的函数,而y是常量, y ^ \hat{y} y^是一个关于w的函数,因此 l o s s loss loss也进行如下表示:
l o s s ( y ^ ) loss(\hat{y}) loss(y^)
  在进行梯度求解时候,假设 w 1 w_1 w1对应梯度为 g r a d 1 grad_1 grad1, w 2 w_2 w2对应梯度为 g r a d 2 grad_2 grad2, w 3 w_3 w3对应梯度为 g r a d 3 grad_3 grad3,为了简化计算,我们假设所有的 x 、 w 1 、 w 2 、 w 3 x、w_1、w_2、w_3 xw1w2w3都是标量,根据链式法则,有计算过程如下:
g r a d 1 = ∂ l o s s ∂ w 1 = ∂ l o s s ∂ y ^ ⋅ ∂ y ^ ∂ w 1 = ∂ l o s s ∂ y ^ ⋅ ∂ ( F ( F ( X ∗ w 1 ) ∗ w 2 ) ∗ w 3 ) ∂ w 1 = ∂ l o s s ∂ y ^ ⋅ ∂ ( F ( F ( X ∗ w 1 ) ∗ w 2 ) ∗ w 3 ) ∂ ( F ( F ( X ∗ w 1 ) ∗ w 2 ) ⋅ ∂ F ( F ( X ∗ w 1 ) ∗ w 2 ) ∂ F ( X ∗ w 1 ) ⋅ ∂ F ( X ∗ w 1 ) ∂ w 1 = ∂ l o s s ∂ y ^ ⋅ w 3 ⋅ f ( F ( X ∗ w 1 ) ∗ w 2 ) ⋅ w 2 ⋅ f ( X ∗ w 1 ) ⋅ X \begin{aligned} grad_1 &=\frac{\partial loss}{\partial w_1} \\ &= \frac{\partial loss}{\partial \hat y} \cdot \frac{\partial \hat y}{\partial w_1} \\ &= \frac{\partial loss}{\partial \hat y} \cdot \frac{\partial (F(F(X * w_1) * w_2) * w_3)}{\partial w_1} \\ &= \frac{\partial loss}{\partial \hat y} \cdot \frac{\partial (F(F(X * w_1) * w_2) * w_3)}{\partial (F(F(X * w_1) * w_2)} \cdot \frac{\partial F(F(X * w_1) * w_2)}{\partial F(X * w_1)} \cdot \frac{\partial F(X * w_1)}{\partial w_1}\\ &= \frac{\partial loss}{\partial \hat y} \cdot w_3 \cdot f(F(X * w_1) * w_2) \cdot w_2 \cdot f(X * w_1) \cdot X \\ \end{aligned} grad1=w1loss=y^lossw1y^=y^lossw1(F(F(Xw1)w2)w3)=y^loss(F(F(Xw1)w2)(F(F(Xw1)w2)w3)F(Xw1)F(F(Xw1)w2)w1F(Xw1)=y^lossw3f(F(Xw1)w2)w2f(Xw1)X
  值得注意的是,此时 g r a d 1 grad_1 grad1中计算了两次激活函数的导函数,并且在上述过程中, X ∗ w 1 X * w_1 Xw1是第一层隐藏层接收到的数据,而 F ( X ∗ w 1 ) ∗ w 2 F(X*w_1)*w_2 F(Xw1)w2则是第二层隐藏层接收到的数据。而对比如果是计算 w 2 w_2 w2的梯度,则有如下过程:
g r a d 2 = ∂ l o s s ∂ w 2 = ∂ l o s s ∂ y ^ ⋅ ∂ y ^ ∂ w 2 = ∂ l o s s ∂ y ^ ⋅ ∂ ( F ( F ( X ∗ w 1 ) ∗ w 2 ) ∗ w 3 ) ∂ w 2 = ∂ l o s s ∂ y ^ ⋅ ∂ ( F ( F ( X ∗ w 1 ) ∗ w 2 ) ∗ w 3 ) ∂ ( F ( F ( X ∗ w 1 ) ∗ w 2 ) ⋅ ∂ F ( F ( X ∗ w 1 ) ∗ w 2 ) ∂ w 2 = ∂ l o s s ∂ y ^ ⋅ w 3 ⋅ ∂ F ( F ( X ∗ w 1 ) ∗ w 2 ) ∂ w 2 = ∂ l o s s ∂ y ^ ⋅ w 3 ⋅ f ( F ( X ∗ w 1 ) ∗ w 2 ) ⋅ ∂ ( F ( X ∗ w 1 ) ∗ w 2 ) ∂ w 2 = ∂ l o s s ∂ y ^ ⋅ w 3 ⋅ f ( F ( X ∗ w 1 ) ∗ w 2 ) ⋅ F ( X ∗ w 1 ) \begin{aligned} grad_2 &=\frac{\partial loss}{\partial w_2} \\ &= \frac{\partial loss}{\partial \hat y} \cdot \frac{\partial \hat y}{\partial w_2} \\ &= \frac{\partial loss}{\partial \hat y} \cdot \frac{\partial (F(F(X * w_1) * w_2) * w_3)}{\partial w_2} \\ &= \frac{\partial loss}{\partial \hat y} \cdot \frac{\partial (F(F(X * w_1) * w_2) * w_3)}{\partial (F(F(X * w_1) * w_2)} \cdot \frac{\partial F(F(X * w_1) * w_2)}{\partial w_2} \\ &= \frac{\partial loss}{\partial \hat y} \cdot w_3 \cdot \frac{\partial F(F(X * w_1) * w_2)}{\partial w_2} \\ &= \frac{\partial loss}{\partial \hat y} \cdot w_3 \cdot f(F(X*w_1)*w_2) \cdot \frac{\partial (F(X * w_1) * w_2)}{\partial w_2} \\ &= \frac{\partial loss}{\partial \hat y} \cdot w_3 \cdot f(F(X*w_1)*w_2) \cdot F(X * w_1) \\ \end{aligned} grad2=w2loss=y^lossw2y^=y^lossw2(F(F(Xw1)w2)w3)=y^loss(F(F(Xw1)w2)(F(F(Xw1)w2)w3)w2F(F(Xw1)w2)=y^lossw3w2F(F(Xw1)w2)=y^lossw3f(F(Xw1)w2)w2(F(Xw1)w2)=y^lossw3f(F(Xw1)w2)F(Xw1)
  我们发现,在计算过程中只出现了一次激活函数的导函数。当然如果我们是计算 w 3 w_3 w3的梯度,则与如下计算过程:
g r a d 3 = ∂ l o s s ∂ w 3 = ∂ l o s s ∂ y ^ ⋅ ∂ y ^ ∂ w 3 = ∂ l o s s ∂ y ^ ⋅ ∂ ( F ( F ( X ∗ w 1 ) ∗ w 2 ) ∗ w 3 ) ∂ w 3 = ∂ l o s s ∂ y ^ ⋅ F ( F ( X ∗ w 1 ) ∗ w 2 ) \begin{aligned} grad_3 &=\frac{\partial loss}{\partial w_3} \\ &= \frac{\partial loss}{\partial \hat y} \cdot \frac{\partial \hat y}{\partial w_3} \\ &= \frac{\partial loss}{\partial \hat y} \cdot \frac{\partial (F(F(X * w_1) * w_2) * w_3)}{\partial w_3} \\ &= \frac{\partial loss}{\partial \hat y} \cdot F(F(X * w_1) * w_2) \\ \end{aligned} grad3=w3loss=y^lossw3y^=y^lossw3(F(F(Xw1)w2)w3)=y^lossF(F(Xw1)w2)

  此时 g r a d 3 grad_3 grad3在计算过程中就已经不涉及激活函数的导函数的计算了。

  其实如果当神经网络层数继续增加、激活函数的数量继续增加,第一层参数在计算梯度的过程中需要相乘的激活函数导函数个数也会随之增加,而后面几层参数的梯度计算中涉及到的激活函数导函数个数逐级递减。

当然,上述过程如果换成矩阵求导,公式主体部分基本不变,只有最后一项会发生变化。由于最终运算结果无法写成较为简洁的矩阵运算形式(矩阵变元的实向量函数),因此此处以标量运算为例。

  而累乘就容易造成指数级变化,当激活函数值 F ( F ( X ∗ w 1 ) ) F(F(X*w_1)) F(F(Xw1))、激活函数导函数值 f ( X ∗ w 1 ) f(X*w_1) f(Xw1)或者参与相乘的参数取值( w 3 w_3 w3)较大(>1)时,会出现 g r a d 1 grad_1 grad1远大于 g r a d 2 grad_2 grad2远大于 g r a d 3 grad_3 grad3的情况,也就是神经网络前几层参数梯度非常大、而后几层参数梯度相对较小的情况,此时就被称为梯度爆炸,并且受到累乘效应的影响,前几层梯度也会大于甚至远大于1,此时就会造成模型迭代过程不稳定的情况发生;而反之如果上述几个变量均小于1,甚至远小于1,则会出现前几层参数梯度非常小、而后几层参数梯度非常大的情况,此时就被称为梯度消失,此时由于模型各层参数学习率伴随层数增加逐渐增加,并且由于构成梯度的基本参数均小于1,因此最后几层梯度也会小于1甚至远小于1,此时前几层参数梯度取值将非常小,甚至趋于0,因而会使得前几层的参数无法在迭代中得到更新。

  总结一下,不同层参数的梯度在计算过程中都有很大的差异,并且这种差异是一种累乘效应,我们也可以简单理解为是一种伴随着层数增加指数级变化的差异。而这种累乘效应会导致线性层参数的一部分梯度过大而另一部分过小,从而影响模型平稳训练。而从具体原因来说,每一层参数的梯度主要和两个因素相关,其一是线性层输入数据,如 X X X F ( X ∗ W ) F(X*W) F(XW),其二则是激活函数导函数计算结果 f ( X ∗ w 1 ) f(X*w_1) f(Xw1)

  接下来,我们就从梯度消失和梯度爆炸的角度剖析Sigmoid和tanh激活函数叠加过程中可能存在的隐患。

二、Sigmoid和tanh激活函数的梯度更新问题

1.Sigmoid激活函数的梯度消失问题

  • 理论说明

  对于sigmoid激活函数来说,简答的叠加是极容易出现梯度消失的问题。sigmoid函数及导函数图像如下所示:

在这里插入图片描述

在这里插入图片描述

  我们发现,Sigmoid导函数最大值为0.25(在0点处取到),当x较大或者较小时,导函数取值趋于0。

  此时如果我们假设还是上述结构的三层神经网络,则第一层参数梯度 g r a d 1 grad_1 grad1由于计算过程出现两次导函数连乘,哪怕两次都导函数都取到最大值(虽然可能性较小), g r a d 1 grad_1 grad1都将在0.0625的基础上进行其余部分相乘,最终结果也极有可能是个非常小的值,因此对于Sigmoid激活函数叠加的情况来说,是极容易出现梯度消失情况的。
g r a d 1 = ∂ l o s s ∂ y ^ ⋅ w 3 ⋅ f ( F ( X ∗ w 1 ) ∗ w 2 ) ⋅ w 2 ⋅ f ( X ∗ w 1 ) ⋅ X grad_1 = \frac{\partial loss}{\partial \hat y} \cdot w_3 \cdot f(F(X*w_1)*w_2) \cdot w_2 \cdot f(X * w_1) \cdot X grad1=y^lossw3f(F(Xw1)w2)w2f(Xw1)X

g r a d 2 = ∂ l o s s ∂ y ^ ⋅ w 3 ⋅ f ( F ( X ∗ w 1 ) ∗ w 2 ) ⋅ F ( X ∗ w 1 ) grad_2 = \frac{\partial loss}{\partial \hat y} \cdot w_3 \cdot f(F(X*w_1)*w_2) \cdot F(X * w_1) grad2=y^lossw3f(F(Xw1)w2)F(Xw1)

g r a d 3 = ∂ l o s s ∂ y ^ ⋅ F ( F ( X ∗ w 1 ) ∗ w 2 ) 梯度消失或者梯度爆炸,始终是个概率问题。我们不能说导函数取值取值小就一定会发生梯度消失问题,只是导函数最大值越小,越有可能发生梯度消失。 grad_3 = \frac{\partial loss}{\partial \hat y} \cdot F(F(X * w_1) * w_2)\> 梯度消失或者梯度爆炸,始终是个概率问题。我们不能说导函数取值取值小就一定会发生梯度消失问题,只是导函数最大值越小,越有可能发生梯度消失。 grad3=y^lossF(F(Xw1)w2)梯度消失或者梯度爆炸,始终是个概率问题。我们不能说导函数取值取值小就一定会发生梯度消失问题,只是导函数最大值越小,越有可能发生梯度消失。

  • Sigmoid函数饱和区间

  一般来说我们会将靠近sigmoid函数的左右两端的区间称为函数的饱和区间(如下图圈出部分)(也就是自变量绝对值较大的区间),不难发现,当自变量落入饱和区间时,因变量会趋于0或者1,而无论自变量是极小(负数绝对值极大)还是极大,都会使得导函数取值趋于0,从而更容易导致模型梯度消失。

在这里插入图片描述

  设计一个函数,构建一个使用了三层sigmoid激活层的函数

在这里插入图片描述

  将多层网络的权重进行输出:

for i, m in enumerate(sigmoid_model3.modules()):if isinstance(m, nn.Linear):vp_x = m.weight.grad.detach().reshape(-1, 1).numpy()       # 每一层参数梯度vp_y = np.full_like(vp_x, i)                   				# 对层进行标记vp_a = np.concatenate((vp_x, vp_y), 1)vp.append(vp_a)

在这里插入图片描述

  类似的,tanh也存在问题:

在这里插入图片描述

随着训练次数的增多,网络之间的权重逐渐消失,模型无法有效学习,最终影响模型效果。

三、Zero-Centered Data与Glorot条件

  通过对Sigmoid和tanh激活函数叠加后的模型梯度变化情况分析,我们不难发现,梯度不平稳是影响模型建模效果的非常核心的因素。而这个看似简单问题的解决方案,却花费了研究人员数十年的时间才逐渐完善,我们现在所接触到的优化方法,也基本上是在15年前后提出的,而这些被验证的切实可行的优化方法,也是推动这一轮深度学习浪潮的技术因素。

当然,这些优化方法主要是针对深层次神经网络的。

  整体来看,针对梯度不平稳的解决方案(优化方法)总共分为五类,分别是参数初始化方法、输入数据的归一化方法、衍生激活函数使用方法、学习率调度方法以及梯度下降优化方法。接下来,先介绍所有上述优化算法的一个基本理论,由Xavier Glorot在2010提出的Glorot条件。

值得注意的是,虽然不同优化算法有不同的出发点和不同的论证方式,但基本都可以从Glorot条件出发进行思考。

1.Zero-centered Data

  在介绍Glorot条件之前,我们先从一个更加朴素的角度出发,讨论关于Zero-Centered Data相关作用,从而帮助我们理解后续Glorot条件。

  首先,我们还是假设当前模型是一个三层神经网络,其中两个隐藏层的激活函数为 F ( x ) F(x) F(x),对应的导函数为 f ( x ) f(x) f(x),设X为输入训练的数据特征,y为标签, y ^ \hat y y^为模型向前传播输出结果, w 1 w_1 w1为第一层参数、 w 2 w_2 w2为第二层参数、 w 3 w_3 w3为第三层参数,loss为损失函数,则有如下计算公式:

  每一次正向传播计算结果:
y ^ = F ( F ( X ∗ w 1 ) ∗ w 2 ) ∗ w 3 \hat y = F(F(X * w_1) * w_2) * w_3 y^=F(F(Xw1)w2)w3
  假设 Z i Z_i Zi为第i层接收到的数据, P i P_i Pi为第i层输出的数据,则有:
Z 1 = X ∗ w 1 Z_1 = X*w_1 Z1=Xw1

P 1 = F ( Z 1 ) = F ( X ∗ w 1 ) P_1 = F(Z_1) = F(X*w_1) P1=F(Z1)=F(Xw1)

Z 2 = P 1 ∗ w 2 = F ( X ∗ w 1 ) ∗ w 2 Z_2 = P_1 * w_2 = F(X*w_1)*w_2 Z2=P1w2=F(Xw1)w2

P 2 = F ( Z 2 ) = F ( F ( X ∗ w 1 ) ∗ w 2 ) P_2 = F(Z_2) = F(F(X*w_1)*w_2) P2=F(Z2)=F(F(Xw1)w2)

Z 3 = y ^ = F ( F ( X ∗ w 1 ) ∗ w 2 ) ∗ w 3 Z_3 = \hat y = F(F(X * w_1) * w_2) * w_3 Z3=y^=F(F(Xw1)w2)w3

  依次类推。而在反向传播过程,各参数层的梯度如下:
g r a d 1 = ∂ l o s s ∂ y ^ ⋅ w 3 ⋅ f ( F ( X ∗ w 1 ) ∗ w 2 ) ⋅ w 2 ⋅ f ( X ∗ w 1 ) ⋅ X grad_1 = \frac{\partial loss}{\partial \hat y} \cdot w_3 \cdot f(F(X*w_1)*w_2) \cdot w_2 \cdot f(X * w_1) \cdot X grad1=y^lossw3f(F(Xw1)w2)w2f(Xw1)X

g r a d 2 = ∂ l o s s ∂ y ^ ⋅ w 3 ⋅ f ( F ( X ∗ w 1 ) ∗ w 2 ) ⋅ F ( X ∗ w 1 ) grad_2 = \frac{\partial loss}{\partial \hat y} \cdot w_3 \cdot f(F(X*w_1)*w_2) \cdot F(X * w_1) grad2=y^lossw3f(F(Xw1)w2)F(Xw1)

g r a d 3 = ∂ l o s s ∂ y ^ ⋅ F ( F ( X ∗ w 1 ) ∗ w 2 ) grad_3 = \frac{\partial loss}{\partial \hat y} \cdot F(F(X * w_1) * w_2) grad3=y^lossF(F(Xw1)w2)

  在梯度消失和梯度爆炸的案例中,我们不难发现,为了确保多层神经网络的有效性,各层梯度的差距不应太大,此时一个最为基本的想法就是,就是能否让所有的输入数据(也就是X)以及所有层的参数都设置为Zero-Centered Data,也就是零点对称数据,不难发现,由于X和 w i w_i wi都是零点对称的,因此每一个线性层中的导函数也取值也能够维持在0-1之间,进而每一层的梯度基本能维持在比较平稳的状态。

另外,除了能够避免梯度不平稳问题以外,创建Zero-Centered的参数和数据集,还能够更好的在正向传播中将信息传播到各层,以及确保各层学习的平稳性。

  关于如何将带入模型训练的数据转化为Zero-Centered Data,一般来说我们会使用一系列标准化方法对其进行转化,具体方法我们会在Lesson 14进行详细介绍,由于我们此前创建的数据生成器生成的就是Zero-Centered Data,因此暂时这些数据不会影响接下来的优化方法使用。而如何将参数转化为Zero-Centered Data,就是核心需要考虑的问题了。

对于输入的数据来说,我们可以尽量保证其Zero-Centered的特性,但模型参数是随着模型迭代不断变化的,我们无法把控模型每一轮迭代后的情况,因此只能从模型参数初始值入手,尽量保证其Zero-Centered属性。

  很明显,我们不能将参数的初始值全部设为0,我们只能考虑借助统计工具生成均值是0的随机数,也就是0均值的均匀分布或者是0均值的高斯分布,但这里需要考虑的另一个问题就是,该随机数的方差应该如何确定?

2.Glorot条件和Xavier方法

  初始化参数的方差如何确定这一问题在一个严谨论述如何保证模型有效性的论文中,从另一个角度出发,得到了回答。根据Xavier Glorot在2010年发表的《Understanding the difficulty of training deep feedforward neural networks》论文中的观点,为保证模型本身的有效性和稳定性,我们希望正向传播时,每个线性层输入数据的方差等于输出数据的方差,同时我们也希望反向传播时,数据流经某层之前和流经某层之后该层的梯度也具有相同的方差,虽然二者很难同时满足(除非相邻两层神经元个数相同),但Glorot和Bengio(论文第二作者)表示,如果我们适当修改计算过程、是可以找到一种折中方案去设计初始参数取值,从而同时保证二者条件尽可能得到满足,这种设计参数初始值的方法也被称为Xavier方法,而这种方法也经过一段时间的实践验证被证明是很好的一种初始化模型参数的方法,尤其是对于使用tanh激活函数的神经网络来说,效果更为显著。

  而这种正向传播时数据方差保持一致、反向传播时参数梯度方差保持一致的条件,也被称为Glorot条件,满足该条件的模型能够进行有效平稳的训练,而为了满足该条件而创建的(当然也是由上述论文提出的)模型初始化参数值设计方法,也被称为Xavier方法。而在Xavier方法中,最核心解决的问题,也就是为了创建Zero-Centered的初始化参数时参数的方差。和我们从朴素的角度思考的方向是一致的。

  由于Glorot条件和Xavier方法是在2010年提出的,彼时ReLU激活函数还未兴起,因此Xavier方法主要是围绕tanh激活函数可能存在的梯度爆炸或梯度消失进行的优化,Sigmoid激活函数效果次之。不过尽管如此,Glorot条件却是一个通用条件,后续围绕ReLU激活函数、用于解决神经元活性失效的优化方法(如HE初始化方法),也是遵照Glorot条件进行的方法设计。

3.模型初始化参数取值影响

  Xavier初始化方法的推导和使用我们将在下一节详细介绍,此处我们先通过另外一个实例,去展示为何初始参数取值不同,会够得到不同的建模结果。模型初始化时得到的不同参数,本质上等价于在损失函数上找到了不同的初始点,而同一损失函数,初始点选取的不同应该不会影响最终迭代结果才对,但事实情况并非如此。

在这里插入图片描述

  我们发现,初始参数值的选取不仅会影响模型收敛速度,甚至在某些情况下还会影响模型的最终表现。造成此现象的根本原因还是在于神经网络模型在进行训练时,不确定性过多,而在一个拥有诸多不确定性的系统中再加上不确定的初始参数,初始参数的不确定性会被这个系统放大。并且,值得一提的是,每一个epoch中的每一次迭代并不是在一个损失函数上一步步下降的,当我们使用小批量梯度下降算法时,带入不同批的数据,实际创建的损失函数也会不同。

参考

菜菜子的深度学习

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

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

相关文章

ADW300多功能无线计量仪表

仪表应用背景 电力运维行业:运维服务系统实时采集大量用户站的运行和动环数据,经专业数据分析,当用户站发生异常情况或运行故障时,及时反馈到运维指挥中心,并通过移动终端通知相应的运维工程师,指导现场作…

浅谈Javascript虚拟列表(virtaul list)改造成虚拟表格(virtaul table)的技术

前端加载百万条数据列表,如果采用真实的DOM插入100万个div(或li)标签,肯定是非常卡顿的。这就不得不使用虚拟列表技术方案,但是虚拟列表技术方案网上有很详细的实现方法,今天我就来谈谈根据网上的方案&…

ChatGPT:提升论文写作能力

ChatGPT无限次数:点击直达 ChatGPT:开启智能对话,提升论文写作能力 ChatGPT是一款强大的人工智能对话引擎,它不仅可以帮助用户进行智能对话,还能在论文写作中发挥重要作用。本文将探讨如何利用ChatGPT提升论文写作能力&#xff0c…

Educational Codeforces Round 163 (Rated for Div. 2)(A,B,C,D,E)

比赛链接 好忙好忙好忙,慢慢补老比赛的题解了。 这场没啥算法,全是思维。有也是BFS,屎。 A. Special Characters 题意: 您将得到一个整数 n n n 。 您的任务是构建一串大写的拉丁字母。此字符串中必须正好有 n n n 个特殊字…

Docker - 哲学 默认网络和 自定义网络 与 linux 网络类型 和 overlay2

默认网络:不指定 --nerwork 不指定 网络 run 一个容器时,会直接使用默认的网络桥接器 (docker0) 自定义网络:指定 --nerwork 让这两台容器互相通信 的前提 - 共享同一个网络 关于 ip addr 显示 ens160 储存驱动 ov…

入门linux之Ubuntu学习

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言1、介绍Ubuntu2、虚拟机目录解析3、常用指令ls:罗列当前目录文件信息对ls -l 的结果解析1.第一个字符2.每三个字符(第一个字符后&#x…

jmeter超高并发报错解决方法

1、比如jmeter设置并发量为5000,运行后报错socket closed。原因是客户端与服务端做了三次握手之后,后面不需要握手了,但是jmeter没有这个功能,5000个并发每次发接口请求都是独立的,jmeter端口处理不了这么大量的请求&a…

【嵌入式DIY实例】-DIY 灌水机

DIY 灌水机 文章目录 DIY 灌水机1、硬件准备2、YF-S201 霍尔效应水流量传感器介绍3、4x4矩阵键盘4、硬件接线5、代码实现在这个项目中,我们将使用流量传感器和 Arduino 构建一个水灌装机。用户使用 44 键盘输入液体总量。泵在收到用户输入后抽取准确数量的水。当准确数量完成后…

v-for=“item in arr“ 的理解

在 Vue.js 中,v-for 是一个指令,用于在模板中渲染一个列表的数据。v-for"item in arr" 这个语法中,item 和 arr 分别代表以下含义: item: item 是当前迭代到的数组元素或对象的别名。在每次迭代中,item 会被…

Android视角看鸿蒙第八课(module.json5中的各字段含义之abilities)下

Android视角看鸿蒙第八课(module.json5中的各字段含义之abilities)下 导读 上篇文章开始学习abilities下的各字段含义,因为篇幅原因只学习了name、srcEntry、description、icon和label字段的含义和用法, 这篇文章继续学习和了解其他字段。 …

GPT4.0

GPT4.0 支持官网所有功能以及所有第三方GPTS,完全同步官网。无需魔法,填写授权码直达官网。全天超18小时维护,无需担心不稳定。没有永久卡,3.5免费提供,4.0可以按需下单即可,不存在跑路。 需要的联系

格瑞纳电子邀您参观2024杭州快递物流展

2024长三角快递物流供应链与技术装备展览会 2024.7.8-10 杭州国际博览中心 参展企业介绍 北京格瑞纳电子产品有限公司是一家立足于专业科学技术领域集产品代理、培训咨询和个性化增值服务的高科技公司,于2009年成立于北京,立足于复杂系统仿真领域&…

皓学IT:WEB06_ EL表达式JSTL标签库

一、EL表达式 1.1.特点 是一个由java开发的工具包 用于从特定域对象中读取并写入到响应体开发任务,不能向域对象中写入。 EL工具包自动存在Tomcat的lib中(el-api.jar),开发是可以直接使用,无需其他额外的包。 标准…

seleniumUI自动化实例(登录CSDN页面)

今天分享一个CSDN登录模块的登录场景 1.配置文件 CSDNconf.py: from selenium import webdriver options webdriver.ChromeOptions() options.binary_location r"D:\Program Files\360\360se6\Application\360se.exe" # 360浏览器安装地址 driver w…

电脑审计系统知多少

域智盾的电脑审计系统是一款功能强大的软件,主要用于监控和审计电脑的使用情况。通过安装该系统,组织能够全面了解员工的电脑活动。 首先,该系统能够详细记录用户的上网记录,包括访问的网站、浏览的网页内容等。这使得管理员可以监…

剑指offer面试题42 翻转字符顺序 VS 左旋字符串

考察点 字符串遍历知识点 题目 分析 类似这种题目只能观察元素特点找出规律确定算法了,像第一道题目翻转单词顺序,我们可以很容易的得到翻转每个字符后的的字符串,再仔细观察一下紧接着以空格为间隔分别翻转俩边的字符串就可以解这道题目了…

鸿蒙Harmony应用开发—ArkTS-@Provide装饰器和@Consume装饰器:与后代组件双向同步

Provide和Consume,应用于与后代组件的双向数据同步,应用于状态数据在多个层级之间传递的场景。不同于上文提到的父子组件之间通过命名参数机制传递,Provide和Consume摆脱参数传递机制的束缚,实现跨层级传递。 其中Provide装饰的变…

YOLOv8 如何实现多主干特征融合方式 | GhostNet+ShuffleNet / SwinTransformer+ShuffleNet

文章目录 前言模块添加方法双特征提取例子`GhostNet+ShuffleNet` 双主干结构图代码`Swin+ShuffleNet` 双主干结构图代码参数量与计算量1. 什么是YOLO-Magic框架?2. 如何加入这个框架?3. 加入后如何使用框架?4. GitHub组织是什么?

LeetCode-热题100:39.组合总和

题目描述 给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。 candidates 中的 同一个 数字可以 无限制重复被…

使用 Python 读取 Word 文件

使用 Python 读取 Word 文件 0. 引言安装必要的库 1. 读取和提取 Word 文件中的文本2. 提取 Word 文件中的图片 0. 引言 要使用 Python 读取 Word 文件并识别其中的对象(如图片)和文本,你可以使用 python-docx 库来处理文本,和 d…