吴恩达深度学习笔记:神经网络的编程基础2.9-2.14

目录

  • 第一门课:神经网络和深度学习 (Neural Networks and Deep Learning)
    • 第二周:神经网络的编程基础 (Basics of Neural Network programming)
      • [2.9 逻辑回归中的梯度下降(Logistic Regression Gradient Descent)](#2.9 逻辑回归中的梯度下降(Logistic Regression Gradient Descent))
      • [2.10 m 个样本的梯度下降(Gradient Descent on m Examples)](#2.10 m 个样本的梯度下降(Gradient Descent on m Examples))
      • [2.11 向量化(Vectorization)](#2.11 向量化(Vectorization))
      • [2.12 向量化的更多例子(More Examples of Vectorization)](#2.12 向量化的更多例子(More Examples of Vectorization))
      • [2.13 向量化逻辑回归(Vectorizing Logistic Regression)](#2.13 向量化逻辑回归(Vectorizing Logistic Regression))
      • [2.14 向量化 logistic 回归的梯度输出(Vectorizing Logistic Regression's Gradient)](#2.14 向量化 logistic 回归的梯度输出(Vectorizing Logistic Regression's Gradient))

第一门课:神经网络和深度学习 (Neural Networks and Deep Learning)

第二周:神经网络的编程基础 (Basics of Neural Network programming)

2.9 逻辑回归中的梯度下降(Logistic Regression Gradient Descent)

本节我们讨论怎样通过计算偏导数来实现逻辑回归的梯度下降算法。它的关键点是几个重要公式,其作用是用来实现逻辑回归中梯度下降算法。但是在本节视频中,我将使用计算图对梯度下降算法进行计算。我必须要承认的是,使用计算图来计算逻辑回归的梯度下降算法有点大材小用了。但是,我认为以这个例子作为开始来讲解,可以使你更好的理解背后的思想。从而在讨论神经网络时,你可以更深刻而全面地理解神经网络。接下来让我们开始学习逻辑回归的梯度下降算法。

假设样本只有两个特征 x 1 x_1 x1和 x 2 x_2 x2,为了计算𝑧,我们需要输入参数 w 1 、 w 2 w_1、w_2 w1、w2 和𝑏,除此之外还有特征值 x 1 x_1 x1和 x 2 x_2 x2。因此𝑧的计算公式为: z = w 1 x 1 + w 2 x 2 + b z = w_1x_1 + w_2x_2 + b z=w1x1+w2x2+b;

回想一下逻辑回归的公式定义如下:

y ^ = a = σ ( z ) 其中 z = w T x + b , σ ( z ) = 1 1 + e − z \hat{y}= a = σ(z) 其中 z= w^Tx + b, σ(z) =\frac{1}{1+e^{-z}} y^=a=σ(z)其中z=wTx+b,σ(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)log(y^(i))−(1−y(i))log(1−y^(i))

代价函数: 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))

假设现在只考虑单个样本的情况,单个样本的代价函数定义如下:
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))

其中𝑎是逻辑回归的输出,𝑦是样本的标签值。现在让我们画出表示这个计算的计算图。

这里先复习下梯度下降法,𝑤和𝑏的修正量可以表达如下:

如图:在这个公式的外侧画上长方形。然后计算: 𝑦^ = 𝑎 = 𝜎(𝑧) 也就是计算图的下一步。最后计算损失函数𝐿(𝑎, 𝑦)。 有了计算图,我就不需要再写出公式了。因此,为了使得逻辑回归中最小化代价函数𝐿(𝑎, 𝑦),我们需要做的仅仅是修改参数𝑤和𝑏的值。前面我们已经讲解了如何在单个训练样本上计算代价函数的前向步骤。现在让我们来讨论通过反向计算出导数。 因为我们想要计算出的代价函数𝐿(𝑎, 𝑦)的导数,首先我们需要反向计算出代价函数𝐿(𝑎, 𝑦)关于𝑎的导数,在编写代码时,你只需要用𝑑𝑎 来表示 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}=\frac{-y}{a}+\frac{1-y}{1-a} dadL(a,y)=a−y+1−a1−y

