在本文开始之前墙裂建议大家回顾下之前的文章,以便新老朋友丝滑入戏😼。
本文概览
在之前的两篇文章中我们学习了简单的单层神经网络,实现了线性回归、逻辑回归和softmax回归,已经可以解决一些简单的回归问题和分类问题,但是对于复杂问题的求解,单层神经网络的性能是不够的🙃,今天我们就一起来学习一下深层神经网络又有哪些独到之处。与以往一样,我们还是顺着技术的发展去讲解,了解整个过程的来龙去脉。
简单网络对于简单数据的处理是没问题的,但是如今数据量爆炸的年代,解决实际问题仅靠简单神经网络却是杯水车薪。比如说识别一张喵的照片😾
假设我们把图片中的像素值视为特征,就算这张图片是50×50的小图片,也会存在着2500个特征,将这些特征与对应的权重相乘形成多项式,又是一个非常复杂的工作,更不用说再加上激活函数了,这时就需要更深层的神经网络。有了技术限制就会有技术发展,行则将至,接下来我们一起来探索深度神经网络。
1. 由单层到深层
1.1 逻辑与(AND)
同样的,我们先挑单层神经网络的毛病,之前我们实现的神经网络给的数据都是同一组,也就是“逻辑与”数据。现在我们深入了解一下该数据。
y_and | ||
0 | 0 | 0 |
0 | 1 | 0 |
1 | 0 | 0 |
1 | 1 | 1 |
我们使用这组数据构建了二分类神经网络,输出的预测值与给出的真实值y_and相同,那么我们就认为这个分类的神经网络起了作用。它是怎么实现分类的呢?我们知道分类就是将一堆数据分开嘛🦕,最简单的就是将互不相干的两类数据用一条直线将其划分开。其实我们从中学就学会了这件事情,对于上面的数据,在分类之前我们要考虑一下怎么去可视化。
中学我们学习过y=ax+b这种简单的一元函数,还记得当时怎么在纸上画出来它的图形吗?现在让我们换一个高级一点的说法,还记得当时怎么做的数据可视化吗👀?我们将变量x作为横轴,变量y作为纵轴,通过描点连线作出图像表示,这便是将x和y进行了可视化。如法炮制,“逻辑与”数据中有两个特征 x1,x2,我们将这两个特征进行可视化,那么形成的式子就是x1 = ax2+ b
,其中a是权重,b是偏置。如此一来就将特征 的分布呈现在图形之上。这一步可以用python中的matploitlib包实现非常简单,在这里我就直接给出手绘图像
可以看到根据标签的不同,将特征点分为不同的颜色,红色的点的坐标为(1,1),对应的标签为1,因此两类数据呈现在图像之上。我们可以很轻松的找到一条直线将两类数据划分开,如图所示
图中这条紫色的直线就可以将两类数据区分开,这条直线就叫做决策边界(decision boundary)。这条直线的表达式为
w
1
x
1
+
w
2
x
2
+
b
=
0
w_1x_1+w_2x_2+b=0
w1x1+w2x2+b=0 ,也就是之前我们提到过的累加求和的式子,我们用z来表示,即为
z
=
w
1
x
1
+
w
2
x
2
+
b
z={w}_{1}{x}_{1}+{w}_{2}{x}_{2}+b
z=w1x1+w2x2+b 令 x0 = 1,w0 = b , 就又变成了我们非常熟悉的一般表达式
z
=
w
0
x
0
+
w
1
x
1
+
w
2
x
2
z={w}_{0}{{x}_{0}+w}_{1}{x}_{1}+{w}_{2}{x}_{2}
z=w0x0+w1x1+w2x2
通过上面的分析可以看的出,在相同特征的情况下,决策边界由权重w所决定。所以说,权重就是神经网络最后自己学习出来的,当学习到很好的权重参数,再进行计算的时候就可以得到一个很好的准确度。
与门结构
1.2 逻辑或(OR)
对“逻辑与”数据可视化后,我们将同样的操作换到“逻辑或”数据上。
y_or | ||
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 1 |
逻辑或即有1则1,全0才0。我们继续将其可视化,如图
通过图像可以看的出,对于”逻辑或“数据而言,我们也可以轻易的找到一条决策边界将其进行二分类。
或门结构
1.3 逻辑与非(NAND)
“逻辑与非” 是"与门"和"非门"的叠加,先进行"与"运算,再进行"非"运算,有多个输入和一个输出。下面给出"与非门"的数据
y_nand | ||
0 | 0 | 1 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 0 |
将其可视化
下面给出与非门的网络结构
1.4 逻辑异或(XOR)
上面的几个逻辑运算我们可以很简单的去找到决策边界,那么接下来我们来看这么一组数据,异或门数据,异或运算是相同为0,不同为1。
y_xor | ||
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 0 |
同样的,我们对这组数据进行可视化
好像有些问题,由上图可以看出,之前可以用一条直线简单的分开数据的方式在这里行不通了,那么大家思考一下对于这个异或门数据,它的决策边界怎么画呢?这种难以用直线分开的数据才是我们常见的数据,这也是单层神经网络的局限性所在。有了问题就会有解决办法,直线不能划分那我们可以用曲线🌙。如图
异或门数据的决策边界是一条曲线,这条曲线就可以很好的划分出两类数据,但是单层神经网络是实现不了曲线的决策边界,为了解决这个问题,我们可以在神经网络上叠加层来实现,也就有了深层神经网络。
2. 拼凑深层神经网络
2.1 叠加层解决异或问题
我们先来看一下异或运算的逻辑推导式
XNOR = ( x1 NAND x2 ) AND ( x1 OR x2 )
从该推导式我们可以看到,逻辑异或可以由逻辑与非、逻辑或和逻辑与三种运算推导得出,那么我们尝试去一步步拼凑该公式。首先是先构造一个“与非网络”,然后再构造一个“或网络”,这样得到的两个神经网络作为“与网络”的两个输入。根据上文中给出的网络结构图,可以得到
这便形成了一个多层神经网络😉,图中不同颜色代表了异或计算中的小网络,小网络之间互相连接,构成一个多层的神经网络,将本来只能计算直线边界的功能拓展到计算曲线边界。由简单到复杂,由单层到多层,可以看到简单层的累积形成的复杂网络就能实现很多难处理的功能,当然再加上各种激活函数会使模型变得更加复杂,一般认为越复杂的神经网络其解决问题的能力也就越强, 相信大家在看完前两篇文章之后再到这里应该能轻松的理解深层神经网络。
我们将上图一般化,就可以得到常见的神经网络架构图
2.2 异或网络架构里的细节
在这个深层神经网络中,三种逻辑运算就相当于激活函数,可以看得出除输入层以外的层,每个神经元都接收的的是上一层的神经元和它们之间权重矩阵的累加求和,即 z = w 1 x 1 + w 2 x 2 + b z={w}_{1}{x}_{1}+{w}_{2}{x}_{2}+b z=w1x1+w2x2+b 。得到的加权求和的值作为激活函数的输入,即 a ( z ) a(z) a(z) ,这里我们假设a是激活函数的函数名,这样就做了一个运算,使得输入加权求和后得到另外一个值 a i a_i ai 。现在让我们遮住左半部分再来看一下
如果只关注右半部分,这就和我们之前提到的单层神经网络一样,只不过这里的输入特征不是 x 1 , x 2 x_1,x_2 x1,x2了,而是变成了经过激活函数处理过的 a 1 , a 2 a_1,a_2 a1,a2和固定的偏置项b ,我们可以把现在的输入 a 1 , a 2 a_1,a_2 a1,a2 看作 x 1 , x 2 x_1,x_2 x1,x2 的变体,按照这个逻辑再去看隐藏层到输出层的部分就非常的清晰明了,复杂网络的内部结构都是相同的,但是奈于参数的随机性和体量问题,我们没有办法去完全的理解深层神经网络,这也是深度学习的黑盒问题的原因所在⬛。
3. 从头搭建深层神经网络
3.1 Pytorch实现
通过上文的分析我们可以知道,对于输入层以外的所有层,每一个输出都是由上一层的所有特征决定的(这里我们暂且叫特征,但是你明白的),这样从左到右,学习我们给出的训练集的算法就被称为前向传播(Forward Propagation)。那么我们就来简单的去实现这个深层网络的前向传播算法。
同样的我们给出一些简单数据,我们准备300个样本,每个样本有10个特征,这300个样本共有3种类别。现在给出神经网络的架构,假设输入层有8个神经元,隐藏层有6个神经元,输出层有1个神经元,我们使用sigmoid、ReLU和softmax激活函数。现在让我们一起来实现这个三层神经网络吧。
网络结构如图所示,这种简单的神经网络其含有的参数量也是很多的,如果不借助深度学习框架,难以想象其中的复杂度
pytorch代码实现
'''1.导包'''
import torch
import torch.nn as nn
from torch.nn import functional as F
'''2.生成数据'''
torch.manual_seed(55)
X = torch.rand((300,10),dtype=torch.float32) # 生成训练数据
y = torch.randint(0, 3, size=(300,1)) # 生成 0,1,2 标签
'''3.定义神经网络'''
class DeepNN(nn.Module):
def __init__(self,in_features,out_features):
super(DeepNN,self).__init__()
self.linear1 = nn.Linear(in_features, 8) # 定义网络结构
self.linear2 = nn.Linear(8, 6)
self.linear3 = nn.Linear(6, out_features)
def forward(self, X):
a1 = torch.relu(self.linear1(X)) # z1 = self.linear1(X) 加权求和
a2 = torch.sigmoid(self.linear2(a1)) # z2 = self.linear2(a1)
a3 = F.softmax(self.linear3(a2),dim=1) # z3 = self.linear3(a2)
return a3
'''4.实例化'''
model = DeepNN(10, 3)
'''5.前向传播'''
model(X)
上面就是整个深层神经网络前向传播的实现代码,其中前向传播之后会生成(300,3)的数据,即300个样本分别被分为3类的概率值。
在jupyter lab 中运行如下
查看输出结果的大小,如图
如果我们取出概率最大的类别作为当前样本的最终分类,那么每一个样本都会预测出来对应的类别,有了预测值和真实值,就可以去计算预测的是否正确,其中的差值称之为损失loss,将这个loss降到最低,得到的模型参数就是比较好的参数,也就能得到一个好的模型。
如何降低loss,这就会引出梯度下降法(Gradient Descent)和大名鼎鼎的反向传播(BP,Backpropagation),限于篇幅,本文暂时不先介绍。
以上就是本文的全部内容了,希望能给到你一点点帮助,DDL就是第一生产力⌛。
分享知识,记录生活,一起进步。