1. 第i层网络 Z[i] = W[i]A[i-1] + B[i],A[i] = f[i](Z[i])。
其中, W[i]形状是n[i]*n[i-1],n[i]是第i层神经元的数量;
A[i-1]是第i-1层的神经元,形状是n[i-1]*p,p是样本数量;
B[i]形状是n[i]*p,B[i]的每一列都是一样的,所以其实有效的参数只是n[i]个,python里直接用n[i]*1的b[i]然后boradcasting成n[i]*p方便做加法。
A[0]对应输入层,n[0]是单个输入样本的特征数量。f[i]()是第i层的激活函数。
Notation:a4[2](12)表示第2层,第12个样本,第4个神经元。
2. 永远不要用sigmoid函数, 唯一的例外是二元分类问题的输出层,因为这需要输出是0或1。
tanh(z) = (e^z-e^(-z))/(e^z+e^(-z)) 作为激活函数几乎总比sigmoid函数效果好,直观地说是因为tanh的中心店是0而不像sigmoid是0.5,于是对于下一层有中心化数据的作用。
tanh和sigmoid函数的共同缺点是当输入很大或者很小时,函数会饱和,梯度为0。
ReLU是现在的默认选择,f(z) = max(0, z)。ReLU的收敛速度比tanh和sigmoid都要快得多。Leaky ReLU ( max(0.01z, z) )的表现更好一点,但是不明显,所以还是ReLU用的更多。
3. 为什么激活函数是必须的?如果没有激活函数,系统就成了只对输入做线性运算,内部的隐含层一点用也没有,网络层数再多也没用,因为线性函数的组合也是线性函数。
4. sigmoid σ(z) = 1/(1+e^(-z)),σ'(z) = σ(z)(1-σ(z)).
tanh g(z) = (e^z-e^(-z))/(e^z+e^(-z)) ,g'(z) = 1-(g(z))^2.
ReLU g(z) = max(0, z),g'(z) = { 0 , if z<0;
{ 1, if z>0;
{ undefined, if z = 0. 实际使用中可以把这个点的导数设为0或者1,都无所谓。
5. 训练神经网络时,随机初始化权重W非常重要,全部设为0的话会使得梯度下降法无效,b可以初始化为0。
w[i] = np.random.randn((n[i],n[i-1]))*0.01. 乘以0.01是为了把它初始化成很小的随机数,因为对于tanh、sigmoid类似的函数,权重太大容易饱和。如果不用tanh、sigmoid,用ReLU,那么问题不大,但如果最终是个二分类问题,最后一个输出还是用了sigmoid,那么还会遇到这个问题。所以干脆全部初始化成很小的随机数吧。对于浅层神经网络,一般这种初始化方法就ok了。但对于很深的神经网络,有时候要挑选0.01以外的常数。
b[i] = np.zero((n[i], 1))。
但对于Logistic回归来说,并没有隐含层,所以是可以把W和b都初始化为0的。这样初始化的时候,第一次前向计算的输出确实是0,但是反向传播计算的梯度值是和输入相关的,所以不同的神经元会有不同的值,破坏了对称性,所以算法有效。
6. 神经网络算法的一般流程:
1)定义神经网络的结构:输入的大小,隐藏层层数,每层神经元的数量,等等。
2)初始化参数,W初始化为小随机数,b初始化为0。
3)循环:
a)前向传播,对于第[i]层网络,已知前一层传进来的输入A[i-1],和这一层的参数W[i]、b[i],以及激活函数,计算出Z[i]和A[i],并且把Z[i]、A[i]、W[i]保存起来为反向传播的计算做准备。
b)计算loss。
c)反向传播得到梯度,对于第[i]层网络,已知后一层传来的输入dA[i],从cache中取出Z[i]、A[i]、W[i],计算出dW[i]、db[i]、dA[i-1]。
d)更新参数。