矩阵数学和NumPy
25 Feb 2018 | deep-learning |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]])
注意它是如何同时修改转置和原始矩阵的!这是因为它们共享相同的数据副本。所以记住,将转置视为矩阵的不同视图,而不是完全不同的矩阵。