BPTT算法详解:深入探究循环神经网络(RNN)中的梯度计算【原理理解】

引言

在深度学习领域中,我们经常处理的是独立同分布(i.i.d)的数据,比如图像分类、文本生成等任务,其中每个样本之间相互独立。然而,在现实生活中,许多数据具有时序结构,例如语言模型中的单词序列、股票价格随时间的变化、视频中的帧等。对于这类具有时序关系的数据,传统的深度学习模型可能无法很好地捕捉到其内在的 时间相关性 。为了解决这一问题,循环神经网络(Recurrent Neural Network, RNN)被广泛应用于处理时序数据。

为什么说反向传播算法不能处理时序数据呢?
在传统的反向传播算法中,处理静态数据时,网络的输出 y ^ \hat{y} y^ 通常只依赖于当前时刻的隐藏状态 h h h,其更新规则可以表示为:
h = W x + b h = Wx + b h=Wx+b
y ^ = V h + c \hat{y} = Vh + c y^=Vh+c
其中, h h h 是隐藏状态, x x x 是输入, W W W V V V 是网络的参数, b b b c c c 是偏置项。

与传统反向传播算法不同,BPTT(Back-Propagation Through Time)算法引入了时间维度,并考虑了序列数据中的时序关系。在 BPTT 中,隐藏状态 h t h_t ht 的更新规则包含了当前时刻的输入 X t X_t Xt 和上一个时刻的隐藏状态 h t − 1 h_{t-1} ht1,从而能够更好地捕捉到序列数据中的时间相关性。
h t = f ( U X t + W h t − 1 ) h_t = f(UX_t + Wh_{t-1}) ht=f(UXt+Wht1)
y t ^ = f ( V h t ) \hat{y_t} = f(Vh_t) yt^=f(Vht)

RNN 结构与BPTT

首先,让我们来了解一下常见的循环神经网络结构。在 RNN 中,隐藏状态会随着时间步的推移而更新,并在每个时间步生成一个输出。这种结构允许网络捕捉到序列数据中的时间相关性,使得其在时序任务中表现出色。

一个常见的RNN结构如下所示:
RNN结构
在RNN中,参数U、V和W是共享的,这意味着它们在每个时间步都保持不变。这意味着它们的值在整个模型运行过程中 始终保持一致

BPTT算法概述

前向传播

在 RNN 中,前向传播阶段通过计算隐藏状态和输出来生成预测结果。

h t = f ( U X t + W h t − 1 ) h_t = f(UX_t + Wh_{t-1}) ht=f(UXt+Wht1)

y t ^ = f ( V h t ) \hat{y_t} = f(Vh_t) yt^=f(Vht)

损失函数

这些结果与真实标签之间的差异通过损失函数来衡量,我们的目标是最小化这个损失函数。整个网络的损失值 L L L是每个时刻损失值 L t L_t Lt的求和,其中 L t L_t Lt是关于预测值 y t ^ \hat{y_t} yt^的函数。

L t = f ( y t ^ ) L_t = f(\hat{y_t}) Lt=f(yt^)
L = ∑ i = 1 T L t L = \sum_{i=1}^{T} L_t L=i=1TLt

损失函数 L L L 可以表示为:

  1. 均方误差(MSE):
    L = ∑ t = 1 T 1 2 ( y t − y ^ t ) 2 L = \sum_{t=1}^T \frac{1}{2} (y_t - \hat{y}_t)^2 L=t=1T21(yty^t)2
    这里,我们计算每个时间步的输出 y t y_t yt 与真实输出 y ^ t \hat{y}_t y^t 之间的平方误差,并将所有时间步的误差求和。
  2. 交叉熵损失:
    L = − ∑ t = 1 T [ y ^ t log ⁡ ( y t ) + ( 1 − y ^ t ) log ⁡ ( 1 − y t ) ] L = -\sum_{t=1}^T [\hat{y}_t \log(y_t) + (1 - \hat{y}_t) \log(1 - y_t)] L=t=1T[y^tlog(yt)+(1y^t)log(1yt)]
    这里,我们计算每个时间步的输出 y t y_t yt 与真实输出 y ^ t \hat{y}_t y^t 之间的交叉熵损失,并将所有时间步的损失求和。

反向传播

