乘法
Numpy 中有三种常用的乘法:dot
、matmul
和 multiply
,对于新手来说很容易混淆三者的用法。
1. multiply: element-wise 乘法
这种乘法也叫 Hadamard product、Schur product,在数学上是指“两个矩阵的对应元素相乘”:
但 Numpy 要更复杂一点,它操作的对象是 N 维的数组(或者更常见地叫 Tensor),不单是两个维度的矩阵;并且支持广播 (Broadcasting) 机制。
>>> a = np.array([1, 2, 3])
>>> b = np.array([4, 5, 6])
>>> np.multiply(a, b)
array([ 4, 10, 18])
>>> np.multiply(a, 2) # 进行了广播
array([2, 4, 6])
Numpy 实现了运算符重载,使用 *
可以代替 np.multiply()
,这也是官方推荐的做法。
>>> a * b # 等价于 np.multiply(a, b)
array([ 4, 10, 18])
2. matmul: 矩阵乘法
矩阵相乘,就是矩阵的乘法操作,要求左矩阵的列和右矩阵的行数要一样,即M*N维矩阵乘以和N*Y维矩阵
这是最常见的矩阵乘法:
np.matmul(a, b)
的操作对象 a
, b
只能是多维数组 (array-like),不能是标量 (scalar)。而如果 a
或 b
- 若
a
- 是向量,则把
a
- 的
dim
- 若
b
- 是向量,则把
b
- 的
dim
运算完成后再去除多余的维度。这样才能进行正常的矩阵运算。
>>> A = np.array([[1, 2], [3, 4]])
>>> B = np.array([[1, 1], [1, 1]])
>>> np.matmul(A, B)
array([[3, 3],
[7, 7]])
# 发生了维度转换,这和后面要讲的 np.dot 等价
>>> a = np.array([1, 2])
>>> b = np.array([3, 4])
>>> np.matmul(a, b)
11
Numpy 实现了运算符重载,使用 @
可以代替 np.matmul()
。
>>> a @ b # 等价于 np.matmul(a, b)
11
3. dot: 向量点乘
矩阵点乘,就是矩阵各个对应元素相乘,要求矩阵必须维数相等,即MxN维矩阵乘以MxN维矩阵 。
这是数学上的向量点乘:
>>> a = np.array([1, 2])
>>> b = np.array([3, 4])
>>> np.dot(a, b)
11
这是它的本意。但实际上对于 np.dot(a, b)
:
- 如果
a
- 和
b
- 都是二维数组(矩阵),则它等价于
np.matmul
- 如果
a
- 或
b
- 有一个是零维数组(标量),则它等价于
np.multiply
所以 np.dot
的适用性最广,可以完成 np.matmul
和 np.multiply
的工作。但非常不建议这样做:这会降低代码的可读性!
4. 总结
基于操作对象的不同,应该选用不同的乘法,下面是我总结的用法:
- 数乘向量、数乘矩阵或 Hadamard product 用
np.multiply
- (
*
- )
- 矩阵乘法用
np.matmul
- (
@
- )
- 向量点乘用
np.dot
linalg 模块
1. 概述
Numpy中的 linalg 模块包含线性代数中的函数方法,用于求解矩阵的逆矩阵、求特征值、解线性方程组以及求行列式等,有了这个模块,在涉及到矩阵时,将极大的节约我们的时间和代码量。
下面对linalg模块中的函数进行一一讲解,因为相对简单,本文直接给出实际演示,若有不明白出可评论留言。
2. numpy.linalg
2.1 计算逆矩阵 linalg.inv(A)
from numpy import *
A = array([[1, 2], [4, 5]])
B = mat(A) # [[1 2][4 5]]
print(B)
C = linalg.inv(B)
print(C) # [[-1.66666667 0.66666667][ 1.33333333 -0.33333333]]
注:矩阵必须是方阵且可逆,否则会抛出LinAlgError异常。
2.2 计算行列式 linalg.det(A)
from numpy import *
A = array([[1, 2], [4, 5]])
B = mat(A) # [[1 2][4 5]]
print(B)
C = linalg.det(B)
print(C) # -2.9999999999999996
2.3 计算特征值和特征向量
linalg模块中,eigvals()函数可以计算矩阵的特征值,而eig()函数可以返回一个包含特征值和对应的特征向量的元组
from numpy import *
A = array([[1, 2], [4, 5]])
B = mat(A) # [[1 2][4 5]]
print(B)
C = linalg.eigvals(B)
print(C) # [-0.46410162 6.46410162]
D = linalg.eig(B)
print(D) # (array([-0.46410162, 6.46410162]), matrix([[-0.80689822, -0.34372377],[ 0.59069049, -0.9390708 ]]))
2.4 求解线性方程组
linalg中的函数solve()可以求解形如 Ax = b 的线性方程组,其中 A 为矩阵,b 为一维或二维的数组,x 是未知变量
from numpy import *
A = mat("1 -2 1;0 2 -8;-4 5 9")
B = array([0, 8, -9])
print(A) # [[ 1 -2 1][ 0 2 -8][-4 5 9]]
X = linalg.solve(A, B)
print(X) # [29. 16. 3.]
2.5 奇异值分解
SVD(Singular Value Decomposition,奇异值分解)是一种因子分解运算,将一个矩阵分解为3个矩阵的乘积;
linalg模块中的svd()函数可以对矩阵进行奇异值分解,该函数返回3个矩阵——U、Sigma和V,其中U和V是正交矩阵,Sigma包含输入矩阵的奇异值。
from numpy import *
A = mat("1 -2 1;0 2 -8;-4 5 9")
U, Sigma, V = linalg.svd(A)
print(U)
# [[ 0.0154013 0.42284401 0.9060716 ]
# [-0.54920827 -0.75366444 0.36105427]
# [ 0.83554358 -0.50318272 0.22062201]]
print(Sigma) # [12.753856 5.77394316 0.02715914]
print(V)
# [[-0.26084449 0.23902565 0.93532181]
# [ 0.42182177 -0.84325917 0.33313716]
# [ 0.86834702 0.48143609 0.11913328]]
2.6 广义逆矩阵
linalg模块中的 pinv()函数可进行求解广义逆矩阵;
#注:inv函数只接受方阵作为输入矩阵,而pinv函数则没有这个限制
from numpy import *
A = mat("1 -2 1;0 2 -8")
print(A) # [[ 1 -2 1][ 0 2 -8]]
B = linalg.pinv(A)
print(B) # [[ 0.25757576 0.04545455][-0.42424242 -0.04545455][-0.10606061 -0.13636364]]
数组转置的三种方法T、transpose、swapaxes
1.数组的转置
进行矩阵运算时,经常要用数组转置,比如计算矩阵内积XT.X.这时就需要利用数组转置,如下:
>>> import numpy as np
>>> data = np.arange(10).reshape(2,5)
>>> data
array([[0, 1, 2, 3, 4],
[5, 6, 7, 8, 9]])
>>> data.T
array([[0, 5],
[1, 6],
[2, 7],
[3, 8],
[4, 9]])
>>> np.dot(data.T,data)
array([[25, 30, 35, 40, 45],
[30, 37, 44, 51, 58],
[35, 44, 53, 62, 71],
[40, 51, 62, 73, 84],
[45, 58, 71, 84, 97]])
2.轴对换之transpose
对于高维数组,可以使用轴对换来对多个维度进行变换。
>>> data = np.arange(24).reshape(2,3,4)
>>> data
这里创建了一个三维数组,各维度大小分别为2,3,4。
array([[[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]],
[[12, 13, 14, 15],
[16, 17, 18, 19],
[20, 21, 22, 23]]])
>>> data.transpose(1,0,2)
array([[[ 0, 1, 2, 3],
[12, 13, 14, 15]],
[[ 4, 5, 6, 7],
[16, 17, 18, 19]],
[[ 8, 9, 10, 11],
[20, 21, 22, 23]]])
>>>
transpose进行的操作其实是将各个维度重置,原来(2,3,4)对应的是(0,1,2)。使用transpose(1,0,2)后,各个维度大小变为(3,2,4),其实就是将第一维和第二维互换。
对于这个三维数组,转置T其实就等价于transpose(2,1,0),如下:
>>> data.transpose(2,1,0)
array([[[ 0, 12],
[ 4, 16],
[ 8, 20]],
[[ 1, 13],
[ 5, 17],
[ 9, 21]],
[[ 2, 14],
[ 6, 18],
[10, 22]],
[[ 3, 15],
[ 7, 19],
[11, 23]]])
>>> data.T
array([[[ 0, 12],
[ 4, 16],
[ 8, 20]],
[[ 1, 13],
[ 5, 17],
[ 9, 21]],
[[ 2, 14],
[ 6, 18],
[10, 22]],
[[ 3, 15],
[ 7, 19],
[11, 23]]])
3.两轴对换swapaxes:swapaxes方法接受的参数是一对轴编号,使用transpose方法是对整个轴进行对换,而swapaxes是将参数的两个轴进行对换。刚刚上面的transpose(1,0,2),实际上就是将0和1轴进行对换,因此使用swapaxes也可以实现,如下:
>>> data.swapaxes(0,1)
array([[[ 0, 1, 2, 3],
[12, 13, 14, 15]],
[[ 4, 5, 6, 7],
[16, 17, 18, 19]],
[[ 8, 9, 10, 11],
[20, 21, 22, 23]]])
>>> data
array([[[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]],
[[12, 13, 14, 15],
[16, 17, 18, 19],
[20, 21, 22, 23]]])
上面就是Numpy包里面进行数组转置和轴对换最常用的方法。