😄理解numpy中array和pytorch中tensor的操作是开始科学运算的第一步!
首先明白维度的感念:
维度
我们通常能听到的都是2D, 3D,其实这边的D就是dimension的含义即维度。2D,我们通常理解为是平面,如我们最熟悉的直角坐标系就是平面坐标系,还有极坐标系。而3D呢,就是在平面的基础上增加了一维——高度,从而使平面的物体立起来了,同样3D也有耳熟能详的坐标系——3维坐标系。
更官方的解释呢:维度(Dimension),又称为维数,是数学中独立参数的数目。在物理学和哲学的领域内,指独立的时空坐标的数目。0维是一个无限小的点,没有长度。1维是一条无限长的线,只有长度。2维是一个平面,是由长度和宽度(或部分曲线)组成面积。3维是2维加上高度组成体积。4维分为时间上和空间上的4维,人们说的4维通常是指关于物体在时间线上的转移。(4维准确来说有两种。1.四维时空,是指三维空间加一维时间。2.四维空间,只指四个维度的空间。)四维运动产生了五维。
从哲学角度看,人们观察、思考与表述某事物的“思维角度”,简称“维度”。例如,人们观察与思考“月亮”这个事物,可以从月亮的“内容、时间、空间”三个思维角度去描述;也可以从月亮的“载体、能量、信息”三个思维角度去描述。这边的维度其实也可以理解为角度,从不同的方面去看待、确定一个事物。
所以代数上来说,维度其实是数学里在表示方面的一个重要的概念,它反映的是一个空间的本质性质。
科学计算中维度的概念
从二维点位置->编程中的坐标系
维度的考量主要集中在矩阵的运算上。首先我们来看一个元素:4,其实它就是一个点,可以被认为是0维的。但往往我们不会只有一个元素。我们最常见的是编程中的数组,如[1,2,3,4],这个是由多个元素构成的,它的维度就是一维的,这个我们也比较好理解。
而二维是什么呢?我们能直观理解的二维是平面坐标系的那种:(1,3), (4,5)…即给一个x,一个y,那么在平面中就可以在直角坐标系下确定这个点(物)。现在我们规整下这些坐标点[ (1, 3), (4, 5) ],从这个角度上离我们的矩阵,或是数组好像还是有点远。那么我们继续变形。
如果我们需要画出坐标系中有哪些点的话, 1.第一种做法就是跟上述一样, 把点都存一个vector中[ (1, 3), (4, 5) ],然后遍历,再在坐标系中点出。2.第二种呢,就是在坐标系中把所有的位置都列出来,如果有点存在就把它标出来,即跟我们列出迷宫地图一样,先把地图画出来,然后再把宝藏标出来。所以上述的两个点可以理解为。在给出了map[20][20]
的地图上,(1, 3)和(4, 5)位置为true, 即map[1][3] = 1
, map[4][5] = 1
,其他位置map[x][y] = 0
,所以这样我们就从[(1, 3), (4, 5)]==> 用map形式表现出了这两个点,两者成功在二维上进行了转换。接下来我们就来分析这个二维的map。
数组,在编程中,我们都不陌生,如int arr[50][50]
,虽然可以通过这个二维的数组,根据val的不同来表示三维的量,但是我们这边不把它这么理解,仅是当做bool arr[50][50]
来理解维度上的概念。===>同样,面对numpy中的array我们也是这么个理解。
1 | import numpy as np |
我们从numpy的x上理解,这边是创建了一个2*3的矩阵,其中x[0][1], x[0][2], x[1][0], x[0][2]
全1,其余为0,输入x.shape
得到的结果是(2, 3),有两项,跟我们从map的理解上是一致的,这个地图map拥有长和宽两个维度。
然后我们从编程中观察这个2*3的矩阵或是叫数组,可以发现x[a][b]
第一个[]
中的内容a范围是从0-1的,第一个[]
中的内容b范围是从0-2的,0的话学编程的人都能很快的理解,而第二个的范围却不太那么肯定。为什么呢?因为它跟我们普通认知的直角坐标系不一致。下面我们把x画出来(不改变输出显示的形式,而是让坐标系去适应这种表现形式)。
为什么是这样画的呢?首先明确的原则是,不改变输出显示的形式,而是让坐标系去适应这种输出形式,因此输出长啥样,我们坐标系只能去适应。由于我们碰到有x,有y的时候,习惯上把第一个出现的当作x,第二个当作y,所以就有了第一个[]为x,第二个[]为y。
好了,现在我们确定好坐标系长什么样了。接下来就是具体理解dim的含义了
编程中坐标系->科学计算中array的dim
想必大家在学习numpy或者torch的时候都被各种函数方法中的dim参数折磨过,感觉怎么理解都有问题,不敢自己使用。因此,这边就是解决,这些函数中的dim到底是怎么确定的
比如我们创建一个高维的array
1 | A = torch.randint(2, size = (1,2,3,4)) |
举个我自己最初理解dim的笨方法:硬记x为第一维(dim = 0), y为第二维(dim = 1)
实际上这种记法是比较低效的,最好的方法是我们怎么定义这个array就怎么记,比如我们这边创建的是一个size=(1, 2, 3, 4),输出len(A.shape)
为4,可以看到这就是个4维的tensor,那么我们顺理成章地就把把各个维度依次定义出来了。如dim = 0地指的就是size = 1的那层,dim = 1就是指size = 2的那层,依次类推。这样说可能有点抽象,因此我们回归简单的。
1 | B = torch.randint(2, size = (3, 2)) |
按照我们刚的定义,dim=0就是size=3的这一层,也就是我们坐标系中的X轴orX面。
好了,想必大家这个时候还不知道我在说什么。接下来就带大家来测试函数。
测试dim在函数参数中的定义
提前指出把:要注意函数介绍中dim指的是"沿着dim这个维度"or"删除、增加…dim这个维度(在dim这个维度上进行维度修改)"
规约计算
一般是指分组聚合计算,表现结果就是会进行维度压缩
sum
沿着dim累加元素
1 | C = torch.randint(5, size = (2, 5)) |
可以看到sum就是比较典型的"沿着dim"的例子,当dim = 0时就沿着dim = 0即x轴进行累加,由于sum这个函数会进行维度的压缩,所以最后的结果为tensor([3, 3, 5, 8, 4])
cumprod
通过dim指定沿着某个维度计算累积
其他的函数还有cumsum、prod、sum,实际上两者是相同的,还有mean、median、var、std、min、max
1 | # |
索引、切片、连接
squeeze,unsqueeze
unsqueeze关键字:参数dim指定在第几个维度增加"[]",以提升维度
squeeze: unsqueeze的逆操作,删除dim指定的维度
unsqueeze
1 | D = torch.Tensor( [1, 2, 3, 4, 5] ) |
可以看到的是dim = 0的时候就是在dim = 0 维度上增加了一维, 使得变成了[1, 5]。第二个是在dim=1的位置加了一维变成了[5, 1] (这也就是为什么很多书上会说其实就是在dim维度上加了1)
▲这个典型就是要区分: 在dim维度上 和 沿着dim维度了
squeeze
1 | F = torch.Tensor( [ [0, 2, 3, 4], |
这边变换不大的原因是因为dim上没有size=1可以删除
split
按(沿着)dim维度将tensor分成n个部分
1 | x = torch.Tensor([ |
unbind
删除某个维度后,返回所有切片组成的元组
1 |
|
cat、stack
通过关键字dim指定按哪个维度拼接
1 | x = torch.randint(1, 100, size=(2,3)) |
官方文档: https://pytorch.org/docs/stable/generated/torch.cat.html?highlight=cat#torch.cat
总结
正确理解姿势
dim是指tensor在shape上的顺序(可以这么理解),如x的shape是2x3x4,也就是[2, 3, 4]。故可以这样一一对应来。
比如dim = 1就是按具有3个元素的那个轴操作,从而不用死记硬背那些dim = 0是对列操作还是对行操作了。
强记三维
但还是不提倡强记,因为一旦高维就理解不了了。
附:
关于size的设置
在ones、rand等函数上,size = (2,3,4),我们在C++数组中int arr[x][y][z]
的理解是然后z为4, 但实际上在科学运算中size = (2,3,4)的矩阵是有4个的矩阵叠加而成,这边是要区分的
1 | import numpy as np |
关于如何看图
1 | import numpy as np |
画图效果如下:
可以得到的结果是针对np.random.rand(5,5,3)
来说,三个通道被列成了三列,因此每一列就是一个通道。
1 | res = np.uint8(np.random.rand(5,5,3) * 255) |
总结: RGB图像的shape:(H, W, C)
pytorchAPI:
Author: Mrli
Link: https://nymrli.top/2020/12/05/理解科学计算中的dim参数/
Copyright: All articles in this blog are licensed under CC BY-NC-SA 3.0 unless stating additionally.