神经网络权重更新的简单演示

神经网络如何更新参数:以线性拟合为例

在机器学习中,神经网络是一种强大的工具,能够通过学习数据中的模式来进行预测。在本篇博客中,我们将重点讨论神经网络如何更新参数(权重和偏置),并通过一个简单的线性拟合示例来演示这一过程。

1. 什么是线性拟合?

线性拟合是指通过一条直线来近似表达数据点之间的关系。简单来说,我们希望找到一个线性方程:

y = w x + b y = wx + b y=wx+b

其中, y y y 是输出, x x x 是输入特征, w w w 是权重, b b b 是偏置。我们的目标是通过训练数据来学习合适的 w w w b b b

2. 神经网络的基本结构

在本示例中,我们将使用一个简单的神经网络模型来进行线性拟合。模型结构如下:

  • 输入层:接收输入特征 x x x
  • 输出层:生成预测值 y y y

在 PyTorch 中,我们可以使用 nn.Linear 来创建一个线性层,该层会自动处理权重和偏置的初始化。

3. 损失函数

为了评估模型的预测效果,我们使用均方误差(Mean Squared Error, MSE)作为损失函数,其公式如下:

MSE = 1 n ∑ i = 1 n ( y i − y ^ i ) 2 \text{MSE} = \frac{1}{n} \sum_{i=1}^{n} (y_i - \hat{y}_i)^2 MSE=n1i=1n(yiy^i)2

其中, y i y_i yi 是真实值, y ^ i \hat{y}_i y^i 是模型预测值, n n n 是样本数量。我们的目标是最小化这个损失函数。

4. 反向传播与权重更新

4.1 反向传播

反向传播是神经网络训练的核心算法。它通过链式法则计算损失函数相对于每个参数(权重和偏置)的梯度。具体步骤如下:

  1. 前向传播:计算模型的输出。
  2. 计算损失:通过损失函数评估输出与真实值之间的差异。
  3. 计算梯度:通过反向传播算法计算损失相对于每个参数的梯度。

4.2 权重更新

根据计算得到的梯度,我们可以使用梯度下降法更新权重和偏置。更新公式如下:
w : = w − η ∂ MSE ∂ w = w − η ( − 2 n ∑ i = 1 n ( y i − ( w ⋅ x i + b ) ) ⋅ x i ) w := w - \eta \frac{\partial \text{MSE}}{\partial w} = w - \eta \left( -\frac{2}{n} \sum_{i=1}^{n} (y_i - (w \cdot x_i + b)) \cdot x_i \right) w:=wηwMSE=wη(n2i=1n(yi(wxi+b))xi)

b : = b − η ∂ MSE ∂ b = b − η ( − 2 n ∑ i = 1 n ( y i − ( w ⋅ x i + b ) ) ) b := b - \eta \frac{\partial \text{MSE}}{\partial b} = b - \eta \left( -\frac{2}{n} \sum_{i=1}^{n} (y_i - (w \cdot x_i + b)) \right) b:=bηbMSE=bη(n2i=1n(yi(wxi+b)))

  • 这里的 η \eta η 是学习率,控制每次更新的步长。
  • 通过多次迭代更新,我们希望能找到最优的 w w w b b b

5. 示例代码

下面是使用 PyTorch 进行线性拟合的完整代码示例:

import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np
import matplotlib.pyplot as plt
import imageio.v2 as imageio  # 使用 imageio.v2 导入
import os

# 设置随机种子
np.random.seed(42)
torch.manual_seed(42)

# 生成数据
X = np.linspace(-1, 1, 100).reshape(-1, 1)  # 输入特征
y = 2 * X + 1 + np.random.normal(0, 0.1, X.shape)  # 线性关系 y = 2x + 1 + 噪声

# 转换为 PyTorch 张量
X_tensor = torch.FloatTensor(X)
y_tensor = torch.FloatTensor(y)

# 可视化数据
plt.scatter(X, y, label='Data Points', color='blue')
plt.title("Synthetic Data for Linear Fitting")
plt.xlabel("X")
plt.ylabel("y")
plt.legend()
plt.show()

# 创建线性回归模型
model = nn.Sequential(
    nn.Linear(1, 1)  # 1个输入特征,1个输出
)

# 定义损失函数和优化器
criterion = nn.MSELoss()  # 均方误差损失
optimizer = optim.SGD(model.parameters(), lr=0.1)  # 学习率为 0.1

# 训练模型并记录拟合过程
epochs = 50
images = []  # 用于存储每一步的图像

