本节课中,我们将学习如何利用Python的矢量化来实现神经网络。
根本上讲,矢量化的目的是为了提高计算的效率,加快计算速度。
矢量化
深度学习想要发挥其优势,一个最重要的前提条件是有足够大量的数据。
而面对足够大量的数据时,代码的运算速度就非常重要了。
以Logistic回归为例,前向计算公式如下:
其中,w和b都是nx维的向量。
此时,对于一个非矢量的实现方式大致如下:z = 0
for i in range(n_x):
z += w[i] * x[i]
z += b
而在Python中的矢量运算方式如下:import numpy as np
z = np.dot(w, x) + b
而实际运行过程中,矢量的运算方式运算速度要快很多。
我们使用jupyter notebook来进行一下验证:
从上图可以看出,使用矢量的计算方法,运行时间可以降低250倍左右。
目前,很多深度学习的计算都是在GPU上实现的。
对在使用Python的Numpy库时,你可以不用考虑这一点差异。
Numpy自身会根据你的机器配置,选择最合适的并行方式进行计算,充分利用机器的并行能力。
接下来,我们用实例来描述一个矩阵和向量相乘的矢量化运算。
假设我们的需要是向量A和矢量v相乘,即
此时,非矢量化的Python代码版本如下:
而此时,矢量化的版本仅需要如下即可:
下一个例子,假设有一个向量v,你希望对向量v中的每一个元素进行矢量运算,那么实现方式如下:
此时,同样可以使用矢量化的操作:import numpy as np
u = np.exp(v)
同理,此类的函数还包含如下:np.log()
np.abs()
np.maximum(v, 0)
v ** 2
1 / v
经验:在编程实现神经网络时,尽可能的避免使用for循环。
Logistic回归中的矢量化表示
接下来,我们来看如何针对Logistic回归进行矢量化的实现:
原始的实现方式如下:
其中,里面包含了两部分循环:
第一部分:训练样本的循环,for i = 1 to m。
第二部分:nx维度的循环:
首先,我们来对第二部分进行矢量化。
Step1:我们需要调整dw的初始化方式:dw = np.zeros((n_x, 1))
Step2:调整dw的计算方式:dw += x_i * dz_i
Step3:调整求每个dw的平均值方式:dw /= m
接下来,我们来对训练集部分进行矢量化。
原始针对多个训练集的方法需要依次对每一个训练样本进行循环计算:
然而,为了进行食量还,我们可以把所有训练样本(列向量)组合成一个矩阵。
其中,X的维度是nx*m。
此时,z的计算方式变为如下方法:
即可以可以用Python代码表示如下:Z = np.dot(w.T, X) + b
Ps:此时,Z是一个1*m的向量,np.dot(w.T, X)也是一个1*m的向量,而b是一个标量。
之所以能够标量和矢量相加,是利用到了numpy中的广播功能,该功能会在后续内容中详细进行描述。
当我们得到Z后,可以很方便的利用np提供的函数来计算A。
dz的计算方式如下:
而进行矢量化的表示后,dZ,A和Y的表示如下:
此时,dZ的计算方式如下:dZ = A - Y
接下来,dw和db的原始计算方式如下:
将其进行矢量化可以得到如下结果:
最终,得到的完整的Logistic矢量表示如下:
广播
广播是numpy实现的一个特殊的功能,接下来,我们将通过一些示例来了解广播的使用。
假设下表是不同食物每100g包含的碳水化合物,蛋白质和脂肪的量。
我们希望计算出每种食物中碳水化合物,蛋白质和脂肪分别占有的比例。
那么,如果不使用显示循环,我们该如何计算呢?
Step1:假设A是一个三行四列的矩阵。首先需要对矩阵A进行按列求和:
Step2:接下来,用每个元素除以该列的和即可:
此时,矩阵A的维度为3*4,而cal.reshape(1, 4)的维度为1*4。
这就是一个典型的Python的广播示例。
那么3*4的矩阵是如何与1*4的矩阵相除么?
首先,numpy会向1*4的矩阵进行按行复制得到3*4的矩阵,然后进行元素进行相除。
所有的广播实现的机制都与此类似。
一个通用的原则可以总结如下: