矩阵数学和NumPy

Python 很方便,但也会很慢。不过,它允许你访问执行用 C 等语言编写的代码的库。NumPy 就是这样一个库:它为 Python 中的数学运算提供了一个更快速的替代方案,可以与数字组高效搭配使用 - 如矩阵。

数据类型

NumPy可以存储任意维度的数据类型:标量(0维)、向量(1维)、矩阵(2维)、张量(多维)。

>>> import numpy as np # 导入numpy命名为np
>>> s = np.array(3)    #创建零维标量
>>> s
array(3)
>>> s.shape
()   # 0维
>>> s = np.array([1, 2, 3]) # 创建一维向量
>>> s
array([1, 2, 3])
>>> s.shape
(3,) # 1维
>>> s = np.array([[1,1], [2, 2]]) # 创建二维矩阵
>>> s
array([[1, 1],
       [2, 2]])
>>> s.shape
(2, 2) # 2 维
>>> s = np.array([[[1,1], [2,2]],[[3,3], [4,4]] ]) #创建三维张量
>>> s
array([[[1, 1],
        [2, 2]],

       [[3, 3],
        [4, 4]]])
>>> s.shape
(2, 2, 2) # 3 维
>>>

更改形状

有时,你需要更改数据的形状,而无需实际更改其内容。例如,你可能有一个一维的向量,但是需要一个二维的矩阵。

>>> v = np.array([1, 2, 3]) # 创建一维向量
>>> v.shape
(3,) # 1维
>>> v
array([1, 2, 3])
>>> x = v.reshape(1, 3) #方式1: 更改形状为 1 x 3 的二维矩阵
>>> x.shape
(1, 3)# 2维
>>> x
array([[1, 2, 3]])
>>> x = v[None, :]  #方式2: 更改形状为 1 x 3 的二维矩阵
>>> x
array([[1, 2, 3]])
>>> x.shape
(1, 3)# 2维
>>> x = v.reshape(3,1)  #方式1: 更改形状为 3 x 1 的二维矩阵
>>> x.shape
(3, 1)# 2 维
>>> x
array([[1],
       [2],
       [3]])
>>> x = v[:, None]  #方式2: 更改形状为 3 x 1 的二维矩阵
>>> x.shape
(3, 1)# 2 维
>>> x
array([[1],
       [2],
       [3]])
>>> 

x = v[None, :]或者x = v[:, None] 将会创建一个切片,查看 v 的所有项目,要求 NumPy 为相关轴添加大小为 1 的新维度(在None的地方增加一个维度)。

>>> n = np.array([[1,2],[3,4]]) #创建一个二维矩阵
>>> n.shape
(2, 2)# 2 维
>>> n
array([[1, 2],
       [3, 4]])
>>> z = n[:, :, None]  #增加一维变为三维
>>> z
array([[[1],
        [2]],

       [[3],
        [4]]])
>>> z.shape
(2, 2, 1) # 3维
>>> z = n[None, :, :] #增加一维变为三维
>>> z
array([[[1, 2],
        [3, 4]]])
>>> z.shape
(1, 2, 2) # 3维
>>> z = n[:, None, :] #增加一维变为三维
>>> z
array([[[1, 2]],

       [[3, 4]]])
>>> z.shape
(2, 1, 2) # 3维
>>> 

NumPy 矩阵乘法
元素级乘法

你可以使用 multiply 函数或 * 运算符来实现。回顾一下,它看起来是这样的:

m = np.array([[1,2,3],[4,5,6]])
m
# 显示以下结果:
# array([[1, 2, 3],
#        [4, 5, 6]])

n = m * 0.25
n
# 显示以下结果:
# array([[ 0.25,  0.5 ,  0.75],
#        [ 1.  ,  1.25,  1.5 ]])

m * n
# 显示以下结果:
# array([[ 0.25,  1.  ,  2.25],
#        [ 4.  ,  6.25,  9.  ]])

np.multiply(m, n)   # 相当于 m * n
# 显示以下结果:
# array([[ 0.25,  1.  ,  2.25],
#        [ 4.  ,  6.25,  9.  ]])
矩阵乘积

要获得矩阵乘积,你可以使用 NumPy 的 matmul 函数。

如果你有兼容的形状,那就像这样简单:

a = np.array([[1,2,3,4],[5,6,7,8]])
a
# 显示以下结果:
# array([[1, 2, 3, 4],
#        [5, 6, 7, 8]])
a.shape
# 显示以下结果:
# (2, 4)

b = np.array([[1,2,3],[4,5,6],[7,8,9],[10,11,12]])
b
# 显示以下结果:
# array([[ 1,  2,  3],
#        [ 4,  5,  6],
#        [ 7,  8,  9],
#        [10, 11, 12]])
b.shape
# 显示以下结果:
# (4, 3)

c = np.matmul(a, b)
c
# 显示以下结果:
# array([[ 70,  80,  90],
#        [158, 184, 210]])
c.shape
# 显示以下结果:
# (2, 3)

如果你的矩阵具有不兼容的形状,则会出现以下错误:

np.matmul(b, a)
# 显示以下错误:
# ValueError: shapes (4,3) and (2,4) not aligned: 3 (dim 1) != 2 (dim 0)
NumPy 的 dot 函数

有时候,在你以为要用 matmul 函数的地方,你可能会看到 NumPy 的 dot 函数。事实证明,如果矩阵是二维的,那么 dot 和 matmul 函数的结果是相同的。

所以这两个结果是等价的:

a = np.array([[1,2],[3,4]])
a
# 显示以下结果:
# array([[1, 2],
#        [3, 4]])

np.dot(a,a)
# 显示以下结果:
# array([[ 7, 10],
#        [15, 22]])

a.dot(a)  # you can call你可以直接对 `ndarray` 调用 `dot` 
# 显示以下结果:
# array([[ 7, 10],
#        [15, 22]])

np.matmul(a,a)
# array([[ 7, 10],
#        [15, 22]])

虽然这两个函数对于二维数据返回相同的结果,但在用于其他数据形状时,你应该谨慎选择。

转置

在 NumPy 中获得矩阵的转置非常容易。只需访问其 T 属性即可。

NumPy 在进行转置时不会实际移动内存中的任何数据 - 只是改变对原始矩阵的索引方式 - 所以是非常高效的。

但是,这也意味着你要特别注意修改对象的方式,因为它们共享相同的数据。例如,对于上面同一个矩阵 m,我们来创建一个新的变量 m_t 来存储 m 的转置。然后看看如果我们修改 m_t 中的值,会发生什么:

m_t = m.T
m_t[3][1] = 200
m_t
# 显示以下结果:
# array([[ 1,   5, 9],
#        [ 2,   6, 10],
#        [ 3,   7, 11],
#        [ 4, 200, 12]])

m
# 显示以下结果:
# array([[ 1,  2,  3,   4],
#        [ 5,  6,  7, 200],
#        [ 9, 10, 11,  12]])

注意它是如何同时修改转置和原始矩阵的!这是因为它们共享相同的数据副本。所以记住,将转置视为矩阵的不同视图,而不是完全不同的矩阵。