如果你不熟悉微积分,也不必太担心,我们会列出本课程涉及的所有求导公式。那么如果你非常熟悉微积分,我们鼓励你主动推导前面介绍的代价函数的求导公式,使用微积分直接求出𝐿(𝑎, 𝑦)关于变量𝑎的导数。如果你不太了解微积分,也不用太担心。现在我们已经计算出𝑑𝑎,也就是最终输出结果的导数。 现在可以再反向一步,在编写 Python 代码时,你只需要用𝑑𝑧来表示代价函数𝐿关于𝑧 的导数 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})*(\frac{da}{dz}) dzdL(a,y)=dzdL=(dadL)∗(dzda),并且 d a d z = a ∗ ( 1 − a ) \frac{da}{dz} =a*(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=(a−y+1−a1−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}=\frac{dL}{da}*\frac{da}{dz}=(\frac{-y}{a}+\frac{1-y}{1-a})*a(1-a) =a-y dz=dzdL(a,y)=dzdL=dadL∗dzda=(a−y+1−a1−y)∗a(1−a)=a−y

视频中为了简化推导过程,假设𝑛𝑥这个推导的过程就是我之前提到过的链式法则。如果你对微积分熟悉,放心地去推导整个求导过程,如果不熟悉微积分,你只需要知道𝑑𝑧 = (𝑎 −𝑦)已经计算好了。

现在进行最后一步反向推导,也就是计算𝑤和𝑏变化对代价函数𝐿的影响,特别地,可以用:
d w 1 = 1 m ∑ n = i m x 1 ( i ) ( a ( i ) − y ( i ) ) dw_1=\frac{1}{m}\sum_{n=i}^mx_1^{(i)}(a^{(i)} -y^{(i)}) dw1=m1n=i∑mx1(i)(a(i)−y(i))
d w 2 = 1 m ∑ n = i m x 2 ( i ) ( a ( i ) − y ( i ) ) dw_2=\frac{1}{m}\sum_{n=i}^mx_2^{(i)}(a^{(i)} -y^{(i)}) dw2=m1n=i∑mx2(i)(a(i)−y(i))
d b = 1 m ∑ n = i m ( a ( i ) − y ( i ) ) db=\frac{1}{m}\sum_{n=i}^m(a^{(i)} -y^{(i)}) db=m1n=i∑m(a(i)−y(i))

视频中, 𝑑𝑤1 表示 ∂ L ∂ w 1 = x 1 ⋅ d z ∂L ∂w_1= x_1 ⋅ dz ∂L∂w1=x1⋅dz, 𝑑𝑤2 表示 ∂ L ∂ w 2 = x 2 ⋅ d z ∂L∂w_2= x_2 ⋅ dz ∂L∂w2=x2⋅dz, d b = d z db = dz db=dz。

因此,关于单个样本的梯度下降算法,你所需要做的就是如下的事情:

使用公式 d z = ( a − y ) dz = (a − y) dz=(a−y)计算𝑑𝑧,

使用 d w 1 = x 1 ⋅ d z dw_1 = x_1 ⋅ dz dw1=x1⋅dz 计算𝑑𝑤1, d w 2 = x 2 ⋅ d z dw_2 = x_2 ⋅ dz dw2=x2⋅dz计算𝑑𝑤2, d b = d z db= dz db=dz 来计算𝑑𝑏,

然后: 更新 w 1 = w 1 − α d w 1 w_1 = w_1 − αdw_1 w1=w1−αdw1, 更新 w 2 = w 2 − α d w 2 w_2 = w_2 − αdw_2 w2=w2−αdw2, 更新 b = b − α d b b = b − αdb b=b−αdb。

这就是关于单个样本实例的梯度下降算法中参数更新一次的步骤。

现在你已经知道了怎样计算导数,并且实现针对单个训练样本的逻辑回归的梯度下降算法。但是,训练逻辑回归模型不仅仅只有一个训练样本,而是有𝑚个训练样本的整个训练集。因此在下一节视频中,我们将这些思想应用到整个训练样本集中,而不仅仅只是单个样本上。

2.10 m 个样本的梯度下降(Gradient Descent on m Examples)

在之前的视频中,你已经看到如何计算导数,以及应用梯度下降在逻辑回归的一个训练样本上。现在我们想要把它应用在𝑚个训练样本上。

首先,让我们时刻记住有关于损失函数𝐽(𝑤, 𝑏)的定义。
J ( w , b ) = 1 m ∑ i = 1 m L ( a ( i ) , y ( i ) ) J(w,b) = \frac{1}{m} \sum_{i=1}^{m} L( a^{(i)},y^{(i)}) J(w,b)=m1i=1∑mL(a(i),y(i))

当你的算法输出关于样本𝑦的𝑎(𝑖),𝑎(𝑖)是训练样本的预测值,即: σ ( z ( i ) ) = σ ( w T x ( i ) + b ) σ(z^{(i)}) =σ(w^Tx^{(i)} + b) σ(z(i))=σ(wTx(i)+b) 。所以我们在前面的幻灯中展示的是对于任意单个训练样本,如何计算微分当你只有一个训练样本。因此 d w 1 , d w 2 dw_1,dw_2 dw1,dw2和 d b db db 添上上标𝑖表示你求得的相应的值。如果你面对的是我们在之前的幻灯中演示的那种情况,但只使用了一个训练样本 ( x ( i ) , y ( i ) ) (x^{(i)},y^{(i)}) (x(i),y(i))。

现在你知道带有求和的全局代价函数,实际上是 1 到𝑚项各个损失的平均。 所以它表明全局代价函数对𝑤1的微分,对𝑤1的微分也同样是各项损失对𝑤1微分的平均。

但之前我们已经演示了如何计算这项,即之前幻灯中演示的如何对单个训练样本进行计算。所以你真正需要做的是计算这些微分,如我们在之前的训练样本上做的。并且求平均,这会给你全局梯度值,你能够把它直接应用到梯度下降算法中。

所以这里有很多细节,但让我们把这些装进一个具体的算法。同时你需要一起应用的就是逻辑回归和梯度下降。

我们初始化𝐽 = 0, 𝑑𝑤1 = 0, 𝑑𝑤2 = 0, 𝑑𝑏 = 0

代码流程:

python 复制代码
J=0;dw1=0;dw2=0;db=0;
for i = 1 to m
 z(i) = wx(i)+b;
 a(i) = sigmoid(z(i));
 J += -[y(i)log(a(i))+(1-y(i))log(1-a(i));
 dz(i) = a(i)-y(i);
 dw1 += x1(i)dz(i);
 dw2 += x2(i)dz(i);
 db += dz(i);
J/= m;
dw1/= m;
dw2/= m;
db/= m;
w=w-alpha*dw
b=b-alpha*db

但这种计算中有两个缺点,也就是说应用此方法在逻辑回归上你需要编写两个 for 循环。第一个 for 循环是一个小循环遍历𝑚个训练样本,第二个 for 循环是一个遍历所有特征的 for循环。这个例子中我们只有 2 个特征,所以𝑛等于 2 并且𝑛𝑥 等于 2。 但如果你有更多特征,你开始编写你的因此𝑑𝑤1,𝑑𝑤2,你有相似的计算从𝑑𝑤3一直下去到𝑑𝑤𝑛。所以看来你需要一个 for 循环遍历所有𝑛个特征。

当你应用深度学习算法,你会发现在代码中显式地使用 for 循环使你的算法很低效,同时在深度学习领域会有越来越大的数据集。所以能够应用你的算法且没有显式的 for 循环会是重要的,并且会帮助你适用于更大的数据集。所以这里有一些叫做向量化技术,它可以允许你的代码摆脱这些显式的 for 循环。

我想在先于深度学习的时代,也就是深度学习兴起之前,向量化是很棒的。可以使你有时候加速你的运算,但有时候也未必能够。但是在深度学习时代向量化,摆脱 for 循环已经变得相当重要。因为我们越来越多地训练非常大的数据集,因此你真的需要你的代码变得非常高效。所以在接下来的几个视频中,我们会谈到向量化,以及如何应用向量化而连一个 for循环都不使用。所以学习了这些,我希望你有关于如何应用逻辑回归,或是用于逻辑回归的梯度下降,事情会变得更加清晰。当你进行编程练习,但在真正做编程练习之前让我们先谈谈向量化。然后你可以应用全部这些东西,应用一个梯度下降的迭代而不使用任何 for 循环。

2.11 向量化(Vectorization)

向量化是非常基础的去除代码中 for 循环的艺术,在深度学习安全领域、深度学习实践中,你会经常发现自己训练大数据集,因为深度学习算法处理大数据集效果很棒,所以你的代码运行速度非常重要,否则如果在大数据集上,你的代码可能花费很长时间去运行,你将要等待非常长的时间去得到结果。所以在深度学习领域,运行向量化是一个关键的技巧,让我们举个栗子说明什么是向量化。

在逻辑回归中你需要去计算 z = w T x + b z = w^Tx+ b z=wTx+b,𝑤、𝑥都是列向量。如果你有很多的特征那么就会有一个非常大的向量,所以𝑤 ∈ R n x R^{nx} Rnx , 𝑥 ∈ R n x R^{nx} Rnx,所以如果你想使用非向量化方法去计算𝑤𝑇𝑥,你需要用如下方式(python)

python 复制代码
z=0
for i in range(n_x)
 z+=w[i]*x[i]
z+=b

这是一个非向量化的实现,你会发现这真的很慢,作为一个对比,向量化实现将会非常直接计算 w T x w^Tx wTx,代码如下:

python 复制代码
z=np.dot(w,x)+b

这是向量化计算 w T x w^Tx wTx的方法,你将会发现这个非常快。

让我们用一个小例子说明一下,在我的我将会写一些代码(以下为教授在他的 Jupyter notebook 上写的 Python 代码,)

----非向量化的版本

python 复制代码
import numpy as np #导入 numpy 库
a = np.array([1,2,3,4]) #创建一个数据 a
print(a)
# [1 2 3 4]
import time #导入时间库
a = np.random.rand(1000000)
b = np.random.rand(1000000) #通过 round 随机得到两个一百万维度的数组
tic = time.time() #现在测量一下当前时间
#继续增加非向量化的版本
c = 0
for i in range(1000000):
 c += a[i]*b[i]
toc = time.time()
print(c)
print("For loop:" + str(1000*(toc-tic)) + "ms")#打印 for 循环的版本的时间

----向量化的版本

python 复制代码
import numpy as np #导入 numpy 库
a = np.array([1,2,3,4]) #创建一个数据 a
print(a)
# [1 2 3 4]
import time #导入时间库
a = np.random.rand(1000000)
b = np.random.rand(1000000) #通过 round 随机得到两个一百万维度的数组
tic = time.time() #现在测量一下当前时间
#向量化的版本
c = np.dot(a,b)
toc = time.time()
print("Vectorized version:" + str(1000*(toc-tic)) +"ms") #打印一下向量

在两个方法中,向量化和非向量化计算了相同的值,如你所见,向量化版本花费了 1.5毫秒,非向量化版本的 for 循环花费了大约几乎 500 毫秒,非向量化版本多花费了 300 倍时间。所以在这个例子中,仅仅是向量化你的代码,就会运行 300 倍快。这意味着如果向量化方法需要花费一分钟去运行的数据,for 循环将会花费 5 个小时去运行。

一句话总结,以上都是再说和 for 循环相比,向量化可以快速得到结果。

2.12 向量化的更多例子(More Examples of Vectorization)

从上节视频中,你知道了怎样通过 numpy 内置函数和避开显式的循环(loop)的方式进行向量化,从而有效提高代码速度。

经验提醒我,当我们在写神经网络程序时,或者在写逻辑(logistic)回归,或者其他神经网络模型时,应该避免写循环(loop)语句。虽然有时写循环(loop)是不可避免的,但是我们可以使用比如 numpy 的内置函数或者其他办法去计算。当你这样使用后,程序效率总是快于循环(loop)。

让我们看另外一个例子。如果你想计算向量𝑢 = 𝐴𝑣,这时矩阵乘法定义为,矩阵乘法的定义就是:𝑢𝑖 = ∑𝑗 𝐴ij𝑣𝑖 ,这取决于你怎么定义𝑢𝑖 值。同样使用非向量化实现,𝑢 =𝑛𝑝. 𝑧𝑒𝑟𝑜𝑠(𝑛, 1), 并且通过两层循环𝑓𝑜𝑟(𝑖): 𝑓𝑜𝑟(𝑗):,得到𝑢[𝑖] = 𝑢[𝑖] + 𝐴[𝑖][𝑗] ∗ 𝑣[𝑗] 。现在就有了𝑖 和 𝑗 的两层循环,这就是非向量化。向量化方式就可以用𝑢 = 𝑛𝑝. 𝑑𝑜𝑡(𝐴, 𝑣),右边这种向量化实现方式,消除了两层循环使得代码运行速度更快。

下面通过另一个例子继续了解向量化。如果你已经有一个向量𝑣,并且想要对向量𝑣的每个元素做指数操作,得到向量𝑢等于𝑒的𝑣1,𝑒的𝑣2,一直到𝑒的𝑣𝑛次方。这里是非向量化的实现方式,首先你初始化了向量𝑢 = 𝑛𝑝. 𝑧𝑒𝑟𝑜𝑠(𝑛, 1),并且通过循环依次计算每个元素。但事实证明可以通过 python 的 numpy 内置函数,帮助你计算这样的单个函数。所以我会引入import numpy as np,执行 𝑢 = 𝑛𝑝. 𝑒𝑥𝑝(𝑣) 命令。注意到,在之前有循环的代码中,这里仅用了一行代码,向量𝑣作为输入,𝑢作为输出。你已经知道为什么需要循环,并且通过右边代码实现,效率会明显的快于循环方式。

事实上,numpy 库有很多向量函数。比如 u=np.log 是计算对数函数(𝑙𝑜𝑔)、np.abs()是计算数据的绝对值、 np.maximum() 计算元素 𝑦 中的最大值,你也可以np.maximum(v,0) 、 𝑣 ∗∗ 2 代表获得元素 𝑦 每个值得平方、 1/𝑣 获取元素 𝑦 的倒数等等。所以当你想写循环时候,检查 numpy 是否存在类似的内置函数,从而避免使用循环(loop)方式。

那么,将刚才所学到的内容,运用在逻辑回归的梯度下降上,看看我们是否能简化两个计算过程中的某一步。这是我们逻辑回归的求导代码,有两层循环。在这例子我们有𝑛个特征值。如果你有超过两个特征时,需要循环 𝑑𝑤1 、𝑑𝑤2 、𝑑𝑤3 等等。所以 𝑗 的实际值是1、2 和 𝑛𝑥,就是你想要更新的值。所以我们想要消除第二循环,在这一行,这样我们就不用初始化 𝑑𝑤1 , 𝑑𝑤2 都等于 0。去掉这些,而是定义 𝑑𝑤 为一个向量,设置 𝑢 =𝑛𝑝. 𝑧𝑒𝑟𝑜𝑠(𝑛(𝑥),1)。定义了一个𝑥行的一维向量,从而替代循环。我们仅仅使用了一个向量操作 𝑑𝑤 = 𝑑𝑤 + 𝑥(𝑖)𝑑𝑧(𝑖) 。最后,我们得到 𝑑𝑤 = 𝑑𝑤/𝑚 。现在我们通过将两层循环转成一层循环,我们仍然还有这个循环训练样本。

希望这个视频给了你一点向量化感觉,减少一层循环使你代码更快,但事实证明我们能做得更好。所以在下个视频,我们将进一步的讲解逻辑回归,你将会看到更好的监督学习结果。在训练中不需要使用任何 for 循环,你也可以写出代码去运行整个训练集。到此为止一切都好,让我们看下一个视频。

2.13 向量化逻辑回归(Vectorizing Logistic Regression)

我们已经讨论过向量化是如何显著加速你的代码,在本次视频中我们将讨论如何实现逻辑回归的向量化计算。这样就能处理整个数据集,甚至不会用一个明确的 for 循环就能实现对于整个数据集梯度下降算法的优化。我对这项技术感到非常激动,并且当我们后面谈到神经网络时同样也不会用到一个明确的 for 循环。

让我们开始吧,首先我们回顾一下逻辑回归的前向传播步骤。所以,如果你有 𝑚 个训练样本,然后对第一个样本进行预测,你需要这样计算。计算 z,我正在使用这个熟悉的公式 z ( 1 ) = w T x ( 1 ) + b z^{(1)} = w^Tx^{(1)} + b z(1)=wTx(1)+b。然后计算激活函数 a ( 1 ) = σ ( z ( 1 ) ) a^{(1)} = σ(z^{(1)}) a(1)=σ(z(1)) ,计算第一个样本的预测值 𝑦 。

对第二个样本进行预测,你需要计算 z ( 2 ) = w T x ( 2 ) + b z^{(2)} = w^Tx^{(2)} + b z(2)=wTx(2)+b , a ( 2 ) = σ ( z ( 2 ) ) a^{(2)} = σ(z^{(2)}) a(2)=σ(z(2)) 。

对第三个样本进行预测,你需要计算 z ( 3 ) = w T x ( 3 ) + b z^{(3)} = w^Tx^{(3)} + b z(3)=wTx(3)+b , a ( 3 ) = σ ( z ( 3 ) ) a^{(3)} = σ(z^{(3)}) a(3)=σ(z(3)) ,依次类推。

如果你有 𝑚 个训练样本,你可能需要这样做 𝑚 次,可以看出,为了完成前向传播步骤,即对我们的 𝑚 个样本都计算出预测值。有一个办法可以并且不需要任何一个明确的 for 循环。让我们来看一下你该怎样做。

首先,回忆一下我们曾经定义了一个矩阵 𝑋 作为你的训练输入,(如下图中蓝色 𝑋 )像这样在不同的列中堆积在一起。这是一个 𝑛𝑥 行 𝑚 列的矩阵。我现在将它写为 Python numpy 的形式 (𝑛𝑥, 𝑚) ,这只是表示 𝑋 是一个 𝑛𝑥 乘以 𝑚 的矩阵 R n x × m R^{nx×m} Rnx×m。

现在我首先想做的是告诉你该如何在一个步骤中计算 z 1 、 z 2 、 z 3 z_1、 z_2 、z_3 z1、z2、z3等等。实际上,只用了一行代码。所以,我打算先构建一个 1 × 𝑚 的矩阵,实际上它是一个行向量,同时我准备计算 z ( 1 ) , z ( 2 ) . . . . . . z^{(1)},z^{(2)}...... z(1),z(2)......一直到 z ( m ) z^{(m)} z(m) ,所有值都是在同一时间内完成。结果发现它可以表达为 𝑤 的转置乘以大写矩阵 𝑥 然后加上向量 [ b b . . . b ] ) [bb. . . b]) [bb...b]), ( [ z ( 1 ) z ( 2 ) . . . . . z ( m ) ] = w T + [ b b . . . b ] ) ([z^{(1)}z^{(2)}..... z^{(m)}] = w^T +[bb. . . b]) ([z(1)z(2).....z(m)]=wT+[bb...b])。 [ b b . . . b ] ) [bb. . . b]) [bb...b]) 是一个 1 × 𝑚 的向量或者 1 × 𝑚 的矩阵或者是一个 𝑚 维的行向量。

