本文参考黄海广主编针对吴恩达深度学习课程DeepLearning.ai 《深度学习课程 笔记 (V5.1 )》
怎样通过计算偏导数来实现逻辑回归的梯度下降算法。它的关键点是几个重要公式,其作用是用来实现逻辑回归中梯度下降算法。使用计算图对梯度下降算法进行计算。
假设样本只有两个特征x1 和x 2 ,为了计算z,我们需要输入参数w 1 、w 2 和b,除此之外 还有特征值x 1 和x2 。因此z的计算公式为:
通过反向计算出导数
在之前学到了如何计算导数,以及应用梯度下降在逻辑回归的一个训练样本上。现在我们想要把它应用在m个训练样本上。
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 循环的艺术,在深度学习安全领域、深度学习实践中,你会经常发现自己训练大数据集,因为深度学习算法处理大数据集效果很棒,所以你的代码运行速度非常重要,否则如果在大数据集上,你的代码可能花费很长时间去运行,你将要等待非常长的时间去得到结果。所以在深度学习领域,运行向量化是一个关键的技巧。
非向量化方法去计算Wtx,你需要用如下方式
z=0 for i in range(n_x) z+=w[i]*x[i] z+=b向量化计算Wtx,代码如下:
z=np.dot(w,x)+b这是向量化计算𝑥 𝑈 𝑦的方法,你将会发现这个非常快
在两个方法中,向量化和非向量化计算了相同的值,如你所见,向量化版本花费了 1.5毫秒,非向量化版本的 for 循环花费了大约几乎 500 毫秒,非向量化版本多花费了 300 倍时间。所以在这个例子中,仅仅是向量化你的代码,就会运行 300 倍快。这意味着如果向量化方法需要花费一分钟去运行的数据,for 循环将会花费 5 个小时去运行。一句话总结,以上都是再说和 for 循环相比,向量化可以快速得到结果。
如何实现逻辑回归的向量化计算。
结果发现,为了计算Wtx + [bb...b] ,numpy 命令z=np.dot(w,x)+b。这里在 Python 中有一个巧妙的地方,这里 b是一个实数,或者你可以说是一个 1 × 1 矩阵,只是一个普通的实数。但是当你将这个向量加上这个实数时,Python 自动把这个实数 b扩展成一个 1 × m 的行向量,它在 Python 中被称作广播。总结一下,在这张幻灯片中我们已经看到,不需要要 for 循环,利用 m个训练样本一次性计算出小写 z 和小写 a,用一行代码即可完成。
注:本节中大写字母代表向量,小写字母代表元素
现在我们利用前五个公式完成了前向和后向传播,也实现了对所有训练样本进行预测和求导,再利用后两个公式,梯度下降更新参数。我们的目的是不使用 for 循环,所以我们就通过一次迭代实现一次梯度下降,但如果你希望多次迭代进行梯度下降,那么仍然需要 for循环,放在最外层。不过我们还是觉得一次迭代就进行一次梯度下降,避免使用任何循环比较舒服一些。
这是一个不同食物(每 100g)中不同营养成分的卡路里含量表格,表格为 3 行 4 列,列表示不同的食物种类,从左至右依次为苹果,牛肉,鸡蛋,土豆。行表示不同的营养成分,从上到下依次为碳水化合物,蛋白质,脂肪。那么,我们现在想要计算不同食物中不同营养成分中的卡路里百分比。
我们打算使用两行代码完成,第一行代码对每一列进行求和,第二行代码分别计算每种食物每种营养成分的百分比。
下面使用如下代码计算每列的和,可以看到输出是每种食物(100g)的卡路里总和。
(axis=0 竖向相加 axis=1 横向相加)其中 sum 的参数 axis=0 表示求和运算按列执行
接下来计算百分比,这条指令将 3 × 4的矩阵A除以一个1 × 4的矩阵,得到了一个 3 × 4的结果矩阵,这个结果矩阵就是我们要求的百分比含量。
下面再来解释一下 A.sum(axis = 0) 中的参数 axis 。axis 用来指明将要进行的运算是沿着哪个轴执行,在 numpy 中,0 轴是垂直的,也就是列,而 1 轴是水平的,也就是行。
而第二个 A/cal.reshape(1,4) 指令则调用了 numpy 中的广播机制。这里使用 3 × 4的矩阵𝐵除以 1 × 4的矩阵。技术上来讲,其实并不需要再将矩阵 reshape (重塑)成1 × 4,因为矩阵本身已经是 1 × 4了。但是当我们写代码时不确定矩阵维度的时候,通常会对矩阵进行重塑来确保得到我们想要的列向量或行向量。重塑操作 reshape 是一个常量时间的操作,时间复杂度是𝑃(1),它的调用代价极低。
更多的广播的例子。
在 numpy 中,当一个 4 × 1的列向量与一个常数做加法时,实际上会将常数扩展为一个4 × 1的列向量的列向量,然后两者做逐元素加法。结果就是右边的这个向量。这种广播机制对于行向量和列向量均可以使用。
再看下一个例子。
用一个 2 × 3的矩阵和一个 1 × 3 的矩阵相加,其泛化形式是 m × n 的矩阵和 1 × n的矩阵相加。在执行加法操作时,其实是将 1 × n 的矩阵复制成为 m × mn的矩阵,然后两者做逐元素加法得到结果。针对这个具体例子,相当于在矩阵的第一列加 100,第二列加 200,第三列加 300。
numpy 广播机制:如果两个数组的后缘维度的轴长度相符或其中一方的轴长度为 1,则认为它们是广播兼容的。广播会在缺失维度和轴长度为 1 的维度上进行。
总结一下 broadcasting ,可以看看下面的图:
Python 的特性允许你使用广播(broadcasting)功能,这是 Python 的 numpy 程序语言库中最灵活的地方。而我认为这是程序语言的优点,也是缺点。
优点的原因在于它们创造出语言的表达性,Python 语言巨大的灵活性使得你仅仅通过一行代码就能做很多事情。
但是这也是缺点,由于广播巨大的灵活性,有时候你对于广播的特点以及广播的工作原理这些细节不熟悉的话,你可能会产生很细微或者看起来很奇怪的 bug。例如,如果你将一个列向量添加到一个行向量中,你会以为它报出维度不匹配或类型错误之类的错误,但是实际上你会得到一个行向量和列向量的求和。
据结构。相反,如果你设置 a 为(5,1),那么这就将置于 5 行 1 列向量中。在先前的操作里 a 和 a 的转置看起来一样,而现在这样的 a 变成一个新的 a 的转置,并且它是一个行向量。请注意一个细微的差别,在这种数据结构中,当我们输出 a 的转置时有两对方括号,而之前只有一对方括号,所以这就是 1 行 5 列的矩阵和一维数组的差别。
如果你输出 a 和 a 的转置的乘积,然后会返回给你一个向量的外积,是吧?所以这两个向量的外积返回给你的是一个矩阵。
如果你每次创建一个数组,你都得让它成为一个列向量,产生一个(5,1)向量或者你让它成为一个行向量,那么你的向量的行为可能会更容易被理解。所以在这种情况下,a.shape等同于(5,1)。这种表现很像 a,但是实际上却是一个列向量。同时这也是为什么当它是一个列向量
我写代码时还有一件经常做的事,那就是如果我不完全确定一个向量的维度(dimension),我经常会扔进一个断言语句(assertion statement)。