for epoch in range(epochs):
    model.train()  # 设置模型为训练模式

    # 前向传播
    optimizer.zero_grad()  # 清空梯度
    y_pred = model(X_tensor)  # 预测

    # 计算损失
    loss = criterion(y_pred, y_tensor)

    # 反向传播
    loss.backward()  # 计算梯度

    # 打印梯度
    print(f'Epoch {epoch + 1}/{epochs}, Loss: {loss.item():.4f}')
    for name, param in model.named_parameters():
        if param.grad is not None:
            print(f'Gradient for {name}: {param.grad.data.numpy()}')

    # 更新参数
    optimizer.step()  # 更新参数

    # 绘制图像
    plt.figure()
    plt.scatter(X, y, label='Data Points', color='blue')
    plt.plot(X, y_pred.detach().numpy(), label='Fitted Line', color='red')
    plt.title(f"Epoch {epoch + 1}/{epochs}, Loss: {loss.item():.4f}")
    plt.xlabel("X")
    plt.ylabel("y")
    plt.legend()
    plt.xlim(-1.5, 1.5)
    plt.ylim(-1.5, 3.5)

    # 保存图像
    plt.savefig(f'epoch_{epoch + 1}.png')
    plt.close()

# 生成动图
for epoch in range(1, epochs + 1):
    images.append(imageio.imread(f'epoch_{epoch}.png'))

imageio.mimsave('linear_fitting.gif', images, duration=0.5)

# 清理生成的图像文件
for epoch in range(1, epochs + 1):
    os.remove(f'epoch_{epoch}.png')

print("GIF saved as 'linear_fitting.gif'")

6. 结果与分析

  • 损失值:随着训练的进行,损失值应该逐渐降低,表明模型的预测效果在改善。
  • 梯度:每个参数的梯度将显示在控制台中,反映出当前参数对损失的影响程度。

训练过程的结果

在训练过程中,我们记录了每个 epoch 的损失值和每个参数的梯度。以下是结果的总结:

EpochLossGradient for WeightGradient for Bias
10.5585([-0.8449708])([-0.31921485])
20.4804([-0.7875014])([-0.25537187])
30.4146([-0.7339407])([-0.20429748])
40.3588([-0.6840228])([-0.163438])
50.3112([-0.63750005])([-0.13075046])
60.2704([-0.5941415])([-0.10460036])
70.2353([-0.5537319])([-0.08368032])
80.2051([-0.5160706])([-0.0669443])
90.1789([-0.48097098])([-0.05355542])
100.1563([-0.4482584])([-0.04284435])
110.1368([-0.41777086])([-0.03427548])
120.1198([-0.3893568])([-0.02742033])
130.1051([-0.36287528])([-0.02193622])
140.0923([-0.3381949])([-0.01754898])
150.0812([-0.31519306])([-0.01403922])
160.0716([-0.2937557])([-0.01123137])
170.0633([-0.27377635])([-0.0089851])
180.0560([-0.25515595])([-0.00718815])
190.0497([-0.23780188])([-0.00575048])
200.0443([-0.22162813])([-0.00460032])
210.0395([-0.20655444])([-0.00368026])
220.0354([-0.19250602])([-0.00294429])
230.0318([-0.17941296])([-0.00235539])
240.0287([-0.16721046])([-0.00188428])
250.0260([-0.15583795])([-0.00150746])
260.0237([-0.14523886])([-0.00120598])
270.0216([-0.13536069])([-0.00096482])
280.0198([-0.12615432])([-0.00077182])
290.0183([-0.11757411])([-0.00061745])
300.0170([-0.10957752])([-0.00049394])
310.0158([-0.1021248])([-0.00039512])
320.0148([-0.09517898])([-0.00031608])
330.0139([-0.08870552])([-0.00025291])
340.0132([-0.08267231])([-0.00020235])
350.0125([-0.07704946])([-0.00016194])
360.0119([-0.07180902])([-0.00012952])
370.0114([-0.06692503])([-0.00010365])
380.0110([-0.06237321])([-8.2912156e-05])
390.0106([-0.05813103])([-6.6339504e-05])
400.0103([-0.05417732])([-5.3104945e-05])
410.0100([-0.05049255])([-4.2504398e-05])
420.0098([-0.04705839])([-3.4027966e-05])
430.0096([-0.04385783])([-2.724817e-05])
440.0094([-0.04087488])([-2.1751272e-05])
450.0092([-0.03809486])([-1.7460436e-05])
460.0091([-0.03550391])([-1.399708e-05])
470.0090([-0.03308916])([-1.1263182e-05])
480.0088([-0.03083867])([-8.999137e-06])
490.0088([-0.02874126])([-7.2116964e-06])
500.0087([-0.02678646])([-5.795853e-06])

在这里插入图片描述

7. 什么是二阶拟合?

二阶拟合是指通过一条二次曲线来近似表达数据点之间的关系。简单来说,我们希望找到一个二次方程:

y = a x 2 + b x + c y = ax^2 + bx + c y=ax2+bx+c

其中, y y y 是输出, x x x 是输入特征, a a a b b b c c c 是需要学习的参数。我们的目标是通过训练数据来学习合适的 a a a b b b c c c

8. 神经网络的基本结构

在本示例中,我们将使用一个简单的神经网络模型来进行二阶拟合。模型结构如下:

  • 输入层:接收输入特征 x x x x 2 x^2 x2
  • 隐藏层:使用一个隐藏层,包含多个神经元。
  • 输出层:生成预测值 y y y

9. 损失函数

