这周主要内容是神经网络的超参数调试、Batch正则化以及TensorFlow框架的使用。TensorFlow框架将会是本周学习的重点,使用过之后发现它确实可以节省很多构建网络的时间。!!这次的源码请点击这里查看!!

参数调试

Andrew Ng说这些参数的重要性是有分级的,并且他给出了他心中的优先级,因为我没有更多的实践测试,所以直接把这些准则搬过来了。

  • 第一梯队
    • α\alpha -> 学习率,默认值0.001,这个地方的默认值Andrew没给,是我看了好多次实例中的一般值
  • 第二梯队
    • β\beta -> momentum算法的参数,默认值0.9
    • 隐藏单元数量(number of hidden units)
    • Mini-batch size
  • 第三梯队
    • 隐藏层数量(number of hidden layers)
    • 学习率衰减(learning rate decay)
  • 固定
    • β1,β2,ϵ\beta_1, \beta_2, \epsilon -> adam算法的参数,Andrew总是使用β1=0.9,β2=0.999,ϵ=108\beta_1 = 0.9, \beta_2 = 0.999, \epsilon = 10^{-8}

怎么调试?

基本是把这些参数分批次的做了划分,但是有时候还是会遇到多参数同时调整的问题,总体的思路是“先粗调后细调”,以两个hyperparameters需要调试为例,则可以画一个二维平面:

  • 第一步在给定区间上均匀随机取点,这其实相当于粗调;
  • 第二步找到效果更好的几个临近的点(红色点标识),缩小范围(红色方框)后继续细调;
  • 重复上述步骤直到效果最优。

为超参数选择合适的范围

比如像是hidden layers的数量,可以在一定的范围内随机均匀的取值,但是对类似与α\alpha这样的参数,取值区间在0.0001到1之间,如果按照随机均匀取值的防止,则将有90%的值将会落在0.1到1之间,这是我们不愿意看到的,我们更希望值在0.0001到0.1之间集中一些,这是我们不愿意看到。

以上图为例我们可以从$-4 \le a \le 0 $中均匀的取值,然后使用10a10^{a}即可获取值($0.0001 \le 10^a \le 1 0.00010.1),这样落到0.0001到0.1之间的概率就提升至\frac{3}{4}EWA。 再比如EWA中的参数\beta$,我们认为可能的值是0.9β0.9990.9 \le \beta \le 0.999,但是值在0.9附近和0.999附近波动,EWA相当于求近一段时间的平均值:

  • β=0.9β=0.9005\beta = 0.9 \to \beta = 0.9005时,平均的天数1010.0510 \to 10.05
  • β=0.9985β=0.999\beta = 0.9985 \to \beta = 0.999,平均的天数666.661000666.66 \to 1000

所以我们更希望值落在0.999附近更多,但是使用上一个方法必须要对应到0.001才行,所以我们需要一些转换手段,这里可以使用(1β)(1 - \beta),最后β=110r\beta = 1 - 10^r即可。

超参数搜索

  • 计算资源不足时,手动设置参数并观察loss function的值来进行
  • 计算资源充足时,并行多次计算达到寻找最有参数

正则化网络的激活网络

之前正则化时用来把输入项进行正则化的,这样将mean和variance置为1有利于优化,那么这次的优化目标是Z[l]Z^{[l]},如下图左边是优化前的图片,右边是经过正则化后的优化的图片。

如下图每个神经元可以分为两步:

  • 计算Z[l]Z^{[l]}
  • 使用激活函数计算A[l]A^{[l]}

在l层(z是属于l层的),启用对Z[l]Z^{[l]}正则化后的步骤:

  • 计算Z[l]Z^{[l]}
  • 求平均值:μ=1miz(i)\mu = \frac{1}{m} \sum_{i} z^{(i)}
  • 求variance:σ2=1mi(z(i)μ)2\sigma^2 = \frac{1}{m} \sum_i (z^{(i)} - \mu)^2
  • 正则化:znorm(i)=z(i)μσ2+ϵz^{(i)}_{norm} = \frac{z^{(i)} - \mu}{\sqrt{\sigma^2 + \epsilon}},这个ϵ\epsilon的作用是防止σ2\sigma^2太趋近于0
  • 调整正则化:Z~norm(i)=γznorm(i)+β\widetilde{Z}^{(i)}_{norm} = \gamma z^{(i)}_{norm} + \beta,这是一个学习参数(learnable parameters),我认为这个主要是来调节正则化的结果的,比如你需要更大的方差或者不想让均值为0等等,学习参数就意味着这个数会根据每次的迭代进行自主的学习
    • γ=γαdγ\gamma = \gamma - \alpha \mathrm{d}\gamma
    • β=βαdβ\beta = \beta - \alpha \mathrm{d}\beta

