深度学习笔记(五)——网络优化(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.mzph.cn/news/619740.shtml

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

相关文章

【Linux笔记】自定义一个简单的shell

一、命令行解释器shell的原理 我们已经知道Linux给我们提供了一系列由exec开头的系统调用接口&#xff0c;可以让我们在自己所写的程序中调用各种指令或者我们自己写的其他程序&#xff1a; 而我们的shell命令行解释器也是接收用户输入的指令&#xff0c;然后执行&#xff1a;…

腾讯滑块(1-13,js逆向)

前言&#xff1a;之前打算写的猿人学比赛题系列因为种种原因耽搁了&#xff0c;主要还是比完赛之后热情就少了很多&#xff0c;看到评论区有人说做了这么久才做出一题&#xff0c;这里需要狡辩一下&#xff0c;我虽然菜但是还没到那种地步&#xff0c;比赛两天时间里我跟队友是…

CH341 SPI方式烧录BK7231U

CH341是一个USB总线的转接芯片&#xff0c;通过USB总线提供异步串口、打印口、并口以及常用的2线和4线等同步串行接口。 BK7231U Wi-Fi SOC芯片&#xff0c;内嵌处理器。1. 符合802.11b/g/n 1x1协议 2. 17dBm 输出功率3. 支持20/40 MHz带宽和STBC 4. 支持Wi-Fi STA、AP、…

ftp安装与配置 云服务器 CentOS7

1、FTP的安装 #安装 yum install -y vsftpd#设置开机启动 systemctl enable vsftpd.service#启动 systemctl start vsftpd.service#停止 systemctl stop vsftpd.service#查看状态 systemctl status vsftpd.service 2、配置FTP #修改前先进行备份文件 cp /etc/vsftpd/vsftpd…

腾讯云怎么领取免费云服务器?

腾讯云免费服务器申请入口 https://curl.qcloud.com/FJhqoVDP 免费服务器可选轻量应用服务器和云服务器CVM&#xff0c;轻量配置可选2核2G3M、2核8G7M和4核8G12M&#xff0c;CVM云服务器可选2核2G3M和2核4G3M配置&#xff0c;腾讯云服务器网txyfwq.com分享2024年最新腾讯云免费…

弟12章 1 网络编程

文章目录 网络协议概述 p164TCP协议与UDP协议的区别 p165 网络协议概述 p164 ipv4&#xff1a;十进制点分制 ipv6&#xff1a;十六进制冒号分隔 TCP协议与UDP协议的区别 p165 tcp协议的三次握手&#xff1a;

行为型设计模式——备忘录模式

备忘录模式 备忘录模式提供了一种状态恢复的实现机制&#xff0c;使得用户可以方便地回到一个特定的历史步骤&#xff0c;当新的状态无效或者存在问题时&#xff0c;可以使用暂时存储起来的备忘录将状态复原&#xff0c;很多软件都提供了撤销&#xff08;Undo&#xff09;操作…

【教程】微信小程序如何拍摄图片及视频并上传到后台进行存储

需求分析 在微信小程序中需要使用手机拍摄照片以及视频上传到后台进行进一步的操作&#xff0c;需要解决以下两个问题&#xff1a; 微信小程序如何拍摄照片及视频如何将拍摄的照片及视频上传到后台进行存储 解决方案 前端开发&#xff1a;微信小程序原生 后端开发&#xf…

sentinel熔断与限流

文章目录 一、sentinel简介Sentinel 是什么&#xff1f;Sentinel安装 二、sentinel整合工程新建cloudalibaba-sentinel-service8401微服务引入依赖yml配置主启动类添加EnableDiscoveryClient业务类测试 三、sentinel流控规则基本介绍流控模式直接&#xff08;默认&#xff09;关…

Web前端-移动web开发_流式布局

文章目录 移动web开发流式布局1.0 移动端基础1.1浏览器现状1.2 手机屏幕的现状1.3常见移动端屏幕尺寸1.4移动端调试方法 2.0 视口2.1 布局视口 layout viewport2.2视觉视口 visual viewport2.3理想视口 ideal viewport&#xff08;苹果&#xff09;2.4meta标签 3.0 物理像素(手…

十三、QPalette的简单使用(Qt5 GUI系列)

目录 一、设计需求 二、实现代码 三、代码解析 四、总结 一、设计需求 在实际应用中&#xff0c;经常需要改变某个控件的颜色外观&#xff0c;如背景、文字颜色等。Qt提供的调色板类 QPalette 专门用于管理对话框的外观显示。QPalette 类相当于对话框或是控件的调色板&…

记录:排查create_ap偶发无法开启自发AP的问题

背景说明&#xff1a; 系统&#xff1a;Xubuntu16.04&#xff1b;内核&#xff1a;4.14&#xff1b;无线网卡&#xff1a;EDIMAX EW-7822UAC 关于无线网卡的驱动安装和create_ap配置参考博文&#xff1a;Xubuntu16.04系统中使用EDIMAX EW-7822UAC无线网卡开启5G自发AP 目录 问题…

分布式系统的三字真经CAP

文章目录 前言C&#xff08;Consistency 数据一致性&#xff09;A&#xff08;Availability 服务可用性&#xff09;P&#xff08;Partition Tolerance 分区容错性&#xff09;CAP理论最后 前言 你好&#xff0c;我是醉墨居士&#xff0c;我一起探索一下分布式系统的三字真经C…

大数据调度框架Oozie,这个学习网站让你事半功倍!

Oozie是一个基于工作流引擎的开源框架&#xff0c;由Cloudera公司贡献给Apache。它主要用于管理和调度Apache Hadoop作业&#xff0c;支持的任务类型包括Hadoop MapReduce、Pig Jobs等。 Oozie的核心概念包括workflow jobs和coordinator jobs。Workflow jobs是由多个动作&#…

Jmeter 性能-监控服务器

Jmeter监控Linux需要三个文件 JMeterPlugins-Extras.jar (包&#xff1a;JMeterPlugins-Extras-1.4.0.zip) JMeterPlugins-Standard.jar (包&#xff1a;JMeterPlugins-Standard-1.4.0.zip) ServerAgent-2.2.3.zip 1、Jemter 安装插件 在插件管理中心的搜索Servers Perform…

AIGC视频生成:Pika1.0快速入门详解

Pika1.0快速入门详解 一、简介二、登录三、参数设置1、改变画面大小&#xff08;Aspect ratio&#xff09;2、改变帧数大小&#xff08;Frames per second&#xff09;3、镜头平移&#xff08;Camera control&#xff09;4、画面运动控制&#xff08;Strength of motion&#x…

永不停止,永远在路上!MIAOYUN 2023年度回顾

2023 MIAOYUN年度关键词&#xff1a;坚持/沉淀/成长 2023年&#xff0c;我们身处虚浮遥荡的世界&#xff1a;支原体肺炎、流感接二连三、经济下行成热词、人人思危&#xff1b;更有暴雨成灾&#xff0c;核污水扩散&#xff0c;战火与地震不断。 坏事发生时&#xff0c;你有三种…

【NLP】多标签分类【上】

简介 《【NLP】多标签分类》主要介绍利用三种机器学习方法和一种序列生成方法来解决多标签分类问题&#xff08;包含实验与对应代码&#xff09;。共分为上下两篇&#xff0c;上篇聚焦三种机器学习方法&#xff0c;分别是&#xff1a;Binary Relevance (BR)、Classifier Chain…

绝地求生:【PC】第27赛季第2轮更新公告

各位玩家大家好&#xff01;欢迎收看本期闲游盒更新公告。 正式服维护时间 ※ 下列时间可能会根据维护情况而发生变化。 1月10日上午8:00 – 下午4:30 地图轮换 ※ 地图轮换将于每周三上午10点进行。 ※ 在随机选择地图的地区中&#xff0c;第1周可选择荣都地图&#xff0c…

Java SE入门及基础(11)

程序调试 1. 什么是程序调试 当程序出现问题时&#xff0c;我们希望程序能够暂停下来&#xff0c;然后通过我们操作使代码逐行执行&#xff0c;观察整个过程中变量的变化是否按照我们设计程序的思维变化&#xff0c;从而找问题并解决问题&#xff0c;这个过程称之为程序调试…