day33 MLP神经网络的训练
目录
神经网络
神经网络的概念
神经网络的核心组成与结构
神经网络的工作原理:从输入到输出
神经网络的学习能力和特点
神经网络和深度学习的关系
准备工作
数据准备
模型架构定义
模型训练(CPU版本)
定义损失函数和优化器
开始循环训练
可视化结果
神经网络
神经网络的概念
神经网络(Neural Network),全称为人工神经网络(Artificial Neural Network, ANN),是一种受生物大脑神经元结构启发的计算模型。其核心思想是通过多层相互连接的“人工神经元”(节点)传递和处理信息,模拟生物神经系统的学习和决策过程。
神经网络的核心组成与结构
-
人工神经元(节点)
- 输入层:接收原始数据(如图像像素、文本特征)。
- 隐藏层:处理和转换输入信息,提取抽象特征(层数和节点数根据任务调整)。
- 输出层:生成最终结果(如分类标签、回归值)。
-
网络连接与权重
- 神经元之间通过 “权重(Weight)” 连接,权重决定信息传递的强度,是模型学习的核心参数。
- 每个神经元的输出通过激活函数(如 ReLU、Sigmoid)引入非线性变换,使网络能处理复杂问题。
-
典型网络结构示例
- 多层感知机(MLP):全连接网络,适用于简单分类任务。
- 卷积神经网络(CNN):通过卷积层提取空间特征,擅长图像处理。
- 循环神经网络(RNN):通过记忆单元处理序列数据,适用于自然语言处理。
神经网络的工作原理:从输入到输出
-
前向传播(Forward Propagation)
- 输入数据经过各层神经元的加权求和与激活函数计算,逐层传递至输出层。
- 数学表达式:
其中 wi 为权重,b为偏置,f为激活函数,a为神经元输出。 - 激活函数的作用:赋予神经网络“思考”的灵魂。它是神经网络中对神经元输入进行变换的函数,其核心作用是为网络引入非线性映射能力。若没有激活函数,多层神经网络将退化为线性模型,只能处理线性问题(如简单的线性回归),无法拟合现实世界中的复杂非线性关系(如图像识别、语言理解等)。
-
反向传播(Backpropagation)
- 计算预测结果与真实值的误差(损失函数),通过链式法则将误差反向传播至各层,更新权重以减小误差。
- 优化目标:通过梯度下降等算法最小化损失函数(如交叉熵、均方误差)。
- 损失函数的作用:衡量模型“对错”的标尺与优化方向的指引。它是神经网络中用于度量模型预测结果和标签差异的函数,其核心作用是将“模型预测的准确性”转化为可计算的数值——数值越小,代表预测与真实越接近。从优化角度看,损失函数为模型训练提供了明确的“目标”:通过调整参数使损失值最小化,从而让模型不断逼近真实规律。
- 梯度下降的思想:从“下山寻谷”到神经参数的优化。本质是“局部最优驱动全局优化”:不追求一步到位,而是通过每一步的“最优局部选择”逐步逼近目标。这种思想在现实中也有映射 —— 如企业管理中通过每日迭代优化流程,最终实现整体效率提升。在深度学习中,尽管存在非凸优化、梯度消失等挑战,但梯度下降仍是目前最有效、最易实现的参数优化方法,其与反向传播的结合,构成了现代神经网络训练的基石。理解梯度下降,就是理解机器学习 “从数据中学习” 的动态过程。
- 优化器:负责更新模型参数、最小化损失函数的算法框架。不同的优化器采用不同的策略来更新参数,直接影响模型的训练速度、稳定性和最终性能。
神经网络的学习能力和特点
-
强大的表征能力
- 多层网络可近似任意复杂函数(通用近似定理),能捕捉数据中的非线性关系。
- 示例:CNN 通过多层卷积提取图像中的边缘、纹理等特征,最终实现物体识别。
-
数据驱动的学习方式
- 无需手动设计特征,直接从数据中学习模式(如自动驾驶通过海量图像数据学习路况识别)。
-
局限性
- 需大量标注数据,训练计算成本高(如 GPT-4 训练需数千块 GPU 协作)。
- 模型可解释性较差(“黑箱” 特性),难以直观理解决策逻辑。
神经网络和深度学习的关系
- 深度学习是神经网络的延伸,特指具有多层隐藏层的复杂神经网络(如 10 层以上的 CNN 或 Transformer)。
- 深度学习通过增加网络深度和复杂度,结合大数据和算力,在图像识别、语音处理、自然语言生成等领域实现了突破性进展(如 AlphaGo、ChatGPT)。
准备工作
# 安装pytorch
%pip install torch torchvision torchaudio -i https://pypi.tuna.tsinghua.edu.cn/simpleimport torch
# 检查CUDA是否可用 (因为我是macos所以不可用)if torch.cuda.is_available():print("CUDA可用!")# 获取可用的CUDA设备数量device_count = torch.cuda.device_count()print(f"可用的CUDA设备数量: {device_count}")# 获取当前使用的CUDA设备索引current_device = torch.cuda.current_device()print(f"当前使用的CUDA设备索引: {current_device}")# 获取当前CUDA设备的名称device_name = torch.cuda.get_device_name(current_device)print(f"当前CUDA设备的名称: {device_name}")# 获取CUDA版本cuda_version = torch.version.cudaprint(f"CUDA版本: {cuda_version}")
else:print("CUDA不可用。")
数据准备
# 仍然使用4特征,3分类的鸢尾花数据集
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
import numpy as np# 加载鸢尾花数据集
X = iris.data # 特征数据
y = iris.target # 标签数据
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)# 打印尺寸
print(X_train.shape)
print(X_test.shape)
print(y_train.shape)
print(y_test.shape)
(120, 4)
(120,)
(30, 4)
(30,)
# 归一化数据,神经网络对于输入数据的尺寸敏感,归一化湿最常见的处理方式
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test) # 确保训练集和测试集是相同的缩放# 将数据转换为 PyTorch 张量,因为 PyTorch 使用张量进行训练
# y_train和y_test是整数,所以需要转化为long整型,如果是float32,会输出1.0 0.0
X_train = torch.FloatTensor(X_train)
y_train = torch.LongTensor(y_train)
X_test = torch.FloatTensor(X_test)
y_test = torch.LongTensor(y_test)
注意事项:
1. 分类任务中,若标签是整数(如 0/1/2 类别),需转为long类型(对应 PyTorch 的torch.long),否则交叉熵损失函数会报错。
2. 回归任务中,标签需转为float类型(如torch.float32)。
模型架构定义
定义一个简单的全连接神经网络模型,包含一个输入层、一个隐藏层和一个输出层。
定义层数+定义前向传播顺序
import torch.nn as nn # 神经网络模块
import torch.optim as optim # 优化器模块class MLP(nn.Module): # 定义一个多层感知机(MLP)模型,继承父类nn.Moduledef __init__: # 初始化函数super(MPL, self).__init__() # 调用父类的初始化函数# 前三行是八股文,后面是自定义的self.fc1 = nn.Linear(4, 10) # 输入层到隐藏层self.relu = nn.ReLU() # 激活函数self.fc2 = nn.Linear(10, 3) # 隐藏层到输出层# 输出层不需要激活函数,因为后面会用到交叉熵函数cross_entropy,交叉熵函数内部有softmax函数,会把输出转化为概率def forward(self, x):out = self.fc1(x)out = self.relu(out)out = self.fc2(out)return out# 其实模型层的写法又很多,relu也可以不写,在后面前向出传播的时候计算下即可,因为relu其实不算一个层,只是一个计算而已
# def forward(self, x):
# out = torch.relu(self.fc1(x))
# out = self.fc2(out)
# return out# 实例化模型
model = MLP()
模型训练(CPU版本)
定义损失函数和优化器
# 分类问题使用交叉熵损失函数
criterion = nn.CrossEntropyLoss()# 使用随机梯度下降优化器
optimizer = optim.SGD(model.parameters(), lr=0.01)# 使用自适应学习率优化器
# optimizer = optim.Adam(model.parameter(), lr=0.01)
开始循环训练
实际上在训练的时候,可以同时观察每个epoch训练完后的测试集的表现:测试集的loss和准确度
# 训练模型
num_epochs = 20000 # 训练的论数# 用于存储每个 epoch 的损失值
losses = []for epoch in range(num_epochs): # range从0开始,所以epoch从0开始# 向前传播outputs = model.forward(X_train) # 显式调用forward函数# outputs = model(X_train) # 常见写法隐式调用forward函数,其实是用了model类的__call__方法 loss = criterion(outputs, y_train) # output是模型预测值,y_train是真实标签# 反向传播和优化optimizer.zero_grad() # 梯度清零,因为PyTorch会累积梯度,所以每次迭代需要清零,梯度累积是那种小的bitchsize模拟大的bitchsizeloss.backward() # 反向传播计算梯度optimizer.step() # 根据学习学习率和梯度更新参数# 记录损失值losses.append(loss.item())# 打印训练信息if (epoch + 1) % 100 == 0: # 每100次训练打印一次print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item():.4f}')
Epoch [100/20000], Loss: 1.0599
Epoch [200/20000], Loss: 1.0205
Epoch [300/20000], Loss: 0.9727
Epoch [400/20000], Loss: 0.9150
Epoch [500/20000], Loss: 0.8493
Epoch [600/20000], Loss: 0.7800
Epoch [700/20000], Loss: 0.7141
Epoch [800/20000], Loss: 0.6562
Epoch [900/20000], Loss: 0.6078
Epoch [1000/20000], Loss: 0.5677
Epoch [1100/20000], Loss: 0.5343
...
Epoch [19600/20000], Loss: 0.0622
Epoch [19700/20000], Loss: 0.0620
Epoch [19800/20000], Loss: 0.0619
Epoch [19900/20000], Loss: 0.0618
Epoch [20000/20000], Loss: 0.0617
如果你重新运行上面这段训练循环,模型参数、优化器状态和梯度会继续保留, 导师训练结果叠加,模型参数和优化器状态(如动量、学习率等)不会被重置。这会导致训练从之前的状态继续,而不是从头开始
可视化结果
import matplotlib.pyplot as plt
# 可视化损失曲线
plt.plot(range(num_epochs), losses)
plt_xlabel('Epoch')
plt_ylabel('Loss')
plt.title('Train Loss over Epochs')
plt.show()
@浙大疏锦行