问题描述:误差反向传播算法是否存在过拟合问题,如何解决这个问题?
介绍
误差反向传播算法是用于训练神经网络的一种重要算法。在训练过程中,我们通过计算网络输出与真实值的差异(即误差),并将误差反向传播到网络的各个层,从而更新网络的参数。然而,这种算法存在一个常见问题,即过拟合。过拟合指的是模型在训练数据上表现良好,但是在未见过的测试数据上表现较差的现象。
算法原理
误差反向传播算法主要通过链式法则计算梯度以更新模型参数。在每一次迭代训练中,通过计算模型对训练样本的预测值与真实值之间的误差(损失函数),可以得到一个代表误差大小的值。然后,通过计算损失函数对网络参数的偏导数,即梯度,我们可以通过优化算法(如梯度下降法)来更新网络参数,以尽可能减少误差。
公式推导
让我们以一个简单的例子来推导误差反向传播算法的公式。假设我们有一个单隐藏层的前馈神经网络,输入为x,隐藏层神经元个数为h,输出层神经元个数为o。网络的输入层到隐藏层的权重为W1,隐藏层到输出层的权重为W2,隐藏层的偏置为b1,输出层的偏置为b2。我们使用平方损失函数,即$L(y, \hat{y}) = \frac{1}{2}(y – \hat{y})^2$。其中,y为真实值,$\hat{y}$为网络的预测值。
Step 1: 前向传播
首先,我们根据输入x计算隐藏层的激活值$a_1$:
$$a_1 = \sigma(W_1 \cdot x + b_1)$$
其中,$\sigma$为激活函数,通常使用ReLU或Sigmoid函数。
然后,我们使用隐藏层的激活值计算输出层的激活值$a_2$:
$$a_2 = \sigma(W_2 \cdot a_1 + b_2)$$
Step 2: 计算损失函数
接下来,我们计算损失函数对输出层激活值的偏导数$\frac{\partial L}{\partial a_2}$:
$$\frac{\partial L}{\partial a_2} = \frac{\partial}{\partial a_2} \frac{1}{2}(y – a_2)^2 = (a_2 – y)$$
Step 3: 反向传播
现在,我们使用链式法则计算损失函数对隐藏层到输出层权重W2的偏导数$\frac{\partial L}{\partial W_2}$:
$$\frac{\partial L}{\partial W_2} = \frac{\partial L}{\partial a_2} \cdot \frac{\partial a_2}{\partial (W_2 \cdot a_1 + b_2)} = (a_2 – y) \cdot \sigma'(W_2 \cdot a_1 + b_2) \cdot a_1^T$$
其中,$\sigma’$为激活函数的导数。
接着,我们计算损失函数对隐藏层偏置b2的偏导数$\frac{\partial L}{\partial b_2}$:
$$\frac{\partial L}{\partial b_2} = \frac{\partial L}{\partial a_2} \cdot \frac{\partial a_2}{\partial (W_2 \cdot a_1 + b_2)} = (a_2 – y) \cdot \sigma'(W_2 \cdot a_1 + b_2)$$
类似地,我们计算损失函数对输入层到隐藏层权重W1的偏导数$\frac{\partial L}{\partial W_1}$:
$$\frac{\partial L}{\partial W_1} = \frac{\partial L}{\partial a_2} \cdot \frac{\partial a_2}{\partial a_1} \cdot \frac{\partial a_1}{\partial (W_1 \cdot x + b_1)} \cdot x^T$$
最后,我们计算损失函数对隐藏层偏置b1的偏导数$\frac{\partial L}{\partial b_1}$:
$$\frac{\partial L}{\partial b_1} = \frac{\partial L}{\partial a_2} \cdot \frac{\partial a_2}{\partial a_1} \cdot \frac{\partial a_1}{\partial (W_1 \cdot x + b_1)}$$
Step 4: 参数更新
通过梯度下降法等优化算法,我们可以更新模型参数。假设学习率为$\alpha$,参数更新的具体公式如下:
$$W_2 = W_2 – \alpha \cdot \frac{\partial L}{\partial W_2}$$
$$b_2 = b_2 – \alpha \cdot \frac{\partial L}{\partial b_2}$$
$$W_1 = W_1 – \alpha \cdot \frac{\partial L}{\partial W_1}$$
$$b_1 = b_1 – \alpha \cdot \frac{\partial L}{\partial b_1}$$
计算步骤
- 初始化输入层到隐藏层的权重W1、隐藏层到输出层的权重W2、隐藏层的偏置b1、输出层的偏置b2。
- 根据输入x,进行前向传播,计算网络的预测值。
- 根据网络的预测值和真实值,计算损失函数。
- 根据损失函数,进行反向传播,计算梯度。
- 使用优化算法,根据梯度更新模型参数。
- 重复步骤2-5,直至达到收敛条件或达到最大迭代次数。
Python代码示例
下面是一个使用Python实现的简单的误差反向传播算法的例子。我们使用一个虚拟数据集,并使用Numpy库来进行矩阵运算。
首先,我们需要导入必要的库:
import numpy as np
import matplotlib.pyplot as plt
然后,我们定义激活函数和其导数:
def sigmoid(x):
return 1 / (1 + np.exp(-x))
def sigmoid_derivative(x):
return sigmoid(x) artical cgpt2md_gpt.sh cgpt2md_johngo.log cgpt2md_johngo.sh cgpt2md.sh _content1.txt _content.txt current_url.txt history_url history_urls log nohup.out online pic.txt seo test.py topic_gpt.txt topic_johngo.txt topic.txt upload-markdown-to-wordpress.py urls (1 - sigmoid(x))
接下来,我们初始化输入、隐藏层、输出层的维度和学习率等参数:
input_dim = 1
hidden_dim = 3
output_dim = 1
learning_rate = 0.01
然后,我们生成虚拟数据集:
X = np.array([[0], [1], [2], [3], [4], [5], [6], [7], [8], [9]])
y = np.array([[0], [1], [4], [9], [16], [25], [36], [49], [64], [81]])
接着,我们随机初始化参数权重W1、W2以及偏置b1、b2:
np.random.seed(1)
W1 = np.random.randn(hidden_dim, input_dim)
b1 = np.zeros((hidden_dim, 1))
W2 = np.random.randn(output_dim, hidden_dim)
b2 = np.zeros((output_dim, 1))
然后,我们开始迭代训练模型:
losses = []
for i in range(10000):
# 前向传播
hidden_layer_output = sigmoid(np.dot(W1, X) + b1)
y_pred = sigmoid(np.dot(W2, hidden_layer_output) + b2)
# 计算损失函数
loss = 0.5 artical cgpt2md_gpt.sh cgpt2md_johngo.log cgpt2md_johngo.sh cgpt2md.sh _content1.txt _content.txt current_url.txt history_url history_urls log nohup.out online pic.txt seo test.py topic_gpt.txt topic_johngo.txt topic.txt upload-markdown-to-wordpress.py urls np.mean((y_pred - y) artical cgpt2md_gpt.sh cgpt2md_johngo.log cgpt2md_johngo.sh cgpt2md.sh _content1.txt _content.txt current_url.txt history_url history_urls log nohup.out online pic.txt seo test.py topic_gpt.txt topic_johngo.txt topic.txt upload-markdown-to-wordpress.py urls 2)
losses.append(loss)
# 反向传播
dL_dypred = y_pred - y
dypred_dz2 = sigmoid_derivative(np.dot(W2, hidden_layer_output) + b2)
dz2_dW2 = hidden_layer_output
dL_dW2 = np.dot((dL_dypred artical cgpt2md_gpt.sh cgpt2md_johngo.log cgpt2md_johngo.sh cgpt2md.sh _content1.txt _content.txt current_url.txt history_url history_urls log nohup.out online pic.txt seo test.py topic_gpt.txt topic_johngo.txt topic.txt upload-markdown-to-wordpress.py urls dypred_dz2), dz2_dW2.T)
dL_db2 = np.sum(dL_dypred artical cgpt2md_gpt.sh cgpt2md_johngo.log cgpt2md_johngo.sh cgpt2md.sh _content1.txt _content.txt current_url.txt history_url history_urls log nohup.out online pic.txt seo test.py topic_gpt.txt topic_johngo.txt topic.txt upload-markdown-to-wordpress.py urls dypred_dz2, axis=1, keepdims=True)
dz2_dhidden_layer = W2
dhidden_layer_dz1 = sigmoid_derivative(np.dot(W1, X) + b1)
dz1_dW1 = X
dL_dW1 = np.dot((np.dot(dz2_dhidden_layer.T, (dL_dypred artical cgpt2md_gpt.sh cgpt2md_johngo.log cgpt2md_johngo.sh cgpt2md.sh _content1.txt _content.txt current_url.txt history_url history_urls log nohup.out online pic.txt seo test.py topic_gpt.txt topic_johngo.txt topic.txt upload-markdown-to-wordpress.py urls dypred_dz2)) artical cgpt2md_gpt.sh cgpt2md_johngo.log cgpt2md_johngo.sh cgpt2md.sh _content1.txt _content.txt current_url.txt history_url history_urls log nohup.out online pic.txt seo test.py topic_gpt.txt topic_johngo.txt topic.txt upload-markdown-to-wordpress.py urls dhidden_layer_dz1), dz1_dW1.T)
dL_db1 = np.sum(np.dot(dz2_dhidden_layer.T, (dL_dypred artical cgpt2md_gpt.sh cgpt2md_johngo.log cgpt2md_johngo.sh cgpt2md.sh _content1.txt _content.txt current_url.txt history_url history_urls log nohup.out online pic.txt seo test.py topic_gpt.txt topic_johngo.txt topic.txt upload-markdown-to-wordpress.py urls dypred_dz2)) artical cgpt2md_gpt.sh cgpt2md_johngo.log cgpt2md_johngo.sh cgpt2md.sh _content1.txt _content.txt current_url.txt history_url history_urls log nohup.out online pic.txt seo test.py topic_gpt.txt topic_johngo.txt topic.txt upload-markdown-to-wordpress.py urls dhidden_layer_dz1, axis=1, keepdims=True)
# 更新参数
W2 -= learning_rate artical cgpt2md_gpt.sh cgpt2md_johngo.log cgpt2md_johngo.sh cgpt2md.sh _content1.txt _content.txt current_url.txt history_url history_urls log nohup.out online pic.txt seo test.py topic_gpt.txt topic_johngo.txt topic.txt upload-markdown-to-wordpress.py urls dL_dW2
b2 -= learning_rate artical cgpt2md_gpt.sh cgpt2md_johngo.log cgpt2md_johngo.sh cgpt2md.sh _content1.txt _content.txt current_url.txt history_url history_urls log nohup.out online pic.txt seo test.py topic_gpt.txt topic_johngo.txt topic.txt upload-markdown-to-wordpress.py urls dL_db2
W1 -= learning_rate artical cgpt2md_gpt.sh cgpt2md_johngo.log cgpt2md_johngo.sh cgpt2md.sh _content1.txt _content.txt current_url.txt history_url history_urls log nohup.out online pic.txt seo test.py topic_gpt.txt topic_johngo.txt topic.txt upload-markdown-to-wordpress.py urls dL_dW1
b1 -= learning_rate artical cgpt2md_gpt.sh cgpt2md_johngo.log cgpt2md_johngo.sh cgpt2md.sh _content1.txt _content.txt current_url.txt history_url history_urls log nohup.out online pic.txt seo test.py topic_gpt.txt topic_johngo.txt topic.txt upload-markdown-to-wordpress.py urls dL_db1
最后,我们绘制损失函数的变化曲线来观察模型的训练情况:
plt.plot(losses)
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.show()
代码细节解释
- 计算隐藏层和输出层的激活值时,使用了sigmoid激活函数。
- 计算损失函数时,使用了平方损失函数。
- 实现了参数的随机初始化,以防止所有参数具有相同的初始值,陷入局部最优点。
- 反向传播过程中,需要使用激活函数的导数来计算偏导数,这里使用了sigmoid函数的导数。
- 在更新参数时,需要乘以学习率。
- 每次迭代都记录下损失函数的值,便于之后绘制损失函数曲线进行分析。
通过运行以上代码,我们可以观察到损失函数逐渐减小,模型通过误差反向传播算法不断进行参数更新,逐渐优化模型。
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/823879/
转载文章受原作者版权保护。转载请注明原作者出处!