深度学习优化器【学习率调整和梯度修正,Optimizer】

文章目录

  • 一、学习率调整
    • 1 余弦退火
      • (1)Warm up
      • (2)Cosine Anneal
    • 2 AdaGrad
    • 3 RMSprop算法
    • 4 AdaDelta算法
  • 二、梯度估计修正
    • 1 动量法
    • 2 Nesterov加速梯度
    • 3 Adam
      • 修正的原因
    • 4 AdamW
  • 三、总结
  • 参考资料


在当今快速发展的人工智能领域,优化算法在深度学习模型(比如语言大模型)的训练过程中尤为重要。本文主要介绍与随机梯度下降法相关的两个技巧——学习率调整和梯度修正方法

本文主要基于1,将介绍各种学习率策略,如余弦退火和自适应学习率算法(如 AdaGrad、RMSprop、AdaDelta),这些策略旨在加速收敛和提高训练稳定性。此外,还介绍了梯度估计修正技术,如动量法和 Nesterov 加速梯度,它们通过减少梯度的随机性来提高训练效率。特别地,我们分析了 Adam 和 AdamW 算法,展示了如何通过解耦权重衰减来增强模型的正则化能力。

一、学习率调整

学习率是神经网络优化时的重要超参数.在梯度下降法中,参数的更新规则为:
θ t = θ t − 1 − α ⋅ g t \theta_t = \theta_{t-1}-\alpha\cdot g_t θt=θt1αgt
其中 θ t \theta_t θt为第 t t t次迭代的参数, g t g_t gt是第 t t t次迭代的梯度, α \alpha α为学习率。学习率𝛼的取值非常关键,如果过大就不会收敛,如果过小则收敛速度太慢.

常用的学习率调整方法包括学习率衰减、学习率预热、周期性学习率调整以及一些自适应调整学习率的方法,比如 AdaGrad、RMSprop、AdaDelta 等1.

1 余弦退火

从经验上看,学习率在一开始要保持大些来保证收敛速度,在收敛到最优点附近时要小些以避免来回振荡.下面介绍一个常用的学习率调整方法:warm up + Cosine Anneal.

(1)Warm up

由于一开始参数不稳定, 梯度较大, 如果此时学习率设置过大可能导致数值不稳定。为了提高训练稳定性,我们可以在最初几轮迭代时,采用比较小的学习率,等梯度下降到一定程度后再恢复到初始的学习率,这种方法称为学习率预热(Learning Rate Warmup).使用warm up有助于减缓模型在初始阶段对mini-batch 的提前过拟合现象, 保持分布的平稳, 其次也有助于保持模型深层的稳定性。

(2)Cosine Anneal

梯度下降算法使得loss值接近全局最小值, 学习率应当变得更小, 才能够使其更加容易实现。余弦退火首先使得学习率先缓慢下降, 然后再快速下降, 可以满足上面的需求。公式如下:

α t = α min ⁡ + 1 2 ( α max ⁡ − α min ⁡ ) ( 1 + cos ⁡ ( T cur  T max ⁡ π ) ) \alpha_t=\alpha_{\min }+\frac{1}{2}\left(\alpha_{\max }-\alpha_{\min }\right)\left(1+\cos \left(\frac{T_{\text {cur }}}{T_{\max }} \pi\right)\right) αt=αmin+21(αmaxαmin)(1+cos(TmaxTcur π))

其中, α max ⁡ \alpha_{\max} αmax 为学习率最大值, α min ⁡ \alpha_{\min} αmin 为最小值, T c u r T_{c u r} Tcur 为当前轮次, T m a x T_{ m a x} Tmax 为训练最大迭代步。

Warm up + Cosine Anneal两阶段结合起来的学习率变化如下图所示:

image-20241014150449651

2 AdaGrad

在标准的梯度下降法中,每个参数在每次迭代时都使用相同的学习率.由于每个参数的维度上收敛速度都不相同,因此有必要根据不同参数的收敛情况分别设置学习率.

AdaGrad 算法( Adaptive Gradient Algorithm )[Duchi et al., 2011]是借鉴 ℓ 2 \ell_2 2正则化的思想,每次迭代时自适应地调整每个参数的学习率. 在第 t t t 次迭代时, 先计算每个参数梯度平方的累计值