将batch normalization拟合进网络

使用mini-batch的神经网络:

  1. 正向传播(FORWARD PROPAGATION):

    X{1}Z[1]Z~[1]A[1]=g[1](Z~[1])A[l]X^{\{1\}} \to Z^{[1]} \to \widetilde{Z}^{[1]} \to A^{[1]} = g^{[1]}(\widetilde{Z}^{[1]}) \to \cdots \to A^{[l]}

    \cdots

    Xm/tX^{m / t}

    这里参数有4个:w[l],b[l],β[l],γ[l]w^{[l]}, b^{[l]}, \beta^{[l]}, \gamma^{[l]},但是由于BN过程中有减去均值这一项,所以执行这一项时z[l]=w[l]x+b[l]z^{[l]} = w^{[l]}x + b^{[l]}中的常数b[l]b^{[l]}将会被抵消,也就变成了三个参数w[l],β[l],γ[l]w^{[l]}, \beta^{[l]}, \gamma^{[l]},其中β[l],γ[l]\beta^{[l]}, \gamma^{[l]}的维度都是(n[l],1)(n^{[l]}, 1)

  2. 反向传播(BACKWARD PROPAGATION):

    W[l]=W[l]αdW[l]W^{[l]} = W^{[l]} - \alpha \mathrm{d}W^{[l]}β[l]=β[l]αdβ[l]\beta^{[l]} = \beta^{[l]} - \alpha \mathrm{d}\beta^{[l]}γ[l]=γ[l]αdγ[l]\gamma^{[l]} = \gamma^{[l]} - \alpha \mathrm{d}\gamma^{[l]}

为什么Batch Norm可以有效运行?

主要分为两点,第一在输入数据分布变化的情况下起作用,第二有轻微的正则化功能。先说第一点,我们之前全部假设是数据的分布不能改变,但是在实际的应用中不可能一直不变,比如我们使用神经模型找猫,我们给神经网络投喂的是黑色的猫,但是在实际的检测中却要其识别有颜色的猫,这其实就是输入数据分布的改变了,如下图:

在这种情况下神经网络不可能在只有左侧的数据的情况下预测右边的曲线,而且根据网络的特点每一层都有非常紧密的连接,当x的分布改变时,每一层的数据都会相应的改变,所以就会出现“covariate shift”现象,那么BN的作用就是限制前一层的改变,让它的mean趋向于0(或者说β\beta),让variance趋向于1(或者说γ\gamma),这样可以让整个网络更加稳定。

从第二个方面来说,Batch Norm也具有很轻微的正则化的功能,并且mini-batch size越小效果越好,因为每次只能拿到总数据的一部分,所以求的mean和variance也都有不同程度的噪声,那么同样的Z~[l]\widetilde{Z}^{[l]}也会有噪声,这就相当于减弱了每一个神经元的功能,这样使得不过分依赖任何一个隐藏单元,有点类似于dropout的方法,所以64的比512的有更大的噪点,也就有更好的dropout特性。

测试时的Batch Norm

在训练阶段,不管是使用mini-batch也好,不使用也好,我们都很容易计算出μ\muσ2\sigma^2,但是在测试中,值是一个个的取出的,我们没有办法一次性计算出那些值,这样我们只能通过类似Eponentially Weighted Averages来进行估算,具体可以参照deeplearning.ai_l2_w3_#8的视频。

Softmax

之前我们处理的都是二分类模型,softmax则是logistic的一般化的模型,可以识别多分类的问题,softmax是在最后一层进行的,这里C = # of class输入的分类总数。

最后这一层就是softmax,在这个示例中共有4种分类C=4C = 4,假设我们要识别猫、狗、鸭、其他,那么1可以对应猫的概率P(catx)P(cat|x),2可以对应狗的概率P(dogx)P(dog|x)…Softmax的激活函数、Cost函数、Y的输入都需要变化,同理在反向传播中的求导过程也需要有相应改变。