所以希望你熟悉矩阵乘法,你会发现的 𝑤 转置乘以 x ( 1 ) , x ( 2 ) x^{(1)} , x^{(2)} x(1),x(2) 一直到 x ( m ) x^{(m)} x(m) 。所以 𝑤转置可以是一个行向量。所以第一项 w T X w^TX wTX将计算 𝑤 的转置乘以 x ( 1 ) x^{(1)} x(1), 𝑤 转置乘以 x ( 2 ) x^{(2)} x(2) 等等。然后我们加上第二项 [ b b . . . b ] ) [bb. . . b]) [bb...b]),你最终将 b加到了每个元素上。所以你最终得到了另 一 个 1 × m 的 向 量 , [ z ( 1 ) z ( 2 ) . . . . . z ( m ) ] = w T X + [ b b . . . b ] = [ w T x ( 1 ) + b , w T x ( 2 ) + b . . . w T x ( m ) + b ] [z^{(1)}z^{(2)}..... z^{(m)}] = w^TX + [bb. . . b] = [w^Tx^{(1)} + b, w^Tx^{(2)} +b. . . w^Tx^{(m)} + b] [z(1)z(2).....z(m)]=wTX+[bb...b]=[wTx(1)+b,wTx(2)+b...wTx(m)+b] 。

