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

【深度学习】基于多层感知机的手写数字识别

案例2:构建自己的多层感知机: MNIST手写数字识别

相关知识点: numpy科学计算包,如向量化操作,广播机制等

1 任务目标

1.1 数据集简介

​ MNIST手写数字识别数据集是图像分类领域最常用的数据集之一,它包含60,000张训练图片,10,000张测试图片,图片中的数字均被缩放到同一尺寸且置于图像中央,图片大小为28×28。MNIST数据集中的每个样本都是一个大小为784×1的矩阵(从28×28转换得到)。MNIST数据集中的数字包括0到9共10类,如下图所示。注意,任何关于测试集的信息都不该被引入训练过程。

image.png

​ 在本次案例中,我们将构建多层感知机来完成MNIST手写数字识别。

1.2 构建多层感知机

​ 本次案例提供了若干初始代码,可基于初始代码完成案例,各文件简介如下:
(运行初始代码之前请自行安装TensorFlow 2.0及以上版本,仅用于处理数据集,禁止直接调用TensorFlow函数)

  • mlp.ipynb包含了本案例的主要内容,运行文件需安装Jupyter Noterbook.

  • network.py定义了网络,包括其前向和后向计算。

  • optimizer.py定义了随机梯度下降(SGD),用于完成反向传播和参数更新。

  • solver.py定义了训练和测试过程需要用到的函数。

  • plot.py用来绘制损失函数和准确率的曲线图。

​ 此外,在/criterion//layers/路径下使用模块化的思路定义了多个层,其中每个层均包含三个函数:__init__用来定义和初始化一些变量, f o r w a r d forward forward b a c k w a r d backward backward函数分别用来完成前向和后向计算:

  • FCLayer为全连接层,输入为一组向量(必要时需要改变输入尺寸以满足要求),与权重矩阵作矩阵乘法并加上偏置项,得到输出向量: u = W x + b u=Wx+b u=Wx+b

  • SigmoidLayer s i g m o i d sigmoid sigmoid激活层,根据 f ( u ) = 1 1 + e − u f(u)=\frac{1}{1+e^{-u}} f(u)=1+eu1计算输出。

  • ReLULayer R e L U ReLU ReLU激活层,根据 f ( u ) = m a x ( 0 , u ) f(u)=max(0,u) f(u)=max(0,u)计算输出。

  • EuclideanLossLayer为欧式距离损失层,计算各样本误差的平方和得到: 1 2 ∑ n ∣ ∣ l o g i t s ( n ) − g t ( n ) ∣ ∣ 2 2 \frac{1}{2}\sum_n||logits(n)-gt(n)||_2^2 21n∣∣logits(n)gt(n)22

  • SoftmaxCrossEntropyLossLayer可以看成是输入到如下概率分布的映射:
    P ( t k = 1 / x ) = e X K ∑ j = 1 K e X j P(t_k=1/x)=\frac{e^{X_K}}{\sum_{j=1}^Ke^{X_j}} P(tk=1/x)=j=1KeXjeXK
    其中 X k X_k Xk是输入向量X中的第k个元素, P ( t k = 1 / x ) P(t_k=1/x) P(tk=1/x)该输入被分到第 k k k个类别的概率。由于 s o f t m a x softmax softmax层的输出可以看成一组概率分布,我们可以计算delta似然及其对数形式,称为Cross Entropy误差函数:
    E = − l n p ( t ( 1 ) , . . . , t ( N ) ) = ∑ n = 1 N E ( n ) E=-ln\ p(t^{(1)},...,t^{(N)})=\sum_{n=1}^NE^{(n)} E=ln p(t(1),...,t(N))=n=1NE(n)
    其中
    E ( n ) = − ∑ k = 1 K t k ( n ) l n h k ( n ) h k ( n ) = P ( t k = 1 / X ( n ) ) = e X k ( n ) ∑ j = 1 K e X j ( n ) E^{(n)}=-\sum_{k=1}^Kt_k^{(n)}ln\ h_k{(n)}\\h_k^{(n)}=P(t_k=1/X^{(n)})=\frac{e^{X_k^{(n)}}}{\sum_{j=1}^Ke^{X_j^{(n)}}} E(n)=k=1Ktk(n)ln hk(n)hk(n)=P(tk=1/X(n))=j=1KeXj(n)eXk(n)

​ 注意:此处的softmax损失层与案例1中有所差异,本次案例中的softmax层不包含可训练的参数,这些可训练的参数被独立成一个全连接层。

1.3 案例要求

​ 完成上述文件里的‘#TODO’部分(红色标记的文件),提交全部代码及一份案例报告,要求如下:

  • 记录训练和测试准确率,绘制损失函数和准确率曲线图;

  • 比较分别使用 S i g m o i d Sigmoid Sigmoid R e L U ReLU ReLU激活函数时的结果,可以从收敛情况、准确率等方面比较;

  • 比较分别使用欧式距离损失和交叉熵损失时的结果;

  • 构造具有两个隐含层的多层感知机,自行选取合适的激活函数和损失函数,与只有一个隐含层的结果相比较;

  • 本案例中给定的超参数可能表现不佳,请自行调整超参数尝试取得更好的结果,记录下每组超参数的结果,并作比较和分析。

1.4 注意事项

  • 提交所有代码和一份案例报告;

  • 注意程序的运行效率,尽量使用矩阵运算,而不是for循环;

  • 本案例中不允许直接使用TensorFlow, Caffe, PyTorch等深度学习框架;

  • 禁止任何形式的抄袭。

2 代码设计

​ 本节中介绍了代码整体架构,以及需要补全部分的函数设计。

2.1 数据处理

