当前位置: 首页 > news >正文

11.28深度学习_bp算法

七、BP算法

多层神经网络的学习能力比单层网络强得多。想要训练多层网络,需要更强大的学习算法。误差反向传播算法(Back Propagation)是其中最杰出的代表,它是目前最成功的神经网络学习算法。现实任务使用神经网络时,大多是在使用 BP 算法进行训练,值得指出的是 BP 算法不仅可用于多层前馈神经网络,还可以用于其他类型的神经网络。通常说 BP 网络时,一般是指用 BP 算法训练的多层前馈神经网络。

误差反向传播算法(BP)的基本步骤:

  1. 前向传播:正向计算得到预测值。
  2. 计算损失:通过损失函数 L ( y pred , y true ) L(y_{\text{pred}}, y_{\text{true}}) L(ypred,ytrue) 计算预测值和真实值的差距。
  3. 梯度计算:反向传播的核心是计算损失函数 L L L 对每个权重和偏置的梯度。
  4. 更新参数:一旦得到每层梯度,就可以使用梯度下降算法来更新每层的权重和偏置,使得损失逐渐减小。
  5. 迭代训练:将前向传播、梯度计算、参数更新的步骤重复多次,直到损失函数收敛或达到预定的停止条件。

1. 前向传播

前向传播(Forward Propagation)把输入数据经过各层神经元的运算并逐层向前传输,一直到输出层为止。

下面是一个简单的三层神经网络(输入层、隐藏层、输出层)前向传播的基本步骤分析。

1.1输入层到隐藏层

给定输入 x x x 和权重矩阵 W 1 W_1 W1 及偏置向量 b 1 b_1 b1,隐藏层的输出(激活值)计算如下:
z ( 1 ) = W 1 ⋅ x + b 1 z^{(1)} = W_1 \cdot x + b_1 z(1)=W1x+b1
z ( 1 ) z^{(1)} z(1) 通过激活函数 σ \sigma σ进行激活:
a ( 1 ) = σ ( z ( 1 ) ) a^{(1)} = \sigma(z^{(1)}) a(1)=σ(z(1))

1.2隐藏层到输出层

隐藏层的输出 a ( 1 ) a^{(1)} a(1) 通过输出层的权重矩阵 W 2 W_2 W2和偏置 b 2 b_2 b2 生成最终的输出:
z ( 2 ) = W 2 ⋅ a ( 1 ) + b 2 z^{(2)} = W_2 \cdot a^{(1)} + b_2 z(2)=W2a(1)+b2
输出层的激活值 a ( 2 ) a^{(2)} a(2) 是最终的预测结果:
y pred = a ( 2 ) = σ ( z ( 2 ) ) y_{\text{pred}} = a^{(2)} = \sigma(z^{(2)}) ypred=a(2)=σ(z(2))

前向传播的主要作用是:

  1. 计算神经网络的输出结果,用于预测或计算损失。
  2. 在反向传播中使用,通过计算损失函数相对于每个参数的梯度来优化网络。

2. 反向传播

反向传播(Back Propagation,简称BP)通过计算损失函数相对于每个参数的梯度来调整权重,使模型在训练数据上的表现逐渐优化。反向传播结合了链式求导法则和梯度下降算法,是神经网络模型训练过程中更新参数的关键步骤。

2.1 原理

利用链式求导法则对每一层进行求导,直到求出输入层x的导数,然后利用导数值进行梯度更新

2.2. 链式法则

链式求导法则(Chain Rule)是微积分中的一个重要法则,用于求复合函数的导数。在深度学习中,链式法则是反向传播算法的基础,这样就可以通过分层的计算求得损失函数相对于每个参数的梯度。以下面的复合函数为例:
f ( x ) = 1 1 + e − ( w x + b ) \mathrm{f(x)=\frac{1}{1+e^{-(wx+b)}}} f(x)=1+e(wx+b)1
其中 x x x 是输入数据, w w w 是权重, b b b 是偏置。

2.2.1 函数分解

可以将该复合函数分解为:

函数导数我们假设 w=0, b=0, x=1
h 1 = x × w h_1 = x \times w h1=x×w ∂ h 1 ∂ w = x , ∂ h 1 ∂ x = w \frac{\partial h_1}{\partial w} = x, \quad \frac{\partial h_1}{\partial x} = w wh1=x,xh1=w h 1 = x × w = 0 h_1 = x \times w = 0 h1=x×w=0
h 2 = h 1 + b h_2 = h_1 + b h2=h1+b ∂ h 2 ∂ h 1 = 1 , ∂ h 2 ∂ b = 1 \frac{\partial h_2}{\partial h_1} = 1, \quad \frac{\partial h_2}{\partial b} = 1 h1h2=1,bh2=1 h 2 = h 1 + b = 0 + 0 = 0 h_2 = h_1 + b = 0 + 0 = 0 h2=h1+b=0+0=0
h 3 = h 2 × − 1 h_3 = h_2 \times -1 h3=h2×1 ∂ h 3 ∂ h 2 = − 1 \frac{\partial h_3}{\partial h_2} = -1 h2h3=1 h 3 = h 2 × − 1 = 0 × − 1 = 0 h_3 = h_2 \times -1=0 \times -1 = 0 h3=h2×1=0×1=0
h 4 = e x p ( h 3 ) h_4 = exp(h_3) h4=exp(h3) ∂ h 4 ∂ h 3 = e x p ( h 3 ) \frac{\partial h_4}{\partial h_3} = exp(h_3) h3h4=exp(h3) h 4 = e x p ( h 3 ) = e x p ( 0 ) = 1 h_4 = exp(h_3) = exp(0)=1 h4=exp(h3)=exp(0)=1
h 5 = h 4 + 1 h_5 = h_4 + 1 h5=h4+1 ∂ h 5 ∂ h 4 = 1 \frac{\partial h_5}{\partial h_4} = 1 h4h5=1 h 5 = h 4 + 1 = 1 + 1 = 2 h_5 = h_4 + 1 = 1 + 1 = 2 h5=h4+1=1+1=2
h 6 = 1 / h 5 h_6 = 1/h_5 h6=1/h5 ∂ h 6 ∂ h 5 = − 1 h 5 2 \frac{\partial h_6}{\partial h_5} = -\frac{1}{h^2_5} h5h6=h521 h 6 = 1 / h 5 = 1 / 2 = 0.5 h_6 = 1/h_5 = 1 / 2 = 0.5 h6=1/h5=1/2=0.5
2.2.2 链式求导

