第二周:神经网络的编程基础(Basics of Neural Network programming)
文章目录
- 第二周:神经网络的编程基础(Basics of Neural Network programming)
- 2.1 二分类(Binary Classification)
- 2.2 逻辑回归(Logistic Regression)
- 2.3 逻辑回归的代价函数(Logistic Regression Cost Function)
- 2.4 梯度下降法(Gradient Descent)
- 2.5 导数(Derivatives)
- 2.6 更多的导数例子(More Derivative Examples)
- 2.7 计算图(Computation Graph)
- 2.8 使用计算图求导数(Derivatives with a Computation Graph)
- 2.9 逻辑回归中的梯度下降(Logistic Regression Gradient Descent)
2.1 二分类(Binary Classification)
这周我们将学习神经网络的基础知识,其中需要注意的是,当实现一个神经网络的时候,我们需要知道一些非常重要的技术和技巧。例如有一个包含 m m m个样本的训练集,你很可能习惯于用一个for循环来遍历训练集中的每个样本,但是当实现一个神经网络的时候,我们通常不直接使用for循环来遍历整个训练集,所以在这周的课程中你将学会如何处理训练集。
另外在神经网络的计算中,通常先有一个叫做前向暂停(forward pause)或叫做前向传播(foward propagation)的步骤,接着有一个叫做反向暂停(backward pause) 或叫做反向传播(backward propagation)的步骤。所以这周我也会向你介绍为什么神经网络的训练过程可以分为前向传播和反向传播两个独立的部分。
在课程中我将使用逻辑回归(logistic regression)来传达这些想法,以使大家能够更加容易地理解这些概念。即使你之前了解过逻辑回归,我认为这里还是有些新的、有趣的东西等着你去发现和了解,所以现在开始进入正题。
逻辑回归是一个用于二分类(binary classification)的算法。首先我们从一个问题开始说起,这里有一个二分类问题的例子,假如你有一张图片作为输入,比如这只猫,如果识别这张图片为猫,则输出标签1作为结果;如果识别出不是猫,那么输出标签0作为结果。现在我们可以用字母 y y y来 表示输出的结果标签,如下图所示:
我们来看看一张图片在计算机中是如何表示的,为了保存一张图片,需要保存三个矩阵,它们分别对应图片中的红、绿、蓝三种颜色通道,如果你的图片大小为64x64像素,那么你就有三个规模为64x64的矩阵,分别对应图片中红、绿、蓝三种像素的强度值。为了便于表示,这里我画了三个很小的矩阵,注意它们的规模为5x4 而不是64x64,如下图所示:
为了把这些像素值放到一个特征向量中,我们需要把这些像素值提取出来,然后放入一个特征向量 x x x。为了把这些像素值转换为特征向量 x x x,我们需要像下面这样定义一个特征向量 x x x 来表示这张图片,我们把所有的像素都取出来,例如255、231等等,直到取完所有的红色像素,接着最后是255、134、…、255、134等等,直到得到一个特征向量,把图片中所有的红、绿、蓝像素值都列出来。如果图片的大小为64x64像素,那么向量 x x x 的总维度,将是64乘以64乘以3,这是三个像素矩阵中像素的总量。在这个例子中结果为12,288。现在我们用 n x = 12 , 288 n_x=12,288 nx=12,288,来表示输入特征向量的维度,有时候为了简洁,我会直接用小写的 n n n来表示输入特征向量 x x x的维度。所以在二分类问题中,我们的目标就是习得一个分类器,它以图片的特征向量作为输入,然后预测输出结果 y y y为1还是0,也就是预测图片中是否有猫:
接下来我们说明一些在余下课程中,需要用到的一些符号。
符号定义 :
x x x:表示一个 n x n_x nx维数据,为输入数据,维度为 ( n x , 1 ) (n_x,1) (nx,1);
y y y:表示输出结果,取值为 ( 0 , 1 ) (0,1) (0,1);
( x ( i ) , y ( i ) ) (x^{(i)},y^{(i)}) (x(i),y(i)):表示第 i i i组数据,可能是训练数据,也可能是测试数据,此处默认为训练数据;
X = [ x ( 1 ) , x ( 2 ) , . . . , x ( m ) ] X=[x^{(1)},x^{(2)},...,x^{(m)}] X=[x(1),x(2),...,x(m)]:表示所有的训练数据集的输入值,放在一个 n x × m n_x×m nx×m的矩阵中,其中 m m m表示样本数目;
Y = [ y ( 1 ) , y ( 2 ) , . . . , y ( m ) ] Y=[y^{(1)},y^{(2)},...,y^{(m)}] Y=[y(1),y(2),...,y(m)]:对应表示所有训练数据集的输出值,维度为 1 × m 1×m 1×m。
用一对
(
x
,
y
)
(x,y)
(x,y)来表示一个单独的样本,
x
x
x代表
n
x
n_x
nx维的特征向量,
y
y
y 表示标签(输出结果)只能为0或1。
而训练集将由
m
m
m个训练样本组成,其中
(
x
(
1
)
,
y
(
1
)
)
(x^{(1)},y^{(1)})
(x(1),y(1))表示第一个样本的输入和输出,
(
x
(
2
)
,
y
(
2
)
)
(x^{(2)},y^{(2)})
(x(2),y(2))表示第二个样本的输入和输出,直到最后一个样本
(
x
(
m
)
,
y
(
m
)
)
(x^{(m)},y^{(m)})
(x(m),y(m)),然后所有的这些一起表示整个训练集。有时候为了强调这是训练样本的个数,会写作
M
t
r
a
i
n
M_{train}
Mtrain,当涉及到测试集的时候,我们会使用
M
t
e
s
t
M_{test}
Mtest来表示测试集的样本数,所以这是测试集的样本数:
最后为了能把训练集表示得更紧凑一点,我们会定义一个矩阵用大写 X X X的表示,它由输入向量 x ( 1 ) x^{(1)} x(1)、 x ( 2 ) x^{(2)} x(2)等组成,如下图放在矩阵的列中,所以现在我们把 x ( 1 ) x^{(1)} x(1)作为第一列放在矩阵中, x ( 2 ) x^{(2)} x(2)作为第二列, x ( m ) x^{(m)} x(m)放到第 m m m列,然后我们就得到了训练集矩阵 X X X。所以这个矩阵有 m m m列, m m m是训练集的样本数量,然后这个矩阵的高度记为 n x n_x nx,注意有时候可能因为其他某些原因,矩阵 X X X会由训练样本按照行堆叠起来而不是列,如下图所示: x ( 1 ) x^{(1)} x(1)的转置直到 x ( m ) x^{(m)} x(m)的转置,但是在实现神经网络的时候,使用左边的这种形式,会让整个实现的过程变得更加简单:
现在来简单温习一下:
X
X
X是一个规模为
n
x
n_x
nx乘以
m
m
m的矩阵,当你用Python实现的时候,你会看到X.shape
,这是一条Python命令,用于显示矩阵的规模,即X.shape
等于
(
n
x
,
m
)
(n_x,m)
(nx,m),
X
X
X是一个规模为
n
x
n_x
nx乘以
m
m
m的矩阵。所以综上所述,这就是如何将训练样本(输入向量
X
X
X的集合)表示为一个矩阵。
那么输出标签
y
y
y呢?同样的道理,为了能更加容易地实现一个神经网络,将标签
y
y
y放在列中将会使得后续计算非常方便,所以我们定义大写的
Y
Y
Y等于
y
(
1
)
,
y
(
m
)
,
.
.
.
,
y
(
m
)
{{y}^{\left( 1 \right)}},{{y}^{\left( m \right)}},...,{{y}^{\left( m \right)}}
y(1),y(m),...,y(m),所以在这里是一个规模为1乘以
m
m
m的矩阵,同样地使用Python将表示为Y.shape
等于
(
1
,
m
)
(1,m)
(1,m),表示这是一个规模为1乘以
m
m
m的矩阵。
当你在后面的课程中实现神经网络的时候,你会发现,一个好的符号约定能够将不同训练样本的数据很好地组织起来。而我所说的数据不仅包括
x
x
x 或者
y
y
y 还包括之后你会看到的其他的量。将不同的训练样本的数据提取出来,然后就像刚刚我们对
x
x
x 或者
y
y
y 所做的那样,将他们堆叠在矩阵的列中,形成我们之后会在逻辑回归和神经网络上要用到的符号表示。如果有时候你忘了这些符号的意思,比如什么是
m
m
m,或者什么是
n
n
n,或者忘了其他一些东西,我们也会在课程的网站上放上符号说明,然后你可以快速地查阅每个具体的符号代表什么意思,好了,我们接着到下一个视频,在下个视频中,我们将以逻辑回归作为开始。
备注:附录里也写了符号说明。
2.2 逻辑回归(Logistic Regression)
在这个视频中,我们会重温逻辑回归学习算法,该算法适用于二分类问题,本节将主要介绍逻辑回归的Hypothesis Function(假设函数)。
对于二元分类问题来讲,给定一个输入特征向量 X X X,它可能对应一张图片,你想识别这张图片识别看它是否是一只猫或者不是一只猫的图片,你想要一个算法能够输出预测,你只能称之为 y ^ \hat{y} y^,也就是你对实际值 y y y 的估计。更正式地来说,你想让 y ^ \hat{y} y^ 表示 y y y 等于1的一种可能性或者是机会,前提条件是给定了输入特征 X X X。换句话来说,如果 X X X是我们在上个视频看到的图片,你想让 y ^ \hat{y} y^ 来告诉你这是一只猫的图片的机率有多大。在之前的视频中所说的, X X X是一个 n x n_x nx维的向量(相当于有 n x n_x nx个特征的特征向量)。我们用 w w w来表示逻辑回归的参数,这也是一个 n x n_x nx维向量(因为 w w w实际上是特征权重,维度与特征向量相同),参数里面还有 b b b,这是一个实数(表示偏差)。所以给出输入 x x x以及参数 w w w和 b b b之后,我们怎样产生输出预测值 y ^ \hat{y} y^,一件你可以尝试却不可行的事是让 y ^ = w T x + b \hat{y}={{w}^{T}}x+b y^=wTx+b。
这时候我们得到的是一个关于输入 x x x的线性函数,实际上这是你在做线性回归时所用到的,但是这对于二元分类问题来讲不是一个非常好的算法,因为你想让 y ^ \hat{y} y^表示实际值 y y y等于1的机率的话, y ^ \hat{y} y^ 应该在0到1之间。这是一个需要解决的问题,因为 w T x + b {{w}^{T}}x+b wTx+b可能比1要大得多,或者甚至为一个负值。对于你想要的在0和1之间的概率来说它是没有意义的,因此在逻辑回归中,我们的输出应该是 y ^ \hat{y} y^等于由上面得到的线性函数式子作为自变量的sigmoid函数中,公式如上图最下面所示,将线性函数转换为非线性函数。
下图是sigmoid函数的图像,如果我把水平轴作为 z z z轴,那么关于 z z z的sigmoid函数是这样的,它是平滑地从0走向1,让我在这里标记纵轴,这是0,曲线与纵轴相交的截距是0.5,这就是关于 z z z的sigmoid函数的图像。我们通常都使用 z z z来表示 w T x + b {{w}^{T}}x+b wTx+b的值。
关于sigmoid函数的公式是这样的, σ ( z ) = 1 1 + e − z \sigma \left( z \right)=\frac{1}{1+{{e}^{-z}}} σ(z)=1+e−z1,在这里 z z z是一个实数,这里要说明一些要注意的事情,如果 z z z非常大那么 e − z {{e}^{-z}} e−z将会接近于0,关于 z z z的sigmoid函数将会近似等于1除以1加上某个非常接近于0的项,因为 e e e 的指数如果是个绝对值很大的负数的话,这项将会接近于0,所以如果 z z z很大的话那么关于 z z z的sigmoid函数会非常接近1。相反地,如果 z z z非常小或者说是一个绝对值很大的负数,那么关于 e − z {{e}^{-z}} e−z这项会变成一个很大的数,你可以认为这是1除以1加上一个非常非常大的数,所以这个就接近于0。实际上你看到当 z z z变成一个绝对值很大的负数,关于 z z z的sigmoid函数就会非常接近于0,因此当你实现逻辑回归时,你的工作就是去让机器学习参数 w w w以及 b b b这样才使得 y ^ \hat{y} y^成为对 y = 1 y=1 y=1这一情况的概率的一个很好的估计。
在继续进行下一步之前,介绍一种符号惯例,可以让参数
w
w
w和参数
b
b
b分开。在符号上要注意的一点是当我们对神经网络进行编程时经常会让参数
w
w
w和参数
b
b
b分开,在这里参数
b
b
b对应的是一种偏置。在之前的机器学习课程里,你可能已经见过处理这个问题时的其他符号表示。比如在某些例子里,你定义一个额外的特征称之为
x
0
{{x}_{0}}
x0,并且使它等于1,那么现在
X
X
X就是一个
n
x
n_x
nx加1维的变量,然后你定义
y
^
=
σ
(
θ
T
x
)
\hat{y}=\sigma \left( {{\theta }^{T}}x \right)
y^=σ(θTx)的sigmoid函数。在这个备选的符号惯例里,你有一个参数向量
θ
0
,
θ
1
,
θ
2
,
.
.
.
,
θ
n
x
{{\theta }_{0}},{{\theta }_{1}},{{\theta }_{2}},...,{{\theta }_{{{n}_{x}}}}
θ0,θ1,θ2,...,θnx,这样
θ
0
{{\theta }_{0}}
θ0就充当了
b
b
b,这是一个实数,而剩下的
θ
1
{{\theta }_{1}}
θ1 直到
θ
n
x
{{\theta }_{{{n}_{x}}}}
θnx充当了
w
w
w,结果就是当你实现你的神经网络时,有一个比较简单的方法是保持
b
b
b和
w
w
w分开。但是在这节课里我们不会使用任何这类符号惯例,所以不用去担心。
现在你已经知道逻辑回归模型是什么样子了,下一步要做的是训练参数
w
w
w和参数
b
b
b,你需要定义一个代价函数,让我们在下节课里对其进行解释。
2.3 逻辑回归的代价函数(Logistic Regression Cost Function)
在上个视频中,我们讲了逻辑回归模型,这个视频里,我们讲逻辑回归的代价函数(也翻译作成本函数)。
为什么需要代价函数:
为了训练逻辑回归模型的参数参数 w w w和参数 b b b我们,需要一个代价函数,通过训练代价函数来得到参数 w w w和参数 b b b。先看一下逻辑回归的输出函数:
为了让模型通过学习调整参数,你需要给予一个 m m m样本的训练集,这会让你在训练集上找到参数 w w w和参数 b b b,,来得到你的输出。
对训练集的预测值,我们将它写成 y ^ \hat{y} y^,我们更希望它会接近于训练集中的 y y y值,为了对上面的公式更详细的介绍,我们需要说明上面的定义是对一个训练样本来说的,这种形式也使用于每个训练样本,我们使用这些带有圆括号的上标来区分索引和样本,训练样本 i i i所对应的预测值是 y ( i ) {{y}^{(i)}} y(i),是用训练样本的 w T x ( i ) + b {{w}^{T}}{{x}^{(i)}}+b wTx(i)+b然后通过sigmoid函数来得到,也可以把 z z z定义为 z ( i ) = w T x ( i ) + b {{z}^{(i)}}={{w}^{T}}{{x}^{(i)}}+b z(i)=wTx(i)+b,我们将使用这个符号 ( i ) (i) (i)注解,上标 ( i ) (i) (i)来指明数据表示 x x x或者 y y y或者 z z z或者其他数据的第 i i i个训练样本,这就是上标 ( i ) (i) (i)的含义。
损失函数:
损失函数又叫做误差函数,用来衡量算法的运行情况,Loss function: L ( y ^ , y ) L\left( \hat{y},y \right) L(y^,y).
我们通过这个 L L L称为的损失函数,来衡量预测输出值和实际值有多接近。一般我们用预测值和实际值的平方差或者它们平方差的一半,但是通常在逻辑回归中我们不这么做,因为当我们在学习逻辑回归参数的时候,会发现我们的优化目标不是凸优化,只能找到多个局部最优值,梯度下降法很可能找不到全局最优值,虽然平方差是一个不错的损失函数,但是我们在逻辑回归模型中会定义另外一个损失函数。
我们在逻辑回归中用到的损失函数是: L ( y ^ , y ) = − y log ( y ^ ) − ( 1 − y ) log ( 1 − y ^ ) L\left( \hat{y},y \right)=-y\log(\hat{y})-(1-y)\log (1-\hat{y}) L(y^,y)=−ylog(y^)−(1−y)log(1−y^)
为什么要用这个函数作为逻辑损失函数?当我们使用平方误差作为损失函数的时候,你会想要让这个误差尽可能地小,对于这个逻辑回归损失函数,我们也想让它尽可能地小,为了更好地理解这个损失函数怎么起作用,我们举两个例子:
当 y = 1 y=1 y=1时损失函数 L = − log ( y ^ ) L=-\log (\hat{y}) L=−log(y^),如果想要损失函数 L L L尽可能得小,那么 y ^ \hat{y} y^就要尽可能大,因为sigmoid函数取值 [ 0 , 1 ] [0,1] [0,1],所以 y ^ \hat{y} y^会无限接近于1。
当 y = 0 y=0 y=0时损失函数 L = − log ( 1 − y ^ ) L=-\log (1-\hat{y}) L=−log(1−y^),如果想要损失函数 L L L尽可能得小,那么 y ^ \hat{y} y^就要尽可能小,因为sigmoid函数取值 [ 0 , 1 ] [0,1] [0,1],所以 y ^ \hat{y} y^会无限接近于0。
在这门课中有很多的函数效果和现在这个类似,就是如果
y
y
y等于1,我们就尽可能让
y
^
\hat{y}
y^变大,如果
y
y
y等于0,我们就尽可能让
y
^
\hat{y}
y^ 变小。
损失函数是在单个训练样本中定义的,它衡量的是算法在单个训练样本中表现如何,为了衡量算法在全部训练样本上的表现如何,我们需要定义一个算法的代价函数,算法的代价函数是对
m
m
m个样本的损失函数求和然后除以
m
m
m:
J
(
w
,
b
)
=
1
m
∑
i
=
1
m
L
(
y
^
(
i
)
,
y
(
i
)
)
=
1
m
∑
i
=
1
m
(
−
y
(
i
)
log
y
^
(
i
)
−
(
1
−
y
(
i
)
)
log
(
1
−
y
^
(
i
)
)
)
J\left( w,b \right)=\frac{1}{m}\sum\limits_{i=1}^{m}{L\left( {{{\hat{y}}}^{(i)}},{{y}^{(i)}} \right)}=\frac{1}{m}\sum\limits_{i=1}^{m}{\left( -{{y}^{(i)}}\log {{{\hat{y}}}^{(i)}}-(1-{{y}^{(i)}})\log (1-{{{\hat{y}}}^{(i)}}) \right)}
J(w,b)=m1i=1∑mL(y^(i),y(i))=m1i=1∑m(−y(i)logy^(i)−(1−y(i))log(1−y^(i)))
损失函数只适用于像这样的单个训练样本,而代价函数是参数的总代价,所以在训练逻辑回归模型时候,我们需要找到合适的
w
w
w和
b
b
b,来让代价函数
J
J
J 的总代价降到最低。
根据我们对逻辑回归算法的推导及对单个样本的损失函数的推导和针对算法所选用参数的总代价函数的推导,结果表明逻辑回归可以看做是一个非常小的神经网络,在下一个视频中,我们会看到神经网络会做什么。
2.4 梯度下降法(Gradient Descent)
梯度下降法可以做什么?
在你测试集上,通过最小化代价函数(成本函数) J ( w , b ) J(w,b) J(w,b)来训练的参数 w w w和 b b b,
如图,在第二行给出和之前一样的逻辑回归算法的代价函数(成本函数)
梯度下降法的形象化说明
在这个图中,横轴表示你的空间参数 w w w和 b b b,在实践中, w w w可以是更高的维度,但是为了更好地绘图,我们定义 w w w和 b b b,都是单一实数,代价函数(成本函数) J ( w , b ) J(w,b) J(w,b)是在水平轴 w w w和 b b b上的曲面,因此曲面的高度就是 J ( w , b ) J(w,b) J(w,b)在某一点的函数值。我们所做的就是找到使得代价函数(成本函数) J ( w , b ) J(w,b) J(w,b)函数值是最小值,对应的参数 w w w和 b b b。
如图,代价函数(成本函数) J ( w , b ) J(w,b) J(w,b)是一个凸函数(convex function),像一个大碗一样。
如图,这就与刚才的图有些相反,因为它是非凸的并且有很多不同的局部最小值。由于逻辑回归的代价函数(成本函数)
J
(
w
,
b
)
J(w,b)
J(w,b)特性,我们必须定义代价函数(成本函数)
J
(
w
,
b
)
J(w,b)
J(w,b)为凸函数。
初始化
w
w
w和
b
b
b,
可以用如图那个小红点来初始化参数 w w w和 b b b,也可以采用随机初始化的方法,对于逻辑回归几乎所有的初始化方法都有效,因为函数是凸函数,无论在哪里初始化,应该达到同一点或大致相同的点。
我们以如图的小红点的坐标来初始化参数 w w w和 b b b。
2. 朝最陡的下坡方向走一步,不断地迭代
我们朝最陡的下坡方向走一步,如图,走到了如图中第二个小红点处。
我们可能停在这里也有可能继续朝最陡的下坡方向再走一步,如图,经过两次迭代走到第三个小红点处。
3.直到走到全局最优解或者接近全局最优解的地方
通过以上的三个步骤我们可以找到全局最优解,也就是代价函数(成本函数) J ( w , b ) J(w,b) J(w,b)这个凸函数的最小值点。
梯度下降法的细节化说明(仅有一个参数)
假定代价函数(成本函数) J ( w ) J(w) J(w) 只有一个参数 w w w,即用一维曲线代替多维曲线,这样可以更好画出图像。
迭代就是不断重复做如图的公式:
: = := :=表示更新参数,
$a $ 表示学习率(learning rate),用来控制步长(step),即向下走一步的长度 d J ( w ) d w \frac{dJ(w)}{dw} dwdJ(w) 就是函数 J ( w ) J(w) J(w)对 w w w 求导(derivative),在代码中我们会使用 d w dw dw表示这个结果
对于导数更加形象化的理解就是斜率(slope),如图该点的导数就是这个点相切于 J ( w ) J(w) J(w)的小三角形的高除宽。假设我们以如图点为初始化点,该点处的斜率的符号是正的,即 d J ( w ) d w > 0 \frac{dJ(w)}{dw}>0 dwdJ(w)>0,所以接下来会向左走一步。
整个梯度下降法的迭代过程就是不断地向左走,直至逼近最小值点。
假设我们以如图点为初始化点,该点处的斜率的符号是负的,即 d J ( w ) d w < 0 \frac{dJ(w)}{dw}<0 dwdJ(w)<0,所以接下来会向右走一步。
整个梯度下降法的迭代过程就是不断地向右走,即朝着最小值点方向走。
梯度下降法的细节化说明(两个参数)
逻辑回归的代价函数(成本函数) J ( w , b ) J(w,b) J(w,b)是含有两个参数的。
$\partial $ 表示求偏导符号,可以读作round,
∂
J
(
w
,
b
)
∂
w
\frac{\partial J(w,b)}{\partial w}
∂w∂J(w,b) 就是函数
J
(
w
,
b
)
J(w,b)
J(w,b) 对
w
w
w 求偏导,在代码中我们会使用
d
w
dw
dw 表示这个结果,
∂
J
(
w
,
b
)
∂
b
\frac{\partial J(w,b)}{\partial b}
∂b∂J(w,b) 就是函数
J
(
w
,
b
)
J(w,b)
J(w,b)对
b
b
b 求偏导,在代码中我们会使用
d
b
db
db 表示这个结果,
小写字母
d
d
d 用在求导数(derivative),即函数只有一个参数,
偏导数符号$\partial $ 用在求偏导(partial derivative),即函数含有两个以上的参数。
2.5 导数(Derivatives)
这个视频我主要是想帮你获得对微积分和导数直观的理解。或许你认为自从大学毕以后你再也没有接触微积分。这取决于你什么时候毕业,也许有一段时间了,如果你顾虑这点,请不要担心。为了高效应用神经网络和深度学习,你并不需要非常深入理解微积分。因此如果你观看这个视频或者以后的视频时心想:“哇哦,这些知识、这些运算对我来说很复杂。”我给你的建议是:坚持学习视频,最好下课后做作业,成功的完成编程作业,然后你就可以使用深度学习了。在第四周之后的学习中,你会看到定义的很多种类的函数,通过微积分他们能够帮助你把所有的知识结合起来,其中有的叫做前向函数和反向函数,因此你不需要了解所有你使用的那些微积分中的函数。所以你不用担心他们,除此之外在对深度学习的尝试中,这周我们要进一步深入了解微积分的细节。所有你只需要直观地认识微积分,用来构建和成功的应用这些算法。最后,如果你是精通微积分的那一小部分人群,你对微积分非常熟悉,你可以跳过这部分视频。其他同学让我们开始深入学习导数。
一个函数 f ( a ) = 3 a f(a)=3a f(a)=3a,它是一条直线。下面我们来简单理解下导数。让我们看看函数中几个点,假定 a = 2 a=2 a=2,那么 f ( a ) f(a) f(a)是 a a a的3倍等于6,也就是说如果 a = 2 a=2 a=2,那么函数 f ( a ) = 6 f(a)=6 f(a)=6。假定稍微改变一点点 a a a的值,只增加一点,变为2.001,这时 a a a将向右做微小的移动。0.001的差别实在是太小了,不能在图中显示出来,我们把它右移一点,现在 f ( a ) f(a) f(a)等于 a a a的3倍是6.003,画在图里,比例不太符合。请看绿色高亮部分的这个小三角形,如果向右移动0.001,那么 f ( a ) f(a) f(a)增加0.003, f ( a ) f(a) f(a)的值增加3倍于右移的 a a a,因此我们说函数 f ( a ) f(a) f(a)在 a = 2 a=2 a=2,.是这个导数的斜率,或者说,当 a = 2 a=2 a=2时,斜率是3。导数这个概念意味着斜率,导数听起来是一个很可怕、很令人惊恐的词,但是斜率以一种很友好的方式来描述导数这个概念。所以提到导数,我们把它当作函数的斜率就好了。更正式的斜率定义为在上图这个绿色的小三角形中,高除以宽。即斜率等于0.003除以0.001,等于3。或者说导数等于3,这表示当你将 a a a右移0.001, f ( a ) f(a) f(a)的值增加3倍水平方向的量。
现在让我们从不同的角度理解这个函数。
假设
a
=
5
a=5
a=5 ,此时
f
(
a
)
=
3
a
=
15
f(a)=3a=15
f(a)=3a=15。
把
a
a
a右移一个很小的幅度,增加到5.001,
f
(
a
)
=
15.003
f(a)=15.003
f(a)=15.003。
即在
a
=
5
a=5
a=5 时,斜率是3,这就是表示,当微小改变变量
a
a
a的值,
d
f
(
a
)
d
a
=
3
\frac{df(a)}{da}=3
dadf(a)=3 。一个等价的导数表达式可以这样写
d
d
a
f
(
a
)
\frac{d}{da}f(a)
dadf(a) ,不管你是否将
f
(
a
)
f(a)
f(a)放在上面或者放在右边都没有关系。
在这个视频中,我讲解导数讨论的情况是我们将
a
a
a偏移0.001,如果你想知道导数的数学定义,导数是你右移很小的
a
a
a值(不是0.001,而是一个非常非常小的值)。通常导数的定义是你右移
a
a
a(可度量的值)一个无限小的值,
f
(
a
)
f(a)
f(a)增加3倍(增加了一个非常非常小的值)。也就是这个三角形右边的高度。
那就是导数的正式定义。但是为了直观的认识,我们将探讨右移 a = 0.001 a=0.001 a=0.001 这个值,即使0.001并不是无穷小的可测数据。导数的一个特性是:这个函数任何地方的斜率总是等于3,不管 a = 2 a=2 a=2或 a = 5 a=5 a=5,这个函数的斜率总等于3,也就是说不管 a a a的值如何变化,如果你增加0.001, f ( a ) f(a) f(a)的值就增加3倍。这个函数在所有地方的斜率都相等。一种证明方式是无论你将小三角形画在哪里,它的高除以宽总是3。
我希望带给你一种感觉:什么是斜率?什么是导函数?对于一条直线,在例子中函数的斜率,在任何地方都是3。在下一个视频让我们看一个更复杂的例子,这个例子中函数在不同点的斜率是可变的。
2.6 更多的导数例子(More Derivative Examples)
在这个视频中我将给出一个更加复杂的例子,在这个例子中,函数在不同点处的斜率是不一样的,先来举个例子:
我在这里画一个函数, f ( a ) = a 2 f(a)={{\text{a}}^{\text{2}}} f(a)=a2,如果 a = 2 a=\text{2} a=2 的话,那么 f ( a ) = 4 f(a)=4 f(a)=4。让我们稍稍往右推进一点点,现在 a = 2 . 001 a=\text{2}.\text{001} a=2.001 ,则 f ( a ) ≈ 4.004 f(a)\approx 4.004 f(a)≈4.004 (如果你用计算器算的话,这个准确的值应该为4.004。0.001 我只是为了简便起见,省略了后面的部分),如果你在这儿画,一个小三角形,你就会发现,如果把 a a a往右移动0.001,那么 f ( a ) f(a) f(a)将增大四倍,即增大0.004。在微积分中我们把这个三角形斜边的斜率,称为 f ( a ) f(a) f(a)在点 a = 2 a=\text{2} a=2 处的导数(即为4),或者写成微积分的形式,当 a = 2 a=\text{2} a=2 的时候, d d a f ( a ) = 4 \frac{d}{da}f(a)=4 dadf(a)=4 由此可知,函数 f ( a ) = a 2 f(a)={{a}^{{2}}} f(a)=a2,在 a a a取不同值的时候,它的斜率是不同的,这和上个视频中的例子是不同的。
这里有种直观的方法可以解释,为什么一个点的斜率,在不同位置会不同如果你在曲线上,的不同位置画一些小小的三角形你就会发现,三角形高和宽的比值,在曲线上不同的地方,它们是不同的。所以当 a = 2 a=2 a=2 时,斜率为4;而当 a = 5 a=5 a=5时,斜率为10 。如果你翻看微积分的课本,课本会告诉你,函数 f ( a ) = a 2 f(a)={{a}^{{2}}} f(a)=a2的斜率(即导数)为 2 a 2a 2a。这意味着任意给定一点 a a a,如果你稍微将 a a a,增大0.001,那么你会看到 f ( a ) f(a) f(a)将增大 2 a 2a 2a,即增大的值为点在 a a a处斜率或导数,乘以你向右移动的距离。
现在有个小细节需要注意,导数增大的值,不是刚好等于导数公式算出来的值,而只是根据导数算出来的一个估计值。
为了总结这堂课所学的知识,我们再来看看几个例子:
假设 f ( a ) = a 3 f(a)={{a}^{\text{3}}} f(a)=a3 如果你翻看导数公式表,你会发现这个函数的导数,等于 3 a 2 3{{a}^{2}} 3a2。所以这是什么意思呢,同样地举一个例子:我们再次令 a = 2 a=2 a=2,所以 a 3 = 8 {{a}^{3}}=8 a3=8 ,如果我们又将 a a a增大一点点,你会发现 f ( a ) ≈ 8.012 f(a)\approx 8.012 f(a)≈8.012, 你可以自己检查一遍,如果我们取8.012,你会发现 2.001 3 {{2.001}^{3}} 2.0013 ,和8.012很接近,事实上当 a = 2 a=2 a=2时,导数值为 3 × 2 2 3×{{2}^{2}} 3×22,即 3 × 4 = 12 3×4=12 3×4=12。所以导数公式,表明如果你将 a a a向右移动0.001时, f ( a ) f(a) f(a) 将会向右移动12倍,即0.012。
来看最后一个例子,假设 f ( a ) = log e a f(a)={{\log }_{e}}a f(a)=logea,有些可能会写作 ln a \ln a lna,函数 log a \log a loga 的斜率应该为 1 a \frac{1}{a} a1,所以我们可以解释如下:如果 a a a取任何值,比如又取 a = 2 a=2 a=2,然后又把 a a a向右边移动0.001 那么 f ( a ) f(a) f(a)将增大 1 a × 0.001 \frac{\text{1}}{a}\times \text{0}\text{.001} a1×0.001,如果你借助计算器的话,你会发现当 a = 2 a=2 a=2时 f ( a ) ≈ 0.69315 f(a)\approx \text{0}\text{.69315} f(a)≈0.69315 ;而 a = 2.001 a=2.001 a=2.001时, f ( a ) ≈ 0.69365 f(a)\approx \text{0}\text{.69365} f(a)≈0.69365。所以 f ( a ) f(a) f(a)增大了0.0005,如果你查看导数公式,当 a = 2 a=2 a=2的时候,导数值 d d a f ( a ) = 1 2 \frac{d}{da}f(a)=\frac{\text{1}}{\text{2}} dadf(a)=21。这表明如果你把 增大0.001, f ( a ) f(a) f(a)将只会增大0.001的二分之一,即0.0005。如果你画个小三角形你就会发现,如果 x x x 轴增加了0.001,那么 y y y 轴上的函数 log a \log a loga,将增大0.001的一半 即0.0005。所以 1 a \frac{1}{a} a1 ,当 a = 2 a=2 a=2时这里是 ,就是当 a = 2 a=2 a=2时这条线的斜率。这些就是有关,导数的一些知识。
在这个视频中,你只需要记住两点:
第一点,导数就是斜率,而函数的斜率,在不同的点是不同的。在第一个例子中 f ( a ) = 3 a f(a)=\text{3}a f(a)=3a ,这是一条直线,在任何点它的斜率都是相同的,均为3。但是对于函数 f ( a ) = a 2 f(a)={{\text{a}}^{\text{2}}} f(a)=a2 ,或者 f ( a ) = log a f(a)=\log a f(a)=loga,它们的斜率是变化的,所以它们的导数或者斜率,在曲线上不同的点处是不同的。
第二点,如果你想知道一个函数的导数,你可参考你的微积分课本或者维基百科,然后你应该就能找到这些函数的导数公式。
最后我希望,你能通过我生动的讲解,掌握这些有关导数和斜率的知识,下一课我们将讲解计算图,以及如何用它来求更加复杂的函数的导数。
2.7 计算图(Computation Graph)
可以说,一个神经网络的计算,都是按照前向或反向传播过程组织的。首先我们计算出一个新的网络的输出(前向过程),紧接着进行一个反向传输操作。后者我们用来计算出对应的梯度或导数。计算图解释了为什么我们用这种方式组织这些计算过程。在这个视频中,我们将举一个例子说明计算图是什么。让我们举一个比逻辑回归更加简单的,或者说不那么正式的神经网络的例子。
我们尝试计算函数
J
J
J,
J
J
J是由三个变量
a
,
b
,
c
a,b,c
a,b,c组成的函数,这个函数是
3(a
+
bc)
\text{3(a}+\text{bc)}
3(a+bc) 。计算这个函数实际上有三个不同的步骤,首先是计算
b
b
b 乘以
c
c
c,我们把它储存在变量
u
u
u中,因此
u
=
b
c
{u}={bc}
u=bc;
然后计算
v
=
a
+
u
v=a+u
v=a+u;最后输出
J
=
3
v
J=3v
J=3v,这就是要计算的函数
J
J
J。我们可以把这三步画成如下的计算图,我先在这画三个变量
a
,
b
,
c
a,b,c
a,b,c,第一步就是计算
u
=
b
c
u=bc
u=bc,我在这周围放个矩形框,它的输入是
b
,
c
b,c
b,c,接着第二步
v
=
a
+
u
v=a+u
v=a+u,最后一步
J
=
3
v
J=3v
J=3v。
举个例子:
a
=
5
,
b
=
3
,
c
=
2
a=5,b=3,c=2
a=5,b=3,c=2 ,
u
=
b
c
u=bc
u=bc就是6,
v
=
a
+
u
v=a+u
v=a+u ,就是5+6=11。
J
J
J是3倍的 ,因此。即
3
×
(
5
+
3
×
2
)
3×(5+3×2)
3×(5+3×2)。如果你把它算出来,实际上得到33就是
J
J
J的值。
当有不同的或者一些特殊的输出变量时,例如本例中的
J
J
J和逻辑回归中你想优化的代价函数
J
J
J,因此计算图用来处理这些计算会很方便。从这个小例子中我们可以看出,通过一个从左向右的过程,你可以计算出
J
J
J的值。为了计算导数,从右到左(红色箭头,和蓝色箭头的过程相反)的过程是用于计算导数最自然的方式。
概括一下:计算图组织计算的形式是用蓝色箭头从左到右的计算,让我们看看下一个视频中如何进行反向红色箭头(也就是从右到左)的导数计算,让我们继续下一个视频的学习。
2.8 使用计算图求导数(Derivatives with a Computation Graph)
在上一个视频中,我们看了一个例子使用流程计算图来计算函数J。现在我们清理一下流程图的描述,看看你如何利用它计算出函数 J J J的导数。
下面用到的公式:
d J d u = d J d v d v d u \frac{dJ}{du}=\frac{dJ}{dv}\frac{dv}{du} dudJ=dvdJdudv , d J d b = d J d u d u d b \frac{dJ}{db}=\frac{dJ}{du}\frac{du}{db} dbdJ=dudJdbdu , d J d a = d J d u d u d a \frac{dJ}{da}=\frac{dJ}{du}\frac{du}{da} dadJ=dudJdadu
这是一个流程图:
假设你要计算 d J d v \frac{{dJ}}{{dv}} dvdJ,那要怎么算呢?好,比如说,我们要把这个 v v v值拿过来,改变一下,那么 J J J的值会怎么变呢?
所以定义上 J = 3 v J = 3v J=3v,现在 v = 11 v=11 v=11,所以如果你让 v v v增加一点点,比如到11.001,那么 J = 3 v = 33.003 J =3v =33.003 J=3v=33.003,所以我这里 v v v增加了0.001,然后最终结果是 J J J上升到原来的3倍,所以 d J d v = 3 \frac{{dJ}}{{dv}}=3 dvdJ=3,因为对于任何 v v v 的增量 J J J都会有3倍增量,而且这类似于我们在上一个视频中的例子,我们有 f ( a ) = 3 a f(a)=3a f(a)=3a,然后我们推导出 d f ( a ) d a = 3 \frac{{df}(a)}{{da}}= 3 dadf(a)=3,所以这里我们有 J = 3 v J=3v J=3v,所以 d J d v = 3 \frac{{dJ}}{{dv}} =3 dvdJ=3,这里 J J J扮演了 f f f的角色,在之前的视频里的例子。
在反向传播算法中的术语,我们看到,如果你想计算最后输出变量的导数,使用你最关心的变量对 v v v的导数,那么我们就做完了一步反向传播,在这个流程图中是一个反向步骤。
我们来看另一个例子, d J d a \frac{{dJ}}{da} dadJ是多少呢?换句话说,如果我们提高 a a a的数值,对 J J J的数值有什么影响?
好,我们看看这个例子。变量 a = 5 a=5 a=5,我们让它增加到5.001,那么对v的影响就是 a + u a+u a+u,之前 v = 11 v=11 v=11,现在变成11.001,我们从上面看到现在 J J J 就变成33.003了,所以我们看到的是,如果你让 a a a增加0.001, J J J增加0.003。那么增加 a a a,我是说如果你把这个5换成某个新值,那么 a a a的改变量就会传播到流程图的最右,所以 J J J最后是33.003。所以J的增量是3乘以 a a a的增量,意味着这个导数是3。
要解释这个计算过程,其中一种方式是:如果你改变了 a a a,那么也会改变 v v v,通过改变 v v v,也会改变 J J J,所以 J J J值的净变化量,当你提升这个值(0.001),当你把 a a a值提高一点点,这就是 J J J的变化量(0.003)。
首先a增加了, v v v也会增加, v v v增加多少呢?这取决于 d v d a \frac{{dv}}{da} dadv,然后 v v v的变化导致 J J J也在增加,所以这在微积分里实际上叫链式法则,如果 a a a影响到 v v v, v v v影响到 J J J,那么当你让 a a a变大时, J J J的变化量就是当你改变 a a a时, v v v的变化量乘以改变 v v v时 J J J的变化量,在微积分里这叫链式法则。
我们从这个计算中看到,如果你让 a a a增加0.001, v v v也会变化相同的大小,所以 d v d a = 1 \frac{{dv}}{da}= 1 dadv=1。事实上,如果你代入进去,我们之前算过 d J d v = 3 \frac{{dJ}}{{dv}} =3 dvdJ=3, d v d a = 1 \frac{{dv}}{da} =1 dadv=1,所以这个乘积3×1,实际上就给出了正确答案, d J d a = 3 \frac{{dJ}}{da} = 3 dadJ=3。
这张小图表示了如何计算, d J d v \frac{{dJ}}{{dv}} dvdJ就是 J J J对变量 v v v的导数,它可以帮助你计算 d J d a \frac{{dJ}}{da} dadJ,所以这是另一步反向传播计算。
现在我想介绍一个新的符号约定,当你编程实现反向传播时,通常会有一个最终输出值是你要关心的,最终的输出变量,你真正想要关心或者说优化的。在这种情况下最终的输出变量是J,就是流程图里最后一个符号,所以有很多计算尝试计算输出变量的导数,所以输出变量对某个变量的导数,我们就用 d v a r dvar dvar命名,所以在很多计算中你需要计算最终输出结果的导数,在这个例子里是 J J J,还有各种中间变量,比如 a 、 b 、 c 、 u 、 v a、b、c、u、v a、b、c、u、v,当你在软件里实现的时候,变量名叫什么?你可以做的一件事是,在python中,你可以写一个很长的变量名,比如 d F i n a l O u t p u t v a r _ d v a r {dFinalOutputvar}\_{dvar} dFinalOutputvar_dvar,但这个变量名有点长,我们就用 d J _ d v a r dJ\_dvar dJ_dvar,但因为你一直对 d J dJ dJ求导,对这个最终输出变量求导。我这里要介绍一个新符号,在程序里,当你编程的时候,在代码里,我们就使用变量名 d v a r dvar dvar,来表示那个量。
好,所以在程序里是 d v a r dvar dvar表示导数,你关心的最终变量 J J J的导数,有时最后是 L L L,对代码中各种中间量的导数,所以代码里这个东西,你用 d v dv dv表示这个值,所以 d v = 3 dv=3 dv=3,你的代码表示就是 d a = 3 da=3 da=3。
好,所以我们通过这个流程图完成部分的后向传播算法。我们在下一张幻灯片看看这个例子剩下的部分。
我们清理出一张新的流程图,我们回顾一下,到目前为止,我们一直在往回传播,并计算 d v = 3 dv=3 dv=3,再次, d v dv dv是代码里的变量名,其真正的定义是 d J d v \frac{{dJ}}{{dv}} dvdJ。我发现 d a = 3 da=3 da=3,再次, d a da da是代码里的变量名,其实代表 d J d a \frac{{dJ}}{da} dadJ的值。
大概手算了一下,两条直线怎么计算反向传播。
好,我们继续计算导数,我们看看这个值 u u u,那么 d J d u \frac{dJ}{du} dudJ是多少呢?通过和之前类似的计算,现在我们从 u = 6 u=6 u=6出发,如果你令 u u u增加到6.001,那么 v v v之前是11,现在变成11.001了, J J J 就从33变成33.003,所以 J J J 增量是3倍,所以 d J d u = 3 \frac{{dJ}}{du}= 3 dudJ=3。对 u u u的分析很类似对a的分析,实际上这计算起来就是 d J d v ⋅ d v d u \frac{{dJ}}{dv}\cdot \frac{{dv}}{du} dvdJ⋅dudv,有了这个,我们可以算出 d J d v = 3 \frac{{dJ}}{dv} =3 dvdJ=3, d v d u = 1 \frac{{dv}}{du} = 1 dudv=1,最终算出结果是 3 × 1 = 3 3×1=3 3×1=3。
所以我们还有一步反向传播,我们最终计算出 d u = 3 du=3 du=3,这里的 d u du du当然了,就是 d J d u \frac{{dJ}}{du} dudJ。
现在,我们仔细看看最后一个例子,那么 d J d b \frac{{dJ}}{db} dbdJ呢?想象一下,如果你改变了 b b b的值,你想要然后变化一点,让 J J J 值到达最大或最小,那么导数是什么呢?这个 J J J函数的斜率,当你稍微改变 b b b值之后。事实上,使用微积分链式法则,这可以写成两者的乘积,就是 d J d u ⋅ d u d b \frac{{dJ}}{du}\cdot \frac{{du}}{db} dudJ⋅dbdu,理由是,如果你改变 b b b一点点,所以 b b b变化比如说3.001,它影响J的方式是,首先会影响 u u u,它对 u u u的影响有多大?好, u u u的定义是 b ⋅ c b\cdot c b⋅c,所以 b = 3 b=3 b=3时这是6,现在就变成6.002了,对吧,因为在我们的例子中 c = 2 c=2 c=2,所以这告诉我们 d u d b = 2 \frac{{du}}{db}= 2 dbdu=2当你让 b b b增加0.001时, u u u就增加两倍。所以 d u d b = 2 \frac{{du}}{db} =2 dbdu=2,现在我想 u u u的增加量已经是 b b b的两倍,那么 d J d u \frac{{dJ}}{du} dudJ是多少呢?我们已经弄清楚了,这等于3,所以让这两部分相乘,我们发现 d J d b = 6 \frac{{dJ}}{db}= 6 dbdJ=6。
好,这就是第二部分的推导,其中我们想知道 u u u 增加0.002,会对 J J J 有什么影响。实际上 d J d u = 3 \frac{{dJ}}{du}=3 dudJ=3,这告诉我们u增加0.002之后, J J J上升了3倍,那么 J J J 应该上升0.006,对吧。这可以从 d J d u = 3 \frac{{dJ}}{du}= 3 dudJ=3推导出来。
如果你仔细看看这些数学内容,你会发现,如果 b b b变成3.001,那么 u u u就变成6.002, v v v变成11.002,然后 J = 3 v = 33.006 J=3v=33.006 J=3v=33.006,对吧?这就是如何得到 d J d b = 6 \frac{{dJ}}{db}= 6 dbdJ=6。
为了填进去,如果我们反向走的话, d b = 6 db=6 db=6,而 d b db db其实是Python代码中的变量名,表示 d J d b \frac{{dJ}}{db} dbdJ。
我不会很详细地介绍最后一个例子,但事实上,如果你计算 d J d c = d J d u ⋅ d u d c = 3 × 3 \frac{{dJ}}{dc} =\frac{{dJ}}{du}\cdot \frac{{du}}{dc} = 3 \times 3 dcdJ=dudJ⋅dcdu=3×3,这个结果是9。
我不会详细说明这个例子,在最后一步,我们可以推出 d c = 9 dc=9 dc=9。
所以这个视频的要点是,对于那个例子,当计算所有这些导数时,最有效率的办法是从右到左计算,跟着这个红色箭头走。特别是当我们第一次计算对 v v v的导数时,之后在计算对 a a a导数就可以用到。然后对 u u u的导数,比如说这个项和这里这个项:
可以帮助计算对 b b b的导数,然后对 c c c的导数。
所以这是一个计算流程图,就是正向或者说从左到右的计算来计算成本函数J,你可能需要优化的函数,然后反向从右到左计算导数。如果你不熟悉微积分或链式法则,我知道这里有些细节讲的很快,但如果你没有跟上所有细节,也不用怕。在下一个视频中,我会再过一遍。在逻辑回归的背景下过一遍,并给你介绍需要做什么才能编写代码,实现逻辑回归模型中的导数计算。
2.9 逻辑回归中的梯度下降(Logistic Regression Gradient Descent)
本节我们讨论怎样通过计算偏导数来实现逻辑回归的梯度下降算法。它的关键点是几个重要公式,其作用是用来实现逻辑回归中梯度下降算法。但是在本节视频中,我将使用计算图对梯度下降算法进行计算。我必须要承认的是,使用计算图来计算逻辑回归的梯度下降算法有点大材小用了。但是,我认为以这个例子作为开始来讲解,可以使你更好的理解背后的思想。从而在讨论神经网络时,你可以更深刻而全面地理解神经网络。接下来让我们开始学习逻辑回归的梯度下降算法。
假设样本只有两个特征
x
1
{{x}_{1}}
x1和
x
2
{{x}_{2}}
x2,为了计算
z
z
z,我们需要输入参数
w
1
{{w}_{1}}
w1、
w
2
{{w}_{2}}
w2 和
b
b
b,除此之外还有特征值
x
1
{{x}_{1}}
x1和
x
2
{{x}_{2}}
x2。因此
z
z
z的计算公式为:
z
=
w
1
x
1
+
w
2
x
2
+
b
z={{w}_{1}}{{x}_{1}}+{{w}_{2}}{{x}_{2}}+b
z=w1x1+w2x2+b
回想一下逻辑回归的公式定义如下:
y
^
=
a
=
σ
(
z
)
\hat{y}=a=\sigma (z)
y^=a=σ(z)
其中
z
=
w
T
x
+
b
z={{w}^{T}}x+b
z=wTx+b
σ
(
z
)
=
1
1
+
e
−
z
\sigma \left( z \right)=\frac{1}{1+{{e}^{-z}}}
σ(z)=1+e−z1
损失函数:
L
(
y
^
(
i
)
,
y
(
i
)
)
=
−
y
(
i
)
log
y
^
(
i
)
−
(
1
−
y
(
i
)
)
log
(
1
−
y
^
(
i
)
)
L( {{{\hat{y}}}^{(i)}},{{y}^{(i)}})=-{{y}^{(i)}}\log {{\hat{y}}^{(i)}}-(1-{{y}^{(i)}})\log (1-{{\hat{y}}^{(i)}})
L(y^(i),y(i))=−y(i)logy^(i)−(1−y(i))log(1−y^(i))
代价函数:
J
(
w
,
b
)
=
1
m
∑
i
m
L
(
y
^
(
i
)
,
y
(
i
)
)
J\left( w,b \right)=\frac{1}{m}\sum\nolimits_{i}^{m}{L( {{{\hat{y}}}^{(i)}},{{y}^{(i)}})}
J(w,b)=m1∑imL(y^(i),y(i))
假设现在只考虑单个样本的情况,单个样本的代价函数定义如下:
L
(
a
,
y
)
=
−
(
y
log
(
a
)
+
(
1
−
y
)
log
(
1
−
a
)
)
L(a,y)=-(y\log (a)+(1-y)\log (1-a))
L(a,y)=−(ylog(a)+(1−y)log(1−a))
其中
a
a
a是逻辑回归的输出,
y
y
y是样本的标签值。现在让我们画出表示这个计算的计算图。
这里先复习下梯度下降法,
w
w
w和
b
b
b的修正量可以表达如下:
w : = w − a ∂ J ( w , b ) ∂ w w:=w-a \frac{\partial J(w,b)}{\partial w} w:=w−a∂w∂J(w,b), b : = b − a ∂ J ( w , b ) ∂ b b:=b-a\frac{\partial J(w,b)}{\partial b} b:=b−a∂b∂J(w,b)
如图:在这个公式的外侧画上长方形。然后计算:
y
^
=
a
=
σ
(
z
)
\hat{y}=a=\sigma(z)
y^=a=σ(z)
也就是计算图的下一步。最后计算损失函数
L
(
a
,
y
)
L(a,y)
L(a,y)。
有了计算图,我就不需要再写出公式了。因此,为了使得逻辑回归中最小化代价函数
L
(
a
,
y
)
L(a,y)
L(a,y),我们需要做的仅仅是修改参数
w
w
w和
b
b
b的值。前面我们已经讲解了如何在单个训练样本上计算代价函数的前向步骤。现在让我们来讨论通过反向计算出导数。
因为我们想要计算出的代价函数
L
(
a
,
y
)
L(a,y)
L(a,y)的导数,首先我们需要反向计算出代价函数
L
(
a
,
y
)
L(a,y)
L(a,y)关于
a
a
a的导数,在编写代码时,你只需要用
d
a
da
da 来表示
d
L
(
a
,
y
)
d
a
\frac{dL(a,y)}{da}
dadL(a,y) 。
通过微积分得到:
d
L
(
a
,
y
)
d
a
=
−
y
/
a
+
(
1
−
y
)
/
(
1
−
a
)
\frac{dL(a,y)}{da}=-y/a+(1-y)/(1-a)
dadL(a,y)=−y/a+(1−y)/(1−a)
如果你不熟悉微积分,也不必太担心,我们会列出本课程涉及的所有求导公式。那么如果你非常熟悉微积分,我们鼓励你主动推导前面介绍的代价函数的求导公式,使用微积分直接求出
L
(
a
,
y
)
L(a,y)
L(a,y)关于变量
a
a
a的导数。如果你不太了解微积分,也不用太担心。现在我们已经计算出
d
a
da
da,也就是最终输出结果的导数。
现在可以再反向一步,在编写Python代码时,你只需要用
d
z
dz
dz来表示代价函数
L
L
L关于
z
z
z 的导数
d
L
d
z
\frac{dL}{dz}
dzdL,也可以写成
d
L
(
a
,
y
)
d
z
\frac{dL(a,y)}{dz}
dzdL(a,y),这两种写法都是正确的。
d
L
d
z
=
a
−
y
\frac{dL}{dz}=a-y
dzdL=a−y 。
因为
d
L
(
a
,
y
)
d
z
=
d
L
d
z
=
(
d
L
d
a
)
⋅
(
d
a
d
z
)
\frac{dL(a,y)}{dz}=\frac{dL}{dz}=(\frac{dL}{da})\cdot (\frac{da}{dz})
dzdL(a,y)=dzdL=(dadL)⋅(dzda),
并且
d
a
d
z
=
a
⋅
(
1
−
a
)
\frac{da}{dz}=a\cdot (1-a)
dzda=a⋅(1−a),
而
d
L
d
a
=
(
−
y
a
+
(
1
−
y
)
(
1
−
a
)
)
\frac{dL}{da}=(-\frac{y}{a}+\frac{(1-y)}{(1-a)})
dadL=(−ay+(1−a)(1−y)),因此将这两项相乘,得到:
d z = d L ( a , y ) d z = d L d z = ( d L d a ) ⋅ ( d a d z ) = ( − y a + ( 1 − y ) ( 1 − a ) ) ⋅ a ( 1 − a ) = a − y {dz} = \frac{{dL}(a,y)}{{dz}} = \frac{{dL}}{{dz}} = \left( \frac{{dL}}{{da}} \right) \cdot \left(\frac{{da}}{{dz}} \right) = ( - \frac{y}{a} + \frac{(1 - y)}{(1 - a)})\cdot a(1 - a) = a - y dz=dzdL(a,y)=dzdL=(dadL)⋅(dzda)=(−ay+(1−a)(1−y))⋅a(1−a)=a−y
视频中为了简化推导过程,假设 n x {{n}_{x}} nx 这个推导的过程就是我之前提到过的链式法则。如果你对微积分熟悉,放心地去推导整个求导过程,如果不熟悉微积分,你只需要知道 d z = ( a − y ) dz=(a-y) dz=(a−y)已经计算好了。
现在进行最后一步反向推导,也就是计算
w
w
w和
b
b
b变化对代价函数
L
L
L的影响,特别地,可以用:
d
w
1
=
1
m
∑
i
m
x
1
(
i
)
(
a
(
i
)
−
y
(
i
)
)
d{{w}_{1}}=\frac{1}{m}\sum\limits_{i}^{m}{x_{1}^{(i)}}({{a}^{(i)}}-{{y}^{(i)}})
dw1=m1i∑mx1(i)(a(i)−y(i))
d
w
2
=
1
m
∑
i
m
x
2
(
i
)
(
a
(
i
)
−
y
(
i
)
)
d{{w}_{2}}=\frac{1}{m}\sum\limits_{i}^{m}{x_{2}^{(i)}}({{a}^{(i)}}-{{y}^{(i)}})
dw2=m1i∑mx2(i)(a(i)−y(i))
d
b
=
1
m
∑
i
m
(
a
(
i
)
−
y
(
i
)
)
db=\frac{1}{m}\sum\limits_{i}^{m}{({{a}^{(i)}}-{{y}^{(i)}})}
db=m1i∑m(a(i)−y(i))
视频中,
d
w
1
d{{w}_{1}}
dw1 表示
∂
L
∂
w
1
=
x
1
⋅
d
z
\frac{\partial L}{\partial {{w}_{1}}}={{x}_{1}}\cdot dz
∂w1∂L=x1⋅dz,
d
w
2
d{{w}_{\text{2}}}
dw2 表示
∂
L
∂
w
2
=
x
2
⋅
d
z
\frac{\partial L}{\partial {{w}_{2}}}={{x}_{2}}\cdot dz
∂w2∂L=x2⋅dz,
d
b
=
d
z
db=dz
db=dz。
因此,关于单个样本的梯度下降算法,你所需要做的就是如下的事情:
使用公式
d
z
=
(
a
−
y
)
dz=(a-y)
dz=(a−y)计算
d
z
dz
dz,
使用
d
w
1
=
x
1
⋅
d
z
d{{w}_{1}}={{x}_{1}}\cdot dz
dw1=x1⋅dz 计算
d
w
1
d{{w}_{1}}
dw1,
d
w
2
=
x
2
⋅
d
z
d{{w}_{2}}={{x}_{2}}\cdot dz
dw2=x2⋅dz计算
d
w
2
d{{w}_{2}}
dw2,
d
b
=
d
z
db=dz
db=dz 来计算
d
b
db
db,
然后:
更新
w
1
=
w
1
−
a
d
w
1
{{w}_{1}}={{w}_{1}}-a d{{w}_{1}}
w1=w1−adw1,
更新
w
2
=
w
2
−
a
d
w
2
{{w}_{2}}={{w}_{2}}-a d{{w}_{2}}
w2=w2−adw2,
更新
b
=
b
−
α
d
b
b=b-\alpha db
b=b−αdb。
这就是关于单个样本实例的梯度下降算法中参数更新一次的步骤。
现在你已经知道了怎样计算导数,并且实现针对单个训练样本的逻辑回归的梯度下降算法。但是,训练逻辑回归模型不仅仅只有一个训练样本,而是有 m m m个训练样本的整个训练集。因此在下一节视频中,我们将这些思想应用到整个训练样本集中,而不仅仅只是单个样本上。