接下来,我们使用BPTT算法(随时间反向传播,Back-Propagation Through Time,BPTT)进行反向传播。在这一步中,我们计算损失函数对参数U、V和W的偏导数,以便更新参数以最小化损失。

为什么要使用整个序列的损失函数L对参数U、V和W求导呢?

这是因为我们的目标是最小化整个序列的损失。在梯度下降算法中,梯度指向了损失函数增长最快的方向。因此,通过对整个序列的损失函数求导,我们可以找到在参数空间中使得损失函数逐步减小的方向,然后通过反向传播来更新参数。

由于RNN处理的是时序数据,因此需要基于时间进行反向传播,这也是BPTT名称的由来。尽管BPTT是在时序数据上进行反向传播,但本质上它仍然是反向传播算法,因此求解每个时间步的梯度是该算法的核心操作。

梯度计算

我们以一个长度为3的时间序列为例,展示对于参数U、V和W的偏导数的计算过程。

长度为3的时间序列
首先看看前向传播的计算

隐藏层输出:
h t = f ( U X t + W h t − 1 ) h_t = f(UX_t + Wh_{t-1}) ht=f(UXt+Wht1)

为什么“RNN的隐藏状态更新规则是 h t = f ( U X t + W h t − 1 ) h_t = f(UX_t + Wh_{t-1}) ht=f(UXt+Wht1)”?

从数学角度来看,这个更新规则是由RNN的结构决定的。在RNN中,隐藏状态
h t h_t ht

  • 由当前时间步的输入 X t X_t Xt
  • 前一个时间步的隐藏状态 h t − 1 h_{t-1} ht1

组合而成的。通过线性变换 U X t + W h t − 1 UX_t + Wh_{t-1} UXt+Wht1,加上激活函数 f f f 的作用,得到了新的隐藏状态 h t h_t ht。这个结构使得RNN能够记忆之前的信息并将其应用于当前的预测任务中。

输出层:
y t ^ = f ( V h t ) \hat{y_t} = f(Vh_t) yt^=f(Vht)

  • h t h_t ht 是隐藏状态
  • y t ^ \hat{y_t} yt^ 是输出值
  • X t X_t Xt 输入的序列
  • f f f是激活函数

