名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》
创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊)
👋 专栏介绍: Python星球日记专栏介绍(持续更新ing)
✅ 上一篇: 《Python星球日记》 第50天:深度学习概述与环境搭建
今天是我们Python星球探索之旅的第51天!🪐
一、引言:走进神经网络的世界
经过前面的机器学习基础学习,我们今天将揭开深度学习的神秘面纱,探索神经网络的基本原理。
神经网络是什么?简单来说,它是一种模仿人类大脑结构和工作方式的算法模型,能够从大量数据中学习复杂的模式。无论是语音识别、图像分类还是自然语言处理,神经网络都展现出了强大的能力。让我们一起踏上这段奇妙的旅程吧!
二、神经元与激活函数
1. 神经元:计算的基本单元
神经元(Neuron)是神经网络的基本构建块,其灵感来源于生物神经元的工作方式。在数学上,一个神经元接收多个输入,对这些输入进行加权求和,然后通过一个激活函数转换为输出。
神经元模型是一个接收多个输入值,并产生单一输出的基本计算单元。它包含三个关键组成部分:
- 输入与权重:每个输入值
x_i
都与一个对应的权重w_i
相乘 - 求和函数:将所有加权输入求和,并加上一个偏置项
b
- 激活函数:将线性求和的结果转化为非线性输出
神经元的数学表达式为:
y = f(w₁x₁ + w₂x₂ + ... + wₙxₙ + b)
其中 f
就是激活函数,它决定了神经元的输出特性。
2. 激活函数的种类与特点
激活函数 是 神经网络中引入非线性的关键组件,它能让网络学习复杂的模式。让我们来了解几种常用的激活函数:
a) Sigmoid 函数
Sigmoid 函数是最早使用的激活函数之一,其数学表达式为:
σ(x) = 1 / (1 + e^(-x))
它将任何输入映射到 0 到 1 之间的值,形成一条 S 形曲线。
- 优点:输出范围有限,可以用于表示概率,平滑可导
- 缺点:在输入值较大或较小时,梯度接近于零,导致梯度消失问题;输出不是零中心的
b) ReLU 函数
ReLU(Rectified Linear Unit,修正线性单元)是目前最受欢迎的激活函数之一,其数学表达式简单:
ReLU(x) = max(0, x)
它将所有负值置为零,而正值保持不变。
- 优点:计算效率高,有效缓解梯度消失问题,促进稀疏激活
- 缺点:可能导致"神经元死亡"(当输入总是为负时),输出不是零中心的
c) Tanh 函数
Tanh(双曲正切)函数在形状上类似于 Sigmoid,但其输出范围是 -1 到 1:
tanh(x) = (e^x - e^(-x)) / (e^x + e^(-x))
- 优点:输出是零中心的,有助于后续层的学习
- 缺点:仍然存在梯度消失问题,但比 Sigmoid 要轻微
三、多层感知机(MLP)结构
1. 从单个神经元到神经网络
单个神经元的能力有限,但当我们将多个神经元按层组织起来,就形成了多层感知机(Multilayer Perceptron,MLP)。MLP 是最基础的前馈神经网络,信息只从输入层向输出层单向传播。
2. MLP 的基本组成部分
多层感知机通常由以下三种层构成:
- 输入层:接收原始数据的神经元,每个输入特征对应一个神经元
- 隐藏层:位于输入层和输出层之间的中间层,可以有一层或多层
- 输出层:产生最终预测结果的神经元层
每一层的神经元都与下一层的所有神经元全连接,这种结构也称为全连接层或密集层。一个典型的 MLP 网络可以表示为:
输入层 → 隐藏层1 → 隐藏层2 → ... → 隐藏层n → 输出层
3. 多层感知机的特点与优势
多层感知机相比单层感知机有以下显著优势:
- 非线性映射能力:通过激活函数引入非线性,能够学习复杂的非线性决策边界
- 表达能力增强:隐藏层越多,网络的表达能力越强,可以拟合更复杂的函数
- 分层特征学习:浅层网络学习简单特征,深层网络学习复杂特征,形成层次化的特征表示
四、前向传播与损失计算
1. 前向传播:信息的正向流动
前向传播(Forward Propagation)是神经网络中信息从输入层流向输出层的过程。
在这个过程中,每一层的神经元接收前一层的输出,进行计算,然后将结果传递给下一层。让我们用一个简单的双隐层神经网络来解释这个过程:
1》输入层接收数据:假设我们有特征向量 X = [x₁, x₂, …, xₙ]
2》计算第一隐藏层:
- 线性变换:Z₁ = W₁X + b₁,其中 W₁ 是权重矩阵,b₁ 是偏置向量
- 应用激活函数:A₁ = f₁(Z₁),如 ReLU(Z₁)
3》计算第二隐藏层:
- 线性变换:Z₂ = W₂A₁ + b₂
- 应用激活函数:A₂ = f₂(Z₂)
4》计算输出层:
- 线性变换:Z₃ = W₃A₂ + b₃
- 应用输出激活函数:Ŷ = f_out(Z₃)
- 对于回归问题,可能不使用激活函数
- 对于二分类问题,通常使用 Sigmoid 函数
- 对于多分类问题,通常使用 Softmax 函数
前向传播是一个从输入到输出的计算过程,每一步都依赖于前一步的结果和当前层的参数(权重和偏置)。
2. 损失函数:评估预测质量
损失函数(Loss Function)用于衡量神经网络预测值与真实值之间的差距。不同类型的问题需要不同的损失函数:
均方误差(Mean Squared Error, MSE):常用于回归问题
MSE = (1/n) * Σ(y_i - ŷ_i)²
交叉熵损失(Cross-Entropy Loss):常用于分类问题
对于二分类:CE = -[y * log(ŷ) + (1-y) * log(1-ŷ)] 对于多分类:CE = -Σ(y_i * log(ŷ_i))
损失函数越小,表示模型的预测越接近真实值,这是神经网络训练的目标。
五、激活函数的选择与影响
1. 不同层的激活函数选择策略
在神经网络设计中,激活函数的选择对模型性能有显著影响。这里有一些常用的选择策略:
1》隐藏层激活函数:
- ReLU 是目前隐藏层最常用的激活函数,因为它计算简单且有效缓解梯度消失问题
- Leaky ReLU 和 ELU 是 ReLU 的变种,解决了 “神经元死亡” 问题
- Tanh 在某些情况下仍然有用,特别是在循环神经网络中
2》输出层激活函数:
- 线性函数(即不使用激活函数):适用于回归问题
- Sigmoid:适用于二分类问题(输出0-1之间的概率值)
- Softmax:适用于多分类问题(输出多个类别的概率分布)
2. 激活函数对模型性能的影响
激活函数的选择会影响以下几个方面:
- 训练速度:ReLU系列通常比 Sigmoid 和 Tanh 训练更快
- 梯度流动:好的激活函数应当避免梯度消失或爆炸
- 模型表达能力:非线性激活函数让网络能学习复杂模式
- 稀疏激活:如 ReLU 让部分神经元不激活,增加模型稀疏性
一般的经验法则是:从 ReLU 作为隐藏层的激活函数开始,如果遇到问题再尝试其他选择。
六、代码练习:使用框架构建简单神经网络
现在让我们动手实践,分别使用 PyTorch 和 TensorFlow 构建一个简单的神经网络,来解决手写数字识别的经典问题(MNIST数据集)。
1. 使用 PyTorch 构建神经网络
import torch
import torch.nn as nn
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms
from torch.utils.data import DataLoader
# 设定随机种子,确保结果可重复
torch.manual_seed(42)
# 1. 数据准备
transform = transforms.Compose([
transforms.ToTensor(), # 将图像转换为张量
transforms.Normalize((0.1307,), (0.3081,)) # 标准化(MNIST数据集的均值和标准差)
])
# 加载MNIST训练集和测试集
train_dataset = torchvision.datasets.MNIST(root='./data', train=True, download=True, transform=transform)
test_dataset = torchvision.datasets.MNIST(root='./data', train=False, download=True, transform=transform)
# 创建数据加载器
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=1000, shuffle=False)
# 2. 定义神经网络模型
class MLP(nn.Module):
def __init__(self):
super(MLP, self).__init__()
# 输入层 -> 隐藏层1
self.fc1 = nn.Linear(28*28, 128) # MNIST图像是28x28像素
# 隐藏层1 -> 隐藏层2
self.fc2 = nn.Linear(128, 64)
# 隐藏层2 -> 输出层
self.fc3 = nn.Linear(64, 10) # 10个类别(数字0-9)
# 定义激活函数
self.relu = nn.ReLU()
def forward(self, x):
# 将28x28的图像展平为向量
x = x.view(-1, 28*28)
# 前向传播过程
x = self.fc1(x) # 线性变换
x = self.relu(x) # 应用ReLU激活函数
x = self.fc2(x) # 线性变换
x = self.relu(x) # 应用ReLU激活函数
x = self.fc3(x) # 线性变换到输出层
return x # 返回logits,不应用softmax(CrossEntropyLoss会内部处理)
# 3. 实例化模型、定义损失函数和优化器
model = MLP()
criterion = nn.CrossEntropyLoss() # 交叉熵损失函数(内部包含softmax)
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9) # 随机梯度下降优化器
# 4. 训练模型
num_epochs = 5
print("开始训练...")
for epoch in range(num_epochs):
running_loss = 0.0
for i, (inputs, labels) in enumerate(train_loader):
# 清空梯度
optimizer.zero_grad()
# 前向传播
outputs = model(inputs)
# 计算损失
loss = criterion(outputs, labels)
# 反向传播
loss.backward()
# 更新参数
optimizer.step()
# 统计损失
running_loss += loss.item()
# 每100批次打印一次训练信息
if (i+1) % 100 == 0:
print(f'Epoch [{epoch+1}/{num_epochs}], Step [{i+1}/{len(train_loader)}], Loss: {running_loss/100:.4f}')
running_loss = 0.0
# 5. 测试模型
model.eval() # 设置为评估模式
correct = 0
total = 0
with torch.no_grad(): # 不计算梯度
for inputs, labels in test_loader:
outputs = model(inputs)
_, predicted = torch.max(outputs.data, 1) # 获取最大概率的索引
total += labels.size(0)
correct += (predicted == labels).sum().item()
print(f'测试准确率: {100 * correct / total:.2f}%')
2. 使用 TensorFlow/Keras 构建神经网络
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers, models
import numpy as np
# 设置随机种子,确保结果可重复
tf.random.set_seed(42)
np.random.seed(42)
# 1. 加载和预处理MNIST数据集
(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()
# 标准化像素值到[0, 1]范围
x_train = x_train.astype("float32") / 255
x_test = x_test.astype("float32") / 255
# 将图像展平为一维向量
x_train = x_train.reshape(-1, 28*28)
x_test = x_test.reshape(-1, 28*28)
# One-hot编码标签
y_train = keras.utils.to_categorical(y_train, 10)
y_test = keras.utils.to_categorical(y_test, 10)
# 2. 定义神经网络模型
model = models.Sequential([
# 输入层 -> 隐藏层1
layers.Dense(128, activation='relu', input_shape=(28*28,)),
# 隐藏层1 -> 隐藏层2
layers.Dense(64, activation='relu'),
# 隐藏层2 -> 输出层
layers.Dense(10, activation='softmax') # 输出层使用softmax激活函数
])
# 3. 编译模型
model.compile(
optimizer=keras.optimizers.SGD(learning_rate=0.01, momentum=0.9), # 随机梯度下降优化器
loss='categorical_crossentropy', # 分类交叉熵损失函数
metrics=['accuracy'] # 评估指标
)
# 4. 查看模型结构
model.summary()
# 5. 训练模型
print("开始训练...")
history = model.fit(
x_train, y_train,
epochs=5,
batch_size=64,
validation_split=0.1, # 使用10%的训练数据作为验证集
verbose=1
)
# 6. 评估模型性能
test_loss, test_acc = model.evaluate(x_test, y_test)
print(f"测试准确率: {test_acc*100:.2f}%")
# 7. 进行预测示例
predictions = model.predict(x_test[:5])
print("预测结果示例:")
for i in range(5):
predicted_label = np.argmax(predictions[i])
true_label = np.argmax(y_test[i])
print(f"样本 {i+1}: 预测为 {predicted_label}, 实际为 {true_label}")
七、总结与展望
今天,我们学习了神经网络的基础知识,包括神经元结构、激活函数、多层感知机以及前向传播的原理。通过实践,我们使用 PyTorch 和 TensorFlow 实现了简单的神经网络模型。这些概念是深度学习的基石,将帮助我们理解更复杂的神经网络架构。
1. 关键知识点回顾
- 神经元是神经网络的基本单元,由输入、权重、偏置和激活函数组成
- 激活函数(Sigmoid、ReLU、Tanh)为网络引入非线性,使其能学习复杂模式
- 多层感知机(MLP)是一种基本的前馈神经网络,由输入层、隐藏层和输出层组成
- 前向传播是信息从输入层到输出层的流动过程,用于生成预测
- 损失函数用于衡量预测值与真实值之间的差距,是模型优化的目标
2. 深度学习的下一步
神经网络基础只是深度学习的开始。在接下来的学习中,我们将探索:
- 反向传播算法:如何通过梯度下降更新网络权重
- 卷积神经网络(CNN):特别适用于图像处理的神经网络
- 循环神经网络(RNN):处理序列数据的网络架构
- 注意力机制:提高模型对关键信息的关注
- 预训练模型:如何利用迁移学习加速模型训练
深度学习是一个不断发展的领域,持续学习和实践是掌握这些技术的关键。明天,我们将继续我们的Python星球之旅,探索更多深度学习的奥秘!
祝你学习愉快,Python星球的探索者!👨🚀🌠
创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊)
如果你对今天的内容有任何问题,或者想分享你的学习心得,欢迎在评论区留言讨论!