吴恩达深度学习课程笔记(一):神经网络与深度学习
第一周:深度学习概论
数据量大的时候大的网络能提高性能。在小的数据集上,我们更应该关注特征的选取、算法实现的细节之类的内容,因为在小的数据集上,各种规模的网络表现差不多。
第二周 神经网络基础
2.1 二分类
使用这种方式表达样本在神经网络中是更常见的方式,即,每一列表示一个样本,每一行表示一个特征。
m 样本数量
n 特征数量
2.2 逻辑回归
- 用sigmoid函数去限制
WX+b
的范围,即为逻辑回归。
y^=σ(wx+b), where σ(z)=11+e−z
2.3 逻辑回归的代价函数
L(y,y^)=−ylog(y^)−(1−y)log(1−y^)
Cost function:
J(w,b)=−1m∑i=1m{ ylog(y^)+(1−y)log(1−y^)}
2.4 梯度下降
repeat:{
w=w−α∂J(w)∂w
b=b−α∂J(b)∂b
}
2.5 导数
略
2.6 更多关于导数的例子
略
2.7 计算图
略
2.8计算图上的导数
链式法则
扫描二维码关注公众号,回复:
2876521 查看本文章
2.9逻辑回归的梯度下降
a表示的是
y^
,即逻辑回归的预测值。
对于sigmoid函数的求导为:
即
σ(z)′=σ(z)( 1−σ(z) )
。
dz=∂L(y^,y)∂z=∂L(y^,y)∂y^dσ(z)dz=(−yy^+1−y1−y^){y^(1−y^)}=y^−y(1)(2)(3)(4)
dw1=x1dz
、
dw2=x2dz
、
db=dz
那么,沿着代价函数梯度下降的方向更新参数:
w1=w1−α dw1
w2=w2−α dw2
b=b−α db
就可以最终到达一个局部最优点。
这就是逻辑回归的梯度下降。
推导的最终结果是,我们在梯度下降的时候,不需要再去推导,直接利用结论
目前为止还只是单个样本的梯度下降。
2.10 在整个样本集上的梯度下降
Random initialization w1、w2、b
Repeat until convergence:
1.
J=0, dw1=0, dw2=0, db=0
2.
For i=1 to m:
3.
z(i)=WTX+b
4.
a(i)=σ(z(i))
5.
dz(i)=a(i)−y(i)
6.
J +=−y(i)loga(i)−(1−y(i))log(1−a(i))
7.
dw1 +=x(i)1dz(i)
8.
dw2 +=x(i)2dz(i)
9.
db +=dz(i)
10.
end For
11.
J=J/m
12.
dw1=dw1/m
13.
dw2=dw2/m
14.
db=db/m
15.
w1=w1−α dw1
16.
w2=w2−α dw2
17.
b=b−α db
缺点:
- 需要两个for循环,第一个遍历所有样本,第二个遍历所有特征(12-13行),导致算法很低效;
- 解决办法:矢量化表示
2.11 矢量化
vectorization
矢量化使得运算过程变得更快。
试验中,矢量化比非矢量化快400多倍。
矢量化你的代码
经验法则:无论什么时候,避免显式的使用for循环。
import numpy as np
import time
a = np.random.rand(1000000)
b = np.random.rand(1000000)
tic = time.time()
c = np.dot(a, b)
toc = time.time()
print('Vectorization version :' + str(1000* (toc - tic)) + ' ms')
tic = time.time()
for i in range(len(a)):
c = a[i] * b[i]
toc = time.time()
print('NO vectorization version :' + str(1000* (toc - tic)) + ' ms')
2.12更多矢量化的例子
numpy中很多函数都是直接对矩阵中的元素进行操作。
2.10的矢量化表示:
Random initialization W、b
Repeat until convergence:
1.
J=0, dW=np.zeros((n,1)), db=0
2.
For i=1 to m:
3.
z(i)=WTX+b
4.
a(i)=σ(z(i))
5.
dz(i)=a(i)−y(i)
6.
J +=−y(i)loga(i)−(1−y(i))log(1−a(i))
7.
dW +=x(i)dz(i)
9.
db +=dz(i)
10.
end For
11.
J=J/m
12.
dW=dW/m
14.
db=db/m
15.
W=W−α dW
17.
b=b−α db
即,W的计算去掉了循环,使用了矩阵。
减少一层循环使得代码变快。
2.13 矢量化量化逻辑回归(正向)
不用for循环
Z=wTX+b
- 其中,w为
n×1
,
wT=[w1,w2,...,wn]
;
- b为
1×m
,
b=[b,b,...,b]
;
- X为
n×m
,
X=[x(1),x(2),...,x(m)]
。
-
Z∈R1×m
A=σ(Z)
2.14 矢量化逻辑回归的梯度下降(反向)
-
dZ=A−Y
-
dw=1mX(dZ)T
-
db=1m∑mi=1dz(i)=1m np.sum(dZ)
再次实现算法:
1.
J=0, dw=np.zeros((n,1)), db=0
3.
Z=WTX+b
4.
A=σ(Z)
5.
dZ=A−Y
6.
dw=1mX(dZ)T
7.
db=1m∑mi=1dz(i)=1m np.sum(dZ)
8.
W=W−α dW
9.
b=b−α db
2.15 Python 的numpy的广播机制
matrixm×n+−×÷(1,n)−−−−−−−−−−−>(m,n)
matrixm×n+−×÷(m,1)−−−−−−−−−−−>(m,n)
matrixm×n+−×÷(1,1)−−−−−−−−−−−>(m,n)
2.16 广播的注意事项—技巧
因为numpy的灵活性,常常导致出现奇怪的bug。
- 不要使用秩为1的数组,向量的秩至少为2:
a = np.random.randn(1, 5)
a = np.random.randn(5, 1)
- 断言向量的形状:
assert(a.shape == (5, 1))
这些assert执行起来很快,某种意义上可以看成是代码的文档,随意使用。
- 如果代码中出现秩为1的数组,使用reshape将其转换为向量:
a = np.reshape((5,1))
通过以上三种手段,彻底杜绝代码中的秩为1的数组。
2.17 Jupyter or iPython的快速指南
如果前面代码框的文本后边的代码框有用到,一定要先运行过前边的代码框。
2.18 逻辑回归代价函数的扩展
- 为什么逻辑回归的代价函数是这个样子的?
P(y|x)=y^y(1−y^)(1−y)={y^,1−y^,if y = 1if y = 0(1)(2)
也就是,
y^
是在样本x的条件下,结果为
y
的概率。
其中,
y^=σ(z)
,
z=wTX+b
。
那么,根据上边的式子,我们可以通过求极大似然估计来估算一组合理的参数值。
极大似然估计是说,我通过利用已知的样本分布,找到最有可能导致这种分布的参数值,即导致最大概率的参数值。
log(P)=ylog(y^)+(1−y)log(1−y^)
通过log化的式子,一样可以通过找最大似然值的方式找到参数值。
log函数是严格递增函数,把上式log化,不改变增减性,带来的好处是乘积变成加减。
而损失函数是将每一个样本的损失求和(再求平均也可)。
同时损失函数的目标是求最小的损失。加负号。
将求极大似然值和最小化cost统一起来。
即:
L=−ylog(y^)−(1−y)log(1−y^)
最大化训练样本出现的次数相当于最小化代价函数。
那么最后,
logP(所有样本)=log∏mi=1P(y(i)|x(i))=∑−L
所以,
J(w,b)=−1/m∑L(y^(i),y)
1/m是为了缩放。
第三周 浅层神经网络
3.1 神经网络概述
略
3.2 神经网络表示
略
3.3 计算神经网络的输出
略
3.4 多个例子中的向量化
略
3.5 向量化的解释
略
3.6 激活函数
激活函数:
g(z)
- sigmoid函数
a=11+e−z
很少使用。
- tanh函数
a=ez−e−zez+e−z
结果介于-1到1之间。
有时候数据是中心化的时候,可以使用。
和sigmoid共同的缺点是:在输入很大or很小的时候,梯度变化很小,会降低梯度下降的速度。
- ReLU
修正线性单元
a=max(0, z)
z为正,梯度为1;
z为负,梯度为0;
z = 0, 给其假设一个导数。
不同层的激活函数可以是不同的。
二分类问题,输出为0和1的,输出层选择sigmoid;其他所有层都选择ReLU。
- leaky ReLU
a=max(0.01z, z)
0.01表示一个很小的值,也可以是其他值。
效果比ReLU好。
吴恩达老师一般用ReLU。
和ReLU的共同优点是:即使输入z很大的情况下,梯度都会远远大于0;所以,使用这两个,比使用sigmoid和tanh的学习速率要快很多(因为梯度下降为0,即梯度消失的情况减少了)。
总结:
- sigmoid除二分类输出层外,基本不用;
- tanh比sigmoid优秀;
- ReLU更常用;
- 如果选择 ReLU,可以尝试一下leaky ReLU,尽管最后不一定使用leaky ReLU。
- 在构建自己的网络时,如果不确定用什么激活函数,就多选择几种,测试一下。
3.7 为什么需要非线性激活函数
如果隐藏层只用线性激活函数or不用激活函数:
a[1]=z[1]=w[1]x+b[1]
a[2]=z[2]=w[2]a[1]+b[2]
即:
a[2]=w[2](w[1]x+b[1])+b[2]
=(w[2]w[1])x+(w[2]b[1]+b[2])
=w′x+b′
那么,仍旧是线性变换。即对输入进行线性组合然后输出。这根本就不需要什么隐藏层。
只有一个地方会用到线性激活函数or不用激活函数,就是回归问题。网络的输出是一个连续值。即
y^∈R
,即使这样,隐藏层依然使用的是诸如tanh、ReLU or leaky ReLU,只有在输出层使用线性激活函数。
3.8 激活函数的导数
- sigmoid
g(z)=11+e−z
g′(z)=g(z)(1−g(z))=a(1−a)
- tanh
g(z)=ez−e−zez+e−z
g′(z)=1−(tanh(z))2=1−a2
- ReLU
g(z)=ma(0, z)
g′(z)=⎧⎩⎨1,0,undefined,if z > 0if z < 0if z = 0
z=0
处的导数在代码实现中,可以设置成1,也可以设置成0。因为碰到
z=0
的概率不大,设置成多少对结果没多少影响。
3.9 神经网络的梯度下降法
前行传播,然后反向传播。
- Forward propagation
Z[1]=w[1]X+b[1]
A[1]=g[1](Z[1])
Z[2]=w[2]A[1]+b[2]
A[2]=g2(Z[2])=σ(Z[2])
- Back propagation
dZ[2]=A[2]−Y
dw[2]=1mdZ[2](A[1])T
db[2]=1mnp.sum(dZ[2], axis=1, keepdims=True)
dZ[1]=(w[2])TdZ[2]∗g[1] ′(Z[1])
dw[1]=1mdZ[1]XT
db[1]=1mnp.sum(dZ[1], axis=1, keepdims=True)
3.10 直观理解反向传播
- 解决掉矩阵的维度匹配问题就能杜绝很多bug。
- 上图仅为一个样本时。
dZ即
∂L(a,y)∂z
输入特征数量:
n[0]=nx
隐藏单元个数:
n[1]
输出单元个数:
n[2]=1
那么,
X−−>(n[0], m)
W[1]、dW[1]−−>(n[1], n[0])
b[1]
、
db[1]−−>(n[1], 1)
Z[1]
、
dZ[1]−−>(n[1], m)
W[2]、dW[2]−−>(n[2], n[1])
b[2]
、
db[2]−−>(n[2], 1)
Z[2]
、
dZ[2]−−>(n[2], m)
dZ[1]=(w[2])TdZ[2]∗g[1] ′(Z[1])
的维度分别为:
(n[1], m)
=
(n[1], n[2])
(n[2], m)
∗(n[1], m)
dw[1]=1mdZ[1]XT
的维度分别为:
(n[1], n[0])
=
(n[1], m)
(m, n[0])
db[1]=1mnp.sum(dZ[1], axis=1, keepdims=True)
的维度分别为:
(n[1], 1)
3.11 随机初始化
如果不对参数w进行随机初始化,比如全部设置为0,那么,可以证明,所有隐藏层单元都在计算相同的函数,呈对称状态,没有意义。
所以需要对w随机初始化。
一般都将w随机成很小的值。因为如果激活函数是sigmoid或者tanh这样的,小的输入有大的梯度。如果w太大,很可能学习速率很慢。或者隐藏层没有用sigmoid或者tanh,但是是二分类问题,输出层用到了sigmoid,那么也不希望初始参数太大。
b设置成0不影响,但一般也进行随机初始化。
第四周 深层神经网络
4.1 深层神经网络
算网络层数时候不算输入层。
4.2 深层神经网络的前向传播
Z[l]=w[l]A[l−1]+b[l]
A[l]=g[l](Z[l])
4.3 核对 矩阵的维数
- 用一张纸算。
-
w[l]、dw[l]−−>(n[l], n[l−1])
-
b[l]、db[l]−−>(n[l], 1)
-
A[l]、z[l]、dA[l]、dz[l]−−>(n[l], m)
4.4 为什么使用深层表示
4.5 搭建深层神经网络块
z[l]
会被缓存下来到反向传播的时候使用。
一次完整的梯度下降过程:
反向传播到最后不需要计算
da[0]
4.6 前向传播和反向传播
- Forward propagation
输入:
A[l−1]
输出:
A[l]
缓存:
Z[l]、w[l]、b[l]
Z[l]=w[l]A[l−1]+b[l]
A[l]=g[l](Z[l])
Back propagation
输入:
dA[l]
输出:
dA[l−1]
、
dw[l]、db[l]
dZ[l]=dA[l]∗g[l] ′(Z[l])
dw[l]=1mdZ[l](A[l−1])T
db[l]=1mnp.sum(dZ[l], axis=1, keepdims=True)
dA[l−1]=(w[l])TdZ[l]
4.7 参数 vs 超参数
- 参数:
-
w[l]、b[l]
超参数:
- 学习速率
α
- 迭代次数 iterations
- 隐藏层的层数 L
- 隐藏单元数
n[l]
- 激活函数
- mini batch size
- momentum动量
- 几种不同的正则化参数。。。
- ……
超参数的选择,就是不断的尝试。
4.8 这和大脑有什么关系
没什么关系。
用神经元类比深度神经网络只是一个粗浅的类比。
一个神经元到底在干什么,没有人能够解释。
人到底是怎么学习的,也还是一个未解之谜。
深度学习是一个强大的工具,能学习到各种灵活、复杂的函数,来实现
x
到
y
的映射。
但和人类的学习比起来,不值一提。