G t = ∑ τ = 1 t g τ ⊙ g τ , G_t=\sum_{\tau=1}^tg_\tau\odot g_\tau, Gt=τ=1tgτgτ,
其中 ⊙ \odot 为按元素乘积, g τ ∈ R ∣ θ ∣ \mathbf{g}_\tau\in\mathbb{R}^{|\theta|} gτRθ是第 τ \tau τ次迭代时的梯度, G t ∈ R ∣ θ ∣ G_t\in\mathbb{R}^{|\theta|} GtRθ。AdaGrad 算法的参数更新差值为
Δ θ t = − α G t + ε ⊙ g t , \Delta\theta_t=-\frac\alpha{\sqrt{G_t+\varepsilon}}\odot\mathbf{g}_t, Δθt=Gt+ε αgt,
其中

  • α \alpha α 是初始的学习率, ε \varepsilon ε是为了保持数值稳定性而设置的非常小的常数,一般取值 e − 7 \mathrm{e}^{-7} e7 e − 10 \mathrm{e}^{-10} e10.
  • 此外,这里的开平方、除、加运算都是按元素进行的操作
  • 在 AdaGrad 算法中,如果某个参数的偏导数累积比较大,其学习率相对较小; 相反,如果其偏导数累积较小,其学习率相对较大,但整体是随着迭代次数的增加,学习率逐渐缩小.

AdaGrad 算法的缺点是在经过一定次数的迭代依然没有找到最优点时,由于这时的学习率已经非常小,很难再继续找到最优点.

3 RMSprop算法

RMSprop 算法是 Geoff Hinton 提出的一种自适应学习率的方法, 可以在有些情况下避免 AdaGrad 算法中学习率不断单调下降以至于过早衰减的缺点.

RMSprop 算法首先计算每次迭代梯度 g t \mathbf{g}_t gt平方的指数加权移动平均
G t = β G t − 1 + ( 1 − β ) g t ⊙ g t = ( 1 − β ) ∑ τ = 1 t β t − τ g τ ⊙ g τ , \begin{aligned}G_{t}&=\beta G_{t-1}+(1-\beta)\mathbf{g}_t\odot\mathbf{g}_t\\&=(1-\beta)\sum_{\tau=1}^t\beta^{t-\tau}\mathbf{g}_\tau\odot\mathbf{g}_\tau,\end{aligned} Gt=βGt1+(1β)gtgt=(1β)τ=1tβtτgτgτ,
其中 β \beta β 为衰减率,一般取值为0.9.RMSprop 算法的参数更新差值为

Δ θ t = − α G t + ε ⊙ g t , \Delta\theta_t=-\frac\alpha{\sqrt{G_t+\varepsilon}}\odot\mathbf{g}_t, Δθt=Gt+ε αgt,
其中 α \alpha α是初始的学习率,比如0.001.从上式可以看出,RMSProp 算法和 AdaGrad 算法的区别在于 G t G_t Gt 的计算由累积方式变成了指数加权移动平均.在迭代过程中,每个参数的学习率并不是呈衰减趋势,既可以变小也可以变大。

4 AdaDelta算法

AdaDelta 算法 也是 AdaGrad 算法的一个改进. 和 RMSprop算法类似,AdaDelta 算法通过梯度平方的指数衰减移动平均来调整学习率. 此外, AdaDelta算法还引入了每次参数更新差值 Δ θ \Delta \theta Δθ 的平方的指数衰減权移动平均.

t t t 次迭代时, 参数更新差值 Δ θ \Delta \theta Δθ 的平方的指数衰减权移动平均为

Δ X t − 1 2 = β 1 Δ X t − 2 2 + ( 1 − β 1 ) Δ θ t − 1 ⊙ Δ θ t − 1 \Delta X_{t-1}^2=\beta_1 \Delta X_{t-2}^2+\left(1-\beta_1\right) \Delta \theta_{t-1} \odot \Delta \theta_{t-1} ΔXt12=β1ΔXt22+(1β1)Δθt1Δθt1

