复现反向传播BP算法:手动实现与Sklearn MLP对比分析【复现】

2024-08-29 16:20

本文主要是介绍复现反向传播BP算法:手动实现与Sklearn MLP对比分析【复现】,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

完整代码

import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_moons
from sklearn.model_selection import train_test_split
from sklearn.datasets import load_breast_cancer
from sklearn.metrics import accuracy_score# 生成月亮型二分类数据集
X, Y = make_moons(n_samples=500, noise=0.2, random_state=42)
Y = Y.reshape(-1, 1)  # 将 Y 转换为列向量# 数据集拆分为训练集和测试集
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.2, random_state=42)# 初始化参数
input_size = 2
hidden_size = 4
output_size = 1# 可视化数据集
plt.figure(figsize=(8, 6))
plt.scatter(X[:, 0], X[:, 1], c=Y.ravel(), cmap=plt.cm.Spectral)
plt.title("Moon Dataset")
plt.xlabel("Feature 1")
plt.ylabel("Feature 2")
plt.show()np.random.seed(42)
W1 = np.random.randn(input_size , hidden_size)
b1 = np.zeros((1,hidden_size))
W2 = np.random.randn(hidden_size , hidden_size)
b2 = np.zeros((1 , hidden_size))
W3 = np.random.randn(hidden_size ,output_size )
b3 = np.zeros((1, output_size))def sigmod(X):return 1 / (1 + np.exp(-X))def sigmoid_derivative(x):return sigmod(x) * ( 1 - sigmod(x))def forward(X, W1, b1, W2, b2 , W3 , b3):# 隐藏层Z1 = np.dot(X,W1) + b1A1 = sigmod(Z1)print(f"输入-隐藏层;A1.shape:{A1.shape}")# 隐藏层Z2 = np.dot(A1 , W2) + b2A2 = sigmod(Z2)print(f"隐藏-隐藏层;A2.shape:{A2.shape}")Z3 = np.dot(A2 , W3) + b3A3 = sigmod(Z3)print(f"隐藏-输出层;A3.shape:{A3.shape}")return A1 , A2 , A3# X = np.array([[0.5 , 0.1]])
print(f"X.shape:{X.shape}")A1 , A2 , A3 = forward(X,W1, b1, W2, b2 , W3 , b3)# print("A1 (Hidden Layer Output):", A1)
# print("A2 (Output Layer Output):", A2)def mean_loss(Y , A3):m = Y.shape[0]print(f"Y.shape[0]:{Y.shape[0]}")loss = np.sum( (Y - A3) ** 2 ) / mreturn lossdef compute_loss(Y , A3):m = Y.shape[0]print(f"Y.shape[0]:{Y.shape[0]}")loss = -np.sum(Y * np.log(A3) + (1 - Y) * np.log(1 - A3 )) / mreturn lossdef backward(X, Y, A1, A2, A3, W1, W2, W3):m = Y.shape[0]dA3 = -(np.divide(Y, A3) - np.divide(1-Y, 1-A3))dZ3 = dA3 * sigmoid_derivative(A3)dW3 = np.dot(A2.T, dZ3) / mdB3 = np.sum(dZ3, axis=0, keepdims=True) / mdA2 = np.dot(dZ3, W3.T)dZ2 = dA2 * sigmoid_derivative(A2)dW2 = np.dot(A1.T, dZ2) / mdB2 = np.sum(dZ2, axis=0, keepdims=True) / mdA1 = np.dot(dZ2, W2.T)dZ1 = dA1 * sigmoid_derivative(A1)dW1 = np.dot(X.T, dZ1) / mdB1 = np.sum(dZ1, axis=0, keepdims=True) / mreturn dW1, dB1, dW2, dB2, dW3, dB3def update_parameters(W1, b1, W2, b2, W3, b3, dW1, db1, dW2, db2, dW3, db3, learning_rate=0.01):W1 = W1 - learning_rate * dW1b1 = b1 - learning_rate * db1W2 = W2 - learning_rate * dW2b2 = b2 - learning_rate * db2W3 = W3 - learning_rate * dW3b3 = b3 - learning_rate * db3return W1, b1, W2, b2, W3, b3def train(X, Y, W1, b1, W2, b2, W3, b3, learning_rate=0.01, num_iterations=100):loss_history = []m = X.shape[0]for i in range(num_iterations):# 随机打乱数据permutation = np.random.permutation(m)X_shuffled = X[permutation, :]Y_shuffled = Y[permutation, :]# 遍历每一个样本for j in range(m):# 获取单个样本X_sample = X_shuffled[j:j + 1]Y_sample = Y_shuffled[j:j + 1]# 前向传播A1, A2, A3 = forward(X_sample, W1, b1, W2, b2, W3, b3)# 计算损失(可选,仅用于监控)loss = compute_loss(Y_sample, A3)# 反向传播dW1, db1, dW2, db2, dW3, db3 = backward(X_sample, Y_sample, A1, A2, A3, W1, W2, W3)# 更新参数W1, b1, W2, b2, W3, b3 = update_parameters(W1, b1, W2, b2, W3, b3, dW1, db1, dW2, db2, dW3, db3,learning_rate)# 计算并存储整个训练集的损失值(用于每个epoch的监控)A1, A2, A3 = forward(X, W1, b1, W2, b2, W3, b3)loss = compute_loss(Y, A3)loss_history.append(loss)if i % 100 == 0:print(f"Iteration {i}, Loss: {loss:.4f}")return W1, b1, W2, b2, W3, b3, loss_historyW1, b1, W2, b2, W3, b3, loss_history = train(X_train, Y_train, W1, b1, W2, b2, W3, b3, learning_rate=0.01, num_iterations=100)# 绘制损失函数随迭代次数的变化曲线
plt.figure(figsize=(8, 6))
plt.plot(loss_history)
plt.title("Loss over Iterations")
plt.xlabel("Iterations")
plt.ylabel("Loss")
plt.show()def predict(X, W1, b1, W2, b2, W3, b3):_, _, A3 = forward(X, W1, b1, W2, b2, W3, b3)predictions = (A3 > 0.5).astype(int)return predictions# 使用训练后的模型进行预测
train_predictions = predict(X_train, W1, b1, W2, b2, W3, b3)
test_predictions = predict(X_test, W1, b1, W2, b2, W3, b3)# 计算准确度
train_accuracy = accuracy_score(Y_train, train_predictions)
test_accuracy = accuracy_score(Y_test, test_predictions)from sklearn.neural_network import MLPClassifier
from sklearn.metrics import accuracy_score# 创建与手动实现相同结构的 MLPClassifier
# 设置两个隐藏层,每层有4个神经元,激活函数为'sigmoid',对应 'logistic'
model = MLPClassifier(hidden_layer_sizes=(4,),activation='logistic',learning_rate='constant',solver='sgd',learning_rate_init=0.01,max_iter=1000,random_state=42)# 训练模型
model.fit(X_train, Y_train.ravel())  # 注意:sklearn 的 MLPClassifier 期望 Y 是一维数组,因此需要使用 .ravel()# 预测
train_predictions_sklearn = model.predict(X_train)
test_predictions_sklearn = model.predict(X_test)# 计算准确度
train_accuracy_sklearn = accuracy_score(Y_train, train_predictions_sklearn)
test_accuracy_sklearn = accuracy_score(Y_test, test_predictions_sklearn)# 输出对比结果
print(f"Training Accuracy (Manual): {train_accuracy:.4f}")
print(f"Testing Accuracy (Manual): {test_accuracy:.4f}")
print(f"Training Accuracy (Sklearn MLP): {train_accuracy_sklearn:.4f}")
print(f"Testing Accuracy (Sklearn MLP): {test_accuracy_sklearn:.4f}")

