深度学习笔记(五)——网络优化(1):学习率自调整、激活函数、损失函数、正则化

文中程序以Tensorflow-2.6.0为例
部分概念包含笔者个人理解,如有遗漏或错误,欢迎评论或私信指正。
截图和程序部分引用自北京大学机器学习公开课

通过学习已经掌握了主要的基础函数之后具备了搭建一个网络并使其正常运行的能力,那下一步我们还需要进一步对网络中的重要节点进行优化并加深认知。
首先我们知道NN(自然神经)网络算法能够相比传统建模类算法发挥更好效果的原因是网络对复杂非线性函数的拟合效果更好,且使用简单。实际应用中通常利用多层神经网络级联来发挥效果。既然涉及到回归和分类问题,不难发现,拟合效果,系统线性还是非线性,拟合结果输入输出结构,如何自动更新参数等等概念,成为了深度学习研究的重中之重。 这篇笔记重点关注优化网络时除了参数优化器以外的其它概念。同时数学概念会相对增加一些。

神经网络复杂度

评价一个网络模型的好坏是多方面的,其中很重要的一点就是看网络的复杂程度。网络完整的计算一次需要的运算次数可以用浮点运算次数(FPLOPs)或者乘加运算次数来表示。通过计算次数可以表示网络的时间复杂度,计算量需要越大的网络,时间复杂度自然越高。
除了时间复杂度,还可以从网络的规模,也就是所有层中总的参数量和输出特征图的数量来表示,网络规模的大小表示了网络的空间复杂度。在ML神经元模型中,单一 一个神经元就含有n+1个参数,其中n个数是上个层的输入,1个偏置系数。而在一个层中可以包含多个神经元。
这里涉及两个概念

  • 网络参数量:指网络模型中所有层中总的可变参数(权重参数)数量;
  • 特征图:网络在运行过程中,每个层的输出矩阵被称之为这个层的特征图,矩阵的维度大小称为特征图大小。

学习率调整策略

在权重参数反向传播跟新的过程中,学习率是一个直接影响权重跟新变化大小的参数。在权重更新时,最基础的方法是原权重参数减去学习率乘以损失函数梯度 W t + 1 = W t − l r × ∂ l o s s ∂ W t W_{t+1} = W_{t} - lr \times \frac{\partial loss}{\partial W_{t}} Wt+1=Wtlr×Wtloss 但是这样的参数更新是线性函数,有时候需要学习的数据呈现复杂的非线性特征,并且网络规模也各不相同,最主要的还是线性函数导致自始至终网络的学习能力(就是参数的跟新效率)是不变的。所以我们希望在损失计算较大时用一个较大学习率更新参数,随着损失的降低能不断地调小学习率,更加精细的调整网络权重。
所以引入指数衰减学习率 D l r = l r × φ N θ Dlr = lr\times \varphi ^{\frac{N}{\theta } } Dlr=lr×φθN 其中Dlr 是当前需要更新计算中使用的学习率,lr是初始学习率,fai是定义的衰减率,N是从开始到当前总的训练次数,sita表示每多少轮衰减一次学习率。根据指数函数性质可以知道要使学习率是下降趋势fai需要取值0到1之间。

import tensorflow as tf
from matplotlib import pyplot as pltw = tf.Variable(tf.constant(5, dtype=tf.float32))epoch = 40
LR_BASE = 0.4  # 最初学习率
LR_DECAY = 0.9  # 学习率衰减率
LR_STEP = 2  # 定义每经过两次BATCH_SIZE后,更新一次学习率
ls_list = []for epoch in range(epoch):  # for epoch 定义顶层循环,表示对数据集循环epoch次,此例数据集数据仅有1个w,初始化时候constant赋值为5,循环100次迭代。lr = LR_BASE * LR_DECAY ** (epoch / LR_STEP)    # 学习率指数衰减,每两轮衰减一次学习率ls_list.append(lr)with tf.GradientTape() as tape:  # with结构到grads框起了梯度的计算过程。loss = tf.square(w + 1)grads = tape.gradient(loss, w)  # .gradient函数告知谁对谁求导,这里是loss函数对w求导w.assign_sub(lr * grads)  # .assign_sub 对变量做自减,更新参数 即:w -= lr*grads 即 w = w - lr*gradsprint("After %s epoch,w is %f,loss is %f,lr is %f" % (epoch, w.numpy(), loss, lr))plt.title('Learning rate attenuation')  # 图片标题
plt.xlabel('Epoch')  # x轴变量名称
plt.ylabel('Lr')  # y轴变量名称
plt.plot(ls_list, label="$Learning Rate$")  # 逐点画出test_acc值并连线,连线图标是Accuracy
plt.legend()
plt.show()