其中 β 1 \beta_1 β1 为衰减率. 此时 Δ θ t \Delta \theta_t Δθt 还未知, 因此只能计算到 Δ X t − 1 \Delta X_{t-1} ΔXt1.AdaDelta算法的参数更新差值为

Δ θ t = − Δ X t − 1 2 + ε G t + ε g t \Delta \theta_t=-\frac{\sqrt{\Delta X_{t-1}^2+\varepsilon}}{\sqrt{G_t+\varepsilon}} g_t Δθt=Gt+ε ΔXt12+ε gt

其中 G t G_t Gt 的计算方式和 RMSprop 算法一样 , Δ X t − 1 2 \Delta X_{t-1}^2 ΔXt12 为参数更新差值 Δ θ \Delta \theta Δθ 的指数衰减权移动平均.

从上式可以看出,AdaDelta 算法将 RMSprop 算法中的初始学习率 α \alpha α 改为动态计算的 Δ X t − 1 2 \sqrt{\Delta X_{t-1}^2} ΔXt12 , 在一定程度上平抑了学习率的波动.

二、梯度估计修正

在随机(小批量)梯度下降法中,如果每次选取样本数量比较小,损失会呈现振荡的方式下降.也就是说,随机梯度下降方法中每次迭代的梯度估计和整个训练集上的最优梯度并不一致,具有一定的随机性.

如何缓解梯度的随机性?

  • 增加批量大小.
  • 使用最近一段时间内的平均梯度来代替当前时刻的随机梯度来作为参数更新的方向,从而提高优化速度.

1 动量法

动量( Momentum ) 是模拟物理中的概念. 一个物体的动量指的是该物体在它运动方向上保持运动的趋势,是该物体的质量和速度的乘积.动量法( Momentum Method)是用之前积累动量来替代真正的梯度.每次迭代的梯度可以看作加速度.在第 t t t 次迭代时,计算负梯度的“加权移动平均”作为参数的更新方向,

Δ θ t = ρ Δ θ t − 1 − α g t = − α ∑ τ = 1 t ρ t − τ g τ , \Delta\theta_t=\rho\Delta\theta_{t-1}-\alpha\mathbf{g}_t=-\alpha\sum_{\tau=1}^t\rho^{t-\tau}\mathbf{g}_\tau, Δθt=ρΔθt1αgt=ατ=1tρtτgτ,
其中 ρ \rho ρ 为动量因子,通常设为0.9 , α ,\alpha ,α为学习率(注意这里学习率并没有调整)。这样,每个参数的实际更新差值取决于最近一段时间内梯度的加权平均值. 当某个参数在最近一段时间内的梯度方向不一致时,其真实的参数更新幅度变小;相反,当在最近一段时间内的梯度方向都一致时,其真实的参数更新幅度变大,起到加速作用。

如下图所示,引进 momentum 不仅可以加速梯度下降法的收敛速度,还可以一定程度避免在局部最小值处“收敛”(借助前一次的 Momentum,或许可以“冲”过局部最小值处)。

image-20241028141356995

注意到:

  • 一般而言,在迭代初期,梯度方向都比较一致,动量法会起到加速作用,可以更快地到达最优点.(如上图前三个点所示)
  • 在迭代后期,梯度方向会不一致,在收敛值附近振荡,动量法会起到减速作用,增加稳定性,从某种角度来说,当前梯度叠加上部分的上次梯度,一定程度上可以近似看作二阶梯度。

2 Nesterov加速梯度

Nesterov 加速梯度 (Nesterov Accelerated Gradient, NAG) 是一种对动量法的改进 , 也称为Nesterov 动量法 (Nesterov Momentum ).

在动量法中,实际的参数更新方向 Δ θ t \Delta \theta_t Δθt 为上一步的参数更新方向 Δ θ t − 1 \Delta \theta_{t-1} Δθt1 和当前梯度的反方向 − g t -g_t gt 的叠加. 这样, Δ θ t \Delta \theta_t Δθt 可以被拆分为两步进行, 先根据 Δ θ t − 1 \Delta \theta_{t-1} Δθt1 更新一次得到参数 θ ^ \hat{\theta} θ^, 再用 − g t -g_t gt 进行更新.

