NumPy 矩阵乘法(从理论到实际示例)

2023-10-24

矩阵乘法是线性代数中的基本运算,它采用一对矩阵并产生另一个矩阵。

用数学术语来说,给定两个矩阵 ( A ) 和 ( B ),乘积 ( AB ) 是通过 ( A ) 的行与 ( B ) 的列的点积来计算的。

本教程旨在深入了解矩阵乘法NumPy,

 

 

逐元素与矩阵乘法

逐元素乘法,也称为哈达玛积,将两个形状相同的矩阵的相应元素相乘。

Example:


import numpy as np
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
result = A * B
print(result)
  

Output:


[[ 5, 12],
 [21, 32]]
  

结果矩阵中的每个元素都是矩阵 ( A ) 和 ( B ) 中相应元素的乘积:

  • ( 1 X 5 = 5 )
  • ( 2X 6 = 12 )
  • ( 3 X 7 = 21 )
  • ( 4 X 8 = 32 )

另一方面,矩阵乘法要求第一个矩阵中的列数等于第二个矩阵中的行数。

这是一个更复杂的运算,涉及行和列的点积。

使用相同的矩阵 ( A ) 和 ( B ),我们得到:


result = np.dot(A, B)
print(result)
  

Output:


[[19, 22],
 [43, 50]]
  

 

使用 np.dot 进行乘法

The np.dotNumPy 中的函数是执行矩阵乘法的主要方法之一。它处理二维数组(矩阵)和一维数组(向量)。

矩阵相乘

这是一个使用的示例np.dot两个矩阵相乘的函数:


import numpy as np
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
result = np.dot(A, B)
print(result)
  

Output:


[[19, 22],
 [43, 50]]
  

输出是通过执行标准矩阵乘法获得的,其中第一个矩阵的行与第二个矩阵的相应列相乘。

向量相乘

np.dot也可用于计算两个向量的点积:


v1 = np.array([3, 4])
v2 = np.array([5, 6])
result = np.dot(v1, v2)
print(result)
  

Output:


39
  

输出是向量 ( v1 ) 和 ( v2 ) 的点积,计算公式为 ( 3X 5 + 4X 6 = 39 )。

 

np.matmul 函数

Unlike np.dotnp.matmul专门为矩阵乘法而设计,并提供了更明确的方法来执行此操作。

使用方法如下np.matmul两个矩阵相乘:


import numpy as np
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
result = np.matmul(A, B)
print(result)
  

Output:


[[19, 22],
 [43, 50]]
  

输出与获得的结果相同np.dot, as np.matmul还执行标准矩阵乘法。

多维数组乘法

np.matmul还处理多维数组并提供数组广播的特定行为。

Example:


A = np.random.rand(2, 3, 4)
B = np.random.rand(2, 4, 3)
result = np.matmul(A, B)
print(result.shape)  

Output:


(2, 3, 3)  

在这种情况下,np.matmul对输入数组的最后两个维度执行矩阵乘法,并在其余维度上进行广播。最终的形状是(2, 3, 3).

 

运营商

在Python 3.5及更高版本中,您可以使用@运算符作为矩阵乘法的简写。该运算符提供了一种干净简洁的矩阵相乘方法。

这是如何使用的示例@两个矩阵相乘的运算符:


import numpy as np
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
result = A @ B
print(result)  

Output:


[[19, 22],
 [43, 50]]  

结果与使用获得的结果相同np.dot or np.matmul. The @运算符简化了代码,使矩阵乘法运算直观清晰。

The @运算符也可以与高维数组一起使用,就像np.matmul,遵循相同的广播规则。

 

比较 np.dot、np.matmul 和 @

了解之间的差异和相似之处np.dotnp.matmul,以及@操作员可以帮助您选择适合您特定需求的工具。

np.dot

  • 可用于矩阵乘法和向量点积。
  • 将二维数组作为一般矩阵乘法处理。
  • 对于高维数组,其行为可能不同于np.matmul.

np 矩阵相乘

  • 专为矩阵乘法而设计。
  • 在不同维度上提供一致的行为。
  • 处理多维数组最后两个维度的广播。

@ 操作员

  • 适用于 Python 3.5 及以上版本。
  • 为矩阵乘法提供干净简洁的表示法。

 