w T x ( 1 ) + b w^Tx^{(1)} + b wTx(1)+b这是第一个元素, w T x ( 2 ) + b w^Tx^{(2)} +b wTx(2)+b 这是第二个元素, w T x ( m ) + b w^Tx^{(m)} + b wTx(m)+b 这是第 𝑚 个元素。

如果你参照上面的定义,第一个元素恰好是 z ( 1 ) z^{(1)} z(1) 的定义,第二个元素恰好是 z ( 2 ) z^{(2)} z(2) 的定义,等等。所以,因为𝑋是一次获得的,当你得到你的训练样本,一个一个横向堆积起来,这里我将 [ z ( 1 ) z ( 2 ) . . . . . z ( m ) ] [z^{(1)}z^{(2)}..... z^{(m)}] [z(1)z(2).....z(m)]定义为大写的 𝑍 ,你用小写 𝑧 表示并将它们横向排在一起。所以当你将不同训练样本对应的小写 𝑥 横向堆积在一起时得到大写变量 𝑋 并且将小写变量也用相同方法处理,将它们横向堆积起来,你就得到大写变量 𝑍 。结果发现,为了计算 w T X + [ b b . . . b ] w^TX +[bb. . . b] wTX+[bb...b] ,numpy 命令是𝑍 = 𝑛𝑝. 𝑑𝑜𝑡(𝑤. 𝑇,𝑋) + 𝑏。这里在 Python 中有一个巧妙的