θ ^ = θ t − 1 + ρ Δ θ t − 1 θ t = θ ^ − α g t \begin{aligned} \hat{\theta} & =\theta_{t-1}+\rho \Delta \theta_{t-1} \\ \theta_t & =\hat{\theta}-\alpha g_t \end{aligned} θ^θt=θt1+ρΔθt1=θ^αgt

其中梯度 g t g_t gt 为点 θ t − 1 \theta_{t-1} θt1 上的梯度, 因此在第二步更新中有些不太合理. 更合理的更新方向应该为 θ ^ \hat{\theta} θ^ 上的梯度.这样,所以Nesterov Momentum修改后的更新方向为
θ ^ = θ t − 1 + ρ Δ θ t − 1 θ t = θ ^ − α g ( θ ^ ) \begin{aligned} \hat{\theta} & =\theta_{t-1}+\rho \Delta \theta_{t-1} \\ \theta_t & =\hat{\theta}-\alpha {g(\hat{\theta})} \end{aligned} θ^θt=θt1+ρΔθt1=θ^αg(θ^)
合并后更新方向为:
Δ θ t = ρ Δ θ t − 1 − α g t ( θ t − 1 + ρ Δ θ t − 1 ) \Delta \theta_t=\rho \Delta \theta_{t-1}-\alpha g_t\left(\theta_{t-1}+\rho \Delta \theta_{t-1}\right) Δθt=ρΔθt1αgt(θt1+ρΔθt1)

其中 g t ( θ t − 1 + ρ Δ θ t − 1 ) g_t\left(\theta_{t-1}+\rho \Delta \theta_{t-1}\right) gt(θt1+ρΔθt1) 表示损失函数在 θ ^ = θ t − 1 + ρ Δ θ t − 1 \hat{\theta}=\theta_{t-1}+\rho \Delta \theta_{t-1} θ^=θt1+ρΔθt1 上的偏导数.

下图给出了动量法和Nesterov加速梯度在参数更新时的比较,红色虚线是主要区别。

截屏2024-10-28 14.30.021

3 Adam

Adam 算法 (Adaptive Moment Estimation Algorithm ) 2可以看作动量法和 RMSprop 算法的结合. 不但使用动量作为参数更新方向, 而且可以自适应调整学习率。

Adam 算法一方面计算梯度平方 g t 2 \mathbf{g}_t^2 gt2 的指数加权平均(和 RMSprop 算法类似), 另一方面计算梯度 g t \mathrm{g}_t gt 的指数加权平均 (和动量法类似).

M t = β 1 M t − 1 + ( 1 − β 1 ) g t , ( 一阶矩估计 ) G t = β 2 G t − 1 + ( 1 − β 2 ) g t ⊙ g t , ( 二阶矩估计 ) \begin{aligned} M_t&=\beta_1 M_{t-1}+\left(1-\beta_1\right) g_t, &(\text{一阶矩估计})\\ G_t&=\beta_2 G_{t-1}+\left(1-\beta_2\right) g_t \odot g_t,&(\text{二阶矩估计})\\ \end{aligned} MtGt=β1Mt1+(1β1)gt,=β2Gt1+(1β2)gtgt,(一阶矩估计)(二阶矩估计)

其中 β 1 \beta_1 β1 β 2 \beta_2 β2 分别为两个移动平均的衰減率, 通常取值为 β 1 = 0.9 , β 2 = 0.99 \beta_1=0.9, \beta_2=0.99 β1=0.9,β2=0.99. 我们可以把 M t M_t Mt G t G_t Gt 分别看作梯度的均值(一阶矩)和未减去均值的方差(二阶矩).

假设 M 0 = 0 , G 0 = 0 M_0=0, G_0=0 M0=0,G0=0 ,那么在迭代初期 M t M_t Mt G t G_t Gt 的值会比真实的均值和方差要小, 特别是当 β 1 \beta_1 β1 β 2 \beta_2 β2 都接近于 1 时, 偏差会很大. 因此, 需要对偏差进行修正.

