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

神经网络训练过程详解

神经网络训练过程详解

神经网络训练过程是一个动态的、迭代的学习过程,接下来基于一段代码展示模型是如何逐步学习数据规律的。

神经网络拟合二次函数:代码详解

下面将详细解释这段代码,它使用神经网络拟合一个带有噪声的二次函数 y = x² + 2x + 1

import torch
import numpy as np
import matplotlib.pyplot as plt# 1. 生成模拟数据
np.random.seed(22)  # 设置随机种子确保结果可重现
X = np.linspace(-5, 5, 100).reshape(-1,1)  # 创建100个点,范围-5到5
y_ = X**2 + 2* X + 1  # 二次函数:y = x² + 2x + 1
y = y_ + np.random.rand(100, 1) * 1.5  # 添加均匀分布噪声# 转换为PyTorch张量
X_tensor = torch.tensor(X, dtype=torch.float32)
y_tensor = torch.tensor(y, dtype=torch.float32)# 打印数据信息
print("X 原类型:", type(X), " 形状:", X.shape)
print("X_tensor 类型:", type(X_tensor), " 形状:", X_tensor.shape)
print("\ny 原类型:", type(y), " 形状:", y.shape)
print("y_tensor 类型:", type(y_tensor), " 形状:", y_tensor.shape)
print("\nX_tensor 前 2 个元素:\n", X_tensor[:2])
print("y_tensor 前 2 个元素:\n", y_tensor[:2])

代码解析

1. 神经网络定义

import torch.nn as nnclass SimpleNN(nn.Module):def __init__(self):super(SimpleNN, self).__init__()# 定义三个全连接层self.layer1 = nn.Linear(1, 64)    # 输入层到隐藏层1 (1个特征->64个神经元)self.layer2 = nn.Linear(64, 128)  # 隐藏层1到隐藏层2 (64个神经元->128个神经元)self.layer3 = nn.Linear(128, 1)   # 隐藏层2到输出层 (128个神经元->1个输出)self.relu = nn.ReLU()             # ReLU激活函数def forward(self, x):# 前向传播过程(带激活函数)x = self.relu(self.layer1(x))  # 第一层后接ReLU激活x = self.relu(self.layer2(x))  # 第二层后接ReLU激活x = self.layer3(x)             # 输出层(无激活函数)return xdef forward_line(self, x):# 前向传播过程(不带激活函数)x = self.layer1(x)  # 无激活x = self.layer2(x)  # 无激活x = self.layer3(x)  # 输出层return x

网络结构详解

输入层(1个神经元) → [ReLU激活] → 隐藏层1(64个神经元) → [ReLU激活] → 隐藏层2(128个神经元) → 输出层(1个神经元)
  • 输入层:1个神经元,对应单个特征x
  • 隐藏层1:64个神经元,使用ReLU激活函数
  • 隐藏层2:128个神经元,使用ReLU激活函数
  • 输出层:1个神经元,无激活函数(回归问题)
  • 为什么需要多层和ReLU?:为了拟合非线性关系(二次函数)

2. 模型训练

import torch.optim as optim# 初始化模型、损失函数和优化器
model = SimpleNN()
criterion = nn.MSELoss()  # 均方误差损失(回归问题常用)
optimizer = optim.Adam(model.parameters(), lr=0.01)  # Adam优化器
epochs = 200  # 训练轮数# 训练循环
loss_history = []  # 记录损失变化
for epoch in range(epochs):# 前向传播outputs = model(X_tensor)loss = criterion(outputs, y_tensor)# 反向传播和优化optimizer.zero_grad()  # 清空梯度loss.backward()        # 反向传播计算梯度optimizer.step()       # 更新参数# 记录损失loss_history.append(loss.item())# 每50轮打印一次损失if(epoch + 1)%50 == 0:print(f'Epoch [{epoch + 1}/{epochs}], Loss: {loss.item():.4f}')

训练过程说明

  1. 前向传播:输入数据通过网络得到预测值
  2. 损失计算:比较预测值与真实值的差异(均方误差)
  3. 反向传播
    • zero_grad(): 清空之前的梯度
    • backward(): 计算新的梯度
  4. 参数更新step()使用梯度更新权重
  5. 损失记录:跟踪训练过程中的损失变化

3. 结果可视化

# 创建图表
plt.figure(figsize=(12,4))# 左图:训练损失曲线
plt.subplot(1,2,1)
plt.plot(loss_history)
plt.title('Training Loss')
plt.xlabel('Epochs')
plt.ylabel('MSE Loss')# 右图:预测结果对比
plt.subplot(1, 2, 2)
with torch.no_grad():  # 禁用梯度计算(预测时不需要)predictions = model(X_tensor).numpy()  # 获取预测值# 绘制三种数据:
plt.scatter(X, y, label='original data')  # 散点:带噪声的原始数据
plt.plot(X, y_, 'g--', label='True Relation', linewidth=4)  # 绿色虚线:真实二次函数
plt.plot(X, predictions, 'r--', label='prediction', linewidth=4)  # 红色虚线:神经网络预测plt.title('pre vs true')
plt.legend()  # 显示图例plt.tight_layout()
plt.show()