地方,这里 𝑏 是一个实数,或者你可以说是一个 1 × 1 矩阵,只是一个普通的实数。但是当你将这个向量加上这个实数时,Python 自动把这个实数 𝑏 扩展成一个 1 × 𝑚 的行向量。所以这种情况下的操作似乎有点不可思议,它在 Python 中被称作广播(brosdcasting),目前你不用对此感到顾虑,我们将在下一个视频中进行进一步的讲解。话说回来它只用一行代码,用这一行代码,你可以计算大写的 𝑍,而大写 𝑍 是一个包含所有小写 z ( 1 ) z^{(1)} z(1)到 z ( m ) z^(m) z(m)的 1 ×𝑚 的矩阵。这就是 𝑍 的内容,关于变量 𝑎 又是如何呢?

我们接下来要做的就是找到一个同时计算 [ a ( 1 ) a ( 2 ) . . . a ( m ) ] [a^{(1)}a^{(2)}. . . a^{(m)}] [a(1)a(2)...a(m)] 的方法。就像把小写 𝑥 堆积起来得到大写 𝑋 和横向堆积小写 𝑧 得到大写 𝑍 一样,堆积小写变量 𝑎 将形成一个新的变量,我们将它定义为大写 𝐴。在编程作业中,你将看到怎样用一个向量在 sigmoid 函数中进行计算。所以 sigmoid 函数中输入大写 𝑍 作为变量并且非常高效地输出大写 𝐴。你将在编程作业中看到它的细节。