我们一步一步来实现反向传播算法(Backpropagation,简称BP)。这个过程将分成多个步骤,每一步都会解释相应的原理和代码实现。

第一步:理解BP算法的目标

反向传播算法用于训练神经网络,通过最小化损失函数来调整网络的权重。主要过程包括:

  1. 前向传播:计算输入数据通过网络的输出。
  2. 计算损失:衡量预测输出与真实标签之间的差异。
  3. 反向传播:计算损失函数对每个权重的梯度,并利用这些梯度更新权重。

计划的步骤

  1. 构建网络结构:我们首先需要定义一个简单的前馈神经网络(Feedforward Neural Network)。
  2. 前向传播:实现前向传播,计算每一层的输出。
  3. 计算损失函数:选择一个损失函数,比如均方误差(Mean Squared Error, MSE),并计算损失值。
  4. 反向传播计算梯度:计算损失对每个权重的梯度(即偏导数)。
  5. 更新权重:使用梯度下降法更新网络中的权重。

网络结构定义

我们从最简单的网络结构开始:一个输入层、两个隐藏层和一个输出层。假设我们要解决的是一个二分类问题,输入层有2个节点,隐藏层有4个节点,输出层有1个节点。激活函数我们先使用Sigmoid;网络结构如下所示
在这里插入图片描述

