Neural Networks and Deep Learning
week1 深度学习概论
1.1欢迎
1.2 什么是神经网络
Relu recity:取不小于0的值
我们把房屋的面积作为神经网络的输入(我们称之为x ),通过一个节点(一个小圆圈),最终输出了价格(我们用y 表示)。其实这个小圆圈就是一个单独的神经元。
神经网络当你实现它之后,你要做的只是输入 x,就能得到输出 y。因为它可以自己计算你训练集中样本的数目以及所有的中间过程。
所以,你实际上要做的就是:这里有四个输入的神经网络,这输入的特征可能是房屋的大小、卧室的数量、邮政编码和区域的富裕程度。给出这些输入的特征之后,神经网络的工作就是预测对应的价格。
只要给出足够多的(x,y)神经网络非常擅长计算从 x 到 y 的精准映射函数。
1.3 用神经网络进行监督学习
到目前几乎所有由神经网络创造的经济价值,本质上都离不开监督学习。
在监督学习中,输入 x ,学习一个函数来映射到一些输出 y ,比如我们之前提到的房价预测的例子,你只要输入有关房屋的一些特征,估计价格 y 。
深度学习系统通过智能的选择,哪些作为 x 哪些作为 y,来针对于你当前的问题拟合监督学习部分。
-
对于图像应用,CNN。
-
对于序列数据,RNN。
-
结构化数据:数据库,每个特征都有一个很好的定义。
-
非结构化数据:比如音频,原始音频或者你想要识别的图像或文本中的内容。这里的特征可能是图像中的像素值或文本中的单个单词。
1.4 为什么深度学习会兴起
仅仅在过去的20年里对于很多应用,我们便收集到了大量的数据,远超过机器学习算法能够高效发挥它们优势的规模。
- 如果你训练一个小型的神经网络,黄色曲线
- 如果你训练一个稍微大一点的神经网络,比如说一个中等规模的神经网络,蓝色曲线
- 如果你训练一个非常大的神经网络,绿色曲线,并且保持变得越来越好。
水平轴:所有任务的数据量。 垂直轴:机器学习算法的性能。
它的性能一开始在增加,更多数据时会上升,但是一段变化后它的性能就会像一个变平。它们不知道如何处理规模巨大的数据,而过去十年的社会里,我们遇到的很多问题只有相对较少的数据量。
因此可以注意到两点:
如果你想要获得较高的性能体现,那么你有两个条件要完成:
- 训练一个规模足够大的神经网络,以发挥数据规模量巨大的优点
- 很多的数据
神经网络方面的一个巨大突破是从sigmoid函数转换到一个ReLU函数。
-
使用sigmoid函数,在两侧,函数的梯度会接近零,当你实现梯度下降时,参数会更新的很慢,学习的速率也会变的很慢
-
ReLU它的梯度对于所有输入的负值都是零,因此梯度更加不会趋向逐渐减少到零。
1.5 关于这门课
第一门课:神经网络与深度学习,教会你最重要的基础知识。当学习到第一门课末尾,你将学到如何建立一个深度神经网络。
-
第一周:关于深度学习的介绍。在每一周的结尾也会有十个多选题用来检验自己对材料的理解;
-
第二周:关于神经网络的编程知识,了解神经网络的结构,逐步完善算法并思考如何使得神经网络高效地实现。从第二周开始做一些编程训练(付费项目),自己实现算法;
-
第三周:在学习了神经网络编程的框架之后,你将可以编写一个隐藏层神经网络,所以需要学习所有必须的关键概念来实现神经网络的工作;
-
第四周:建立一个深层的神经网络。
week2 神经网络基础
2.1 二元分类
神经网络的训练过程可以分为前向传播和反向传播两个独立的部分
为了保存一张图片,需要保存三个矩阵,它们分别对应图片中的红、绿、蓝三种颜色通道,如果你的图片大小为64x64像素,那么你就有三个规模为64x64的矩阵,分别对应图片中红、绿、蓝三种像素的强度值。
2.2 logistic 回归
逻辑回归的Hypothesis Function(假设函数)。
对于二元分类问题来讲,给定一个输入特征向量\(X\) ,它可能对应一张图片,你想识别这张图片识别看它是否是一只猫或者不是一只猫的图片,你想要一个算法能够输出预测,你只能称之为\(\hat{y}\)(对实际值y的估计)
想让 \(\hat{y}\) 表示为实际值y等于1的机会,\(\hat{y}\)应该在[0,1],但是\(w^{T}x+b\)可能会比1大很多,或者是负数。所以输出\(\hat{y}\)应该是sigmoid(\(w^{T}x+b\)),将线性函数转换为非线性函数。
2.3 Logistic 回归损失函数
为什么需要代价函数:
为了训练逻辑回归模型的参数 w 和参数 b ,我们需要一个代价函数,通过训练代价函数来得到参数 w 和参数 b 。
先看一下逻辑回归的输出函数:
为了让模型通过学习调整参数,你需要给予一个 m 样本的训练集,这会让你在训练集上找到参数 w 和参数 b,来得到你的输出。
损失函数:
损失函数又叫做误差函数,用来衡量算法的运行情况,Loss function:\(L(\hat{y},y)\)
我们通过这个称为L的损失函数,来衡量预测输出值和实际值有多接近。一般我们用预测值和实际值的平方差或者它们平方差的一半,但是通常在逻辑回归中我们不这么做,因为当我们在学习逻辑回归参数的时候,会发现我们的优化目标不是凸优化,只能找到多个局部最优值,梯度下降法很可能找不到全局最优值,虽然平方差是一个不错的损失函数,但是我们在逻辑回归模型中会定义另外一个损失函数。
我们在逻辑回归中用到的损失函数是:
\[L(\hat{y}^,y)=−ylog(\hat{y})−(1−y)log(1−\hat{y}) \]在这门课中有很多的函数效果和现在这个类似,就是如果 y =1,我们就尽可能让$\hat{y} \(变大,如果等 y=0,我们就尽可能让\)\hat{y} $ 变小。
损失函数是在单个训练样本中定义的,它衡量的是算法在单个训练样本中表现如何,为了衡量算法在全部训练样本上的表现如何,我们需要定义一个算法的代价函数,算法的代价函数是对 m个样本的损失函数求和然后除以 m:
损失函数只适用于像这样的单个训练样本,而代价函数是参数的总代价,所以在训练逻辑回归模型时候,我们需要找到合适的 w和 b ,来让代价函数 J 的总代价降到最低。
2.4 梯度下降法
在你测试集上,通过最小化代价函数(成本函数) J(w,b) 来训练的参数 w 和 b ,
如图,在第二行给出和之前一样的逻辑回归算法的代价函数(成本函数)
梯度下降法的形象化说明
代价函数(成本函数)J(w,b) 是在水平轴w和b上的曲面,因此曲面的高度就是J(w,b) 在某一点的函数值,我们要做的就是找到使代价函数J(w,b)的值最小时的参数w和b
如图,代价函数J(w,b)是一个凹函数,像一个大碗一样
如图,这就与刚才的图有些相反,因为它是非凸的,并且有很多的局部最小值。但是由于这个代价函数的特性,我们必须定义J(w,b) 为凸函数
朝着最陡的下坡方向走一步,不断的迭代
梯度下降法的细节化说明(仅有一个参数)
假定代价函数(成本函数J(w) 只有一个参数 w ,即用一维曲线代替多维曲线,这样可以更好画出图像。
- := 表示更新参数
- α表示学习率 learning rate,用来控制步长step,即向下走一步的长度\(\frac{dJ(w)}{dw}\)
对导数更加形象化的理解就是斜率slope,如图该点的导数就是这个点相切于 J(w) 的小三角形的高除宽。假设我们以如图点为初始化点,该点处的斜率的符号是正的,即 \(\frac{dJ(w)}{dw}\)>0,所以接下来会向左走一步
整个梯度下降法的迭代过程就是不断地向左走,直至逼近最小值点。
假设我们以如图点为初始化点,该点处的斜率的符号是负的,即 \(\frac{dJ(w)}{dw}\)<0所以接下来会向右走一步。
整个梯度下降法的迭代过程就是不断地向右走,即朝着最小值点方向走。
梯度下降法的细节化说明(两个参数)
逻辑回归的代价函数(成本函数)J(w,b) 是含有两个参数的。
2.5 导数
导数这个概念意味着斜率,导数听起来是一个很可怕、很令人惊恐的词,但是斜率以一种很友好的方式来描述导数这个概念。
所以提到导数,我们把它当作函数的斜率就好了。更正式的斜率定义为在上图这个绿色的小三角形中,高除以宽。即斜率等于0.003除以0.001,等于3。或者说导数等于3,这表示当你将 a 右移0.001,f(a) 的值增加3倍水平方向的量。
2.6 更多导数的例子
在这个视频中,你只需要记住两点:
第一点,导数就是斜率,而函数的斜率,在不同的点是不同的。在第一个例子中f(a)=3a ,这是一条直线,在任何点它的斜率都是相同的,均为3。但是对于函数 f(a)=a^2或者 f(a)=loga ,它们的斜率是变化的,所以它们的导数或者斜率,在曲线上不同的点处是不同的。
第二点,如果你想知道一个函数的导数,你可参考你的微积分课本或者维基百科,然后你应该就能找到这些函数的导数公式。
2.7 计算图
- 计算图组织计算的形式是用蓝色箭头从左到右的
- 计算反向红色箭头(也就是从右到左)的导数计算
2.8 计算图的导数计算
\[\frac{\mathrm{d}J}{\mathrm{d}u}=\frac{\mathrm{d}J}{\mathrm{d}v} \frac{\mathrm{d}v}{\mathrm{d}u} \]\[\frac{\mathrm{d}J}{\mathrm{d}b}=\frac{\mathrm{d}J}{\mathrm{d}u} \frac{\mathrm{d}u}{\mathrm{d}b} \]\[\frac{\mathrm{d}J}{\mathrm{d}a}=\frac{\mathrm{d}J}{\mathrm{d}v} \frac{\mathrm{d}v}{\mathrm{d}a} \]计算\(\frac{\mathrm{d}J}{\mathrm{d}v}\):
v = 11 -> v = 11.001 ,J = 33 ->J = 33.003变为3倍
=> \(\frac{\mathrm{d}J}{\mathrm{d}v}\)=3
计算\(\frac{\mathrm{d}J}{\mathrm{d}a}\):
a = 5 -> a=5.001, v = 11->v = 11.001, J = 33.003 变为3倍 改变a,会改变v,改变v也会改变J。
a增加了,v也会增加,v增加多少取决于\(\frac{\mathrm{d}v}{\mathrm{d}a}\),v的变化导致J增加,这里在微积分中实际上是链式法则。J的变化量实际上就是【改变a时候v的变化量】*【改变v时候J的变化量】
a增加0.001,v也变化相同的大小,故\(\frac{\mathrm{d}v}{\mathrm{d}a}\)=1。故\(\frac{\mathrm{d}J}{\mathrm{d}a}\)=\(\frac{\mathrm{d}v}{\mathrm{d}a}\)*\(\frac{\mathrm{d}J}{\mathrm{d}v}\)=1✖️3 =3
一个新的符号约定:输出变量对某个变量的导数,我们就用dvar 命名
计算\(\frac{\mathrm{d}J}{\mathrm{d}u}\),同上,结果为3
计算\(\frac{\mathrm{d}J}{\mathrm{d}b}\),b =3 => b=3.001, u =6 => u=6.002, v = 11 = > v=11.002 ,J = 33 => 33.006 ,故b增加0.001,J就会增加0.006 ,\(\frac{\mathrm{d}J}{\mathrm{d}b}\) = 6
当计算所有这些导数时,最有效率的办法是从右到左计算,跟着这个红色箭头走。特别是当我们第一次计算对 v的导数时,之后在计算对 a导数就可以用到。
2.9 Logistic 回归的梯度下降法
假设样本只有两个特征\(x_1\)和\(x_2\),为了计算z,我们需要输入参数\(x_1\)、\(x_2\)、\(w_1\)、\(w_2\)、b
\[z = w_1x_1 + w_2x_2 +b \]逻辑回归的公式:
\[\hat{y} = a = \sigma(z) \]\[z = w^T + b \]\[\sigma(z) = \frac{1}{1+e^{-z}} \]损失函数:
\[L(\hat{y}^{(i)},y^{(i)}) = -y^{(i)}log \hat{y}^{(i)}-(1-y^{(i)})log(1 - \hat{y}^{(i)}) \]代价函数:
\[J(w,b) = \frac{1}{m} \sum^m_i L(\hat{y}^{(i)},y^{(i)}) \]只考虑单个样本,代价函数:
\[L(a,y) = -(ylog(a)+(1-y)log(1-a)) \]梯度下降法中,w和b的修正量:
\[w: = w - \alpha \frac{\partial{J(w,b)}}{\partial{w}} \]\[b: = b - \alpha \frac{\partial{J(w,b)}}{\partial{b}} \] \[\frac{dL(a,y)}{da} = \frac{-y}{a}+\frac{1-y}{1-a} \]\[\frac{dL(a,y)}{dz} = a(1-a) \]\[\frac{dL(a,y)}{dz} = (\frac{dL}{da})(\frac{da}{dz}) = a-y \]再反向推导,计算w和b变化对代价函数L的影响
\[\frac{\partial{L}}{\partial{w_1}} = x_1*dz \]\[db = dz \]1.使用dz = a-y计算dz
2.dw1 = x1 * dz 计算dw1,同理dw2 ,db = dz
然后更新w1 = w1-αdw1,dw2同理,b = b - α db
2.10 m 个样本的梯度下降
上面是计算导数以及应用梯度下降在逻辑回归的一个训练样本上,现在我们将其应用在m个训练样本上
\[J(w,b) = \frac{1}{m} \sum^m_i L(a^{(i)},y^{(i)}) \]现在是带有求和的全局代价函数,是1到m项各个损失的评价,表明全局代价函数对于\(w_1\)的微分,对\(w_1\)的微分也同样是各项损失对\(w_1\)微分的平均
2.11 向量化
在逻辑回归中,你需要去计算 \(z = w^T + b\),w、x都是列向量,如果你有很多的特征,那么就会有一个非常大的向量。
x
z=0
for i in range(n_x)
z+=w[i]*x[i]
z+=b
这是一个非向量化的实现,你会发现这真的很慢,作为一个对比,向量化实现将会非常直接计算\(w^Tx\),代码如下:z=np.dot(w,x)+b,计算速度非常快
CPU和GPU都有并行化的指令,他们有时候会叫做SIMD指令,这个代表了一个单独指令多维数据,这个的基础意义是,如果你使用了built-in函数,像np.function或者并不要求你实现循环的函数,它可以让python的充分利用并行化计算,这是事实在GPU和CPU上面计算,GPU更加擅长SIMD计算,但是CPU事实上也不是太差,可能没有GPU那么擅长吧。
2.12 向量化的更多例子
当我们在写神经网络程序时,或者在写逻辑(logistic)回归,或者其他神经网络模型时,应该避免写循环(loop)语句。虽然有时写循环(loop)是不可避免的,但是我们可以使用比如numpy的内置函数或者其他办法去计算。当你这样使用后,程序效率总是快于循环(loop)。
2.13 向量化逻辑回归
逻辑回归的前向传播步骤,如果你有m个训练样本,需要做m次来得到样本的预测值
\[z^{(i)} = w^Tx^{(i)}+b \]Z: 一个1 * m的矩阵,这是一个行向量
\[[z^{(1)}z^{(2)}...z^{(m)}] = w^{T} + [bb...b] \]b:一个1*m的矩阵,一个行向量
w:一个m*1列向量,\(w^{T}\)是一个行向量
利用 m 个训练样本一次性计算出小写 z 和小写 a ,用一行代码即可完成:
Z = np.dot(w.T,X) + b
利用向量化在同一时间内高效地计算所有激活函数的a
\[A = [a^{(1)},a^{(2)},...,a^{(m)}] = \sigma(Z) \]2.14 向量化逻辑回归的梯度输出
\(dZ\) : m维的行向量
在之前的计算中,我们已经去掉了一个for循环
但是还需要一个循环来遍历训练集:
\[db = \frac{1}{m}\sum^m_{i=1} dz^{(i)} \]在python中可以:
db = (1/m)*np.sum(dZ)
\[dw = \frac{1}{m}\sum^m_{i=1} x^{(i)}dz^{(i)}
\]在python中可以:
dw = (1/m)*X*dz.T
使用了前面五个公式完成了前向和后向传播,也实现了对所有训练样本进行预测和求导,利用后面两个公式,梯度下降更新参数。
2.15 python中的 广播
不同食物(每100g)中不同营养成份的卡路里含量表:
现在计算苹果中的碳水化合物卡路里百分比含量,首先计算苹果(100g)中三种营养成分卡路里总和56+1.2+1.8 = 59,然后用56/59 = 94.9%算出结果。
[!TIP]
A.sum(axis = 0)
中的参数axis
。axis用来指明将要进行的运算是沿着哪个轴执行,在numpy中,0轴是垂直的,也就是列,而1轴是水平的,也就是行。第二个
A/cal.reshape(1,4)
指令则调用了numpy中的广播机制。这里使用 3∗4 的矩阵 A 除以1∗4 的矩阵cal 。技术上来讲,其实并不需要再将矩阵 calreshape
(重塑)成 1∗4 ,因为矩阵 cal 本身已经是 1∗4 了。但是当我们写代码时不确定矩阵维度的时候,通常会对矩阵进行重塑来确保得到我们想要的列向量或行向量。重塑操作reshape
是一个常量时间的操作,时间复杂度是 O(1),它的调用代价极低。
一些广播的例子:
python广播中的通用规则:
当对两个 array 进行操作时,numpy 会逐元素比较它们的形状。从尾(即最右边)维度开始,然后向左逐渐比较。只有当两个维度 1)相等
or 2)其中一个维度是1
时,这两个维度才会被认为是兼容。
2.16 关于python numpy的说明
由于广播巨大的灵活性,有时候你对于广播的特点以及广播的工作原理这些细节不熟悉的话,你可能会产生很细微或者看起来很奇怪的bug。例如,如果你将一个列向量添加到一个行向量中,你会以为它报出维度不匹配或类型错误之类的错误,但是实际上你会得到一个行向量和列向量的求和。
首先设置 a=np.random.randn(5)
,这样会生成存储在数组 a 中的5个高斯随机数变量。之后输出 a,从屏幕上可以得知,此时 a的shape(形状)是一个(5,) 的结构。
这在Python中被称作一个一维数组。它既不是一个行向量也不是一个列向量,这也导致它有一些不是很直观的效果。举个例子,如果我输出一个转置阵,最终结果它会和 a看起来一样,所以 a 和 a 的转置阵最终结果看起来一样。而如果我输出 a 和 a 的转置阵的内积,你可能会想: a 乘以 a 的转置返回给你的可能会是一个矩阵。但是如果我这样做,你只会得到一个数。
请注意一个细微的差别,在这种数据结构中,当我们输出 a 的转置时有两对方括号,而之前只有一对方括号,所以这就是1行5列的矩阵和一维数组的差别。
当你在编程练习或者在执行逻辑回归和神经网络时,你不需要使用这些一维数组。
一个向量或许是column vector or row vector,但是绝对不会是一维数组
为了确保你的矩阵或向量所需要的维 数时,不要羞于 reshape 操作。
2.17 jupter (略)
2.18 Logistic 损失函数的解释
证明逻辑回归的损失函数为什么是这种形式。
在逻辑回归中,我们需要预测的结果\(\hat{y}\),可以表示为\(\hat{y} = \sigma(w^Tx+b)\),\(\sigma(z) = \sigma(w^Tx+b)=\frac{1}{1+e^{-z}}\)
我们约定\(\hat{y} = p(y = 1|x)\) 给定训练样本x条件下 y =1的概率。
换句话说,如果 y = 1,在给定训练样本x条件下 \(y = \hat{y}\)
y = 0, 在给定训练样本x条件下\(y = 1 - \hat{y}\)
故 \(\hat{y}\) 代表y = 1的概率,那么 \(1 - \hat{y}\) 就是 y = 0的概率。
上面的两个条件概率公式可以合并为如下公式:
\[p(y|x) = \hat{y}^y(1-\hat{y})^{(1-y)} \]第一种情况,假设 y = 1, 那么
\[p(y|x) = \hat{y}^y(1-\hat{y})^{(1-y)} = \hat{y}*1=\hat{y} \]第二种情况,假设 y = 0,那么
\[p(y|x) = \hat{y}^y(1-\hat{y})^{(1-y)} = 1*(1-\hat{y})^1 = 1 - \hat{y} \]由于 log函数是严格单调递增的函数,最大化log(p(y|x))等价p(y|x )最大化
\[log(p(y|x)) = log(\hat{y}^y(1-\hat{y})^{(1-y)}) =ylog \hat{y}+(1-y)log(1-\hat{y}) \]上面的式子就是我们提到的\(-L(\hat{y},y)\),前面有一个负号的原因是因为我们训练学习算法的时候,需要算法输出值的概率是最大的(以最大的概率预测这个值),然而在逻辑回归中我们需要最小化损失函数,因此最小化损失函数与最大化条件概率的对数关联起来了
那么在m个训练样本的整个训练集中:
假设所有的训练样本服从同一分布且相互独立,也就是独立同分布的,所有这些样本的联合概率就是每个样本概率的乘积。
\[P(labels \quad in \quad training \quad set) = \prod_{i=1}^mP(y^{(i)}|x^{(x)}) \]如果想要做最大似然估计,需要寻找一组参数,使得给定样本的观测值的概率最大,但令这个概率最大化,等价于令其对数最大化
\[\log{P(labels \quad in \quad training \quad set)} \\ = \log{\prod_{i=1}^mP(y^{(i)}|x^{(x)})} \\ = \sum_{i=1}^m \log{P(y^{(i)}|x^{(x)})} \\ = \sum_{i=1}^m-L(\hat{y}^{ (i)},y^{(i)}) \]在统计学中,有一个方法叫做最大似然估计,就是求出一组参数,让这个式子取最大值。但是我们训练模型的目标是让成本函数最小化,我们要去掉这里的负号
\[\mathrm{J(w,b)} = \frac{1}{m}\sum ^m_{i=1}L(\hat{y}^{(i)},y^{(i)}) \]week3 浅层神经网络
3.1 神经网络概览
如何实现神经网络:
首先需要输入特征x,参数w和b,通过这些计算出来z
然后计算出来a
\[\hat{y} -> a = \sigma(z) \]然后计算出loss function \(L(a,y)\)
神经网络看起来如下图:
可以将许多的sigmoid单元堆叠起来形成一个神经网络。
在这个神经网络中,首先计算第一层网络中各个节点相关的数\(z^{[1]}\),接着计算\(a^{[1]}\),计算下一层网络同理
这里使用\(^{[m]}\)表示第m层的神经网络节点相关的数,这些节点的集合被称为第m层网络。
\[\left.\begin{array}{c} \mathrm{x}^{[1]} \\ \mathrm{W}^{[1]} \\ \mathrm{b}^{[1]} \end{array}\right\} \longrightarrow z^{[1]} = W^{[1]}x+b^{[1]} \longrightarrow \sigma(z^{[1]}) \]\[\left.\begin{array}{c} \mathrm{a}^{[1]} \\ \mathrm{W}^{[2]} \\ \mathrm{b}^{[2]} \end{array}\right\} \longrightarrow z^{[2]} = W^{[2]}a^{[1]}+b^{[2]} \longrightarrow \sigma(z^{[2]}) \\ \longrightarrow L(a^{[2]},y) \]类似于逻辑回归,但是在计算后需要使用另外一个线性方程对应的参数来计算\(z^{[2]}\),计算后得到的\(a^{[2]}\)就是整个神经网络的输出
3.2 神经网络表示
隐藏层:使用监督学习训练的时候,训练集包含了输入x和目标输出y,在训练集中,这些中间节点的准确值我们是不知道的。
\(a^{[0]}\)(输入层的激活值):表示输入特征,a是激活的意思,输入层将x传递给隐藏层
\(a^{[1]}\)(隐藏层的激活值)
\(a^{[1]}_{1}\) 第一个单元的第一个节点,\(a^{[2]}_{2}\) 第一个单元的第二个节点...
\[a^{[1]} = \begin{bmatrix} a^{[1]}_{1} \\ a^{[1]}_{2} \\ a^{[1]}_{3}\\ a^{[1]}_{4}\\ \end{bmatrix} \]输出层产生数值a,这里只是一个单独的实数,故 \(\hat{y}\) 的值将会被取为 \(a^{[2]}\),这里与逻辑回归相似。在逻辑回归中 \(\hat{y} = a\),在逻辑回归中我们只有一个输出层,所以我们没有使用带方括号的上标,在神经网络中,我们使用这种带上标的形式来明确的指出这些值来自于哪一层
在这个例子中,只是一个两层的神经网络,计算神经网络的层数的时候,输入层是不算入总层数里面的,所以隐藏层是第一层,输出层是第二层,将输入层称为第0层
隐藏层和最后的输出层是带有参数的,这里隐藏层拥有W和b两个参数
\(W^{[1]},b^{[1]}\) : 表示与第一层有关的参数,W 是一个4 ✖ 3的矩阵,b是一个 4✖1的向量,4因为我们有四个隐藏层单元,3因为有三个输入特征。
\(W^{[2]},b^{[2]}\) : 表示与第二层有关的参数,W 是一个1 ✖ 4的矩阵,b是一个 1✖1的向量,1✖4因为隐藏层有四个隐藏层单元输出层有一个单元,3因为有三个输入特征。
3.3 神经网络的输出
只有一个隐藏层的简单两层神经网络结构:
x表示输入特征,a表示每个神经元的输出,W表示特征的权重,上标表示神经网络的层数(隐藏层为1),下标表示是这一层的第几个神经元。
神经网络的计算
圆圈表示神经网络的计算单元,逻辑回归的计算有两个步骤:
- 按照步骤计算出z
- 以sigmoid函数为激活函数来计算z,得出a
一个神经网络只是这样做了很多次重复计算。
- 计算 \(z_{1}^{[1]} = w_{1}^{[1]T}x + b_{1}^{[1]}\)
- 通过激活函数计算\(a^{[1]}_{1} = \sigma(z_{1}^{[1]})\)
向量化计算
将这四个等式向量化,向量化的过程是将神经网络中的一层神经元参数纵向堆积起来,例如隐藏层中的w纵向堆积起来变成一个(4,3)矩阵,用\(W^{[1]}\)来表示。有四个逻辑回归单元,每个逻辑回归单元都有相对应的参数-向量w,将这四个向量堆积在一起,就是这个4 × 3的矩阵
\[z^{[n]} = w^{[n]} x + b^{[n]} \]\[a^{[n]} = \sigma(z^{[n]}) \]\[a^{[1]} = \begin{bmatrix} a^{[1]}_{1} \\ a^{[1]}_{2} \\ a^{[1]}_{3}\\ a^{[1]}_{4}\\ \end{bmatrix} = \sigma(z^{[1]}) \]\[\begin{bmatrix} z^{[1]}_{1} \\ z^{[1]}_{2} \\ z^{[1]}_{3}\\ z^{[1]}_{4}\\ \end{bmatrix} = \overbrace{ \begin{bmatrix} ...W^{[1]}_{1}... \\ ...W^{[1]}_{2}... \\ ...W^{[1]}_{3}...\\ ...W^{[1]}_{4}...\\ \end{bmatrix} }^{{W^{[1]}}} * \overbrace{ \begin{bmatrix} x_{1} \\ x_{2} \\ x_{3}\\ \end{bmatrix} }^{input} + \overbrace{ \begin{bmatrix} b^{[1]}_{1} \\ b^{[1]}_{2} \\ b^{[1]}_{3}\\ b^{[1]}_{4}\\ \end{bmatrix} }^{b^{[1]}} \]对于神经网络的第一层, 给与一个输入x,得到\(a^{[1]}\),x可以表示为\(a^{[0]}\)。
3.4 多个例子中的向量化
了解如何向量化多个训练样本,并计算出结果。
逻辑回归是将各个训练样本组合成矩阵,对矩阵的各列进行计算。神经网络是对逻辑回归中的等式简单的变形,让神经网络计算出输出值。
对于一个给定的输出特征向量X,这四个等式可以计算出\(a^{[2]}\)=\(\hat{y}\)。
如果有m个训练样本,就需要重复这个过程。
用第一个训练样本\(x^{[1]}\)计算出预测值\(\hat{y}^{[1]}\),这就是第一个训练样本上得出的结果。
用\(x^{[2]}\)来计算预测值\(\hat{y}^{[2]}\),循环往复,直到用\(x^{[m]}\)计算出\(\hat{y}^{[m]}\),可以写作\(a^{[2](m)}\)
注:\(a^{[2](i)}\)指的是第i个训练样本,[2]指的是第2层
如果有一个非向量化形式实现,并且要计算出预测值,需要对于所有的训练样本,让i从i到m实现这四个等式:
\[z^{[1](i)} = w^{[1](i)} x^{(i)} + b^{[1](i)} \]\[a^{[1](i)} = \sigma(z^{[1](i)}) \]\[z^{[2](i)} = w^{[2](i)} a^{[1](i)} + b^{[2](i)} \]\[a^{[2](i)} = \sigma(z^{[2](i)}) \]对于上面的这个方程中的\(^{(i)}\),是依赖于所有训练样本的变量,即将(i)添加到x、z、a。如果想对计算m个训练样本上的所有输出,就应该向量化整个计算。
\[\mathrm{x}=\left[\begin{array}{cccc} \vdots & \vdots & \vdots & \vdots \\ \mathrm{x}^{(1)} & \mathrm{x}^{(2)} & \cdots & \mathrm{x}^{(\mathrm{m})} \\ \vdots & \vdots & \vdots & \vdots \end{array}\right] \]\[\mathbf{Z}^{[1]}=\left[\begin{array}{cccc} \vdots & \vdots & \vdots & \vdots \\ \mathbf{Z}^{[1](1)} & \mathrm{Z}^{[1](2)} & \cdots & \mathrm{Z}^{[1](\mathrm{m})} \\ \vdots & \vdots & \vdots & \vdots \end{array}\right] \]\[\mathbf{A}^{[1]}=\left[\begin{array}{cccc} \vdots & \vdots & \vdots & \vdots \\ \mathbf{a}^{[1](1)} & \mathrm{a}^{[1](2)} & \cdots & \mathrm{a}^{[1](\mathrm{m})} \\ \vdots & \vdots & \vdots & \vdots \end{array}\right] \]\[\left.\begin{array}{c} z^{[1](i)} = w^{[1](i)} x^{(i)} + b^{[1](i)} \\ a^{[1](i)} = \sigma(z^{[1](i)}) \\ z^{[2](i)} = w^{[2](i)} a^{[1](i)} + b^{[2](i)}\\ a^{[2](i)} = \sigma(z^{[2](i)}) \end{array}\right\} \Rightarrow \begin{array} A^{[1]} = \sigma(z^{[1]}) \\ z^{[2]} = W^{[2]}A^{[1]}+b^{[2]} \\ A^{[2]} = \sigma(z^{[2]}) \end{array} \]for 循环来遍历所有训练样本
X:组合x 作为矩阵的各列
\(Z^{[1]}\):存储\(z^{[1](1)},z^{[1](2)}...z^{[1](m)}\)
\(A^{[1]}\):存储\(a^{[1](1)},a^{[1](2)}...a^{[1](m)}\)
3.5 向量化实现的解释
现在先忽略\(b^{[1]}\),后面用python的广播机制可以很容易将其加入
现在\(W^{[1]}\)是一个矩阵,\(x^{(1)},x^{(2)},x^{(3)}\)都是列向量,矩阵乘以列向量得到列向量
\[W^{[1]}x = \left[\begin{array}{cccc} ... \\ ... \\ ... \\ \end{array}\right] \left[\begin{array}{cccc} \vdots & \vdots & \vdots & \vdots \\ \mathrm{x}^{(1)} & \mathrm{x}^{(2)} & \cdots & \mathrm{x}^{(\mathrm{m})} \\ \vdots & \vdots & \vdots & \vdots \end{array}\right] \\ =\left[\begin{array}{cccc} \vdots & \vdots & \vdots & \vdots \\ \mathrm w^{[1](1)}{x}^{(1)} & \mathrm w^{[1](2)}{x}^{(2)} & \cdots & \mathrm w^{[1](m)}{x}^{(m)} \\ \vdots & \vdots & \vdots & \vdots \end{array}\right] \\ =\left[\begin{array}{cccc} \vdots & \vdots & \vdots & \vdots \\ \mathrm z^{[1](1)} & \mathrm z^{[1](1)} & \cdots & \mathrm z^{[1](m)} \\ \vdots & \vdots & \vdots & \vdots \end{array}\right] \\ = Z^{[1]} \]当加入更多样本时,只需向矩阵 X 中加入更多列。
3.6 激活函数
到目前为止,之前的视频只用过sigmoid激活函数,但是,有时其他的激活函数效果会更好。
\[a = \sigma(z) = \frac{1}{1+e^{-z}} \]在更通常的情况下,使用不同的函数\(g(z^{[1]})\),g可以是除了sigmoid函数之外的非线性函数,\(tanh\)函数或者双曲正切函数是总体上都优于\(sigmoid\)函数的激活函数。
\[a = tanh(z) = \frac{e^z-e^{-z}}{e^z+e^{-z}} \]\(a = tanh(z)\)的值域是位于[-1,+1], tanh函数是sigmoid函数向下平移和伸缩后的结果。对它进行变形后,穿过了(0,0)点。
使用tanh效果总是优于sigmoid函数,因为函数值域在-1和+1的激活函数,其均值是更接近零均值的。在训练一个算法模型时,如果使用tanh函数代替sigmoid函数中心化数据,使得数据的平均值更接近0而不是0.5.
在讨论优化算法时,有一点要说明:已经不用sigmoid激活函数了,tanh函数在所有场合都优于sigmoid函数。
但有一个例外:在二分类的问题中,对于输出层,因为 y yy 的值是0或1,所以想让 \(\hat{y}\) 的数值介于0和1之间,而不是在-1和+1之间。所以需要使用sigmoid激活函数。对隐藏层使用tanh激活函数,输出层使用sigmoid函数。
sigmoid函数和tanh函数两者共同的缺点是,在 z 特别大或者特别小的情况下,导数的梯度或者函数的斜率会变得特别小,最后就会接近于0,导致梯度下降的速度降低。
\[a = max(0,z) \]只要 z是正值的情况下,导数恒等于1,当 z是负值的时候,导数恒等于0。
选择激活函数的经验法则:
如果输出是0、1值(二分类问题),则输出层选择sigmoid函数,然后其它的所有单元都选择Relu函数。
这是很多激活函数的默认选择,如果在隐藏层上不确定使用哪个激活函数,那么通常会使用Relu激活函数。有时,也会使用tanh激活函数,但Relu的一个优点是:当 z是负值的时候,导数等于0。=
这里也有另一个版本的Relu被称为Leaky Relu。
当 z 是负值时,这个函数的值不是等于0,而是轻微的倾斜,如图。
这个函数通常比Relu激活函数效果要好,尽管在实际中Leaky ReLu使用的并不多。
第一,在 z 的区间变动很大的情况下,激活函数的导数或者激活函数的斜率都会远大于0,在程序实现就是一个if-else语句,而sigmoid函数需要进行浮点四则运算,在实践中,使用ReLu激活函数神经网络通常会比使用sigmoid或者tanh激活函数学习的更快。
第二,sigmoid和tanh函数的导数在正负饱和区的梯度都会接近于0,这会造成梯度弥散,而Relu和Leaky ReLu函数大于0部分都为常数,不会产生梯度弥散现象。(同时应该注意到的是,Relu进入负半区的时候,梯度为0,神经元此时不会训练,产生所谓的稀疏性,而Leaky ReLu不会有这问题)
z 在ReLu的梯度一半都是0,但是,有足够的隐藏层使得z值大于0,所以对大多数的训练数据来说学习过程仍然可以很快。
3.7 为什么需要非线性激活函数
这是神经网络正向传播的方程,去掉函数g,令\(a^{[1]} = z^{[1]}\),\(a^{[2]} = z^{[2]}\),那么这个模型的输出y或仅仅是输入特征x的线性组合。
\[a^{[1]} = z^{[1]} = W^{[1]}x+b^{[1]} (1) \]\[a^{[2]} = z^{[2]} = W^{[2]}a^{[1]}+b^{[2]} (2) \]将式子(1)代入(2)
\[a^{[2]} = z^{[2]} \\= W^{[2]}(W^{[1]}x+b^{[1]})+b^{[2]} \\ = W^{[2]}W^{[1]}x+W^{[2]}b^{[1]}+b^{[2]} \]可以简化多项式得:
\[a^{[2]} = = W^{'}x+b \]如果你使用线性激活函数或者没有使用一个激活函数,那么无论你的神经网络有多少层一直在做的只是计算线性函数,所以不如直接去掉全部隐藏层。
在这里线性隐藏层一点用也没有,因为这两个线性函数的组合本身就是线性函数,所以除非你引入非线性,否则你无法计算更有趣的函数,即使你的网络层数再多也不行。
总而言之,不能在隐藏层用线性激活函数,可以用ReLU或者tanh或者leaky ReLU或者其他的非线性激活函数,唯一可以用线性激活函数的通常就是输出层;除了这种情况,会在隐层用线性函数的,除了一些特殊情况,比如与压缩有关的,那方面在这里将不深入讨论。在这之外,在隐层使用线性激活函数非常少见。因为房价都是非负数,所以我们也可以在输出层使用ReLU函数这样你的\(\hat{y}\)都大于等于0。
3.8 激活函数的导数
在神经网络中使用反向传播的时候,你真的需要计算激活函数的斜率或者导数。针对以下四种激活,求其导数如下:
(1)sigmoid
\[\frac{d}{dz}g(z) = \frac{1}{1+e^{-z}}(1-\frac{1}{1+e^{-z}})\\ = g(z)(1-g(z)) \](2) tanh
\[g(z) = tanh(z) = \frac{e^{z}-e^{-z}} {e^{z}+e^{-z}} \]\[\frac{d}{dz}g(z) = 1 - (tanh(z))^2 \](3) ReLU
\[g(z) = max(0,z) \]\[g'(z) = \left\{\begin{array}{cl} 0 & \text { if } z<0 \\ 1 & \text { if } z > 0\\ 0.000000...1 & \text { if } z = 0 \end{array}\right. \](4) Leaky ReLU
\[g(z) = max(0.01z,z) \]\[g^{\prime}(z)=\left\{\begin{array}{cl} 0.01 & \text { if } z<0 \\ 1 & \text { if } z \geqslant 0 \end{array}\right. \]3.9 神经网络的梯度下降法
\(n_{x}\)个表示输入特征的个数,\(n^{[1]}\)表示隐藏单元的个数,\(n^{[2]}\)表示输出单元个数。
在我们的例子中,参数:
矩阵\(W^{[1]}\)的维度就是(\(n^{[1]}\),\(n^{[0]}\)),\(b^{[1]}\)就是\(n^{[1]}\)维向量,可以写成(\(n^{[1]}\),1)
矩阵\(W^{[2]}\)的维度就是(\(n^{[2]}\),\(n^{[1]}\)),\(b^{[2]}\)就是\(n^{[2]}\)维向量,可以写成(\(n^{[2]}\),1)
假设做二分类任务,成本函数等于:
\[J(W^{{[1]}},b^{[1]},W^{[2]},b^{[2]}) = \frac{1}{m} \sum_{i=1}^m L(\hat{y},y) \]训练参数需要做梯度下降,在训练神经网络的时候,随机初始化参数很重要,而不是初始化成全零。当你参数初始化成某些值后,每次梯度下降都会循环计算以下预测值:\(\hat{y}\),$ i = 1,2,...,m$
\[dW^{[1]} = \frac{dJ}{dW^{[1]}}, db^{[1]} = \frac{dJ}{db^{[1]}} \\ dW^{[2]} = \frac{dJ}{dW^{[2]}}, db^{[2]} = \frac{dJ}{db^{[2]}} \]其中
\[\begin{aligned} & \mathrm{W}^{[1]} \Rightarrow \mathrm{W}^{[1]}-\alpha \mathrm{dW}^{[1]}, \mathrm{b}^{[1]} \Rightarrow \mathrm{b}^{[1]}-\alpha \mathrm{db}^{[1]} \\ & \mathrm{W}^{[2]} \Rightarrow \mathrm{W}^{[2]}-\alpha \mathrm{W}^{[2]}, \mathrm{b}^{[2]} \Rightarrow \mathrm{b}^{[2]}-\alpha \mathrm{db}^{[2]} \end{aligned} \]正向传播(forward propagation)方程如下:
(1) \(\mathrm{z}^{[1]}=\mathrm{W}^{[1]} \mathrm{x}+\mathrm{b}^{[1]}\)
(2) \(\mathrm{a}^{[1]}=\sigma\left(\mathrm{z}^{[1]}\right)\)
(3) \(\mathrm{z}^{[2]}=\mathrm{W}^{[2]} \mathrm{a}^{[1]}+\mathrm{b}^{[2]}\)
(4) \(\mathrm{a}^{[2]}=\mathrm{g}^{[2]}\left(\mathrm{z}^{[2]}\right)=\sigma\left(\mathrm{z}^{[2]}\right)\)
反向传播(backward propagation)方程如下:
\[\begin{aligned} & \mathrm{dz}^{[2]}=\mathrm{A}^{[2]}-\mathrm{Y}, \mathrm{Y}=\left[\begin{array}{llll} \mathrm{y}^{[1]} & \mathrm{y}^{[2]} & \cdots & \mathrm{y}^{[\mathrm{m}]} \end{array}\right] \\ & \mathrm{dW}^{[2]}=\frac{1}{\mathrm{~m}} \mathrm{dz}^{[2]} \mathrm{A}^{[1] \mathrm{T}} \\ & \mathrm{db}^{[2]}=\frac{1}{\mathrm{~m}} \mathrm{np} \cdot \operatorname{sum}\left(\mathrm{dz}^{[2]} \text {, axis }=1 \text {, keepdims }=\text { True }\right) \\ & \mathrm{dz}^{[1]}=\underbrace{\mathrm{W}^{[2] \mathrm{T}} \mathrm{dz}^{[2]}}_{\left(\mathrm{n}^{11}, \mathrm{~m}\right)} * \underbrace{\mathrm{g}^{[1]^{\prime}}}_{\text {activation function of hidden layer }} * \underbrace{\left(\mathrm{z}^{[1]}\right)}_{\left(\mathrm{n}^{11}, \mathrm{~m}\right)} \\ & \mathrm{dW}{ }^{[1]}=\frac{1}{\mathrm{~m}} \mathrm{dz}^{[1]} \mathrm{x}^{\mathrm{T}} \\ & \underbrace{\mathrm{db}^{[1]}}=\frac{1}{\mathrm{~m}} \mathrm{np} \cdot \operatorname{sum}\left(\mathrm{dz}^{[1]} \text {, axis }=1, \text { keepdims }=\text { True }\right) \\ & \end{aligned} \]上述是反向传播的步骤,注:这些都是针对所有样本进行过向量化, Y YY 是 1 ∗ m 的矩阵;这里np.sum
是python的numpy命令,axis=1
表示水平相加求和,keepdims
是防止python输出那些古怪的秩数 ( n , ) ,加上这个确保矩阵\(db^{[2]}\)这个向量输出的维度为 (n,1) 这样标准的形式。
3.10直观理解反向传播
回想一下逻辑回归的公式
\[\left.\begin{array}{l} \mathrm{x} \\ \mathrm{w} \\ \mathrm{b} \end{array}\right\} \Rightarrow \mathrm{z}=\mathrm{w}^{\mathrm{T}}+\mathrm{b} \Rightarrow \mathrm{a}=\sigma(\mathrm{z}) \Rightarrow \mathrm{L}(\mathrm{a}, \mathrm{y}) \]所以回想当时我们讨论逻辑回归的时候,我们有这个正向传播步骤,其中我们计算 z ,然后 a ,然后损失函数 L。
\[\underbrace{\begin{array}{c} x \\ b \end{array}}_{d w=d z \cdot x, d b=d z} \Leftarrow \underbrace{z=w^T+b}_{d z=d a \cdot g^{\prime}(z), g(z)=\sigma(z), \frac{d L}{d z}=\frac{d L}{d a} \cdot \frac{d a}{d z}, \frac{d}{d z} g(z)=g^{\prime}(z)} \Leftarrow \underbrace{\mathrm{a}=\sigma(\mathrm{z}) \Leftarrow L(a, y)}_{d a=\frac{d}{d a} L(a, y)=(-y \log \mathrm{a}-(1-y) \log (1-a))^{\prime}=-\frac{y}{a}+\frac{1}{1-\mathrm{y}}} \]神经网络的计算中,与逻辑回归十分类似,但中间会有多层的计算。下图是一个双层神经网络,有一个输入层,一个隐藏层和一个输出层。
前向传播:
计算\(z^{[1]},a^{[1]}\) ,再计算 \(z^{[2]},a^{[2]}\),最后得到loss function。
反向传播:
向后推算出 \(da^{[2]}\) ,然后推算出 \(dz^{[2]}\) ,接着推算出\(da^{[1]}\),然后推算出\(dz^{[1]}\)。我们不需要对 x求导,因为 x是固定的,我们也不是想优化 x 。向后推算出\(da^{[2]}\) ,然后推算出 $ dz^{[2]}$的步骤可以合为一步:
\[\mathrm{dz}^{[2]}=\mathrm{a}^{[2]}-\mathrm{y}, \mathrm{dW}^{[2]}=\mathrm{dz}^{[2]} \mathrm{a}^{[1] \mathrm{T}} \]\[db^{[2]} = dz^{[2]} \]\[dz^{[1]} = W^{[2]T}dz^{[2]}*g^{[1]'}(z^{[1]}) \]注意:这里的矩阵:\(W^{[2]}\)的维度是:\((n^{[2]},n^{[1]})\) 。
\(z^{[2]}\),\(dz^{[2]}\)的维度都是:(\(n^{[2]}\),1),如果是二分类,那维度就是(1,1)
\(z^{[1]}\),\(dz^{[1]}\)的维度都是:(\(n^{[1]}\),1)
3.11 随机初始化
当你训练神经网络时,权重随机初始化是很重要的。对于逻辑回归,把权重初始化为0当然也是可以的。但是对于一个神经网络,如果你把权重或者参数都初始化为0,那么梯度下降将不会起作用。
如果有两个输入特征,\(n^{[0]}\)=2,两个隐藏层单元\(n^{[1]}\)就等于2。因此与一个隐藏层相关的矩阵,\(W^{[1]}\)是2*2的矩阵,假设将其初始化为全为0的·2*2
的矩阵。\(b^{[1]}\)也就等于\([0,0]^{T}\),把偏置项\(b\)初始化为0是合理的,但是如果把\(w\)初始化为0就有问题了。你总是会发现\(a_{1}^{[1]}\),\(a_{2}^{[1]}\)相等,这个激活单元和这个激活单元就会一样,因为两个隐藏函数计算相同的函数,当你做反向传播的时候,会导致\(dz_1^{[1]}\)和\(dz_2^{[1]}\)也会一样。
你应该这么做:把 \(W^{[1]}\)设为np.random.randn(2,2)
(生成高斯分布),通常再乘上一个小的数,比如0.01,这样把它初始化为很小的随机数。然后 b没有这个对称的问题(叫做symmetry breaking problem),所以可以把 b 初始化为0,因为只要随机初始化 W你就有不同的隐含单元计算不同的东西,因此不会有symmetry breaking问题了。相似的,对于 \(W^{[2]}\) 你可以随机初始化,\(b^{[2]}\)可以初始化为0。
你也许会疑惑,这个常数从哪里来,为什么是0.01,而不是100或者1000。我们通常倾向于初始化为很小的随机数。因为如果你用tanh或者sigmoid激活函数,或者说只在输出层有一个Sigmoid,如果(数值)波动太大,当你计算激活值时如果 W 很大, z 就会很大,因此这种情况下你很可能停在tanh/sigmoid函数的平坦的地方,这些地方梯度很小也就意味着梯度下降会很慢,因此学习也就很慢。
事实上有时有比0.01更好的常数,当你训练一个只有一层隐藏层的网络时(这是相对浅的神经网络,没有太多的隐藏层),设为0.01可能也可以。但当你训练一个非常非常深的神经网络,你可能要试试0.01以外的常数。
week4 深层神经网络
4.1 深层神经网络
4.2 深层网络中的前向传播
跟往常一样,我们先来看对其中一个训练样本 x 如何应用前向传播,之后讨论向量化的版本。
第一层需要计算
\[z^{[1]} = w^{[1]}x + b^{[1]},a^{[1]} = g^{[1]}(z^{[1]}) \]x可以看作\(a^{[0]}\)
第二层需要计算
\[z^{[2]} = w^{[2]}a^{[1]} + b^{[2]},a^{[2]} = g^{[2]}(z^{[2]}) \]以此类推
\[z^{[l]} = w^{[l]}a^{[l-1]} + b^{[l]},a^{[l]} = g^{[l]}(z^{[l]}) \]向量化实现过程可以写成:
\[Z^{[l]} = W^{[l]}A^{[l-1]} + b^{[l]},A^{[l]} = g^{[l]}(Z^{[l]}) \]\(A^{[0]} = X\)
4.3 核对矩阵的维数
当实现深度神经网络的时候,其中一个我常用的检查代码是否有错的方法就是拿出一张纸过一遍算法中矩阵的维数。
w的维度是(下一层的维数,前一层的维数),即\(w^{[l]}:(n^{[l]},n^{[l-1]})\)
b的维度是(下一层的维数,1),即\(b^{[l]}:(n^{(l)},1)\)
\(z^{[l]},a^{[l]}:(n^{[1]},1)\)
这里\(dw^{[l]}\)和\(w^{[l]}\)维度相同,\(db^{[l]}和b^{[l]}\)维度相同,且\(w\)和\(b\)向量化维度不变,但\(z\),\(a\)以及\(x\)的维度会向量化后发生变化。
向量化后:
\(Z^{[1]}\)可以看成由每个单独的\(Z^{[l]}\)叠加而得到,\(Z^{[l]}=(z^{[l][1]},z^{[l][2]},...,z^{[l][m]})\),m为训练集大小,所以\(Z^{[l]}\)的维度不再是(\(n^{[l]}\),1),而是(\(n^{[l]}\),m)。
\(A^{[l]}:(n^{[l]},m),A^{[0]} = X = (n^{[l]},m)\)
在你做深度神经网络的反向传播时,一定要确认所有的矩阵维数是前后一致的,可以大 大提高代码通过率。下一节我们讲为什么深层的网络在很多问题上比浅层的好。
4.4 为什么使用深层表示
首先,深度网络在计算什么?
首先,深度网络究竟在计算什么?如果你在建一个人脸识别或是人脸检测系统,深度神经网络所做的事就是,当你输入一张脸部的照片,然后你可以把深度神经网络的第一层,当成一个特征探测器或者边缘探测器。
在这个例子里,我会建一个大概有20个隐藏单元的深度神经网络,是怎么针对这张图计算的。隐藏单元就是这些图里这些小方块(第一张大图),举个例子,这个小方块(第一行第一列)就是一个隐藏单元,它会去找这张照片里“|”边缘的方向。那么这个隐藏单元(第四行第四列),可能是在找(“—”)水平向的边缘在哪里。之后的课程里,我们会讲专门做这种识别的卷积神经网络,到时候会细讲,为什么小单元是这么表示的。你可以先把神经网络的第一层当作看图,然后去找这张照片的各个边缘。我们可以把照片里组成边缘的像素们放在一起看,然后它可以把被探测到的边缘组合成面部的不同部分(第二张大图)。比如说,可能有一个神经元会去找眼睛的部分,另外还有别的在找鼻子的部分,然后把这许多的边缘结合在一起,就可以开始检测人脸的不同部分。最后再把这些部分放在一起,比如鼻子眼睛下巴,就可以识别或是探测不同的人脸(第三张大图)
你可以直觉上把这种神经网络的前几层当作探测简单的函数,比如边缘,之后把它们跟后几层结合在一起,那么总体上就能学习更多复杂的函数。这些图的意义,我们在学习卷积神经网络的时候再深入了解。还有一个技术性的细节需要理解的是,边缘探测器其实相对来说都是针对照片中非常小块的面积。就像这块(第一行第一列),都是很小的区域。面部探测器就会针对于大一些的区域,但是主要的概念是,一般你会从比较小的细节入手,比如边缘,然后再一步步到更大更复杂的区域,比如一只眼睛或是一个鼻子,再把眼睛鼻子装一块组成更复杂的部分。
我再来举个例子,用没那么正式的语言介绍这个概念。假设你想要对输入特征计算异或或是奇偶性,你可以算\(x_1XORx_2XORx_3XOR\cdots x_n\) ,假设你有 n 或者\(n_x\) 个特征,如果你画一个异或的树图,先要计算 \(x_1\),$x_2 $的异或,然后是 \(x_3\)和 \(x_4\)。技术上来说如果你只用或门,还有非门的话,你可能会需要几层才能计算异或函数,但是用相对小的电路,你应该就可以计算异或了。然后你可以继续建这样的一个异或树图(上图左),那么你最后会得到这样的电路来输出结果\(\hat{y}=y\) ,也就是输入特征的异或,或是奇偶性,要计算异或关系。这种树图对应网络的深度应该是 O ( log ( n ) ) ,那么节点的数量和电路部件,或是门的数量并不会很大,你也不需要太多门去计算异或。
如果你不能使用多隐层的神经网络的话,在这个例子中隐层数为 O ( log ( n ) ) ,比如你被迫只能用单隐藏层来计算的话,这里全部都指向从这些隐藏单元到后面这里,再输出 y ,那么要计算奇偶性,或者异或关系函数就需要这一隐层(上图右方框部分)的单元数呈指数增长才行,因为本质上来说你需要列举耗尽 2^n种可能的配置,或是 2^n种输入比特的配置。
我希望这能让你有点概念,意识到有很多数学函数用深度网络计算比浅网络要容易得多,我个人倒是认为这种电路理论,对训练直觉思维没那么有用,但这个结果人们还是经常提到的,用来解释为什么需要更深层的网络。
除了这些原因,说实话,我认为“深度学习”这个名字挺唬人的,这些概念以前都统称为有很多隐藏层的神经网络,但是深度学习听起来多高大上,太深奥了,对么?这个词流传出去以后,这是神经网络的重新包装或是多隐藏层神经网络的重新包装,激发了大众的想象力。抛开这些公关概念重新包装不谈,深度网络确实效果不错,有时候人们还是会按照字面意思钻牛角尖,非要用很多隐层。但是当我开始解决一个新问题时,我通常会从logistic回归开始,再试试一到两个隐层,把隐藏层数量当作参数、超参数一样去调试,这样去找比较合适的深度。但是近几年以来,有一些人会趋向于使用非常非常深邃的神经网络,比如好几打的层数,某些问题中只有这种网络才是最佳模型。
4.5 搭建深层神经网络块
在$ l $ 层,你会有正向函数,输入$ a^{[l-1]}$并且输出 \(a^{[l]}\),为了计算结果你需要用\(w^{[l]}\)和 \(b^{[l]}\),以及输出到缓存的 $ z^{[l]}$
然后用作反向传播的反向函数,是另一个函数,输入 \(da^{[l]}\) ,输出 \(da^{[l-1]}\),你就会得到对激活函数的导数,也就是希望的导数值 \(da^{[l]}\) 。 \(a^{[l-1]}\)是会变的,前一层算出的激活函数导数。
然后如果实现了这两个函数(正向和反向),然后神经网络的计算过程会是这样的:
把输入特征 \(a^{[0]}\) ,放入第一层并计算第一层的激活函数,用\(a^{[1]}\)表示,你需要$ w^{[1]}\(和\)b {[1]}$来计算,之后也缓存$z\(值。之后喂到第二层,第二层里,需要用到\) w^{[2]}$和 \(b^{[2]}\),你会需要计算第二层的激活函数\(a^{[2]}\)。后面几层以此类推,直到最后你算出了$a^{[L]} $,第 L 层的最终输出值 \(\hat{y}\)。在这些过程里我们缓存了所有的 z值,这就是正向传播的步骤。
当你做编程练习的时候去实现它时,你会发现缓存可能很方便,可以迅速得到\(w^{[l]}\)和\(b^{[l]}\)的值,非常方便的一个方法。
4.6 前向和反向传播
先讲前向传播,输入 \(a^{[l-1]}\),输出是\(a^{[l]}\),缓存为 \(z^{[l]}\);从实现的角度来说我们可以缓存下 \(w^{[l]}\)和 \(b^{[l]}\),这样更容易在不同的环节中调用函数
前向传播的步骤:
\[z^{[1]} = W^{[1]}a^{[l-1]}+b^{[1]},a^{[1]} = g^{[1]}(z^{[1]}) \]向量化
\[Z^{[1]} = W^{[1]}A^{[l-1]}+b^{[1]},A^{[1]} = g^{[1]}(Z^{[1]}) \]前向传播需要喂入\(A^{[0]}\)也就是 \(X\) ,来初始化;初始化的是第一层的输入值。 \(a^{[0]}\)对应于一个训练样本的输入特征,而\(A^{[0]}\)对应于一整 (m) 个训练样本的输入特征,所以这就是这条链的第一个前向函数的输入,重复这个步骤就可以从左到右计算前向传播。
下面讲反向传播的步骤:
输入为 \(da^{[l]}\),输出为 $da^{[l-1]} $, \(dw^{[l]}\) ,\(db^{[l]}\)
所以反向传播的步骤可以写成:
式子(5)由式子(4)带入式子(1)得到,前四个式子就可实现反向函数。
向量化实现过程可以写成:
第一层你可能有一个ReLU激活函数,第二层为另一个ReLU激活函数,第三层可能是sigmoid函数(如果你做二分类的话),输出值为,用来计算损失;这样你就可以向后迭代进行反向传播求导来求 \(dw^{[3]} ,db^{[3]} ,dw^{[2]} ,db^{[2]} ,dw^{[1]} ,db^{[1]}\)。在计算的时候,缓存会把$ z{[1]} z\ z^{[3]}$传递过来,然后回传 \(da^{[2]},da^{[1]}\) ,可以用来计算$ da^{[0]} $,但我们不会使用它,这里讲述了一个三层网络的前向和反向传播,还有一个细节没讲就是前向递归——用输入数据来初始化,那么反向递归(使用Logistic回归做二分类)——对 $ A^{[l]} $求导。
4.7 参数 vs 超参数
比如算法中的learning rate α (学习率)、iterations(梯度下降法循环的数量)、 L 隐藏层数目)、\(n^{[l]}\)(隐藏层单元数目)、choice of activation function(激活函数的选择)都需要你来设置,这些数字实际上控制了最后的参数 W 和 b 的值,所以它们被称作超参数。
走Idea—Code—Experiment—Idea这个循环,尝试各种不同的参数,实现模型并观察是否成功,然后再迭代。
在前面几页中,还有很多不同的超参数。然而,当你开始开发新应用时,预先很难确切知道,究竟超参数的最优值应该是什么。所以通常,你必须尝试很多不同的值,并走这个循环,试试各种参数。试试看5个隐藏层,这个数目的隐藏单元,实现模型并观察是否成功,然后再迭代。
在你开发途中,很有可能学习率的最优数值或是其他超参数的最优值是会变的,所以即使你每天都在用当前最优的参数调试你的系统,你还是会发现,最优值过一年就会变化,因为电脑的基础设施,CPU或是GPU可能会变化很大。所以有一条经验规律可能每几个月就会变。如果你所解决的问题需要很多年时间,只要经常试试不同的超参数,勤于检验结果,看看有没有更好的超参数数值,相信你慢慢会得到设定超参数的直觉,知道你的问题最好用什么数值。
4.8 这和大脑有什么关系
这是一个过度简化的对比,把一个神经网络的逻辑单元和右边的生物神经元对比。至今为止其实连神经科学家们都很难解释,究竟一个神经元能做什么。一个小小的神经元其实却是极其复杂的,以至于我们无法在神经科学的角度描述清楚,它的一些功能,可能真的是类似logistic回归的运算,但单个神经元到底在做什么,目前还没有人能够真正解释,大脑中的神经元是怎么学习的,至今这仍是一个谜之过程。到底大脑是用类似于后向传播或是梯度下降的算法,或者人类大脑的学习过程用的是完全不同的原理。
标签:frac,函数,Neural,Deep,第一课,神经网络,vdots,hat,mathrm From: https://www.cnblogs.com/MyBlogForRecord/p/18281326