[论文笔记]GAUSSIAN ERROR LINEAR UNITS (GELUS)

news/2024/5/17 14:47:43

引言

今天来看一下GELU的原始论文。

作者提出了GELU(Gaussian Error Linear Unit,高斯误差线性单元)非线性激活函数: GELU = x Φ ( x ) \text{GELU}= x\Phi(x) GELU=xΦ(x),其中 Φ ( x ) \Phi(x) Φ(x)​是标准高斯累积分布函数。与ReLU激活函数通过输入的符号进行门控不同,GELU非线性激活函数通过输入的数值进行加权。

Tags

#ActivateFunction#

总体介绍

随着网络变得更深,使用sigmoid激活函数进行训练的效果不如非平滑、非概率性的ReLU,后者基于输入的符号进行硬门控决策。ReLU通常比sigmoid激活函数能够实现更快更好的收敛性。在ReLU的成功基础上,最近的一种修改称为ELU允许ReLU-like的非线性激活函数输出负值,这有时可以提高训练速度。总之,激活函数选择对于神经网络仍然是一个必要的架构决策,否则网络将成为一个深度线性分类器。

深度非线性分类器能够很好地拟合数据,以至于网络设计者通常需要选择是否包含随机正则化器,例如在隐藏层添加噪声或应用dropout,而这个选择与激活函数是相互独立的。因此,非线性和dropout共同决定了神经元的输出,但这两个创新仍然是不同的。此外,由于流行的随机正则化器不考虑输入,而非线性受到这些正则化器的帮助,因此两者都不会取代对方。

在这篇工作中,作者引入了一种新称为GELU的激活函数。它与随机正则化器有关,因为它是自适应dropout修改的期望。这表明了对神经元输出的更概率性的观点。

补充知识

正态分布与误差函数

本节介绍一些相关知识,以便更好地理解这篇论文。正态分布的概率密度函数为:
f ( x ) = 1 2 π σ exp ⁡ ( − ( x − μ ) 2 2 σ 2 ) (p1) f(x) = \frac{1}{\sqrt{2\pi} \sigma} \exp\left( -\frac{(x-\mu)^2}{2\sigma^2} \right) \tag{p1} f(x)=2π σ1exp(2σ2(xμ)2)(p1)
假设 X X X是随机变量,累积分布函数是概率 P ( X ≤ x ) P(X \leq x) P(Xx),用概率密度函数可以表示为:
F ( x ; μ , σ ) = 1 2 π σ ∫ − ∞ x exp ⁡ ( − ( t − μ ) 2 2 σ 2 ) d t (p2) F(x;\mu,\sigma) = \frac{1}{\sqrt{2\pi} \sigma} \int_{-\infty}^x \exp\left( -\frac{(t-\mu)^2}{2\sigma^2} \right)dt \tag{p2} F(x;μ,σ)=2π σ1xexp(2σ2(tμ)2)dt(p2)
正态分布可记为 N ( μ , σ 2 ) N(\mu,\sigma^2) N(μ,σ2),其中 μ \mu μ σ 2 \sigma^2 σ2分别代表均值和方差。当 μ = 0 , σ = 1 \mu=0,\sigma=1 μ=0,σ=1时称为标准正态分布。

下图是一些不同取值的概率密度函数图像(左)和累积分布函数图像(右):

image-20240427161126069

误差函数(Error function, erf \text{erf} erf)的定义如下:
erf ( x ) = 1 π ∫ − x x e − t 2 d t = 2 π ∫ 0 x e − t 2 d t (p3) \text{erf}(x) = \frac{1}{\sqrt \pi} \int_{-x}^x e^{-t^2} dt = \frac{2}{\sqrt \pi} \int_0^x e^{-t^2} dt \tag{p3} erf(x)=π 1xxet2dt=π 20xet2dt(p3)
互补误差函数( erfc \text{erfc} erfc),在误差函数的基础上定义:
erfc = 1 − erf ( x ) = 2 π ∫ x ∞ e − t 2 d t (p4) \text{erfc} = 1 - \text{erf}(x) = \frac{2}{\sqrt \pi} \int_x^\infty e^{-t^2} dt \tag{p4} erfc=1erf(x)=π 2xet2dt(p4)
为什么要介绍误差函数,因为它和正态分布式有关系的,重点是作者也引入了它。

