Linuxword Global
当前位置: Linux技术 > DNN-全连接神经网络

全连接神经网络模型是一种多层感知机(MLP),感知机的原理是寻找类别间最合理、最具有鲁棒性的超平面,最具代表的感知机是SVM支持向量机算法。神经网络同时借鉴了感知机和仿生学,通常来说,动物神经接受一个信号后会发送各个神经元,各个神经元接受输入后根据自身判断,激活产生输出信号后汇总从而实现对信息源实现识别、分类,一个典型的神经网络如下图所示:

1614062703682077014

上图是典型的全连接神经网络模型(DNN),有的场合也称作深度神经网络,与传统的感知机不同,每个结点和下一层所有结点都有运算关系,这就是名称中‘全连接’的含义,上图的中间层也成为隐藏层,全连接神经网络通常有多个隐藏层,增加隐藏层可以更好分离数据的特征,但过多的隐藏层也会增加训练时间以及产生过拟合。

    全连接神经网络与感知机一样,仍然是利用超平面提取样本数据特征,在SVM和逻辑回归曾介绍过,感知机通过核函数将样本数据升维后可实现线性可分。观察上图,输入数据是一个3维向量,隐藏层有5个结点,意味着通过线性映射将3维向量映射为一个5维向量,最后再变为一个2维向量输出。当原输入数据是线性不可分时,全连接神经网络是通过激活函数产生出非线性输出,常见的激活函数有Sigmoid,Tanh,Relu,分别如下图所示:

1614067370631030940

全连接神经网络训练分为前向传播、后向传播两个过程,前向传播数据沿输入到输出后计算损失函数值,后向传播则是一个优化过程,利用梯度下降法减小前向传播产生的损失函数值,从而优化、更新参数,接下来详细介绍这两个过程。

一、前向传播(FP)

    以一个线性回归问题为例,下图是蓝色点代表样板点,样本点大致符合线性方程y=2.3x +4.7走势,红色线标注出目标直线方程。

1614148827096020670

选择的全连接神经网络输入维度是1维,两个隐藏层,其中第一个隐藏层有2个结点,第二个隐藏层含5个结点,输出维度为1维,效果图如下:

1614155115030012359

本例是线性回归问题,选用的损失函数是平方误差函数,如果全连接神经网络用于分类,一般选择交叉熵作为损失函数。上图的神经网络经过第一次隐藏层后,一维变量转换为2维,再经过第二层隐藏层后由2维变为5维向量,如果没有激活函数引入,这两个过程是简单的线性映射过程,以结点1、2传递到结点3过程为例,用矩阵可表示该过程:

1614156057926025753

通常将x1w13+x2w23称为结点3的输入,用net3表示, net3再经过激活函数处理后变为结点3的输出,不妨用out3表示结点3的输出:

out3=σ(net3)

σ表示激活函数,本例中使用Sigmoid函数作为激活函数,在逻辑回归一篇中曾介绍过Sigmoid函数,设y=σ(x),其导数为:

σ'(x)=σ(x)(1-σ(x))=y(1-y)

激活函数可使结点产生非线性输出进而拟合复杂的曲线特征,将输入和输出结合起来,上图中结点3放大后可用下图来表示:

1614171023653082013

结点中∑代表输入,σ表示激活函数输出,有两点需要注意:

1、目前神经网络训练中已经很少使用Sigmoid函数,Sigmoid函数容易过早的梯度为0,当神经网络层数、结点增多时,这种特性易造成梯度消失,实践中大多采用Relu函数,Relu函数表示为:

Relu(x)=max{0,x}

当输入值x大于0时,Relu函数梯度始终为1。另外观察激活函数可以发现:当输入值出了某个范围之后梯度(函数斜率)变化不显著或者急剧上升,也就是产生了梯度爆炸或消失,一般将数据输入到激活函数前通过Batch Normalize做归一化处理,可有效的解决梯度爆炸或消失。

2、当网络层数变多时训练结果正确率反而下降,这可通过ResNet(残差网络)将部分层跳跃直连。