与标量相乘

除了矩阵相乘之外,您还可以将矩阵乘以标量(单个数值)。此运算将矩阵的每个元素乘以标量。


import numpy as np
A = np.array([[1, 2], [3, 4]])
scalar = 2
result = A * scalar
print(result)  

Output:


[[2, 4],
 [6, 8]]  

将矩阵 ( A ) 中的每个元素乘以标量值 2 即可获得输出:

  • ( 1X2 = 2 )
  • ( 2X 2 = 4 )
  • ( 3 X 2 = 6 )
  • ( 4 X 2 = 8 )

 

与标量的就地乘法

与标量的就地乘法意味着通过将原始矩阵与标量相乘来修改原始矩阵,而不创建矩阵的新副本。

这对于提高内存效率非常有用,尤其是在处理大型矩阵时。

您可以使用*=运算符将矩阵与标量相乘。


import numpy as np
A = np.array([[1, 2], [3, 4]])
scalar = 2
A *= scalar
print(A)  

Output:


[[2 4]
[6 8]]  

矩阵A就地乘以2,意味着原始矩阵A无需创建新矩阵即可进行修改。

您还可以使用numpy.multiply函数与out论证实现就地乘法。


A = np.array([[1, 2], [3, 4]])
scalar = 2
np.multiply(A, scalar, out=A) # In-place multiplication
print(A)  

Output:
[[2 4]
[6 8]]  

The np.乘法函数与out指向原始矩阵的参数A确保乘法就地执行。

空间复杂度比较

我们可以使用memory_profiler包来分析标量乘法的内存使用情况。


from memory_profiler import memory_usage

def inplace_multiplication(numbers, scalar):
    for i in range(len(numbers)):
        numbers[i] *= scalar

def multiplication_with_scalar(numbers, scalar):
    result = [num * scalar for num in numbers]

def main():
    numbers = [1, 2, 3, 4, 5] * 1000000
    scalar = 2
    mem_usage_inplace = memory_usage((inplace_multiplication, (numbers, scalar)))
    print(f'In-place multiplication memory used: {max(mem_usage_inplace) - min(mem_usage_inplace)} MiB')
    mem_usage_with_scalar = memory_usage((multiplication_with_scalar, (numbers, scalar)))
    print(f'Multiplication with scalar memory used: {max(mem_usage_with_scalar) - min(mem_usage_with_scalar)} MiB')
if __name__ == '__main__':
    main()
  

Output:


In-place multiplication memory used: 0.2265625 MiB
Multiplication with scalar memory used: 37.125 MiB  

就地乘法的内存使用量显着降低。

 

np.dot、np.matmul 和 @ 的时间复杂度

让我们使用以下方法测量每个方法的执行时间time module.


import numpy as np
import time
A = np.random.rand(5000, 5000)
B = np.random.rand(5000, 5000)

# Measuring time for np.dot
start_time = time.time()
result_dot = np.dot(A, B)
end_time = time.time()
print(f"np.dot execution time: {(end_time - start_time) :.2f} seconds")

# Measuring time for np.matmul
start_time = time.time()
result_matmul = np.matmul(A, B)
end_time = time.time()
print(f"np.matmul execution time: {(end_time - start_time) :.2f} seconds")

# Measuring time for @ operator
start_time = time.time()
result_at_operator = A @ B
end_time = time.time()
print(f"@ operator execution time: {(end_time - start_time) :.2f} seconds")  

输出(示例时间;实际时间可能有所不同):


np.dot execution time: 1.13 seconds
np.matmul execution time: 1.18 seconds
@ operator execution time: 1.17 seconds  

执行时间为np.dotnp.matmul,以及@操作员反映了这些方法的实际性能几乎相同。

 

并行处理和 GPU 加速

并行处理和 GPU(图形处理单元)加速可以显着加快矩阵乘法,尤其是大型矩阵。

NumPy 本身不提供直接 GPU 加速,但可以利用提供直接 GPU 加速的库。

CuPy 等库为 GPU 加速计算提供了与 NumPy 兼容的接口。

首先,安装与您的 CUDA 版本相对应的 CuPy。然后,您可以执行 GPU 加速的矩阵乘法,如下所示:


import cupy as cp
A_gpu = cp.random.rand(1000, 1000)
B_gpu = cp.random.rand(1000, 1000)

# Matrix multiplication on the GPU
result_gpu = A_gpu @ B_gpu  

只需将 NumPy 替换为 CuPy,您就可以在 GPU 上执行相同的代码。

这可以显着提高大规模矩阵乘法的性能。

注意事项

  • GPU 加速对于可以充分利用 GPU 并行性的大型矩阵最为有效。
  • CPU 和 GPU 之间的数据传输可能会带来开销,尤其是对于小型矩阵。
  • GPU 加速库并不支持所有 NumPy 函数,因此应检查兼容性。

 

类型错误

在矩阵乘法中,了解所涉及矩阵的数据类型至关重要。

混合整数和浮点矩阵可能会导致意外结果或类型错误。以下是处理它们的方法:

隐式类型转换

将整数矩阵与浮点矩阵相乘时,NumPy 会执行隐式类型转换,将整数矩阵转换为浮点矩阵。


import numpy as np
A = np.array([[1, 2], [3, 4]], dtype=int)
B = np.array([[1.5, 2.5], [3.5, 4.5]], dtype=float)
result = A @ B
print(result.dtype)  

Output:


float64
  

这里,整数矩阵A在乘法之前隐式转换为 float,结果是 float 矩阵。

显式类型转换

将矩阵显式转换为通用数据类型可以防止意外行为。

Example:


A = A.astype(float)  # Explicitly converting A to float
result = A @ B  

通过显式转换A要浮动,您需要确保乘法按预期进行,不会出现与数据类型相关的意外情况。

 

使用矩阵乘法的真实示例

缩放计算机图形学中的 2D 对象

假设您有一个由顶点定义的 2D 三角形,并且您想要缩放它。

1. 定义对象

首先将三角形顶点的坐标定义为矩阵。


import numpy as np

# Vertices of the triangle
triangle_vertices = np.array([
    [0, 0],
    [4, 0],
    [2, 3]
])
  

在这里,顶点增加了一个附加维度,以使用齐次坐标处理平移。

2. 定义缩放矩阵

创建一个缩放矩阵,在 x 方向缩放 2,在 y 方向缩放 3。


scale_x = 2
scale_y = 3
scaling_matrix = np.array([
    [scale_x, 0],
    [0, scale_y]
])
  

3. 执行缩放

将顶点乘以缩放矩阵以缩放三角形。


scaled_triangle_vertices = triangle_vertices.dot(scaling_matrix)
print(scaled_triangle_vertices)  

Output:


array([[0, 0],
       [8, 0],
       [4, 9]]))  

该三角形在 x 方向上缩放了 2 倍,在 y 方向上缩放了 3 倍。

让我们绘制缩放后的坐标,在matplotlib中绘制缩放后的对象:


import matplotlib.pyplot as plt
def plot_triangle(vertices, color, label):
    plt.plot(*vertices.T, color=color, label=label) # Plot the edges
    plt.scatter(*vertices.T, color=color) # Plot the vertices
    plt.fill(vertices[:, 0], vertices[:, 1], alpha=0.1, color=color) # Fill the triangle

# Plot the original triangle
plot_triangle(triangle_vertices, color='blue', label='Original Triangle')

# Plot the scaled triangle
plot_triangle(scaled_triangle_vertices, color='red', label='Scaled Triangle')

# Add labels and legend
plt.xlabel('X')
plt.ylabel('Y')
plt.legend()
plt.grid(True)
plt.axis('equal') # Equal scaling ensures that the plot is not distorted
plt.title('Scaling Transformation of a 2D Triangle')
plt.show()
  

Output:

图像旋转

首先,图像以颜色加载,并且由于 OpenCV 默认以 BGR 读取图像,因此颜色方案从 BGR 转换为 RGB。还导入了必要的库。


import numpy as np
from scipy.ndimage import affine_transform
import cv2
import matplotlib.pyplot as plt
image = cv2.imread("image.jpg")
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # Convert from BGR to RGB  

使用矩阵乘法组合变换

然后我们定义旋转角度并创建相应的旋转矩阵。

此外,我们计算变换矩阵以将旋转中心移动到图像的中心。

然后使用矩阵乘法将这些变换组合成full_transform matrix.


