前馈神经网络基本原理及两层ReLU网络的Pytorch实现

1 神经元

神经元是构成神经网络的基本单元,接受一组输入信号并产生输出

1.1 基本机制

接收一个列向量输入X=[x_1, x_2,\cdot\cdot\cdot, x_d]^T,通过下列表达式产生净输出z

z =W^TX + b

  • 行向量\omega = [\omega_1,\omega_2, \cdot\cdot\cdot,\omega_d]为权重向量
  • b为偏置

净输入z通过激活函数f(\cdot),得到输入为X时该神经元的活性值a

a = f(z)
图示

神经元的基本结构图示

1.2 激活函数

1.2.1 为什么需要激活函数?

神经网络属于非线性分类。当输入X与权重W及偏置b经过线性组合之后得到净输出z,如果没有激活函数对净输出z的处理,那么不管有多少神经元最后得到的输出仍然是线性输出,这也就是为什么感知机无法处理XOR(异或)问题的原因。而经非线性的激活函数的处理之后,神经网络得以解决这一问题。

1.2.2 激活函数的特征
  • 连续并可导的非线性函数,连续:每一个净输入则产生对应的活性值;可导:只有有限个点不可导,则可以使用数值优化的方法(如梯度下降)训练网络。
  • 激活函数的导数的值域要处于一个合适的区间,因为这将影响训练的效率及稳定性。
1.2.3 两种典型的激活函数

Logistic函数

\sigma(x) = \frac{1}{1+e^{(-x)}}

  • 输入越小,越接近于0;输入越大,越接近1
  • 输出值直接可以视作概率分布

ReLU函数

ReLU(x)=\left\{\begin{array}{l}{x,x \geqslant 0} \\ {0,else}\end{array}\right.

  • x \geqslant 0时,导数为1,有利于加速梯度下降的收敛
  • 一定程度上缓解了神经网络的梯度消失问题

2 网络结构

网络中各个神经元根据接受信息的先后分为不同的组别,每一组视为一个神经层。每一层的神经元接受前一层神经元的输出,并输出到下一层神经元。整个网络的信息从输入端朝输出端传播,不存在反向的信息传播。网络结构可使用一个有向无环图表示,整个网络可以视作一个非线性函数,实现从输入到输出的复杂映射。

2.1 结构组成

共分为三大层,第0层:输入层;最后一层:输出层;其他中间层:隐藏层

一个三层的前馈神经网络
  • L:神经网络的层数
  • m^{(l)}:第l层神经元的个数
  • f_{l}(\cdot):第l层神经元的激活函数
  • W^{(l)} \in \mathbb{R}^{m^{(l)} \times m^{l-1}}l − 1 层到第 l 层的权重矩阵
  • \mathbf{b}^{(l)} \in \mathbb{R}^{m^{l}}l 层神经元的净输入
  • \mathbf{a}^{(l)} \in \mathbb{R}^{m^{l}}:表示 l 层神经元的输出(活性值)

3 反向传播算法

为了训练参数,最小化损失函数\mathcal{L}(\mathbf{y}, \hat{\mathbf{y}})。可采用梯度下降(通过计算损失函数\mathcal{L}(\mathbf{y}, \hat{\mathbf{y}})对参数的偏导数,不断迭代使得损失函数最小化)等算法。而梯度下降需要对每一个参数求解偏导,效率低下。故诞生了反向传播算法

3.1 算法思路

  1. 前馈计算每一层的净输入与输出(活性值)
  2. 反向传播计算每一层的误差项\frac{\partial \mathcal{L}(\mathbf{y}, \hat{\mathbf{y}})}{\partial \mathbf{z}^{(l)}}(表示第l层的神经元对损失函数的影响)
  3. 计算每一层关于参数的偏导数,并更新参数

3.2 数学原理

根据链式求导法则,可令损失函数\mathcal{L}(\mathbf{y}, \hat{\mathbf{y}})对权重及偏置的偏导数分别如下

\frac{\partial \mathcal{L}(\mathbf{y}, \hat{\mathbf{y}})}{\partial W^{(l)}}=\frac{\partial \mathbf{z}^{(l)}}{\partial W^{(l)}} \delta^{(l)}
\frac{\partial \mathcal{L}(\mathbf{y}, \hat{\mathbf{y}})}{\partial \mathbf{b}^{(l)}}=\frac{\partial \mathbf{z}^{(l)}}{\partial \mathbf{b}^{(l)}} \delta^{(l)}

  • 其中\frac{\partial \mathcal{L}(\mathbf{y}, \hat{\mathbf{y}})}{\partial \mathbf{b}^{(l)}}=\delta^{(l)}

由上列表达式可只,想要求损失函数对于第l层的神经元的偏导可以通过求解\frac{\partial \mathbf{z}^{(l)}}{\partial W^{(l)}}, \frac{\partial \mathbf{z}^{(l)}}{\partial \mathbf{b}^{(l)}}\delta^{(l)}可一次性求得。

  • \frac{\partial \mathbf{z}^{(l)}}{\partial W^{(l)}} = \left(\mathbf{a}^{(l-1)}\right)^{\mathrm{T}}
  • \frac{\partial \mathbf{z}^{(l)}}{\partial \mathbf{b}^{(l)}}=\mathbf{I}_{m^{(l)}} \in \mathbb{R}^{m^{(l)} \times m^{(l)}}
  • \delta^{(l)}=f_{l}^{\prime}\left(\mathbf{z}^{(l)}\right) \odot\left(\left(W^{(l+1)}\right)^{\mathrm{T}} \delta^{(l+1)}\right)

\mathcal{L}(\mathbf{y}, \hat{\mathbf{y}})关于第 l 层权重 W^{(l)}的梯度为
\frac{\partial \mathcal{L}(\mathbf{y}, \hat{\mathbf{y}})}{\partial W^{(l)}}=\delta^{(l)}\left(\mathbf{a}^{(l-1)}\right)^{\mathrm{T}}
\mathcal{L}(\mathbf{y}, \hat{\mathbf{y}})关于第 l 层权重 \mathbf{b}^{(l)}的梯度为\frac{\partial \mathcal{L}(\mathbf{y}, \hat{\mathbf{y}})}{\partial \mathbf{b}^{(l)}}=\delta^{(l)}
其中\delta^{(l)}=f_{l}^{\prime}\left(\mathbf{z}^{(l)}\right) \odot\left(\left(W^{(l+1)}\right)^{\mathrm{T}} \delta^{(l+1)}\right)\odot代表点积)