可视化解读

  1. 损失曲线:展示训练过程中损失值如何下降
  2. 预测对比
    • 散点:带噪声的原始数据
    • 绿色虚线:真实的二次函数 y = x² + 2x + 1
    • 红色虚线:神经网络预测的曲线

4. 模型保存与预测

# 1. 保存完整模型
torch.save(model, 'full_model.pth')# 2. 加载完整模型(无需提前定义模型结构)
loaded_model = torch.load('full_model.pth')
loaded_model.eval()  # 设置为评估模式(关闭dropout等)# 使用模型进行预测
new_data = torch.tensor([[3.0], [-2.5]], dtype=torch.float32)
predictions = loaded_model(new_data).detach().numpy()print("\nPrediction Examples:")
for x, pred in zip(new_data, predictions):# 计算真实值(注意:这里是二次函数,不是线性)true_value = x.item()**2 + 2 * x.item() + 1print(f"Input {x.item():.1f} -> Predicted: {pred[0]:.2f} | True: {true_value:.2f}")

模型保存与加载

  1. torch.save(model, 'full_model.pth'):保存整个模型结构+参数
  2. torch.load('full_model.pth'):加载完整模型
  3. model.eval():将模型设置为评估模式(影响dropout、batchnorm等层)

预测示例

  • 输入x=3.0:真实值=3² + 2×3 + 1 = 16.00
  • 输入x=-2.5:真实值=(-2.5)² + 2×(-2.5) + 1 = 2.25

代码关键点总结

  1. 数据生成

    • 创建二次函数 y = x² + 2x + 1
    • 添加均匀分布噪声模拟真实数据
  2. 网络结构

    • 深度网络(1-64-128-1)适合拟合非线性关系
    • 使用ReLU激活函数引入非线性能力
  3. 训练配置

    • 均方误差损失(MSE)适合回归问题
    • Adam优化器自动调整学习率
    • 200个训练轮次足够收敛
  4. 可视化

    • 损失曲线监控训练过程
    • 预测对比评估模型性能
  5. 模型部署

    • 保存和加载完整模型
    • 对新数据进行预测

为什么这个网络能拟合二次函数?

  1. 非线性激活函数:ReLU使网络能学习非线性关系
  2. 足够容量:两个隐藏层提供足够的表达能力
  3. 优化能力:Adam优化器有效调整参数
  4. 迭代训练:200轮训练使网络逐步逼近目标函数

这个示例展示了神经网络如何学习复杂的非线性关系,即使数据中存在噪声,网络也能捕捉到潜在的函数规律。

训练过程可视化

让我们通过一个动画来理解训练过程(想象以下动态变化):

Epoch 0:  损失: 35.42  | 预测线: 随机波动
Epoch 50: 损失: 2.65   | 预测线: 开始呈现线性趋势
Epoch 100:损失: 2.29   | 预测线: 接近真实关系但仍有偏差
Epoch 200:损失: 1.97   | 预测线: 几乎与真实关系重合
Epoch 500:损失: 1.97   | 预测线: 稳定在最优解附近

训练过程分步解析

1. 初始化阶段 (Epoch 0)

  • 权重和偏置随机初始化(通常使用正态分布或均匀分布)
  • 神经网络对数据一无所知
  • 预测结果完全随机
  • 损失值非常高(约35.42)

2. 早期训练阶段 (Epoch 1-50)

# 第一次迭代
outputs = model(X_tensor)  # 随机预测
loss = criterion(outputs, y_tensor)  # 计算损失(很大)
loss.backward()  # 计算梯度
optimizer.step()  # 首次更新参数
  • 网络开始识别数据的基本模式
  • 预测线开始呈现大致正确的斜率
  • 损失值快速下降(从35.42到约2.65)
  • 模型学习速度最快(梯度最大)

3. 中期训练阶段 (Epoch 50-200)

# 典型迭代
outputs = model(X_tensor)  # 预测接近真实值
loss = criterion(outputs, y_tensor)  # 中等损失
loss.backward()  # 计算较小梯度
optimizer.step()  # 微调参数
  • 网络捕捉到线性关系的基本特征
  • 预测线越来越接近绿色真实关系线
  • 损失值缓慢下降(从2.65到1.97)
  • 学习速度变慢(梯度变小)

4. 后期训练阶段 (Epoch 200-500)

