一文搞懂前馈网络(全连接):原理 + MindSpore 实战
# 优化器:Adam+权重衰减optimizer = nn.Adam(net.trainable_params(), lr=0.001, weight_decay=1e-4)。全连接网络的训练,本质是 “优化权重 w和偏置 b”,让模型预测越来越准。MindSpore 不用改损失函数,直接在优化器里加参数:。损失函数是 “裁判”,用来衡量模型预测值 y^和真实标签 y的差距。(
一、什么是前馈网络 / 全连接网络?
前馈网络(Feedforward Neural Network)是信号单向传播的网络:输入层→隐藏层→输出层,没有反馈回路,就像水流从高处往低处流,不会回头。
而全连接网络(Fully Connected Network, FCN) 是前馈网络的 “经典款”—— 它的每一层神经元,都会和下一层的所有神经元建立连接。比如输入层有 10 个神经元,隐藏层有 20 个神经元,那这两层之间就有 10×20=200 个连接(每个连接对应一个权重参数)。
优点:结构简单、易于理解;缺点:参数多,容易过拟合(后面会讲怎么解决)。
二、最小单元:单个神经元的构造与激活函数
全连接网络的 “积木” 是单个神经元,先搞懂它,再看多层网络就很简单了。
2.1 单个神经元的结构
一个神经元的工作流程分 3 步:
- 接收输入:比如特征向量
x1,x2,...,xn;
- 线性组合:给每个输入加个 “权重”(重要性),再加上 “偏置”(调整基线),公式是
z=i=1∑nwixi+b;
- 非线性变换:通过激活函数把线性结果 “掰弯”,得到输出 a=f(z)
—— 没有这一步,多层网络就等价于单层线性模型,根本拟合不了复杂数据!
2.2 3 个常用激活函数(附 MindSpore 调用)
激活函数的选择直接影响模型效果,这 3 个是实战中最常用的,记牢它们的特点和用法:
|
激活函数 |
公式 |
核心特点 |
适用场景 |
MindSpore API |
|
Sigmoid |
σ(z)=1+e−z1 |
输出范围 (0,1),可表示概率;但输入绝对值大时会 “梯度消失” |
二分类输出层、早期隐藏层 |
mindspore.nn.Sigmoid() |
|
ReLU |
ReLU(z)=max(0,z) |
解决梯度消失,计算快;但输入为负时神经元会 “死亡”(永久失效) |
隐藏层(最常用) |
mindspore.nn.ReLU() |
|
Softmax |
softmax(zi)=∑j=1kezjezi |
输出是概率分布(和为 1),多分类任务必用 |
多分类输出层 |
mindspore.nn.Softmax(axis=-1) |
小技巧:隐藏层优先用 ReLU,多分类输出层固定用 Softmax;如果 ReLU “死亡” 严重,换成 Leaky ReLU(mindspore.nn.LeakyReLU(alpha=0.1))。
三、模型怎么训练?参数求解的核心逻辑
全连接网络的训练,本质是 “优化权重 w和偏置 b”,让模型预测越来越准。整个过程分两步:定目标、找方法。
3.1 第一步:定优化目标 —— 损失函数
损失函数是 “裁判”,用来衡量模型预测值 y^和真实标签 y的差距。不同任务选不同的损失函数:
(1)回归任务(预测连续值,如房价、温度)用均方误差(MSE),计算预测值和真实值的平方差平均值:
公式:L=N1i=1∑N(yi−y^i)2
MindSpore 调用:mindspore.nn.MSELoss()
(2)分类任务(预测离散类别,如图片分类)
用交叉熵损失,结合 Softmax 把输出转成概率,再算负对数似然(对多分类超友好):
公式:L=−N1i=1∑Nc=1∑kyi,clog(y^i,c)
MindSpore 调用:mindspore.nn.CrossEntropyLoss()(内置 Softmax,输入直接传 logits 就行,不用自己加 Softmax 层!)
3.2 第二步:找优化方法 —— 梯度下降与反向传播
有了损失函数,怎么让损失变小?靠梯度下降:沿损失函数的梯度负方向更新参数(梯度指 “损失增大最快的方向”,负方向就是 “损失减小最快的方向”)。
而反向传播(Backpropagation) 是计算梯度的 “高效工具”—— 不用暴力遍历所有参数,而是用链式法则从输出层往输入层 “倒推”,快速算出每个参数的梯度。
重点:MindSpore 会自动做反向传播!不用你手动推公式,框架通过 “自动微分” 帮你算好梯度,只管调用优化器就行。
四、实战必看:4 个常用优化器(附 MindSpore 用法)
基础梯度下降收敛慢、易震荡,实战中我们用改进后的优化器。这 4 个是业界主流,记好它们的优缺点:
|
优化器 |
核心逻辑 |
优点 |
注意事项 |
MindSpore API |
|
动量(Momentum) |
累积历史梯度的 “动量”,像滚雪球一样加速收敛 |
减少震荡,收敛更快 |
需调 “动量因子”(通常设 0.9) |
nn.Momentum(net.trainable_params(), lr=0.01, momentum=0.9) |
|
Adagrad |
对稀疏特征用小学习率,密集特征用大学习率 |
适合稀疏数据(如 NLP) |
学习率会单调下降,后期可能停滞 |
nn.Adagrad(net.trainable_params(), lr=0.01) |
|
RMSprop |
改进 Adagrad,用 “指数移动平均” 代替累积梯度 |
解决 Adagrad 停滞问题 |
衰减系数默认 0.9,一般不用改 |
nn.RMSprop(net.trainable_params(), lr=0.01, decay=0.9) |
|
Adam |
结合动量(一阶矩)和 RMSprop(二阶矩),自适应学习率 |
收敛快、稳定,几乎万能 |
学习率默认 0.001,大多数场景够用 |
nn.Adam(net.trainable_params(), lr=0.001) |
实战建议:优先用 Adam!除非数据是稀疏的(如文本),再试 Adagrad/RMSprop。
五、避坑关键:正则化解决过拟合
训练时最头疼的问题就是过拟合:模型在训练集上准确率 99%,一到测试集就掉成 60%—— 这是因为模型 “死记” 了训练数据的噪声,没学会通用规律。
5.1 过拟合 vs 欠拟合
先分清两种常见问题:
|
问题 |
表现 |
原因 |
|
过拟合 |
训练集准,测试集差 |
模型太复杂(参数多)、数据少 / 噪声大 |
|
欠拟合 |
训练集、测试集都差 |
模型太简单(层数少)、特征没选好 |
5.2 4 个实战级正则化方法(附 MindSpore 实现)
重点解决过拟合,这 4 个方法简单有效,直接上代码:
(1)权重衰减(L2 正则化)
给损失函数加个 “权重惩罚”,限制权重不能太大。MindSpore 不用改损失函数,直接在优化器里加参数:
# 给Adam优化器加权重衰减(正则化系数1e-4是常用值)
optimizer = nn.Adam(net.trainable_params(), lr=0.001, weight_decay=1e-4)
(2)Dropout:随机 “关掉” 部分神经元
训练时随机让一部分神经元输出为 0(保留概率 0.5~0.8),强迫模型不依赖单个神经元:
# 定义网络时,在隐藏层后加Dropout(保留概率0.5)
class FCNet(nn.Cell):
def __init__(self):
super().__init__()
self.fc1 = nn.Dense(100, 64, activation=nn.ReLU())
self.drop = nn.Dropout(keep_prob=0.5) # 关键:Dropout层
self.fc2 = nn.Dense(64, 10)
def construct(self, x):
x = self.fc1(x)
x = self.drop(x) # 前向传播时经过Dropout
return self.fc2(x)
(3)早停(Early Stopping)
训练时监控验证集损失,一旦连续几轮不下降就停止,避免过度训练:
from mindspore.train.callback import EarlyStopping
# 定义早停:监控验证集准确率,连续3轮不涨就停
early_stop = EarlyStopping(monitor='val_accuracy', patience=3, mode='max')
# 训练时传入早停回调
model.train(epochs=20, train_dataset=train_ds, val_dataset=val_ds, callbacks=[early_stop])
(4)数据增强:给数据 “加戏”
对训练数据做随机变换(如图片旋转、翻转),增加数据多样性,让模型学通用规律:
import mindspore.dataset.vision as vision
# 对图片数据集做增强:随机水平翻转(概率0.5)+随机裁剪(224x224)
train_ds = train_ds.map(
operations=[vision.RandomHorizontalFlip(prob=0.5),vision.RandomCrop(size=(224, 224))],
input_columns=["image"])
六、MindSpore 全流程实战:搭建全连接网络
最后,把前面的知识点串起来,用 MindSpore 搭一个全连接网络,做 10 分类任务(比如 MNIST):
步骤 1:导入库
import mindspore as ms
import mindspore.nn as nn
from mindspore.train import Model, EarlyStopping
from mindspore.dataset import MnistDataset, vision, transforms
步骤 2:加载并预处理数据
# 加载MNIST数据集
train_ds = MnistDataset('train')
val_ds = MnistDataset('val')
# 数据预处理:转Tensor+归一化+展平(MNIST是28x28图片,展平成784维向量)
trans = transforms.Compose([
vision.ToTensor(), # 转成Tensor,像素值归一化到[0,1]
vision.Reshape((-1, 784)) # 展平:(batch_size, 784)])
train_ds = train_ds.map(operations=trans, input_columns=["image"]).batch(32)
val_ds = val_ds.map(operations=trans, input_columns=["image"]).batch(32)
步骤 3:定义全连接网络
class FCNet(nn.Cell):
def __init__(self, in_dim=784, hidden_dim=128, out_dim=10):
super().__init__()
self.fc1 = nn.Dense(in_dim, hidden_dim, activation=nn.ReLU())
self.drop = nn.Dropout(keep_prob=0.7) # Dropout防止过拟合
self.fc2 = nn.Dense(hidden_dim, out_dim) # 输出层:10个类别
def construct(self, x):
# 前向传播
x = self.fc1(x)
x = self.drop(x)
x = self.fc2(x)
return x
# 初始化网络
net = FCNet()
步骤 4:定义损失、优化器和模型
# 损失函数:多分类用交叉熵
loss_fn = nn.CrossEntropyLoss()
# 优化器:Adam+权重衰减optimizer = nn.Adam(net.trainable_params(), lr=0.001, weight_decay=1e-4)
# 早停回调
early_stop = EarlyStopping(monitor='val_accuracy', patience=3, mode='max')
# 封装模型(加准确率评估指标)
model = Model(net, loss_fn=loss_fn, optimizer=optimizer, metrics={"accuracy"})
步骤 5:训练模型
# 训练20轮,用验证集监控,早停防止过拟合
model.train(
epochs=20,
train_dataset=train_ds,
val_dataset=val_ds,
callbacks=[early_stop],
dataset_sink_mode=False # 非下沉模式,方便调试
)
# 评估模型在测试集的效果
acc = model.eval(val_ds)
print(f"验证集准确率:{acc['accuracy']:.4f}")
运行代码,你会看到模型准确率稳步上升,早停会在合适的时候停止训练 —— 这样训练出的模型,泛化能力会更强!
更多推荐


所有评论(0)