误差函数本质上与标准正态累积分布函数 Φ \Phi Φ(将 μ = 0 , σ = 1 \mu=0,\sigma=1 μ=0,σ=1代入式p2)是等价的:
Φ ( x ) = 1 2 π ∫ − ∞ x exp ⁡ ( − t 2 2 ) d t = 1 2 [ 1 + erf ( x 2 ) ] = 1 2 erfc ( − x 2 ) (p5) \Phi(x) =\frac{1}{\sqrt{2\pi}} \int_{-\infty}^x \exp\left( -\frac{t^2}{2} \right)dt = \frac{1}{2}\left[1+ \text{erf}\left( \frac{x}{\sqrt 2}\right) \right ] = \frac{1}{2} \text{erfc} \left(-\frac{x}{\sqrt 2} \right)\tag{p5} Φ(x)=2π 1xexp(2t2)dt=21[1+erf(2 x)]=21erfc(2 x)(p5)
可整理为如下形式:
erf ( x ) = 2 Φ ( x 2 ) − 1 erfc ( x ) = 2 Φ ( − x 2 ) = 2 ( 1 − Φ ( x 2 ) ) \begin{aligned} \text{erf}(x) &= 2\Phi(x\sqrt 2) -1 \\ \text{erfc}(x) &= 2\Phi(-x\sqrt 2) = 2(1-\Phi(x\sqrt 2)) \end{aligned} erf(x)erfc(x)=(x2 )1=(x2 )=2(1Φ(x2 ))

关于泰勒展开可以参考——人工智能数学基础之高等数学。

误差函数泰勒展开收敛:
erf ( z ) = 2 π ∑ n = 0 ∞ ( − 1 ) n z 2 n + 1 n ! ( 2 n + 1 ) = 2 π ( z − z 3 3 + z 5 10 − z 7 42 + z 9 216 − ⋯ ) (p6) \text{erf}(z) = \frac{2}{\sqrt \pi} \sum_{n=0}^\infty \frac{(-1)^n z^{2n+1}}{n!(2n+1)} =\frac{2}{\sqrt \pi} \left(z - \frac{z^3}{3} + \frac{z^5}{10} - \frac{z^7}{42} + \frac{z^9}{216} - \cdots \right) \tag{p6} erf(z)=π 2n=0n!(2n+1)(1)nz2n+1=π 2(z3z3+10z542z7+216z9)(p6)
误差函数和 tanh ⁡ \tanh tanh的关系:
erf ( x ) ≈ tanh ⁡ ( 2 π ( x + 11 123 x 3 ) ) (p7) \text{erf}(x) \approx \tanh\left(\frac{2}{\sqrt \pi} \left(x +\frac{11}{123}x^3 \right) \right) \tag{p7} erf(x)tanh(π 2(x+12311x3))(p7)

import numpy as np
import matplotlib.pyplot as plt
import scipy.specialx = np.linspace(-3, 3, 100)y1 = scipy.special.erf(x)y2 = np.tanh(2/np.sqrt(np.pi)*(x + 11/123 * x**3))plt.figure()
plt.plot(x, y1, label='erf(x)')
plt.plot(x, y2, label='tanh(x)')
plt.xlabel('x')
plt.ylabel('y')
plt.title('Plot of erf(x) and tanh(x)')
plt.legend()
plt.grid(True)
plt.show()

image-20240428075759147

当通常如上代码画图,这两个函数的图几乎重叠。

伯努利分布