将上面的RNN用数学表达式来表示就是
{ h 1 = f ( U x 1 + W h 0 ) y ^ 1 = f ( V h 1 ) \left\{\begin{array}{l}h_{1}=f\left(U x_{1}+W h_{0}\right) \\\hat{y}_{1}=f\left(V h_{1}\right)\end{array}\right. {h1=f(Ux1+Wh0)y^1=f(Vh1)
{ h 2 = f ( U x 2 + W h 1 ) y ^ 2 = f ( V h 2 ) \left\{\begin{array}{l}h_{2}=f\left(U x_{2}+W h_{1}\right) \\\hat{y}_{2}=f\left(V h_{2}\right)\end{array}\right. {h2=f(Ux2+Wh1)y^2=f(Vh2)
{ h 3 = f ( U x 3 + W h 2 ) y ^ 3 = f ( V h 1 ) \left\{\begin{array}{l}h_{3}=f\left(U x_{3}+W h_{2}\right) \\\hat{y}_{3}=f\left(V h_{1}\right)\end{array}\right. {h3=f(Ux3+Wh2)y^3=f(Vh1)

针对 t = 3 t=3 t=3时刻,求U,V,W的梯度(偏导),使用链式法则得到:

∂ L 3 ∂ V = ∂ L 3 y ^ 3 × ∂ y ^ 3 ∂ V \frac{\partial L_3}{\partial V} = \frac{\partial L_3}{\hat{y}_{3}} \times \frac{\partial \hat{y}_{3}}{\partial V} VL3=y^3L3×Vy^3

∂ L 3 ∂ W = ∂ L 3 ∂ y ^ 3 × ∂ y ^ 3 ∂ h 3 × ∂ h 3 ∂ W + ∂ L 3 ∂ y ^ 3 × ∂ y ^ 3 ∂ h 3 × ∂ h 3 ∂ h 2 × ∂ h 2 ∂ W + ∂ L 3 ∂ y ^ 3 × ∂ y ^ 3 ∂ h 3 × ∂ h 3 ∂ h 2 × ∂ h 2 ∂ h 1 × ∂ h 1 ∂ W \frac{\partial L_{3}}{\partial W}=\frac{\partial L_{3}}{\partial \hat{y}_{3}} \times \frac{\partial \hat{y}_{3}}{\partial h_{3}} \times \frac{\partial h_{3}}{\partial W}+\frac{\partial L_{3}}{\partial \hat{y}_{3}} \times \frac{\partial \hat{y}_{3}}{\partial h_{3}} \times \frac{\partial h_{3}}{\partial h_{2}} \times \frac{\partial h_{2}}{\partial W}+\frac{\partial L_{3}}{\partial \hat{y}_{3}} \times \frac{\partial \hat{y}_{3}}{\partial h_{3}} \times\frac{\partial h_{3}}{\partial h_{2}} \times \frac{\partial h_{2}}{\partial h_{1}} \times \frac{\partial h_{1}}{\partial W} WL3=y^3L3×h3y^3×Wh3+y^3L3×h3y^3×h2h3×Wh2+y^3L3×h3y^3×h2h3×h1h2×Wh1

∂ L 3 ∂ U = ∂ L 3 ∂ y ^ 3 × ∂ y ^ 3 ∂ h 3 × ∂ h 3 ∂ U + ∂ L 3 ∂ y ^ 3 × ∂ y ^ 3 ∂ h 3 × ∂ h 3 ∂ h 2 × ∂ h 2 ∂ U + ∂ L 3 ∂ y ^ 3 × ∂ y ^ 3 ∂ h 3 × ∂ h 3 ∂ h 2 × ∂ h 2 ∂ h 1 × ∂ h 1 ∂ U \frac{\partial L_{3}}{\partial U}=\frac{\partial L_{3}}{\partial \hat{y}_{3}} \times \frac{\partial \hat{y}_{3}}{\partial h_{3}} \times \frac{\partial h_{3}}{\partial U}+\frac{\partial L_{3}}{\partial \hat{y}_{3}} \times \frac{\partial \hat{y}_{3}}{\partial h_{3}} \times \frac{\partial h_{3}}{\partial h_{2}} \times \frac{\partial h_{2}}{\partial U}+\frac{\partial L_{3}}{\partial \hat{y}_{3}} \times \frac{\partial \hat{y}_{3}}{\partial h_{3}} \times\frac{\partial h_{3}}{\partial h_{2}} \times \frac{\partial h_{2}}{\partial h_{1}} \times \frac{\partial h_{1}}{\partial U} UL3=y^3L3×h3y^3×Uh3+y^3L3×h3y^3×h2h3×Uh2+y^3L3×h3y^3×h2h3×h1h2×Uh1

其实这个时候我们就可以看出,W和U两个参数的需要追溯之前的历史数据,参数V只需关注目前

所以,我们可以根据t3时刻的偏导,来计算任意时刻对U,V,W的偏导

对于V的偏导

对于V的偏导,我们直接将3替换成t即可:
∂ L t ∂ V = ∂ L t y ^ t × ∂ y ^ t ∂ V \frac{\partial L_t}{\partial V} = \frac{\partial L_t}{\hat{y}_{t}} \times \frac{\partial \hat{y}_{t}}{\partial V} VLt=y^tLt×Vy^t

对于W的偏导

对于W的偏导,在 t = 3 t=3 t=3的时刻有三项,那么对应的在T时刻就有T项

∂ L t ∂ W = ∑ k = 1 t ∂ L t ∂ y t ^ × ∂ y t ^ ∂ h t × ∂ h t ∂ h k × ∂ h k W \frac{\partial L_{t}}{\partial W}= \sum_{k=1}^{t} \frac{\partial L_t}{\partial \hat{y_t} } \times \frac{\partial \hat{y_t} }{\partial h_t} \times \frac{\partial h_t}{\partial h_k} \times \frac{\partial h_k}{W} WLt=k=1tyt^Lt×htyt^×hkht×Whk

其中的 ∂ h t ∂ h k \frac{\partial h_t}{\partial h_k} hkht,我们可以进行展开:

例如在 k = 1 k=1 k=1时, ∂ h 3 ∂ h 1 = ∂ h 3 ∂ h 2 × ∂ h 2 ∂ h 1 \frac{\partial h_3}{\partial h_1} = \frac{\partial h_3}{\partial h_2} \times \frac{\partial h_2}{\partial h_1} h1h3=h2h3×h1h2

所以我们推导得到以下式子:
∂ h t ∂ h k = ∂ h t ∂ h k × ∂ h t − 1 ∂ h t − 2 × . . . × ∂ h t − k + 1 ∂ h k \frac{\partial h_t}{\partial h_k} = \frac{\partial h_t}{\partial h_k} \times \frac{\partial h_{t-1}}{\partial h_{t-2}} \times ... \times \frac{\partial h_{t-k+1}}{\partial h_{k}} hkht=hkht×ht2ht1×...×hkhtk+1

也就是等于:
∂ h t ∂ h k = ∏ i = k + 1 t ∂ h i ∂ h i − 1 \frac{\partial h_t}{\partial h_k} = \prod_{i = k+1}^{t} \frac{\partial h_i}{\partial h_{i-1}} hkht=i=k+1thi1hi

所以,
∂ L t ∂ W = ∑ k = 1 t ∂ L t ∂ y t ^ × ∂ y t ^ ∂ h t × ( ∏ i = k + 1 t ∂ h i ∂ h i − 1 ) × ∂ h k W \frac{\partial L_{t}}{\partial W}= \sum_{k=1}^{t} \frac{\partial L_t}{\partial \hat{y_t} } \times \frac{\partial \hat{y_t} }{\partial h_t} \times (\prod_{i = k+1}^{t} \frac{\partial h_i}{\partial h_{i-1}} ) \times \frac{\partial h_k}{W} WLt=k=1tyt^Lt×htyt^×(i=k+1thi1hi)×Whk

对于U的偏导

同样的,我们也可以得到对于U的偏导

∂ L t ∂ U = ∑ k = 1 t ∂ L t ∂ y t ^ × ∂ y t ^ ∂ h t × ( ∏ i = k + 1 t ∂ h i ∂ h i − 1 ) × ∂ h k U \frac{\partial L_{t}}{\partial U}= \sum_{k=1}^{t} \frac{\partial L_t}{\partial \hat{y_t} } \times \frac{\partial \hat{y_t} }{\partial h_t} \times (\prod_{i = k+1}^{t} \frac{\partial h_i}{\partial h_{i-1}} ) \times \frac{\partial h_k}{U} ULt=k=1tyt^Lt×htyt^×(i=k+1thi1hi)×Uhk

为什么U也是这样的链式求导?
h t = f ( U X t + W h t − 1 ) h_t = f(UX_t + Wh_{t-1}) ht=f(UXt+Wht1)
U也是通过链式法则求导的,因为隐藏状态 h t h_t ht是由 U U U X t X_t Xt h t − 1 h_{t-1} ht1共同决定的。因此,当我们计算损失函数关于U的偏导数时,需要考虑 h t h_t ht U U U的影响,而 h t h_t ht又依赖于 h t − 1 h_{t-1} ht1,因此需要使用链式法则进行求导。

当前我们得到了是t时刻的导数,现在我们需要推广到整个网络中的损失值对U,V,W的偏导

总的损失值

因为和的导数等于导数,所以我们可以直接将 L = ∑ i = 1 T L t L = \sum_{i=1}^{T} L_t L=i=1TLt前面的求和符号提出来
所以有,
∂ L ∂ W = ∑ i = 1 T ∂ L t ∂ W \frac{\partial L}{\partial W}= \sum_{i=1}^{T} \frac{\partial L_t}{\partial W} WL=i=1TWLt

现在我们只需要将前面求得的t时刻的带入即可,
∂ L ∂ W = ∑ i = 1 T ∑ k = 1 t ∂ L t ∂ y t ^ × ∂ y t ^ ∂ h t × ( ∏ i = k + 1 t ∂ h i ∂ h i − 1 ) × ∂ h k W \frac{\partial L}{\partial W}= \sum_{i=1}^{T}\sum_{k=1}^{t} \frac{\partial L_t}{\partial \hat{y_t} } \times \frac{\partial \hat{y_t} }{\partial h_t} \times (\prod_{i = k+1}^{t} \frac{\partial h_i}{\partial h_{i-1}} ) \times \frac{\partial h_k}{W} WL=i=1Tk=1tyt^Lt×htyt^×(i=k+1thi1hi)×Whk

同样的,对于U,我们得到:
∂ L ∂ U = ∑ i = 1 T ∑ k = 1 t ∂ L t ∂ y t ^ × ∂ y t ^ ∂ h t × ( ∏ i = k + 1 t ∂ h i ∂ h i − 1 ) × ∂ h k U \frac{\partial L}{\partial U}= \sum_{i=1}^{T}\sum_{k=1}^{t} \frac{\partial L_t}{\partial \hat{y_t} } \times \frac{\partial \hat{y_t} }{\partial h_t} \times (\prod_{i = k+1}^{t} \frac{\partial h_i}{\partial h_{i-1}} ) \times \frac{\partial h_k}{U} UL=i=1Tk=1tyt^Lt×htyt^×(i=k+1thi1hi)×Uhk

对于V,我们得到:

∂ L ∂ V = ∑ i = 1 T ∂ L t y ^ t × ∂ y ^ t ∂ V \frac{\partial L}{\partial V}= \sum_{i=1}^{T}\frac{\partial L_t}{\hat{y}_{t}} \times \frac{\partial \hat{y}_{t}}{\partial V} VL=i=1Ty^tLt×Vy^t

梯度爆炸和梯度消失问题

在W和U中,存在一个连乘 ∏ i = k + 1 t ∂ h i ∂ h i − 1 \prod_{i = k+1}^{t} \frac{\partial h_i}{\partial h_{i-1}} i=k+1thi1hi;也就是说,会出现指数级别的问题;

如果 ∂ h i ∂ h i − 1 > 1 \frac{\partial h_i}{\partial h_{i-1}} > 1 hi1hi>1的话,那么连乘的结果可能会快速增长,导致梯度爆炸。
在这里插入图片描述

如果 ∂ h i ∂ h i − 1 < 1 \frac{\partial h_i}{\partial h_{i-1}} < 1 hi1hi<1的话,连乘的结果会迅速衰减到零,导致梯度消失
在这里插入图片描述
我们来求解一下关于 ∂ h i ∂ h i − 1 \frac{\partial h_i}{\partial h_{i-1}} hi1hi数学上的表示:

因为 h t = f ( U X t + W h t − 1 ) h_t = f(UX_t + Wh_{t-1}) ht=f(UXt+Wht1),所以我们可以得到
∂ h i ∂ h i − 1 = f ′ × W \frac{\partial h_i}{\partial h_{i-1}} = f'\times W hi1hi=f×W

因为 f ′ ∈ [ 0 , 0.25 ] f'∈[0,0.25] f[0,0.25](假设为Sigmoid函数),所以说

  • 如果 W < 4 W < 4 W<4,那么连乘很多次后,导致梯度消失
  • 如果 W > 4 W > 4 W>4,那么连乘很多次后,导致梯度爆炸

为什么 f ′ ∈ [ 0 , 0.25 ] f' \in [0, 0.25] f[0,0.25]

f f f 是Sigmoid函数,其导数 f ′ f' f 的取值范围在0到0.25之间。

Sigmoid函数的导数表达式为 f ′ ( x ) = f ( x ) ( 1 − f ( x ) ) f'(x) = f(x)(1-f(x)) f(x)=f(x)(1f(x)),其中 f ( x ) f(x) f(x) 的取值范围在0到1之间。因此, f ′ ( x ) f'(x) f(x) 的最大值为 0.25 0.25 0.25,在 x = 0.5 x = 0.5 x=0.5 时取得。
如图所示
在这里插入图片描述

解决梯度消失和梯度爆炸的方法

为了缓解梯度消失和梯度爆炸问题,可以采用以下几种常见的方法:

  1. 梯度裁剪(Gradient Clipping)

    • 将梯度的绝对值限制在某个阈值范围内,防止梯度爆炸。
    • 例如,当梯度超过某个阈值时,将其裁剪到这个阈值。
  2. 正则化方法

    • 使用L2正则化(权重衰减)防止过度活跃的神经元。
    • 增加权重更新时的惩罚项,控制权重值不至于过大。
  3. 批归一化(Batch Normalization)

    • 对每个时间步的隐藏状态进行归一化,稳定训练过程。
    • 通过归一化,控制每个时间步的输出范围,防止梯度过大或过小。
  4. 调整激活函数

    • 选择适当的激活函数(如ReLU、Leaky ReLU等),防止梯度消失和爆炸。
    • 例如,Leaky ReLU 在负区间也有非零导数,避免了完全的梯度消失问题。

为什么很小的梯度无法更新权重并导致无法捕捉长期依赖关系?

当梯度非常小时,反向传播的权重更新公式:

Δ W = − η ⋅ ∂ L ∂ W \Delta W = -\eta \cdot \frac{\partial L}{\partial W} ΔW=ηWL

梯度项 ∂ L ∂ W \frac{\partial \mathcal{L}}{\partial W} WL 会非常小。这里, η \eta η 是学习率。当梯度接近零时,权重更新 Δ W \Delta W ΔW 也会接近零。这意味着神经网络的权重几乎不会发生变化,导致模型无法从训练数据中学习到有用的信息,从而无法有效捕捉长期依赖关系。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于EasyX的贪吃蛇小游戏 - C语言

游戏基本功能演示&#xff1a; 1.主菜单界面 2.自定难度界面 在这里可以自行设定游戏的难度&#xff0c;包括蛇的移动速度&#xff0c;初始节数&#xff0c;以及默认模式&#xff0c;参考线&#xff08;网格&#xff09;。这些设定的数据都会在右上角的游戏属性栏中实时显示。…

二叉树的算法题目

二叉树的遍历题目 二叉树遍历一般包含三种分别为&#xff1a;根左右、左根右、左右根&#xff08;又称为前序遍历、中序遍历、后序遍历&#xff09; 方法一&#xff1a;使用递归遍历 方法二&#xff1a;使用迭代使用栈 我们以左根右&#xff08;中序遍历&…

Spring系列-SpringMvc父子容器启动原理解析

1、Spring整合SpringMVC 特性&#xff1a; 说到Spring整合SpringMVC唯一的体现就是父子容器&#xff1a; 通常我们会设置父容器&#xff08;Spring&#xff09;管理Service、Dao层的Bean, 子容器(SpringMVC)管理Controller的Bean .子容器可以访问父容器的Bean, 父容器无法访…

【PCB]射频电路pcb设计

学习改变命运&#xff0c;技能成就未来&#xff01;❤~~ 1射频信号的基础知识及工作原理介绍 射频的基础知识介绍 2射频板PCB的布局要求 3射频板布局要求 4屏蔽帐设计 5射频板的层叠阻抗设计 6射频板的PCB布线原则 7射频板的PCB布线要求 8射频板的设计实战

王道408数据结构CH1_绪论

概述 1.数据结构 1.1 数据结构三要素 逻辑结构 存储结构 顺序存储、链式存储、索引存储、散列存储 数据的运算

做自媒体素材哪里找?做自媒体必备的几个高质量素材网站分享

在自媒体的世界里&#xff0c;内容是王道。无论是视频还是文章&#xff0c;优秀的自媒体作品都需要有力的内容和高质量的素材作支撑。今天&#xff0c;我为大家整理了一些优质的素材网站&#xff0c;帮助每一位自媒体创作者&#xff0c;无论新手还是老手&#xff0c;都能找到适…

鸿蒙状态管理-@Builder自定义构建函数

Builder 将重复使用的UI元素抽象成一个方法 在build方法里调用 使其成为 自定义构建函数 Entry Component struct BuilderCase {build() {Column(){Row(){Text("西游记").fontSize(20)}.justifyContent(FlexAlign.Center).backgroundColor("#f3f4f5").hei…

Etcd Raft架构设计和源码剖析2:数据流

Etcd Raft架构设计和源码剖析2&#xff1a;数据流 | Go语言充电站 前言 之前看到一幅描述etcd raft的流程图&#xff0c;感觉非常直观&#xff0c;但和自己看源码的又有些不同&#xff0c;所以自己模仿着画了一下&#xff0c;再介绍一下。 下图从左到右依次分为4个部分&…

如何检查网站文件是否有病毒

本周有一个客户&#xff0c;购买Hostease的主机&#xff0c; 客户购买的是Linux虚拟主机&#xff0c;带cPanel面板的。询问我们的在线客服&#xff0c;他想检查下他的网站程序是否有病毒文件。Hostease虚拟主机附带病毒扫描软件功能&#xff0c;可以协助检查网站程序是否有病毒…

HTML静态网页成品作业(HTML+CSS)—— 节日端午节介绍网页(5个页面)

&#x1f389;不定期分享源码&#xff0c;关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 &#x1f3f7;️本套采用HTMLCSS&#xff0c;未使用Javacsript代码&#xff0c;共有5个页面。 二、作品演示 三、代…

算法训练营第四十六天 | 卡码网52 携带研究材料、LeetCode 518 零钱兑换II、LeetCode 377 组合总和IV

写在前面 这次算法训练营题目&#xff0c;其实完全是按照代码随想录一路跟着来的&#xff0c;上面也有更好的、讲得更清楚的题解&#xff0c;有需要的小伙伴可以去那里看。 我这里是之前已经大体刷过一遍&#xff0c;为了应对有可能会考到的面试题&#xff0c;现在在跟着一个专…

MySQL—多表查询—外连接

一、引言 学到内连接&#xff0c;它是查询的数据两张表交集的部分。而接下来看看外连接。 外连接查询语法&#xff1a;&#xff08;分为两种&#xff09; 1、左外连接 语法结构&#xff1a; 表1 LEFT [OUTER] JOIN 表2 ON 条件 ...; ( ... left out join on ...) 注意&#x…

kafka-消费者服务搭建配置简单消费(SpringBoot整合Kafka)

文章目录 1、使用efak 创建 主题 my_topic1 并建立6个分区并给每个分区建立3个副本2、创建生产者发送消息3、application.yml配置4、创建消费者监听器5、创建SpringBoot启动类6、屏蔽 kafka debug 日志 logback.xml7、引入spring-kafka依赖 1、使用efak 创建 主题 my_topic1 并…

awfawfaw

c语言中的小小白-CSDN博客c语言中的小小白关注算法,c,c语言,贪心算法,链表,mysql,动态规划,后端,线性回归,数据结构,排序算法领域.https://blog.csdn.net/bhbcdxb123?spm1001.2014.3001.5343 给大家分享一句我很喜欢我话&#xff1a; 知不足而奋进&#xff0c;望远山而前行&am…

12- Redis 中的 链表 数据结构

Redis 的 List 对象的底层实现之一就是链表。C 语言本身没有链表这个数据结构&#xff0c;所以 Redis 自己设计了一个链表数据结构。 1. 链表节点结构设计 先来看看【链表节点】结构的样子&#xff1a; typedef struct listNode {//前置节点struct listNode *prev;//后置节点…

【AI大模型】Transformers大模型库(二):AutoModelForCausalLM

目录​​​​​​​ 一、引言 二、AutoModelForCausalLM 2.1 概述 2.2 主要功能 2.3 代码示例 三、总结 一、引言 这里的Transformers指的是huggingface开发的大模型库&#xff0c;为huggingface上数以万计的预训练大模型提供预测、训练等服务。 &#x1f917; Transfo…

K8s service 底层逻辑

文章目录 K8s service 底层逻辑Kube-proxy 代理模式Service 请求情况Service-Iptables 模式iptables 规则介绍ClusterIP 模式分析NodePort 模式分析 Service- IPVS 模式 服务发现环境变量CoreDNSCoreDNS 策略ClusterFirst&#xff08;默认DNS策略&#xff09;CluterFirstWithHo…

Python学习从0开始——Kaggle机器学习004总结2

Python学习从0开始——Kaggle机器学习004总结2 一、缺失值二、分类变量2.1介绍2.2实现1.获取训练数据中所有分类变量的列表。2.比较每种方法方法1(删除分类变量)方法2(序数编码)方法3独热编码 三、管道3.1介绍3.2实现步骤1:定义预处理步骤步骤2:定义模型步骤3:创建和评估管道 四…

网络安全快速入门(十五)(中)用户的文件属性及用户相关文件详解

15.4 序言 我们之前已经了解了关于用户管理的一些基础命令&#xff0c;本章节我们就来了解一下关于文件权限的一些小知识以及基于某些文件来手动创建一个用户&#xff0c;话不多说&#xff0c;我们开始吧&#xff01; 15.5 文件权限 在linux中&#xff0c;文件都是通过查看属主…

深入解析ArrayList是如何实现自动扩容的?【源码深度解析】

一 、分析ArrayList扩容源码 通过在 &#xff08;JDK 6 及更低版本中&#xff09;API我们知道&#xff0c;调用无参构造创建的ArrayList集合&#xff0c;初始容量为10 接下来我们深入源码&#xff0c;探究当时作者是怎么构思的 借助Debug工具&#xff0c;一步一步进入 上图看到…