总结一下,在这张幻灯片中我们已经看到,不需要 for 循环,利用 𝑚 个训练样本一次性计算出小写 𝑧 和小写 𝑎,用一行代码即可完成。Z = np.dot(w.T,X) + b这一行代码: A = [ a ( 1 ) a ( 2 ) . . . a ( m ) ] = σ ( Z ) A = [a^{(1)}a^{(2)}. . . a^{(m)}] = σ(Z) A=[a(1)a(2)...a(m)]=σ(Z) ,通过恰当地运用𝜎一次性计算所有 𝑎。这就是在同一时间内你如何完成一个所有 𝑚 个训练样本的前向传播向量化计算。

概括一下,你刚刚看到如何利用向量化在同一时间内高效地计算所有的激活函数的所有𝑎值。接下来,可以证明,你也可以利用向量化高效地计算反向传播并以此来计算梯度。让我们在下一个视频中看该如何实现。

2.14 向量化 logistic 回归的梯度输出(Vectorizing Logistic Regression's Gradient)

如何向量化计算的同时,对整个训练集预测结果𝑎,这是我们之前已经讨论过的内容。在本次视频中我们将学习如何向量化地计算𝑚个训练数据的梯度,本次视频的重点是如何同时计算 𝑚 个数据的梯度,并且实现一个非常高效的逻辑回归算法(Logistic Regression)。

之前我们在讲梯度计算的时候,列举过几个例子, d z ( 1 ) = a ( 1 ) − y ( 1 ) , d z ( 2 ) = a ( 2 ) − y ( 2 ) ... ... dz^{(1)} = a^{(1)} − y^{(1)},dz^{(2)} = a^{(2)} −y^{(2)} ...... dz(1)=a(1)−y(1),dz(2)=a(2)−y(2)......等等一系列类似公式。现在,对 𝑚个训练数据做同样的运算,我们可以定义一个新的变量 d Z = [ d z ( 1 ) , d z ( 2 ) . . . d z ( m ) ] dZ = [dz^{(1)}, dz^{(2)}. . . dz^{(m)}] dZ=[dz(1),dz(2)...dz(m)] ,所有的 𝑑𝑧 变量横向排列,因此,𝑑𝑍 是一个 1 × 𝑚的矩阵,或者说,一个 𝑚 维行向量。在之前的幻灯片中,我们已经知道如何计算𝐴,即[a(1), a(2). . . a(m)],我们需要找到这样的一个行向量 Y = [ y ( 1 ) y ( 2 ) . . . y ( m ) ] Y = [y^{(1)}y^{(2)}. . . y^{(m)}] Y=[y(1)y(2)...y(m)] ,由此,我们可以这样计算 d Z = A − Y = [ a ( 1 ) − y ( 1 ) a ( 2 ) − y ( 2 ) . . . a ( m ) − y ( m ) ] dZ = A − Y = [a^{(1)} − y^{(1)}a^{(2)} − y^{(2)}. . . a^{(m)} − y^{(m)}] dZ=A−Y=[a(1)−y(1)a(2)−y(2)...a(m)−y(m)],不难发现第一个元素就是 d z ( 1 ) dz^{(1)} dz(1),第二个元素就是 d z ( 2 ) dz^{(2)} dz(2) ......所以我们现在仅需一行代码,就可以同时完成这所有的计算。

在之前的实现中,我们已经去掉了一个 for 循环,但我们仍有一个遍历训练集的循环,如下所示:

上述(伪)代码就是我们在之前实现中做的,我们已经去掉了一个 for 循环,但用上述方法计算 𝑑𝑤 仍然需要一个循环遍历训练集,我们现在要做的就是将其向量化!

首先我们来看 𝑑𝑏,不难发现 𝑑𝑏 = 1 m \frac{1}{m} m1 ∑ i = 1 m d z ( i ) \sum_{i=1}^m{dz^{(i)}} ∑i=1mdz(i), 之前的讲解中,我们知道所有的𝑑𝑧𝑖)已经组成一个行向量 𝑑𝑍了,

