代码之家  ›  专栏  ›  技术社区  ›  IBArbitrary

如何使numpy数组启动更快?

  •  0
  • IBArbitrary  · 技术社区  · 1 年前

    我的工作需要将1000阶的大型方形矩阵构造为numpy数组,其元素被解析定义为其索引的函数。现在,我启动一个零数组,并在元素上循环以构建所需的数组。这本身就需要很长的时间来评估。有没有什么方法可以让构建更高效或更快,比如使用GPU或并行计算等?

    示例:我想构建 (1000, 1000) 矩阵 J (这是我正在研究的系统的雅可比矩阵)。我有的定义 J ij 由函数给定 J_ij(i, j, a, b) 哪里 a b 是为给定矩阵固定的参数。我首先创建数组 J 的值,并沿着条目迭代并根据函数更新它们 J_ij 。完整的代码库是 given here ,但以下是唯一相关的部分:

        def J_ij(self, i: int, j: int, xi_mat: np.ndarray, eta: np.ndarray):
            """
            Calculates the ij-th element of the jacobian* for input pattern eta
    
            Parameters
            ----------
            i,j : int
                Indices of the matrix
            xi_mat : np.ndarray
                Memory pattern matrix of shape (p, n)
            eta : np.ndarray
                Input pattern of shape (n, )
    
            Returns
            -------
            float value of ij-th element of J
            """
            n = self.n
            p = self.p
            jij = 0
            for mu in range(p):
                jij += xi_mat[mu, i]*xi_mat[mu, j]*eta[i]*eta[j]
            if i == j:
                for k in range(n):
                    for mu in range(p):
                        jij -= xi_mat[mu, i]*xi_mat[mu, k]*eta[i]*eta[k]
            return jij/n
    
        def jacobian(self, eta: np.ndarray):
            """
            Generates the jacobian* for stability analysis
    
            Parameters
            ----------
            eta : np.ndarray
                Input pattern
    
            Returns
            -------
            jacobian matrix as a (n, n) numpy array
            """
            n = self.n
            J = np.zeros((n, n))
            if self.xi_mat is None:
                self.init_patterns()
            xi_mat = self.xi_mat
            for _ in range(n**2):
                i = _ // n
                j = _  % n
                J[i, j] += self.J_ij(i, j, xi_mat, eta)
            return J
    

    xi_mat 是由1和-1组成的二进制模式行的矩阵。 eta 就是这样一种二进制模式。

    我试着使用cupy而不是numpy,但由于我的专用GPU在我的Arch linux安装中未被CUDA识别(独立)问题,它实际上比numpy花费了更长的时间。

    2 回复  |  直到 1 年前
        1
  •  2
  •   nneonneo    1 年前

    你的 jacobian 函数看起来很像一个简单的矩阵乘,可以简单地矢量化为以下(假设xi_mat是p-by-n矩阵,eta是n元素向量):

    M = xi_mat * eta
    J = M.T @ M
    J -= np.diag(np.sum(J, axis=0))
    J /= n
    

    由于这使用了NumPy矩阵乘法,因此它的运行速度应该比手动循环快得多。iPhone上n=1000、p=10以及随机xi_mat和eta的计时结果:

    • 原始雅可比计算:10.37秒
    • 新计算:0.00609秒(快1700x)
        2
  •  0
  •   vahvero    1 年前

    相关的 f 这是

    def J_ij(self, i: int, j: int, xi_mat: np.ndarray, eta: np.ndarray):
        """
        Calculates the ij-th element of the jacobian* for input pattern eta
    
        Parameters
        ----------
        i,j : int
            Indices of the matrix
        xi_mat : np.ndarray
            Memory pattern matrix of shape (p, n)
        eta : np.ndarray
            Input pattern of shape (n, )
    
        Returns
        -------
        float value of ij-th element of J
        """
        n = self.n # Pass as parameter
        p = self.p # Pass as parameter
        jij = 0
        for mu in range(p):
            jij += xi_mat[mu, i] * xi_mat[mu, j] * eta[i] * eta[j]
        if i == j:
            for k in range(n):
                for mu in range(p):
                    jij -= xi_mat[mu, i] * xi_mat[mu, k] * eta[i] * eta[k]
        return jij / n
    

    对于numba来说,这是非常简单的编译代码。正在删除 self 和通过 n p 作为参数,然后使用 numba.njit 超过它将使功能更快。

    此外,您可以使用 divmod 以获得您的 i j 在调用函数中。

    i = _ // n
    j = _  % n
    # Is equivelant to 
    i, j = divmod(_, n)