1 参考
学习时间:2020-12-17 - 2020-12-18
神经网络和深度学习BY Andrew Ng.
2 个人评价
讲的很细致,比较适合初始初始入门,毕竟Andrew Ng连求导等都讲。
相比于西瓜书而言,我更喜欢西瓜书+南瓜书的推导,因为更有数学感,而Andrew Ng喜欢用简写进行推导,让我有些不适应。
但是,观看Andrew Ng的视频之后,发现他的讲解更加贴近编程,而西瓜书可能更贴近于理论。
Andrew Ng的视频会给你平时一些经验建议及想法,同时也会解释我在看西瓜书时产生的一些问题。
总的来说,尽管在看了其他一些视频和西瓜书之后,再看他的视频仍会有所收获。
个人推荐入门顺序:该视频第一周+第二周内容=》西瓜书+南瓜书(重在推导)=》该视频的其他内容过一遍查漏补缺
3 总结
由于视频&slides¬es都是开放的,且之前个人已经对一些概念推导有所了解,所以不在这里做详细的笔记,仅陈列一些自己新学到的东西。
- 对于不同的应用场景,学会抽象成合适的x与y
- 结构化数据与非结构化数据
- 结构化:通常用表格列出,每个特征都有清晰的定义
- 非结构化:如音频、图像、文本,让计算机理解起来很难
- NN的出现让计算机更好的理解非结构化数据了
- 为什么深度学习会兴起?
个人觉得这个图片真的很好解释了这个原因:
- 时代=》限制了大量数据的产生 以及 无需考虑到大数据的处理需求
- 模型自身问题=》无法处理大量数据/处理效果不好
- 当然,在比较小的训练集上,这些模型的排名不是绝对固定的
- logistic回归是一个用于二分类的算法,可以看作是一个非常小的NN
- 为了训练logistic回归模型的参数w和b=》loss(error) function(单个样本训练上得到),而cost function是衡量在全体训练样本上的表现;这两者都是用来衡量预测值和实际值的差异
关于loss function的选择?为什么是 L 2 {L_2} L2而不是 L 1 {L_1} L1??
L 1 ( y ^ , y ) = 1 2 ( y ^ − y ) 2 {L_1(\hat{y},y)=\frac{1}{2}(\hat{y}-y)^2} L1(y^,y)=21(y^−y)2
L 2 ( y ^ , y ) = y l o g y ^ + ( 1 − y ) l o g ( 1 − y ^ ) {L_2(\hat{y},y)=ylog\hat{y}+(1-y)log(1-\hat{y})} L2(y^,y)=ylogy^+(1−y)log(1−y^)
因为 L 1 {L_1} L1是非凸的,且利用最大似然估计可以推出。
下面整理记录一下Andrew Ng的推导
已知:
y ^ = σ ( w T x + b ) {\hat{y}=\sigma(w^Tx+b)} y^=σ(wTx+b) where σ ( z ) = 1 1 + e − z {\sigma(z)=\frac{1}{1+e^{-z}}} σ(z)=1+e−z1
假设: y ^ = P ( y = 1 ∣ x ) {\hat{y}=P(y=1|x)} y^=P(y=1∣x)
i f y = 1 : P ( y ∣ x ) = y ^ i f y = 0 : P ( y ∣ x ) = 1 − y ^ {if \quad y=1:P(y|x)=\hat{y}} \\ {if \quad y=0:P(y|x)=1- \hat{y}} ify=1:P(y∣x)=y^ify=0:P(y∣x)=1−y^
构造一个函数符合上述的条件:
P ( y ∣ x ) = y ^ y ( 1 − y ^ ) ( 1 − y ) {P(y|x)=\hat{y}^y(1-\hat{y})^{(1-y)}} P(y∣x)=y^y(1−y^)(1−y)
因为log函数是严格单调递增的:
l o g P ( y ∣ x ) = l o g y ^ y ( 1 − y ^ ) ( 1 − y ) = y l o g ( y ^ ) + ( 1 − y ) l o g ( 1 − y ^ ) = − L ( y ^ , y ) {logP(y|x)=log\hat{y}^y(1-\hat{y})^{(1-y)}} \\ {=ylog(\hat{y})+(1-y)log(1-\hat{y})} \\ {= -L(\hat{y},y)} logP(y∣x)=logy^y(1−y^)(1−y)=ylog(y^)+(1−y)log(1−y^)=−L(y^,y)
分析:想要loss function尽可能小,则需要 l o g P ( y ∣ x ) {logP(y|x)} logP(y∣x)尽可能大
使用最大似然估计(求出一组参数使得这个式子取最大值)(m个样本):
l o g P ( l a b e l s i n t r a i n i n g s e t ) = l o g Π i = 1 m P ( y ( i ) ∣ x ( i ) ) l o g P ( . . . ) = ∑ i = 1 m l o g P ( y ( i ) ∣ x ( i ) ) = − ∑ i = 1 m L ( y ^ ( i ) , y ( i ) ) {log P(labels in training set)=log \Pi_{i=1}^{m} P(y^{(i)} | x^{(i)} )} \\ {log P(...)=\sum_{i=1}^m logP(y^{(i)} | x^{(i)}) } \\ {= - \sum_{i=1}^m L(\hat{y}^{(i)} ,y^{(i)} )} logP(labelsintrainingset)=logΠi=1mP(y(i)∣x(i))logP(...)=i=1∑mlogP(y(i)∣x(i))=−i=1∑mL(y^(i),y(i))
最终cost function:
J ( w , b ) = 1 m ∑ i = 1 m L ( y ^ ( i ) , y ( i ) ) {J(w,b)=\frac{1}{m} \sum_{i=1} ^m L(\hat{y}^{(i)} ,y^{(i)} )} J(w,b)=m1i=1∑mL(y^(i),y(i))
分析:
- 去掉负号是因为需要最小化cost function
- 求和前面的 1 m {\frac{1}{m}} m1是对cost function进行适当的缩放
- 利用计算图模型进行向前传播(计算出预测值)与反向传播(求梯度)的推导。什么是计算图模型?(这里引用了慕课网的一个课程中的讲解)
命令式编程不是不可以用来实现神经网络模型,而是更麻烦
-
标准BP:每一次遍历所有特征,所有样本,计算梯度;
即需要显示调用两次for循环=》效率下降=》向量化 -
去掉显式for循环,python的numpy库中内置函数可以充分利用并行化进行加速运算
-
python中的broadcast:
详情参见numpy文档,搜索broadcast
常见使用:
- 把数字自动转为向量,再进行运算
- 若一个matrix维度为(m,n)分别对(1,n)与(m,1)的矩阵进行 + − ∗ / {+ - * /} +−∗/的操作,则两个矩阵先通过copy扩展成(m,n)矩阵。
-
编程中的小tips:
-
神经网络经典三层:输入层、隐藏(含)层、输出层
隐含层:在训练集中,这些中间节点的真正数值我们是不知道的,在训练集中看不到它们的数值
不把输入层看作一个标准的层,可以称为0层 -
从单个样本的向量化到多个样本的向量化,实际上是将原来的向量堆砌成矩阵。而验证堆砌后的矩阵运算的正确性,即可以从一个样本的第一层进行解释。然后验证同一层的所有样本结果正确,最后每一层的步骤重复也是正确的。
-
关于激活函数,我们常常会有以下几个问题,而在该视频中Andrew Ng都直观的解释了一下:
如何选用合适的激活函数?
常用的激活函数有:
- tanh(z):是sigmoid函数平移后的效果,在隐藏层中,几乎所有场合都更优于sigmoid函数,因为输出范围为 [-1,1] 使得一层的数据的平均值更接近0
- sigmoid(z):当二分类的时候,希望输出为0或者1,这时候可以使用sigmoid函数作为输出层的激活函数
- ReLU(Rectified Linear Unit):为了解决sigmoid和tanh函数梯度很小(学习速度较慢)的情况而更加常用,但是不可微,当为负数的时候,导数为0(尽管这种情况会很少出现)
- Leaky ReLU :解决ReLU函数负数时导数为0的情况
tanh和sigmoid函数的共同缺点:
当z特别大或者特比小的时候,导数的梯度可能就很小,会拖慢梯度下降算法
选择激活函数的一些经验法则:
如果输出为0或者1(二分类),那么sigmoid函数很适合做输出层的激活函数,然后,其他所有单元都用ReLU为什么需要非线性激活函数?
如果去掉或者使用线性激活函数,则输出则变成输入特征的线性组合。而如果一直在做特征的线性组合,那么不如去掉隐藏层。
如果需要用如g(z)=z等线性函数,那么只有在你要机器学习的是回归问题,或者压缩相关问题
- 不同层的激活函数可以不同
- 对于logistic回归,可以将权重全部初始化为0,但是如果将NN的全部参数数组初始化为0,再使用梯度下降算法,那么会完全无效。
事实上,b设置为0是可以的,但是w全部设置为0就会有问题:
=》无论训练多久,因为完全对称性,多个隐藏单元永远在计算相同的东西,是没有意义的,因为你需要不同的隐藏单元去计算不同的东西,对最终的输出产生不同的影响。
通常会将权重矩阵中的元素值设置为小数(如单隐层:0.01)。因为如果用tanh或者sigmoid激活函数,或者在输出层有一个sigmoid函数,如果W很大,那么Z就会很大,很可能落在平缓的部分,从而一开始就减慢学习速度。
如果没有用tanh或者sigmoid函数,可能问题还不是很大。
- NN的深浅:深浅只是相对的,logistic是一个shadow NN
- 为什么需要深层NN表示?
对于深层网络来说,较早的前几层可以学习一些低层次的简单特征,而后几层可以将简单的特征结合起来,去探测更加复杂的东西。
使用n个数的异或的例子可以知道:有些情况下使用浅层网络会使得隐藏层的单元数量呈指数式的增长。
- 关于参数与超参
超参:可以最终控制/决定参数的变化
比如:有些激活函数只能在深层上训练表现比较好,因此选用网络层数可以当做一个超参
常见超参:
- 核对矩阵维数 :
4 解惑
在评论区中看到有人发了一些问题,自己进行思考,在这里给出自己的答案。
Q1:
根据logistic回归在m个样本上的应用,第i个输入的dw,对每个j样本求和(根据cost function 可以推导):
d
w
i
=
∂
J
(
w
,
b
)
∂
w
i
=
1
m
∑
j
=
1
m
∂
L
(
a
(
j
)
,
y
(
j
)
)
∂
w
i
=
1
m
∑
j
=
1
m
d
w
i
(
j
)
{ dw_i = \frac{\partial J(w,b)}{ \partial w_i} = \frac{1}{m} \sum_{j=1}^m \frac{\partial L( a^{(j)}, y^{(j)} ) }{\partial w_i} } \\ {= \frac{1}{m} \sum_{j=1}^m dw_i^{(j)} }
dwi=∂wi∂J(w,b)=m1j=1∑m∂wi∂L(a(j),y(j))=m1j=1∑mdwi(j)
而之前对于单个样本的dw,已经可以推导出:
d
w
i
=
x
i
⋅
d
z
{dw_i = x_i \cdot dz}
dwi=xi⋅dz
因此,写成带有for循环的代码为:
for j in range(m):
dwi += xi[j]*dz
dwi /= m
而这里,向量化为了去掉for循环而直接求m个样本的和,加入了转置:
而最终的结果确实为(n,1),且第i个元素代表
d
w
i
{dw_i}
dwi。
确实十分巧妙。
Q2:
我觉得大部分让人看起来别扭,一时之间没有想到推导的原因在于他的简写推导。这个实质上是跟之前单个样本的操作是一样的:
已知:A就是原来logistic回归公式中相对应的X
∂
J
∂
Z
[
1
]
=
∂
J
∂
Z
[
2
]
∂
Z
[
2
]
∂
A
[
1
]
∂
A
[
1
]
∂
Z
[
1
]
=
d
Z
[
2
]
⋅
W
[
2
]
⋅
g
′
(
Z
[
1
]
)
{\frac{ \partial J}{\partial Z^{[1]}} = \frac{ \partial J}{\partial Z^{[2]}} \frac{ \partial Z^{[2]}}{\partial A^{[1]} } \frac{ \partial A^{[1]}}{\partial Z^{[1]}} } \\ {= dZ^{[2]} \cdot W^{[2]} \cdot g'(Z^{[1]}) }
∂Z[1]∂J=∂Z[2]∂J∂A[1]∂Z[2]∂Z[1]∂A[1]=dZ[2]⋅W[2]⋅g′(Z[1])
这里应该是经过了转置处理。
Q3:
我认为,确实
g
[
1
]
(
z
[
1
]
)
{g^{[1]}(z^{[1]}) }
g[1](z[1])的导数是一个维度为
(
n
[
1
]
,
1
)
{(n^{[1]} ,1) }
(n[1],1)的矩阵,可以看Andrew Ng后来的笔记:
这里最后成为两个维度为
(
n
[
1
]
,
1
)
{(n^{[1]} ,1) }
(n[1],1)的矩阵相乘,其实也就是把后面矩阵第i个元素值乘到前面的矩阵的第i个元素上的值,作用类似一个常数吧。