【深度学习】深度学习理论与实战:全面解析多层神经网络
在机器学习领域,线性回归和 Logistic 回归作为基础模型,分别解决了简单的回归与分类问题。但面对现实世界中复杂的非线性数据,这些单层模型往往显得力不从心。今天我们将走进深度学习的入门模型 —— 多层神经网络,从理论原理到代码实现,全方位拆解其核心逻辑、关键组件与实战应用,搞懂 “多层” 与 “激活函数” 的真正价值。
一、神经网络的核心原理:从人脑神经元到数学模型
1. 神经网络的灵感来源
神经网络的设计灵感源自人脑的神经元系统。人脑神经元通过突触接收信号,激活后将冲动传递给下一个神经元;而人工神经网络则模拟这一过程:先接收数据输入,经过计算与激活处理,再将结果传递给下一层,最终输出预测结果。
线性回归(y=wx+b)和 Logistic 回归(y=Sigmoid(wx+b))本质上都是单层神经网络,其中 Logistic 回归引入的 Sigmoid 函数,正是神经网络中至关重要的激活函数。
2. 激活函数:神经网络的 “非线性灵魂”
激活函数是多层神经网络的核心,其核心作用是为模型注入非线性,否则无论堆叠多少层,最终都会退化为单层线性模型。
(1)为什么必须用激活函数?
从数学角度看,假设一个两层神经网络没有激活函数,其输出为:
y=w2(w1x)=(w2w1)x=wx两层参数会合并为一个新权重w,多层结构完全失效。而加入激活函数A后,输出变为:y=w2A(w1x)非线性特性让模型能够拟合复杂的数据分布,这也是神经网络万有逼近定理的核心 —— 带激活函数的神经网络可通过调整权重实现任意复杂形状的拟合。
(2)三种常用激活函数
- Sigmoid 函数:σ(x)=1+e−x1,输出范围 [0,1],常用于二分类任务的输出层,但存在梯度消失问题。
- Tanh 函数:tanh(x)=2σ(2x)−1,输出范围 [-1,1],相比 Sigmoid 更接近零均值,梯度消失问题有所缓解。
- ReLU 函数:ReLU(x)=max(0,x),目前 90% 的场景首选。正数直接输出,负数归零,计算简单、收敛速度快,有效避免梯度消失。
3. 多层神经网络的结构:输入层→隐藏层→输出层
神经网络的结构简洁清晰,由三层核心组件构成:
- 输入层:接收原始数据,神经元数量等于数据的特征维度(如房屋预测中的面积、房龄对应 2 个神经元)。
- 隐藏层:数据加工的核心,层数和神经元数量可灵活调整。层数越多、神经元数越多,模型拟合复杂问题的能力越强。
- 输出层:输出最终结果,神经元数量由任务类型决定(二分类用 1 个神经元,多分类用类别数对应的神经元,回归任务用 1 个神经元)。
数据在网络中的传播过程称为前向传播,即从输入层逐层经过线性计算(wx+b)和激活函数处理,最终由输出层输出结果。
二、PyTorch 实战:多层神经网络完整实现
下面通过 “螺旋形数据分类” 任务,对比单层 Logistic 回归与多层神经网络的效果,同时演示Sequential和Module两种模型构建方式,以及模型的保存与加载。
1. 环境准备与工具导入
import torch
import numpy as np
from torch import nn
from torch.autograd import Variable
import torch.nn.functional as F
import matplotlib.pyplot as plt
2. 辅助函数:绘制分类边界
为了直观观察模型效果,定义分类边界绘制函数:
def plot_decision_boundary(model, x, y):
# 设置绘图范围并添加边距
x_min, x_max = x[:, 0].min() - 1, x[:, 0].max() + 1
y_min, y_max = x[:, 1].min() - 1, x[:, 1].max() + 1
h = 0.01 # 网格点间隔
# 生成网格点
xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))
# 预测网格点类别
Z = model(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
# 绘制分类区域和原始数据
plt.contourf(xx, yy, Z, cmap=plt.cm.Spectral)
plt.ylabel('x2')
plt.xlabel('x1')
plt.scatter(x[:, 0], x[:, 1], c=y.reshape(-1), s=40, cmap=plt.cm.Spectral)
3. 生成复杂数据集:螺旋形分类数据
模拟现实中非线性分布的数据,共 400 个样本,分为两类:
np.random.seed(1) # 固定随机种子以复现结果
m = 400 # 总样本数
N = int(m/2) # 每类样本数
D = 2 # 特征数(x1和x2)
x = np.zeros((m, D)) # 特征数组
y = np.zeros((m, 1), dtype='uint8') # 标签数组(0或1)
a = 4 # 螺旋幅度参数
for j in range(2):
ix = range(N*j, N*(j+1))
t = np.linspace(j*3.12, (j+1)*3.12, N) + np.random.randn(N)*0.2 # 角度(含噪声)
r = a*np.sin(4*t) + np.random.randn(N)*0.2 # 半径(含噪声)
x[ix] = np.c_[r*np.sin(t), r*np.cos(t)] # 极坐标转平面坐标
y[ix] = j # 标记类别
plt.scatter(x[:, 0], x[:, 1], c=y.reshape(-1), s=40, cmap=plt.cm.Spectral)
plt.show()

4. 基线模型:单层 Logistic 回归(效果有限)
先用单层模型尝试分类,验证其局限性:
# 数据类型转换(numpy→torch.Tensor)
x = torch.from_numpy(x).float()
y = torch.from_numpy(y).float()
# 定义模型参数
w = nn.Parameter(torch.randn(2, 1)) # 权重:2特征→1输出
b = nn.Parameter(torch.zeros(1)) # 偏置
# 优化器(SGD)和损失函数(二分类交叉熵)
optimizer = torch.optim.SGD([w, b], 1e-1)#学习率1e-1
criterion = nn.BCEWithLogitsLoss()
# 定义Logistic回归模型
def logistic_regression(x):
return torch.mm(x, w) + b
# 训练模型(100轮)
for e in range(100):
out = logistic_regression(Variable(x))
loss = criterion(out, Variable(y))
optimizer.zero_grad() # 清空梯度
loss.backward() # 反向传播
optimizer.step() # 更新参数
if (e + 1) % 20 == 0:
print(f'epoch: {e+1}, loss: {loss.item()}')
# 定义预测函数(用于绘图)
def plot_logistic(x):
x = Variable(torch.from_numpy(x).float())
out = F.sigmoid(logistic_regression(x))
out = (out > 0.5) * 1 # 概率阈值判断
return out.data.numpy()
# 绘制分类边界
plot_decision_boundary(lambda x: plot_logistic(x), x.numpy(), y.numpy())
plt.title('logistic regression')
plt.show()


结果:训练后损失稳定在 0.67 左右,分类边界为直线,无法分开缠绕的螺旋数据,充分体现了单层线性模型的局限性。
5. 多层神经网络:手动定义参数(两层网络)
加入隐藏层和激活函数,构建两层神经网络,突破线性限制:
# 定义两层网络参数
w1 = nn.Parameter(torch.randn(2, 4) * 0.01) # 输入层→隐藏层:2→4
b1 = nn.Parameter(torch.zeros(4)) # 隐藏层偏置
w2 = nn.Parameter(torch.randn(4, 1) * 0.01) # 隐藏层→输出层:4→1
b2 = nn.Parameter(torch.zeros(1)) # 输出层偏置
# 定义两层神经网络(含tanh激活函数)
def two_network(x):
x1 = torch.mm(x, w1) + b1 # 第一层线性计算
x1 = F.tanh(x1) # 激活函数(注入非线性)
x2 = torch.mm(x1, w2) + b2 # 第二层线性计算
return x2
# 优化器(学习率1.0)
optimizer = torch.optim.SGD([w1, w2, b1, b2], 1.)
criterion = nn.BCEWithLogitsLoss()
x = torch.from_numpy(x).float() # 特征转float张量(神经网络默认用float计算)
y = torch.from_numpy(y).float() # 标签转float张量(匹配BCEWithLogitsLoss的输入要求)
# 训练模型(10000轮)
for e in range(10000):
out = two_network(x)
loss = criterion(out, y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
if (e + 1) % 1000 == 0:
print(f'epoch: {e+1}, loss: {loss.item()}')
# 定义预测函数
def plot_network(x):
x = torch.from_numpy(x).float()
x1 = torch.mm(x, w1) + b1
x1 = F.tanh(x1)
x2 = torch.mm(x1, w2) + b2
out = F.sigmoid(x2)
out = (out > 0.5) * 1
return out.data.numpy()
# 绘制分类边界
plot_decision_boundary(lambda x: plot_network(x), x.numpy(), y.numpy())
plt.title('2 layer network')
plt.show()


结果:损失降至 0.24 左右,分类边界呈现复杂曲线,完美分开螺旋数据,验证了多层 + 激活函数的强大威力。
6. 简化模型构建:Sequential 与 Module
手动定义参数在深层网络中过于繁琐,PyTorch 提供了Sequential和Module两种高效构建方式。
(1)Sequential:积木式构建(适合简单顺序结构)
# 构建两层网络
seq_net = nn.Sequential(
nn.Linear(2, 4), # 线性层:2→4(自动生成参数)
nn.Tanh(), # 激活函数
nn.Linear(4, 1) # 线性层:4→1
)
# 访问网络层和参数
print(seq_net[0]) # 查看第一层:Linear(in_features=2, out_features=4)
w0 = seq_net[0].weight # 访问第一层权重
criterion = nn.BCEWithLogitsLoss()
x = torch.from_numpy(x).float()
y = torch.from_numpy(y).float()
# 训练模型
optim = torch.optim.SGD(seq_net.parameters(), 1.)
for e in range(10000):
out = seq_net(x)
loss = criterion(out, Variable(y))
optim.zero_grad()
loss.backward()
optim.step()
if (e + 1) % 1000 == 0:
print(f'epoch: {e+1}, loss: {loss.item()}')
# 预测与绘图
def plot_seq(x):
out = F.sigmoid(seq_net(Variable(torch.from_numpy(x).float()))).data.numpy()
out = (out > 0.5) * 1
return out
plot_decision_boundary(lambda x: plot_seq(x), x.numpy(), y.numpy())
plt.title('sequential')
plt.show()


(2)Module:灵活定制(适合复杂结构)
Module支持自定义层连接逻辑,灵活性更高:
# 定义网络类(继承nn.Module)
class module_net(nn.Module):
def __init__(self, num_input, num_hidden, num_output):
super(module_net, self).__init__()
self.layer1 = nn.Linear(num_input, num_hidden) # 输入层→隐藏层
self.layer2 = nn.Tanh() # 激活函数
self.layer3 = nn.Linear(num_hidden, num_output) # 隐藏层→输出层
def forward(self, x): # 定义前向传播路径
x = self.layer1(x)
x = self.layer2(x)
x = self.layer3(x)
return x
# 创建模型实例(2输入→4隐藏→1输出)
mo_net = module_net(2, 4, 1)
x = torch.from_numpy(x).float()
y = torch.from_numpy(y).float()
# 二分类任务专用损失函数:BCEWithLogitsLoss(自动处理“未激活的原始分数”,比手动加sigmoid更稳定)
criterion = nn.BCEWithLogitsLoss()
# 训练模型
optim = torch.optim.SGD(mo_net.parameters(), 1.)
for e in range(10000):
out = mo_net(x)
loss = criterion(out, y)
optim.zero_grad()
loss.backward()
optim.step()
if (e + 1) % 1000 == 0:
print(f'epoch: {e+1}, loss: {loss.item()}')
def plot_module(x):
x_tensor = torch.from_numpy(x).float() # 输入数据:numpy→Tensor
out = mo_net(x_tensor) # 模型预测(得到原始分数)
out = F.sigmoid(out) # 原始分数→概率(0~1,二分类的“属于正类的概率”)
out = (out > 0.5) * 1 # 概率阈值判断:>0.5为1类,否则为0类(二分类最终结果)
return out.data.numpy() # 结果:Tensor→numpy(适配绘图函数)
# 绘制分类边界(复用你之前的plot_decision_boundary函数)
plot_decision_boundary(lambda x: plot_module(x), x.numpy(), y.numpy())
plt.title('module_net') # 图标题:标注是自定义类搭建的网络
plt.show()


7. 模型保存与加载(实战必备)
训练好的模型需要保存以便后续使用,PyTorch 提供两种保存方式:
(1)保存模型结构 + 参数
# 保存
torch.save(seq_net, 'save_seq_net.pth')
# 加载
seq_net1 = torch.load('save_seq_net.pth')
(2)仅保存参数(推荐,移植性更强)
# 保存参数
torch.save(seq_net.state_dict(), 'save_seq_net_params.pth')
# 加载参数(需先重建模型结构)
seq_net2 = nn.Sequential(
nn.Linear(2, 4),
nn.Tanh(),
nn.Linear(4, 1)
)
seq_net2.load_state_dict(torch.load('save_seq_net_params.pth'))
8. 进阶练习:更深的网络(5 层)
增加网络深度和训练次数,进一步提升模型性能:
# 构建5层网络
net = nn.Sequential(
nn.Linear(2, 10),
nn.Tanh(),
nn.Linear(10, 10),
nn.Tanh(),
nn.Linear(10, 10),
nn.Tanh(),
nn.Linear(10, 1)
)
x = torch.from_numpy(x).float() # 特征:numpy→Tensor(浮点型,适配神经网络)
y = torch.from_numpy(y).float() # 标签:numpy→Tensor(浮点型,匹配损失函数)
criterion = nn.BCEWithLogitsLoss() # 二分类专用损失函数(自动处理sigmoid激活)
# 训练20000轮(学习率0.1)
optim = torch.optim.SGD(net.parameters(), 0.2)
for e in range(20000):
out = net(x)
loss = criterion(out, y)
optim.zero_grad()
loss.backward()
optim.step()
if (e + 1) % 1000 == 0:
print(f'epoch: {e+1}, loss: {loss.item()}')
# 绘图
def plot_net(x):
out = F.sigmoid(net(Variable(torch.from_numpy(x).float()))).data.numpy()
out = (out > 0.5) * 1
return out
plot_decision_boundary(lambda x: plot_net(x), x.numpy(), y.numpy())
plt.title('deep network')
plt.show()


结果:损失降至 0.09 左右,分类精度进一步提升,证明深层网络能提取更复杂的特征。
三、核心总结与关键启示
- 多层神经网络的核心价值:通过 “输入层→隐藏层→输出层” 的结构分工,结合激活函数的非线性特性,突破单层模型的线性限制,能够处理螺旋形、交叉形等复杂数据分布。
- 激活函数的选择:ReLU 函数因其计算简单、收敛快、梯度消失风险低,成为绝大多数场景的首选;Sigmoid 和 Tanh 适用于特定场景(如输出层概率映射)。
- 模型构建技巧:简单模型用
Sequential(搭积木高效),复杂模型用Module(自定义路径灵活);模型保存优先选择 “仅保存参数”,提升移植性。 - 训练关键参数:网络深度、隐藏层神经元数、训练次数、学习率都会影响模型效果,需根据任务调整(如深层网络建议减小学习率、增加训练次数)。
多层神经网络作为深度学习的基础,其核心逻辑 ——“分层加工 + 非线性激活”—— 贯穿于 CNN、Transformer 等复杂模型中。掌握本文的理论与代码实践,将为你后续深入深度学习领域打下坚实基础。

更多推荐
所有评论(0)