可以看到最终结果,随着迭代次数的增加,学习率逐渐逐渐降低,成非线性下降,且下降程度根据学习率衰减率的减小而加快。
在这里插入图片描述
除了指数衰减以外,还可以根据迭代次数,设置分段函数的学习率。一般来说使用分段函数,各个段的长度取多少,每段内学习率设置为多少取决于设计者对网络调试的经验,往往难度较高。

# 其它上下文同上一段代码相同
epochs = epoch
for epoch in range(epoch):  # for epoch 定义顶层循环,表示对数据集循环epoch次,此例数据集数据仅有1个w,初始化时候constant赋值为5,循环100次迭代。# lr = LR_BASE * LR_DECAY ** (epoch / LR_STEP)    # 学习率指数衰减,每两轮衰减一次学习率if epoch < epochs*0.5:  # 分段处理lr = 0.4elif epoch < epochs*0.7:lr = 0.2elif epoch < epochs*0.9:lr = 0.1elif epoch < epochs:lr = 0.05

举例设置了分四段的学习率函数
在这里插入图片描述

激活函数

在前面的博文中针对神经元只使用了输入权重参数和偏置系数。回顾一下基础的ML神经元模型结构:
在这里插入图片描述
其实神经元的输出位置还有一个非线性函数。假如我们不使用这个函数,那么无论网络复合多少层,依然总体上是一个线性函数的组合,所以引入这个函数对于复杂非线性数据的学习显得尤为重要。简而言之,为了提高模型对非线性数据的表达能力,需要添加输出位置的非线性函数。这个函数称之为激活函数
根据激活函数的用途以及位置特点,一个优秀的激活应该满足:

  • 非线性:使用非线性激活函数,并通过多层网络组合,几乎可以拟合所有函数类型。也正是由于非线性激活函数的存在才使得多层网络的深度叠加有了意义
  • 连续单调可导:因为绝大多数参数更新是利用梯度下降法,反向传播过程中从输出层到输入层逐层链式求导,满足连续可导是梯度下降法的要求。同时函数应该单调,最好能是凸函数,这样能使求导后结果正有负,可以更加快速的完成参数更新
  • 近似恒等:指函数输出应该近似等于输入,这样可以避免数据的丢失,并且保证在求和结果很小时依然能有稳定输出值。
    在使用激活函数应该注意,对于使用梯度下降法更新参数的网络,适合使用输出是有界的激活函数。当激活函数输出无届时,应该尽量使用小的学习率,避免输出数据爆炸增涨。

常用激活函数

在选择激活函数时有几个简单的规律:

  • 首选ReLu函数
  • 学习率设置一个较小的数,例如0.01
  • 将输入的数据先进行标准归一化,也就是竟可能让数据的值域分布满足均值为0,标准差为1,或者处于[-1,1]之间
  • 初始化网络参数时,各层的随机参数可以符合均值为0,方差为 σ = 2 当 前 层 输 入 特 征 个 数 \sigma = \sqrt{\frac{2}{当前层输入特征个数} } σ=2 的标准正态分布
1、Sigmoid函数

f ( x ) = 1 1 + e − x f(x) = \frac{1}{1+e^{-x} } f(x)=1+ex1 其导数为 f ( x ) ′ = ( 1 1 + e − x ) ′ = − ( − e − x ) ( 1 + e − x ) 2 = 1 1 + e − x × e − x 1 + e − x = f ( x ) × ( 1 − f ( x ) ) f{(x)}' = {( \frac{1}{1+e^{-x} }) } ' =\frac{-(-e^{-x}) }{(1+e^{-x})^{2}}= \frac{1}{1+e^{-x} }\times \frac{e^{-x} }{1+e^{-x} }=f(x)\times (1-f(x)) fx=(1+ex1)=(1+ex)2(ex)=1+ex1×1+exex=f(x)×(1f(x)) (根据“小学一年级数学”,后文函数偏导数可自行计算)

