向量化逻辑回归(Vectorizing Logistic Regression)
我们已经讨论过向量化是如何显著加速你的代码,在本次视频中我们将讨论如何实现逻辑回归的向量化计算。这样就能处理整个数据集,甚至不会用一个明确的 for 循环就能实现对于整个数据集梯度下降算法的优化。我对这项技术感到非常激动,并且当我们后面谈到神经网络时同样也不会用到一个明确的 for 循环。
让我们开始吧,首先我们回顾一下逻辑回归的前向传播步骤。所以,如果你有 个训练样本,然后对第一个样本进行预测,你需要这样计算。计算 ,我正在使用这个熟悉的公式 z ( 1 ) = w T x ( 1 ) + b {{z}^{(1)}}={{w}^{T}}{{x}^{(1)}}+b z(1)=wTx(1)+b 。然后计算激活函数 a ( 1 ) = σ ( z ( 1 ) ) {{a}^{(1)}}=\sigma ({{z}^{(1)}}) a(1)=σ(z(1)),计算第一个样本的预测值 。
对第二个样本进行预测,你需要计算 z ( 2 ) = w T x ( 2 ) + b {{z}^{(2)}}={{w}^{T}}{{x}^{(2)}}+b z(2)=wTx(2)+b , a ( 2 ) = σ ( z ( 2 ) ) {{a}^{(2)}}=\sigma ({{z}^{(2)}}) a(2)=σ(z(2)) 。
对第三个样本进行预测,你需要计算 z ( 3 ) = w T x ( 3 ) + b {{z}^{(3)}}={{w}^{T}}{{x}^{(3)}}+b z(3)=wTx(3)+b , a ( 3 ) = σ ( z ( 3 ) ) {{a}^{(3)}}=\sigma ({{z}^{(3)}}) a(3)=σ(z(3)) ,依次类推。如果你有 个训练样本,你可能需要这样做 次,可以看出,为了完成前向传播步骤,即对我们的 个样本都计算出预测值。有一个办法可以并且不需要任何一个明确的 for 循环。让我们来看一下你该怎样做。
首先,回忆一下我们曾经定义了一个矩阵 作为你的训练输入,(如下图中蓝色 )像这样在不同的列中堆积在一起。这是一个 n x {{n}_{x}} nx 行 列的矩阵。我现在将它写为 Python numpy 的形式 ( n x {{n}_{x}} nx, ) ,这只是表示 是一个 n x {{n}_{x}} nx 乘以 的矩阵 R n x × m {{R}^{{{n}_{x}}\times m}} Rnx×m。
现在我首先想做的是告诉你该如何在一个步骤中计算 z 1 {{z}_{1}} z1、 z 2 {{z}_{2}} z2 、 z 3 {{z}_{3}} z3 等等。实际上,只用了一行代码。所以,我打算先构建一个 1 × 的矩阵,实际上它是一个行向量,同时我准备计算 z ( 1 ) {{z}^{(1)}} z(1), z ( 2 ) {{z}^{(2)}} z(2)……一直到 z ( m ) {{z}^{(m)}} z(m) ,所有值都是在同一时间内完成。结果发现它可以表达为 的转置乘以大写矩阵 然后加上向量 [. . . ] , ( z ( 1 ) z ( 2 ) . . . z ( m ) {{z}^{(1)}}{{z}^{(2)}}...{{z}^{(m)}} z(1)z(2)...z(m)] = w T {{w}^{T}} wT +[. . . ]) 。[. . . ] 是一个 1 × 的向量或者 1 × 的矩阵或者是一个 维的行向量。所以希望你熟悉矩阵乘法,你会发现的 转置乘以 x ( 1 ) {{x}^{(1)}} x(1) , x ( 2 ) {{x}^{(2)}} x(2) 一直到 x ( m ) {{x}^{(m)}} x(m) 。所以 转置可以是一个行向量。所以第一项 w T X {{w}^{T}}X wTX将计算 的转置乘以 x ( 1 ) {{x}^{(1)}} x(1), 转置乘以 x ( 2 ) {{x}^{(2)}} x(2)等等。然后我们加上第二项 [. . . ] ,你最终将 加到了每个元素上。所以你最终得到了另 一 个 1 × 的 向 量 , [ z ( 1 ) z ( 2 ) . . . z ( m ) {{z}^{(1)}}{{z}^{(2)}}...{{z}^{(m)}} z(1)z(2)...z(m)] = w T X {{w}^{T}}X wTX + [. . . ] = [ w T x ( 1 ) {{w}^{T}}{{x}^{(1)}} wTx(1)+ , w T x ( 2 ) {{w}^{T}}{{x}^{(2)}} wTx(2) +. . . w T x ( m ) {{w}^{T}}{{x}^{(m)}} wTx(m) + ] 。
w T x ( 1 ) {{w}^{T}}{{x}^{(1)}} wTx(1)+ 这是第一个元素, w T x ( 2 ) {{w}^{T}}{{x}^{(2)}} wTx(2)+ 这是第二个元素, w T x ( m ) {{w}^{T}}{{x}^{(m)}} wTx(m)+ 这是第 个元素。
如果你参照上面的定义,第一个元素恰好是 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 {{w}^{T}}X wTX + [. . . ] ,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 ( 1 ) a ( 2 ) . . . a ( m ) {{a}^{(1)}}{{a}^{(2)}}...{{a}^{(m)}} a(1)a(2)...a(m)] = () ,通过恰当地运用一次性计算所有 。这就是在同一时间内你如何完成一个所有 个训练样本的前向传播向量化计算。
概括一下,你刚刚看到如何利用向量化在同一时间内高效地计算所有的激活函数的所有值。接下来,可以证明,你也可以利用向量化高效地计算反向传播并以此来计算梯度。让我们在下一个视频中看该如何实现。