M ^ t = M t 1 − β 1 t ( 偏差修正 ) G ^ t = G t 1 − β 2 t ( 偏差修正 ) \begin{aligned} \hat{M}_t & =\frac{M_t}{1-\beta_1^t} &(\text{偏差修正})\\ \hat{G}_t & =\frac{G_t}{1-\beta_2^t} &(\text{偏差修正})\\ \end{aligned} M^tG^t=1β1tMt=1β2tGt(偏差修正)(偏差修正)

Adam算法的参数更新差值为

θ t = θ t − 1 − α G ^ t + ϵ M ^ t ( 参数更新 ) \theta_t=\theta_{t-1}-\frac{\alpha}{\sqrt{\hat{G}_t+\epsilon}} \hat{M}_t \quad(\text{参数更新})\\ θt=θt1G^t+ϵ αM^t(参数更新)

其中学习率 α \alpha α 通常设为 0.001 , 并且也可以进行衰減, 比如采用余弦退火.

修正的原因

在 Adam 算法中,初期的估计值偏小是因为 指数加权平均 的计算方式依赖于前面的梯度信息,而在初始阶段,梯度的历史数据很少,因此计算出的平均值往往低于实际值。我们可以用一个具体的例子来说明这个偏差为什么会发生。

假设我们有一个优化过程,在第一个时间步 t = 1 t = 1 t=1,我们记录到的梯度值为 g 1 g_1 g1。我们使用 Adam 的一阶矩的指数加权平均来计算梯度的平均值。

Adam 中一阶矩的指数加权平均的公式为:

M t = β 1 M t − 1 + ( 1 − β 1 ) g t M_t = \beta_1 M_{t-1} + (1 - \beta_1) g_t Mt=β1Mt1+(1β1)gt

  • 假设 β 1 = 0.9 \beta_1 = 0.9 β1=0.9
  • 初始时刻 M 0 = 0 M_0 = 0 M0=0,即指数加权平均的初值为 0。

在第一个时间步 t = 1 t = 1 t=1

M 1 = β 1 M 0 + ( 1 − β 1 ) g 1 M 1 = 0.9 × 0 + ( 1 − 0.9 ) × g 1 M 1 = 0.1 × g 1 \begin{aligned} M_1 &= \beta_1 M_0 + (1 - \beta_1) g_1\\ M_1 &= 0.9 \times 0 + (1 - 0.9) \times g_1\\ M_1 &= 0.1 \times g_1 \end{aligned} M1M1M1=β1M0+(1β1)g1=0.9×0+(10.9)×g1=0.1×g1
由于 M 0 = 0 M_0 = 0 M0=0,所以 M 1 M_1 M1 的值只包含了当前梯度值的 10%。这明显比实际的 g 1 g_1 g1 要小很多,若 β 1 \beta_1 β1约接近于0,那么偏差就越大。

同理可知 G t G_t Gt也是如此,所以两者都需要引入一个修正项。

4 AdamW

AdamW是一个在深度学习中广泛应用的优化器,它是经典Adam优化器的一个变体,专门为了解决Adam在权重衰减(Weight Decay)方面的问题而提出。AdamW最早在论文《Decoupled Weight Decay Regularization》中提出3,它结合了Adam的自适应学习率和L2正则化的优势, 解决了Adam中错误使用权重衰减的问题。

在深度学习中,权重衰减(weight decay) 是一种常用的正则化技术,它通过在每次更新参数时引入一个额外的项来控制权重的增长,从而防止过拟合。在经典的梯度下降方法中,权重衰减的实现非常简单:

θ = θ − η ( ∇ L ( θ ) + λ θ ) \theta=\theta-\eta(\nabla L(\theta)+\lambda \theta) θ=θη(L(θ)+λθ)

其中, λ θ \lambda \theta λθ 是权重衰减项(L2正则化项)。这种方法能够有效防止模型参数过拟合。在原始的 Adam 算法中,如果使用了权重衰减(也就是 L2 正则化),它是将正则化项添加到梯度上,即损失函数对参数的梯度 g t g_t gt被修改为:
g t = ∇ L ( θ t ) + λ θ t g_t = \nabla L(\theta_t)+\lambda \theta_t gt=L(θt)+λθt
L2正则化和Adam的动量更新机制耦合在了一起,导致了不理想的正则化效果。AdamW通过将权重衰减与梯度更新解耦, 解决了Adam在权重衰减方面的缺陷。 它将权重衰减独立处理, 而不再作为Adam优化器更新的一部分。这使得模型在参数更新时可以保持更高的稳定性,并且防止过拟合。

AdamW的参数更新公式如下:

  1. 动量和二阶矩的计算(与Adam相同):

M t = β 1 M t − 1 + ( 1 − β 1 ) g t , ( 一阶矩估计 ) G t = β 2 G t − 1 + ( 1 − β 2 ) g t ⊙ g t , ( 二阶矩估计 ) \begin{aligned} M_t&=\beta_1 M_{t-1}+\left(1-\beta_1\right) g_t, &(\text{一阶矩估计})\\ G_t&=\beta_2 G_{t-1}+\left(1-\beta_2\right) g_t \odot g_t,&(\text{二阶矩估计})\\ \end{aligned} MtGt=β1Mt1+(1β1)gt,=β2Gt1+(1β2)gtgt,(一阶矩估计)(二阶矩估计)
2. 偏差修正:
M ^ t = M t 1 − β 1 t ( 偏差修正 ) G ^ t = G t 1 − β 2 t ( 偏差修正 ) \begin{aligned} \hat{M}_t & =\frac{M_t}{1-\beta_1^t} &(\text{偏差修正})\\ \hat{G}_t & =\frac{G_t}{1-\beta_2^t} &(\text{偏差修正})\\ \end{aligned} M^tG^t=1β1tMt=1β2tGt(偏差修正)(偏差修正)
3. 参数更新:

θ t = θ t − 1 − α ( M ^ t G ^ t + ϵ + λ θ t − 1 ) \theta_t=\theta_{t-1}-\alpha\left(\frac{\hat{M}_t}{\sqrt{\hat{G}_t}+\epsilon}+\lambda \theta_{t-1}\right) θt=θt1α(G^t +ϵM^t+λθt1)

其中:

  • M ^ t G t + ϵ \frac{\hat{M}_t}{\sqrt{G_t}+\epsilon} Gt +ϵM^t 是Adam的梯度更新部分。
  • λ θ t − 1 \lambda \theta_{t-1} λθt1 是独立的权重衰减项,与梯度更新无关。

通过这种方式, AdamW能够更好地控制权重衰减, 防止模型过拟合,而不影响优化器的动量和自适应学习率更新。相比于传统的Adam,AdamW 具有以下优势4

  • 更准确的权重衰减: AdamW将权重衰减与梯度更新解耦,避免了Adam优化器中的错误正则化处理。这样可以有效防止模型的参数过度增长,并提高泛化能力。
  • 更好的正则化效果: 由于权重衰减项是独立处理的,AdamW能够更有效地施加L2正则化,尤其在大模型(如LLaMA等)训练中表现更为稳定。
  • 提高模型的泛化能力: 通过更好的权重控制,AdamW能够在不影响优化过程的前提下提升模型在测试集上的表现,减少过拟合问题。

三、总结

本节介绍的几种优化方法大体上可以分为两类:

  1. 调整学习率,使得优化更稳定;
  2. 梯度估计修正,优化训练速度.

这些优化算法可以使用下面公式来统一描述概括:
Δ θ t = − α t G t + ϵ M t \Delta\theta_t = -\frac{\alpha_t}{\sqrt{G_t+\epsilon}}M_t Δθt=Gt+ϵ αtMt
其中 G t , M t G_t,M_t Gt,Mt都是梯度 g 1 , g 2 , ⋯ , g t g_1,g_2,\cdots,g_t g1,g2,,gt的函数,不同算法的 G t , M t G_t,M_t Gt,Mt取法总结如下表所示:

截屏2024-10-28 15.40.57

参考资料


  1. 邱锡鹏,神经网络与深度学习,机械工业出版社,https://nndl.github.io/, 2020. ↩︎ ↩︎ ↩︎

  2. Kingma, Diederik P., and Jimmy Ba. "Adam: A method for stochastic optimization."arXiv preprint arXiv:1412.6980(2014). ↩︎

  3. Loshchilov, Ilya, and Frank Hutter. "Decoupled weight decay regularization."arXiv preprint arXiv:1711.05101(2017). ↩︎

  4. AdamW和Adam优化器对比分析 ↩︎

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

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