这里需要最好还是使用两个隐藏层,因为较好的提取对应的特征

代码实现:

我们先定义网络的权重和偏置:

import numpy as np# 网络结构
input_size = 2  # 输入层节点数
hidden_size = 4  # 隐藏层节点数
output_size = 1  # 输出层节点数# 初始化权重和偏置
np.random.seed(0)
W1 = np.random.randn(input_size, hidden_size)  # 输入层到隐藏层的权重
b1 = np.zeros((1, hidden_size))  # 隐藏层的偏置
W2 = np.random.randn(hidden_size, output_size)  # 隐藏层到隐藏层的权重
b2 = np.zeros((1, output_size))  # 隐藏层的偏置
W3 = np.random.randn(hidden_size ,output_size ) # 隐藏层到输出层的权重
b3 = np.zeros((1, output_size)) # 输出层的偏置

解释

  1. W1:表示输入层到隐藏层的权重矩阵,大小为(input_size, hidden_size)
  2. b1:隐藏层的偏置向量,大小为(1, hidden_size)
  3. W2:隐藏层到隐藏层的权重矩阵,大小为(hidden_size, hidden_size)
  4. b1:隐藏层的偏置向量,大小为(1, hidden_size)
  5. W3:隐藏层到输出层的权重矩阵,大小为(hidden_size, output_size)
  6. b3:输出层的偏置向量,大小为(1, output_size)

这些参数是通过随机初始化的,并且会在反向传播过程中被更新。

第二步:前向传播(继续)

我们已经定义了 Z1A1 计算隐藏层的激活值,现在继续定义第二层隐藏层的激活值 Z2A2,以及输出层的激活值 Z3A3。具体代码如下:

def sigmoid(x):return 1 / (1 + np.exp(-x))def forward_propagation(X):# 第一隐藏层Z1 = np.dot(X, W1) + b1A1 = sigmoid(Z1)# 第二隐藏层Z2 = np.dot(A1, W2) + b2A2 = sigmoid(Z2)# 输出层Z3 = np.dot(A2, W3) + b3A3 = sigmoid(Z3)return A1, A2, A3

解释

  • Z1A1:表示输入层到第一隐藏层的线性组合结果和激活值。
  • Z2A2:表示第一隐藏层到第二隐藏层的线性组合结果和激活值。
  • Z3A3:表示第二隐藏层到输出层的线性组合结果和激活值。

这样,通过调用 forward_propagation 函数,我们可以从输入数据 X 中得到每一层的激活值,最终的 A3 是网络的预测输出。

第三步:计算损失函数

在这个步骤中,我们选择一个适合二分类问题的损失函数。常用的选择是二分类交叉熵损失(Binary Cross-Entropy Loss)。

公式