复合函数数学过程如下:
∂ f ( x ; w , b ) ∂ w = ∂ f ( x ; w , b ) ∂ h 6 ∂ h 6 ∂ h 5 ∂ h 5 ∂ h 4 ∂ h 4 ∂ h 3 ∂ h 3 ∂ h 2 ∂ h 2 ∂ h 1 ∂ h 1 ∂ w ∂ f ( x ; w , b ) ∂ b = ∂ f ( x ; w , b ) ∂ h 6 ∂ h 6 ∂ h 5 ∂ h 5 ∂ h 4 ∂ h 4 ∂ h 3 ∂ h 3 ∂ h 2 ∂ h 2 ∂ b \frac{\partial f(x;w,b)}{\partial w}=\frac{\partial f(x;w,b)}{\partial h_6}\frac{\partial h_6}{\partial h_5}\frac{\partial h_5}{\partial h_4}\frac{\partial h_4}{\partial h_3}\frac{\partial h_3}{\partial h_2}\frac{\partial h_2}{\partial h_1}\frac{\partial h_1}{\partial w} \\ \\ \frac{\partial f(x;w,b)}{\partial b}=\frac{\partial f(x;w,b)}{\partial h_6}\frac{\partial h_6}{\partial h_5}\frac{\partial h_5}{\partial h_4}\frac{\partial h_4}{\partial h_3}\frac{\partial h_3}{\partial h_2}\frac{\partial h_2}{\partial b} wf(x;w,b)=h6f(x;w,b)h5h6h4h5h3h4h2h3h1h2wh1bf(x;w,b)=h6f(x;w,b)h5h6h4h5h3h4h2h3bh2
可以得到:
∂ f ( x ; w , b ) ∂ w ∣ x = 1 , w = 0 , b = 0 = ∂ f ( x ; w , b ) ∂ h 6 ∂ h 6 ∂ h 5 ∂ h 5 ∂ h 4 ∂ h 4 ∂ h 3 ∂ h 3 ∂ h 2 ∂ h 2 ∂ h 1 ∂ h 1 ∂ w = 1 × − 0.25 × 1 × 1 × − 1 × 1 × 1 = 0.25. \begin{aligned} \frac{\partial f(x;w,b)}{\partial w}|_{x=1,w=0,b=0}& =\frac{\partial f(x;w,b)}{\partial h_6}\frac{\partial h_6}{\partial h_5}\frac{\partial h_5}{\partial h_4}\frac{\partial h_4}{\partial h_3}\frac{\partial h_3}{\partial h_2}\frac{\partial h_2}{\partial h_1}\frac{\partial h_1}{\partial w} \\ &=1\times-0.25\times1\times1\times-1\times1\times1 \\ &=0.25. \end{aligned} wf(x;w,b)x=1,w=0,b=0=h6f(x;w,b)h5h6h4h5h3h4h2h3h1h2wh1=1×0.25×1×1×1×1×1=0.25.

2.2.3 代码实现

我们通过代码来实现以上过程:

import torch
import torch.nn as nndef test006():x = torch.tensor(1.0)w = torch.tensor(0.0, requires_grad=True)b = torch.tensor(0.0, requires_grad=True)# 计算函数y = (torch.exp(-(w * x + b)) + 1) ** -1# 自动微分y.backward()# 梯度打印print(w.grad)if __name__ == "__main__":test006()

打印结果:

tensor(0.2500)

2.4 重要性

反向传播算法极大地提高了多层神经网络训练的效率,使得训练深度模型成为可能。通过链式法则逐层计算梯度,反向传播可以有效地处理复杂的网络结构,确保每一层的参数都能得到合理的调整。

2.5 案例助解

这里我们通过一个实际的案例,去理解反向传播整个过程~

2.5.1 数据准备

整体网络结构及神经元数据和权重参数如下图所示:

数据解释如下:

  • i 1 = 0.05 , i 2 = 0.10 i_1=0.05, i_2=0.10 i1=0.05i2=0.10 代表输入层输入数据的2个特征;
  • w 1 = 0.15 , w 2 = 0.20 w_1=0.15, w_2=0.20 w1=0.15w2=0.20代表的是输入数据映射到 h 1 h_1 h1的权重参数;
  • w 3 = 0.25 , w 4 = 0.30 w_3=0.25, w_4=0.30 w3=0.25w4=0.30代表的是输入数据映射到 h 2 h_2 h2的权重参数;
  • b 1 = 0.35 , b 2 = 0.60 b1=0.35,b2=0.60 b1=0.35b2=0.60分别代表输入层到隐藏层、隐藏层到输出层的偏执;
  • w 5 = 0.40 , w 6 = 0.45 w_5=0.40, w_6=0.45 w5=0.40w6=0.45代表的是隐藏层的神经元映射到 o 1 o_1 o1的权重参数;
  • w 7 = 0.50 , w 8 = 0.55 w_7=0.50, w_8=0.55 w7=0.50w8=0.55代表的是隐藏层的神经元映射到 o 2 o_2 o2的权重参数;
  • o 1 o_1 o1下面标注的 0.01 0.01 0.01表示target为 0.01 0.01 0.01 o 2 o_2 o2下面标注的 0.99 0.99 0.99表示target为 0.99 0.99 0.99
2.5.2 神经元计算

所以,我们可以得到如下数据:

计算h1的相关数据:
h 1 = w 1 ∗ i 1 + w 2 ∗ i 2 + b 1 = 0.15 ∗ 0.05 + 0.20 ∗ 0.10 + 0.35 = 0.3775 k 1 = s i g m o i d ( h 1 ) = s i g m o i d ( 0.3775 ) = 0.5933 \mathrm{h}_{1}=\mathrm{w}_{1}*\mathrm{i}_{1}+\mathrm{w}_{2}*\mathrm{i}_{2}+\mathrm{b}_{1}\quad =0.15 * 0.05 + 0.20 * 0.10 + 0.35 =0.3775 \\ k_{1}=sigmoid(h1)=sigmoid(0.3775)=0.5933 h1=w1i1+w2i2+b1=0.150.05+0.200.10+0.35=0.3775k1=sigmoid(h1)=sigmoid(0.3775)=0.5933
计算h2的相关数据:
h 2 = w 3 ∗ i 1 + w 4 ∗ i 2 + b 1 = 0.25 ∗ 0.05 + 0.30 ∗ 0.10 + 0.35 = 0.3925 k 2 = s i g m o i d ( h 2 ) = s i g m o i d ( 0.3925 ) = 0.5969 \mathrm{h}_{2}=\mathrm{w}_{3}*\mathrm{i}_{1}+\mathrm{w}_{4}*\mathrm{i}_{2}+\mathrm{b}_{1}\quad =0.25 * 0.05 + 0.30 * 0.10 + 0.35 =0.3925 \\ k_{2}=sigmoid(h2)=sigmoid(0.3925)=0.5969 h2=w3i1+w4i2+b1=0.250.05+0.300.10+0.35=0.3925k2=sigmoid(h2)=sigmoid(0.3925)=0.5969
计算o1的相关数据:
o 1 = w 5 ∗ k 1 + w 6 ∗ k 2 + b 2 = 0.40 ∗ 0.5933 + 0.45 ∗ 0.5969 + 0.60 = 1.1059 m 1 = s i g m o i d ( o 1 ) = s i g m o i d ( 1.1059 ) = 0.7514 \mathrm{o}_{1}=\mathrm{w}_{5}*\mathrm{k}_{1}+\mathrm{w}_{6}*\mathrm{k}_{2}+\mathrm{b}_{2}\quad =0.40 * 0.5933 + 0.45 * 0.5969 + 0.60 =1.1059 \\ m_{1}=sigmoid(o1)=sigmoid(1.1059)=0.7514 o1=w5k1+w6k2+b2=0.400.5933+0.450.5969+0.60=1.1059m1=sigmoid(o1)=sigmoid(1.1059)=0.7514
计算o2的相关数据:
o 2 = w 7 ∗ k 1 + w 8 ∗ k 2 + b 2 = 0.50 ∗ 0.5933 + 0.55 ∗ 0.5969 + 0.60 = 1.2249 m 2 = s i g m o i d ( o 2 ) = s i g m o i d ( 1.2249 ) = 0.7729 \mathrm{o}_{2}=\mathrm{w}_{7}*\mathrm{k}_{1}+\mathrm{w}_{8}*\mathrm{k}_{2}+\mathrm{b}_{2}\quad =0.50 * 0.5933 + 0.55 * 0.5969 + 0.60 =1.2249 \\ m_{2}=sigmoid(o2)=sigmoid(1.2249)=0.7729 o2=w7k1+w8k2+b2=0.500.5933+0.550.5969+0.60=1.2249m2=sigmoid(o2)=sigmoid(1.2249)=0.7729
所以,最终的预测结果分别为: 0.7514、0.7729

