Vectorization
Last updated
Was this helpful?
Last updated
Was this helpful?
欢迎回来.向量化基本上就是 一项让你的代码变得更高效的艺术 在深度学习的实际应用中 你可能会遇到大量的训练数据 因为深度学习算法在这个情况下表现更好 所以你的代码运行的运行速度非常重要,否则 如果它运行在一个大的数据集上面 你的代码可能花费很长时间去运行,你会发现 你将要等待非常长的时间去得到结果 所以在深度学习领域 我认为实现向量化的能力已经变成一个关键的技巧 让我们从一个例子开始 让我们用一个例子开始 什么是向量化? 在逻辑回归中,你需要去计算Z=WTX+B W是列向量,X也是列向量 如果你有很多的特征,那么就会有一个非常大的向量,所以W和X是R内的nx维向量 所以去计算WTX 如果你有一个非向量化的实现 你将会做一些事情,例如Z=0 pythn代码 i in range(n-x) python代码 所以i=1,2....nx Z plus equals W I times XI. Z+=W[i]X[i] 所以你在最后z+=b 所以,这是一个非向量化的实现 你会发现这是真的很慢 作为对比,一个向量化的实现将会非常直接计算WTX 在Python 或者numpy 你实现的命令是Z=np.dot(W,X) 这是在计算WTX 你也可以直接加上B 你将会发现这个非常快 让我们用一个小例子说明一下 在我的Jupyter notebook 我将会写一些Python代码 首先,让我们导入numpy库 作为 np,例如 像下面这样我将要创建一个数据A 让我们看下打印A 现在,写下这些代码块 如果我在键盘敲击shift 和 enter 两个键 它将会执行这个代码 所以,它创建了数组A以及打印它 现在,让我们完成向量化的例子 我将要导入time库 因为我要使用那个 为了去计算两次不同的操作花费了多长时间 他们能创建一个数组A吗 通过random.rand函数随机得到 用随机数值创建了一个百万维度的数组 b = np.random.rand.(1000000) 另外一个百万维度的数组 现在 tic=time.time() 测量一下当前时间 c = np.dot (a, b). toc = time.time. 打印一下 向量化的版本 这是一个向量化的版本 现在让我们打印一下 让我们看一下持续时间 python代码 toc - tic x 1000 所以我们表达这个在毫秒级上 ms代表毫秒 我将要同时敲击Shift和 Enter 所以这个代码花费3毫秒或者这个时间的1.5倍 或许大概 1.5 或者3.5毫秒 它有点变化当我再次运行它的时候 但是好像,平均她要花费1.5毫秒 或许我这次运行是2毫秒 好吧那就这样吧! 让我们继续增加这个代码 这是非向量化的版本 让我们看看,c=0 tic = time.time. 现在它实现了一个loop python 代码:for i in range 1:1000000 我将要取出0右边的数字 C += (a,i) x (b,i) 以及 toc = time.time. 最后,打印for loop 它花费的时间是1000*toc-tic ms 目的是为了知道我们正在做这个在毫秒级别 让我们再做点其他的事情 我们打印出C的值 计算一下它,确认在两个案例中他们是相同的 我打算去敲击shift和enter去运行这个,检查一下结果 在两个案例中,向量化版本 和非向量化版本计算了相同的值 正如你知道的,250286左右 向量化版本花费了1.5毫秒 很明确,for loop和非向量化版本花费了大约400,几乎500毫秒 非向量化版本多花费了 300倍向量化版本的时间 用这个例子你将会看见如果你仅仅记住去向量化你的代码 你的代码完全运行300倍快 让我们再次运行一下它 再次运行一下它 向量化版本1.5毫秒,循环使用了 481毫秒 大约慢300倍用循环做这个 如果时间变慢 这个有很大的不同在你的代码花费一分钟去运行和 花费5个小时去运行 当你正在实现深度学习算法 你能真正的快速得到一个返回的结果 它将会更快,如果你向量化你的代码 你可能听过很多这样的话 大规模的深度学习使用了GPU或者图像处理单元实现 但是我做的所有的案例都是在Jupiter notebook上面实现,这里只有CPU CPU和GPU都有并行化的指令 他们有时候会叫做SIMD指令 这个代表了一个单独指令多维数据 这个的基础意义是什么? 如果你使用了built-in函数,像这样 np.function 或者 并不要求你实现循环的函数 这使得python中的numpy充分 利用并行化去更快的计算 这是事实在GPU和CPU上面计算 GPU被标记更加擅长 SIMD计算但是CPU事实上也不是太差 可能没有GPU那么擅长吧 你看下,怎么向量化能够加速你的代码 经验规律是无论什么时候 都避免使用明确的for循环 让我们进入到下一个视频去看下更多的向量化的案例 和开始学习向量化逻辑回归
在上一节视频中 我们通过一些例子说明了如何通过内置函数 和避免使用显式的for循环来实现向量化 这可以有效地提高代码的运行速度 让我们再看几个例子 需要记住的经验之谈是 当你在编写神经网络或逻辑回归时 都要尽可能避免使用显式的for循环 虽然有时候无法完全避免使用for循环 但如果你能使用内置函数 或者找到其他方式 来计算你想要的答案 这通常会比直接使用for循环更快 我们再来看一个例子 比如你想要计算向量u 它是矩阵A和 向量v的乘积 根据矩阵乘法的定义 u_i等于A_ijv_j 对j求和 对吧 这就是u_i的定义 非向量化的实现方法是 u=np.zeros(n, 1) u=np.zeros(n, 1) 然后对i循环 接着对j循环 然后 u[i]+=A[i][j]v[j] 这是一个两层的for循环 分别对i和j进行循环 这是一种非向量化的实现方式 向量化的实现方式是 u=np.dot(A,v) 右边这种 向量化的实现方式 消除了两层for循环 运行速度要快得多 我们再来看一个例子 假设在内存中已经有了一个向量v 你想要对向量v中的每一个元素进行指数运算 计算u等于e^v_1 e^v_2直到 e^v_n这样一个向量 这是一种非向量化的实现方式 即首先初始化u为一个零向量 然后用一个for循环每次计算一个元素 但实际上 Python和NumPy中有许多内置函数 只需调用一个函数 就可以完成这些向量计算 所以我的实现方式是 import numpy as np 然后只需调用 u=np.exp(v) 注意之前的这个显式的for循环 在这里变成了一行代码 输入为向量v 输出为向量u 右边的实现方式避免了使用显式的for循环 而且比直接使用for循环更快 实际上 Numpy中有很多支持向量值的函数 比如np.log(v)会对向量中每个元素进行对数计算 np.abs()可以计算绝对值 np.maximum(v,0)会将v中的每个元素 与0相比求最大值 v**2 计算向量v中每个元素的平方 1/v计算每个元素的倒数 等等 所以每当你想要写for循环的时候 先看看是否可以通过调用 NumPy的内置函数来避免for循环 接下来 让我们将这些知识 运用到逻辑回归的梯度下降算法实现中 看看我们是否可以至少摆脱两个for循环中的一个 这是我们计算逻辑回归中的导数的代码 这里有两个for循环 一个在这里 另一个在这里 在这个例子中 n_x等于2 但如果你的特征数量超过2个 那就需要在for循环中处理 dw_1 dw_2 dw_3等等 相当于这里有一个 for j=1...n_x 在循环中更新dw_j 接下来我们要消灭这第二个for循环 这就是我们要在这一页做的 我们的做法是 不再将dw_1 dw_2等 显式地初始化为零 移除这一段 然后令dw成为一个向量 令dw=np.zeros((n_x,1)) 即n_x乘1的向量 接着在这里 不再使用针对单个元素的for循环 而是使用这个针对向量值的运算 dw+=x^(i)dz^(i) dw+=x^(i)dz^(i) 最后 不再使用这一段 而是使用dw/=m 现在 我们从两个for循环简化为只有一个for循环 我们保留了这层针对各个训练样本的for循环 我希望本节视频能给你一种向量化的概念 通过移除一个for循环 你的代码会运行地更快 但实际上我们还可以做得更好 在下节视频中 我们会进一步讨论如何对逻辑回归进行向量化 你会惊讶地看到一种没有for循环的实现方式 不需要对于训练样本的for循环 你的代码几乎可以同时处理 整个训练集 我们下个视频再见