伯努利分布(Bernoulli distribution),又称为两点分布或0-1分布,是一个离散型概率分布。若伯努利试验成功,则伯努利随机变量取值为1,否则伯努利试验失败,伯努利随机变量取值为0。即其成功概率为 p ( 0 ≤ p ≤ 1 ) p( 0 \leq p \leq 1) p(0p1),失败概率为 q = 1 − p q= 1-p q=1p

其概率质量函数(probability mass function, pmf)为:
f X ( x ) = p x ( 1 − p ) 1 − x = { p if x = 1 q if x = 0 f_X(x) = p^x (1-p)^{1-x} = \begin{cases} p & \text{if x = 1}\\ q & \text{if x = 0} \end{cases} fX(x)=px(1p)1x={pqif x = 1if x = 0
概率质量函数是离散型随机变量在各特定取值上的概率,和概率密度函数的一个不同之处在于:概率质量函数是对离散随机变量定义的,本身代表该值的概率;概率密度函数本身不是概率,只有对连续随机变量的概率密度函数必须在某一个区间内被积分后才能产生出概率。

不同成功概率下的概率质量函数图像:

image-20240427170444091

其期望为:
E [ X ] = ∑ i = 0 1 x i f X ( x ) = 0 + p = p (p6) E[X] = \sum_{i=0}^1 x_i f_X(x) = 0 + p = p \tag {p6} E[X]=i=01xifX(x)=0+p=p(p6)

GELU公式

作者通过结合dropout、zoneout和ReLU的特性来激发出这个激活函数。

首先要注意的是,ReLU和dropout都可以确定地将神经元的输出乘零或一,而dropout则是随机地将其乘零。此外,一种新的RNN正则化器称为zoneout可以通过乘一来随机地乘输入。

作者通过将输入乘零或一来合并这种功能,但是这个零一掩码的值是随机确定的,同时也依赖于输入。具体来说,将神经元的输入 x x x m ∼ Bernoulli ( Φ ( x ) ) m ∼ \text{Bernoulli}(\Phi(x)) mBernoulli(Φ(x)),其中 Φ ( x ) = P ( X ≤ x ) \Phi(x) = P(X ≤ x) Φ(x)=P(Xx) X ∼ N ( 0 , 1 ) X ∼ N(0, 1) XN(0,1)​​​是标准正态分布的累积分布函数。

选择这个分布是因为神经元的输入往往遵循正态分布,尤其是在批归一化的情况下。在这种情况下,随着 x x x的减小,输入“被丢弃”的概率更高,因此应用于 x x x​​的转换是随机的,但取决于输入。

以这种方式屏蔽输入保留了非确定性,但仍然依赖于输入值。随机选择的掩码相当于对输入进行随机的恒等映射(不变)和置零映射。这非常类似于自适应dropout,但自适应dropout与非线性一起使用,并使用逻辑斯蒂分布而不是标准正态分布。作者发现,只使用这种随机正则化器就可以训练出具有竞争力的网络,而不需要使用任何非线性。

我们通常希望神经网络做出确定性的决策,所以激活函数的输出应该是确定的,不能是随机的。为了移除这个随机性,我们可以计算期望:
E [ m x ] = x E [ m ] = x Φ ( x ) E[mx] = x E[m] = x\Phi(x) E[mx]=xE[m]=xΦ(x)
因为 m m m是伯努利分布,根据(p6)我们知道该伯努利分布的期望为 Φ ( x ) \Phi(x) Φ(x)

由于高斯分布的累积分布函数通常使用误差函数计算,因此作者将GELU定义为:
GELU ( x ) = x P ( X ≤ x ) = x Φ ( x ) = x ⋅ 1 2 [ 1 + erf ( x / 2 ) ] \text{GELU} (x) = x P(X \leq x) = x\Phi(x) = x \cdot \frac{1}{2} \left[1 + \text{erf}(x / \sqrt 2) \right] GELU(x)=xP(Xx)=xΦ(x)=x21[1+erf(x/2 )]
这里可以参考(p5)。

image-20240427172743948

其对应的函数图像可以参考上图的蓝线。从其图像可以看出,在负值区域,不像 ReLU \text{ReLU} ReLU全为0,并且是光滑地、处处可导的。

在HuggingFace的transformers库中的实现为如下:

class GELUActivation(nn.Module):"""Original Implementation of the GELU activation function in Google BERT repo when initially created. Forinformation: OpenAI GPT's GELU is slightly different (and gives slightly different results): 0.5 * x * (1 +torch.tanh(math.sqrt(2 / math.pi) * (x + 0.044715 * torch.pow(x, 3)))) This is now written in C in nn.functionalAlso see the Gaussian Error Linear Units paper: https://arxiv.org/abs/1606.08415"""def __init__(self, use_gelu_python: bool = False):super().__init__()if use_gelu_python:self.act = self._gelu_pythonelse:self.act = nn.functional.geludef _gelu_python(self, input: Tensor) -> Tensor:return input * 0.5 * (1.0 + torch.erf(input / math.sqrt(2.0)))def forward(self, input: Tensor) -> Tensor:return self.act(input)

可以看到这里提供了两种实现,一种是直接通过上面的公式实现——_gelu_python();从注释中可以看出,还有一种是基于0.5 * x * (1 +torch.tanh(math.sqrt(2 / math.pi) * (x + 0.044715 * torch.pow(x, 3))))的近似实现,通过C来实现,默认是采用这种方式。

后者是为了加速计算,第一种实现时精确求解。作者有指出可以近似GELU为
0.5 x ( 1 + tanh ⁡ [ 2 / π ( x + 0.044715 x 3 ) ] ) 0.5 x(1+ \tanh[\sqrt{2/\pi} (x + 0.044715x^3)]) 0.5x(1+tanh[2/π (x+0.044715x3)])


x σ ( 1.702 x ) x\sigma(1.702 x) xσ(1.702x)
甚至如果更快的前馈网络速度的价值超过了准确性的代价,还可以使用不同的累积分布函数。例如,我们可以使用逻辑斯蒂分布的累积分布函数 σ ( x ) σ(x) σ(x)来得到所谓的Sigmoid线性单元(Sigmoid Linear Unit, SiLU):
SiLU = x σ ( x ) \text{SiLU} = x\sigma(x) SiLU=xσ(x)
也可以使用 N ( μ , σ 2 ) N(\mu, σ^2) N(μ,σ2)的累积分布函数,并让µ和σ成为可学习的超参数,但在这项工作中,只简单地让 μ = 0 \mu= 0 μ=0 σ = 1 σ = 1 σ=1​。

近似推导

从补充知识中我们看到了 erf ( x ) ≈ tanh ⁡ ( 2 π ( x + 11 123 x 3 ) ) \text{erf}(x) \approx \tanh\left(\frac{2}{\sqrt \pi} \left(x +\frac{11}{123}x^3 \right) \right) erf(x)tanh(π 2(x+12311x3))​。下面我们利用泰勒展开证明一下。

tanh ⁡ x \tanh x tanhx的泰勒展开为:
tanh ⁡ x = ∑ n = 1 ∞ 2 2 n ( 2 2 n − 1 ) B 2 n x 2 n − 1 ( 2 n ) ! = x − x 3 3 + 2 x 5 15 − 17 x 7 315 + ⋯ \tanh x = \sum^{\infty}_{n=1} \frac{2^{2n}(2^{2n}-1)B_{2n}x^{2n-1}}{(2n)!}= x -\frac{x^3}{3} + \frac{2x^5}{15} - \frac{17x^7}{315} + \cdots tanhx=n=1(2n)!22n(22n1)B2nx2n1=x3x3+152x531517x7+
假设我们不知道式(p7)中的具体数值。我们考虑近似形式 tanh ⁡ ( a x + b x 3 ) \tanh (ax+bx^3) tanh(ax+bx3)以及 GELU \text{GELU} GELU中的形式,在 x = 0 x=0 x=0​处展开

考虑 tanh ⁡ ( a x + b x 3 ) \tanh (ax+bx^3) tanh(ax+bx3)的泰勒展开:
a x + b x 3 − ( a 3 3 x 3 + b 3 x 6 3 ) + ⋯ ax + bx^3 - (\frac{a^3}{3}x^3 + \frac{b^3x^6}{3}) + \cdots ax+bx3(3a3x3+3b3x6)+
我们仅考虑前两项,即令同幂次前的系数相等:
erf ( x 2 ) − tanh ⁡ ( a x + b x 3 ) = ( 2 π − a ) x + ( a 3 3 − b − 1 3 2 π ) x 3 \text{erf}(\frac{x}{\sqrt 2}) - \tanh(ax+bx^3) = \left( \sqrt \frac{ 2}{ \pi} -a \right)x +\left(\frac{a^3}{3} -b -\frac{1}{3\sqrt {2\pi}} \right)x^3 erf(2 x)tanh(ax+bx3)=(π2 a)x+(3a3b32π 1)x3
得到两个方程,求解得到

a = 2 π , b = 4 − π 3 2 π 3 / 2 a = \sqrt \frac{2}{\pi},\quad b=\frac{4-\pi}{3\sqrt 2\pi^{3/2}} a=π2 ,b=32 π3/24π

代入GELU中的 x Φ ( x ) x\Phi(x) xΦ(x),得到

x Φ ( x ) ≈ 1 2 x [ 1 + tanh ⁡ ( 2 π ( x + 0.0455399 x 3 ) ) ] \begin{equation}x\Phi(x)\approx \frac{1}{2} x\left[1 + \tanh\left(\sqrt{\frac{2}{\pi}}\left(x + 0.0455399 x^3\right)\right)\right]\end{equation} xΦ(x)21x[1+tanh(π2 (x+0.0455399x3))]

这个值很接近 0.044715 0.044715 0.044715,因为我们仅做了前两项的近似,所以得到的这个值与真实值存在误差,注意的是作者提出的也是一种近似,也存在误差。这种做法仅考虑局部误差,即在 x = 0 x=0 x=0的局部会比较精确。

因此我们还需要考虑全局误差。

我们固定 a = 2 π a=\sqrt{\frac{2}{\pi}} a=π2 ,这个 a a a是一阶局部近似,我们希望保留在这个近似。同时希望 b b b尽可能帮我们减少全局误差。所以我们可以求解:
min ⁡ b max ⁡ x ∣ erf ( x 2 ) − tanh ⁡ ( a x + b x 3 ) ∣ \begin{equation}\min_{b} \max_x \left|\text{erf}\left(\frac{x}{\sqrt{2}}\right)-\tanh\left(a x + b x^3\right)\right|\end{equation} bminxmax erf(2 x)tanh(ax+bx3)
通过参考2中给出的代码来求解:

import numpy as np
from scipy.special import erf
from scipy.optimize import minimize
# 增加设置显示精度
np.set_printoptions(precision=20)def f(x, b):a = np.sqrt(2 / np.pi)return np.abs(erf(x / np.sqrt(2)) - np.tanh(a * x + b * x**3))def g(b):return np.max([f(x, b) for x in np.arange(0, 4, 0.001)])options = {'xtol': 1e-10, 'ftol': 1e-10, 'maxiter': 100000}
result = minimize(g, 0, method='Powell', options=options)
print(result.x)
[0.035677337314877496]

代入 x Φ ( x ) x\Phi(x) xΦ(x),得到:

x Φ ( x ) ≈ 1 2 x [ 1 + tanh ⁡ ( 2 π ( x + 0.04471491123850979 x 3 ) ) ] \begin{equation}x\Phi(x)\approx \frac{1}{2} x\left[1 + \tanh\left(\sqrt{\frac{2}{\pi}}\left(x + 0.04471491123850979 x^3\right)\right)\right]\end{equation} xΦ(x)21x[1+tanh(π2 (x+0.04471491123850979x3))]

怎么代入呢,因为上式提出了一个 2 π \sqrt{\frac{2}{\pi}} π2 ,所以让 0.035677337314877496 × π 2 0.035677337314877496 \times \sqrt \frac{\pi}{2} 0.035677337314877496×2π 即可:

import mathmath.sqrt(math.pi /2) * 0.035677337314877496
0.04471491123850979

此时得到的结果比论文中的 0.044715 0.044715 0.044715更加精确。

本篇工作中还提出了一种近似,即 x σ ( 1.702 x ) x\sigma(1.702 x) xσ(1.702x),它是直接用 σ ( b x ) \sigma(bx) σ(bx)全局逼近 Φ ( x ) \Phi(x) Φ(x)的结果,即
min ⁡ λ max ⁡ x ∣ Φ ( x ) − σ ( b x ) ∣ \begin{equation}\min_{\lambda}\max_{x}\left|\Phi(x) - \sigma(b x)\right|\end{equation} λminxmaxΦ(x)σ(bx)
修改下上面的代码:

import numpy as np
from scipy.special import erf
from scipy.optimize import minimize
from scipy.special import expitnp.set_printoptions(precision=20)def f(x, b):return np.abs(0.5*(1+erf(x / np.sqrt(2))) - 1/(1 + np.exp(-b*x)))def g(b):return np.max([f(x, b) for x in np.arange(0, 4, 0.001)])options = {'xtol': 1e-10, 'ftol': 1e-10, 'maxiter': 100000}
result = minimize(g, 0, method='Powell', options=options)
print(result.x)
[1.7017449256323682]


Φ ( x ) ≈ σ ( 1.7017449256323682 x ) \begin{equation}\Phi(x)\approx \sigma(1.7017449256323682 x)\end{equation} Φ(x)σ(1.7017449256323682x)

实验

讨论

通过实验证明,GELU表现优于先前的非线性激活函数,但在其他方面与ReLU和ELU相似。例如,当 σ → 0 σ → 0 σ0 μ = 0 \mu = 0 μ=0时,GELU变成了ReLU。此外,ReLU和GELU在渐进上是相等的。实际上,可以将GELU视为平滑ReLU的一种方式。

GELU和之前的激活函数比有几个显著的区别。这个非凸非单调函数在正域不是线性的,并且在所有点上都表现出曲率。而ReLU和ELU是凸函数和单调激活函数,在正域中是线性的,因此可能缺乏曲率。因此,增加曲率和非单调性可能使GELU比ReLU或ELU更容易逼近复杂的函数。此外,由于 ReLU ( x ) = x 1 ( x > 0 ) \text{ReLU}(x) = x\Bbb 1(x > 0) ReLU(x)=x1(x>0) GELU ( x ) = x Φ ( x ) \text{GELU}(x) = x\Phi(x) GELU(x)=xΦ(x),如果 μ = 0 , σ = 1 \mu = 0,σ = 1 μ=0σ=1​,我们可以看到ReLU根据输入的符号对其进行门控,而GELU根据其相对于其他输入的大小对其进行加权。此外,重要的是指出,GELU具有概率解释,因为它是随机正则化器的期望。

结论

本篇工作评估了大量的数据集,证明GELU超越了ELU和ReLU的准确率。

总结

⭐ 作者提出了一种新的激活函数——GELU,根据输入相对于其他输入的大小对其进行加权。即随着输入 x x x的减小,输入“被丢弃”的概率更高,因此应用于 x x x的置零或置一变换是随机的,但取决于输入。

参考

  1. 维基百科
  2. 苏剑林. (Mar. 26, 2020). 《GELU的两个初等函数近似是怎么来的 》[Blog post]. Retrieved from https://kexue.fm/archives/7309

http://www.mrgr.cn/p/53242021

相关文章

前端 Vue webpack配置之 webpack.config.js 文件配置

Webpack 在执行的时候,除了在命令行传入参数,还可以通过指定的配置文件来执行。默认情况下,会搜索当前目录的 webpack.config.js 文件,这个文件是一个 node.js 模块,返回一个 json 格式的配置信息对象,或者通过 --config 选项来指定配置文件。 .输入命令:webpack,即可按…

java-stream流案例

需求 代码 Vote类 // 1. 定义一个投票类 public class Vote {private String name;private ArrayList<String> voteList;public Vote(String name, ArrayList<String> voteList) {this.name name;this.voteList voteList;}public String getName() {return nam…

还在愁自己该学什么编程?适龄标准来啦(6到14岁的同学看过来哦)

文章目录 前言一、6岁以下1.推荐2.软件 二、6至10岁1.推荐2.软件&#xff08;1&#xff09;6-8:Nemo编程——Scratch图形化编程&#xff08;2&#xff09;8-10岁&#xff1a;Scratch编程——Python编程 三、10岁以后1.推荐2.软件&#xff08;1&#xff09;Python&#xff08;2&…

攻防世界 serial-150 IDA动调

比较完整的做题记录吧,很基础,当作过一遍题目,大佬请飘过 题目 分析过程 丢到PE里面,是一个64位的ELF丢到IDA里面,查看字符串,发现线索 但是双击进去,不是汇编。是一个只读的rodata段 左边函数也不多,发现没有main函数,先点进去启动的start函数看看,发现main函数 点进…

elementui使用

1 # 1 开源的样式库,方便在vue中使用2 -elementui:饿了么团队开源的 web端3 https://element.eleme.cn/#/zh-CN4 -vant:有赞团队,移动端5 https://vant-ui.github.io/vant/#/zh-CN6 -ant design:阿里团队7 https://1x.antdv.com/docs…

2024/5/2

完成了河北科技查询系统全部内容

Redisson分布式锁,重试锁和锁续命的原理

RedissonLock 锁重试原理 tryLock有三个三个参数&#xff0c;第一个是等待时间&#xff0c;第二个是锁失效后自动释放的时间,不填默认为-1&#xff0c;第三个是时间单位&#xff1b; 当设置了第一个参数&#xff0c;那这个锁就成了可重试锁&#xff1b;获取锁失败后&#xff0c…

Rust Web开发实战:打造高效稳定的服务端应用

Rust Web开发实战&#xff1a;打造高效稳定的服务端应用 本书将带领您从零开始构建Web应用程序&#xff0c;无论是API、微服务还是单体应用&#xff0c;都将一一涵盖。您将学到如何优雅地对外开放API&#xff0c;如何连接数据库以安全存储数据&#xff0c;以及如何对应用程序进…

cpp字符串相关

字符串相关 文章参考: [详解-字符串] C++必知必会 字符串-string常用各种操作解析 - 知乎 (zhihu.com) C++ 字符串(string)常用操作总结 - 知乎 (zhihu.com) c++读取字符串和字符的6种函数_c++获取字符串的每个字符-CSDN博客 头文件 #include <string>定义字符串 stri…

持续更新|UNIAPP适配APP遇到的问题以及解决方案

在使用UNIAPP开发APP的时候遇到的一些奇奇怪怪问题记录 组件样式丢失 问题&#xff1a;组件引入界面中&#xff0c;在小程序和H5环境下样式正常&#xff0c;而在APP中却出现高度异常问题 解决&#xff1a;增加view标签将组件包裹起来即可正常显示 解决前&#xff1a; 解决后…

电能表的广泛应用

电能表是一种用来测量电力消费的设备&#xff0c;它在现代社会中得到了广泛的应用。电能表不仅对于居民家庭来说是必不可少的设备&#xff0c;也在工业生产、商业用电等领域发挥着重要的作用。本文将从电能表的发展历史、工作原理、应用领域以及未来发展趋势等方面来探讨电能表…

debian配置四叶草输入法

效果展示 一、前言 在linux下体验比较好的输入法只有两款&#xff1a;搜狗输入法、四叶草输入法。 ubuntu下可以成功配置搜狗输入法&#xff0c;但debian下从来没有成功过。 今天在用fcitx5 四叶草时发现VNC远程输入法会失灵&#xff0c;于是改用了ibus 四叶草&#xff0c…

Windows 10 使用 Vagrant 快速创建虚拟机

一、下载 VirtualBox 官网地址&#xff1a;Oracle VM VirtualBox 阿里云盘&#xff1a;阿里云盘分享 二、安装 VirtualBox 安装软件前请先确认 CPU 是否开启了虚拟化&#xff0c;要求开启 2.1、双击运行可执行文件后点击下一步 2.2、选择安装路径&#xff0c;为了避免中文乱码…

C语言——栈的实现

栈&#xff08;Stack&#xff09;是一种基于先进后出&#xff08;LIFO&#xff09;原则的数据结构&#xff0c;类似于我们平常堆放书籍或者盘子的方式。 栈通常是从高地址向低地址增长的&#xff0c;也就是说&#xff0c;栈顶位于较高的内存地址&#xff0c;而栈底位于较低的内…

响应式动漫音乐/个人博客杂志主题国漫FM模板

国漫FM主题V1.8是以Ajax加以CSS动画的方式,很好的将优雅的设计感和极度精简的代码同时表现了出来,进而缔造出这样一款十分经典的名为Always for you的WordPress博客主题。正如作者自己所言:如果你想让你的WordPress博客看起来个性十足。FM主题为响应式格子布局(瀑布流),是一…

WordPress CVE-2022-4230复现分析

前言 开始CVE审计之旅 WP Statistics WordPress 插件13.2.9之前的版本不会转义参数,这可能允许经过身份验证的用户执行 SQL 注入攻击。默认情况下,具有管理选项功能 (admin+) 的用户可以使用受影响的功能,但是该插件有一个设置允许低权限用户也可以访问它,其实就是没对admi…

Midjourney之绘画背景的选择

hello 小伙伴们&#xff0c;我是你们的老朋友——树下&#xff0c;今天分享Midjourney提示词中绘画背景的选择&#xff0c;话不多说&#xff0c;直接开始~ 对于背景的选择&#xff0c;Midjourney中主要体现在年代和所处的环境对绘画产生不同的影响 科技的发展&#xff0c;我们…

光影看图 v4.5.6.32 绿色版

更新流水:2024.04.30:跟进官方 4.5.6.32 , 第一版 修改内容:by.星罗月兔&DxFans去校验(方案来自@星罗月兔);去更新;去多余组件及无用菜单!下载地址:https://down.neoimaging.cn/neoimaging/NeoViewerSetup_10000_4.5.6.32.exe本文来自博客园,作者:萌面蛋饺,转载请注明…

保存钉钉群直播回放下载:直播回放下载步骤详解

今天&#xff0c;我们就来拨开云雾&#xff0c;揭开保存钉钉群直播回放的神秘面纱。教会你们如何下载钉钉群直播回放 首先用到的工具我全部打包好了&#xff0c;有需要的自己下载一下 钉钉群直播回放工具下载&#xff1a;https://pan.baidu.com/s/1WVMNGoKcTwR_NDpvFP2O2A?p…

js逆向实战之某证信Accept-Enckey参数加密解析

url:https://webapi.cninfo.com.cn/#/marketDataDate 分析过程抓包,主要关注图中标记的数据包,它的回显数据是我们所需要的。但在该数据包的请求中有一个Accept-Enckey参数是经过加密的,需要知道其加密的逻辑。全局搜索sysapi/p_sysapi1007,只有一处符合的。找到对应地方,…