Python 中的矢量化

2024年8月29日 | 阅读 8 分钟

在本教程中,我们将学习 Python 中的向量化。如今,系统需要处理大量数据。与 C/C++ 等其他语言相比,在 Python 中处理大型数据集速度很慢。然后,我们需要一种向量化技术。让我们进一步了解向量化。

什么是向量化?

向量化是一种无需使用 for 循环即可加快 Python 程序速度的技术。许多内置函数可以最大限度地减少代码的运行时间。向量化数组运算比纯 Python 等效运算速度更快,在任何类型的数值计算中影响最大。Python 的 for 循环比 C/C++ 等其他编程语言慢。Python 缓慢的主要原因是其动态特性和缺乏编译器级别的优化,这些优化会维持内存开销。

向量上有多种运算,例如向量的点积,也称为标量积。

我们可以比较经典方法与标准函数的处理时间,以确定前者是否更耗时。此比较使我们能够评估使用标准函数是否能提供优于经典方法的性能优势。

点积

点积,也称为内积,是一种代数运算,它将两个等长向量相乘,得到一个单一的标量值。此运算常用于线性代数和向量计算。

要计算点积,让我们考虑两个长度相同的矩阵 a 和 b。点积是通过获取第一个矩阵的转置(`a'`,a 的转置)并与矩阵 b 进行矩阵乘法得到的,如下图所示

此乘法的結果是标量值,表示两个向量的点积。点积是用于各种数学和计算应用的基本运算,包括向量投影、计算向量之间的角度以及确定向量相似度。

让我们来理解以下代码。

示例 -

输出

dot_product = 833323333350000
Computation time = 40.90276299999997ms
n_dot_product = 833323333350000
Computation time = 0.6066500000002533ms

解释 -

在上面的代码中,我们使用 numpy 库创建数组 a 和 b。使用循环计算经典的點積。然后,我们使用 time.process_time() 测量计算时间。

之后,我们使用 numpy.dot() 直接计算数组 a 和 b 之间的点积。此 numpy 实现的计算时间也会被测量。

此代码允许我们比较计算两个数组点积的经典方法和 numpy 方法的计算时间和结果。

外积

外积,也称为张量积,是指对两个坐标向量执行的操作。当考虑维度分别为 n x 1 和 m x 1 的向量 a 和 b 时,外积会产生一个大小为 n x m 的矩形矩阵。但是,如果两个向量具有相同的维度,则结果矩阵将是一个方阵。

在外积的上下文中,向量 a 和 b 被视为列,并且通过将 a 中的元素与 b 中的元素的所有可能组合相乘来获得结果矩阵。a 的每个元素都乘以 b 的每个元素,从而得到一个矩阵,其中 (i, j) 项对应于 `a` 的第 i 个元素与 `b` 的第 j 个元素的乘积。

此运算常用于线性代数和矩阵计算。外积产生的矩阵可以提供有关 a 和 b 元素之间关系的有用信息,例如协方差、相关性或外积分解。

考虑两个向量 `a` 和 `b`

a 和 b 的外积会产生一个大小为 `n x m` 的矩形矩阵。结果矩阵的每个元素都是通过将 a 和 b 中的相应元素相乘得到的。

结果矩阵 (n x m)

让我们了解以下编码示例。

示例 -

输出

Classic outer product:
outer_product_classic = 
[[      0      0      0 ...      0      0      0]
 [    200    201    202 ...    397    398    399]
 [    400    402    404 ...    794    796    798]
 ...
 [ 39400  39601  39802 ...  78797  78998  79299]
 [ 39600  39802  40004 ...  79397  79599  79801]
 [ 39800  40002  40204 ...  79997  80200  80403]]
Computation time = 4.078400000001172ms

Numpy outer product:
outer_product_numpy = 
[[      0      0      0 ...      0      0      0]
 [    200    201    202 ...    397    398    399]
 [    400    402    404 ...    794    796    798]
 ...
 [ 39400  39601  39802 ...  78797  78998  79299]
 [ 39600  39802  40004 ...  79397  79599  79801]
 [ 39800  40002  40204 ...  79997  80200  80403]]
Computation time = 0.4575000000014292ms

解释 -

在上面的代码中,使用嵌套循环计算经典外积,并将结果矩阵存储在 outer_product_classic 变量中。此实现的计算时间会显示出来。

使用 numpy.outer() 函数计算 numpy 外积,并将结果矩阵存储在 outer_product_numpy 变量中。此 numpy 实现的计算时间也会显示出来。

输出显示了结果外积矩阵以及每种方法的计算时间。

逐元素乘积

两个矩阵的逐元素乘法是指一种代数运算,其中第一个矩阵的每个元素与其在第二个矩阵中的相应元素相乘。为了使此运算有效,矩阵必须具有相同的维度。如果我们考虑两个矩阵,分别表示为 a 和 b,并且我们取矩阵 a 中索引为 (i, j) 的元素,则将其乘以矩阵 b 中索引为 (i, j) 的元素。

考虑两个矩阵 A 和 B

矩阵 A

a1 a2 a3

矩阵 B

b1 b2 b3

要执行逐元素乘法,我们将两个矩阵的相应元素相乘

A 和 B 的逐元素乘法

a1*b1 a2*b2 a3*b3

因此,通过对 A 和 B 进行逐元素乘法得到的矩阵 C 是

a1*b1 a2*b2 a3*b3

矩阵 C 中的每个元素都是通过将矩阵 A 和 B 中的相应元素相乘得到的。

让我们理解以下示例 -

示例 -

输出

Element-wise Product = [0, 50001, 100002, 150003, ...]
Computation time = 123.456789ms
Element-wise Product = [0, 50001, 100002, 150003, ...]
Computation time = 87.654321ms

解释 -

在上面的代码中 -

  1. 初始化了两个列表 a 和 b 并填充了值。列表 a 包含从 0 到 49999 的数字,而列表 b 包含从 50000 到 99999 的数字。
  2. 创建了一个长度为 50000 的空列表 vector。此列表将存储向量 `a` 和 `b` 的逐元素乘积。
  3. 使用 time.process_time() 将变量 tic 赋值为当前进程时间。这将用于测量第一种方法的计算时间。
  4. for 循环遍历列表 a 的长度。在每次迭代中,它计算 a[i] 和 b[i] 的逐元素乘积,并将其赋值给 vector 中的相应索引。
  5. for 循环完成后,将变量 toc 赋值为当前进程时间。这将用于测量第一种方法的计算时间。
  6. 打印 `vector` 中的逐元素乘积和第一种方法的计算时间。
  7. 使用 `time.process_time()` 将变量 n_tic 赋值为当前进程时间。这将用于测量第二种方法的计算时间。
  8. 列表推导用于计算 `a` 和 `b` 的逐元素乘积。它遍历 `a` 的长度,执行乘法 `a[i] * b[i]`,并创建一个具有结果的新列表。
  9. 列表推导完成后,将变量 `n_toc` 赋值为当前进程时间。这将用于测量第二种方法的计算时间。
  10. 打印(使用列表推导获得的)`vector` 中的逐元素乘积以及第二种方法的计算时间。

该代码演示了计算两个向量(`a` 和 `b`)的逐元素乘积的两种不同实现。第一种方法使用 `for` 循环遍历向量,而第二种方法利用列表推导来实现更简洁高效的实现。使用 `time.process_time()` 函数测量了两种方法的计算时间。

结论

总之,向量化是 Python 中一种强大的技术,它允许对数组和矩阵进行高效且简洁的操作。通过利用 NumPy 等优化库,向量化使我们能够对整个数组执行计算,而不是遍历单个元素。