二分类交叉熵损失的公式为:
L ( Y , A 3 ) = − 1 m ∑ i = 1 m [ Y ( i ) log ⁡ ( A 3 ( i ) ) + ( 1 − Y ( i ) ) log ⁡ ( 1 − A 3 ( i ) ) ] L(Y, A3) = -\frac{1}{m} \sum_{i=1}^{m} \left[Y^{(i)} \log(A3^{(i)}) + (1 - Y^{(i)}) \log(1 - A3^{(i)})\right] L(Y,A3)=m1i=1m[Y(i)log(A3(i))+(1Y(i))log(1A3(i))]
其中:

  • (Y) 是真实标签,(A3) 是预测输出。
  • (m) 是样本数量。

代码实现

def compute_loss(Y, A3):m = Y.shape[0]loss = -np.sum(Y * np.log(A3) + (1 - Y) * np.log(1 - A3)) / mreturn loss

解释

  • YA3:分别是真实标签和预测输出。
  • loss:表示整体的损失值,反映了模型的预测能力。

第四步:反向传播计算梯度

在反向传播过程中,我们计算损失函数相对于每个参数的梯度,然后利用这些梯度更新权重。反向传播使用链式法则(Chain Rule)来计算梯度。

1. 输出层到第二隐藏层的梯度计算

从输出层开始,我们已经知道:

d Z 3 = A 3 − Y dZ3 = A3 - Y dZ3=A3Y

接下来,我们需要计算损失函数相对于输出层权重 (W3) 和偏置 (b3) 的梯度。具体的计算步骤如下:

  1. 计算 (W3) 的梯度:
    d W 3 = ∂ L ∂ W 3 = 1 m ⋅ A 2 T ⋅ d Z 3 dW3 = \frac{\partial L}{\partial W3} = \frac{1}{m} \cdot A2^T \cdot dZ3 dW3=W3L=m1A2TdZ3
    这里, A 2 T A2^T A2T 是第二隐藏层的激活值的转置。
  2. 计算 (b3) 的梯度:
    d b 3 = ∂ L ∂ b 3 = 1 m ⋅ ∑ d Z 3 db3 = \frac{\partial L}{\partial b3} = \frac{1}{m} \cdot \sum dZ3 db3=b3L=m1dZ3
    这里,我们对所有样本的梯度进行求和,并取平均值。

2. 第二隐藏层到第一隐藏层的梯度计算

接着,我们计算第二隐藏层的梯度 d Z 2 dZ2 dZ2

  1. 计算 (dA2)(对第二隐藏层激活值的导数):
    d A 2 = d Z 3 ⋅ W 3 T dA2 = dZ3 \cdot W3^T dA2=dZ3W3T
    这里,(W3^T) 是输出层权重的转置。
  2. 计算 (dZ2)(对第二隐藏层线性组合的导数):
    d Z 2 = d A 2 ⋅ sigmoid_derivative ( Z 2 ) dZ2 = dA2 \cdot \text{sigmoid\_derivative}(Z2) dZ2=dA2sigmoid_derivative(Z2)
    其中, sigmoid_derivative ( Z 2 ) \text{sigmoid\_derivative}(Z2) sigmoid_derivative(Z2) 是对 Z 2 Z2 Z2 进行激活函数求导的结果。
  3. 计算 (W2) 的梯度:
    d W 2 = ∂ L ∂ W 2 = 1 m ⋅ A 1 T ⋅ d Z 2 dW2 = \frac{\partial L}{\partial W2} = \frac{1}{m} \cdot A1^T \cdot dZ2 dW2=W2L=m1A1TdZ2
    这里, A 1 T A1^T A1T 是第一隐藏层的激活值的转置。
  4. 计算 (b2) 的梯度:
    d b 2 = ∂ L ∂ b 2 = 1 m ⋅ ∑ d Z 2 db2 = \frac{\partial L}{\partial b2} = \frac{1}{m} \cdot \sum dZ2 db2=b2L=m1dZ2

3. 第一隐藏层到输入层的梯度计算

最后,我们计算第一隐藏层的梯度 d Z 1 dZ1 dZ1

  1. 计算 (dA1)(对第一隐藏层激活值的导数):
    d A 1 = d Z 2 ⋅ W 2 T dA1 = dZ2 \cdot W2^T dA1=dZ2W2T
    这里, W 2 T W2^T W2T 是第二隐藏层权重的转置。
  2. 计算 (dZ1)(对第一隐藏层线性组合的导数):
    d Z 1 = d A 1 ⋅ sigmoid_derivative ( Z 1 ) dZ1 = dA1 \cdot \text{sigmoid\_derivative}(Z1) dZ1=dA1sigmoid_derivative(Z1)
  3. 计算 (W1) 的梯度:
    d W 1 = ∂ L ∂ W 1 = 1 m ⋅ X T ⋅ d Z 1 dW1 = \frac{\partial L}{\partial W1} = \frac{1}{m} \cdot X^T \cdot dZ1 dW1=W1L=m1XTdZ1
    这里, X T X^T XT 是输入数据的转置。
  4. 计算 (b1) 的梯度:
    d b 1 = ∂ L ∂ b 1 = 1 m ⋅ ∑ d Z 1 db1 = \frac{\partial L}{\partial b1} = \frac{1}{m} \cdot \sum dZ1 db1=b1L=m1dZ1

代码实现

def backward_propagation(X, Y, A1, A2, A3):m = X.shape[0]# 输出层梯度dZ3 = A3 - YdW3 = np.dot(A2.T, dZ3) / mdb3 = np.sum(dZ3, axis=0, keepdims=True) / m# 第二隐藏层梯度dA2 = np.dot(dZ3, W3.T)dZ2 = dA2 * A2 * (1 - A2)dW2 = np.dot(A1.T, dZ2) / mdb2 = np.sum(dZ2, axis=0, keepdims=True) / m# 第一隐藏层梯度dA1 = np.dot(dZ2, W2.T)dZ1 = dA1 * A1 * (1 - A1)dW1 = np.dot(X.T, dZ1) / mdb1 = np.sum(dZ1, axis=0, keepdims=True) / mreturn dW1, db1, dW2, db2, dW3, db3

解释

  • dZ3dZ2dZ1:分别是输出层和隐藏层的梯度。
  • dW3dW2dW1:分别是输出层和隐藏层的权重梯度。
  • db3db2db1:分别是输出层和隐藏层的偏置梯度。

这些梯度将用于更新网络的参数,以减少损失函数的值。

第五步:更新权重

利用梯度下降法,我们使用上一步计算出的梯度更新每个权重和偏置。

公式

权重更新公式为:
W = W − α ⋅ d W W = W - \alpha \cdot dW W=WαdW
b = b − α ⋅ d b b = b - \alpha \cdot db b=bαdb
其中, α \alpha α 是学习率,即每次调整的步长。

代码实现

def update_parameters(W1, b1, W2, b2, W3, b3, dW1, db1, dW2, db2, dW3, db3, learning_rate=0.01):W1 -= learning_rate * dW1b1 -= learning_rate * db1W2 -= learning_rate * dW2b2 -= learning_rate * db2W3 -= learning_rate * dW3b3 -= learning_rate * db3return W1, b1, W2, b2, W3, b3

解释

  • 这里的 learning_rate 是一个超参数,用于控制每次更新的步长。

第六步:训练模型

将前向传播、损失计算、反向传播和权重更新组合起来,我们就可以训练整个模型了。

代码实现

def train(X, Y, W1, b1, W2, b2, W3, b3, learning_rate=0.01, num_iterations=100):loss_history = []for i in range(num_iterations):# 前向传播A1, A2, A3 = forward_propagation(X)# 计算损失loss = compute_loss(Y, A3)loss_history.append(loss)# 反向传播dW1, db1, dW2, db2, dW3, db3 = backward_propagation(X, Y, A1, A2, A3)# 更新参数W1, b1, W2, b2, W3, b3 = update_parameters(W1, b1, W2, b2, W3, b3, dW1, db1, dW2, db2, dW3, db3, learning_rate)# 每隔100次迭代输出一次损失值if i % 100 == 0:print(f"Iteration {i}, Loss: {loss:.4f}")return W1, b1, W2, b2, W3, b3, loss_history