angle_degrees = 60
angle_radians = np.radians(angle_degrees)
rotation_matrix = np.array([[np.cos(angle_radians), -np.sin(angle_radians), 0],
                            [np.sin(angle_radians), np.cos(angle_radians), 0],
                            [0, 0, 1]])
height, width, _ = image.shape
center_x, center_y = width // 2, height // 2
transform_matrix = np.array([[1, 0, -center_x],
                             [0, 1, -center_y],
                             [0, 0, 1]])

# Combine the shift and rotation using matrix multiplication
full_transform = np.dot(np.linalg.inv(transform_matrix), rotation_matrix)
full_transform = np.dot(full_transform, transform_matrix)
  

在最后一步中,使用以下方法将旋转应用于图像affine_transform来自 SciPy 库的函数。

这是针对每个颜色通道单独完成的,以处理图像的 3D 特性。最后,使用 Matplotlib 显示旋转后的图像。


rotated_image = np.zeros_like(image)
for channel in range(image.shape[2]):
    rotated_image[..., channel] = affine_transform(image[..., channel], full_transform[:2, :2], offset=(full_transform[0, 2], full_transform[1, 2]))
plt.imshow(rotated_image)
plt.title("Rotated Image")
plt.axis("off")
plt.show()
  

Output:

 

资源

https://numpy.org/doc/stable/reference/ generated/numpy.dot.html

https://numpy.org/doc/stable/reference/ generated/numpy.matmul.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

NumPy 矩阵乘法(从理论到实际示例) 的相关文章

