乘法

Numpy 中有三种常用的乘法:dotmatmul 和 multiply,对于新手来说很容易混淆三者的用法。

1. multiply: element-wise 乘法

这种乘法也叫 Hadamard productSchur product,在数学上是指“两个矩阵的对应元素相乘”:

Python numpy 矩阵元素个数 numpy中矩阵运算_对换

但 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维矩阵

这是最常见的矩阵乘法:

Python numpy 矩阵元素个数 numpy中矩阵运算_对换_02

np.matmul(a, b) 的操作对象 ab 只能是多维数组 (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维矩阵 。

 

这是数学上的向量点乘: Python numpy 矩阵元素个数 numpy中矩阵运算_对换_03

>>> 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包里面进行数组转置和轴对换最常用的方法。