所以在 Python 中,我们很容易地想到𝑑𝑏 = 1 m \frac{1}{m} m1 ∗ 𝑛𝑝. 𝑠𝑢𝑚(𝑑𝑍);接下来看𝑑𝑤,我们先写出它的公式 d w = 1 m ∗ X ∗ d z T dw =\frac{1}{m}∗ X ∗ dz^T dw=m1∗X∗dzT其

中,𝑋 是一个行向量。因此展开后 d w = 1 m ∗ ( x ( 1 ) d z ( 1 ) + x ( 2 ) d z ( 2 ) + . . . + x m d z m ) dw =\frac{1}{m}∗ (x^{(1)}dz^{(1)} + x^{(2)}dz^{(2)}+. . . +x^{m}dz^{m}) dw=m1∗(x(1)dz(1)+x(2)dz(2)+...+xmdzm) 。因此我们可以仅用两行代码进

行计算:db = 1 m \frac{1}{m} m1∗ 𝑛𝑝. 𝑠𝑢𝑚(𝑑𝑍), d w = 1 m ∗ X ∗ d z T dw =\frac{1}{m}∗ X ∗ dz^T dw=m1∗X∗dzT。这样,我们就避免了在训练集上使用 for 循环。现在,让我们回顾一下,看看我们之前怎么实现的逻辑回归,可以发现,没有向量化是非常低效的,如下图所示代码:

