PYTHON机器学习数学基础:矩阵基本知识
什么是矩阵?
矩阵是向量的集合,把多个向量组织在一起就构成了一个矩阵。例如在三维空间内,有A、B、C三个向量。
将A、B、C三个向量按照行的方式组织在一起构成了矩阵M:
将A、B、C三个向量按照列的方式组织在一起构成了矩阵T:
矩阵M的向量称为行向量,矩阵T的向量称为列向量。下面给出矩阵的定义:
矩阵是由m X n个数aij排列成的m行n列的数表,称为m行n列矩阵,简称m X n 矩阵。矩阵表示如下:
在上述定义中,可以把矩阵A看作是由m个:
向量构成的。
如果矩阵的行和列相同,即矩阵是由n X n个数aij排列成的n行n列的数表,称为n阶矩阵。
矩阵的转置运算
前面的矩阵M和矩阵T可以互相转换,这种转换称为矩阵的转置运算。矩阵的转置就是把矩阵的行列互换,行变成列,列变成行。例如对M矩阵行列互换后,就构成了矩阵T。
下面给出矩阵的转置概念:
把m X n矩阵A的行列依次互换得到n X m矩阵,称为矩阵A的转置矩阵,记作AT。
矩阵的转置运算满足下面的运算律:
(AT)T = A
转置矩阵的转置矩阵是原矩阵。
(A + B)T = AT + BT
A与B和的转置矩阵等于A的转置矩阵与B的转置矩阵的和。
(AB)T = BTAT
A与B矩阵积的转置矩阵等于B的转置矩阵与A的转置的积(顺序不能颠倒)。
矩阵的加法运算
设有矩阵A和矩阵B:
如何计算A+B和A-B呢?
两个矩阵进行加法和减法运算有一个前提条件,就是两个矩阵的行数和列数相同,在这种情况下,两个矩阵相加和相减的结果是一个新的矩阵,新矩阵的行数和列数和原来矩阵的行列数相同,其元素分别是两个矩阵对应元素的和值和差值。
矩阵的加法和减法运算可以看作矩阵内对应向量的加法或减法运算。例如在计算A+B的过程中,A的列向量或行向量分别与B的列向量或行向量相加,结果是新矩阵的列向量或行向量。
纯量与矩阵的乘法运算
纯量与矩阵相乘,结果矩阵与原矩阵的行列数相同,其元素的值是原矩阵中每个对应元素与纯量相乘的数值。
(-1)* B的计算过程如下所示:
例1:编写Python程序,实现前面矩阵A和B的加法运算和减法运算。
在Python程序中,使用嵌套列表定义一个二维数组,这个二维数组就是一个矩阵。
#使用嵌套列表定义矩阵A和B
A = [[-1,3,2],[5,7,-2],[-3,0,1]]
B = [[8,2,-1],[6,4,0],[-2,3,5]]
#定义矩阵C,存储A+B的结果
C = [[0,0,0],[0,0,0],[0,0,0]]
#定义矩阵D,存储A-B的结果
D = [[0,0,0],[0,0,0],[0,0,0]]
# 遍历A矩阵的行
for i in range(len(A)):
# 遍历A矩阵的列
for j in range(len(A[0])):
C[i][j] = A[i][j] + B[i][j]
D[i][j] = A[i][j] - B[i][j]
print(C)
print(D)
在实际应用中,一般使用numpy对矩阵进行运算。
# 导入numpy模块
import numpy as np
# 定义矩阵
A = np.array([[-1,3,2],[5,7,-2],[-3,0,1]])
B = np.array([[8,2,-1],[6,4,0],[-2,3,5]])
# 矩阵运算
print(A+B)
print(A-B)
矩阵的乘法与线性方程组
两个矩阵相乘该如何计算呢?
我们知道矩阵是列向量或者行向量的组合,两个矩阵的乘法运算实际是列向量间的线性组合运算。
例如矩阵A为3X3矩阵,矩阵B为3X1矩阵,我们来计算C=A X B,C为结果矩阵。
列向量的线性组合:
即矩阵C的第i个列向量是矩阵A的列向量组的线性组合,线性组合的纯量系数就是矩阵B第i个列向量的各个分量。
下面给出两个矩阵的乘法规则:
若矩阵A为m X p矩阵,矩阵B为p X n矩阵,矩阵C为A与B的乘积,矩阵C的第i行第j列元素可以表示为:
矩阵A和矩阵可以相乘的前提条件是:矩阵A的列数等于矩阵B的行数。
另外,矩阵的乘法不满足交换律,即A X B != B X A。
下面我们使用Python语言来实现A矩阵与B矩阵的乘法运算。
#定义矩阵A和B
A = [[15,28,5],[12,31,6],[13,29,7]]
B = [[4],[3],[2]]
#定义矩阵乘法运算函数
def matrixMul(A, B):
#创建矩阵C,矩阵C的默认元素都为0
#矩阵C的行数为A矩阵的行数,列数为B矩阵的列数
C = [[0] * len(B[0]) for i in range(len(A))]
#按行遍历A矩阵
for i in range(len(A)):
#按列遍历B矩阵
for j in range(len(B[0])):
#C矩阵的第i行第j列所对应的数值
#等于A矩阵的第i行分别乘以B矩阵的第j列之和
for k in range(len(B)):
C[i][j] += A[i][k] * B[k][j]
return C
if __name__ == '__main__':
C = matrixMul(A,B)
print(C)
使用Numpy库来计算矩阵的乘法就简单多为了。
import numpy as np
#定义矩阵A和B
A = np.array([[15,28,5],[12,31,6],[13,29,7]])
B = np.array([[4],[3],[2]])
#定义矩阵乘法运算函数
def matrixMul(A, B):
#NumPy的matmul完成矩阵乘法运算
C = np.matmul(A,B)
return C
if __name__ == '__main__':
C = matrixMul(A,B)
print(C)
矩阵最早来自于方程组的系数及常数所构成的方阵。因此矩阵的乘法规则是为了满足使用矩阵解线性方程组的要求而设定的。
下面给出一个在生活中应用矩阵乘法的案例。下表是某超市猪肉、牛肉、鸡蛋近三周的价格表
单位(元/千克)
猪肉 | 牛肉 | 鸡蛋 | |
第1周 | 15 | 28 | 5 |
第2周 | 12 | 31 | 6 |
第3周 | 13 | 29 | 7 |
已知某家庭近3周对猪肉、牛肉、鸡蛋消费支出分别为154元、153元、153元,试计算该家庭每周对猪肉、牛肉、鸡蛋的需求量。
可以列方程组求解,设该家庭每周对猪肉、牛肉、鸡蛋的需求分别为x千克、y千克、z千克,可列出三元一次方程组:
若把上面方程组的系数、未知量、已知量用矩阵的方式来表示,就可以使用矩阵运算来求解方程组。
求解三元一次方程组的问题转换为已知A矩阵,A矩阵和B矩阵的乘积C矩阵,求B矩阵的问题。Numpy库的 linalg模块的solve()函数可以使用矩阵求解线性方程组。
# 导入numpy库
import numpy as np
# 程序入口
if __name__ == '__main__':
# 建立3X3矩阵
ta = np.array([[15,28,5],
[12,31,6],
[13,29,7]
])
# 建立3维行向量
tb = np.array([154,153,153])
# 解线性方程组
x = np.linalg.solve(ta,tb)
# 输出x、y、z的值
print("x = %.2f y = %.2f z = %.2f" % (x[0],x[1],x[2]))
矩阵与线性变换
变换本质是一个函数,它是一个映射,它接受输入内容并输出对应结果。
例如函数:
当x取不同实数时,都会有唯一对应的输出结果来对应输入的x。
对线性代数来说,变换是接受一个向量,并输出一个向量。在线性代数中,一个向量到另外一个向量的映射之所以称为变换,不称为函数,是因为考虑到了向量的变换实际是向量的运动。
如:二维空间的向量A到向量B的变换,实际是向量A通过变换移动到了向量B的位置。
A =(10,0,60.0) B=(56.6,22.3)
图中向量A到向量B的变换是一种旋转变换,该变换为线性变换,它满足下面的性质。
设旋转变换为T,对线性空间V中的任意向量A和B及实数k,均有
T(A+B)= T(A)+T(B)
T(kA) = kT(A)
要验证旋转变换是否是线性变换,需要求出变换T,看变换T是否满足线性变换的两个性质。下图是二维空间向量V围绕原点逆时针旋转的示意图。
如上图所示,向量V绕原点逆时针旋转θ角,得到向量V’,假设向量V=(x,y),则向量V’为:
旋转变换T为:
若T是线性变换,应满足线性变换的两个性质。下面我们用Python程序来验证T是线性变换。验证代码如下:
import numpy as np
# 定义向量A
A = np.array([3,2])
# 定义向量B
B = np.array([-1,5])
# 定义纯量K
k = 3.14
# 定义旋转角度
r = 30
# 定义变换T函数
# v:向量 a:旋转的角度
def T(v,a):
# 转换为弧度
a = a / 180 * np.pi
# 向量v旋转a到m
m = np.array([v[0]*np.cos(a)-v[1]*np.sin(a),
v[0]*np.sin(a)+v[1]*np.cos(a)])
return m
print("T(A+B)=T(A)+T(B):%s:%s" % (str(T(A+B,r)),str(T(A,r)+T(B,r))))
print("kT(A)=T(kA):%s:%s" % (str(k*T(A,r)),str(T(k*A,r))))
输出结果如下图所示:
旋转变换T可以用矩阵表示,T也称为旋转变换矩阵:
计算向量V到向量V’的转换可以使用矩阵乘法:
使用矩阵乘法可以对多个二维向量组进行旋转:
Python代码如下:
import numpy as np
def T(a):
# 转换为弧度
a = a / 180 * np.pi
return np.array([
[ ],
[ ]
])
A = np.array([[3,2],
[ ],
[ ],
[ ],
[ ]
])
C = np.matmul(T(30),np.transpose(A))
print(C)
输出结果如下图所示:
单位矩阵与逆矩阵
在使用矩阵求解方程组中,我们列出了下面的方差组:
我们使用矩阵来解上面的方程组,方程组的系数用矩阵A表示,方程组的未知项用矩阵x表示,已知项用矩阵b表示。
方程组可表示为:
若A、x和b不是矩阵,x很容易求解:
矩阵没有定义除法运算,1/A对矩阵运算来说没有意义,但我们可以引入单位矩阵和逆矩阵这两个概念,来完成矩阵的除法运算。
在我们学过的数字中,1是一个比较特别的数字,用数字1乘以任何数还是这个数。在矩阵运算中有没有类似于数字1的矩阵呢?这就是单位矩阵。
单位矩阵是一个n阶矩阵,该矩阵的主对角线(从矩阵的左上角到右下角这一斜线上的n 个元素的位置,叫做n 阶矩阵的主对角线)元素都为1,其余元素都为0。单位矩阵用大写字母I表示。下图是3阶单位矩阵。
用n阶单位矩阵乘以n阶矩阵A,结果还是n阶矩阵A,并且满足乘法交换律。
若一个n阶矩阵A与n阶矩阵B相乘的结果是n阶单位矩阵,则这个n阶B矩阵称为A矩阵的逆矩阵。即:
A的逆矩阵被记为:
设A为n阶矩阵,A与A的逆矩阵满足如下条件:
因此对于方程组Ax=b,我们可以得到x的解:
在上面的推导公式中,只要求出A的逆矩阵,再通过矩阵的乘法运算,就可以得到x的解。
要计算A的逆矩阵,可以使用伴随矩阵法、待定系数法和初等变换法。计算逆矩阵是一个非常复杂的过程,特别是计算3阶以上的逆矩阵。矩阵的运算可以交给Python程序来完成。
# 导入numpy库
import numpy as np
# 定义矩阵A
A = np.array([[15,28,5],
[12,31,6],
[13,29,7]
])
# 定义矩阵b
b = np.array([[154,153,153]])
# 计算矩阵A的逆矩阵inverseA
inverseA = np.linalg.inv(A)
# 输出A与其逆矩阵的乘积
print(np.matmul(inverseA,A))
# 求解x
x = np.matmul(inverseA,np.transpose(b))
# 输出解x
print(x)
输出结果如下图所示:
从输出结果可以看出,x=4,y=3,z=2。还注意看到矩阵A与其逆矩阵的乘积不完全是单位矩阵,这是因为逆矩阵在数字计算机上只能表现出有限的精度,因此逆矩阵主要是作为理论工具来使用。
n阶矩阵的逆矩阵也称为非奇异矩阵,否则称为奇异矩阵。判断一个n阶矩阵A是否可逆,要看矩阵A的行列式|A|是否等于零,若等于零,则矩阵A为奇异矩阵,否则为非奇异矩阵。若A为奇异矩阵,则AX=0有无穷解,AX=b有无穷解或者无解。若A为非奇异矩阵,则AX=0有且只有唯一零解,AX=b有唯一解。
矩阵的行列式
行列式是一个从方阵(行数等于列数的矩阵)中计算出的一个标量值。
2阶矩阵(行数和列数都为2):
行列式定义为 ad-bc
我们将行列式的左上角到右下角的对角线称为主对角线,将右上角到左下角的对角线称为次对角线。根据对角线法,2阶矩阵的行列式的值等主对角线元素的积减去次对角线元素的积。
3阶矩阵(行数和列数都为3):
行列式定义为 aei+bfg+cdh-afh-bdi-ceg
三阶行列式的值等于主对角线元素的积与和主对角线平行的对角线上的元素的积的和,再减去次对角线的元素的积与和次对角线平行的对角线元素积的和。
行列式的值表示矩阵变换对体积的缩放因子。如果行列式的值为正,表示体积被放大;如果为负,表示体积被缩小且方向反转;如果为零,表示变换不可逆,即存在线性相关的列向量或行向量。
下面使用绘图来帮助理解2阶矩阵(即2x2矩阵)行列式的意义。行列式在几何上可以理解为由矩阵的两列向量构成的平行四边形的面积(在二维情况下)。当行列式为正时,表示变换后保持了原有的方向;当行列式为负时,表示变换后方向发生了反转;当行列式为零时,表示变换后面积退化为零(即向量共线)。
Python代码
import numpy as np
import matplotlib.pyplot as plt
# 定义一个2x2矩阵
A = np.array([[2, 1], [1, 3]])
# 计算行列式
det_A = np.linalg.det(A)
print(f"行列式: {det_A}")
# 矩阵的两列向量
v1 = A[:, 0]
v2 = A[:, 1]
# 原始的单位正方形及其顶点
square_vertices = np.array([[0, 0], [1, 0], [1, 1], [0, 1]])
# 通过矩阵A变换正方形
transformed_vertices = np.dot(square_vertices, A.T)
# 设置中文字体
plt.rcParams['font.sans-serif']=['SimHei']
# 设置正常显示负号
plt.rcParams['axes.unicode_minus']=False
# 绘制原始正方形和变换后的平行四边形
plt.figure(figsize=(8, 4))
# 原始正方形
plt.subplot(1, 2, 1)
plt.plot(square_vertices[:, 0], square_vertices[:, 1], 'b-', label='原始正方形')
plt.fill(square_vertices[:, 0], square_vertices[:, 1], 'b', alpha=0.3)
plt.xlim(-1, 3)
plt.ylim(-1, 3)
plt.grid(True)
plt.title('原始正方形')
# 变换后的平行四边形
plt.subplot(1, 2, 2)
plt.plot(transformed_vertices[:, 0], transformed_vertices[:, 1], 'r-', label='变换后的平行四边形')
plt.fill(transformed_vertices[:, 0], transformed_vertices[:, 1], 'r', alpha=0.3)
plt.xlim(-1, 5)
plt.ylim(-1, 5)
plt.grid(True)
plt.title(f'变换后的平行四边形\n行列式={det_A}')
plt.show()
输出结果
示例代码首先定义了一个2x2矩阵A,计算矩阵的行列式。然后创建一个代表原始单位正方形的顶点数组square_vertices,并使用矩阵A(通过其转置)来变换这个正方形的顶点,得到变换后的平行四边形顶点transformed_vertices。最后使用matplotlib绘制了原始正方形和变换后的平行四边形。通过示例可以直观地看到矩阵变换对形状的影响,以及行列式如何反映这种变换在面积上的效果。如果改变矩阵A的元素值,可以观察到变换后的平行四边形形状和面积的变化,以及行列式值的相应变化。
矩阵的秩
矩阵的秩是线性无关的列的数量或行的数量。若只看矩阵的行,那么秩就是这些行中线性无关的行的数量,称为行秩;若只看矩阵的列,那么秩就是这些列中线性无关的列的数量,称为列秩。
给定一个矩阵,例如:
A =
通过观察矩阵A,A的每一行都是前一行加上一个固定的数(对于第一行和第二行之间的差是3,第二行和第三行之间的差也是3)得到的,因此行向量是线性相关的。同样地,列向量也是线性相关的,因为每一列都可以表示为其他列的线性组合(例如,第三列是第二列的两倍减去第一列)。
我们可以使用高斯消元法来简化矩阵A,并找出A的秩。
通过行变换可以将矩阵A转化为一个行阶梯形矩阵,其中每一行都包含比它上面的行更多的非零元素,并且这些非零元素都位于不同的列上。
在这个过程中会发现有些行会变成全零行,这些行对矩阵的秩没有贡献。转换后的矩阵为:
矩阵A的秩为2,矩阵的行秩和列秩是相同的。在机器学习中,数据往往包含大量的特征,这些特征之间可能存在冗余或相关性。通过计算数据矩阵的秩,我们可以了解数据中独立信息的数量,进而通过降维技术去除冗余特征,保留最重要的信息。例如,主成分分析(PCA)就是一种基于矩阵秩的降维方法,它通过计算数据协方差矩阵的特征值和特征向量,选择特征值最大的几个特征向量作为新的特征空间,从而实现数据的降维和特征提取。
来源:郎哥编程