02_神经网络基础
1 神经网络的构成
1 | |
人工神经网络(Artificial Neural Network,ANN)简称神经网络(NN),是一种模仿生物神经网络结构和功能的计算模型,由多个神经元构成。
每个神经元类似机器学习中的感知机,每个输入有不同的权重,加权求和经过激活函数得到输出。

使用多个神经元构成网络,相邻层之间相互连接,每层之间通过激活函数进行非线性映射,最终得到结果。同一层的多个神经元可以看作是并行计算处理相同的输入数据,学习输入数据的不同特征。每个神经元可能会关注输入数据中的不同部分,从而捕捉到数据的不同属性。类似集成学习的思想,多个神经元组合在一起,可以处理更复杂的数据。
每一层神经网络(全连接层)本质都是一次线性变换加一次非线性映射,类似逻辑回归+激活函数。神经网络正是靠这种“线性变换 + 非线性映射”的重复堆叠,才能从简单的线性模型扩展成可以逼近任意函数的复杂结构。

- 输入层(Input Layer): 每个输入特征对应一个神经元
- 输出层(Output Layer): 输出层的神经元根据网络的任务(回归、分类等)生成最终的预测结果
- 隐藏层(Hidden Layers): 输入层和输出层之间都是隐藏层,神经网络的“深度”通常由隐藏层的数量决定
相邻层的神经元相互连接,称为全连接(Fully Connected),全连接神经网络接收的样本数据是二维的,数据在每一层之间需要以二维的形式传递。每个连接都会有一个权重,神经元的信息逐层传递称为前向传播(forward),上一层的输出作为下一层的输入。