本实验进行MNIST手写数字识别,数据集采用 tensorflowtf.keras.datasets.mnist

  1. 划分数据集

    (x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()
    
  2. 数据预处理

    • decode_image() 函数:对图像进行归一化处理。

    该函数将图像的像素值转换为浮点数,然后将图像的形状变为一维的向量,最后将图像的像素值缩放到 (0,1) 之间,并减去图像数据的均值,使得分布接近标准 正态分布

    def decode_image(image):# 归一化处理image = tf.cast(image, tf.float32)image = tf.reshape(image, [784])image = image / 255.0image = image - tf.reduce_mean(image)return image
    
    • decode_label() 函数:将标签变为 one-hot 编码。

      该函数将标签的值转换为一个长度为10的向量,其中只有一个元素为1,其余为0,表示标签的类别。

      def decode_label(label):# 将标签变为one-hot编码return tf.one_hot(label, depth=10)
      
    • 数据预处理:对训练集和测试集的图像和标签进行了预处理。

      将处理后的图像和标签合并为一个数据集,每个元素是一个元组,包含了一个图像和一个标签。

      # 数据预处理
      x_train = tf.data.Dataset.from_tensor_slices(x_train).map(decode_image)
      y_train = tf.data.Dataset.from_tensor_slices(y_train).map(decode_label)
      data_train = tf.data.Dataset.zip((x_train, y_train))x_test = tf.data.Dataset.from_tensor_slices(x_test).map(decode_image)
      y_test = tf.data.Dataset.from_tensor_slices(y_test).map(decode_label)
      data_test = tf.data.Dataset.zip((x_test, y_test))
      
  3. 超参数设置

    本实验中,采用了如下超参数,并对其设置了初值。

    batch_size = 100
    max_epoch = 20
    init_std = 0.01learning_rate_SGD = 0.001
    weight_decay = 0.1disp_freq = 50
    
    • batch_size :表示每次训练时使用的数据样本的数量。
    • max_epoch:表示训练的最大轮数。
    • init_std :表示模型参数的初始化标准差,本次实验并未使用。
    • learning_rate_SGD :表示随机梯度下降(SGD)优化器的学习率。
    • weight_decay :表示权重衰减的系数。
    • disp_freq :表示显示训练信息的频率,也就是每训练多少个批次就打印一次训练指标。

2.2 代码补全

  1. optmizer.py

    该代码实现了一个随机梯度下降(SGD)优化器,用于更新神经网络模型的参数。需要补全的地方是更新梯度的部分,此处代码如下。

    # 计算梯度更新的变化量
    layer.diff_W = - self.learningRate * (layer.grad_W + self.weightDecay * layer.W)
    layer.diff_b = - self.learningRate * layer.grad_b# 更新权重和偏置项
    layer.W += layer.diff_W
    layer.b += layer.diff_b
    

    多层感知机梯度更新公式如下:
    w n e w = w o l d − α ( ∇ J ( w ) + λ w o l d ) b n e w = b o l d − α ∇ J ( b ) w_{new}=w_{old}-\alpha (\nabla J(w)+ \lambda w_{old})\\ b_{new}=b_{old}-\alpha \nabla J(b) wnew=woldα(J(w)+λwold)bnew=boldαJ(b)
    其中 α \alpha α 是学习率, ∇ J ( θ ) \nabla J(\theta) J(θ) 是梯度, λ \lambda λ 是权重衰减的系数。

  2. fc_layer.py

    该代码实现了一个全连接层,用于完全连接前后不同的神经元数的两层。

    • 前向传播(def forward()

      对输入进行线性变换 Y = W X + b Y=WX+b Y=WX+b​ ,然后返回输出。

      def forward(self, Input):"""对输入计算Wx+b并返回结果"""self.input = Inputreturn np.dot(Input, self.W) + self.b
      
    • 反向传播(def backward()

      根据输出的梯度来计算输入的梯度和权重和偏置的梯度,然后返回输入的梯度。
      KaTeX parse error: Undefined control sequence: \part at position 9: \frac {\̲p̲a̲r̲t̲ ̲E^{(n)}}{\part …
      代码如下:

      def backward(self, delta):"""根据delta计算梯度"""self.grad_W = np.dot(self.input.T, delta)self.grad_b = np.sum(delta, axis=0, keepdims=True)delta = np.dot(delta, self.W.T)return delta
      
  3. sigmoid_layer.py

    该代码实现了一个基于 sigmoid 激活函数的激活层。

    • 前向传播(def forward(self, Input)

      对输入进行 Sigmoid 激活函数的处理,然后返回输出。
      f ( x ) = 1 1 + e − x f(x) = \frac{1}{1+e^{-x}} f(x)=1+ex1
      代码如下:

      def forward(self, Input):"""对输入应用Sigmoid激活函数并返回结果"""self.output = 1 / (1 + np.exp(-Input))return self.output
      
    • 反向传播(def backward(self, delta)

      根据输出的梯度来计算输入的梯度,然后返回输入的梯度。
      f ′ ( z ) = f ( z ) ( 1 − f ( z ) ) f^\prime (z)=f(z)(1-f(z)) f(z)=f(z)(1f(z))
      代码如下:

      def backward(self, delta):"""根据delta计算梯度"""return delta * self.output * (1 - self.output)
      
  4. relu_layer.py

    该代码实现了一个基于 Relu 激活函数的激活层。

    • 前向传播(def forward(self, Input)

      对输入进行 Relu 激活函数的处理,然后返回输出。
      f ( z ) = m a x ( 0 , z ) f(z)=max(0,z) f(z)=max(0,z)
      代码如下:

      def forward(self, Input):"""对输入应用ReLU激活函数并返回结果"""self.input = Inputreturn np.maximum(0, Input)
      
    • 反向传播(def backward(self, delta)

      根据输出的梯度来计算输入的梯度,然后返回输入的梯度。
      f ′ ( z ) = { 1 , i f z ≥ 0 0 , e l s e f^\prime (z)=\begin{cases}1,if\ z≥0\\0,else\end{cases} f(z)={1,if z00,else
      代码如下:

      def backward(self, delta):"""根据delta计算梯度"""return delta * (self.input > 0)
      
  5. euclidean_loss.py

    该代码实现了一个欧氏距离损失层。

    • 前向传播(def forward(self, logit, gt)

      对输出和真实标签之间的欧式距离损失进行计算,并返回损失值。它接受两个参数,logit:表示最后一个全连接层的输出结果;gt:表示真实标签。
      L ( y , f ( x ) ) = 1 2 n ∑ i = 1 n ( y i − f ( x i ) ) 2 L(\mathbf{y}, \mathbf{f}(\mathbf{x})) = \frac{1}{2n} \sum_{i=1}^n (\mathbf{y}_i - \mathbf{f}(\mathbf{x}_i))^2 L(y,f(x))=2n1i=1n(yif(xi))2
      代码如下:

      def forward(self, logit, gt):"""输入: (minibatch)- logit: 最后一个全连接层的输出结果, 尺寸(batch_size, 10)- gt: 真实标签, 尺寸(batch_size, 10)"""# 计算欧式距离损失self.logit = logitself.diff = logit - gtself.loss = 0.5 * np.sum(self.diff ** 2) / logit.shape[0]  # 计算平均损失self.acc = np.sum(np.argmax(logit, axis=1) == np.argmax(gt, axis=1)) / logit.shape[0]  # 计算平均准确率return self.loss
      
    • 反向传播(def backward(self)

      根据损失值的梯度来计算输出的梯度,并返回输出的梯度。
      ∂ L ∂ f ( x i ) = 1 n ( f ( x i ) − y i ) \frac{\partial L}{\partial \mathbf{f}(\mathbf{x}_i)} = \frac{1}{n}(\mathbf{f}(\mathbf{x}_i) - \mathbf{y}_i) f(xi)L=n1(f(xi)yi)
      代码如下:

      def backward(self):# 欧式距离损失的梯度即为(logit - gt) / batch_sizereturn self.diff / self.logit.shape[0]
      
  6. softmax_cross_entropy.py

    该代码实现了一个Softmax交叉熵损失层。

    • 前向传播(def forward(self, logit, gt)

      对输出和真实标签之间的Softmax交叉熵损失进行计算,并返回损失值。它接受两个参数,logit:表示最后一个全连接层的输出结果;gt:表示真实标签。

      交叉熵损失函数:
      E ( θ ) = − 1 n l n P ( t ( 1 ) , . . . , t ( n ) ) = − 1 n ∑ n = 1 n ( t ( n ) l n h ( x ( n ) + ( 1 − t ( n ) ) l n ( 1 − h ( x ( n ) ) ) E(θ)=-\frac{1}{n}lnP(t^{(1)},...,t^{(n)})=-\frac{1}{n}\sum^{n}_{n=1}\left(t^{(n)}ln\ h(x^{(n)}+(1-t^{(n)})ln\ (1-h(x^{(n)})\right)\\ E(θ)=n1lnP(t(1),...,t(n))=n1n=1n(t(n)ln h(x(n)+(1t(n))ln (1h(x(n)))
      平均准确率:
      a c c u r a c y = 正确分类的样本数 总样本数 \mathbf{accuracy} = \frac{\text{正确分类的样本数}}{\text{总样本数}} accuracy=总样本数正确分类的样本数
      ​ 对 logitgt 分别沿着第二个维度求最大值的索引,也就是得到每个样本的预测类别和真实类别,然后比较它们是否相等,得到一个一维布尔数组,表示每个样本是否正确分类。

      ​ 然后,它对这个数组求和,得到一个标量,表示正确分类的样本数。然后,它除以 batch_size ,得到一个标量,表示平均准确率,保存在 self.acc 中。

      代码如下:

      def forward(self, logit, gt):"""输入: (minibatch)- logit: 最后一个全连接层的输出结果, 尺寸(batch_size, 10)- gt: 真实标签, 尺寸(batch_size, 10)"""# 计算softmax激活函数exp_logit = np.exp(logit - np.max(logit, axis=1, keepdims=True))self.softmax_output = exp_logit / np.sum(exp_logit, axis=1, keepdims=True)# 计算交叉熵损失self.loss = -np.sum(gt * np.log(self.softmax_output + EPS)) / logit.shape[0]# 计算平均准确率self.acc = np.sum(np.argmax(logit, axis=1) == np.argmax(gt, axis=1)) / logit.shape[0]# 保存真实标签,用于反向传播self.gt = gtreturn self.loss
      
    • 反向传播(def backward(self)

      根据损失值的梯度来计算输出的梯度,并返回输出的梯度。
      ∇ E ( θ ) = 1 N ∑ N x ( n ) ( h ( x ( n ) ) − t ( n ) ) \nabla E(\theta)=\frac{1}{N}\sum_Nx^{(n)}(h(x^{(n)})-t^{(n)}) E(θ)=N1Nx(n)(h(x(n))t(n))
      代码如下:

      def backward(self):# 计算梯度return np.subtract(self.softmax_output, self.gt) / self.gt.shape[0]
      

3 多层感知机训练

​ 本实验分别使用了欧氏距离损失函数、Softmax交叉熵损失函数来训练具有唯一激活层的多层感知机,之后再以Softmax交叉熵作为损失函数,训练了具有两层隐含层的多层感知机。

3.1 使用欧氏距离损失训练多层感知机

  1. 使用 欧式距离损失Sigmoid激活函数 训练多层感知机

    本次训练采用3层感知机进行训练。

    • 第一层为全连接层,将784个神经元的输入,转化为128个神经元的输出。
    • 第二层采用 Sigmoid 激活层,为128个神经元进行非线性变换。
    • 第三层为全连接层,将128个神经元的输入,转化为对应数字0到9的10个输出。
    sigmoidMLP = Network()
    # 使用FCLayer和SigmoidLayer构建多层感知机
    # 128为隐含层的神经元数目
    sigmoidMLP.add(FCLayer(784, 128))
    sigmoidMLP.add(SigmoidLayer())
    sigmoidMLP.add(FCLayer(128, 10))
    

    训练结束后,在测试集上进行测试,准确率为 0.7810。

  2. 使用 欧式距离损失Relu激活函数 训练多层感知机

    本次训练采用3层感知机进行训练。

    • 第一层为全连接层,将784个神经元的输入,转化为128个神经元的输出。
    • 第二层采用 Relu 激活层,为128个神经元进行非线性变换。
    • 第三层为全连接层,将128个神经元的输入,转化为对应数字0到9的10个输出。
    reluMLP = Network()
    # 使用FCLayer和ReLULayer构建多层感知机
    reluMLP.add(FCLayer(784, 128))
    reluMLP.add(ReLULayer())
    reluMLP.add(FCLayer(128, 10))
    

    训练结束后,在测试集上进行测试,准确率为 0.8586。

  3. 训练曲线对比

    绘制了 loss 曲线与 acc 曲线,对比以上两个感知机的训练结果。

    image-20240203021442746

    Sigmoid 的损失训练的初值低于 Relu,然而在训练过程中收敛效果不如 Relu,20轮训练后 Relu 损失更小。

    image-20240203021638630

    Relu 训练过程中的准确率始终高于 Sigmoid 的准确率。

    由以上训练结果可知,在使用欧氏距离作为损失函数时,Relu 作为隐藏层激活函数效果好于 Sigmoid 函数。

3.2 使用Softmax交叉熵损失训练多层感知机

  1. 使用 Softmax交叉熵损失Sigmoid激活函数 训练多层感知机

    本次训练采用3层感知机进行训练。

    • 第一层为全连接层,将784个神经元的输入,转化为128个神经元的输出。
    • 第二层采用 Sigmoid 激活层,为128个神经元进行非线性变换。
    • 第三层为全连接层,将128个神经元的输入,转化为对应数字0到9的10个输出。
    # 使用FCLayer和SigmoidLayer构建多层感知机
    # 128为隐含层的神经元数目
    sigmoidMLP.add(FCLayer(784, 128))
    sigmoidMLP.add(SigmoidLayer())
    sigmoidMLP.add(FCLayer(128, 10))
    

    训练结束后,在测试集上进行测试,准确率为 0.6968。

  2. 使用 Softmax交叉熵损失Relu激活函数 训练多层感知机

    本次训练采用3层感知机进行训练。

    • 第一层为全连接层,将784个神经元的输入,转化为128个神经元的输出。
    • 第二层采用 Relu 激活层,为128个神经元进行非线性变换。
    • 第三层为全连接层,将128个神经元的输入,转化为对应数字0到9的10个输出。
    reluMLP = Network()
    # 使用FCLayer和ReLULayer构建多层感知机
    reluMLP.add(FCLayer(784, 128))
    reluMLP.add(ReLULayer())
    reluMLP.add(FCLayer(128, 10))
    

    训练结束后,在测试集上进行测试,准确率为 0.8687。

  3. 训练曲线对比

    绘制了 loss 曲线与 acc 曲线,对比以上两个感知机的训练结果。

    image-20240203022103359

    Sigmoid 的损失下降速率较慢,而 Relu 的损失下降明显更好,始终低于前者。

    image-20240203022350249

    Relu 的准确率始终高于 Sigmoid,且 Relu+Softmax交叉损失 的组合较好于 Relu+欧氏距离损失 的组合,Sigmoid+Softmax交叉损失 的组合差于 Sigmoid+欧氏距离损失 的组合。

    由以上训练结果可知,在使用Softmax交叉损失作为损失函数时,Relu 作为隐藏层激活函数效果好于 Sigmoid 函数;且好于用欧式距离作为损失函数的训练效果。

3.3 具有两层隐含层的多层感知机

​ 本章中采用 Softmax交叉损失 作为损失函数,将 Relu 和 Sigmoid 组成四组组合,作为隐藏层的两层激活层,进行训练。

  1. 隐藏层为 两层Relu函数 的多层感知机

    本次训练采用4层感知机进行训练。

    • 第一层为全连接层,将784个神经元的输入,转化为128个神经元的输出。
    • 第二层采用 Relu 激活层,为128个神经元进行非线性变换。
    • 第三层采用 Relu 激活层,为128个神经元进行非线性变换。
    • 第四层为全连接层,将128个神经元的输入,转化为对应数字0到9的10个输出。
    relu2MLP = Network()
    # 128为隐含层的神经元数目
    relu2MLP.add(FCLayer(784, 128))
    relu2MLP.add(ReLULayer())
    relu2MLP.add(ReLULayer())
    relu2MLP.add(FCLayer(128, 10))
    

    训练结束后,在测试集上进行测试,准确率为 0.8696。

    绘制曲线,与1层 Relu 进行对比。

    image-20240203023106008

    image-20240203023112616

    与1层 Relu 对比,2层 Relu 训练效果略微提升,但是提升不大。

  2. 隐藏层为 两层Sigmoid函数 的多层感知机

    本次训练采用4层感知机进行训练。

    • 第一层为全连接层,将784个神经元的输入,转化为128个神经元的输出。
    • 第二层采用 Sigmoid 激活层,为128个神经元进行非线性变换。
    • 第三层采用 Sigmoid 激活层,为128个神经元进行非线性变换。
    • 第四层为全连接层,将128个神经元的输入,转化为对应数字0到9的10个输出。
    sigmoid2MLP = Network()
    # 128为隐含层的神经元数目
    sigmoid2MLP.add(FCLayer(784, 128))
    sigmoid2MLP.add(SigmoidLayer())
    sigmoid2MLP.add(SigmoidLayer())
    sigmoid2MLP.add(FCLayer(128, 10))
    

    训练结束后,在测试集上进行测试,准确率为 0.1137。

    绘制曲线,与1层 Sigmoid 进行对比。

    image-20240203023225950

    image-20240203023230126

    在使用两层 Sigmoid 作为隐藏层时,训练结果极差,出现了梯度消失的现象。在训练两轮之后, Loss 值不再降低,准确率不再提升。

  3. 隐藏层 先为Relu层,后为Sigmoid层 的多层感知机

    本次训练采用4层感知机进行训练。

    • 第一层为全连接层,将784个神经元的输入,转化为128个神经元的输出。
    • 第二层采用 Relu 激活层,为128个神经元进行非线性变换。
    • 第三层采用 Sigmoid 激活层,为128个神经元进行非线性变换。
    • 第四层为全连接层,将128个神经元的输入,转化为对应数字0到9的10个输出。
    ReluSigmoidMLP = Network()
    # 128为隐含层的神经元数目
    ReluSigmoidMLP.add(FCLayer(784, 128))
    ReluSigmoidMLP.add(ReLULayer())
    ReluSigmoidMLP.add(SigmoidLayer())
    ReluSigmoidMLP.add(FCLayer(128, 10))
    

    训练结束后,在测试集上进行测试,准确率为 0.6315。

  4. 隐藏层 先为Sigmoid层,后为Relu层 的多层感知机

    本次训练采用4层感知机进行训练。

    • 第一层为全连接层,将784个神经元的输入,转化为128个神经元的输出。
    • 第二层采用 Sigmoid 激活层,为128个神经元进行非线性变换。
    • 第三层采用 Relu 激活层,为128个神经元进行非线性变换。
    • 第四层为全连接层,将128个神经元的输入,转化为对应数字0到9的10个输出。
    SigmoidReluMLP = Network()
    # 128为隐含层的神经元数目
    SigmoidReluMLP.add(FCLayer(784, 128))
    SigmoidReluMLP.add(SigmoidLayer())
    SigmoidReluMLP.add(ReLULayer())
    SigmoidReluMLP.add(FCLayer(128, 10))
    

    训练结束后,在测试集上进行测试,准确率为 0.6996。

    绘制曲线,与 先为Relu层,后为Sigmoid层 的做对比。

    image-20240203023631497

    image-20240203023638358

    由上图可知,先 Sigmoid 层,后 Relu 层的效果更好,但是两者效果都不如两层都是 Relu 的效果好。

4 寻找最佳超参数

​ 本章通过遍历不同超参数,探索超参数对训练结果的影响,寻找最佳的超参数。

4.1 利用网格搜索寻找最佳超参数

编写代码,遍历超参数可能的取值,寻找最佳超参数。

from criterion import SoftmaxCrossEntropyLossLayer
from optimizer import SGD
from layers import FCLayer, ReLULayer
import itertools
import gc# 定义超参数的可能取值
batch_sizes = [32,64,128]
max_epochs = [10,20,30]
learning_rates = [0.001, 0.005, 0.01]
weight_decays = [0.1, 0.01, 0.001]# 保存最佳结果的变量
best_accuracy = 0.0
best_hyperparameters = {}
criterion = SoftmaxCrossEntropyLossLayer()def build_and_train_model(batch_size, max_epoch, learning_rate, weight_decay):sgd = SGD(learning_rate, weight_decay)model = Network()# 128为隐含层的神经元数目model.add(FCLayer(784, 128))model.add(ReLULayer())model.add(ReLULayer())model.add(FCLayer(128, 10))model, model_loss, model_acc = train(model, criterion, sgd, data_train, max_epoch, batch_size, 1000)return model, model_loss, model_acc# 遍历所有超参数组合
for batch_size, max_epoch, learning_rate, weight_decay in itertools.product(batch_sizes, max_epochs, learning_rates, weight_decays
):# 构建和训练模型(使用当前超参数组合)model, model_loss, model_acc = build_and_train_model(batch_size, max_epoch, learning_rate, weight_decay)accuracy = test(model, criterion, data_test, batch_size, disp_freq)# 如果当前组合的性能更好,则更新最佳结果if accuracy > best_accuracy:best_accuracy = accuracybest_hyperparameters = {'batch_size': batch_size,'max_epoch': max_epoch,'learning_rate': learning_rate,'weight_decay': weight_decay}del model  # 删除网络对象gc.collect()     # 执行垃圾回收# 打印最佳结果
print("Best Hyperparameters:", best_hyperparameters)
print("Best Accuracy:", best_accuracy)

因为内存空间不足,未能跑完所有的超参数,在有限的内存中跑出的最佳参数,及测试结果如下。

Best Hyperparameters: {‘batch_size’: 32, ‘max_epoch’: 20, ‘learning_rate’: 0.005, ‘weight_decay’: 0.001} Best Accuracy: 0.9524238782051282

可以观察到,在batch_size选择较小值,训练轮次较大,学习率较高,权重衰减较小时,结果更好。

4.2 探寻学习率对训练结果的影响

编写代码,让模型分别在学习率为 [0.001, 0.005, 0.01] 的值训练,对比训练结果,寻找最佳的取值。

# 探寻学习率对训练结果的影响
from criterion import SoftmaxCrossEntropyLossLayer
from optimizer import SGD
from layers import FCLayer, ReLULayerlrs = [0.001, 0.005, 0.01]
loss_lrs = []
acc_lrs = []
# 单层Relu,学习率0.1
criterion = SoftmaxCrossEntropyLossLayer()for lr in lrs:sgd = SGD(lr, 0.001)model = Network()# 128为隐含层的神经元数目model.add(FCLayer(784, 128))model.add(ReLULayer())model.add(FCLayer(128, 10))model, model_loss, model_acc = train(model, criterion, sgd, data_train, max_epoch, batch_size, 1000)loss_lrs.append(model_loss)acc_lrs.append(model_acc)plot_loss_and_acc({'lr=0.001': [loss_lrs[0], acc_lrs[0]],'lr=0.005': [loss_lrs[1], acc_lrs[1]],'lr=0.01': [loss_lrs[2], acc_lrs[2]]})

训练结果:

image-20240203024449115

image-20240203024454243

由上图可知,学习率越高,训练结果越好,其中学习率为 0.01 时训练效果最好。

4.3 探寻权重衰减对训练效果的影响

编写代码,让模型分别在权重衰减为 [0.001, 0.005, 0.01] 的值训练,对比训练结果,寻找最佳的取值。

# 探寻权重衰减对训练效果的影响
from criterion import SoftmaxCrossEntropyLossLayer
from optimizer import SGD
from layers import FCLayer, ReLULayerwds = [0.001, 0.005, 0.01]
loss_wds = []
acc_wds = []
criterion = SoftmaxCrossEntropyLossLayer()for wd in wds:sgd = SGD(0.005, wd)model = Network()# 128为隐含层的神经元数目model.add(FCLayer(784, 128))model.add(ReLULayer())model.add(FCLayer(128, 10))model, model_loss, model_acc = train(model, criterion, sgd, data_train, max_epoch, batch_size, 1000)loss_wds.append(model_loss)acc_wds.append(model_acc)plot_loss_and_acc({'wd=0.001': [loss_wds[0], acc_wds[0]],'wd=0.005': [loss_wds[1], acc_wds[1]],'wd=0.01': [loss_wds[2], acc_wds[2]]})

训练结果:

image-20240203024613105

image-20240203024619311

由上图可知,权重衰减值越小,训练结果最好,其中权重衰减值为 0.001 时结果最好。

4.4 探寻batch_size对训练效果的影响

编写代码,让模型分别在batch_size为 [32, 64, 128] 的值训练,对比训练结果,寻找最佳的取值。

# 探寻batch_size对训练效果的影响
from criterion import SoftmaxCrossEntropyLossLayer
from optimizer import SGD
from layers import FCLayer, ReLULayerbss = [32, 64, 128]
loss_bss = []
acc_bss = []
criterion = SoftmaxCrossEntropyLossLayer()for bs in bss:sgd = SGD(0.01, 0.001)model = Network()# 128为隐含层的神经元数目model.add(FCLayer(784, 128))model.add(ReLULayer())model.add(FCLayer(128, 10))model, model_loss, model_acc = train(model, criterion, sgd, data_train, max_epoch, bs, 1000)loss_bss.append(model_loss)acc_bss.append(model_acc)plot_loss_and_acc({'batch_size=32': [loss_bss[0], acc_bss[0]],'batch_size=64': [loss_bss[1], acc_bss[1]],'batch_size=128': [loss_bss[2], acc_bss[2]]})

image-20240203024730083

image-20240203024737358

由上图可知,batch size 越小,训练效果越好,其中 batch size = 32 时训练效果最好。

4.5 测试最佳多层感知机

​ 根据以上研究,我选取了以下超参数,作为本次实验找到的最佳超参数,并选取了 Softmax交叉熵 作为损失函数,两层 Relu 作为隐藏层进行测试,得到的结果即为本实验训练出的最佳多层感知机。

  • batch size = 32:每批次选取32张图片进行训练。
  • max epoch = 50:进行50轮迭代训练。
  • learning rate = 0.1:学习率设置为0.1。
  • weight decay = 0.001:权重衰减设置为0.001。
# 最佳训练超参数
from criterion import SoftmaxCrossEntropyLossLayer
from optimizer import SGD
from layers import FCLayer, ReLULayerbatch_size = 32
max_epoch = 50learning_rate_SGD = 0.1
weight_decay = 0.001disp_freq = 1000
criterion = SoftmaxCrossEntropyLossLayer()sgd = SGD(learning_rate_SGD, weight_decay)Best_model = Network()
# 128为隐含层的神经元数目
Best_model.add(FCLayer(784, 128))
Best_model.add(ReLULayer())
Best_model.add(ReLULayer())
Best_model.add(FCLayer(128, 10))
Best_model, Best_model_loss, Best_model_acc = train(Best_model, criterion, sgd, data_train, max_epoch, batch_size, disp_freq)test(Best_model, criterion, data_test, batch_size, disp_freq)

本实验得到的最佳多层感知机测试准确率为0.9759。

绘图对比这组超参数与之前训练较好的 Relu+Softmax交叉熵损失 组合,发现该组结果明显很好。

image-20240203024948942

image-20240203024953683

相关文章:

【深度学习】基于多层感知机的手写数字识别

案例2:构建自己的多层感知机: MNIST手写数字识别 相关知识点: numpy科学计算包,如向量化操作,广播机制等 1 任务目标 1.1 数据集简介 ​ MNIST手写数字识别数据集是图像分类领域最常用的数据集之一,它包含60,000张训练图片&am…...

给定n,m(200),构造一个n*m的矩阵a,使得每个4*4的子矩阵,左上角2*2的子矩阵的异或和等于右下角的,左下角的异或和等于右上角的

题目 #include <bits/stdc.h> using namespace std; #define int long long #define pb push_back #define fi first #define se second #define lson p << 1 #define rson p << 1 | 1 const int maxn 1e6 5, inf 1e18 5, maxm 4e4 5, mod 998244353…...

【开源】基于JAVA+Vue+SpringBoot的假日旅社管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 系统介绍2.2 QA 问答 三、系统展示四、核心代码4.1 查询民宿4.2 新增民宿评论4.3 查询民宿新闻4.4 新建民宿预订单4.5 查询我的民宿预订单 五、免责说明 一、摘要 1.1 项目介绍 基于JAVAVueSpringBootMySQL的假日旅社…...

kafka 文件存储机制

文章目录 1. 思考四个问题&#xff1a;1.1 topic中partition存储分布&#xff1a;1.2 partiton中文件存储方式&#xff1a;1.3 partiton中segment文件存储结构&#xff1a;1.4 在partition中如何通过offset查找message: 2. kafka日志存储参数配置 Topic是逻辑上的概念&#xff…...

引入BertTokenizer出现OSError: Can‘t load tokenizer for ‘bert-base-uncased‘.

今天在跑一个模型的时候出现该报错&#xff0c;完整报错为&#xff1a; OSError: Cant load tokenizer for bert-base-uncased. If you were trying to load it from https://huggingface.co/models, make sure you dont have a local directory with the same name. Otherwis…...

陶陶摘苹果C++

题目&#xff1a; 代码&#xff1a; #include<iostream> using namespace std; int main(){//一、分析问题//已知&#xff1a;10 个苹果到地面的高度a[10],陶陶把手伸直的时候能够达到的最大高度height//未知&#xff1a;陶陶能够摘到的苹果的数目sum。//关系&#xff…...

STM32F1 引脚重映射功能

STM32 端口引脚重映射 文章目录 STM32 端口引脚重映射前言1、查阅芯片数据手册1.1 串口引脚重映射描述 2、代码部分2.1 核心代码部分 3、实验现象4、总结 前言 在写程序时遇到想要的端口功能&#xff0c;而这个引脚又被其它的功能占用了无法删除掉或直接使用&#xff0c;这种情…...

c语言的各类输出函数(带完善更新)

printf double x; x 218.82631; printf("%-6.2e\n", x);printf(“%-6.2e\n”, x);使用printf函数以指定的格式输出x的值。"%-6.2e"是格式化字符串&#xff0c;其中&#xff1a; %e表示以科学计数法的形式输出浮点数。 6表示输出的总宽度为6个字符&#…...

【linux温故】CFS调度

写在前面 网上关于CFS 调度器的文章多如牛毛&#xff0c;没必要自己写。很多文章写的都非常好。 很多文章里&#xff0c;关键的技术点&#xff0c;都是一样的&#xff0c;只是各个文章说法不一样。 掌握了核心的&#xff0c;关键的&#xff0c;其他的&#xff0c;如果工作中…...

计算机网络之一

目录 1.因特网概述 1.1网络、互连网&#xff08;互联网&#xff09;和因特网 1.2.因特网发展的三个阶段 1.3基于ISP的三层架构的因特网 1.4.因特网的组成 2.三种交换方式 2.1电路交换 2.2分组交换 1.因特网概述 1.1网络、互连网&#xff08;互联网&#xff09;和因特网…...

从一到无穷大 #23 《流计算系统图解》书评

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。 本作品 (李兆龙 博文, 由 李兆龙 创作)&#xff0c;由 李兆龙 确认&#xff0c;转载请注明版权。 文章目录 引言内容总结 引言 春节假期回到家里断然是不会有看纸质书的时间的。造化弄人&#…...

华为问界M9:领跑未来智能交通的自动驾驶黑科技

华为问界M9是一款高端电动汽车&#xff0c;其自动驾驶技术是该车型的重要卖点之一。华为在问界M9上采用了多种传感器和高级算法&#xff0c;实现了在不同场景下的自动驾驶功能&#xff0c;包括自动泊车、自适应巡航、车道保持、自动变道等。 华为问界M9的自动驾驶技术惊艳之处…...

Java图形化界面编程——弹球游戏 笔记

Java也可用于开发一些动画。所谓动画&#xff0c;就是间隔一定的时间(通常小于0 . 1秒 )重新绘制新的图像&#xff0c;两次绘制的图像之间差异较小&#xff0c;肉眼看起来就成了所谓的动画 。 ​ 为了实现间隔一定的时间就重新调用组件的 repaint()方法&#xff0c;可以借助于…...

浅谈人工智能之深度学习~

目录 前言&#xff1a;深度学习的进展 一&#xff1a;深度学习的基本原理和算法 二&#xff1a;深度学习的应用实例 三&#xff1a;深度学习的挑战和未来发展方向 四&#xff1a;深度学习与机器学习的关系 五&#xff1a;深度学习与人类的智能交互 悟已往之不谏&#xff0…...

【复现】大华 DSS SQL 注入漏洞_46

目录 一.概述 二 .漏洞影响 三.漏洞复现 1. 漏洞一&#xff1a; 四.修复建议&#xff1a; 五. 搜索语法&#xff1a; 六.免责声明 一.概述 大华DSS是大华的大型监控管理应用平台&#xff0c;支持几乎所有涉及监控等方面的操作&#xff0c;支持多级跨平台联网等操作。 可…...

Python 中的断点类型详解

前言 在 Python 中&#xff0c;断点是一种在代码中设置的标记&#xff0c;用于在程序执行过程中停止或中断程序的执行&#xff0c;以便调试和查看程序的内部状态。断点是调试工具的关键组成部分&#xff0c;能够帮助开发者定位和解决代码中的错误。本文将详细介绍 Python 中的…...

一步步建立一个C#项目(连续读取S7-1200PLC数据)

这篇博客作为C#的基础系列,和大家分享如何一步步建立一个C#项目完成对S7-1200PLC数据的连续读取。首先创建一个窗体应用。 1、窗体应用 2、配置存储位置 3、选择框架 拖拽一个Button,可以选择视图菜单---工具箱 4、工具箱 拖拽Lable控件和TextBook控件 5、拖拽控件 接下来…...

Hive窗口函数详解

一、 窗口函数知识点 1.1 窗户函数的定义 窗口函数可以拆分为【窗口函数】。窗口函数官网指路&#xff1a; LanguageManual WindowingAndAnalytics - Apache Hive - Apache Software Foundationhttps://cwiki.apache.org/confluence/display/Hive/LanguageManual%20Windowing…...

车载电子电器架构 —— 电子电气系统功能开发

车载电子电器架构 —— 电子电气系统功能开发 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 本就是小人物,输了就是输了,不要在意别人怎么看自己。江湖一碗茶,喝完再挣扎,出门靠自己,四海皆…...

LeetCode--代码详解 7.整数反转

7.整数反转 题目 给你一个 32 位的有符号整数 x &#xff0c;返回将 x 中的数字部分反转后的结果。 如果反转后整数超过 32 位的有符号整数的范围 [−231, 231 − 1] &#xff0c;就返回 0。 假设环境不允许存储 64 位整数&#xff08;有符号或无符号&#xff09;。 示例 …...

《统计学简易速速上手小册》第6章:多变量数据分析(2024 最新版)

文章目录 6.1 主成分分析&#xff08;PCA&#xff09;6.1.1 基础知识6.1.2 主要案例&#xff1a;客户细分6.1.3 拓展案例 1&#xff1a;面部识别6.1.4 拓展案例 2&#xff1a;基因数据分析 6.2 聚类分析6.2.1 基础知识6.2.2 主要案例&#xff1a;市场细分6.2.3 拓展案例 1&…...

创新S3存储桶检索:Langchain社区S3加载器搭载OpenAI API

在瞬息万变的数据存储和处理领域&#xff0c;将高效的云存储解决方案与先进的 AI 功能相结合&#xff0c;为处理大量数据提供了一种变革性的方法。本文演示了使用 MinIO、Langchain 和 OpenAI 的 GPT-3.5 模型的实际实现&#xff0c;重点总结了存储在 MinIO 存储桶中的文档。 …...

【Linux技术宝典】Linux入门:揭开Linux的神秘面纱

文章目录 官网Linux 环境的搭建方式一、什么是Linux&#xff1f;二、Linux的起源与发展三、Linux的核心组件四、Linux企业应用现状五、Linux的发行版本六、为什么选择Linux&#xff1f;七、总结 Linux&#xff0c;一个在全球范围内广泛应用的开源操作系统&#xff0c;近年来越来…...

C语言---------对操作符的进一步认识

操作符中有⼀些操作符和⼆进制有关系&#xff0c;我们先学习了⼀下⼆进制的和进制转换的知识。 1.原码、反码和补码。 有符号整数的三种表⽰⽅法均有符号位和数值位两部分&#xff0c; 2进制序列中&#xff0c;最⾼位的1位是被当做符号位&#xff0c;剩余的都是数值位。 符号…...

HarmonyOS 鸿蒙 ArkTS ArkUI 页面之间切换转换动画设置

第一步&#xff1a;导入 import promptAction from ohos.promptAction 第二步&#xff1a;在build下方写入 pageTransition(){PageTransitionEnter({ duration: 1200 }).slide(SlideEffect.Right)PageTransitionExit({ delay: 100 }).translate({ x: 100.0, y: 100.0 }).opac…...

《CSS 简易速速上手小册》第8章:CSS 性能优化和可访问性(2024 最新版)

文章目录 8.1 CSS 文件的组织和管理8.1.1 基础知识8.1.2 重点案例&#xff1a;项目样式表结构8.1.3 拓展案例 1&#xff1a;使用BEM命名规范8.1.4 拓展案例 2&#xff1a;利用 Sass 混入创建响应式工具类 8.2 提高网页加载速度的技巧8.2.1 基础知识8.2.2 重点案例&#xff1a;图…...

Peter算法小课堂—背包问题

我们已经学过好久好久的动态规划了&#xff0c;动态规划_Peter Pan was right的博客-CSDN博客 那么&#xff0c;我用一张图片来概括一下背包问题。 大家有可能比较疑惑&#xff0c;优化决策怎么优化呢&#xff1f;答案是&#xff0c;滚动数组&#xff0c;一个神秘而简单的东西…...

网易腾讯面试题精选----50 个 Git 面试问题

介绍 Git 是 DevOps 之旅的起点。所以,我只是概述了 50 个快速问题以及 Git 的答案。这些问题非常快,你可以在 DevOps 面试中问。它适合初学者到中级水平。 面试问答 1.问:什么是Git? 答:Git 是一个分布式版本控制系统,允许多个开发人员在一个项目上进行协作并跟踪源代…...

Android CMakeLists.txt语法详解

一.CMake简介 你或许听过好几种 Make 工具&#xff0c;例如 GNU Make &#xff0c;QT 的 qmake &#xff0c;微软的 MSnmake&#xff0c;BSD Make&#xff08;pmake&#xff09;&#xff0c;Makepp&#xff0c;等等。这些 Make 工具遵循着不同的规范和标准&#xff0c;所执行的…...

Vue3快速上手(二)VSCode官方推荐插件安装及配置

一、VSCode官方插件安装&#xff0c;如下图2款插件 在用vite创建的程序里&#xff0c;提示提安装推荐的插件了&#xff0c;如下图&#xff1a; 二、配置 在设置-扩展里找到Volar插件&#xff0c;将Dot Value勾选上。这样在ref()修改变量时&#xff0c;会自动填充.value,无需…...

等保2、3级所需设备

三级等保要求及所需设备 《等级保护基本要求》所需设备 结构安全&#xff08;G3&#xff09; b)应保证网络各个部分的宽带满足业务高峰期需要&#xff1b; g)应按照对业务服务的需要次序来指定宽带分配优先级别&#xff0c;保证在网络发生拥堵的时候优先保护重要主机 负载均衡…...

6 scala-面向对象编程基础

Scala 跟 Java 一样&#xff0c;是一门面向对象编程的语言&#xff0c;有类和对象的概念。 1 类与对象 与 Java 一样&#xff0c;Scala 也是通过关键字 class 来定义类&#xff0c;使用关键字 new 创建对象。 要运行我们编写的代码&#xff0c;同样像 Java 一样&#xff0c;…...

【linux温故】linux调度机制

假如你是设计者&#xff0c;你会设计怎样的调度机制呢&#xff1f; 时间片 最简单的&#xff0c;小学生都能想出来的一种&#xff0c;每个 ready task&#xff0c;按照一个固定的时间片轮流执行。 大家不要抢&#xff0c;挨个儿排队执行。执行完时间片&#xff0c;就排在后面…...

django中如何使用mysql连接池

一&#xff1a;介绍 在Django中使用MySQL时&#xff0c;通常情况下&#xff0c;Django的数据库层会为你管理数据库连接。Django的数据库接口是线程安全的&#xff0c;这意味着它会自动为每个线程创建和管理数据库连接。在大多数情况下&#xff0c;你不需要手动创建线程池来管理…...

3D高斯溅射:面向三维场景的实时渲染技术

1. 前言 高斯溅射技术【1】一经推出&#xff0c;立刻引起学术界和工业界的广泛关注。相比传统的隐式神经散射场渲染技术&#xff0c;高斯溅射依托椭球空间&#xff0c;显性地表示多目图像的三维空间关系&#xff0c;其计算效率和综合性能均有较大的提升&#xff0c;且更容易理…...

【数据结构】13:表达式转换(中缀表达式转成后缀表达式)

思想&#xff1a; 从头到尾依次读取中缀表达式里的每个对象&#xff0c;对不同对象按照不同的情况处理。 如果遇到空格&#xff0c;跳过如果遇到运算数字&#xff0c;直接输出如果遇到左括号&#xff0c;压栈如果遇到右括号&#xff0c;表示括号里的中缀表达式已经扫描完毕&a…...

MySQL进阶查询篇(9)-视图的创建和应用

数据库视图是MySQL中一个非常重要的概念。它是一个虚拟表&#xff0c;由一个查询的结果集组成。数据库视图为用户提供了一种简化数据查询和操作的方式。本文将介绍MySQL数据库视图的创建和应用。 1. 创建数据库视图 要创建MySQL数据库视图&#xff0c;我们使用CREATE VIEW语句…...

Rhino.Inside带材质将Revit模型bake到Rhino

Hello大家好&#xff01;我是九哥~ 今天来讲一个小技巧&#xff0c;就是我通常采用RIR将Revit的模型的Geometry Bake到Rhino&#xff0c;肯定是没有材质的&#xff0c;那么如果我们需要带材质那要怎么办呢&#xff1f; 对于会的人&#xff0c;其实挺简单的&#xff0c;只需要…...

随记-Java项目处理SQL注入问题

现象&#xff1a;http://10.xx.xx.xx:xx/services/xxService 存在SQL注入情况 加固意见&#xff1a; 需要对网站所有参数中提交的数据进行过滤&#xff0c;禁止输入“"、"xor"、"or"、”--“、”#“、”select“、”and“等特殊字符&#xff1b;所有…...

精读《js 模块化发展》

1 引言 如今&#xff0c;Javascript 模块化规范非常方便、自然&#xff0c;但这个新规范仅执行了 2 年&#xff0c;就在 4 年前&#xff0c;js 的模块化还停留在运行时支持&#xff0c;10 年前&#xff0c;通过后端模版定义、注释定义模块依赖。对经历过来的人来说&#xff0c;…...

Proteus -模拟串口被关闭后怎样打开

Proteus -模拟串口被关闭后怎样打开 点击恢复弹出窗口&#xff0c;即可重新打开...

【深度学习】pytorch 与 PyG 安装(pip安装)

【深度学习】pytorch 与 PyG 安装&#xff08;pip安装&#xff09; 一、PyTorch安装和配置&#xff08;一&#xff09;、安装 CUDA&#xff08;二&#xff09;、安装torch、torchvision、torchaudio三个组件&#xff08;1&#xff09;下载镜像文件&#xff08;2&#xff09;创建…...

Bert与ChatGPT

1. Bert模型 BERT&#xff08;Bidirectional Encoder Representations from Transformers&#xff09;是一种预训练语言表示的方法&#xff0c;由Google AI在2018年提出。它标志着自然语言处理&#xff08;NLP&#xff09;领域的一个重大进步&#xff0c;因为它能够理解单词在…...

微信自动预约小程序开发指南:从小白到专家

随着互联网的发展&#xff0c;小程序已经成为了一个备受欢迎的在线预约平台。本文将详细介绍如何使用第三方制作平台&#xff0c;如乔拓云网&#xff0c;来搭建一个从入门到精通的预约小程序。 首先&#xff0c;我们需要登录乔拓云网&#xff0c;并选择一个适合自己的小程序模板…...

巴尔加瓦算法图解【完结】:算法运用(下)

目录 布隆过滤器HyperLogLogSHA算法比较文件检查密码 Diffie-Hellman密钥交换线性规划结语&#xff08;完结&#xff09; 布隆过滤器 在元素很多的情况下&#xff0c;判断一个元素是否在集合中可以使用布隆过滤器。布隆过滤器&#xff08;Bloom Filter&#xff09;是 1970 年由…...

hexo部署到gitee(码云)

引言 Hexo 是一个基于Node.js的静态博客框架&#xff0c;而 Gitee&#xff08;也被称为码云&#xff09;是一个国内的代码托管平台&#xff0c;支持 Git 版本控制系统&#xff0c;与 GitHub 类似。将 Hexo 部署到 Gitee Pages 可以让你的博客受益于 Gitee 的国内服务器&#xf…...

linux系统非关系型数据库memcached

memcached 特点原理配置安装Memcached 特点 内置内存存储方式-----------为了提高性能&#xff0c;memcached中保存的数据都存储在memcache内置的内存存储空间中。由于数据仅存在于内存中&#xff0c;重启操作系统会导致全部数据消失简单key/value存储---------------服务器不…...

前端vite+vue3——自动化配置路由布局

文章目录 ⭐前言&#x1f496;vue3系列文章 ⭐ 自动化配置路由&#x1f496;引入vite版本自定义目录映射&#x1f496;自动化读取文件下的路由&#x1f496;main入口加载路由&#x1f496;入口app.vue配置&#x1f496;layout基础布局配置&#x1f496;效果 ⭐总结⭐结束 ⭐前言…...

速盾:怎么拿高防服务器做CDN

想要拿高防服务器做CDN&#xff0c;首先需要了解什么是CDN。CDN&#xff0c;即内容分发网络&#xff08;Content Delivery Network&#xff09;&#xff0c;是一种通过互联网连接多个服务器&#xff0c;将静态和动态内容分发到最接近用户的服务器节点&#xff0c;从而提高用户访…...

SQLite database实现加密

注意&#xff1a;以下操作以VS2022为开发工具&#xff0c;以C#为开发语言。 数据加密原因 软件在使用的各个场景&#xff0c;很多都需要数据具有保密性&#xff0c;于是对于数据库就需要加密。特别是在某些特定领域或存储敏感数据尤其如此。 SQLite加密实现 SQLite加密有两种…...