激活函数

  1. 计算t=eZ[l]t = e ^{Z^{[l]}},例如Z[l]=[5213]Z^{[l]} = \begin{bmatrix} 5 \\ 2 \\ -1 \\ 3 \end{bmatrix},则t=[e5e2e1e3]=[148.47.40.420.1]t = \begin{bmatrix} e^{5} \\ e^2 \\ e^{-1} \\ e^3 \end{bmatrix} = \begin{bmatrix} 148.4 \\ 7.4 \\ 0.4 \\ 20.1 \end{bmatrix}
  2. 计算A[l]=tj=1tiA^{[l]} = \frac{t}{\sum_{j = 1} t_i}j=1ti=148.4+7.4+0.4+20.1=176.3\sum_{j = 1} t_i = 148.4 + 7.4 + 0.4 + 20.1 = 176.3A[l]=[0.8420.0420.0020.114]A^{[l]} = \begin{bmatrix} 0.842 \\ 0.042 \\ 0.002 \\ 0.114 \end{bmatrix}

这样就能算出来对应1的概率为0.842,对应2的概率为0.042,……

Y的输入

Y的维度从原先的单一维度(m,1)(m, 1),到Y=[01100000]Y = \begin{bmatrix} 0 & 1 & \cdots \\ 1 & 0 & \cdots \\ 0 & 0 & \cdots \\ 0 & 0 & \cdots \end{bmatrix}维度为(m,C)(m, C),第一列是狗,第二列是猫……所以就有了one-hot编码,可以把(m,C)(m, C)压缩为(m,1)(m, 1)Yone_hot=[10]Y_{one\_hot} = \begin{bmatrix} 1 \\ 0 \\ \cdots \end{bmatrix}

Cost函数

Loss函数公式改为L(y,y^)=j=1yjlogy^jL(y, \hat{y}) = - \sum_{j = 1} y_j log\hat{y}_j,以上个例子中的狗为例,L(y,y^)=logy^2L(y, \hat{y}) = - log \hat{y}_2,为了让Loss变小,y^2\hat{y}_2就要变大,Cost函数的公式是求Loss函数的平均值,Loss函数变了Cost函数也就跟着变了。

反向传播

因为cost函数变了,所以bp得第一步求导就变得不一样了,细节就不细说了,公式为dz[l]=y^y\mathrm{d}z^{[l]} = \hat{y} - y

TensorFlow使用记录

TensorFlow确实很方便,只需要把正向传播的计算图构建出来,之后的优化(反向传播)工作tf就可以自动进行了。

步骤

获取常量

  1. n_xn\_x:输入变量的特征数量
  2. mm:训练集的样本数量
  3. n_yn\_y:输出变量的类别数量(考虑到softmax这种一般化的方式)

计算图构建

  1. 创建placeholder,因为这是构建计算图,而不是实际的计算,所以这里只需要一个placeholder,在真正需要运行的时候再投喂数据,如果使用了mini-batch则必须使用placeholder,因为不能在这一步就确定能投喂什么样的数据
  2. 初始化参数(w1,b1,w2,b2,w_1, b_1, w_2, b_2, \cdots
  3. 执行正向传播
    • ReLU激活函数tf.nn.relu(Z)
    • Softmax激活函数tf.nn.softmax_cross_entropy_with_logits(logits=logits, labels=labels)
  4. 计算costtf.reduct_mean(A)
  5. 创建一个optimizer(Gradient Descent, Adam等等),这就相当于生成了一个反向传播图

运行计算图

  1. 创建session
  2. 初始化全局变量sess.run(tf.global_variables_initializer())
  3. 迭代epoches次(mini-batch启动)
    1. 计算共有多少组mini-batch(m / mini-batch-size)
    2. 从全部数据中按照mini-batch要求提取数据
    3. mini-batch循环:
      1. 执行优化

遇到的问题

  1. TensorFlow中表示shape与numpy中不同,是用[a, b]表示的,如果使用numpy的(a, b)这种方式,虽然不会报错但是运行结果一直有误
  2. 执行优化的时候不能只运行optimizer,还需要带着cost
# 初始化参数W1的shape设置问题
W1 = tf.get_variable("W1", [25, 12288], initializer=tf.contrib.layers.xavier_initializer(seed=1))

# 执行优化
_, minibatch_cost = sess.run([optimizer, cost], feed_dict={X: minibatch_X, Y: minibatch_Y})