DeepLearning.AI深度学习课程笔记C1
2018-03-31 09:41 by ☆Ronny丶, ... 阅读, ... 评论, 收藏, 编辑Course1-Week1
这一周主要是对课程的介绍,以及简单的介绍了一下神经网络的概念。
什么是神经网络?
It is a powerful learning algorithm inspired by how the brain work.
神经网络这个概念实际上很早就提出来了,当初提出来的时候确实是受到了一些神经学的影响,它试图去模拟人脑神经元之间信号的传递。
课程中通过了一个房价预测的例子,说明了如何建立一个简单的神经网络的模型来进行房价的预测,介绍了神经网络的输入、输出和基本的神经元结构,以及神经元可以组成层次结构(课程中通过组合房子的特征来构建了一层隐藏层,非常的make sense),顺便引入了RELU激活函数的概念。
监督学习
课程随后便引出了监督学习的概念,这个机器学习中现在取得成就最大的方向。监督学习的问题中,我们一般都有一个已经知道答案(输入有对应的输出)的数据集,然后试图找到输入出输出之间的某种内在的关系,并对这个关系来预测一些不知道答案的输入。
监督学习的问题可以分为回归与分类两种,回归问题中我们预测的结果一般是一个连续的输出,而分类问题中,我们输出的是类别,是离散的。课程中举了一些监督学习的例子,来说明哪些问题是回归问题,哪些是分类问题。这些监督问题可以用不同的神经网络模型求解,比如图像类的可以用CNN模型,声音、文本序列类的输入可以选择RNN模型。
最后介绍了结构化数据与非结构化数据的区分,我觉得这是很重要的一个知识点,很多人都分不清楚。结构化数据每一笔数据,有明显的特征来表示,可以被关系数据库记录,一笔数据就是一个recrod,数据的每一维特征都是一个列。而非结构化数据没有清楚的特征来表达,比如一个文本,一段声音信号,一张图片。
为什么深度学习会如此的流行
三方面的原因:
- 大量数据的出现。互联网环境下,大量的sensor采集到的数据可以在互联网上传播分享。
- GPU引起的算力的大幅度提升,给深层的网络运行带来可能,给炼丹师们快速尝试新的想法,快速迭代提供了方便。
- 一些新的算法上的创新与突破。新的激活函数、新的正则化方向、新的优化方法、新的神经网络结构等等。
Course1-Week2
这一周主要介绍了神经网络中的一个基本的神经元的结构,也就是logistic回归单元,介绍了交叉熵loss,以及如何使用梯度下降法进行训练。课程花了很大的精力在介绍向量化,使用一些编程语言提供的向量化的便利,可以让我们纺写的程序运行速度大幅度加快。
用Logistic回归来解决小猫分类问题**
猫的图片的分类问题是一个典型的二分类的问题,特别适合用logistic回归来解决。对于图片这类的输入,一般是把一个图片中的所有像素值展开成一个一维的列向量。比如一个6464的三通道RGB小猫图片,作为神经网络输入时,就转化为一个122881的列向量。
逻辑回归的激活函数一般选择Sigmoid函数,所以整个输入-输出映射为:
\[\hat{y} = \sigma(w \cdot x + b) = \frac{1}{1 + e^{-(w\cdot x + b)}}\]
输出\(\hat{y}\)代表了输入\(x\)分类为类别1的概率。
逻辑回归的缺失函数选择的是交叉熵损失函数,至于为什么选择交叉熵损失函数,其实是因为对于样本分类假设符合伯努力分布,然后用最大似然推算出来的。
\[J(w, b) = =\frac{1}{m}\sum_{i=1}^m\left[(y^{(i)}\log(\hat{y}^{(i)}) + (1 - y^{(i)})\log(1 - \hat{y}^{(i)}))\right]\]
定义好模型及损失函数后,我们就可以利用梯度下降法对模型的参数进行求解。对任意的参数\(w_i\),它的更新公式为:
\[w_i:=w_i - \alpha \frac{\partial L_w(x)}{\partial w_i}\]
梯度下降的核心求损失函数对参数的梯度\(\frac{\partial L_w(x)}{\partial w_i}\),这需要一定的微积分的知识,尤其是偏导数、链式法则等知识。课程中介绍了计算图的概念来方便们理解对于复杂的函数,尤其是多层的神经网络如何来求梯度。
对于计算图讲解的比较好的一篇博客。
向量化
向量化主要是针对于编码实现神经网络而言的,尤其是我们在使用一些解释型语言,如Python的时候,在处理大的矩阵运算时,如果使用循环,那效率就很低,因为解释器本身的问题。而如果我们使用一些向量化的函数,那底层可以直接调用C/C++来运行,效率自然很高。
Python中的向量化,主要是利用Numpy这个科学计算库,它提供了大量的多维数组的运算函数。对于logistic回归,我们可以进行如下的向量化
# 向前计算
X = np.random.normal(size=(256, 10000)) # X为数据集,样本个数为10000,特征维数为256
y = np.random.normal(size=10000)
w = np.random.normal(size=256)
b = np.random.normal(size=1)
Z = np.dot(w, X) + b # 这里用到了python的broadcast机制
y_hat = 1 / (1 + np.exp(-Z))
# 反向求导
dZ = y_hat - y
dw = np.dot(X, dZ) / X.shape
db = np.mean(dZ)Course1-Week3
来到第三周的课程,开始比较正式的介绍神经网络。网络的结构是如何的,用符号怎么表示,向前计算,反向传播等。
神经网络的表示
之前已经介绍过了神经网络的基本单元Logistic回归,把它看是一个神经元,神经网络可以由大量这样的神经元组成一个层次结构。神经网络从大的部件上来看按层来组织,一层有若干个神经元,层与层之前通过连接来表示计算关系。在全连接网络中,前一层所有神经元的输出,作为当前层每一个神经元的输入。
下图展示的是一个二层神经网络的结构。
把最开始的输入称为输入层,最后一层称为输出层,中间的层称为隐藏层。Andrew课程我觉得非常有价值的一点是,给神经网络的符号化表示,给出了一个自己的思路,而且比较的合理。
- 用\(\mathbb{x}\)来表示一个样本,它可以表示为一个特征向量$ = [x_1, x_2,\cdots, x_n]$
- 第\(i\)个样本表示为\(\mathbb{x}^{(i)}\),对应的lablel为\(y^{(i)}\)
- m个样本的特征可以用矩阵\(X\)来表示,其中每一列就是一个单独的样本,\(X\)是一个n行m列的矩阵。
- 用\(L^{[l]}\)来表示神经网络的第\(l\)层。
- 每一层全连接层都有对应的参数\(W\),第\(l\)层的参数为\(W^{[l]}\)
- 对于第\(l\)层网络,它的神经元在未经过激活函数作用之前,表示为\(\mathbb{z}^{(l)}\),它是一个向量,向量的长度等于当前层神经元的个数。将所有样本在该层的计算结果放在一起,表示为\(Z^{[l]}\),它是一个m行k列的矩阵,k为当前层神经元的个数。
- 对于第\(l\)层网络,它的神经元在激活函数作用之后,表示为\(\mathbb{a}^{(l)}\),它是一个向量,向量的长度等于当前层神经元的个数。将所有样本在该层的计算结果放在一起,表示为\(A^{[l]}\),它是一个m行k列的矩阵,k为当前层神经元的个数。
神经网络的计算
有了前面介绍的神经网络的表示后,我们可以很容易把整个神经网络的计算部分表示为矩阵运算的形式。
第一层神经网络的输出:
\[Z^{[1]} = W^{[1]}\cdot X + \mathbb{b}^{[1]} \\
A^{[1]} = \sigma(Z^{[1]})\]
第二层神经网络的输出:
\[Z^{[2]} = W^{[2]}\cdot A^{[1]} + \mathbb{b}^{[2]} \\
A^{[2]} = \sigma(Z^{[2]})\]
...
最后一层的输出为:
\[Z^{[L]} = W^{[L]}\cdot A^{[L-1]} + \mathbb{b}^{[L]} \\
\mathbb{y} = A^{[L]} = \sigma(Z^{[L]})\]
激活函数
之前介绍的都是Sigmoid作为激活函数,它可以很好的把输出结果控制在[0, 1]之间,可以从概率的角度去解释。但是如果神经网络中间层的激活函数都用Sigmoid函数,就会有两方面的问题:
- Sigmoid输出范围[0, 1]导致,中间层的输出均值不为0。
- Sigmoid函数在z很小或很大的时候,梯度基本为0,会导致梯度消失的问题。
那还有其他的激活神经元可以选择吗?
- tanh
- ReLU
- Leaky ReLU
而且这些激活函数的导数都是很容易求的。这里就不多介绍了。
如果没有非线性的激活函数呢?
为什么一定要用激活函数,能不能不用激活函数呢?答案是否定的,如果不用激活函数,就想当于激活函数是一个等价映射,那神经网络的输出就变成了:
\[y = W^{[L]}\cdot(W^{[L-1]}\cdot(\cdots(W^{[1]}\cdot X | b^{[1]})\cdots) + b^{[L-1]}) + b^{[L]} \\
= W^{[L]}\cdot W^{[L-1]}\cdot \cdots W^{[1]}\cdot X + \cdots\]
这是一个线性的函数,并没表示复杂映射关系的能力。
神经网络的反向传播
在有了神经网络向前计算的矩阵表达后,再来看神经网络的反向传播是很容易的,只要我们理解计算图,理解对矩阵微分的一些基础知识。
我们如果知道了\(dZ^{[l+1]}\),那么根据
\[Z^{[l+1]} = W^{[l+1]}\cdot A^{[l]} + \mathbb{b}^{[l+1]}\\
A^{[l]} = \sigma(Z^{[l]}) \\
Z^{[l]} = W^{[l]}\cdot A^{[l-1]} + \mathbb{b}^{[l]}\]
可以得到
\[dZ^{[l]} = (W^{[l+1]})^T\cdot dZ^{[l+1]}\cdot\sigma'(Z^{[l]})\\
dW^{[l]} = \frac{1}{m}dZ^{[l]}\cdot (A^{[l-1]})^T\\
d\mathbb{b}^{[l]} = \sum_{i=1}^m dZ^{[l]}/ m, \text{ 每一行相加再除m}\]
实际上最后一层\(dZ^{[l+1]}\)是很容易求的,那依次用上面的公式,便容易求出来,每一层的参数。
参数的随机初始化
一般情况下,对于神经网络中的参数我们都选择用一些随机接近0的值去初始化。那参数能不能直接初始化为0呢?答案也是否定的。
当所有有参数都初始化为零时,会导致最开始向前计算过程中,所有层的神经元激活值都是零(一样)。因为神经网络的结构从横向上看是对称的,所以所有权重的梯度值也都是一样的。这就导致了所有参数同步更新,幅度一样。 下一次再计算中间的神经元时,同层神经元的激活值还是一样的。这样的网络整个学习很力是很差的,每一层有再多的结点都没用,只相当于一个。
Course1-Week4
课程的第四周开始由神经网络过滤到深度神经网络上来了,但Andrew在课程中非常实际的讲到,他觉得深度学习就是隐藏层多一些的神经网络,旧酒装新瓶,概念包装换学术PR行为。
深度神经网络的表达与计算
开始一始讲到在深度神经多络中如何用符号去表示神经网络年各个部分,各个层,层的参数、激活值等,如何用向量的形式表达。这部分内容和第3课的内容有点重复,区别就是第三课中我们讨论的还是2层或3层的网络,而这里讨论的网络可能有很多层。
正向计算与反向传播的公式都可以参考上面,已经很详细了。值得一提的是课程中告诉了我们一个推导神经网络正向与反向计算时,如果用矩阵的形式时,可以用矩阵维度来校验我们的公式。事实上,我自己平时在推导时,也是采用这个方法来取巧。
举个例子:\(Z = W\cdot X + b\),其中W为维度为50*256,X的维度为256*1000,Z的维度就是50*1000。这时,如果我们知道了dZ(501000)求\(dW\)(50256)。我们可能忘记了公式,模糊中记得dw有几下几种可能的组合dZ*X、dZ*X^T、X*dZ、X^T*dZ。那我们只需要用维度核验一下,就知道dZ*X^T才是正确的。
为什么要"Deep"
我觉得这是一个非常好的问题,任何初学神经网络或深度学习的人都会产生这样的疑问,而且在很多地方都可以看到这样的说法:“拥用一个隐藏层神经网络就可以逼近任意函数”。既然就这样的说明,为什么我们不用一个二层的神经网络就够了呢,中间多加一些神经元而已嘛。
最直观的原因就是人们在实验中发现深度的神经网络直的有效,瘦长的网络比宽扁的网络在很多任务上表现的更好。其次人们也尝试去从一些视觉分层的原理上解释,这种解释在图片上是比较成功的。还有一些从电路原理解释的角度。个人感觉不是很靠谱。
超参数的问题
一个深度的神经网络在构建和训练时们将面临很多超参数的选择:
- 参数学习率
- 训练的迭代次数
- 网络到底要多深
- 每一层隐藏层需要多少个神经元
- 激活函数选择什么类型
等等,还有很多就不一一列举了。如何去选择这些超参数呢:经验!没错,只能靠不断的去实验,不断的尝试来总结经验。这也是为什么深度学习工程师被戏谑为“调参侠”。
深度学习与大脑有什么关系
一毛钱关系都没有!结束!!
还是多说几句,当初在设计感知机、logistic回归单元的时候多少是有点参考了生物神经元的结构与特性,有突触来接收信号,刺激在超过一门限时,该神经元就会被激活,然后信号延着轴突传递。但我们完全搞不明白神经元是如何学习的,大脑也不可能像我们上面那样计算反向传播。