NumPy-矩阵部分
NumPy-矩阵部分
NumPy 简介
numpy可用来存储和处理大型矩阵,比Python自身的嵌套列表(nested list structure)结构要高效的多。
安装NumPy
pip install numpy
导入 NumPy
在导入 NumPy 库时,大多数情况下你会看到的一个约定,是将其命名为 np
import numpy as np
数据类型和形状
NumPy 中处理数字的最常见方式是通过 ndarray
对象。它们与 Python 列表相似,但是可以有任意数量的维度。而且,ndarray
支持快速的数学运算。
NumPy里面的数据类型比Python的多。python只有int
、float
。在NumPy中除了 Python 的 int
,你可以使用 uint8
、int8
、uint16
、int16
等类型。ndarray中的每一项必须具有相同的类型,和c类似。
创建包含一个标量的 NumPy 数组:
s=np.array(5)
可以通过检查数组的 shape
属性来查看数组的形状。
s.shape
它会打印出结果,即一对空括号 ()
。这表示它的维度为零。
即使标量位于数组中,你仍然可以像正常标量一样使用它们。你可以键入:
x = s + 3
x
现在将等于 8
。如果你检查 x
的类型,会发现它可能是 numpy.int64
,因为它在使用 NumPy 类型,而不是 Python 类型。
创建一个向量:
v=np.array([1,2,3])
检查向量的 shape
属性,它将返回表示向量的一维长度的单个数字。在上面的示例中,v.shape
会返回 (3,)
。
现在有了数字,你可以看到 shape 是一个元组,其中包含每个 ndarray
的维度的大小。对于标量,它只是一个空的元组,但是向量有一个维度,所以元组包含一个数字和一个逗号。(Python 不能将 (3)
理解为具有一个项的元组,所以它需要逗号。
你可以使用索引访问向量中的元素,如下所示:
x = v[1]
现在 x
等于 2
。
NumPy 还支持高级索引技术。例如,要访问第二个元素及其后面的项,你可以这样写:
v[1:]
然后它会返回数组 [2, 3]
。
创建矩阵
m=np.array([[1,2,3],[4,5,6],[7,8,9]])
创建了一个包含数字 1 到 9 的 3x3 矩阵。
它的 shape
属性将返回元组 (3, 3)
所以要在上面的矩阵中找到数字 6
,你可以访问 m[1][2]
。
张量
张量可以有更多的维度。
如,要创建一个 3x3x2x1 的张量
t = np.array([[[[1],[2]],[[3],[4]],[[5],[6]]],[[[7],[8]],[[9],[10]],[[11],[12]]],[[[13],[14]],[[15],[16]],[[17],[17]]]])
t[2][1][1][0]
将返回 16
。
更改形状
更改数据的形状,而不实际更改其内容。例如,你可能有一个一维的向量,但是需要一个二维的矩阵。实现它的方式有两种。
假设你有以下向量:
v = np.array([1,2,3,4])
调用 v.shape
会返回 (4,)
。但如果你想要一个 1x4 矩阵呢?你可以使用 reshape
函数,就像这样:
x = v.reshape(1,4)
调用 x.shape
会返回 (1,4)
。如果你想要一个 4x1 矩阵,可以这样做:
x = v.reshape(4,1)
reshape
函数不只是添加大小为 1 的维度。
关于更改 NumPy 数组的形状还有一点:如果你看到经验丰富的 NumPy 使用者的代码,经常会看到他们使用一种特殊的切片语法,而不是调用 reshape
。使用该语法,前面的两个示例会是这样的:
x = v[None, :]
或者
x = v[:, None]
NumPy里面的矩阵运算
将矩阵元素都加一个数
values = [1,2,3,4,5]
values = np.array(values) + 5
# 现在 values 是包含 [6,7,8,9,10] 的一个 ndarray
NumPy 实际上有用于加法、乘法等运算的函数。
以下两行是等价的:
x = np.multiply(some_array, 5)
x = some_array * 5
矩阵归零:
m *= 0
# 现在 m 中的每个元素都是 0,无论它有多少维度
矩阵的平方值:
x = m * m
(或者如果你要将值赋值回 m
,则是 m *= m
)
示例:
a = np.array([[1,3],[5,7]])
a
# 显示以下结果:
# array([[1, 3],
# [5, 7]])
b = np.array([[2,4],[6,8]])
b
# 显示以下结果:
# array([[2, 4],
# [6, 8]])
a + b
# 显示以下结果:
# array([[ 3, 7],
# [11, 15]])
不兼容的形状,会收到一个错误:
a = np.array([[1,3],[5,7]])
a
# 显示以下结果:
# array([[1, 3],
# [5, 7]])
c = np.array([[2,3,6],[4,5,9],[1,8,7]])
c
# 显示以下结果:
# array([[2, 3, 6],
# [4, 5, 9],
# [1, 8, 7]])
a.shape
# 显示以下结果:
# (2, 2)
c.shape
# 显示以下结果:
# (3, 3)
a + c
# 显示以下结果:
# ValueError: operands could not be broadcast together with shapes (2,2) (3,3)
要获得矩阵乘积,可以使用 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)
tf.multiply是点乘,tf.matmul是矩阵乘法.
转置
NumPy 中获得矩阵的转置,可以直接访问其 T
属性,还有一个 transpose()
函数也可以返回同样的结果。
例如:
m = np.array([[1,2,3,4], [5,6,7,8], [9,10,11,12]])
m
# 显示以下结果:
# array([[ 1, 2, 3, 4],
# [ 5, 6, 7, 8],
# [ 9, 10, 11, 12]])
m.T
# 显示以下结果:
# array([[ 1, 5, 9],
# [ 2, 6, 10],
# [ 3, 7, 11],
# [ 4, 8, 12]])
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]])
注意它是如何同时修改转置和原始矩阵的!这是因为它们共享相同的数据副本。所以记住,将转置视为矩阵的不同视图,而不是完全不同的矩阵。