3、当结点多时,全连接神经网络很容易过拟合,Alex、Hinton在其论文《ImageNet Classification with Deep Convolutional Neural Networks》中用到了Dropout算法,用于防止接神经网络过拟合。Dropout事先设定一个概率,如设定概率为0.5,那么每个结点在前向传播时会有50%被激活,也有50%被关闭进而停止工作,Dropout本质是减少结点间相互依赖,利用取平均值的方法在训练过程中产生出一个投票策略,Dropout效果如下图:

1614172314664048049

利用pytorch演示全连接神经网络实现线性回归的过程:

import numpy as np
import matplotlib.pyplot as plt
import torch
from torch import nn, optim
BATCH_SIZE= 20
learning_rate = 0.02
#保存模型
savePath='model/regressmodel.pkl'
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus'] = False
#继承nn.Module类,自定义DNN
class Activation_Net(nn.Module):
    def __init__(self, in_dim, n_hidden_1,  n_hidden_2,  out_dim):
        super(Activation_Net, self).__init__()
        self.layer1 = nn.Sequential( nn.Linear(in_dim, n_hidden_1  ),    nn.Sigmoid())
        self.layer2= nn.Sequential(nn.Linear(n_hidden_1, n_hidden_2),   nn.Sigmoid())
        self.layer3= nn.Sequential( nn.Linear(n_hidden_2, out_dim  ) )

    def forward(self, x):
        x = self.layer1(x)
        x = self.layer2(x)
        x = self.layer3(x)
        return x

def loadsamples(num):
    x =torch.linspace(0,3,num)
    y=2.3*x +4.7  +torch.randn(1,num)
    y_=2.3*x +4.7
    return x,y,y_

def train():
    x, y, y_=loadsamples(100)
    model = Activation_Net(1,2, 5,  1)
    model.train()
    criterion = nn.MSELoss()
    optimizer = optim.Adam(model.parameters(), lr=learning_rate)
    epoch = 0
    iternum = 200
    for i in range(iternum):
        for j in range(x.shape[0]):
            out = model(torch.tensor([x[j].item()]))
            loss = criterion(out, y_[j])
            print_loss = loss.data.item()
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
            epoch += 1
            if epoch % 100 == 0:
                print('迭代轮: {}, 错误率: {:.4}'.format(epoch, print_loss), end='\r', flush=True)
    torch.save(model, savePath)


def test():
    x, y, y_ = loadsamples(100)
    model = torch.load(savePath)
    #测试时切换到到eval模式,取消Dropout激活功能,本例没有使用Dropout
    model.eval()
    x_=torch.from_numpy( np.expand_dims(x.detach().numpy(),1)    )
    predict = model(x_ )
    p1=plt.scatter(x,y,c='cornflowerblue')
    p2=plt.plot(x, y_, c='crimson',lw=2)
    p3=plt.plot(x, predict.detach().numpy(), c='gold',lw=2)
    plt.legend(['实际函数','神经网络', '样本']  )
    plt.show()

if __name__=='__main__':
     train()
     #测试模型
     test()

请先安装pytorch库,另外在程序目录下新建model目录用于保存模型,测试效果图如下:

1614172931890020087

黄色线代表神经网络训练出的模型,由于加入了激活函数,所以该模型具有非线性特征。

二、反向传播(BP)

    反向传播根据前向传播产生的损失函数值,沿输出端向至输入端优化每层之间参数,在此过程中运算利用梯度下降法优化参数,神经网络求解参数本质上仍然是规划中求最优解问题,现代机器学习框架如Tensorflow、pytorch、keras将梯度下降法、Booting、Bagging这些优化中常用技巧封装起来,开发者只要专注于数据建模即可。

1614155115030012359

2.1 输出层权重更新    

以之前代码为例,结点8为输出端,代码:

 self.layer3= nn.Sequential( nn.Linear(n_hidden_2, out_dim  ) )

说明结点8输出时并未使用激活函数,结点8的输入等于输出

y=net8=out8

1614215790144058807

利用链式法则,第二个隐藏层到输出结点中权重梯度为:

1614216656814089113

结合本例,此时wij的下标范围i=3,4,5,6,7 ; j=8。求损失函数最小值,则每个权重取梯度反方向则可获得优化:

1614216272981042434           ⑴

公式(1)中α称为学习率,在一维搜索中曾详细介绍过,α也称为步长系数。为了后期推导方便引入输入误差概念,如输出端即结点⑻的输入误差为:

1614237583351019667                       ②

每层输入误差是对输入到该层没经过激活函数前向量求导,利用输入误差可以统一推导公式,在以后的其他神经网络中还会继续使用这个概念。另外强调一点,在目前的神经网络框架中,最终输出建议为一个实数即一个标量,如本例结点8输出的是一维标量,这样能确保反向传播时第一层误差是标量对向量、矩阵求导,如果输出时多维时,调用反向传播函数loss.backward时会把多维输出与一个向量做内积变为一个实数,向量、矩阵、标量之间求导请参考本站文章:矩阵/向量/标量间相互求导。

2.2 隐藏层之间权重更新   

接下来再看两个隐藏层之间权重更新,即1,2结点与3,4,5,6,7之间权重:

self.layer2= nn.Sequential(nn.Linear(n_hidden_1, n_hidden_2),   nn.Sigmoid())

两个隐藏层间输出使用了激活函数Sigmoid,以结点3输入为例:

net3=x1w13+x2w23 

结点3输出为:

out3=σ(net3)

通过链式法则求两个隐藏层之间两个结点权重wij,此时下标范围i=1,2,而j=3,4,5,6,7:

1614222375419070700  ⑶

上式的关键是需要求出1614222439466014941,可以继续使用链式法则:

1614231279522056149  ④

1614222439466014941是结点3,4,5,6,7其中一个结点误差,而第二个隐藏层的输入误差1614231864952054408是一个向量:

1614232051148031985

1614222439466014941代入⑶式后,得到隐藏层之间权重梯度:

1614231375840017442

隐藏层之间权重更新公式为:

1614231131398098504

本例中输出端是一维的,即只有一个结点8,当输出端是多维度时1614232392883005320是一个向量,这时公式④写成:

1614232650157064050

公式中1614232736917041322是输出端第k个分量的输入误差,K(大写)是所有输出端结点的标号,或者说K是结点j所有下游结点集合,如上图中3,4,5,6,7都是结点1,2的下游结点,而8是3,4,5,6,7的下游结点。需要指出的是,在pytorch框架中要求损失函数必须是一个标量,即这里L函数输出一定是一个实数,所以1614222439466014941是标量对向量求导,这方面的知识请看矩阵/向量/标量间相互求导。

三、利用全连接神经网络识别minist数据集

    上例中利用平方差作为损失函数实现了一个线性回归问题,接下来利用交叉熵作为损失函数实现对minist数据集的识别,minist数据集是手写数字的图像样本

「梦想一旦被付诸行动,就会变得神圣,如果觉得我的文章对您有用,请帮助本站成长」

赞(0) 打赏
一分也是爱

支付宝扫一扫打赏

微信扫一扫打赏

标签:

上一篇:

下一篇:

相关推荐

博客简介

本站CDN采用VmShell免费提供离中国大陆最近的香港CMI高速网络做支撑,ToToTel打造全球最快速的边沿网络支撑服务,具体详情请见 :https://vmshell.com/ 以及 https://tototel.com/,网站所有的文件和内容禁止大陆网站搬迁复制,谢谢,VPS营销投稿邮箱: admin@linuxxword.com,我们免费帮大家发布,不收取任何费用,请提供完整测试文稿!

精彩评论

友情链接

他们同样是一群网虫,却不是每天泡在网上游走在淘宝和网游之间、刷着本来就快要透支的信用卡。他们或许没有踏出国门一步,但同学却不局限在一国一校,而是遍及全球!申请交换友链

站点统计

  • 文章总数: 2348 篇
  • 草稿数目: 13 篇
  • 分类数目: 6 个
  • 独立页面: 0 个
  • 评论总数: 2 条
  • 链接总数: 0 个
  • 标签总数: 6136 个
  • 注册用户: 139 人
  • 访问总量: 8,724,828 次
  • 最近更新: 2024年5月1日