# 后期迭代
outputs = model(X_tensor)  # 预测非常接近真实值
loss = criterion(outputs, y_tensor)  # 小损失
loss.backward()  # 计算微小梯度
optimizer.step()  # 微小调整参数
  • 网络优化细节
  • 预测线与真实关系线几乎重合
  • 损失值稳定在约1.97
  • 模型收敛(参数变化很小)

训练过程关键元素详解

1. 前向传播 (Forward Pass)

outputs = model(X_tensor)
  • 输入数据通过神经网络各层
  • 计算过程:
    输入x → 线性变换: z1 = w1*x + b1→ ReLU激活: a1 = max(0, z1)→ 线性变换: output = w2*a1 + b2
    

2. 损失计算 (Loss Calculation)

loss = criterion(outputs, y_tensor)
  • 计算预测值与真实值的差异
  • 使用均方误差公式:
    MSE = 1/N * Σ(预测值 - 真实值)²
    

3. 反向传播 (Backward Pass)

loss.backward()
  • 计算损失函数对每个参数的梯度
  • 使用链式法则从输出层向输入层反向传播
  • 梯度表示"参数应该如何调整以减少损失"

4. 参数更新 (Parameter Update)

optimizer.step()
  • Adam优化器根据梯度更新参数
  • 更新公式简化表示:
    新参数 = 旧参数 - 学习率 * 梯度
    
  • 学习率(0.01)控制更新步长

训练过程可视化分析

损失曲线图

损失值
35 |*················
30 | *···············
25 |  *··············
20 |   *·············
15 |    *············
10 |     *···········5 |      **·········2 |         ****····1 |             ****0 +-----------------→ Epoch0  50 100 200 500
  • 曲线特点:开始陡峭下降,后期平缓
  • 表明:初期学习快,后期优化细调

预测结果演变

真实关系: y = 2x + 1 (绿色虚线)Epoch 0:预测线: 随机波动 (红色线)Epoch 50:预测线: 大致正确斜率但截距偏差Epoch 100:预测线: 接近真实线,部分区域过拟合噪声Epoch 500:预测线: 几乎与绿色虚线重合

为什么训练有效?

  1. 梯度下降原理:每次更新都向减少损失的方向移动
  2. 链式法则:高效计算所有参数的梯度
  3. 自适应优化器:Adam自动调整学习率
  4. 非线性能力:ReLU激活函数使网络能拟合复杂模式
  5. 迭代优化:多次重复使模型逐步接近最优解

训练结束后的模型状态

  • 权重和偏置已优化到最佳值
  • 网络学习到了潜在规律 y = 2x + 1
  • 能够准确预测新数据点
  • 损失值稳定在最低点(约1.97)

这个训练过程展示了神经网络如何从随机初始状态开始,通过反复的预测、评估和调整,最终学习到数据背后的规律。即使数据中存在噪声,神经网络也能识别出真实的线性关系。

http://www.lryc.cn/news/625764.html

相关文章:

  • 20.2 QLoRA微调全局参数实战:高点击率配置模板+显存节省50%技巧
  • (一)八股(数据库/MQ/缓存)
  • 【赎金信】
  • 环境变量不生效?
  • C#三大核心特性
  • 实现自己的AI视频监控系统-第一章-视频拉流与解码1
  • Laya的适配模式选择
  • 代码随想录算法训练营四十六天|图论part04
  • BROADCHIP广芯电子在各类电子产品的方案与应用
  • 企业如何让内部视频仅限指定域名播放,确保视频不被泄露?
  • 2025年8月16日(星期六):雨骑古莲村游记
  • 机器人控制基础:运动控制中的串级pid原理以及实现方案(包含代码示例)
  • 学习笔记分享——基于STM32的平衡车项目
  • 8.19打卡 DAY 46 通道注意力(SE注意力)
  • RabbitMQ处理流程详解
  • docker回炉重造
  • 无畏契约手游上线!手机远控模拟器畅玩、抢先注册稀有ID!
  • 概率论基础教程第5章 连续型随机变量(一)
  • Flask 路由与视图函数绑定机制
  • 编译器错误消息: CS0016: 未能写入输出文件“c:\Windows\Microsoft.NET... 拒绝访问
  • 概率论基础教程第4章 随机变量(四)
  • Android Cordova 开发 - Cordova 嵌入 Android
  • GaussDB 中 alter default privileges 的使用示例
  • 从H.264到AV1:音视频技术演进与模块化SDK架构全解析
  • Meta首款AR眼镜Hypernova呼之欲出,苹果/微美全息投入显著抢滩市场新增长点!
  • 搭建最新--若依分布式spring cloudv3.6.6 前后端分离项目--步骤与记录常见的坑
  • 磨砂玻璃登录页面使用教程 v0.1.1
  • 可靠性测试:软件稳定性的守护者
  • t12 low power design: power plan脚本分享(4) power stripe
  • 9.Ansible管理大项目