矩阵实验
- 矩阵
- 工程应用:图像平滑
- 看待矩阵的四种视角:数据、系统、变换、空间
- 线性变换
- 工程应用:图形变化
- 矩阵变化的推导
在线性代数里,用的最多的概念是【矩阵】。
一个具体的矩阵:
来源:向量的扩展,向量是横着的一排数字,每个数字代表一个维度的分量。
比方说,学生的考试科目,是有 N N N 个维度。
那在年纪成绩评比时,通常是按所有维度算的 -> V 1 = ( 语 文 、 数 学 、 英 语 、 理 科 、 文 科 ) V1 = (语文、数学、英语、理科、文科) V1=(语文、数学、英语、理科、文科)
而评比单科王是按一个维度算的 -> V 2 = ( 语 文 或 数 学 或 英 语 或 . . . ) V2 = (语文 ~或~ 数学 ~或~ 英语~ 或...) V2=(语文 或 数学 或 英语 或...)
还有一些可能只是按基础算 -> V 3 = ( 语 文 、 数 学 、 英 语 ) V3 = (语文、数学、英语) V3=(语文、数学、英语)
V 3 = ( 8 , 9 , 7 ) V3 = (8, 9, 7) V3=(8,9,7),可以用来计算和某个候选人的相似性。
每一个评比的要求都是一个向量,而又有这么多评比,所以就有了 V 1 、 V 2 、 . . . . . . 、 V n V1、V2、......、Vn V1、V2、......、Vn。
这么多向量如果把它们放在一起,该怎么排列呢?
如同所示,这种把向量按照横竖排起来的摆放方式,是很自然的结果,只不过数学家给它取了一个名字:矩阵
,并且发现了一系列相应的计算
。
所以说,矩阵就是把向量按照横竖排起来的摆放方式而得来的,矩阵不是原因,而是结果,矩阵产生的原因就是向量的扩展。
作用,是将以前的单个计算
(俩个元素的加减乘除)变成了批处理
(俩个矩阵的加减乘除)。
如:
正是这种计算方式,将以前的单个计算
(俩个元素的加减乘除)变成了批处理
(俩个矩阵的加减乘除)。
这种批处理的计算,与计算机搭配起来,简直是绝配 — 所以,线性代数对于我们来说,生活和工作都能用上。
运算:矩阵加法、矩阵数乘、矩阵乘法。
矩阵加法: [ 1 2 3 4 5 6 ] + [ 1 1 1 1 1 1 ] = [ 2 3 4 5 6 7 ] \begin{bmatrix} 1 & 2 &3 \\ 4&5 &6 \end{bmatrix} + \begin{bmatrix} 1 & 1 &1 \\ 1& 1 & 1 \end{bmatrix} = \begin{bmatrix} 2 & 3 &4 \\ 5&6 &7 \end{bmatrix} [142536]+[111111]=[253647],前提是俩个相加的矩阵的行列相同
矩阵数乘: n ∗ [ 1 2 3 4 5 6 ] = [ n 2 n 3 n 4 n 5 n 6 n ] n*\begin{bmatrix} 1 & 2 &3 \\ 4&5 &6 \end{bmatrix}=\begin{bmatrix} n & 2n &3n \\ 4n&5n &6n \end{bmatrix} n∗[142536]=[n4n2n5n3n6n]
矩阵乘法: [ 1 2 3 4 5 6 7 8 9 10 11 12 ] ∗ [ 2 7 1 2 3 6 ] = [ 1 ∗ 2 + 2 ∗ 1 + 3 ∗ 3 1 ∗ 7 + 2 ∗ 2 + 3 ∗ 6 4 ∗ 2 + 5 ∗ 1 + 6 ∗ 3 4 ∗ 7 + 5 ∗ 2 + 6 ∗ 6 7 ∗ 2 + 8 ∗ 1 + 9 ∗ 3 7 ∗ 7 + 8 ∗ 2 + 9 ∗ 6 10 ∗ 2 + 11 ∗ 1 + 12 ∗ 3 10 ∗ 7 + 11 ∗ 2 + 12 ∗ 6 ] \begin{bmatrix} 1 & 2& 3\\ 4 & 5 & 6\\ 7& 8 & 9\\ 10&11 &12 \end{bmatrix}*\begin{bmatrix} 2 & 7\\ 1& 2\\ 3&6 \end{bmatrix} = \begin{bmatrix} 1*2+2*1+3*3 &1*7+2*2+3*6 \\ 4*2+5*1+6*3 &4*7+5*2+6*6 \\ 7*2+8*1+9*3 &7*7+8*2+9*6 \\ 10*2+11*1+12*3 &10*7+11*2+12*6 \end{bmatrix} ⎣⎢⎢⎡147102581136912⎦⎥⎥⎤∗⎣⎡213726⎦⎤=⎣⎢⎢⎡1∗2+2∗1+3∗34∗2+5∗1+6∗37∗2+8∗1+9∗310∗2+11∗1+12∗31∗7+2∗2+3∗64∗7+5∗2+6∗67∗7+8∗2+9∗610∗7+11∗2+12∗6⎦⎥⎥⎤
第一个矩阵的形状是 4 ∗ 3 4*3 4∗3,第二个矩阵的形状是 3 ∗ 2 3*2 3∗2 ,俩俩相乘的矩阵是 4 ∗ 2 4*2 4∗2,中间的 3 3 3 被划掉了 ---- 因此俩个矩阵相乘需要满足一个条件,乘号前的矩阵的 列数 要等于后面矩阵的 行数。
矩阵乘法很重要吧,我在编程时经常听别人说起,况且那时候并没有学工程数学。
矩阵乘法就 3 3 3 种情况,和数字、和向量、和矩阵 相乘。
矩阵卷积(二维):矩阵 B B B 以某个步长在 矩阵 A A A 表面 滑动加权求和。
演示一下卷积过程,
接着矩阵 B B B 从矩阵 A A A 的 左上角 准备滑动,如下图:
黄色区域的元素相乘,得到 4 4 4 个 1 1 1,相加值为 4 4 4。
假设设定的滑动步长为 1 1 1 ,开始滑动,新一轮计算,方法相同,如下图:
上面所说的矩阵卷积是二维的,因此我们以灰色图为例,彩色图是三维的。
图片是由很多 0 − 255 0-255 0−255 的值排列而成,像素值越大图片就越亮;
而矩阵正好有行、列,我们可以把图片转为矩阵,通过操控矩阵来改变图片。
图片平滑:让一张清晰的图片变模糊
。
因为图片的像素值反应了一个图片的亮度,如果我们把图片中的像素值和周围的像素值相似,那整个图片的色调就差不多,也变模糊了。
而图片平滑的过程和矩阵卷积的过程是一样的,最核心的地方就是设计卷积核。
图像平滑算法:
# 运行:在命令行输入 python 当前源文件.py
import numpy as np
from PIL import Image # 图片处理模块
from scipy import signal
# 1.读取一张图片
filename = "./demo.png"
img_rgb = Image.open(filename)
img_rgb.show()
# 2.将彩色图片转为灰度图
img_gray = img_rgb.convert('L')
img_gray.show()
# 3.将灰度图转为像素矩阵
matrix = np.asarray(img_gray)
print("matrix.shape=",matrix.shape)
# 4.定义卷积核(均值滤波器)
filter_3x3 = np.array([[ 1/9, 1/9, 1/9 ],
[ 1/9, 1/9, 1/9 ],
[ 1/9, 1/9, 1/9 ]])
print("filter_3x3=",filter_3x3.shape) # 采用的 3*3 的过滤器
print(np.around(filter_3x3, decimals=2)) # 打印图片的像素值
# 5.开始卷积(图像平滑)
result = signal.convolve2d(matrix,filter_3x3,mode='same')
print("result.shape=",result.shape)
print(np.around(result, decimals=0))
# 6.把像素矩阵转回图片
img_rlt = Image.fromarray(result)
img_rlt.show()
读取的图片 demo.png
:
有些模糊了,但 3 x 3 3x3 3x3 可能不太明显,可以改为 7 x 7 7x7 7x7 看看(会更模糊)。
# 7x7的,均值是 1/49
filter_7x7 = np.ones((7,7)) / (7*7)
除此之外,卷积核还可以改进,一般采用高斯分布(在保留细节方面,图片平滑效果最好),因此也称为“高斯滤波器”。
二维高斯分布:
在 3 ∗ 3 3*3 3∗3 的 矩阵 B B B 中,也就是卷积核的权重不要全设置为相同的数;滤波器的设计应该随中心逐层递减。
# 7x7 高斯滤波器
gaussian_filter_7x7 = np.array([ [ 0.00000067, 0.00002292, 0.00019117, 0.00038771, 0.00019117, 0.00002292, 0.00000067],
[ 0.00002292, 0.00078633, 0.00655965, 0.01330373, 0.00655965, 0.00078633, 0.00002292],
[ 0.00019117, 0.00655965, 0.05472157, 0.11098164, 0.05472157, 0.00655965, 0.00019117],
[ 0.00038771, 0.01330373, 0.11098164, 0.22508352, 0.11098164, 0.01330373, 0.00038771],
[ 0.00019117, 0.00655965, 0.05472157, 0.11098164, 0.05472157, 0.00655965, 0.00019117],
[ 0.00002292, 0.00078633, 0.00655965, 0.01330373, 0.00655965, 0.00078633, 0.00002292],
[ 0.00000067, 0.00002292, 0.00019117, 0.00038771, 0.00019117, 0.00002292, 0.00000067] ])
# 调用的时候,将 filter_3x3 改为 gaussian_filter_7x7。
除此之外,还可以实现图片的边缘检测(应用在自动驾驶的车道检测、计算机视觉基础等等)。
# 第 4 步,定义卷积核改为定义算子
sobel = np.array([[ -1, -2, -1 ],
[ 0, 0, 0 ],
[ 1, 2, 1 ]])
# 调用语句 result = signal.convolve2d(matrix,filter_3x3,mode='same')
result = signal.convolve2d(matrix,sobel,mode='same')
左边是原图,右边是效果图(边缘检测算法):
学线代时,可能比较注重具体的计算,但学完了却发现对线代的理解还是不够深刻。
一个可能的原因是,没有特别深刻的理解,我们在代数中的这些符号,比如说 矩阵 A A A,这个 矩阵 A A A 到底表示什么?
代数,是用字母代表数,但我们到底代表的是哪些数…在更加抽象的数学里,我们的代数代表的不仅仅是数,而是一个对象。
那么,代表的这个对象是什么?
这个就是我们要明确的。
看待矩阵的四种视角:
矩阵可不仅仅是只能处理图片的数字表格,试着换一种角度看矩阵:变换。
向 量 B 向量B 向量B(红色) 经过 矩 阵 A 矩阵A 矩阵A 得到 另一个向量(绿色), 矩 阵 A 矩阵A 矩阵A 如同一个函数,一个向量输入进去,会输出另一个向量。
只不过,在线性代数里,我们称 矩 阵 A 矩阵A 矩阵A 为一种变换,即把一个向量(或矩阵)变成另一个变量(或矩阵)。
若我们把矩阵看做一个变换,图形变换就会十分方便。
图形变换:图片的缩放、旋转、仿射等等,也用于游戏开发、动漫制作等等。
或许您应该有一个疑问,矩阵,是怎样实现图形变换的 ???
图形的变化:
比如,这个是怎么做到的:
先考虑一个小问题吧,怎么使得一个图形绕 y y y 轴左右翻转 ?
其实,黄色的梯形是由 4 4 4 个点组成,经历 4 4 4 个点的坐标,也就是 4 4 4 个列向量。
首先,我们让 ( x 1 , y 1 ) (x_{1},y_{1}) (x1,y1) 翻转,翻转后也就是 ( − x 1 , y 1 ) (-x_{1},y_{1}) (−x1,y1)。
现在我们改为以矩阵的形式翻转: A ∗ [ x 1 y 1 ] = [ − x 1 y 1 ] A*\begin{bmatrix} x_{1}\\ y_{1} \end{bmatrix}=\begin{bmatrix} -x_{1}\\ y_{1} \end{bmatrix} A∗[x1y1]=[−x1y1]
我们需要做的就是找到一个能使其转换完成的 矩 阵 A 矩阵A 矩阵A,因为输入的矩阵和输出的矩阵都是同行同列(2行, 1列),根据矩阵乘法的要求, 矩 阵 A 矩阵A 矩阵A 就必须是 2行, 2列。
得到一个式子:
我们确定好 a , b , c , d a,b,c,d a,b,c,d 四个系数的值,通过比对等式俩边的系数即可,
所以,矩阵 A = [ − 1 0 0 1 ] A =\begin{bmatrix} -1 & 0 \\ 0& 1 \end{bmatrix} A=[−1001]。
可这个翻转计算只是针对 ( x 1 , y 1 ) (x_{1},y_{1}) (x1,y1),为了提高计算效率(批处理),我们把 x 1 ⋯ x n x_{1}\cdots x_{n} x1⋯xn 的点化为列向量后,排成一个矩阵。
矩阵 A A A 再和 排成的矩阵 计算即可:
以上是图形的左右翻转(绕 y y y 轴)。
图形的上下翻转(绕 x x x 轴)原理也相同。
线性变换,也可以实现图形的水平剪切。
剪切:把 正体字 变成 斜体字,就是一个剪切。
结合矩阵: [ a b c d ] ∗ [ x y ] = [ x + k y y ] \begin{bmatrix} a &b \\ c & d \end{bmatrix}*\begin{bmatrix} x\\y \end{bmatrix}=\begin{bmatrix} x+ky\\ y \end{bmatrix} [acbd]∗[xy]=[x+kyy],有一个控制系数 k k k.
根据矩阵乘法 ,令 A = [ a b c d ] , A=\begin{bmatrix} a & b \\ c & d \end{bmatrix}, A=[acbd], 则有: { a x + b y = x + k y c x + d y = y \begin{cases} ax+by&= x+ky\\ cx+dy&= y \end{cases} {ax+bycx+dy=x+ky=y
通过对比系数, [ 1 k 0 1 ] ∗ [ x y ] = [ x + k y y ] \begin{bmatrix} 1 &k \\ 0 & 1 \end{bmatrix}*\begin{bmatrix} x \\ y \end{bmatrix}= \begin{bmatrix} x+ky \\ y \end{bmatrix} [10k1]∗[xy]=[x+kyy],当 k > 0 k>0 k>0 时,往右剪切;当 k < 0 k<0 k<0 时,往左剪切。
图形的竖直剪切如下图,纵坐标运动,横坐标不变。
变换矩阵: [ 1 k 0 1 ] ∗ [ x y ] = [ x k x + y ] \begin{bmatrix} 1 &k \\ 0 & 1 \end{bmatrix}*\begin{bmatrix} x \\ y \end{bmatrix}= \begin{bmatrix} x \\ kx+y \end{bmatrix} [10k1]∗[xy]=[xkx+y],注意 k k k 的值,变换的方向不一样。
完整代码:
# 运行:在命令行输入 python 当前源文件.py
import numpy as np
import matplotlib.pyplot as plt
# 1.定义变换矩阵A,用于图形平移(竖直平移)
A = np.array([[1,0],[0,-1]])
# 1.定义变换矩阵A,用于图形剪切
# k = -0.8
# A = np.array([[1,0],[k,1]])
# 1.定义变换矩阵A,图形旋转
# theta = -(3.14/4)
# A = np.array([[np.cos(theta),np.sin(theta)],[-np.sin(theta),np.cos(theta)]])
# 2. 定义输入矩阵(即输入图形)
B = np.array([[0, 1, 1, 0, 0],[1, 1, 0, 0, 1]])
# 3. 计算输出矩阵(矩阵乘法)
Y = np.dot(A,B)
# 4. 绘制图形
plt.axis([-3,3,-3,3])
plt.axvline(x=0, color='#A9A9A9')
plt.axhline(y=0, color='#A9A9A9')
plt.grid(True)
plt.plot(B[0],B[1],'-yo',lw=2) # 绘制输入图形
plt.plot(Y[0],Y[1],'-go',lw=2) # 绘制输入图形
plt.show()
具体用法,请往下看。
图片平移:
# 1.定义变换矩阵A,用于图形平移(竖直平移)
A = np.array([[1,0],[0,-1]])
图片剪切:
# 1.定义变换矩阵A,用于图形剪切
k = -0.8
A = np.array([[1,0],[k,1]])
图片旋转:
# 1.定义变换矩阵A,图形旋转
theta = -(3.14/4)
A = np.array([[np.cos(theta),np.sin(theta)],[-np.sin(theta),np.cos(theta)]])
我们推导一下,图形旋转的变化过程。
不一定要每一步都弄明白,但要知道我们可以把矩阵看成一种对向量的变换(函数),这个很重要,理解的越深刻越好。
我们看最简单的情况,如下图。
蓝色的向量旋转 θ \theta θ 度角得到红线 ,如果我们设这个变换的矩阵为 a , b , c , d a,b,c,d a,b,c,d,则有这样一个式子:
因为是经过旋转得到的,因此新的坐标和原来的坐标一定是有联系的,这个联系就是角度 θ , α \theta ,~\alpha θ, α。
推导过程: