0

0

NumPy数组乘法详解:*运算符与np.dot()的区别与应用

霞舞

霞舞

发布时间:2025-11-26 11:41:53

|

697人浏览过

|

来源于php中文网

原创

NumPy数组乘法详解:*运算符与np.dot()的区别与应用

本文深入探讨numpy数组乘法中`*`运算符与`np.dot()`/`np.matmul()`函数的关键区别。`*`运算符执行元素级乘法,并遵循numpy的广播机制;而`np.dot()`和`np.matmul()`则用于执行矩阵乘法或点积。理解这两种操作的内在逻辑和适用场景,以及如何通过重塑数组来满足不同乘法运算的要求,对于高效、准确地使用numpy至关重要。

在NumPy中进行数组乘法时,初学者常常会对*运算符和np.dot()(或np.matmul())函数的行为感到困惑。这两种乘法操作虽然都涉及数组元素,但其数学含义和执行机制截然不同。正确区分并应用它们,是掌握NumPy高级功能的基础。

1. NumPy中的元素级乘法 (* 运算符)

*运算符在NumPy中执行的是元素级(element-wise)乘法。这意味着它会将两个数组中对应位置的元素进行相乘,并返回一个新的数组。为了使这种操作成为可能,两个数组必须具有兼容的形状。NumPy通过其强大的广播(broadcasting)机制来处理形状不匹配的数组。

广播机制

广播机制允许NumPy在执行算术运算时,自动扩展较小数组的形状,使其与较大数组的形状兼容。广播遵循一系列规则:

  1. 如果两个数组的维度数不同,那么维度较小的数组的形状会在其前面填充1,直到它们的维度数相同。
  2. 从最后一个维度开始,逐个比较两个数组的维度。如果维度相等,或者其中一个维度为1,则它们是兼容的。
  3. 如果某个维度不兼容(即不相等且都不为1),则会引发错误。

示例分析

考虑以下两个NumPy数组:

import numpy as np

a = np.array([1, 2, 3])  # 形状: (3,)
b = np.array([[1]])      # 形状: (1,1)

当我们尝试执行 a * b 时,NumPy会应用广播规则:

  1. 维度填充: a 是1维数组,b 是2维数组。a 会被视为 (1, 3)。
  2. 形状比较:
    • a 的形状: (1, 3)
    • b 的形状: (1, 1)
    • 从右往左比较:
      • 最后一个维度:3 和 1。兼容(其中一个为1)。
      • 倒数第二个维度:1 和 1。兼容(相等)。
  3. 广播过程: b 的 1 会沿着其第二个维度(列)广播3次,使其成为 [[1, 1, 1]]。a 的 [1, 2, 3] 会沿着其第一个维度(行)广播1次,使其成为 [[1, 2, 3]]。
  4. 元素级乘法: [[1, 2, 3]] * [[1, 1, 1]] 结果为 [[1*1, 2*1, 3*1]] 即 [[1, 2, 3]]。
result_element_wise = a * b
print(f"a 的形状: {a.shape}")
print(f"b 的形状: {b.shape}")
print(f"a * b 的结果: {result_element_wise}")
print(f"a * b 的结果形状: {result_element_wise.shape}")
# 输出:
# a 的形状: (3,)
# b 的形状: (1,1)
# a * b 的结果: [[1 2 3]]
# a * b 的结果形状: (1, 3)

这解释了为什么 a * b 会得到 [[1, 2, 3]]。

2. NumPy中的矩阵乘法 (np.dot() 和 np.matmul())

如果你的目标是执行线性代数中的矩阵乘法(或点积),那么应该使用 np.dot() 或 np.matmul() 函数。这两个函数在大多数情况下行为相似,但在处理多维数组时略有不同(np.matmul() 对堆叠矩阵乘法更友好)。

矩阵乘法规则

对于矩阵乘法 A @ B (或 np.dot(A, B)), 假设 A 的形状是 (m, n),B 的形状是 (n, p),那么结果矩阵的形状将是 (m, p)。关键在于第一个矩阵的列数必须等于第二个矩阵的行数。

AI Content Detector
AI Content Detector

Writer推出的AI内容检测工具

下载

实现期望结果

用户期望的结果是 [[1],[2],[3]],这是一个形状为 (3,1) 的矩阵。要实现这个结果,我们需要将 a 数组转换为一个列向量(形状为 (3,1)),然后与 b 数组进行矩阵乘法。

a_reshaped = a.reshape(3, 1) # 将 a 转换为列向量
print(f"重塑后 a 的形状: {a_reshaped.shape}")

# 使用 np.dot 进行矩阵乘法
result_dot_product = np.dot(a_reshaped, b)
print(f"np.dot(a_reshaped, b) 的结果: {result_dot_product}")
print(f"np.dot(a_reshaped, b) 的结果形状: {result_dot_product.shape}")

# 使用 np.matmul 也可以
result_matmul = np.matmul(a_reshaped, b)
print(f"np.matmul(a_reshaped, b) 的结果: {result_matmul}")
print(f"np.matmul(a_reshaped, b) 的结果形状: {result_matmul.shape}")

# 输出:
# 重塑后 a 的形状: (3, 1)
# np.dot(a_reshaped, b) 的结果:
# [[1]
#  [2]
#  [3]]
# np.dot(a_reshaped, b) 的结果形状: (3, 1)
# np.matmul(a_reshaped, b) 的结果:
# [[1]
#  [2]
#  [3]]
# np.matmul(a_reshaped, b) 的结果形状: (3, 1)

通过将 a 重塑为 (3,1),我们成功地进行了矩阵乘法,得到了期望的 (3,1) 形状的数组。

3. 特殊情况与注意事项

在上述示例中,有一个值得注意的特殊情况:在将 a 重塑为 (3,1) 后,如果继续使用 * 运算符,它竟然也给出了与 np.dot() 相同的结果!

a_reshaped = a.reshape(3, 1) # 形状: (3,1)
b = np.array([[1]])          # 形状: (1,1)

result_element_wise_reshaped = a_reshaped * b
print(f"a_reshaped * b 的结果: {result_element_wise_reshaped}")
print(f"a_reshaped * b 的结果形状: {result_element_wise_reshaped.shape}")

# 输出:
# a_reshaped * b 的结果:
# [[1]
#  [2]
#  [3]]
# a_reshaped * b 的结果形状: (3, 1)

为什么会这样?同样是广播机制在起作用:

  1. a_reshaped 形状: (3, 1)
  2. b 形状: (1, 1)
  3. 从右往左比较:
    • 最后一个维度:1 和 1。兼容。
    • 倒数第二个维度:3 和 1。兼容(其中一个为1)。
  4. 广播过程: b 的 1 会沿着其第一个维度(行)广播3次,使其成为 [[1],[1],[1]]。
  5. 元素级乘法: [[1],[2],[3]] * [[1],[1],[1]] 结果为 [[1*1],[2*1],[3*1]] 即 [[1],[2],[3]]。

重要提示:

尽管在这个特定例子中,重塑后的 a_reshaped * b 与 np.dot(a_reshaped, b) 得到了相同的结果,但这仅仅是巧合,并且是广播规则在特定形状下产生的副作用。

  • *`` 运算符始终执行元素级乘法。**
  • np.dot() / np.matmul() 始终执行矩阵乘法。

为了代码的清晰性、可读性和正确性,当你需要进行矩阵乘法时,务必使用 np.dot() 或 np.matmul()。 依赖 * 运算符在特定广播场景下“恰好”得到矩阵乘法的结果,是非常危险且容易出错的做法。在更复杂的矩阵或多维数组操作中,这种混淆可能导致难以发现的逻辑错误。

总结

理解NumPy中 * 运算符和 np.dot() / np.matmul() 的区别是进行高效数值计算的关键。

  • *`` 运算符 用于元素级乘法,并依赖于NumPy的广播机制**来处理形状不兼容的数组。
  • np.dot() 或 np.matmul() 用于矩阵乘法(或点积),要求输入数组的维度符合线性代数的规则。

始终根据你期望的数学运算类型来选择正确的工具。如果目标是对应位置元素相乘,请使用 *;如果目标是矩阵乘法,请使用 np.dot() 或 np.matmul(),并在必要时使用 reshape() 来调整数组形状以满足乘法要求。

相关专题

更多
java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

1463

2023.10.24

Go语言中的运算符有哪些
Go语言中的运算符有哪些

Go语言中的运算符有:1、加法运算符;2、减法运算符;3、乘法运算符;4、除法运算符;5、取余运算符;6、比较运算符;7、位运算符;8、按位与运算符;9、按位或运算符;10、按位异或运算符等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

228

2024.02.23

php三元运算符用法
php三元运算符用法

本专题整合了php三元运算符相关教程,阅读专题下面的文章了解更多详细内容。

85

2025.10.17

堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

388

2023.07.18

堆和栈区别
堆和栈区别

堆(Heap)和栈(Stack)是计算机中两种常见的内存分配机制。它们在内存管理的方式、分配方式以及使用场景上有很大的区别。本文将详细介绍堆和栈的特点、区别以及各自的使用场景。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

571

2023.08.10

Java 桌面应用开发(JavaFX 实战)
Java 桌面应用开发(JavaFX 实战)

本专题系统讲解 Java 在桌面应用开发领域的实战应用,重点围绕 JavaFX 框架,涵盖界面布局、控件使用、事件处理、FXML、样式美化(CSS)、多线程与UI响应优化,以及桌面应用的打包与发布。通过完整示例项目,帮助学习者掌握 使用 Java 构建现代化、跨平台桌面应用程序的核心能力。

61

2026.01.14

php与html混编教程大全
php与html混编教程大全

本专题整合了php和html混编相关教程,阅读专题下面的文章了解更多详细内容。

31

2026.01.13

PHP 高性能
PHP 高性能

本专题整合了PHP高性能相关教程大全,阅读专题下面的文章了解更多详细内容。

72

2026.01.13

MySQL数据库报错常见问题及解决方法大全
MySQL数据库报错常见问题及解决方法大全

本专题整合了MySQL数据库报错常见问题及解决方法,阅读专题下面的文章了解更多详细内容。

20

2026.01.13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
React 教程
React 教程

共58课时 | 3.6万人学习

Pandas 教程
Pandas 教程

共15课时 | 0.9万人学习

ASP 教程
ASP 教程

共34课时 | 3.6万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号