为了评估模型的预测效果,我们使用均方误差(Mean Squared Error, MSE)作为损失函数,其公式如下:

MSE = 1 n ∑ i = 1 n ( y i − y ^ i ) 2 \text{MSE} = \frac{1}{n} \sum_{i=1}^{n} (y_i - \hat{y}_i)^2 MSE=n1i=1n(yiy^i)2

其中, y i y_i yi 是真实值, y ^ i \hat{y}_i y^i 是模型预测值, n n n 是样本数量。我们的目标是最小化这个损失函数。

10. 反向传播与权重更新

10.1 反向传播

反向传播是神经网络训练的核心算法。它通过链式法则计算损失函数相对于每个参数(权重和偏置)的梯度。具体步骤如下:

  1. 前向传播:计算模型的输出。
  2. 计算损失:通过损失函数评估输出与真实值之间的差异。
  3. 计算梯度:通过反向传播算法计算损失相对于每个参数的梯度。

10.2 权重更新

根据计算得到的梯度,我们可以使用梯度下降法更新权重和偏置。更新公式如下:

w : = w − η ∂ MSE ∂ w w := w - \eta \frac{\partial \text{MSE}}{\partial w} w:=wηwMSE

b : = b − η ∂ MSE ∂ b b := b - \eta \frac{\partial \text{MSE}}{\partial b} b:=bηbMSE

其中, η \eta η 是学习率,控制每次更新的步长。通过多次迭代更新,我们希望能找到最优的 a a a b b b c c c

11. 示例代码

下面是使用 PyTorch 进行二阶拟合的完整代码示例:

import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np
import matplotlib.pyplot as plt
import imageio.v2 as imageio  # 使用 imageio.v2 导入
import os

# 设置随机种子
np.random.seed(42)
torch.manual_seed(42)

# 生成数据
X = np.linspace(-1, 1, 100).reshape(-1, 1)  # 输入特征
y = 2 * (X ** 2) + 1 + np.random.normal(0, 0.1, X.shape)  # 二次关系 y = 2x^2 + 1 + 噪声

# 转换为 PyTorch 张量
X_tensor = torch.FloatTensor(X)
y_tensor = torch.FloatTensor(y)

# 可视化数据
plt.scatter(X, y, label='Data Points', color='blue')
plt.title("Synthetic Data for Quadratic Fitting")
plt.xlabel("X")
plt.ylabel("y")
plt.legend()
plt.show()

# 创建二次回归模型
class QuadraticModel(nn.Module):
    def __init__(self):
        super(QuadraticModel, self).__init__()
        self.linear1 = nn.Linear(1, 10)  # 隐藏层
        self.linear2 = nn.Linear(10, 1)   # 输出层

    def forward(self, x):
        x = torch.relu(self.linear1(x))  # 隐藏层激活
        x = self.linear2(x)  # 输出层
        return x

# 实例化模型、损失函数和优化器
model = QuadraticModel()
criterion = nn.MSELoss()  # 均方误差损失
optimizer = optim.SGD(model.parameters(), lr=0.1)  # 学习率为 0.1

# 训练模型并记录拟合过程
epochs = 300
images = []  # 用于存储每一步的图像

for epoch in range(epochs):
    model.train()  # 设置模型为训练模式

    # 前向传播
    optimizer.zero_grad()  # 清空梯度
    y_pred = model(X_tensor)  # 预测

    # 计算损失
    loss = criterion(y_pred, y_tensor)

    # 反向传播
    loss.backward()  # 计算梯度

    # 打印梯度
    print(f'Epoch {epoch + 1}/{epochs}, Loss: {loss.item():.4f}')
    for name, param in model.named_parameters():
        if param.grad is not None:
            print(f'Gradient for {name}: {param.grad.data.numpy()}')

    # 更新参数
    optimizer.step()  # 更新参数

    # 绘制图像
    plt.figure()
    plt.scatter(X, y, label='Data Points', color='blue')
    plt.plot(X, y_pred.detach().numpy(), label='Fitted Curve', color='red')
    plt.title(f"Epoch {epoch + 1}/{epochs}, Loss: {loss.item():.4f}")
    plt.xlabel("X")
    plt.ylabel("y")
    plt.legend()
    plt.xlim(-1.5, 1.5)
    plt.ylim(-1.5, 3.5)

    # 保存图像
    plt.savefig(f'epoch_{epoch + 1}.png')
    plt.close()

# 生成动图
for epoch in range(1, epochs + 1):
    images.append(imageio.imread(f'epoch_{epoch}.png'))

imageio.mimsave('quadratic_fitting.gif', images, duration=0.5)

# 清理生成的图像文件
for epoch in range(1, epochs + 1):
    os.remove(f'epoch_{epoch}.png')

print("GIF saved as 'quadratic_fitting.gif'")

12. 结果与分析

  • 损失值:随着训练的进行,损失值应该逐渐降低,表明模型的预测效果在改善。
  • 拟合曲线:每个 epoch 的拟合曲线将显示在生成的 GIF 动画中,帮助我们可视化模型的学习过程。
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值