相关文章

运算符重载详解,日期类型的实现

一、运算符重载 当运算符被⽤于类类型的对象时,C语⾔允许我们通过运算符重载的形式指定新的含义。C规定类类型对象使⽤运算符时,必须转换成调⽤对应运算符重载,若没有对应的运算符重载,则会编译报错。 类中含有多种变量&#xff0…

什么是FUSE用户态文件系统

零. 文件系统 1. 为什么要有文件系统 文件系统是操作系统中管理文件和目录的一种机制。它提供了组织、存储、检索和更新文件的方法,主要如下: 数据组织:文件系统将数据组织成文件和目录,使用户能够更方便地管理和查找文件。每个…

HarmonyOS 5.0应用开发——音频播放组件的封装

【高心星出品】 文章目录 音频播放组件的封装开发步骤封装类代码测试代码 音频播放组件的封装 鸿蒙中提供了AVPlayer来实现音频播放的功能,播放的全流程包含:创建AVPlayer,设置播放资源,设置播放参数(音量/倍速/焦点模…

KPRCB结构之ReadySummary和DispatcherReadyListHead

ReadySummary: Uint4B DispatcherReadyListHead : [32] _LIST_ENTRY 请参考 _KTHREAD *__fastcall KiSelectReadyThread(ULONG LowPriority, _KPRCB *Prcb)

【染色时间】

题目 代码 #include <bits/stdc.h> using namespace std; #define x first #define y second typedef pair<int,int> PII; const int N 510; int dx[] {0,0,-1,1}, dy[] {-1,1,0,0}; int d[N][N], w[N][N]; int n, m; void bfs() {memset(d, 0x3f, sizeof d);q…

Apache Dubbo (RPC框架)

本文参考官方文档&#xff1a;Apache Dubbo 1. Dubbo 简介与核心功能 Apache Dubbo 是一个高性能、轻量级的开源Java RPC框架&#xff0c;用于快速开发高性能的服务。它提供了服务的注册、发现、调用、监控等核心功能&#xff0c;以及负载均衡、流量控制、服务降级等高级功能。…

利用钉钉与金蝶云星空进行付款单自动化集成

钉钉数据集成到金蝶云星空&#xff1a;付款申请单下推生成付款单的技术实现 在企业日常运营中&#xff0c;数据的高效流转和准确处理是业务顺利进行的关键。本文将分享一个具体的系统对接集成案例&#xff1a;如何将钉钉平台上的付款申请单&#xff0c;通过轻易云数据集成平台…

前端八股文第七篇

61. React 中有对状态管理做进一步封装吗 在 React 中&#xff0c;除了可以使用原生的状态管理方式&#xff08;即使用组件的 state 属性&#xff09;外&#xff0c;还有一些第三方库对状态管理进行了进一步封装&#xff0c;以提供更强大和便捷的状态管理功能。其中最常见的是…

青少年编程能力等级测评CPA Python编程(一级)

青少年编程能力等级测评CPA Python编程(一级) &#xff08;考试时间90分钟&#xff0c;满分100分&#xff09; 一、单项选择题&#xff08;共20题&#xff0c;每题3.5分&#xff0c;共70分&#xff09; 下列语句的输出结果是&#xff08; &#xff09;。 print(35*2) A&a…

JavaScript语法基础(函数,对象,常用类Array,String,Math和Date)【超详细!!!新手入!!!】

一、函数 1、函数的定义 函数指的是一段可被重复调用的代码块。函数与变量不同&#xff0c;需要先定义再调用。 定义函数的语法格式为&#xff1a; function 函数名&#xff08;参数1&#xff0c;参数2&#xff0c;...&#xff09; { 语句&#xff1b; …

每日OJ题_牛客_NC6二叉树中的最大路径和_树形DP_C++_Java

目录 牛客_NC6二叉树中的最大路径和_树形DP 题目解析 C代码 Java代码 牛客_NC6二叉树中的最大路径和_树形DP 二叉树中的最大路径和_牛客题霸_牛客网 (nowcoder.com) 描述&#xff1a; 二叉树里面的路径被定义为:从该树的任意节点出发&#xff0c;经过父>子或者子>…

PG数据库 jsonb字段 模糊查询

背景&#xff1a; 项目由于多语言的设计&#xff0c;将字段设置成json字段类型&#xff0c;同时存储中文和英文 页面上通过输入框实现模糊的查询 一、表结构&#xff1a;name字段设置jsonb类型 二、表数据 3、Mybatis编写sql select pp.name ->>zh-CN as pmsProductNam…

黑龙江某涝区泵闸站自动化、信息化改造项目案例

项目背景 黑龙江某地区紧邻松花江&#xff0c;雨季时降雨量增大&#xff0c;排水渠水位上涨&#xff0c;如果出现排涝不及时&#xff0c;水位过高时会漫入周边农田&#xff0c;引发洪涝灾害&#xff0c;对作物生长造成重大损害。为应对这一问题&#xff0c;自今年起&#xff0c…

奥数与C++小学四年级(第十八题 小球重量)

参考程序代码&#xff1a; #include <iostream> #include <vector>int main() {// 小球的重量std::vector<int> weights {1, 2, 3, 4, 5};// 用来存储可能的结果int a, b, c, d, e, x;// 穷举所有可能的 a, b, c, d, e 的组合for (int i 0; i < weight…

建筑行业知识库搭建:好处、方法与注意事项

在建筑行业&#xff0c;知识管理对于提升项目效率、降低成本、增强创新能力以及构建竞争优势具有至关重要的作用。搭建一个高效、系统的建筑行业知识库&#xff0c;不仅有助于实现知识的有效沉淀与便捷共享&#xff0c;还能促进知识在项目实践中的灵活应用&#xff0c;从而加速…

控制器一些不常用的的功能说明

1、IIC的特殊功能 1.1、IIC的10bit设备地址 10bit地址格式与7bit地址不同&#xff0c;分发送方向和接收方向。 1.1.1、发送方向 第一个字节的前7位是1111 0XX&#xff1a;XX是10bit地址的最高有效位的前两位 第一个字节的第8bit是读写位&#xff1a;决定传输方向 第二个字节…

ssm+vue657基于spring和vue开发的web新闻流媒体平台

博主介绍&#xff1a;专注于Java&#xff08;springboot ssm 等开发框架&#xff09; vue .net php phython node.js uniapp 微信小程序 等诸多技术领域和毕业项目实战、企业信息化系统建设&#xff0c;从业十五余年开发设计教学工作 ☆☆☆ 精彩专栏推荐订阅☆☆☆☆☆不…

Spring Boot 3项目创建与示例(Web+JPA)

以下是一个Spring Boot 3.3.4整合JPA的示例,它展示了如何在Spring Boot应用程序中使用JPA进行数据持久化。 版本与环境 Spring Boot 3.3.4数据库: MySQL 8.0.40, MySQL的安装使用可以参考: MySQL 8 下载与安装攻略JDK 17Maven 3.6项目创建 可以使用Spring Initializr 初始…

龙迅#LT8668EX显示器图像处理芯片 适用于HDMI1.4+VGA转4PORT LVDS,支持4K30HZ分辨率,可做OSD菜单亮度调节!

1. 一般说明 LT8668EX 是 Lontium 的第二代 LCD 控制器&#xff0c;基于 ClearEdge 技术&#xff0c;支持 VGA 接口和 HDMI 接口&#xff0c;符合 HDMI 1.4 规范。它可以支持带 HDMI 接口的双模 DP。为了向后兼容&#xff0c;该 LCD 控制器还包括一个高性能模拟接口&#xff0…

[pdf,epub]105页《分析模式》漫谈合集01

105页的《分析模式》漫谈合集第1集的pdf、epub文件&#xff0c;已上传到本账号的CSDN资源。 如果无法下载&#xff0c;也可以访问umlchina.com/url/ap.html 已排版成适合手机阅读&#xff0c;pdf的排版更好一些。 ★UMLChina为什么叒要翻译《分析模式》&#xff1f; ★[缝合故…