向量化编程(一)
向量化是实现各种机器学习模型(尤其是深度学习算法)的关键技术。向量化是一种并行计算范式。从硬件角度讲,它在单个数据处理单元(CPU或GPU)内对数字数组执行算术运算。SIMD是一种并行架构类型。在基于SIMD架构的计算机上有多个核心,在任意时间点上所有核心只用一个指令流处理不同的数据流,现在大多数计算机都采用了SIMD架构。SIMD本质上是采用一个控制器来控制多个处理器,同时对一组数据中的每一数据单元分别执行相同的操作,从而实现空间上的并行性的技术。比如,两个向量A=[1,2,3,4]和B=[7,2,5,9]相加,可以在同一时刻由四个核心分别计算A[1]+B[1],A[2]+B[2],A[3]+B[3],A[4]+B[4]。对于SIMD指令来说,CPU只需执行一条指令,即可完成四个加法计算操作,四个加法计算操作并行执行。现代CPU可以执行4到16个单精度(float32)并行计算,具体取决于指令类型。英特尔的AVX512,即高级矢量扩展指令集512,拥有512个寄存器,因此可以同时处理512位数据。这相当于执行16次单精度运算或8次双精度运算。“向量化"一词也指将一次只能处理一个数据点的算法转换为可以同时计算一组数据的操作的过程。一个经典的例子是向量点积运算。假设我们要计算两个向量的内积,这两个向量分别由100万个数组成。使用for循环,我们通常会这样实现这个算法:import timeitdef inner_product1 (n=1_000_000) : result = 0 a = [1]*n b = [1]*n start = timeit.default_timer() for i in range (n) : result += a[i] * b[i] end = timeit.default_timer() print (f"普通方法所需的时间{str(end-start)}") 如果我们使用Numpy和向量化来实现该算法。可以对比一下二者的运行时间,显然,通过向量化,速度有明显提升。import timeitimport numpy as npdef inner_product_vectorization (n=1_000_000) : a = np.random.rand (n) b = np.random.rand (n) start = timeit.default_timer() result = np.dot (a, b) end = timeit.default_timer() print (f"向量化方法所需的时间{str(end-start)}") 向量化操作的内存占用使用向量化操作时,我们需要权衡内存和速度。有时,向量化操作需要为中间步骤的结果分配更多内存。因此,这会影响程序的整体性能,即空间复杂度与时间复杂度之间的关系。例如,考虑一个函数,我们需要计算数组中元素的平方和。这可以用for循环来实现,遍历每个元素,计算其平方,然后再累加起来作为总和。或者,该函数也可以用向量化操作来实现:首先计算每个元素的平方,将结果存储在一个数组中,然后向量化求和。import numpy as npfrom memory_profiler import profile# memory_profiler 分析内存占用@profiledef sum_of_square_loop (vec) : res = 0 for x in vec: res += x ** 2 return res@profiledef sum_of_square_vectorized (vec): x = np.asarray (vec) x_sq = np.square (x) # x ** 2 res = x_sq.sum () return resif __name__ == '__main__': a = [1] * 1_000_000 sum_of_square_loop (a) sum_of_square_vectorized (a) 来源:数值分析与有限元编程