复现反向传播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

相关文章

Python实现精准提取 PDF中的文本,表格与图片

《Python实现精准提取PDF中的文本,表格与图片》在实际的系统开发中,处理PDF文件不仅限于读取整页文本,还有提取文档中的表格数据,图片或特定区域的内容,下面我们来看看如何使用Python实... 目录安装 python 库提取 PDF 文本内容:获取整页文本与指定区域内容获取页面上的所有文本内容获取

基于Python实现一个Windows Tree命令工具

《基于Python实现一个WindowsTree命令工具》今天想要在Windows平台的CMD命令终端窗口中使用像Linux下的tree命令,打印一下目录结构层级树,然而还真有tree命令,但是发现... 目录引言实现代码使用说明可用选项示例用法功能特点添加到环境变量方法一:创建批处理文件并添加到PATH1

Java使用HttpClient实现图片下载与本地保存功能

《Java使用HttpClient实现图片下载与本地保存功能》在当今数字化时代,网络资源的获取与处理已成为软件开发中的常见需求,其中,图片作为网络上最常见的资源之一,其下载与保存功能在许多应用场景中都... 目录引言一、Apache HttpClient简介二、技术栈与环境准备三、实现图片下载与保存功能1.

canal实现mysql数据同步的详细过程

《canal实现mysql数据同步的详细过程》:本文主要介绍canal实现mysql数据同步的详细过程,本文通过实例图文相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的... 目录1、canal下载2、mysql同步用户创建和授权3、canal admin安装和启动4、canal

Nexus安装和启动的实现教程

《Nexus安装和启动的实现教程》:本文主要介绍Nexus安装和启动的实现教程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、Nexus下载二、Nexus安装和启动三、关闭Nexus总结一、Nexus下载官方下载链接:DownloadWindows系统根

SpringBoot集成LiteFlow实现轻量级工作流引擎的详细过程

《SpringBoot集成LiteFlow实现轻量级工作流引擎的详细过程》LiteFlow是一款专注于逻辑驱动流程编排的轻量级框架,它以组件化方式快速构建和执行业务流程,有效解耦复杂业务逻辑,下面给大... 目录一、基础概念1.1 组件(Component)1.2 规则(Rule)1.3 上下文(Conte

MySQL 横向衍生表(Lateral Derived Tables)的实现

《MySQL横向衍生表(LateralDerivedTables)的实现》横向衍生表适用于在需要通过子查询获取中间结果集的场景,相对于普通衍生表,横向衍生表可以引用在其之前出现过的表名,本文就来... 目录一、横向衍生表用法示例1.1 用法示例1.2 使用建议前面我们介绍过mysql中的衍生表(From子句

Mybatis的分页实现方式

《Mybatis的分页实现方式》MyBatis的分页实现方式主要有以下几种,每种方式适用于不同的场景,且在性能、灵活性和代码侵入性上有所差异,对Mybatis的分页实现方式感兴趣的朋友一起看看吧... 目录​1. 原生 SQL 分页(物理分页)​​2. RowBounds 分页(逻辑分页)​​3. Page

MyBatis Plus 中 update_time 字段自动填充失效的原因分析及解决方案(最新整理)

《MyBatisPlus中update_time字段自动填充失效的原因分析及解决方案(最新整理)》在使用MyBatisPlus时,通常我们会在数据库表中设置create_time和update... 目录前言一、问题现象二、原因分析三、总结:常见原因与解决方法对照表四、推荐写法前言在使用 MyBATis

Python主动抛出异常的各种用法和场景分析

《Python主动抛出异常的各种用法和场景分析》在Python中,我们不仅可以捕获和处理异常,还可以主动抛出异常,也就是以类的方式自定义错误的类型和提示信息,这在编程中非常有用,下面我将详细解释主动抛... 目录一、为什么要主动抛出异常?二、基本语法:raise关键字基本示例三、raise的多种用法1. 抛