科学就是矩阵乘法!到最后,一切都归结为矩阵乘法

360影视 2024-12-27 08:15 3

摘要:科学界有句名言:到最后,一切都归结为矩阵乘法。无论你是在物理学或工程学中求解偏微分方程,还是在使用经典模型或深度神经网络进行机器学习,最终在数值上,都是在某种顺序中重复地进行矩阵和向量的乘法。这些矩阵通常可能非常大,比如1000,000 x 1000, 000

科学界有句名言:到最后,一切都归结为矩阵乘法。无论你是在物理学或工程学中求解偏微分方程,还是在使用经典模型或深度神经网络进行机器学习,最终在数值上,都是在某种顺序中重复地进行矩阵和向量的乘法。这些矩阵通常可能非常大,比如1000,000 x 1000, 000。在这种情况下,如果矩阵没有特定的结构,那么就真的需要在内存中存储大量的数据值,并且算术计算也非常耗时。这被称为密集矩阵(dense matrix)

然而,在许多科学应用中,所涉及的矩阵确实具有某种结构。通常情况下,矩阵的大部分值都是零。如果是这种情况,这就是稀疏矩阵(Sparse matrices。对于稀疏矩阵,所需的内存会大幅减少。例如,如果1000,000 x 1000,000矩阵是对角矩阵,你只需存储10^6个值,而不是10^12个值。而且如果你将其与一个10^6维度的向量相乘,只需进行10^6次乘法,而不是10^12次乘法和加法。你看,区别是巨大的!即使矩阵不是对角矩阵,只要大部分值为零,内存和速度的优势也是巨大的,因为你只需保存相对较少的非零值,并且与向量的乘法只需考虑那些非零值。

如何使用稀疏矩阵

Python的SciPy包中有一个优秀的子包专门用于稀疏矩阵的实现。根据稀疏矩阵的结构以及你想用稀疏矩阵做什么,包中有不同的表示形式,这些形式针对特定任务进行了优化。但不用担心选择正确的类型!只要你使用任何稀疏矩阵类,通常会获得99%的速度和内存优势。

首先,考虑一个大的密集矩阵。我们用介于-0.5和0.5之间的随机值填充它:

import numpy as npn = 10000A = np.random.random((n, n)) - 0.5

我只使用了10000行和列,因为我不想等太久。然后让我们使用numpy的matmul函数计算⋅的乘积。

np.matmul(A, A)

在我的电脑上,这耗时7.07秒。你也可以使用numpy的dot函数,但我不确定它是否像matmul一样优化。

现在我们用相同的方法使用一个对角矩阵,并将其存储为普通矩阵:

diag_values = np.random.random(n)A_dense = np.diag(diag_values)AA_dense = np.matmul(A_dense, A_dense)

这在我的电脑上耗时7.00秒,几乎相同的时间,尽管我们知道大部分的乘法必然是零。现在让我们用稀疏矩阵做同样的操作。由于这里的矩阵是对角矩阵,我们可以使用scipy.sparse.diags:

import scipy.sparseA_sparse = scipy.sparse.diags(diag_values)

如果你print A_sparse,它会显示:

' with 10000 stored elements (1 diagonals) in DIAgonal format>

所以它确实是一个10000x10000的矩阵,但它是稀疏矩阵,并以特殊的对角线格式存储。让我们再做一次相同的乘法:

AA_sparse = A_sparse.dot(A_sparse)

在我的电脑上,这花了0.003秒,比密集矩阵快了2000多倍。为了确定结果是否相同?

np.max(np.abs(AA_dense - AA_sparse))Out: 0.0

所以,确实是相同的。

如我所说,scipy.sparse中有不同种类的稀疏矩阵表示形式。每种形式都有其优缺点。例如,使用lil稀疏矩阵类型,矩阵切片操作很快,并且改变结构(如添加元素)也很快。但算术操作(如加法和乘法)则很慢。另一方面,csr_matrix类型在算术操作上很快,但在切片和改变值时则很慢。因此,这实际上取决于你想做什么。通常,你需要以某种方式构建矩阵,然后想用它来进行计算。第一个任务涉及改变矩阵结构,第二个任务则涉及算术运算。那么你该怎么办?使用两种不同的稀疏矩阵类型。首先,使用一种在构建方面优化的类型,然后将其转换为一种在算术方面优化的类型!你只需知道哪种类型适合哪种情况。幸运的是,SciPy的文档非常出色。每种稀疏矩阵类型都有自己的文档页面,其中描述了优缺点和预期用途。

来源:老胡说科学

相关推荐