import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt# 定义 Sigmoid 函数
def sigmoid(x):return 1 / (1 + tf.exp(-x))
# 定义 Sigmoid 函数的导数
def sigmoid_derivative(x):return sigmoid(x) * (1 - sigmoid(x)) # 推导得到的导数公式# 生成 x 值
x_values = np.linspace(-7, 7, 200)
# 计算 Sigmoid 函数在 x_values 上的值
y_sigmoid = sigmoid(x_values)
# 计算 Sigmoid 函数的导数在 x_values 上的值
y_derivative = sigmoid_derivative(x_values)# 绘制 Sigmoid 函数
plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.plot(x_values, y_sigmoid, label='Sigmoid Function')
plt.title('Sigmoid Function')
plt.xlabel('x')
plt.ylabel('sigmoid(x)')
plt.grid(True)
plt.legend()
# 绘制 Sigmoid 函数的导数
plt.subplot(1, 2, 2)
plt.plot(x_values, y_derivative, label='Sigmoid Derivative')
plt.title('Sigmoid Derivative')
plt.xlabel('x')
plt.ylabel('sigmoid\'(x)')
plt.grid(True)
plt.legend()
plt.tight_layout()
plt.show()

在这里插入图片描述
观察sigmoid激活函数的特点可以发现它具有以下特点:

  • 容易梯度消失,导数值小于0.25,多次求导后可能导致输出接近0(解决:让激活函数导数值域更大)
  • 非0均值,函数均值非0收敛速度会比0均值函数更慢(解决:使用分段函数或奇函数)
  • 幂运算会导致计算量大,使训练时间长(解决:钞能力,购买更厉害的显卡和CPU进行训练,👀👻)
2、Tanh函数

f ( x ) = 1 − e − 2 x 1 + e − 2 x f(x)=\frac{1-e^{-2x}}{1+e^{-2x}} f(x)=1+e2x1e2x

# 上下文程序可以根据sigmoid函数部分代码简单修改函数名和画图部分标签名实现
# 定义 tanh 函数
def tanh(x):return tf.math.tanh(x)# 定义 tanh 函数的导数
def tanh_derivative(x):return 1 - tf.math.square(tf.math.tanh(x))

在这里插入图片描述
观察函数图像可以知道tanh 函数的输出是0均值(奇函数),但是只有当x属于[-2, 2]的区间内函数导数输出才有一个相对合适的值,其它大部分范围输出接近0,这样同样会造成梯度消失。并且同样使用了幂运算。

3、ReLu函数