每个神经元在前向传播时保存内部状态值(加权求和值)和激活值,在反向传播时计算并保存的激活值梯度和内部状态值梯度。
- 前向传播
- 内部状态值:𝑧 = 𝑤 ⋅ 𝑥 + 𝑏
- 激活值:𝑎 = 𝑓(𝑧)
- 反向传播
- 激活值梯度:损失函数对激活值的偏导 $\frac{\partial L}{\partial a}$
- 内部状态值梯度:激活值梯度 × 激活函数导数 $\frac{\partial L}{\partial z}=\frac{\partial L}{\partial a} · f^{\prime}(z)$
2 激活函数
如果没有激活函数,无论加入多少隐藏层,整个神经网络都等效于单层线性变换。激活函数为神经网络引入了非线性,使得神经网络能够学习和表示复杂的非线性关系。
2.1 阶跃函数
阶跃函数是在感知机中最简单的激活函数,可以为输入设置一个阈值;一旦超过这个阈值,就切换输出(0 或者 1)。阶跃函数在0点不可导,并且在其他点导数为0,因此无法进行梯度下降。
$$f(x)=\left\{ \begin{array} {c}0,x<0 \\ 1, x\geq0 \end{array}\right. ,\quad f^{\prime}(x)=0$$
1 | |
[<matplotlib.lines.Line2D at 0x176df26e0>]
2.2 Sigmoid函数
Sigmoid(也叫 Logistic 函数)是平滑可微的,能将任意输入映射到区间(0,1)。因其涉及指数运算,计算量相对较高。
$$f(x)=\frac{1}{1+e^{-x} } ,\quad f^{\prime}(x)=\frac{1}{1+e^{-x} }\left(1-\frac{1}{1+e^{-x} }\right)=f(x)\left(1-f(x)\right)$$
Sigmoid 的导数范围是(0, 0.25],梯度较小,并且输入在 [-6, 6] 之外的输出值变化很小,此时网络参数更新极其缓慢,甚至无法更新。
Sigmoid 一般在 5 层之内就会出现梯度消失的情况,并且由于该函数不是以0为中心的,激活值总是正数,在梯度更新时只对某些特征产生相同方向的影响。一般只用在二分类的输出层或者层数较少的神经网络。
Sigmoid 在二分类的输出层,输出为 1 个节点,直接表示样本属于类别 1 的概率。
1 | |
<matplotlib.lines.Line2D at 0x177a2ccd0>
2.3 Tanh函数
上面说到 Sigmoid 不以 0 值为中心,并且导数值比较小,Tanh 就是对 Sigmoid 的改进,进行缩放和偏移,使其以 0 值为中心,并且导数值接近 1。
$$f(x)=\frac{2}{1+e^{-2x} }-1=\frac{2}{1+e^{-2x} }+\frac{-1-e^{-2x} }{1+e^{-2x} }=\frac{1-e^{-2x} }{1+e^{-2x} }$$
$$f^{\prime}(x)=1-\left(\frac{1-e^{-2x} }{1+e^{-2x} }\right)^{2}=1-f^{2}(x)$$
Tanh 在 [-3, 3] 之外的输出值变化也很小,与 Sigmoid 相比,Tanh 以 0 为中心,并且梯度值更大,所以收敛速度更快,但同样存在梯度消失的问题。可以在隐藏层中使用 Tanh,在输出层中使用 Sigmoid。
$\frac{2}{1+e^{-2x} }-1$ 是 Sigmoid 的变形,2 倍的 Sigmoid 将 y 轴 放大 1 倍, 2x 将 x 轴缩放 1倍,再 -1 将 y 轴平移到 (-1, 1) 区间内。
1 | |
<matplotlib.lines.Line2D at 0x177a99150>
2.4 ReLU函数
ReLU(Rectified Linear Unit,修正线性单元)会将小于等于 0 的输入转换为 0,大于 0 的输入则保持不变。ReLu 更加重视正信号,而忽略负信号。
$$ f ( x )=\left\{ {\begin{array} {l} {0, x \leq0} \\ {x, x > 0} \end{array} } ,\quad \right. f^{\prime} ( x )=\left\{ {\begin{array} {l} {0, x \leq0} \\ {1, x > 0} \end{array} } \right. $$
ReLU 在大于 0 时,导数一直为 1,不存在梯度消失的问题。但在输入小于 0 时,输出为 0,ReLu 只会激活部分神经元,这种稀疏性有助于减少计算量提高效率,并且可以缓解过拟合的发生。但如果部分输入一直小于0,输出一直为0,那么就会导致另一个问题,即神经元死亡,如果大量的神经元死亡,会影响模型的学习能力。
ReLu 计算简单,不存在梯度消失的问题,并且可以使网络稀疏,常用于隐藏层,适用于深层神经网络。
1 | |
<matplotlib.lines.Line2D at 0x177c517b0>
2.5 Leaky ReLU函数
为了解决 ReLu 的神经元死亡问题,引入了 Leaky ReLu 激活函数,在输入为负的时候引入一个很小的斜率,从而避免神经元死亡。
$$ f ( x )=\left\{ {\begin{array} {l} {\alpha x, x \leq0} \\ {x, x > 0} \end{array} },\quad \right. f^{\prime} ( x )=\left\{ {\begin{array} {l} {\alpha, x \leq0} \\ {1, x > 0} \end{array} } \right. $$
1 | |
<matplotlib.lines.Line2D at 0x177d371c0>
2.6 Softmax函数
Softmax 将一个任意的实数向量转换为一个概率分布,确保输出值的总和为 1,是二分类激活函数 Sigmoid 在多分类上的推广。常用于多分类问题的输出层,用来表示类别的输出概率。
$$ y_{k}={\frac{e^{x_{k} } } {\sum_{i=1}^{n} e^{x_{i} } } }, \quad k=1 {\sim} n \qquad\qquad \frac{\partial y_{k} } {\partial x_{i} }=\left\{\begin{matrix} {y_{k} ( 1-y_{i} ), k=i} \\ {-y_{k} y_{i}, k \neq i} \\ \end{matrix} \right. $$
Softmax 会放大输入中较大的值,使得最大输入值对应的输出概率较大,其他较小的值会被压缩。
1 | |
1 2 3 4 5
[['1.17%' '3.17%' '8.61%' '23.41%' '63.64%']
['0.62%' '92.46%' '0.62%' '1.69%' '4.6%']]
2.7 其他激活函数
2.7.1 Identity恒等函数
Identity 函数非常简单,它返回输入值本身,适用于回归任务的输出。
f(x) = x, f′(x) = 1
1 | |
Text(0.5, 1.0, 'Identity')
2.7.2 PReLU函数
PReLU(Parametric Rectified Linear Unit)中的 α 是一个可训练的参数,而不是固定常数。
$$ f ( x )=\left\{ {\begin{array} {l} {\alpha x, x \leq0} \\ {x, x > 0} \end{array} },\quad \right. f^{\prime} ( x )=\left\{ {\begin{array} {l} {\alpha, x \leq0} \\ {1, x > 0} \end{array} } \right. $$
2.7.3 RReLU函数
RReLU(Randomized Leaky ReLU)中的 α 是从均匀分布中随机选择的一个值,范围是 [0, 1]。
$$ f ( x )=\left\{ {\begin{array} {l} {\alpha x, x \leq0} \\ {x, x > 0} \end{array} },\quad \right. f^{\prime} ( x )=\left\{ {\begin{array} {l} {\alpha, x \leq0} \\ {1, x > 0} \end{array} } \right. $$
1 | |
Text(0.5, 1.0, 'RReLU')
2.7.4 ELU函数
ELU(Exponential Linear Unit)是在负轴上更加平滑的ReLU函数。
$$ f ( x )=\left\{\begin{array} {c} { {\alpha( e^{x}-1 ), x \leq0} } \\ { {x, x > 0} } \end{array} \right.,\quad \ f^{\prime} ( x )=\left\{\begin{array} {c} { {\alpha e^{x}, x \leq0} } \\ { {1, x > 0} } \end{array} \right. $$
1 | |
Text(0.5, 1.0, 'ELU')
2.7.5 Swish函数
Swish,也称Sigmoid Linear Unit,SiLU函数。
$$ f ( x )=\frac{x} {1+e^{-x} } ,\quad f^{\prime( x )}=\frac{1+e^{-x}+x e^{-x} } {( 1+e^{-x} )^{2} } $$
1 | |
Text(0.5, 1.0, 'Swish')
2.7.6 Softplus函数
$$ f ( x )=\ln( 1+e^{x} ) ,\quad f^{\prime( x )}=\frac{1} {1+e^{-x} } $$
1 | |
Text(0.5, 1.0, 'Softplus')
2.8 激活函数的选择
- 隐藏层
- 首选 ReLU 函数,如果效果不好可尝试 Leaky ReLU 等函数
- Tanh 的输出均值为 0,对中心化数据更友好,但可能出现梯度消失,仅适用于浅层网络
- Sigmoid 在隐藏层容易导致梯度消失,仅适用于浅层网络
- 输出层
- 二分类选择 Sigmoid
- 多分类选择 Softmax
- 回归选择 Identity
2.9 API 实现
Sigmoid
1 | |
Tanh
1 | |
tensor([[ 0.9497, -0.2163, 0.3297],
[-0.9473, 0.1161, 0.8130]])
ReLU
1 | |
tensor([[1.8289, 0.0000, 0.3424],
[0.0000, 0.1166, 1.1357]])
Leaky ReLU
1 | |
tensor([[ 1.8289, -0.0220, 0.3424],
[-0.1805, 0.1166, 1.1357]])
Softmax
1 | |
tensor([[0.7380, 0.0951, 0.1669],
[0.0374, 0.2553, 0.7073]])
其他激活函数在 nn 中也有对应模块,不再一一演示。
3 神经网络简单实现
深度神经网络由多个层(layer)组成,通常将其称之为模型(Model)。整个模型接收原始输入(特征),生成输出(预测),并包含一些参数。下图是一个三层神经网络,权重和神经元的上标 (1) 表示网络层号,下标 21 分别代表输入神经元编号和输出神经元编号。
输入层->第1层,使用矩阵乘法表示:
A(1) = XW(1) + B(1)
其中
$$ A^{( 1 )}=( a_{1}^{( 1 )} \; a_{2}^{( 1 )} \; a_{3}^{( 1 )}), \quad X=( x_{1} \, \, x_{2} ), \quad B^{( 1 )}=( b_{1}^{( 1 )} \, \, \, b_{2}^{( 1 )} \, \, \, b_{3}^{( 1 )}), \quad W^{( 1 )}=\left( \begin{matrix} { {w_{1 1}^{( 1 )} } } & { {w_{1 2}^{( 1 )} } } & { {w_{1 3}^{( 1 )} } } \\ { {w_{2 1}^{( 1 )} } } & { {w_{2 2}^{( 1 )} } } & { {w_{2 3}^{( 1 )} } } \end{matrix} \right) $$
计算得到的 A(1) 再经过激活函数 h() 得到 Z(1),继续作为第1层->第2层的输入,以此类推。

1 | |
[0.88363004 0.34900234]
4 参数初始化
神经网络中的参数是需要初始化,参数初始化的作用:
- 防止梯度消失或爆炸:初始权重值过大或过小会导致梯度在反向传播中指数级增大或缩小。
- 提高收敛速度:合理的初始化使得网络的激活值分布适中,有助于梯度高效更新。
- 保持对称性破除:权重的初始化需要打破对称性,否则网络的学习能力会受到限制。(对称性指权重的值相同,神经元的功能没有差异)
4.1 常数初始化
无法打破对称性,反向传播时全部都会进行相同的更新,使得网络失去了不同神经元的意义,无法有效训练。通常只会使用全 0 初始化偏置。
1 | |
Parameter containing:
tensor([[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.]], requires_grad=True)
Parameter containing:
tensor([[1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1.]], requires_grad=True)
Parameter containing:
tensor([[66., 66., 66., 66., 66.],
[66., 66., 66., 66., 66.]], requires_grad=True)
4.2 秩初始化
权重参数初始化为单位矩阵。
1 | |
Parameter containing:
tensor([[1., 0., 0., 0., 0.],
[0., 1., 0., 0., 0.]], requires_grad=True)
4.3 随机初始化
均匀分布初始化:从区间中均匀随机取值,默认区间为(0,1),可以设为 $(-,) $,其中 d 为神经元的输入数量。
1 | |
Parameter containing:
tensor([[0.4520, 0.7268, 0.0245, 0.7780, 0.5695],
[0.9082, 0.2932, 0.1449, 0.6995, 0.1598]], requires_grad=True)
正态分布初始化:从均值为 0,标准差为 1 的高斯分布中取样,使用一些很小的值进行初始化。
1 | |
Parameter containing:
tensor([[ 1.0646, 0.6571, -1.7206, -0.5381, -1.1052],
[-0.4689, 1.1999, -1.7586, 0.9557, -0.8140]], requires_grad=True)
4.4 Xavier初始化
Xavier 初始化,也叫 Glorot 初始化,设计思想是让每层的输入方差和输出方差一致,保持信号在每一层传播时不衰减、不放大。常用于 Sigmoid 和 Tanh 激活函数,不推荐用于 ReLU 激活函数。
Xavier 均匀分布初始化:$\left[-{\sqrt{\frac{6} {n_{i n}+n_{o u t} } } }, {\sqrt{\frac{6} {n_{i n}+n_{o u t} } } } \right]$
Xavier 正态分布初始化:均值为 0,均值为 ${\sqrt{\frac{2} {n_{i n}+n_{o u t} } } }$
其中 nin 表示输入神经元的数量,也就是上一层神经元的数量,nout 是输出神经元的数量,也就是下一层神经元的数量。
1 | |
Parameter containing:
tensor([[ 0.2802, 0.8988, -0.1018, 0.4321, -0.4568],
[-0.2845, 0.3657, -0.7230, -0.2094, 0.9017]], requires_grad=True)
Parameter containing:
tensor([[ 0.2160, -0.9007, -0.3396, -0.4396, 0.2919],
[-0.0365, -0.2839, -0.6361, -0.3293, 0.3296]], requires_grad=True)
4.5 Kaiming初始化
Kaiming 初始化,也叫 He 初始化,考虑到 ReLU 会把一半的神经元输出设为 0,因此需要更大的方差来维持信号幅度。常用于 ReLu 和 Leaky ReLU,不推荐用于 Sigmoid。
Kaiming 均匀分布初始化:$\left[-{\sqrt{\frac{6} {n_{i n} } } }, {\sqrt{\frac{6} {n_{i n} } } } \right]$
Kaiming 正态分布初始化:均值为 0,均值为 ${\sqrt{\frac{2} {n_{i n} } } }$
其中 nin 表示输入神经元的数量,也就是上一层神经元的数量。
1 | |
Parameter containing:
tensor([[ 0.7357, -0.4194, 0.6922, -0.8139, -1.0905],
[-0.2551, -0.3574, 0.3759, 0.9959, 0.1350]], requires_grad=True)
Parameter containing:
tensor([[ 0.3897, 0.4593, 1.3781, -0.0915, 0.3848],
[ 0.3878, -0.3613, 0.7376, -0.3496, -0.1828]], requires_grad=True)
5 搭建神经网络
5.1 自定义网络
在神经网络框架中,由多个层组成的组件称之为模块(Module),在 PyTorch
中 Module 是所有神经网络的基类。定义 Module
时需要继承nn.Module,并主要重写两个方法:
__init__:定义网络中的层结构,并视情况进行初始化。forward:根据输入进行前向传播,并返回输出。在调用神经网络模型对象的时候,底层会自动调用该函数。
下面神经网络设计如下:
- 输入层:输入层有4个节点,对应输入数据的4个特征
- 第1隐藏层:有3个节点,使用 Xavier 正态分布初始化权重,激活函数使用 Tanh
- 第2隐藏层:有2个节点,使用 Kaiming 正态分布初始化权重,激活函数使用 ReLU
- 输出层:有 3 个节点,按默认方式(PyTorch默认均匀随机分布)进行初始化,激活函数使用 Softmax
1 | |
预测值:
tensor([[0.2632, 0.3978, 0.3391],
[0.2272, 0.3991, 0.3737],
[0.2632, 0.3978, 0.3391],
[0.2632, 0.3978, 0.3391],
[0.2632, 0.3978, 0.3391],
[0.2342, 0.3991, 0.3667]], grad_fn=<SoftmaxBackward0>)
预测标签:
[1 1 1 1 1 1]
5.2 查看模型参数
使用 named_parameters()
查看各层参数,通常迭代和访问所有可训练参数(requires_grad=True)供梯度更新使用。
1 | |
fc1.weight Parameter containing:
tensor([[-1.0289, -0.0880, 0.8888, -0.6779],
[-0.3527, -0.1030, -0.1152, -0.0459],
[-0.2099, -0.1153, 0.2160, -0.9007]], requires_grad=True)
fc1.bias Parameter containing:
tensor([-0.0480, 0.2268, -0.4755], requires_grad=True)
fc2.weight Parameter containing:
tensor([[-0.5187, -0.6714, 0.4459],
[-0.0557, -0.4336, -0.9717]], requires_grad=True)
fc2.bias Parameter containing:
tensor([-0.3928, -0.5646], requires_grad=True)
out.weight Parameter containing:
tensor([[-0.1984, -0.6112],
[-0.5136, -0.2566],
[-0.0660, -0.0348]], requires_grad=True)
out.bias Parameter containing:
tensor([0.0401, 0.4532, 0.2935], requires_grad=True)
使用 state_dict
查看各层参数,返回一个字典,常用于存储和加载模型的所有状态,保存检查点和部署。
1 | |
OrderedDict([('fc1.weight',
tensor([[-1.0289, -0.0880, 0.8888, -0.6779],
[-0.3527, -0.1030, -0.1152, -0.0459],
[-0.2099, -0.1153, 0.2160, -0.9007]])),
('fc1.bias', tensor([-0.0480, 0.2268, -0.4755])),
('fc2.weight',
tensor([[-0.5187, -0.6714, 0.4459],
[-0.0557, -0.4336, -0.9717]])),
('fc2.bias', tensor([-0.3928, -0.5646])),
('out.weight',
tensor([[-0.1984, -0.6112],
[-0.5136, -0.2566],
[-0.0660, -0.0348]])),
('out.bias', tensor([0.0401, 0.4532, 0.2935]))])
1 | |
<All keys matched successfully>
5.3 模型参数可视化
summary
是一个模型结构可视化工具,它可以清晰地模型每一层的:层名称(Layer
Type)、输出张量形状(Output
Shape)、参数数量(Params)、以及总参数量统计。需要使用
pip install torchsummary 安装。
summary 会自动构造一个虚拟输入张量,传入模型做一次
forward,然后记录每一层的输入/输出形状、参数量、占用内存等。
1 | |
----------------------------------------------------------------
Layer (type) Output Shape Param #
================================================================
Linear-1 [16, 3] 15
Tanh-2 [16, 3] 0
Linear-3 [16, 2] 8
Linear-4 [16, 3] 9
================================================================
Total params: 32
Trainable params: 32
Non-trainable params: 0
----------------------------------------------------------------
Input size (MB): 0.00
Forward/backward pass size (MB): 0.00
Params size (MB): 0.00
Estimated Total Size (MB): 0.00
----------------------------------------------------------------
另外也可以使用torchinfo.summary(),只需要model和input_size作为参数传入即可,更加方便。使用pip install torchinfo安装。
1 | |
==========================================================================================
Layer (type:depth-idx) Output Shape Param #
==========================================================================================
Model [16, 3] --
├─Linear: 1-1 [16, 3] 15
│ └─weight ├─12
│ └─bias └─3
├─Tanh: 1-2 [16, 3] --
├─Linear: 1-3 [16, 2] 8
│ └─weight ├─6
│ └─bias └─2
├─Linear: 1-4 [16, 3] 9
│ └─weight ├─6
│ └─bias └─3
==========================================================================================
Total params: 32
Trainable params: 32
Non-trainable params: 0
Total mult-adds (Units.MEGABYTES): 0.00
==========================================================================================
Input size (MB): 0.00
Forward/backward pass size (MB): 0.00
Params size (MB): 0.00
Estimated Total Size (MB): 0.00
==========================================================================================
==========================================================================================
Layer (type:depth-idx) Output Shape Param #
==========================================================================================
Model [16, 3] --
├─Linear: 1-1 [16, 3] 15
│ └─weight ├─12
│ └─bias └─3
├─Tanh: 1-2 [16, 3] --
├─Linear: 1-3 [16, 2] 8
│ └─weight ├─6
│ └─bias └─2
├─Linear: 1-4 [16, 3] 9
│ └─weight ├─6
│ └─bias └─3
==========================================================================================
Total params: 32
Trainable params: 32
Non-trainable params: 0
Total mult-adds (Units.MEGABYTES): 0.00
==========================================================================================
Input size (MB): 0.00
Forward/backward pass size (MB): 0.00
Params size (MB): 0.00
Estimated Total Size (MB): 0.00
==========================================================================================
5.4 顺序网络
使用 nn.Sequential
构建的网络,各层按照添加的顺序逐层连接,其中每一层的输出直接作为下一层的输入。构建非常简单,但限制了网络的复杂性。
1 | |
tensor([[0.5799, 0.2505, 0.1696],
[0.4363, 0.3862, 0.1775]], grad_fn=<SoftmaxBackward0>)
6 神经网络的本质
在神经网络中,每一层都可以看作是对输入特征的抽象与变换。
假设有 10 个样本,每个样本有 5 个特征,即输入矩阵的形状是 (10, 5)。
- 第一隐藏层有 10 个神经元,对应权重矩阵的形状是 (5, 10)。
输入与权重相乘后得到输出矩阵 (10, 10),也就是模型将原来的 5 维特征抽象成了新的 10 维特征表示。
- 第二隐藏层有 4 个神经元,权重矩阵为 (10, 4)。
上一层输出 (10, 10) 乘以 (10, 4),得到新的表示 (10, 4),相当于模型进一步提炼出了 4 维抽象特征。
- 输出层有 3 个神经元,对应权重矩阵 (4, 3)。
(10, 4) @ (4, 3) = (10, 3),表示模型为每个样本生成了 3 个输出值,对应 3 个类别的未归一化得分(logits)。
- 最后通过 softmax 激活函数,得到 10 行 10 个样本,每一行的 3 个得分转化为概率分布,即每个样本属于 3 个类别的概率,形状仍是 (10, 3)。
可以看出,全连接层中的矩阵乘法会消掉连接的维度,保留输入层的批次维度或样本大小,以及输出层的神经元节点数,最终的输出形状为:
最终输出形状 = 批次大小 or 样本大小 × 输出节点数