免费获取学习方案
ARTICLE DETAIL

资讯详情

深耕编程基础知识与建站技术分享的一线实战洞察。

NumPy 进阶原理:einsum、矩阵运算、BLAS/LAPACK、GIL、dtype、vectorize 与 memmap

NumPy 进阶原理:einsum、矩阵运算、BLAS/LAPACK、GIL、dtype、vectorize 与 memmap NumPy 进阶原理:einsum、矩阵运算、BLAS/LAPACK、GIL、dtype、vectorize 与 memmap本章是 NumPy 基础原理之后的进阶部分,重点解决面试中容易被追问的“NumPy 底层到底做了什么”。核心主线是:高阶表达式 → 底层线性代数库 → Python/C 边界 → GIL → dtype 与对象模型 → 磁盘映射。理解这些关系后,可以解释为什么 NumPy 适合数值计算、为什么某些写法非常快、为什么另一些看似“向量化”的写法并不快。一、einsum 是什么?1.1 einsum 的基本思想np.einsum()是 Einstein summation notation(爱因斯坦求和约定)在 NumPy 中的实现。它允许通过一个字符串明确描述:输入数组的每个轴分别对应什么索引,以及哪些索引需要求和、哪些索引需要保留。例如:a=np.array([1,2,3])b=np.array([4,5,6])np.einsum('i,i-',a,b)表示:a_i × b_i对i求和,因此:1×4 + 2×5 + 3×6 = 32也就是向量点积。二维矩阵乘法:np.einsum('ik,kj-ij',A,B)表示:C_ij = Σ_k A_ik B_kj因此:i、j → 出现在输出 k → 输入中出现,但没有出现在输出 → 对 k 求和1.2 einsum 的底层思想einsum的核心价值不是“写法更短”,而是用索引表达式描述张量运算的计算图。例如:np.einsum('ik,kj-ij',A,B)可以理解为:A(i,k) ──┐ ├── k 求和 ──→ C(i,j) B(k,j) ──┘NumPy 根据索引关系确定:哪些轴需要对齐 哪些轴需要遍历 哪些轴需要求和 输出 shape 是什么实际实现会根据操作类型、dtype、stride 和优化策略选择执行路径;复杂einsum还涉及 contraction path(收缩路径)选择。因此einsum可以统一描述很多操作:向量点积 矩阵乘法 批量矩阵乘法 转置 逐元素乘法 张量收缩 求和例如:np.einsum('ij-ji',A)相当于二维转置的索引表达。1.3 einsum 为什么强大?传统 API 往往需要多个操作:tmp=A*B result=tmp.sum(axis=...)而einsum可以直接描述:np.einsum(...)这样在某些情况下可以减少中间数组。尤其对于:多维 tensor + 多个 contraction + 复杂 axis 操作einsum非常有价值。但不要认为:einsum一定比matmul快。对于标准矩阵乘法,专门的矩阵乘法路径通常可以更好地利用 BLAS;而复杂 einsum 的性能还取决于索引表达式、内存布局、收缩顺序和实现路径。二、dot、matmul、@ 有什么区别?这是 NumPy 面试非常高频的一组 API 对比。2.1@是什么?Python 中:A @ B是矩阵乘法运算符。对于 NumPy ndarray:A @ B通常调用:np.matmul(A,B)因此:A @ B ≈ np.matmul(A, B)@本身不是 NumPy 独有语法,而是 Python 提供的矩阵乘法运算符,NumPy 通过ndarray.__matmul__()等机制实现它。2.2 matmul 的规则np.matmul专门用于矩阵乘法。二维:(2,3) @ (3,4) → (2,4)因为:(2,3) × (3,4)中间的3被求和。
返回列表