1 神经元
神经元是构成神经网络的基本单元,接受一组输入信号并产生输出
1.1 基本机制
接收一个列向量输入,通过下列表达式产生净输出
- 行向量为权重向量
- 为偏置
净输入通过激活函数,得到输入为时该神经元的活性值
图示
1.2 激活函数
1.2.1 为什么需要激活函数?
神经网络属于非线性分类。当输入与权重及偏置经过线性组合之后得到净输出,如果没有激活函数对净输出的处理,那么不管有多少神经元最后得到的输出仍然是线性输出,这也就是为什么感知机无法处理XOR(异或)问题的原因。而经非线性的激活函数的处理之后,神经网络得以解决这一问题。
1.2.2 激活函数的特征
- 连续并可导的非线性函数,连续:每一个净输入则产生对应的活性值;可导:只有有限个点不可导,则可以使用数值优化的方法(如梯度下降)训练网络。
- 激活函数的导数的值域要处于一个合适的区间,因为这将影响训练的效率及稳定性。
1.2.3 两种典型的激活函数
Logistic函数
- 输入越小,越接近于;输入越大,越接近
- 输出值直接可以视作概率分布
ReLU函数
- 当时,导数为,有利于加速梯度下降的收敛
- 一定程度上缓解了神经网络的梯度消失问题
2 网络结构
网络中各个神经元根据接受信息的先后分为不同的组别,每一组视为一个神经层。每一层的神经元接受前一层神经元的输出,并输出到下一层神经元。整个网络的信息从输入端朝输出端传播,不存在反向的信息传播。网络结构可使用一个有向无环图表示,整个网络可以视作一个非线性函数,实现从输入到输出的复杂映射。
2.1 结构组成
共分为三大层,第0层:输入层;最后一层:输出层;其他中间层:隐藏层
- :神经网络的层数
- :第层神经元的个数
- :第层神经元的激活函数
- : 层到第 层的权重矩阵
- : 层神经元的净输入
- :表示 层神经元的输出(活性值)
3 反向传播算法
为了训练参数,最小化损失函数。可采用梯度下降(通过计算损失函数对参数的偏导数,不断迭代使得损失函数最小化)等算法。而梯度下降需要对每一个参数求解偏导,效率低下。故诞生了反向传播算法
3.1 算法思路
- 前馈计算每一层的净输入与输出(活性值)
- 反向传播计算每一层的误差项(表示第层的神经元对损失函数的影响)
- 计算每一层关于参数的偏导数,并更新参数
3.2 数学原理
根据链式求导法则,可令损失函数对权重及偏置的偏导数分别如下
- 其中
由上列表达式可只,想要求损失函数对于第层的神经元的偏导可以通过求解和可一次性求得。
而
即
关于第 层权重 的梯度为
关于第 层权重 的梯度为
其中(代表点积)
4 自动微分
在以计算机中,一种求解导数的精确且方便的计算方式。通过链式法则将复杂的表达式拆分成更简单而精确的形式。
例
求解过程:
- 令
- 求得上述表达式偏导数
- 故
5 优化
在训练神经网络时,有两大问题难以解决:
- 非凸优化(容易陷入局部最优解)
- 梯度消失(参数学习停止)
6 两层ReLU网络的Pytorch实现
6.1 Code
导入Pytorch框架
import torch
import torch.nn as nn
import torch.nn.functional as F
from matplotlib import pyplot as plt
网络实现
class Net(nn.Module):
def __init__(self, n_input, n_hidden, n_output):
super(Net, self).__init__()
# 定义层的形式
self.hidden = torch.nn.Linear(n_input, n_hidden)
self.output = torch.nn.Linear(n_hidden, n_output)
def forward(self, x):
# 隐藏层输出
hidden = F.relu(self.hidden(x))
# 输出层
y_predict = self.output(hidden)
return y_predict
def train(self, x, y, learning_rate, train_num):
# 优化器
optimizer = torch.optim.SGD(self.parameters(), learning_rate)
# 损失函数,均方差
loss_func = torch.nn.MSELoss()
# 迭代训练
for i in range(train_num):
# 预测
y_predict = self(x)
# 计算误差
loss = loss_func(y_predict, y)
# 情况上一次参与更新值
optimizer.zero_grad()
# 计算反向传播
loss.backward()
# 更新参数
optimizer.step()
print(str(i) + "----->" + "loss:" + str(loss.data.numpy()))