随机推荐

  • 了解 Terraform 语法和配置文件

    Terraform 由 HashiCorp 开发 是一款因其在基础设施即代码 IaC 方面的强大功能而广为人知的工具 本综合指南旨在让您深入了解 Terraform 语法 特别关注配置文件 了解 Terraform 语法 Terraform
  • Fedora 26 发布,新功能及升级步骤

    Fedora 26 发布 最终的 Fedora 26 已发布于2017 年 7 月 11 日 Fedora 26有各种软件升级 安全补丁 该版本将与 GENOME 3 24 一起发布 您可以从以下位置下载 Fedora 26 预发行版 Be
  • 如何重命名 SQL Server 数据库

    Microsoft SQL Server 为您提供了多种重命名数据库的方法 您可以使用 SQL Server Management Studio 或运行 SQL 查询来重命名数据库 在重命名数据库之前 请记住 它可能会中断使用当前数据库名称
  • 如何使用SCP命令安全传输文件

    SCP 安全复制 是一个命令行实用程序 允许您在两个位置之间安全地复制文件和目录 With scp 您可以复制文件或目录 从本地系统到远程系统 从远程系统到本地系统 在本地系统的两个远程系统之间 当传输数据时scp 文件和密码都经过加密 因
  • 如何在 CentOS 7 上安装 VMware Workstation Player

    VMware是一个成熟稳定的虚拟化解决方案 允许您在一台机器上运行多个独立的操作系统 您可以创建自己的虚拟机并评估许多软件供应商作为虚拟设备分发的软件 这些软件可从VMware 解决方案交流 本教程将指导您完成在 CentOS 7 上安装
  • 如何在 Ubuntu 18.04 上安装 Java

    Java 是世界上最流行的编程语言之一 用于构建不同类型的跨平台应用程序 本教程介绍如何在 Ubuntu 18 04 上安装各种版本的 OpenJDK 以及 Oracle Java 相同的说明适用于 Ubuntu 16 04 和任何基于 U
  • 如何在 Ubuntu 18.04 上安装 Apache Cassandra

    Apache Cassandra 是一个免费的开源 NoSQL 数据库 不存在单点故障 它提供线性可扩展性和高可用性 而不会影响性能 Apache Cassandra 被许多组织使用 包括 Apple NetFlix eBay 和 Easo
  • Linux 中的超时命令

    timeout是一个命令行实用程序 它运行指定的命令 并在给定时间段后终止该命令 如果该命令仍在运行 换句话说 timeout允许您运行有时间限制的命令 这timeout命令是 GNU 核心实用程序包的一部分 几乎所有 Linux 发行版上
  • 如何在 CentOS 7 上添加交换空间

    交换空间是磁盘上的空间 当物理 RAM 内存已满时使用 当 Linux 系统耗尽 RAM 时 非活动页面将从 RAM 移至交换空间 交换空间可以采用专用交换分区或交换文件的形式 在大多数情况下 当在虚拟机上运行 CentOS 时 不存在交换
  • 如何在 Ubuntu 18.04 上设置 Apache 虚拟主机

    在本教程中 我们将提供有关如何在 Ubuntu 18 04 上设置 Apache 虚拟主机的分步说明 Apache 虚拟主机允许您在一台计算机上运行多个网站 使用虚拟主机 您可以指定站点文档根目录 包含网站文件的目录 为每个站点创建单独的安
  • 如何在 Ubuntu 20.04 上安装和配置 VNC

    虚拟网络计算 VNC 是一种图形桌面共享系统 允许您使用键盘和鼠标远程控制另一台计算机 它是 Microsoft 的开源替代品远程桌面协议 RDP 本文介绍如何在 Ubuntu 20 04 上安装和配置 VNC 服务器 我们还将向您展示如何
  • 如何在 Ubuntu 18.04 上禁用防火墙

    Ubuntu 附带了一个名为 UFW 简单防火墙 的防火墙配置工具 UFW 是一个用户友好的前端 用于管理 iptables 防火墙规则 其主要目标是使管理防火墙规则更容易 或者顾名思义 不复杂 强烈建议保持防火墙启用 但是 在某些情况下
  • 如何在 Debian 9 上安装 Jenkins

    Jenkins是一个开源自动化服务器 提供了一种设置持续集成和持续交付 CI CD 管道的简单方法 持续集成 CI 是一种 DevOps 实践 团队成员定期将代码更改提交到版本控制存储库 然后运行自动化构建和测试 持续交付 CD 是自动构建
  • 如何在 Debian 10 上配置 MySQL (MariaDB) 主从复制

    MySQL 复制是将数据从一台数据库服务器 主服务器 复制到一台或多台服务器 从服务器 的过程 MySQL 支持多种复制拓扑 其中主 从拓扑是最著名的拓扑之一 其中一台数据库服务器充当主服务器 而一台或多台服务器充当从服务器 默认情况下 复
  • 如何在 Ubuntu 18.04 上安装 Ghost

    Ghost 是一个构建在 Node js 平台之上的现代源代码发布平台 它完全可定制且易于使用 使您可以以几乎零的学习曲线发布内容 在本教程中 我们将向您展示如何使用 Nginx 作为代理 免费的 Let s Encrypt SSL 证书
  • 使用 NumPy 范数计算向量和矩阵范数

    在线性代数中 范数是为向量空间中的每个向量分配严格正的长度或大小的函数 它是矢量大小的度量 从数学上来说 对于一个向量x the p 范数定义为 x p sum xi p 1 p 在本教程中 我们将使用numpy linalg norm f
  • 在 Pandas query() 中使用字符串方法:数据帧过滤

    The Pandas 中的 query 方法允许您过滤数据帧 并且当与字符串方法结合使用时 可能性变得广泛 在本教程中 您将学习如何在query 功能来细化和过滤数据 目录 hide 1 检查子字符串 str contains 2 str
  • Seaborn histplot(用直方图可视化数据)

    Seaborn 是最广为人知的数据可视化库之一 运行在Matplotlib 通过 Seaborn 我们可以渲染各种类型的绘图 并提供简单 直观且高度可定制的 API 来生成数据可视化 如果没有丰富的可视化 就很难理解数据并与数据进行交流 想
  • Kivy 教程 – 使用 Python 构建桌面 GUI 应用程序

    Kivy 是一个开源 Python 库 您可以使用它在 Windows Linux macOS Android 和 iOS 上创建应用程序 我们将讨论如何使用 Kivy 按钮 标签 回收视图 滚动视图 Kivy Canvas 和其他小部件来
  • NumPy 矩阵乘法(从理论到实际示例)

    矩阵乘法是线性代数中的基本运算 它采用一对矩阵并产生另一个矩阵 用数学术语来说 给定两个矩阵 A 和 B 乘积 AB 是通过 A 的行与 B 的列的点积来计算的 本教程旨在深入了解矩阵乘法NumPy 目录 hide 1 逐元素与矩阵乘法 2