学习目标:
吴恩达深度学习课程
- 浅层神经网络
- 深层神经网络
- 《改善神经网络》深度学习实践层面
学习内容:
- 激活函数 (Activation Function)
- 反向传播 (Backpropagation Intuition (Optional))
- 随机初始化 (Radom Initialization)
- 超参数 (Hyperparameters)
- 正则化(Regularization)
- 课后习题及编程作业
学习时间:
- 5.29 ~ 6.4
学习产出:
例如:
- 编程作业代码 【github仓库代码传送门】
- CSDN 技术博客 1 篇
- 习的深度学习视频 2 期
激活函数
线性激活函数sigmoid函数:
a
=
g
(
z
)
=
1
1
+
e
−
z
a=g(z)=\frac1{1+e^{-z}}
a=g(z)=1+e−z1
求导
d
d
z
g
(
z
)
=
=
g
(
z
)
(
1
−
g
(
z
)
)
\frac{d}{dz}g(z)= =g(z)(1−g(z))
dzdg(z)==g(z)(1−g(z))
除了输出层是一个二分类问题基本不会用它。
非线性激活函数
g
g
g(以双曲正切tanh为例):
a
=
t
a
n
h
(
z
)
=
e
z
−
e
−
z
e
z
+
e
−
z
a = tanh(z) = \frac{e^{z}-e^{-z}}{e^{z}+e^{-z}}
a=tanh(z)=ez+e−zez−e−z
求导
d
d
z
g
(
z
)
=
1
−
(
t
a
n
h
(
z
)
)
2
\frac{d}{dz}g(z)=1-(tanh(z))^2
dzdg(z)=1−(tanh(z))2
tanh激活函数非常优秀,几乎适合所有场合。
线性单元的函数ReLu
a
=
m
a
x
(
0
,
z
)
a=max(0,z)
a=max(0,z)
最常用的默认函数,如果不确定用哪个激活函数,就使用ReLu或者Leaky ReLu
在二分类的问题中,对于输出层,因为
y
y
y 的值是0或1,所以想让
y
^
\hat{y}
y^的数值介于0和1之间,而不是在-1和+1之间。所以需要使用sigmoid激活函数。在这个例子里看到的是,对隐藏层使用tanh激活函数,输出层使用sigmoid函数。
sigmoid函数和tanh函数两者共同的缺点是:在
z
z
z 特别大或者特别小的情况下,导数的梯度或者函数的斜率会变得特别小,最后就会接近于0,导致降低梯度下降的速度。
只要
z
z
z是正值的情况下,导数恒等于1,当
z
z
z是负值的时候,导数恒等于0。从实际上来说,当使用
z
z
z的导数时,
z
=
0
z=0
z=0的导数是没有定义的。
如果使用tanh函数代替sigmoid函数中心化数据,使得数据的平均值更接近0而不是0.5,这会使下一层学习简单一点。讨论优化算法时,tanh函数在所有场合都优于sigmoid函数。
选择激活函数的经验法则:
如果输出是0、1值(二分类问题),则输出层选择sigmoid函数,然后其它的所有单元都选择Relu函数。这是很多激活函数的默认选择,如果在隐藏层上不确定使用哪个激活函数,那么通常会使用Relu激活函数。有时,也会使用tanh激活函数,但Relu的一个优点是:当
z
z
z是负值的时候,导数等于0。
反向传播
逻辑回归的正向传播步骤:
x
w
b
}
=
⇒
z
=
w
T
x
+
b
⇒
a
=
σ
(
z
)
⇒
L
(
a
,
y
)
\left. \begin{matrix} x\\ w\\ b \end{matrix} \right\}= ⇒z=w^Tx+b⇒a=σ(z)⇒L(a,y)
xwb⎭⎬⎫=⇒z=wTx+b⇒a=σ(z)⇒L(a,y)
我们先计算
z
z
z ,然后
a
a
a ,然后损失函数
L
(
a
,
y
)
L(a,y)
L(a,y)
前向传播可以归纳为多次迭代 :
z
[
l
]
=
w
[
l
]
a
[
l
−
1
]
+
b
[
l
]
,
a
[
l
]
=
g
[
l
]
(
z
[
l
]
)
z^{[l]}=w^{[l]}a^{[l-1]}+b^{[l]},\quad a^{[l]}=g^{[l]}(z^{[l]})
z[l]=w[l]a[l−1]+b[l],a[l]=g[l](z[l])
(权重矩阵 W [ l ] W^{[l]} W[l]的维数是 n [ l ] n^{[l]} n[l], n [ l − 1 ] n^{[l-1]} n[l−1])
对于有一个输入层,一个隐藏层和一个输出层的双层神经网络,
前向传播:
计算
z
[
1
]
,
a
[
1
]
z^{[1]},a^{[1]}
z[1],a[1],再计算
z
[
2
]
,
a
[
2
]
z^{[2]},a^{[2]}
z[2],a[2] ,最后得到loss function。
反向传播:
向后推算出
d
a
[
2
]
da^{[2]}
da[2],然后推算出
d
z
[
2
]
dz^{[2]}
dz[2] ,接着推算出
d
a
[
1
]
da^{[1]}
da[1]
,然后推算出
d
z
[
1
]
dz^{[1]}
dz[1]
主要推导过程:
d
Z
[
2
]
=
A
[
2
]
−
Y
dZ ^{[2]}=A^{[2]} −Y
dZ[2]=A[2]−Y
d
W
[
2
]
=
1
m
d
Z
[
2
]
A
[
1
]
T
dW^{[2]}=\frac1mdZ^{[2]}A^{[1]T}
dW[2]=m1dZ[2]A[1]T
d b [ 2 ] = 1 m n p . s u m ( d Z [ 2 ] , a x i s = 1 , k e e p d i m s = T r u e ) db^{[2]}=\frac1mnp.sum(dZ^{[2]},axis=1,keepdims=True) db[2]=m1np.sum(dZ[2],axis=1,keepdims=True)
d
Z
[
1
]
⏟
(
n
[
1
]
,
m
)
=
W
[
2
]
T
d
Z
[
2
]
⏟
(
n
[
1
]
,
m
)
∗
g
[
1
]
′
(
Z
[
1
]
)
⏟
(
n
[
1
]
,
m
)
\underbrace{dZ^{[1]}}_{(n^{[1]},m)}=\underbrace{W^{[2]T}dZ^{[2]}}_{(n^{[1]},m)}*\underbrace{g^{[1]'}(Z^{[1]})}_{(n^{[1]},m)}
(n[1],m)
dZ[1]=(n[1],m)
W[2]TdZ[2]∗(n[1],m)
g[1]′(Z[1])
d
W
[
1
]
=
1
m
d
Z
[
1
]
x
T
dW^{[1]}=\frac1mdZ^{[1]}x^T
dW[1]=m1dZ[1]xT
d b [ 1 ] = 1 m n p . s u m ( d Z [ 1 ] , a x i s = 1 , k e e p d i m s = T r u e ) db^{[1]}=\frac1mnp.sum(dZ^{[1]},axis=1,keepdims=True) db[1]=m1np.sum(dZ[1],axis=1,keepdims=True)
随机初始化 (Radom Initialization)
当初始化神经网络的权重,不要都是0,而是随机初始化。对于逻辑回归,把权重初始化为0当然也是可以的。但是对于一个神经网络,如果把权重或者参数都初始化为0,那么梯度下降将不会起作用。
(叫做symmetry breaking problem)
如果
W
W
W初始化成0,由于所有的隐含单元都是对称的,无论运行梯度下降多久,他们一直计算同样的函数。这没有任何帮助,因为你想要两个不同的隐含单元计算不同的函数,所以随机初始化参数:把
W
[
1
]
W^{[1]}
W[1]设为np.random.randn(2,2)(生成高斯分布),通常再乘上一个小的数,比如0.01,这样把它初始化为很小的随机数。然后
b
b
b没有这个对称的问题,所以可以把
b
b
b初始化为0,因为只要随机初始化
W
W
W就有不同的隐含单元计算不同的东西,因此不会有symmetry breaking问题了。相似的,对于
W
[
2
]
W^{[2]}
W[2]随机初始化,
b
[
2
]
b^{[2]}
b[2]可以初始化为0。
通常倾向于初始化为很小的随机数,如果
W
W
W 很大,
z
z
z就会很大或者很小,这种情况下你很可能停在tanh/sigmoid函数的平坦的地方,梯度下降会很慢,因此学习也就很慢,这会造成tanh/Sigmoid激活函数饱和在龟速的学习上。
建立一个含有隐藏层的神经网络
课程作业(testCases.py
,planar_utils.py
)提供软件包:
testCases:提供了一些测试示例来评估函数的正确性
planar_utils :提供了在这个任务中使用的各种有用的功能
要搭建的神经网络模型图:
加载,可视化数据集:
import numpy as np
import matplotlib.pyplot as plt
from testCases import *
import sklearn
import sklearn.datasets
import sklearn.linear_model
from planar_utils import plot_decision_boundary, sigmoid, load_planar_dataset, load_extra_datasets
np.random.seed(1) #设置一个固定的随机种子,以保证接下来的步骤中我们的结果是一致的。
X, Y = load_planar_dataset()
#X的维度为: (2, 400)
#Y的维度为: (1, 400)
#数据集里面的数据有:400 个
plt.scatter(X[0, :], X[1, :], c=np.squeeze(Y), s=40, cmap=plt.cm.Spectral) #绘制散点图
Logistic回归的分类效果
clf = sklearn.linear_model.LogisticRegressionCV()
clf.fit(X.T,Y.T)
可以看到非线性可分数据的逻辑回归效果并不好。
神经网络模型的训练结果:
#构建模型
def nn_model(X,Y,n_h,num_iterations,print_cost=False):
"""
参数:
X - 数据集,维度为(2,示例数)
Y - 标签,维度为(1,示例数)
n_h - 隐藏层的数量
num_iterations - 梯度下降循环中的迭代次数
print_cost - 如果为True,则每1000次迭代打印一次成本数值
返回:
parameters - 模型学习的参数。
"""
np.random.seed(3) #指定随机种子
n_x = layer_sizes(X, Y)[0]
n_y = layer_sizes(X, Y)[2]
parameters = initialize_parameters(n_x,n_h,n_y)
W1 = parameters["W1"]
b1 = parameters["b1"]
W2 = parameters["W2"]
b2 = parameters["b2"]
for i in range(num_iterations):
A2 , cache = forward_propagation(X,parameters)
cost = compute_cost(A2,Y,parameters)
grads = backward_propagation(parameters,cache,X,Y)
parameters = update_parameters(parameters,grads,learning_rate = 0.5)
if print_cost:
if i%1000 == 0:
print("第 ",i," 次循环,成本为:"+str(cost))
return parameters
在构建一个神经网络的时候,我们需要设置许多参数,例如神经网络的层数、每个隐藏层包含的神经元个数、学习因子(学习速率)、激活函数的选择等等,此外数据集对神经网络的性能影响也很重要。
本次课程作业选择建立包含一个隐藏层的浅层神经网络,神经元较少时偏差较大,较大模型能更好拟合训练集,调优过程中发现隐藏层大小为4或5时比较贴合数据集,且不会过拟合。学习率为1.2时梯度下降能更好的收敛。 通过本次作业,对超参数的调整进一步熟悉,learning rate α \alphaα (学习率)、iterations(梯度下降法循环的数量)、 $L$(隐藏层数目), $n^{[l]}$(隐藏层单元数目)、choice of activation function(激活函数的选择)通过练习,走吴教授说的Idea—Code—Experiment—Idea这个循环,尝试各种不同的参数,实现模型并观察是否成功,然后再迭代的过程,受益颇多。