神经网络的结构与逻辑回归类似,只是神经网络的层数比逻辑回归多一层,多出来的中间那层称为隐藏层或中间层。从计算上来说,神经网络的正向传播和反向传播过程只是比逻辑回归多了一次重复的计算。正向传播过程分成两层,第一层是输入层到隐藏层,用上标[1]来表示;第二层是隐藏层到输出层,用上标[2]来表示。方括号上标[i]表示当前所处的层数;圆括号上标(i)表示第i个样本。
同样,反向传播过程也分成两层。第一层是输出层到隐藏层,第二层是隐藏层到输入层。
单隐藏层神经网络就是典型的浅层(shallow)神经网络。
结构上,从左到右,可以分成三层:输入层(Input layer),隐藏层(Hidden layer)和输出层(Output layer)。隐藏层的真正数值我们是不知道的,在训练集看不到他们的数值。隐藏层是抽象的非线性的中间层。
我们把输入矩阵记做,把隐藏层输出记为
,上标从0开始。下标表示神经元个数,所以有四个神经元的的隐藏层就可以写为:
最后,输出层记做,即
。这种单隐藏层神经网络也被称为两层神经网络(2 layer NN)。之所以叫两层神经网络是因为,通常我们只会计算隐藏层输出和输出层的输出,输入层是不用计算的。这也是我们把输入层层数上标记为0的原因。
关于隐藏层对应的权重和常数项
,
的维度是(4,3)。这里的4对应着隐藏层神经元个数,3对应着输入层x特征向量包含元素个数。常数项
的维度是(4,1),这里的4同样对应着隐藏层神经元个数。关于输出层对应的权重
和常数项
,
的维度是(1,4),这里的1对应着输出层神经元个数,4对应着隐藏层神经元个数。常数项
的维度是(1,1),因为输出只有一个神经元。总结一下,第i层的权重
维度的行等于i层神经元的个数,列等于i-1层神经元的个数;第i层常数项
维度的行等于i层神经元的个数,列始终为1。
对于两层神经网络,从输入层到隐藏层对应一次逻辑回归运算;从隐藏层到输出层对应一次逻辑回归运算。每层计算时,要注意对应的上标和下标,一般我们记上标方括号表示第几层,下标表示第几个神经元。下标从1开始,上标从0开始。
从输入层到输出层的计算公式:
从隐藏层到输出层的计算公式:
其中为:
上述每个节点的计算都对应着一次逻辑运算的过程,分别由计算z和a两部分组成。可以运用向量化来简化:
对于m个训练样本,我们也可以使用矩阵相乘的形式来提高计算效率。
对于每个样本i,可以使用for循环来求解其正向输出:
利用矩阵(输入矩阵的维度为),我们可以把上面的式子化简成:
和
的维度都为(4,m),4是隐藏层神经元个数。
和
的维度都为(1,m)。行表示神经元个数,列表示样本数目m。A = np.random.randn(4,3) B = np.sum(A, axis = 1, keepdims = True),意思是按行相加,并且使用(keepdims = True)来确保A.shape是(4,1)而不是(4,)。
神经网络隐藏层和输出层都需要激活函数(activation function)。之前我们使用的是Sigmoid函数作为激活函数,下面是几个不同的激活函数:
Sigmoid输出的值介于0和1之间,这使其成为二元分类的一个非常好的选择。 如果输出小于0.5,则可以将其归类为0,如果输出大于0.5,则归类为1。 它也可以用tanh来完成,但是它不太方便,因为输出在-1和1之间。
tanh的输出在-1和1之间,因此它将数据集中在一起,使得下一层的学习变得更加简单。
关于选择合适的激活函数:对隐藏层来说,选择tanh函数比选择sigmoid函数合适。因为tanh函数的取值范围在[-1,+1]之间,隐藏层的输出被限定在[-1,+1]之间,可以看成是在0值附近分布,均值为0。这样从隐藏层到输出层,数据起到了归一化(均值为0)的效果。对输出层来说,选择sigmoid函数更合适,因为二分类问题的输出取值为{0,+1}。
sigmoid和tanh函数在|z|很大的时候,激活函数的斜率(梯度)很小。所以在这个区域内梯度下降算法会运行得比较慢。应尽量避免使z落在这个区域,使|z|尽可能限定在零值附近,从而提高梯度下降算法运算速度。
ReLU激活函数可以弥补sigmoid函数和tanh函数的缺点。ReLU函数在z大于零时梯度始终为1;在z小于零时梯度始终为0;z等于零时的梯度可以当成1也可以当成0。对于隐藏层,选择ReLU作为激活函数能够保证z大于零时梯度始终为1,从而提高神经网络梯度下降算法运算速度。但当z小于零时,存在梯度为0的缺点。Leaky ReLU激活函数能弥补这个缺点,保证z小于零时梯度不为0。
总之,输出层的激活函数一般会选择sigmoid函数,隐藏层选择tanh函数更好些,实际应用中,通常会会选择使用ReLU或者Leaky ReLU函数,保证梯度下降速度不会太小。但还是具体情况具体分析。
如果使用线性激活函数,最终得到的结果仍然是输入变量x的线性组合,即使用神经网络与直接使用线性模型的效果并没有什么两样。即便是包含多层隐藏层的神经网络,如果使用线性函数作为激活函数,最终的输出仍然是输入x的线性模型。这样的话神经网络就没有任何作用了。因此,隐藏层的激活函数必须要是非线性的。
如果所有的隐藏层全部使用线性激活函数,只有输出层使用非线性激活函数,那么整个神经网络的结构就类似于一个简单的逻辑回归模型,失去了神经网络模型本身的优势和价值。
但如果是预测问题而不是分类问题,输出y是连续的情况下,输出层的激活函数可以使用线性函数。如果输出y恒为正值,也可以使用ReLU激活函数,这里需要具体问题具体分析。
sigmoid函数的导数:
tanh函数的导数:
ReLU函数的导数:
Leaky ReLU函数的导数:
神经网络正向传播过程为:
其中,g(⋅)表示激活函数。
反向传播是计算导数(梯度)的过程,下面是Cost function对各个参数的梯度:
浅层神经网络(包含一个隐藏层),m个训练样本的正向传播过程和反向传播过程分别包含了6个表达式,其向量化矩阵形式如下图所示:
神经网络模型中的参数权重W是不能全部初始化为零的,会导致隐藏层设置的多个神经元没有意义。但参数b可以初始化为0,并不会影响神经网络训练效果。
所以我们需要对权重W进行随机初始化,通过运用下面的python代码:
W_1 = np.random.randn((2,2))*0.01
b_1 = np.zero((2,1))
W_2 = np.random.randn((1,2))*0.01
b_2 = 0
乘以0.01的目的是希望初始化得到的W值尽量小,使用sigmoid函数或者tanh函数时能使z也比较小,提高梯度下降算法的运算速度。如果激活函数是ReLU或者Leaky ReLU函数,不需要考虑这个问题。但是,如果输出层是sigmoid函数,则对应的权重W最好初始化到比较小的值。