f ( x ) = m a x ( x , 0 ) = { 0 x < 0 x x > = 0 f(x)=max(x, 0)= \left\{\begin{matrix} 0 \qquad x<0 \\ x \qquad x>=0 \end{matrix}\right. f(x)=max(x,0)={0x<0xx>=0

# 上下文程序可以根据sigmoid函数部分代码简单修改函数名和画图部分标签名实现
# 定义 ReLU 函数
def relu(x):return tf.nn.relu(x)
# 定义 ReLU 函数的导数
def relu_derivative(x):return tf.where(x > 0, 1.0, 0.0)x_values = np.linspace(-3, 3, 200)		# 生成 x 值
y_relu = relu(x_values)  # 计算 ReLU 函数的值
y_derivative = relu_derivative(x_values)		# 计算导数

在这里插入图片描述
Relu函数则直接使用无界分段函数,首先解决了梯度消失的问题,但是只当输出值大于0时有效。
其次只用判断输出是否大于0,速度快,但是这样会导致训练过程中某些神经元的输出一直是0,这样该神经元参数不更新,失去学习效果,成为神经元死亡(dead relu)。为了解决这个问题往往初始化网络时会使用较多的正值参数,尽可能保证输出值是正数,同时调小学习率。

4、LeakyRelu函数

f ( x ) = m a x ( a x , x ) = { a x x < 0 x x > = 0 f(x) = max(ax,x)= \left\{\begin{matrix} ax \qquad x<0 \\ x \qquad x>=0 \end{matrix}\right. f(x)=max(ax,x)={axx<0xx>=0
函数在小于0时使用了一个带参数的一元函数。当a取值合适时,理论上LeakyRelu激活函数将具有Relu的所有优点同时避免其缺点,但是目前还没有严格的被证明LeakyRelu总是优于Relu。自己使用时可以多进行实验找到最合适的激活函数。

# 上下文程序可以根据sigmoid函数部分代码简单修改函数名和画图部分标签名实现
# 定义 Leaky ReLU 函数 其中alpha为小于0时的斜率系数,通常应用时这个值取0.02等较小的数
def leaky_relu(x, alpha=0.15):return tf.nn.leaky_relu(x, alpha)# 定义 Leaky ReLU 函数的导数
def leaky_relu_derivative(x, alpha=0.15):return tf.where(x > 0, 1.0, alpha)x_values = np.linspace(-3, 3, 200)
y_leaky_relu = leaky_relu(x_values)
y_derivative = leaky_relu_derivative(x_values)

在这里插入图片描述

损失函数(Loss)

损失函数计算的是计算得到的输出值和已知标签(正确答案)之间的差距
结合之前的介绍,我们知道反向传播的过程中需要使损失函数最小化,不同的损失函数会对网络的跟新和输出趋势产生影响。可以理解为损失函数的值直接影响着反向传播参数的更新(所以反向传播就类似于控制系统中的反馈控制,损失函数就类似反馈回路的传递函数)。所以损失函数的设计十分重要。

常用的损失函数

1、MSE均方误差

M S E ( Y , Y ^ ) = ∑ i = 1 n ( Y i − Y i ^ ) 2 n MSE(Y,\hat{Y} ) = \frac{\sum_{i=1}^{n}(Y_i - \hat{Y_i})^{2} }{n} MSE(Y,Y^)=ni=1n(YiYi^)2
均方误差就是所有实际值和计算输出值差的平方的平均值。均方误差应用最多的领域就是计算回归问题。使用均方误差可以很快的找到数据中的规律性,实现网络的回归拟合。

import numpy as np
import tensorflow as tf
np.random.seed(42) # 设置随机数种子
true_values = np.random.randint(0, 10, 100)		# 生成两组随机数
predicted_values = true_values + np.random.normal(0, 2, 100)# 转换为 TensorFlow 的张量
true_values_tensor = tf.constant(true_values, dtype=tf.float32)
predicted_values_tensor = tf.constant(predicted_values, dtype=tf.float32)# 计算均方误差 (MSE)
# 使用 tf.reduce_mean(平均数) 和 tf.square(平方)计算MSE
mse = tf.reduce_mean(tf.square(true_values_tensor - predicted_values_tensor))	# 输出 MSE 的值
print("Mean Squared Error (MSE):", mse.numpy())
2、CE交叉熵损失

H ( Y , Y ^ ) = − ∑ Y i ^ × l n Y i H(Y,\hat{Y} ) = -\sum \hat{Y_i}\times lnY_i H(Y,Y^)=Yi^×lnYi 上式中Y表示真实值的概率,Y一巴表示网络计算的输出值的概率。
交叉熵表征两个概率分布之间的距离,交叉熵越小说明二者分布越接近。交叉熵的计算考虑了真实数据的概率分布,相比MSE交叉熵有一定的加权效果。除了回归问题,交叉熵则广泛应用在分类和迁移学习上。要注意的一点是,在网络的输出中往往最终输出的结果不符合概率分布,所以需要配合softmax函数一起使用。其原理可以看深度学习笔记(四)——softmax,一般流程值网络计算得到输出张量,经过softmax后转化为概率分布矩阵,随后计算CE。

import numpy as np
import tensorflow as tf
# 生成两组数据 y_ 已经符合概率分布要求,y 可以认为是网轮的输出矩阵
y_ = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1], [1, 0, 0], [0, 1, 0]])
y = np.array([[12, 3, 2], [3, 10, 1], [1, 2, 5], [4, 6.5, 1.2], [3, 6, 1]])
y_pro = tf.nn.softmax(y)	# 计算输出数据的概率分布
loss_ce1 = tf.losses.categorical_crossentropy(y_,y_pro)	# 计算交叉熵
loss_ce2 = tf.nn.softmax_cross_entropy_with_logits(y_, y)	# 复合了softmax和交叉熵计算的函数print('分步计算的结果:\n', loss_ce1)
print('结合计算的结果:\n', loss_ce2)

提示:如果你的环境是tensorflow2.6.0,且安装步骤是按照前面几篇笔记来的,那你可能会遇到这一行代码报错tf.losses.categorical_crossentropy,请终端在对应虚拟环境中执行pip install keras==2.6即可

3、自定义损失函数

根据具体任务和目的,可设计不同的损失函数。例如,我们可以看目标检测中的多种损失函数。目标检测的主要功能是定位和识别,损失函数的功能主要就是让定位更精确,识别准确率更高。目标检测任务的损失函数由分类损失(Classificition Loss)和回归损失(Bounding Box Regeression Loss)两部分构成。近几年来回归损失主要有Smooth L1 Loss(2015), IoU Loss(2016 ACM), GIoU Loss(2019 CVPR), DIoU Loss & CIoU Loss(2020AAAI)等,分类损失有交叉熵、softmax loss、logloss、focal loss等。主要是认识到:需要时,得针对特定的背景、具体的任务设计损失函数。

欠拟合和过拟合

在这里插入图片描述
上面这幅图很好的表达了三种拟合情况的区别,欠拟合时网络判断能力不够,无法输出正确结果,过拟合时网络陷入局部最优,失去了泛化能力,容易把相同但只有细微差别的输入数据判断为两种情况。拟合的好坏决定了训练好的模型迁移到新的环境中对新数据输出的准确程度。良好的模型训练过程应该避免欠拟合与过拟合的出现。
解决欠拟合的方法:

  • 扩大数据的特征数量(学习更多的细节)
  • 增加网络的参数和网络的层数(提高学习和理解能力)
  • 减少正则化参数(降低学习的复杂程度)

解决过拟合的方法:

  • 清洗输入数据(让学习的数据特征更明显,噪声更少,押题战术)
  • 增大训练数据集(学习更多的样本,题海战术)
  • 使用正则化(提高复杂度和计算量,回归基础,认识特征)
  • 增大正则化参数(上强度,深挖概念,排除错误选项)

正则化方法

正则化是用于缓解训练过程中出现过度拟合的一种有效手段。通过正则化可以在损失函数中引入模型的复杂度指标,也就是在原有损失函数计算的基础上,进一步的给权重参数加权。正则化的方式通常有两种:L1正则和L2正则,其中L1正则为: l o s s L 1 ( W ) = ∑ i ∣ W i ∣ loss_{L1}(W) = \sum_{i}^{}\left | W_i \right | lossL1(W)=iWi L2正则为: l o s s L 2 ( W ) = ∑ i ∣ W i 2 ∣ loss_{L2}(W) = \sum_{i}^{}\left | W_i^{2} \right | lossL2(W)=iWi2 正则化计算得到的损失函数为: l o s s = l o s s ( Y , Y ^ ) + R e g u l a r i z e r × l o s s ( W ) loss = loss(Y,\hat{Y} ) + Regularizer \times loss (W) loss=loss(Y,Y^)+Regularizer×loss(W) 其中等式右边第一项loss计算输出值和真实值的损失,第二项的Regularizer 为正则化的权重系数,第二个loss则计算需要正则的参数w。
使用正则化时,L1正则会将较多的参数变为0,所以该方法可以稀疏参数,减少非0的参数数量,降低模型的复杂度。
L2正则由于使用平方计算,会使参数接近0但不等于0,这个方法可以减小部分参数的权重(反应为参数变小),降低模型的复杂度。
抽象一点理解,加入了正则化,局部计算就难以过度拟合,可以让模型的拟合效果变的更加平滑。

正则化拟合示例

1、无正则

# @ 引用自:北京大学-《人工智能实践》-曹建
# 导入所需模块
import tensorflow as tf
from matplotlib import pyplot as plt
import numpy as np
import pandas as pd# 读入数据/标签 生成x_train y_train
df = pd.read_csv('dot.csv')
x_data = np.array(df[['x1', 'x2']])
y_data = np.array(df['y_c'])x_train = np.vstack(x_data).reshape(-1, 2)
y_train = np.vstack(y_data).reshape(-1, 1)Y_c = [['red' if y else 'blue'] for y in y_train]# 转换x的数据类型,否则后面矩阵相乘时会因数据类型问题报错
x_train = tf.cast(x_train, tf.float32)
y_train = tf.cast(y_train, tf.float32)# from_tensor_slices函数切分传入的张量的第一个维度,生成相应的数据集,使输入特征和标签值一一对应
train_db = tf.data.Dataset.from_tensor_slices((x_train, y_train)).batch(32)# 生成神经网络的参数,输入层为2个神经元,隐藏层为11个神经元,1层隐藏层,输出层为1个神经元
# 用tf.Variable()保证参数可训练
w1 = tf.Variable(tf.random.normal([2, 11]), dtype=tf.float32)
b1 = tf.Variable(tf.constant(0.01, shape=[11]))w2 = tf.Variable(tf.random.normal([11, 1]), dtype=tf.float32)
b2 = tf.Variable(tf.constant(0.01, shape=[1]))lr = 0.005  # 学习率
epoch = 800  # 循环轮数# 训练部分
for epoch in range(epoch):for step, (x_train, y_train) in enumerate(train_db):with tf.GradientTape() as tape:  # 记录梯度信息h1 = tf.matmul(x_train, w1) + b1  # 记录神经网络乘加运算h1 = tf.nn.relu(h1)y = tf.matmul(h1, w2) + b2# 采用均方误差损失函数mse = mean(sum(y-out)^2)loss = tf.reduce_mean(tf.square(y_train - y))# 计算loss对各个参数的梯度variables = [w1, b1, w2, b2]grads = tape.gradient(loss, variables)# 实现梯度更新# w1 = w1 - lr * w1_grad tape.gradient是自动求导结果与[w1, b1, w2, b2] 索引为0,1,2,3 w1.assign_sub(lr * grads[0])b1.assign_sub(lr * grads[1])w2.assign_sub(lr * grads[2])b2.assign_sub(lr * grads[3])# 每20个epoch,打印loss信息if epoch % 20 == 0:print('epoch:', epoch, 'loss:', float(loss))# 预测部分
print("*******predict*******")
# xx在-3到3之间以步长为0.01,yy在-3到3之间以步长0.01,生成间隔数值点
xx, yy = np.mgrid[-3:3:.1, -3:3:.1]
# 将xx , yy拉直,并合并配对为二维张量,生成二维坐标点
grid = np.c_[xx.ravel(), yy.ravel()]
grid = tf.cast(grid, tf.float32)
# 将网格坐标点喂入神经网络,进行预测,probs为输出
probs = []
for x_test in grid:# 使用训练好的参数进行预测h1 = tf.matmul([x_test], w1) + b1h1 = tf.nn.relu(h1)y = tf.matmul(h1, w2) + b2  # y为预测结果probs.append(y)# 取第0列给x1,取第1列给x2
x1 = x_data[:, 0]
x2 = x_data[:, 1]
# probs的shape调整成xx的样子
probs = np.array(probs).reshape(xx.shape)
plt.scatter(x1, x2, color=np.squeeze(Y_c))  # squeeze去掉纬度是1的纬度,相当于去掉[['red'],[''blue]],内层括号变为['red','blue']
# 把坐标xx yy和对应的值probs放入contour函数,给probs值为0.5的所有点上色  plt.show()后 显示的是红蓝点的分界线
plt.contour(xx, yy, probs, levels=[.5])
plt.show()

在这里插入图片描述

2、加入L2正则后:

# 接上一段程序,修改43行到46行为下面的 正则化计算# 采用均方误差损失函数mse = mean(sum(y-out)^2)loss_mse = tf.reduce_mean(tf.square(y_train - y))# 添加l2正则化loss_regularization = []# tf.nn.l2_loss(w)=sum(w ** 2) / 2loss_regularization.append(tf.nn.l2_loss(w1))loss_regularization.append(tf.nn.l2_loss(w2))# 求和# 例:x=tf.constant(([1,1,1],[1,1,1]))#   tf.reduce_sum(x)# >>>6loss_regularization = tf.reduce_sum(loss_regularization)loss = loss_mse + 0.03 * loss_regularization  # REGULARIZER = 0.03

在这里插入图片描述
不难看出使用了正则化后参数的拟合变得更加平滑。

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

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

相关文章

JavaScript基础(26)_dom增删改练习

<!DOCTYPE html> <html lang"zh"><head><meta charset"UTF-8"><title>DOM增删改练习</title><link rel"stylesheet" href"../browser_default_style/reset.css"><style>table {borde…

vue路由及参数router

目录 vue项目版本1、创建一个vue项目步骤 &#xff08;windows环境下&#xff09;。创建vue项目前&#xff0c;检查系统是否具备创建项目的条件&#xff08;是否已经安装好了node.js、webpack、vue-cli&#xff09;。cmd打开终端。2、vue路由vue-router解说2.1 路由视图<rou…

【GDAL】Windows下VS+GDAL开发环境搭建

Step.0 环境说明&#xff08;vs版本&#xff0c;CMake版本&#xff09; 本地的IDE环境是vs2022&#xff0c;安装的CMake版本是3.25.1。 Step.1 下载GDAL和依赖的组件 编译gdal之前需要安装gdal依赖的组件&#xff0c;gdal所依赖的组件可以在官网文档找到&#xff0c;可以根据…

Kafka(七)可靠性

目录 1 可靠的数据传递1.1 Kafka的可靠性保证1.2 复制1.3 Broker配置1.3.1 复制系数1.3.2 broker的位置分布1.3.3 不彻底的首领选举1.3.4 最少同步副本1.3.5 保持副本同步1.3.6 持久化到磁盘flush.messages9223372036854775807flush.ms9223372036854775807 1.2 在可靠的系统中使…

Netty开篇——基础介绍与准备(一)

I/O篇 Netty的介绍 Netty 是由JBOSS提供的一个Java开源框架在Github上Netty 是一个异步的、基于事件驱动的网络应用框架&#xff0c;用以快速开发高性能、高可靠性的网络IO程序。Netty 主要针对在TCP协议下面向客户端的高并发应用&#xff0c;或者Peer-to-Peer/P2P场景下的大量…

day17 平衡二叉树 二叉树的所有路径 左叶子之和

题目1&#xff1a;110 平衡二叉树 题目链接&#xff1a;110 平衡二叉树 题意 判断二叉树是否为平衡二叉树&#xff08;每个节点的左右两个子树的高度差绝对值不超过1&#xff09; 递归遍历 递归三部曲 1&#xff09;确定递归函数的参数和返回值 2&#xff09;确定终止条…

数据结构链表完整实现(负完整代码)

文章目录 前言引入1、链表定义及结构链表的分类3、单向不带头链表实现实现完整代码 4、带头双向循环链表实现实现完整代码 前言 引入 在上一篇文章中&#xff0c;我们认识了顺序表&#xff0c;但是在许多情况中&#xff0c;顺序表在处理一些事件时还存在许多问题&#xff0c;比…

鸿鹄电子招投标系统:企业战略布局下的采购寻源解决方案

在数字化采购领域&#xff0c;企业需要一个高效、透明和规范的管理系统。通过采用Spring Cloud、Spring Boot2、Mybatis等先进技术&#xff0c;我们打造了全过程数字化采购管理平台。该平台具备内外协同的能力&#xff0c;通过待办消息、招标公告、中标公告和信息发布等功能模块…

数据分析——快递电商

一、任务目标 1、任务 总体目的——对账 本项目解决同时使用多个快递发货&#xff0c;部分隔离区域出现不同程度涨价等情形下&#xff0c;如何快速准确核对账单的问题。 1、在订单表中新增一列【运费差异核对】来表示订单运费实际有多少差异&#xff0c;结果为数值。 2、将…

【无标题】关于异常处理容易犯的错

一般项目是方法打上 try…catch…捕获所有异常记录日志&#xff0c;有些会使用 AOP 来进行类似的“统一异常处理”。 其实&#xff0c;这种处理异常的方式非常不可取。那么今天&#xff0c;我就和你分享下不可取的原因、与异常处理相关的坑和最佳实践。 捕获和处理异常容易犯…

Feature Fusion for Online Mutual KD

paper&#xff1a;Feature Fusion for Online Mutual Knowledge Distillation official implementation&#xff1a;https://github.com/Jangho-Kim/FFL-pytorch 本文的创新点 本文提出了一个名为特征融合学习&#xff08;Feature Fusion Learning, FFL&#xff09;的框架&…

行走在深度学习的幻觉中:问题缘由与解决方案

如何解决大模型的「幻觉」问题&#xff1f; 我们在使用深度学习大模型如LLM&#xff08;Large Language Models&#xff09;时&#xff0c;可能会遇到一种被称为“幻觉”的现象。没错&#xff0c;它并不是人脑中的错觉&#xff0c;而是模型对特定模式的过度依赖&#xff0c;这…

Linux---gcc编译

目录 前言 一、gcc编译 二、程序的编译过程 三、gcc查看编译过程 1.预处理阶段 2.编译 3.汇编 4.链接 动静态库链接的内容 动静态库链接的优缺点 5.总结记忆 前言 在前面我们学会使用vim对文件进行编辑&#xff0c;如果是C或者C程序&#xff0c;我们编辑好了内容…

【DDR】基于Verilog的DDR控制器的简单实现(一)——初始化

在FPGA中&#xff0c;大规模数据的存储常常会用到DDR。为了方便用户使用&#xff0c;Xilinx提供了DDR MIG IP核&#xff0c;用户能够通过AXI接口进行DDR的读写访问&#xff0c;然而MIG内部自动实现了许多环节&#xff0c;不利于用户深入理解DDR的底层逻辑。 本文以美光(Micro…

【算法刷题】Day28

文章目录 1. 买卖股票的最佳时机 III题干&#xff1a;算法原理&#xff1a;1. 状态表示&#xff1a;2. 状态转移方程3. 初始化4. 填表顺序5. 返回值 代码&#xff1a; 2. Z 字形变换题干&#xff1a;算法原理&#xff1a;1. 模拟2. 找规律 代码&#xff1a; 1. 买卖股票的最佳时…

重学Java 4 进制转换和位运算

天赋不好好使用的话&#xff0c;可是会被收回的哦 ——24.1.13 一、进制转换 1.常用的进制 2.十进制和二进制之间的转换 1.十进制转二进制 辗转相除法——循环除以2&#xff0c;取余数&#xff0c;除到商为0为止&#xff0c;除完后&#xff0c;由下往上&#xff0c;得出换算后…

JVM基础(7)——ParNew垃圾回收器

作者简介&#xff1a;大家好&#xff0c;我是smart哥&#xff0c;前中兴通讯、美团架构师&#xff0c;现某互联网公司CTO 联系qq&#xff1a;184480602&#xff0c;加我进群&#xff0c;大家一起学习&#xff0c;一起进步&#xff0c;一起对抗互联网寒冬 学习必须往深处挖&…

Kafka(四)Broker

目录 1 配置Broker1.1 Broker的配置broker.id0listererszookeeper.connectlog.dirslog.dir/tmp/kafka-logsnum.recovery.threads.per.data.dir1auto.create.topics.enabletrueauto.leader.rebalance.enabletrue, leader.imbalance.check.interval.seconds300, leader.imbalance…

Redis之集群方案比较

哨兵模式 在redis3.0以前的版本要实现集群一般是借助哨兵sentinel工具来监控master节点的状态&#xff0c;如果master节点异常&#xff0c;则会做主从切换&#xff0c;将某一台slave作为master&#xff0c;哨兵的配置略微复杂&#xff0c;并且性能和高可用性等各方面表现一般&a…

Node.js和npm

目录 01_Node.js01.什么是 Node.js目标讲解小结 02.fs模块-读写文件目标讲解小结 03.path模块-路径处理目标讲解小结 04.案例-压缩前端html目标讲解小结 05.认识URL中的端口号目标讲解小结 06.http模块-创建Web服务目标讲解小结 07.案例-浏览时钟目标讲解小结 02_Node.js模块化…