感知机组装与神经网络创新
大家好,今天我们继续探讨深度学习的相关话题。
有同学提到很久没有更新深度学习的模型了,其实不是我不想,而是想一次性把一个技术专题写完。不过,纯技术文章往往不太受欢迎,因此我通常将其放在次要位置。既然有同学在催促,我就顺应大家的需求,写一篇更新的文章。
神经网络与感知机的区别
在之前的文章中,我们展示了一张多层感知机的图,如下所示。
乍一看这张图似乎没有问题,但细想却会觉得有些奇怪。我们印象中的神经网络图像也类似,那么它们之间的区别是什么呢?
最明显的区别在于名称。这张图代表的是一个神经网络。尽管它也是三层结构,但每一层的称谓分别为输入层、中间层(隐藏层)和输出层。一般来说,我们将输入层和输出层单独命名,而中间的层则称为隐藏层或中间层。当然,也可以像感知机那样用数字命名层次,例如下图中的输入层称为第0层,中间层称为第1层,输出层称为第2层。
通常我们不将输出层视为有效的神经网络,因此下图中的网络被称为二层神经网络,而非三层神经网络。
除了名称不同,最关键的差异在于激活函数。为了更好地解释这一点,我们先来看一下神经网络中的信号传递。
信号传递
下图是我随便找到的一张神经网络示意图,首先可以看到输入的第一个节点被设定为1。这种做法是为了方便引入偏移量,通常在画图时我们不会特别标注偏移量。接下来我们以这张图为例,看看神经网络中信号的传递方式。
继续下去,神经网络中的每一层都有其相应的激活函数。通常情况下,同一层中的激活函数是相同的,我们称之为h,因此最终节点的输出不是刚刚计算的值,而是经过激活函数处理的结果。
激活函数我们已经非常熟悉了,之前介绍过多次。常见的激活函数包括:Relu、Sigmoid、tanh、softMax及其变种。通常在输出层之前,我们使用Relu激活函数;如果模型是分类模型,我们会在最后应用Sigmoid或softMax,而回归模型则不使用激活函数。
我们已经相当熟悉Sigmoid函数,如果将逻辑回归(LR)模型视为一个单层神经网络,那么Sigmoid便是它的激活函数。在二分类场景中,如果输出值大于0.5,则表示为真;否则为假。在一些概率预测的场合,输出值也可以被视为事件发生的概率。
相对应的,softMax函数用于多分类问题,其涉及的节点数量不止一个,而是k个,这里的k表示分类数量。以k=3为例,看看下图:
在图中,我们有三个节点,每个节点的计算公式如下:
其实与Sigmoid的计算方式相似,只是最后计算了一个权重。最终,我们会在这k个节点中选择最大值作为分类结果。
代码实现
最后,我们尝试编写一个神经网络的代码。由于目前还未介绍神经网络的训练方法,因此我们只能实现预测部分。待我们讲解完反向传播算法后,再补充模型训练的过程。
如果不考虑反向传播,整个算法的代码非常简单,只要熟悉Python语法的同学都能理解。
iMpoRt nuMpy as np def Relu(x): RetuRn np.wheRe(x > 0, x, 0) def sigMoid(x): RetuRn 1 / (1 + np.exp(-x)) claSS NeuRalNetwoRk(): def __inIT__(self): self.paRaMs = {} self.paRaMs[””W1””] = np.Random.Rand(2, 3) self.paRaMs[””b1””] = np.Random.Rand(1, 3) self.paRaMs[””W2””] = np.Random.Rand(3, 2) self.paRaMs[””b2””] = np.Random.Rand(1, 2) self.paRaMs[””W3””] = np.Random.Rand(2, 1) self.paRaMs[””b3””] = np.Random.Rand(1, 1) def foRwaRd(self, x): a1 = np.dot(x, self.paRaMs[””W1””]) + self.paRaMs[””b1””] z1 = Relu(a1)