Shallow Neural Network
本周课程涉及到的知识点:
- Shallow neural network 的基本结构
- Activation function 的选择和计算
- Gradient descent 和 Backward propagation
Shallow neural network 的基本结构
Shallow neural network 可以看做是前一节课介绍的 logistic regression 的拓展,也即是只有一层的神经网络。 它的结构如下所示:
最左边的是输入层,最右边的是输出层,中间部分是隐藏层。可以看到和 logistic regression 不同的是在隐藏层我们定义了多个 neuron, 每个都会执行 Z[1] = W[1] * X + b[1] 以及 a[1] = g(Z[1]) 的操作,这里的 g(*) 被称为 activation function,我们下面会讨论这个 function 的选择。最后我们对 a[1] 进行类似的线性变换然后通过sigmoid function得到最后的输出。 可以看到 Shallow Neural Network 的计算套路和 logistic regression 是基本一样的:
- 定义神经网络的结构 ( 例如 隐藏层 neuron 的个数).
- 定义模型参数,注意我们有 W1, W2, b1, b2 四个参数
- 循环:
- Forward propagation 计算输出
- 计算Loss 和 cost function的值
- Backward propagation 计算梯度
- Gradient descent 优化参数
假定现在我们要构建一个shallow neural network. 输入层大小(neuron 个数)是 n_x, 中间层大小是 n_h, 输出层大小是 n_y, 下面我们来走一遍整个流程。
定义模型参数
我们需要如下定义模型参数:
- W1 = np.random.randn (n_h, n_x) * 0.01
- W2 = np.random.randn (n_y, n_h) * 0.01
- b1= np.zeros ((n_h, 1))
- b2= np.zeros ((n_y, 1))
关于这个定义,两点需要注意:
- 为什么我们不直接把 W1 和 W2 定义为全0矩阵?
- 为什么要把 W1 和 W2 乘以0.01 ?
关于第一点,课上的讲解是如果 W1 和 W2 是全0的话,那么隐藏层的每个neuron都会给出同样的计算结果(fail to break symmetric),这样导致神经网络跟只有一个neuron没什么区别。然而经过我的计算发现后果其实更加严重 - 如果 W1 和 W2 被初始化为 0, 经过一轮 forward propagation 和 backward propagation 以后 W1 和 W2 依然是 0, 也就是完全没有更新参数。
补充说明:对于logistic regression, 把初始 W 设置为全 0 向量则不会有问题。这是因为经过计算可以发现一轮 forward propagation 和 backward propagation 以后 W 已经被更新成非 0 向量了。
关于第二点,反过来想,如果 W1 和 W2 很大,那么会造成计算得到的 Z 很大,这样会导致经过激活函数计算出来的 a 值非常接近于1或者0。 这样会有可能导致最后的结果 log(a) 以后趋向于无穷大 - 这样会显著的拖慢收敛速度。因此一般来说我们会把 W1 和 W2 的初始值设置的小一点。
Activation function 的选择
一般来说在隐藏层我们会选择 tanh 或者 ReLU (或者 Leaky ReLU) 函数, 在输出层则会选择 sigmoid 函数。 这是一个典型的nobody knows why it just works的例子。虽然有理论说 tanh 的均值为0,因此效果较好,然而这无法解释为何 ReLU 激活函数的性能也非常不错(均值显然不为0)。
这4种函数的图形如下所示(左下为ReLu, 右下为 Leaky ReLu):
值得考虑的是,为何我们需要这样一个激活函数?
答案很显然,如果去掉activation function, 那么可以发现输出 Y 就完全是输入 X 的线性变换了, 这样我们就无法完成一些非线性划分的复杂工作了。
最后我们列一下四种不同激活函数的导数,这个在计算反向传播的时候会用到。
Activation Function | Form | Derivatives |
---|---|---|
sigmoid | g(z) = 1 / (1 + exp(-z)) |
g'(z) = g(z) (1 - g(z)) |
tanh | g(z) = exp(z) - exp(-z) / (exp(z) + exp(-z)) |
g'(z) = (1 - g(z))^2 |
ReLU | max (0, z) |
0 if z < 0, 1 if z >= 1 |
Leaky ReLU | max(0.01z, 1) |
0.01 if z < 0, 1 if z >= 1 |
Forward propagation 计算输出
假设我们隐藏层激活函数采用tanh function, 直接给出公式
[图片上传失败...(image-9c7983-1511717394092)]
[图片上传失败...(image-91df7e-1511717394092)])
[图片上传失败...(image-bb4ea8-1511717394092)]
[图片上传失败...(image-64301a-1511717394092)])
看似很复杂,其实很简单。对每层依次计算线性变换结果 Z[L] 和 激活函数后的结果 a[L] 即可。 需要注意的只是各个矩阵向量的维度。设输入 X 共有 m 个training data, 每层的neuron个数为 n(L), 那么各个矩阵向量的维度为:
-
X:
n_x * m
-
W[L]:
n(L) * n(L-1)
-
b[L]:
n(L) * 1
-
Z[L], A[L]:
n(L-1) * m
Backward propagation 计算梯度
同样直接给出公式(递推一下其实也不难):
需要注意的是在计算反向传播梯度的时候,我们用到了当前层(以及前一层)的输出。例如在计算 dZ[2] 的时候我们需要用到 A[2], 而在计算 dW[2] 的时候直接用到了 A[1] 并且间接用到了 A[2]。因此通常的做法是把每一层的输出放到cache里,以方便计算梯度的时候直接调用。
Gradient descent 优化参数
没有什么可说的,根据计算出来的梯度挨个更新每个参数即可。
总结
这一课基本是个从logistic regression 到 deep neural network的过渡。重点:
- 随机化初始参数的重要性
- 非线性activation function的重要性
- 如何计算正向传播和反向梯度(大概记住原理即可)