解释

  • num_iterations:表示训练的迭代次数。
  • loss_history:用于记录每次迭代后的损失值,方便后续分析。
  • print 语句会在每100次迭代时输出当前的损失值,方便跟踪模型的训练过程。

Loss 函数下降趋势分析

在这里插入图片描述

从图像中可以观察到以下几个关键点和趋势:

  1. 初始快速下降 (0 - 20次迭代):

    • 在训练的初始阶段,损失函数呈现出快速下降的趋势。这是因为在训练初期,权重更新幅度较大,模型对输入数据进行大幅调整,迅速减少预测误差。
    • 这是正常现象,说明学习率设置适当,模型正在有效地学习。
  2. 损失函数平稳期 (20 - 40次迭代):

    • 在迭代20次左右,损失函数的下降速度明显放缓,并进入一个较为平稳的阶段。此时模型已经逐渐接近局部最优,调整的步伐变小。
    • 这是一个典型的现象,表明模型接近收敛。
  3. 微小波动期 (40次迭代后):

    • 从40次迭代后,损失函数继续缓慢下降,但图像上出现了一些微小的波动。这些波动可能是由于学习率较大或SGD中随机性的影响所导致的。模型在局部最优附近小范围波动。

特别点及原因分析

  • 波动原因: 这些微小的波动可能是由于学习率较大,导致模型在接近局部最优时,未能完全稳定在最优点,而是在最优点附近来回波动。同时,SGD本身的随机性也可能引入一定的波动性。
  • 损失不再下降: 在迭代大约40次后,损失函数基本保持在0.45左右。这说明当前的优化可能已经达到了某种局部最优,难以进一步下降。

第七步:模型预测

最后,我们使用训练好的模型进行预测。预测函数通过前向传播得到输出值,然后根据阈值 (0.5) 将输出转化为二分类的预测结果。

代码实现

def predict(X, W1, b1, W2, b2, W3, b3):_, _, A3 = forward_propagation(X)predictions = (A3 > 0.5).astype(int)return predictions

解释

  • predictions:预测结果,表示模型对于输入 X X X 的预测类别;阈值设置为0.5(Sigmoid激活函数的输出在区间 [ 0 , 1 ] [0, 1] [0,1] 内)。

结果对比

使用我们手动实现的模型和 sklearnMLPClassifier 进行对比,观察训练和测试集上的准确率。

from sklearn.neural_network import MLPClassifier
from sklearn.metrics import accuracy_score# 使用手动实现的模型进行预测
train_predictions = predict(X_train, W1, b1, W2, b2, W3, b3)
test_predictions = predict(X_test, W1, b1, W2, b2, W3, b3)# 计算手动实现的模型的准确度
train_accuracy = accuracy_score(Y_train, train_predictions)
test_accuracy = accuracy_score(Y_test, test_predictions)# 使用 sklearn 的 MLPClassifier 进行对比
model = MLPClassifier(hidden_layer_sizes=(4,),activation='logistic',learning_rate_init=0.01,max_iter=1000,solver='sgd',random_state=42)model.fit(X_train, Y_train.ravel())
train_predictions_sklearn = model.predict(X_train)
test_predictions_sklearn = model.predict(X_test)# 计算 sklearn 模型的准确度
train_accuracy_sklearn = accuracy_score(Y_train, train_predictions_sklearn)
test_accuracy_sklearn = accuracy_score(Y_test, test_predictions_sklearn)print(f"Training Accuracy (Manual): {train_accuracy:.4f}")
print(f"Testing Accuracy (Manual): {test_accuracy:.4f}")
print(f"Training Accuracy (Sklearn MLP): {train_accuracy_sklearn:.4f}")
print(f"Testing Accuracy (Sklearn MLP): {test_accuracy_sklearn:.4f}")

对应的结果如下:
在这里插入图片描述

总结

我们已经详细实现了一个三层神经网络的反向传播算法,并逐步拆解了各个过程。整个过程包括:

  1. 前向传播计算输出。
  2. 计算损失函数。
  3. 反向传播计算梯度。
  4. 利用梯度更新参数。
  5. 重复上述步骤训练网络。