我们的目标是不使用 for 循环,而是向量,我们可以这么做:

现在我们利用前五个公式完成了前向和后向传播,也实现了对所有训练样本进行预测和求导,再利用后两个公式,梯度下降更新参数。我们的目的是不使用 for 循环,所以我们就通过一次迭代实现一次梯度下降,但如果你希望多次迭代进行梯度下降,那么仍然需要 for循环,放在最外层。不过我们还是觉得一次迭代就进行一次梯度下降,避免使用任何循环比较舒服一些。

最后,我们得到了一个高度向量化的、非常高效的逻辑回归的梯度下降算法,我们将在下次视频中讨论 Python 中的 Broadcasting 技术。

相关推荐
贰十六22 分钟前
笔记:Centos Nginx Jdk Mysql OpenOffce KkFile Minio安装部署
笔记·nginx·centos
知兀31 分钟前
Java的方法、基本和引用数据类型
java·笔记·黑马程序员
醉陌离2 小时前
渗透测试笔记——shodan(4)
笔记
LateBloomer7772 小时前
FreeRTOS——信号量
笔记·stm32·学习·freertos
legend_jz2 小时前
【Linux】线程控制
linux·服务器·开发语言·c++·笔记·学习·学习方法
Komorebi.py2 小时前
【Linux】-学习笔记04
linux·笔记·学习
余炜yw2 小时前
【LSTM实战】跨越千年,赋诗成文:用LSTM重现唐诗的韵律与情感
人工智能·rnn·深度学习
莫叫石榴姐2 小时前
数据科学与SQL:组距分组分析 | 区间分布问题
大数据·人工智能·sql·深度学习·算法·机器学习·数据挖掘
96772 小时前
对抗样本存在的原因
深度学习
YRr YRr3 小时前
深度学习:神经网络中的损失函数的使用
人工智能·深度学习·神经网络