2.5.3 损失计算

预测值和真实值(target)进行比较计算损失:
M S E L o s s = 1 2 ( ( m 1 − t a r g e t 1 ) 2 + ( ( m 2 − t a r g e t 2 ) 2 ) = 1 2 ( ( 0.7514 − 0.01 ) 2 + ( ( 0.7729 − 0.99 ) 2 ) = 0.2984 MSELoss = \frac{1}{2}((\mathrm{m}_{1}\mathrm{-target}_{1})^{2}+((\mathrm{m}_{2}\mathrm{-target}_{2})^{2}) \\ = \frac{1}{2}((0.7514-0.01)^{2}+((0.7729-0.99)^{2}) =0.2984 MSELoss=21((m1target1)2+((m2target2)2)=21((0.75140.01)2+((0.77290.99)2)=0.2984
得到损失是:0.2984

2.5.4 梯度计算

接下来,我们进行梯度计算和参数更新

计算 w5 权重的梯度
∂ L ∂ w 5 = ∂ L ∂ o 1 1 ∗ ∂ m 1 ∂ o 1 ∗ ∂ o 1 ∂ w 5 = ( m 1 − t a r g e t 1 ) ∗ s i g m o i d ( o 1 ) ∗ ( 1 − s i g m o i d ( o 1 ) ) ∗ k 1 = ( 0.7514 − 0.01 ) ∗ s i g m o i d ( 1.1059 ) ∗ ( 1 − s i g m o i d ( 1.1059 ) ) ∗ 0.5933 = 0.0822 \begin{aligned} \frac{\partial\mathrm{L}}{\partial\mathrm{w}_{5}}& =\frac{\partial\mathrm{L}}{\partial\mathrm{o1}_{1}}*\frac{\partial\mathrm{m}_{1}}{\partial\mathrm{o}_{1}}*\frac{\partial\mathrm{o}_{1}}{\partial\mathrm{w}_{5}} \\ &=(\mathrm{m}_{1}-\mathrm{target}_{1})*\mathrm{sigmoid}(\mathrm{o}_{1})*\left(1-\mathrm{sigmoid}(\mathrm{o}_{1})\right)*\mathrm{k}_{1} \\ &=(0.7514-0.01)*sigmoid(1.1059)*\left(1-sigmoid(1.1059)\right)*0.5933 \\ &=0.0822 \end{aligned} w5L=o11Lo1m1w5o1=(m1target1)sigmoid(o1)(1sigmoid(o1))k1=(0.75140.01)sigmoid(1.1059)(1sigmoid(1.1059))0.5933=0.0822
计算 w7 权重的梯度
∂ L ∂ w 7 = ∂ L ∂ m 2 ∗ ∂ m 2 ∂ o 2 ∗ ∂ o 2 ∂ w 7 = ( m 2 − t a r g e t 2 ) ∗ s i g m o i d ( o 2 ) ∗ ( 1 − s i g m o i d ( o 2 ) ) ∗ k 1 = ( 0.7729 − 0.99 ) ∗ s i g m o i d ( 1.2249 ) ∗ ( 1 − s i g m o i d ( 1.2249 ) ) ∗ 0.5933 = − 0.0226 \begin{aligned} \frac{\partial\mathrm{L}}{\partial\mathrm{w}_7}& =\frac{\partial\mathrm{L}}{\partial\mathrm{m}_2}*\frac{\partial\mathrm{m}_2}{\partial\mathrm{o}_2}*\frac{\partial\mathrm{o}_2}{\partial\mathrm{w}_7} \\ &=(\mathrm{m}_{2}-\mathrm{target}_{2})*\mathrm{sigmoid}(\mathrm{o}_{2})*\left(1-\mathrm{sigmoid}(\mathrm{o}_{2})\right)*\mathrm{k}_{1} \\ &=(0.7729-0.99)*sigmoid(1.2249)*\left(1-sigmoid(1.2249)\right)*0.5933 \\ &=-0.0226 \end{aligned} w7L=m2Lo2m2w7o2=(m2target2)sigmoid(o2)(1sigmoid(o2))k1=(0.77290.99)sigmoid(1.2249)(1sigmoid(1.2249))0.5933=0.0226
计算 w1 权重的梯度

2.5.5 参数更新

现在就可以进行权重更新了:假设学习率是0.5
w 5 = 0.40 − 0.5 ∗ 0.0822 = 0.3589 w 7 = 0.50 + 0.5 ∗ 0.0226 = 0.5113 w 1 = 0.15 − 0.5 ∗ 0.0004 = 0.1498 w_5=0.40-0.5*0.0822=0.3589 \\ w_7=0.50+0.5*0.0226=0.5113 \\ w_1=0.15-0.5*0.0004=0.1498 w5=0.400.50.0822=0.3589w7=0.50+0.50.0226=0.5113w1=0.150.50.0004=0.1498

2.5.6 代码实现

参考代码如下:

import torch
import torch.nn as nn
import torch.optim as optimclass Net(nn.Module):def __init__(self):super(Net, self).__init__()self.linear1 = nn.Linear(2, 2)self.linear2 = nn.Linear(2, 2)# 网络参数初始化self.linear1.weight.data = torch.tensor([[0.15, 0.20], [0.25, 0.30]])self.linear2.weight.data = torch.tensor([[0.40, 0.45], [0.50, 0.55]])self.linear1.bias.data = torch.tensor([0.35, 0.35])self.linear2.bias.data = torch.tensor([0.60, 0.60])def forward(self, x):x = self.linear1(x)x = torch.sigmoid(x)x = self.linear2(x)x = torch.sigmoid(x)return xif __name__ == "__main__":inputs = torch.tensor([[0.05, 0.10]])target = torch.tensor([[0.01, 0.99]])# 获得网络输出值net = Net()output = net(inputs)# 计算误差loss = torch.sum((output - target) ** 2) / 2# 优化方法optimizer = optim.SGD(net.parameters(), lr=0.5)# 梯度清零optimizer.zero_grad()# 反向传播loss.backward()# 打印(w1-w8)观察w5、w7、w1 的梯度值是否与手动计算一致print(net.linear1.weight.grad.data)print(net.linear2.weight.grad.data)#更新梯度optimizer.step()# 打印更新后的网络参数print(net.state_dict())

打印结果:

tensor([[0.0004, 0.0009],[0.0005, 0.0010]])
tensor([[ 0.0822,  0.0827],[-0.0226, -0.0227]])
OrderedDict([('linear1.weight', tensor([[0.1498, 0.1996],[0.2498, 0.2995]])), ('linear1.bias', tensor([0.3456, 0.3450])), ('linear2.weight', tensor([[0.3589, 0.4087],[0.5113, 0.5614]])), ('linear2.bias', tensor([0.5308, 0.6190]))])

另一种写法(常用)

import torch
import torch.nn as nn
import torch.optim as optim
import os
class MyModel(nn.Module):def __init__(self, input_size, output_size):super(MyModel, self).__init__()# 定义网络结构# 输入层到隐藏层self.hidden=nn.Sequential(nn.Linear(input_size, 2),nn.Sigmoid())# 初始化隐藏层权重和偏置(默认自动,这里手动是为了测试案例中运算的数字)self.hidden[0].weight.data = torch.tensor([[0.15, 0.20], [0.25, 0.30]])self.hidden[0].bias.data = torch.tensor([0.35, 0.35])# 隐藏层到输出层self.out = nn.Sequential(nn.Linear(2, output_size),nn.Sigmoid())self.out[0].weight.data = torch.tensor([[0.40, 0.45], [0.50, 0.55]])self.out[0].bias.data = torch.tensor([0.60, 0.60])def forward(self, x):x = self.hidden(x)output = self.out(x)return output
def train(epochs=10):# 模型model = MyModel(input_size=2, output_size=2)# 优化器optimizer = optim.SGD(model.parameters(), lr=0.5)#损失函数criterion = nn.MSELoss()# 输入数据input = torch.tensor([[0.05, 0.10]])target = torch.tensor([[0.01, 0.99]])#前向传播output = model(input)#计算损失loss = criterion(output, target)# 梯度清零optimizer.zero_grad()# 反向传播loss.backward()# 更新权重参数:让损失尽可能小optimizer.step()#更新后的模型参数state_dict=model.state_dict()print("更新后的模型参数:",state_dict)#保存模型参数filepath = os.path.relpath(os.path.join(os.path.dirname(__file__),"weights/model.pth"))def detect():# 加载模型参数filepath = os.path.relpath(os.path.join(os.path.dirname(__file__),"weights/model.pth"))model = MyModel(input_size=2, output_size=2)model.load_state_dict(torch.load(filepath))input = torch.tensor([[0.05, 0.10]])output = model(input)print("预测推理",output)if __name__=="__main__":train()detect()

3. BP之梯度下降

梯度下降算法的目标是找到使损失函数 L ( θ ) L(\theta) L(θ) 最小的参数 θ \theta θ,其核心是沿着损失函数梯度的负方向更新参数,以逐步逼近局部或全局最优解,从而使模型更好地拟合训练数据。

3.1 数学描述

简单回顾下数学知识。

3.1.1 数学公式

w i j n e w = w i j o l d − α ∂ E ∂ w i j w_{ij}^{new}= w_{ij}^{old} - \alpha \frac{\partial E}{\partial w_{ij}} wijnew=wijoldαwijE

其中, α \alpha α是学习率:

  • 学习率太小,每次训练之后的效果太小,增加时间和算力成本。
  • 学习率太大,大概率会跳过最优解,进入无限的训练和震荡中。
  • 解决的方法就是,学习率也需要随着训练的进行而变化。
3.1.2 过程阐述
  1. 初始化参数:随机初始化模型的参数 θ \theta θ,如权重 W W W和偏置 b b b

  2. 计算梯度:损失函数 L ( θ ) L(\theta) L(θ)对参数 θ \theta θ 的梯度 ∇ θ L ( θ ) \nabla_\theta L(\theta) θL(θ),表示损失函数在参数空间的变化率。

  3. 更新参数:按照梯度下降公式更新参数: θ : = θ − α ∇ θ L ( θ ) \theta := \theta - \alpha \nabla_\theta L(\theta) θ:=θαθL(θ),其中, α \alpha α 是学习率,用于控制更新步长。

  4. 迭代更新:重复【计算梯度和更新参数】步骤,直到某个终止条件(如梯度接近0、不再收敛、完成迭代次数等)。

3.2 传统下降方式

根据计算梯度时数据量不同,常见的方式有:

3. 2.1 批量梯度下降

Batch Gradient Descent BGD

  • 特点

    • 每次更新参数时,使用整个训练集来计算梯度。
  • 优点

    • 收敛稳定,能准确地沿着损失函数的真实梯度方向下降。
    • 适用于小型数据集。
  • 缺点

    • 对于大型数据集,计算量巨大,更新速度慢。
    • 需要大量内存来存储整个数据集。
  • 公式
    θ : = θ − α 1 m ∑ i = 1 m ∇ θ L ( θ ; x ( i ) , y ( i ) ) \theta := \theta - \alpha \frac{1}{m} \sum_{i=1}^{m} \nabla_\theta L(\theta; x^{(i)}, y^{(i)}) θ:=θαm1i=1mθL(θ;x(i),y(i))
    其中, m m m 是训练集样本总数, x ( i ) , y ( i ) x^{(i)}, y^{(i)} x(i),y(i)是第 i i i 个样本及其标签。

3.2.2 随机梯度下降

Stochastic Gradient Descent, SGD

  • 特点

    • 每次更新参数时,仅使用一个样本来计算梯度。
  • 优点

    • 更新频率高,计算快,适合大规模数据集。
    • 能够跳出局部最小值,有助于找到全局最优解。
  • 缺点

    • 收敛不稳定,容易震荡,因为每个样本的梯度可能都不完全代表整体方向。
    • 需要较小的学习率来缓解震荡。
  • 公式
    θ : = θ − α ∇ θ L ( θ ; x ( i ) , y ( i ) ) \theta := \theta - \alpha \nabla_\theta L(\theta; x^{(i)}, y^{(i)}) θ:=θαθL(θ;x(i),y(i))

    其中, x ( i ) , y ( i ) x^{(i)}, y^{(i)} x(i),y(i) 是当前随机抽取的样本及其标签。

3.2.3 小批量梯度下降

Mini-batch Gradient Descent MGBD

  • 特点

    • 每次更新参数时,使用一小部分训练集(小批量)来计算梯度。
  • 优点

    • 在计算效率和收敛稳定性之间取得平衡。
    • 能够利用向量化加速计算,适合现代硬件(如GPU)。
  • 缺点

    • 选择适当的批量大小比较困难;批量太小则接近SGD,批量太大则接近批量梯度下降。
    • 通常会根据硬件算力设置为32\64\128\256等2的次方。
  • 公式
    θ : = θ − α 1 b ∑ i = 1 b ∇ θ L ( θ ; x ( i ) , y ( i ) ) \theta := \theta - \alpha \frac{1}{b} \sum_{i=1}^{b} \nabla_\theta L(\theta; x^{(i)}, y^{(i)}) θ:=θαb1i=1bθL(θ;x(i),y(i))
    其中, b b b 是小批量的样本数量,也就是 b a t c h _ s i z e batch\_size batch_size

3.3 存在的问题

  • 收敛速度慢:BGD和MBGD使用固定学习率,太大会导致震荡,太小又收敛缓慢。

  • 局部最小值和鞍点问题:SGD在遇到局部最小值或鞍点时容易停滞,导致模型难以达到全局最优。

  • 训练不稳定:SGD中的噪声容易导致训练过程中不稳定,使得训练陷入震荡或不收敛。

3.4 优化梯度下降方式

传统的梯度下降优化算法中,可能会碰到以下情况:

碰到平缓区域,梯度值较小,参数优化变慢 碰到 “鞍点” ,梯度为 0,参数无法优化 碰到局部最小值 对于这些问题, 出现了一些对梯度下

降算法的优化方法,例如:Momentum、AdaGrad、RMSprop、Adam 等.

3.4.1 指数加权平均

我们最常见的算数平均指的是将所有数加起来除以数的个数,每个数的权重是相同的。

加权平均指的是给每个数赋予不同的权重求得平均数。

移动平均数,指的是计算最近邻的 N 个数来获得平均数。

指数移动加权平均(Exponential Moving Average简称EMA)则是参考各数值,并且各数值的权重都不同,距离越远的数字对平均数计算的贡献就越小(权重较小),距离越近则对平均数的计算贡献就越大(权重越大)。

比如:明天气温怎么样,和昨天气温有很大关系,而和一个月前的气温关系就小一些。

计算公式可以用下面的式子来表示:

其中:

  • St 表示指数加权平均值(EMA);
  • Yt 表示 t 时刻的值;
  • β \beta β 是平滑系数,取值范围为 0 ≤ β < 1 0\leq \beta < 1 0β<1 β \beta β 越接近 1 1 1,表示对历史数据依赖性越高;越接近 0 0 0 则越依赖当前数据。该值越大平均数越平缓

代码演示:

import torch
import matplotlib.pyplot as pltELEMENT_NUMBER = 30# 1. 实际平均温度
def test01():# 固定随机数种子torch.manual_seed(0)# 产生30天的随机温度temperature = torch.randn(size=[ELEMENT_NUMBER,]) * 10print(temperature)# 绘制平均温度days = torch.arange(1, ELEMENT_NUMBER + 1, 1)plt.plot(days, temperature, color='r')plt.scatter(days, temperature)plt.show()# 2. 指数加权平均温度
def test02(beta=0.9):# 固定随机数种子torch.manual_seed(0)# 产生30天的随机温度temperature = torch.randn(size=[ELEMENT_NUMBER,]) * 10print(temperature)exp_weight_avg = []for idx, temp in enumerate(temperature):# 第一个元素的的 EWA 值等于自身if idx == 0:exp_weight_avg.append(temp)continue# 第二个元素的 EWA 值等于上一个 EWA 乘以 β + 当前气温乘以 (1-β)new_temp = exp_weight_avg[-1] * beta + (1 - beta) * tempexp_weight_avg.append(new_temp)days = torch.arange(1, ELEMENT_NUMBER + 1, 1)plt.plot(days, exp_weight_avg, color='r')plt.scatter(days, temperature)plt.show()if __name__ == '__main__':test01()test02(0.5)test02(0.9)

执行效果:

3.4.2 Momentum

a.特点

动量(Momentum)是对梯度下降的优化方法,可以更好地应对梯度变化和梯度消失问题,从而提高训练模型的效率和稳定性。

  • 惯性效应: 该方法加入前面梯度的累积,这种惯性使得算法沿着当前的方向继续更新。如遇到鞍点,也不会因梯度逼近零而停滞。
  • 减少震荡: 该方法平滑了梯度更新,减少在鞍点附近的震荡,帮助优化过程稳定向前推进。
  • 加速收敛: 该方法在优化过程中持续沿着某个方向前进,能够更快地穿越鞍点区域,避免在鞍点附近长时间停留。

b.梯度计算公式

梯度计算公式:Dt = β * St-1 + (1- β) * Dt

  1. St-1 表示历史梯度移动加权平均值
  2. wt 表示当前时刻的梯度值
  3. β 为权重系数

举个例子,假设:权重 β 为 0.9,例如:

第一次梯度值:s1 = d1 = w1 第二次梯度值:s2 = 0.9 + s1 + d2 * 0.1 第三次梯度值:s3 = 0.9 * s2 + d3 * 0.1 第四次梯度值:s4 = 0.9 * s3 + d4 * 0.1- w 表示初始梯度
- d 表示当前轮数计算出的梯度值
- s 表示历史梯度值

梯度下降公式中梯度的计算,就不再是当前时刻 t 的梯度值,而是历史梯度值的指数移动加权平均值。

公式修改为: W t + 1 = W t − α ∗ D t W_{t+1}=W_t-α*D_t Wt+1=WtαDt

c.原理

那么,Monmentum 优化方法是如何一定程度上克服 “平缓”、”鞍点”、”峡谷” 的问题呢?

当处于鞍点位置时,由于当前的梯度为 0,参数无法更新。但是 Momentum 动量梯度下降算法已经在先前积累了一些梯度值,很有可能使得跨过鞍点。

由于 mini-batch 普通的梯度下降算法,每次选取少数的样本梯度确定前进方向,可能会出现震荡,使得训练时间变长。Momentum 使用移动加权平均,平滑了梯度的变化,使得前进方向更加平缓,有利于加快训练过程。一定程度上有利于降低 “峡谷” 问题的影响。

峡谷问题:就是会使得参数更新出现剧烈震荡.

Momentum 算法可以理解为是对梯度值的一种调整,我们知道梯度下降算法中还有一个很重要的学习率,Momentum 并没有学习率进行优化。

d.API

optimizer = optim.SGD(model.parameters(), lr=0.6, momentum=0.9)  # 学习率和动量值可以根据实际情况调整,momentum 参数指定了动量系数,默认为0。动量系数通常设置为 0 到0.5 之间的一个值,但也可以根据具体的应用场景调整

e.总结

  • 动量项更新:利用当前梯度和历史动量来计算新的动量项。
  • 权重参数更新:利用更新后的动量项来调整权重参数。
  • 梯度计算:在每个时间步计算当前的梯度,用于更新动量项和权重参数。

Momentum 算法是对梯度值的平滑调整,但是并没有对梯度下降中的学习率进行优化。

3.4.3 AdaGrad

​ AdaGrad(Adaptive Gradient Algorithm)为每个参数引入独立的学习率,它根据历史梯度的平方和来调整这些学习率,这样就使得参数具有较大的历史梯度的学习率减小,而参数具有较小的历史梯度的学习率保持较大,从而实现更有效的学习。AdaGrad避免了统一学习率的不足,更多用于处理稀疏数据和梯度变化较大的问题。

AdaGrad流程:

  1. 初始化学习率 α、初始化参数 θ、小常数 σ = 1e-6

  2. 初始化梯度累积变量 s = 0

  3. 从训练集中采样 m 个样本的小批量,计算梯度 g

  4. 累积平方梯度 s = s + g ⊙ g,⊙ 表示各个分量相乘

  5. 学习率 α 的计算公式如下:

  6. 参数更新公式如下:

  • 其中:

    • α \alpha α 是全局的初始学习率。

    • σ \sigma σ 是一个非常小的常数,用于避免除零操作(通常取 1 0 − 8 10^{-8} 108)。

    • α s + σ \frac{\alpha}{\sqrt{s }+\sigma} s +σα 是自适应调整后的学习率。

优点

  • 自适应学习率:由于每个参数的学习率是基于其梯度的累积平方和 来动态调整的,这意味着学习率会随着时间步的增加而减少,对梯度较大且变化频繁的方向非常有用,防止了梯度过大导致的震荡。
  • 适合稀疏数据:AdaGrad 在处理稀疏数据时表现很好,因为它能够自适应地为那些较少更新的参数保持较大的学习率。

缺点

  1. 学习率过度衰减:随着时间的推移,累积的时间步梯度平方值越来越大,导致学习率逐渐接近零,模型会停止学习。
  2. 不适合非稀疏数据:在非稀疏数据的情况下,学习率过快衰减可能导致优化过程早期停滞。

AdaGrad是一种有效的自适应学习率算法,然而由于学习率衰减问题,我们会使用改 RMSProp 或 Adam 来替代。

API

optimizer = optim.Adagrad(model.parameters(), lr=0.9)  # 设置学习率
3.4.4 RMSProp

RMSProp(Root Mean Square Propagation)在时间步中,不是简单地累积所有梯度平方和,而是使用指数加权平均来逐步衰减过时的梯度信息。这种方法专门用于解决AdaGrad在训练过程中学习率过度衰减的问题。

RMSProp过程

  1. 初始化学习率 α、初始化参数 θ、小常数 σ = 1e-8( 用于防止除零操作(通常取 1 0 − 8 10^{-8} 108))。

  2. 初始化参数 θ

  3. 初始化梯度累计变量 s=0

  4. 从训练集中采样 m 个样本的小批量,计算梯度 g

  5. 使用指数移动平均累积历史梯度,公式如下:

  6. 学习率 α 的计算公式如下:

  7. 参数更新公式如下:

优点

  • 适应性强:RMSProp自适应调整每个参数的学习率,对于梯度变化较大的情况非常有效,使得优化过程更加平稳。

  • 适合非稀疏数据:相比于AdaGrad,RMSProp更加适合处理非稀疏数据,因为它不会让学习率减小到几乎为零。

  • 解决过度衰减问题:通过引入指数加权平均,RMSProp避免了AdaGrad中学习率过快衰减的问题,保持了学习率的稳定性

缺点

  1. 依赖于超参数的选择:RMSProp的效果对衰减率 β \beta β 和学习率 α \alpha α 的选择比较敏感,需要一些调参工作。

需要注意的是:AdaGrad 和 RMSProp 都是对于不同的参数分量使用不同的学习率,如果某个参数分量的梯度值较大,则对应的学习率就会较小,如果某个参数分量的梯度较小,则对应的学习率就会较大一些

API

optimizer = optim.RMSprop(model.parameters(), lr=0.7, momentum=0.9)  # 设置学习率和动量
3.4.5 Adam

Adam(Adaptive Moment Estimation)算法将动量法和RMSProp的优点结合在一起:

  • 动量法:通过一阶动量(即梯度的指数加权平均)来加速收敛,尤其是在有噪声或梯度稀疏的情况下。
  • RMSProp:通过二阶动量(即梯度平方的指数加权平均)来调整学习率,使得每个参数的学习率适应其梯度的变化。
  • Momentum 使用指数加权平均计算当前的梯度值、AdaGrad、RMSProp 使用自适应的学习率,Adam 结合了 Momentum、RMSProp 的优点,使用:移动加权平均的梯度和移动加权平均的学习率。使得能够自适应学习率的同时,也能够使用 Momentum 的优点。

优点

  1. 高效稳健:Adam结合了动量法和RMSProp的优势,在处理非静态、稀疏梯度和噪声数据时表现出色,能够快速稳定地收敛。

  2. 自适应学习率:Adam通过一阶和二阶动量的估计,自适应调整每个参数的学习率,避免了全局学习率设定不合适的问题。

  3. 适用大多数问题:Adam几乎可以在不调整超参数的情况下应用于各种深度学习模型,表现良好。

缺点

  1. 超参数敏感:尽管Adam通常能很好地工作,但它对初始超参数(如 β 1 \beta_1 β1 β 2 \beta_2 β2 η \eta η)仍然较为敏感,有时需要仔细调参。
  2. 过拟合风险:由于Adam会在初始阶段快速收敛,可能导致模型陷入局部最优甚至过拟合。因此,有时会结合其他优化算法(如SGD)使用。

API

optimizer = optim.Adam(model.parameters(), lr=0.05)  # 设置学习率

3.5 总结

​ 梯度下降算法通过不断更新参数来最小化损失函数,是反向传播算法中计算权重调整的基础。在实际应用中,根据数据的规模和计算资源的情况,选择合适的梯度下降方式(批量、随机、小批量)及其变种(如动量法、Adam等)可以显著提高模型训练的效率和效果。

​ Adam是目前最为流行的优化算法之一,因其稳定性和高效性,广泛应用于各种深度学习模型的训练中。Adam结合了动量法和RMSProp的优点,能够在不同情况下自适应调整学习率,并提供快速且稳定的收敛表现。


相关文章:

11.28深度学习_bp算法

七、BP算法 多层神经网络的学习能力比单层网络强得多。想要训练多层网络&#xff0c;需要更强大的学习算法。误差反向传播算法&#xff08;Back Propagation&#xff09;是其中最杰出的代表&#xff0c;它是目前最成功的神经网络学习算法。现实任务使用神经网络时&#xff0c;…...

U盘文件夹变打不开的文件:深度解析、恢复策略与预防之道

一、U盘文件夹变打不开的文件现象解析 在日常使用U盘的过程中&#xff0c;我们时常会遇到这样的困扰&#xff1a;原本存储有序、可以轻松访问的文件夹&#xff0c;突然之间变成了无法打开的文件。这些文件通常以未知图标或乱码形式显示&#xff0c;双击或右键尝试打开时&#…...

软件工程中的需求分析流程详解

一、需求分析的定义 需求分析&#xff08;Requirements Analysis&#xff09;是指在软件开发过程中&#xff0c;通过与用户、相关人员的沟通与讨论&#xff0c;全面理解和确定软件需求的过程。需求分析的最终目标是清晰、准确地定义软件系统应具备的功能、性能、用户界面、约束…...

springboot369高校教师教研信息填报系统(论文+源码)_kaic

毕 业 设 计&#xff08;论 文&#xff09; 题目&#xff1a;高校教师教研信息填报系统的设计与实现 摘 要 如今社会上各行各业&#xff0c;都喜欢用自己行业的专属软件工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离不开了互联网。新技术的产生&#xff0c…...

Docker Buildx 与 CNB 多平台构建实践

一、Docker Buildx 功能介绍 docker buildx 是 Docker 提供的一个增强版构建工具&#xff0c;支持更强大的构建功能&#xff0c;特别是在构建多平台镜像和高效处理复杂 Docker 镜像方面。 1.1 主要功能 多平台构建支持 使用 docker buildx&#xff0c;可以在单台设备上构建…...

VBA字典与数组第二十一讲:文本转换为数组函数Split

《VBA数组与字典方案》教程&#xff08;10144533&#xff09;是我推出的第三套教程&#xff0c;目前已经是第二版修订了。这套教程定位于中级&#xff0c;字典是VBA的精华&#xff0c;我要求学员必学。7.1.3.9教程和手册掌握后&#xff0c;可以解决大多数工作中遇到的实际问题。…...

开源项目 - 人脸关键点检测 facial landmark 人脸关键点 (98个关键点)

开源项目 - 人脸关键点检测 facial landmark 人脸关键点 &#xff08;98个关键点&#xff09; 示例&#xff1a; ​​​​ 助力快速掌握数据集的信息和使用方式。 数据可以如此美好&#xff01;...

【Postgres_Python】使用python脚本批量导出PG数据库

示例代码说明&#xff1a; 有多个数据库需要导出为.sql格式&#xff0c;数据库名与sql文件名一致,读取的数据库名需要根据文件名进行拼接 import psycopg2 import subprocess import os folder_path D:/HQ/chongqing_20241112 # 获取文件夹下所有文件和文件夹的名称 filename…...

嵌入式Linux(SOC带GPU树莓派)无窗口系统下搭建 OpenGL ES + Qt 开发环境,并绘制旋转金字塔

树莓派无窗口系统下搭建 OpenGL ES Qt 开发环境&#xff0c;并绘制旋转金字塔 1. 安装 OpenGL ES 开发环境 运行以下命令安装所需的 OpenGL ES 开发工具和库&#xff1a; sudo apt install cmake mesa-utils libegl1-mesa-dev libgles2-mesa-dev libdrm-dev libgbm-dev2. 安…...

MySQL事物

目录 何谓事物&#xff1f; 何谓数据库事务? 并发事务带来了哪些问题? 脏读(Dirty read) 丢失修改(Lostto modify) 不可重复读(Unrepeatable read) 幻读(Phantom read) 不可重复读和幻读有什么区别? 并发事务的控制方式有哪些? SQL 标准定义了哪些事务隔离级别?…...

在 CentOS 上安装 Docker:构建容器化环境全攻略

一、引言 在当今的软件开发与运维领域&#xff0c;Docker 无疑是一颗璀璨的明星。它以轻量级虚拟化的卓越特性&#xff0c;为应用程序的打包、分发和管理开辟了崭新的高效便捷之路。无论是开发环境的快速搭建&#xff0c;还是生产环境的稳定部署&#xff0c;Docker 都展现出了…...

基于Spring Boot的宠物咖啡馆平台的设计与实现

私信我获取源码和万字论文&#xff0c;制作不易&#xff0c;感谢点赞支持。 基于Spring Boot的宠物咖啡馆平台的设计与实现 摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了基于Spring Boot的宠物咖啡馆平台的设…...

JAVAWeb之javascript学习

1.js引入方式 1. 内嵌式&#xff1a;在head中&#xff0c;通过一对script标签引入JS代码&#xff1b;cript代码放置位置有一定的随意性&#xff0c;一般放在head标签中&#xff1b;2.引入外部js文件 在head中&#xff0c;通过一对script标签引入外部JS代码&#xff1b;注意&…...

电脑与优傲协作机器人(实体)的TCP通讯(操作记录)

目录 一、UR通信端口 二、电脑&#xff08;客户端&#xff09;连接协作机器人&#xff08;服务端&#xff09; 1.设置网络方法 2.检查设置 3.示教器切换远程控制&#xff08;注&#xff09; 4.客户端与协作机器人建立连接 5.连接测试 三、电脑&#xff08;服务端&#…...

C++初阶——动态内存管理

目录 1、C/C内存区域划分 2、C动态内存管理&#xff1a;malloc/calloc/realloc/free 3、C动态内存管理&#xff1a;new/delete 3.1 new/delete内置类型 3.2 new/delete自定义类型 4、operator new与operator delete函数 5、new和delete的实现原理 5.1 内置类型 5.2 自定…...

Python 【图像分类】之 PyTorch 进行猫狗分类功能的实现(Swanlab训练可视化/ Gradio 实现猫狗分类 Demo)

Python 【图像分类】之 PyTorch 进行猫狗分类功能的实现(Swanlab训练可视化/ Gradio 实现猫狗分类 Demo) 目录 Python 【图像分类】之 PyTorch 进行猫狗分类功能的实现(Swanlab训练可视化/ Gradio 实现猫狗分类 Demo) 一、简单介绍 二、PyTorch 三、CNN 1、神经网络 2、卷…...

Attention显存统计与分析

Attention显存估计 简单的Attention函数 import torch import torch.nn as nn import einops class Attention(nn.Module):def __init__(self, dim, num_heads8, qkv_biasFalse, qk_scaleNone, attn_drop0., proj_drop0.):super().__init__()self.num_heads num_headshead_d…...

java反射

反射 Java 反射是 Java 提供的一种强大特性&#xff0c;它允许程序在运行时动态地获取类的信息&#xff0c;并操作类的属性和方法。这为编写灵活、可扩展的 Java 应用程序提供了强有力的支持 获取Class对象 package ref;public class Person {private String name ;private …...

Spring Boot入门

1、Spring Boot是什么 Spring Boot 帮我们简单、快速地创建一个独立的、生产级别的 Spring 应用&#xff08;说明&#xff1a;Spring Boot底层是Spring&#xff09; 大多数 Spring Boot 应用只需要编写少量配置即可快速整合 Spring 平台以及第三方技术 特性&#xff1a; 快速…...

Spring Web:深度解析与实战应用

概述 大家好&#xff0c;欢迎来到今天的技术分享。我是你们的老朋友&#xff0c;今天&#xff0c;我们要深入探讨的是Spring Web模块&#xff0c;这个模块为Java Web应用程序提供了全面的支持&#xff0c;不仅具备基本的面向Web的综合特性&#xff0c;还能与常见框架如Struts2无…...

学习日志019--初识PyQt

使用pyqt创建一个登录界面 from PyQt6.QtCore import Qt # 引入pyqt6包 from PyQt6.QtGui import QIcon, QMovie from PyQt6.QtWidgets import QApplication, QWidget, QPushButton, QLabel, QLineEdit import sysclass MyWidget(QWidget):# 构造函数&#xff0c;继承父类的构造…...

Swift 宏(Macro)入门趣谈(五)

概述 苹果在去年 WWDC 23 中就为 Swift 语言新增了“其利断金”的重要小伙伴 Swift 宏&#xff08;Swift Macro&#xff09;。为此&#xff0c;苹果特地用 2 段视频&#xff08;入门和进阶&#xff09;颇为隆重的介绍了它。 那么到底 Swift 宏是什么&#xff1f;有什么用&…...

Linux 35.6 + JetPack v5.1.4@DeepStream安装

Linux 35.6 JetPack v5.1.4DeepStream安装 1. 源由2. 步骤Step 1 安装Jetpack 5.1.4 L4T 35.6Step 2 安装依赖组件Step 3 安装librdkafkaStep 4 安装 DeepStream SDKStep 5 测试 deepstream-appStep 6 运行 deepstream-app 3. 总结3.1 版本问题3.2 二进制help 4. 参考资料 1. …...

C++基础:list的底层实现

文章目录 1.基本结构2.迭代器的实现2.1 尾插的实现2.2 迭代器的实现 3.打印函数(模版复用实例化)4.任意位置的插入删除1. 插入2. 删除 5.析构与拷贝构造5.1 析构函数5.2 拷贝构造5.3 赋值重载 1.基本结构 与vector和string不同list需要: 一个类来放入数据和指针也就是节点 一…...

Spring中@Transactional注解与事务传播机制

文章目录 事务传播机制事务失效的场景 事务传播机制 事务的传播特性指的是 当一个事务方法调用另一个事务方法时&#xff0c;事务方法应该如何执行。 事务传播行为类型外部不存在事务外部存在事务使用方式REQUIRED(默认)开启新的事务融合到外部事务中Transactional(propagati…...

实验七 用 MATLAB 设计 FIR 数字滤波器

实验目的 加深对窗函数法设计 FIR 数字滤波器的基本原理的理解。 学习用 Matlab 语言的窗函数法编写设计 FIR 数字滤波器的程序。 了解 Matlab 语言有关窗函数法设计 FIR 数字滤波器的常用函数用法。 掌握 FIR 滤波器的快速卷积实现原理。 不同滤波器的设计方法具有不同的优…...

Linux - selinux

七、selinux 1、说明 SELinux是Security-Enhanced Linux的缩写&#xff0c;意思是安全强化的linux。 SELinux是对程序、文件等权限设置依据的一个内核模块。由于启动网络服务的也是程序&#xff0c;因此刚好也 是能够控制网络服务能否访问系统资源的一道关卡。 传统的文件权…...

【STL】C++ vector类模板

文章目录 基本概念vector的使用定义和初始化构造函数赋值操作容量和大小插入和删除数据存取 互换容器vector的迭代器vector储存自定义数据类型 基本概念 vector是类型相同的对象的容器&#xff0c;vector的大小可以变化&#xff0c;可以向数组中增加元素。因此&#xff0c;vec…...

物联网——WatchDog(监听器)

看门狗简介 独立看门狗框图 看门狗原理&#xff1a;定时器溢出&#xff0c;产生系统复位信号&#xff1b;若定时‘喂狗’则不产生系统复位信号 定时中断基本结构&#xff08;对比&#xff09; IWDG键寄存器 独立看门狗超时时间 WWDG(窗口看门狗) WWDG特性 WWDG超时时间 由于…...

从零开始写游戏之斗地主-网络通信

在确定了数据结构后&#xff0c;原本是打算直接开始写斗地主的游戏运行逻辑的。但是突然想到我本地写出来之后&#xff0c;也测试不了啊&#xff0c;所以还是先写通信模块了。 基本框架 在Java语言中搞网络通信&#xff0c;那么就得请出Netty这个老演员了。 主要分为两个端&…...

【智能控制】实验,基于MATLAB的模糊推理系统设计,模糊控制系统设计

关注作者了解更多 我的其他CSDN专栏 过程控制系统 工程测试技术 虚拟仪器技术 可编程控制器 工业现场总线 数字图像处理 智能控制 传感器技术 嵌入式系统 复变函数与积分变换 单片机原理 线性代数 大学物理 热工与工程流体力学 数字信号处理 光电融合集成电路…...

Vega Editor 基于 Web 的图形编辑器

Vega Editor 是一个强大的基于 Web 的图形编辑器&#xff0c;专为 Vega 和 Vega-Lite 可视化语法设计。它提供了一个交互式的环境&#xff0c;用户可以在其中编写、预览和分享他们的 Vega 和 Vega-Lite 可视化作品。Vega 和 Vega-Lite 是用于声明性可视化的开源语法&#xff0c…...

SQL 中SET @variable的使用

在 SQL 中&#xff0c;SET variable 用于声明和赋值用户定义的变量。具体来说&#xff0c; 符号用于表示一个局部变量&#xff0c;可以在 SQL 语句中存储和使用。它通常在存储过程、函数或简单的 SQL 查询中使用。 1. 声明并赋值给变量 你可以使用 SET 语句给一个变量赋值。例…...

基于 Vite 封装工具库实践

项目背景&#xff1a;公司在多个项目中频繁使用相同的工具函数。为了避免每次开发新项目时都重复复制代码&#xff0c;决定通过 Vite 封装一个时间函数组件库。该库将被发布到 Verdaccio 供团队其他项目使用。 项目介绍 本项目封装了一个时间函数工具库&#xff0c;使用 Momen…...

Oracle DataGuard 主备正常切换 (Switchover)

前言 众所周知&#xff0c;DataGuard 的切换分为两种情况&#xff1a; 系统正常情况下的切换&#xff1a;这种方式称为 switchover&#xff0c;是无损切换&#xff0c;不会丢失数据。灾难情况下的切换&#xff1a;这种情况下一般主库已经启动不起来了&#xff0c;称为 failov…...

[Redis#13] cpp-redis接口 | set | hash |zset

目录 Set 1. Sadd 和 Smembers 2. Sismember 3. Scard 4. Spop 5. Sinter 6. Sinter store Hash 1. Hset 和 Hget 2. Hexists 3. Hdel 4. Hkeys 和 Hvals 5. Hmget 和 Hmset Zset 1. Zadd 和 Zrange 2. Zcard 3. Zrem 4. Zscore cpp-redis 的学习 主要关注于…...

青海摇摇了3天,技术退步明显.......

最近快手上的青海摇招聘活动非常火热&#xff0c;我已经在思考是否备战张诗尧的秋招活动。开个玩笑正片开始&#xff1a; 先说一下自己的情况&#xff0c;大专生&#xff0c;20年通过校招进入杭州某软件公司&#xff0c;干了接近4年的功能测试&#xff0c;今年年初&#xff0c…...

Flask+Minio实现断点续传技术教程

什么是MinIO MinIO是一个高性能的分布式对象存储服务&#xff0c;与Amazon S3 API兼容。它允许用户存储和检索任意规模的数据&#xff0c;非常适合于使用S3 API的应用程序。MinIO支持多租户存储&#xff0c;提供高可用性、高扩展性、强一致性和数据持久性。它还可以作为软件定义…...

Java中Logger定义的三种方式

在 Java 项目中&#xff0c;日志记录是开发中的一个重要部分&#xff0c;用于跟踪系统运行状态、排查问题以及记录重要事件。在定义日志记录器时&#xff0c;经常会遇到一些写法上的选择&#xff0c;比如 Logger 的作用域、是否使用静态变量&#xff0c;以及如何命名变量。本篇…...

模型压缩技术

目录 模型压缩技术 权重剪枝: 量化技术: 知识蒸馏: 低秩分解: 一、权重剪枝 二、量化技术 三、知识蒸馏 四、低秩分解 模型压缩技术 权重剪枝: 描述:通过删除模型中不重要的权重来减少参数数量和计算量。举例说明:假设我们有一个神经网络模型,其中某些神经元的…...

面试题整理

1 spring使用中有哪些设计模式 工厂模式-beanFactory,代理模式-aop,单例模式-每个bean默认都是单例的,原型模式-当将bean的作用域改为prototype时每次获取bean时使用了原型模式创建对象,责任链模式-dispatchServle查找url对应的处理器映射器时使用了,观察者模式-spring的…...

Linux

1、显示系统中所有进程 ps -ef运行效果&#xff1a; [rootredhat-9 ~]# ps -ef UID PID PPID C STIME TTY TIME CMD root 1 0 0 19:01 ? 00:00:01 /usr/lib/systemd/systemd rhgb --switched-r root 2 0 0…...

力扣_2389. 和有限的最长子序列

力扣_2389. 和有限的最长子序列 给你一个长度为 n 的整数数组 nums &#xff0c;和一个长度为 m 的整数数组 queries 。 返回一个长度为 m 的数组 answer &#xff0c;其中 answer[i] 是 nums 中 元素之和小于等于 queries[i] 的 子序列 的 最大 长度 。 子序列 是由一个数组…...

UI设计从入门到进阶,全能实战课

课程内容&#xff1a; ├── 【宣导片】从入门到进阶!你的第一门UI必修课!.mp4 ├── 第0课&#xff1a;UI知识体系梳理 学习路径.mp4 ├── 第1课&#xff1a;IOS设计规范——基础规范与切图.mp4 ├── 第2课&#xff1a;IOS新趋势解析——模块规范与设计原则(上).mp4…...

Formality:等价性检查的流程与模式(Guide、Setup、Preverify、Match与Verify)

相关阅读 Formalityhttps://blog.csdn.net/weixin_45791458/category_12841971.html?spm1001.2014.3001.5482 等价性检查的流程 图1概述了使用Formality进行等效性检查的具体步骤。 图1 等价性检查流程 启动Formality(Start Formality) 要启动Formality&#xff0c;请…...

【Linux】————(日志、线程池及死锁问题)

作者主页&#xff1a; 作者主页 本篇博客专栏&#xff1a;Linux 创作时间 &#xff1a;2024年11月29日 日志 关于日志&#xff0c;首先我们来说一下日志的作用&#xff0c; 作用&#xff1a; 问题追踪&#xff1a;通过日志不仅仅包括我们程序的一些bug&#xff0c;也可以在…...

【自动化】配置信息抽取

公共基本信息配置文件抽取 公共基本信息比如卖家、买家、管理员&#xff0c;验证码等基本信息&#xff0c;再比如数据库、redis、各个服务的域名&#xff0c;这些目前是写死在代码之中的&#xff0c;为了能够更好的维护他们&#xff0c;我们将他们放入配置文件进行管理 公共的…...

Python毕业设计选题:基于django+vue的校园影院售票系统

开发语言&#xff1a;Python框架&#xff1a;djangoPython版本&#xff1a;python3.7.7数据库&#xff1a;mysql 5.7数据库工具&#xff1a;Navicat11开发软件&#xff1a;PyCharm 系统展示 管理员登录 管理员功能界面 用户管理 影院信息管理 电影类型管理 电影信息管理 系统…...

Docker化部署Flask:轻量级Web应用的快速部署方案

Flask是一个用Python编写的轻量级Web应用框架&#xff0c;以其简洁性和灵活性而受到开发者的喜爱。Docker作为一种流行的容器化技术&#xff0c;为应用的部署和管理提供了极大的便利。本文将探讨Flask的优点、Docker部署的好处&#xff0c;并详细介绍如何将Flask应用Docker化部…...

centos怎么通过docker安装一个gitlab

在CentOS上通过Docker安装GitLab的步骤如下&#xff1a; 安装Docker引擎&#xff1a; 首先&#xff0c;需要在你的CentOS系统上安装Docker。可以通过以下命令来安装Docker&#xff1a;yum install -y yum-utils yum-config-manager --add-repo https://download.docker.com/lin…...