这篇关于复现反向传播BP算法:手动实现与Sklearn MLP对比分析【复现】的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1118357

相关文章

使用Sentinel自定义返回和实现区分来源方式

《使用Sentinel自定义返回和实现区分来源方式》:本文主要介绍使用Sentinel自定义返回和实现区分来源方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Sentinel自定义返回和实现区分来源1. 自定义错误返回2. 实现区分来源总结Sentinel自定

Java实现时间与字符串互相转换详解

《Java实现时间与字符串互相转换详解》这篇文章主要为大家详细介绍了Java中实现时间与字符串互相转换的相关方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、日期格式化为字符串(一)使用预定义格式(二)自定义格式二、字符串解析为日期(一)解析ISO格式字符串(二)解析自定义

opencv图像处理之指纹验证的实现

《opencv图像处理之指纹验证的实现》本文主要介绍了opencv图像处理之指纹验证的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学... 目录一、简介二、具体案例实现1. 图像显示函数2. 指纹验证函数3. 主函数4、运行结果三、总结一、

Springboot处理跨域的实现方式(附Demo)

《Springboot处理跨域的实现方式(附Demo)》:本文主要介绍Springboot处理跨域的实现方式(附Demo),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不... 目录Springboot处理跨域的方式1. 基本知识2. @CrossOrigin3. 全局跨域设置4.

Spring Boot 3.4.3 基于 Spring WebFlux 实现 SSE 功能(代码示例)

《SpringBoot3.4.3基于SpringWebFlux实现SSE功能(代码示例)》SpringBoot3.4.3结合SpringWebFlux实现SSE功能,为实时数据推送提供... 目录1. SSE 简介1.1 什么是 SSE?1.2 SSE 的优点1.3 适用场景2. Spring WebFlu

基于SpringBoot实现文件秒传功能

《基于SpringBoot实现文件秒传功能》在开发Web应用时,文件上传是一个常见需求,然而,当用户需要上传大文件或相同文件多次时,会造成带宽浪费和服务器存储冗余,此时可以使用文件秒传技术通过识别重复... 目录前言文件秒传原理代码实现1. 创建项目基础结构2. 创建上传存储代码3. 创建Result类4.

SpringBoot日志配置SLF4J和Logback的方法实现

《SpringBoot日志配置SLF4J和Logback的方法实现》日志记录是不可或缺的一部分,本文主要介绍了SpringBoot日志配置SLF4J和Logback的方法实现,文中通过示例代码介绍的非... 目录一、前言二、案例一:初识日志三、案例二:使用Lombok输出日志四、案例三:配置Logback一

Python如何使用__slots__实现节省内存和性能优化

《Python如何使用__slots__实现节省内存和性能优化》你有想过,一个小小的__slots__能让你的Python类内存消耗直接减半吗,没错,今天咱们要聊的就是这个让人眼前一亮的技巧,感兴趣的... 目录背景:内存吃得满满的类__slots__:你的内存管理小助手举个大概的例子:看看效果如何?1.

Python+PyQt5实现多屏幕协同播放功能

《Python+PyQt5实现多屏幕协同播放功能》在现代会议展示、数字广告、展览展示等场景中,多屏幕协同播放已成为刚需,下面我们就来看看如何利用Python和PyQt5开发一套功能强大的跨屏播控系统吧... 目录一、项目概述:突破传统播放限制二、核心技术解析2.1 多屏管理机制2.2 播放引擎设计2.3 专

Python实现无痛修改第三方库源码的方法详解

《Python实现无痛修改第三方库源码的方法详解》很多时候,我们下载的第三方库是不会有需求不满足的情况,但也有极少的情况,第三方库没有兼顾到需求,本文将介绍几个修改源码的操作,大家可以根据需求进行选择... 目录需求不符合模拟示例 1. 修改源文件2. 继承修改3. 猴子补丁4. 追踪局部变量需求不符合很