4 自动微分

在以计算机中,一种求解导数的精确且方便的计算方式。通过链式法则将复杂的表达式拆分成更简单而精确的形式。

f(x;\omega,b) = \frac{1}{e^{-(\omega x+b)}+1}
求解过程:

  • h_{1}=x \times w
  • h_{2}=h_{1}+b
  • h_{3}=h_{2} \times-1
  • h_{4}=e^ \left(h_{3}\right)
  • h_{5}=h_{4}+1
  • h_{6}=1 / h_{5}
  1. 求得上述表达式偏导数
  • \frac{\partial h_{1}}{\partial w}=x \qquad \frac{\partial h_{1}}{\partial x}=w
  • \frac{\partial h_{2}}{\partial h_{1}}=1 \qquad \frac{\partial h_{2}}{\partial b}=1
  • \frac{\partial h_{3}}{\partial h_{2}}=-1
  • \frac{\partial h_{4}}{\partial h_{3}}=e^ \left(h_{3}\right)
  • \frac{\partial h_{5}}{\partial h_{4}}=1
  • \frac{\partial h_{6}}{\partial h_{5}}=-\frac{1}{h_{5}^{2}}

\begin{aligned} \frac{\partial f(x ; w, b)}{\partial w} &=\frac{\partial f(x ; w, b)}{\partial h_{6}} \frac{\partial h_{6}}{\partial h_{5}} \frac{\partial h_{5}}{\partial h_{4}} \frac{\partial h_{4}}{\partial h_{3}} \frac{\partial h_{3}}{\partial h_{2}} \frac{\partial h_{2}}{\partial h_{1}} \frac{\partial h_{1}}{\partial w} \\ \frac{\partial f(x ; w, b)}{\partial b} &=\frac{\partial f(x ; w, b)}{\partial h_{6}} \frac{\partial h_{6}}{\partial h_{5}} \frac{\partial h_{5}}{\partial h_{4}} \frac{\partial h_{4}}{\partial h_{3}} \frac{\partial h_{3}}{\partial h_{2}} \frac{\partial h_{2}}{\partial b} \end{aligned}


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()))

6.2 拟合曲线y=\sin x效果(学习率:0.1)

训练数据拟合
测试数据拟合
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容