pytorch中对于矩阵要进行的操作很多,但是初学者可能并不很清楚矩阵的维度,以及当矩阵维度变大时候,怎么知道我们要操作的维度在哪里。
1.学会观察中括号,了解你现在的输出数据到底是在几维空间。
tensor([[[0.1205, 0.1218],
[0.1326, 0.1112],
[0.1276, 0.1477],
[0.1228, 0.1192],
[0.1221, 0.1173],
[0.1243, 0.1268],
[0.1252, 0.1277],
[0.1250, 0.1283]]], grad_fn=<SoftmaxBackward>)
第一眼一看,这个数据是一个8行2列的数据,但是实际上它的tensor维度是[1,8,2],这里就和我们的中括号有关,我们知道当只有一个scalar的时候tensor存在时我们的输出是这样的:
tensor(1).shape
output
torch.Size([1])
这里可以看到我们维度为一的tensor是只有一个括号的,那我们再看看当输入的tensor是两个的时候的结果:
a=torch.tensor([1,2])
a
tensor([1, 2])
a.shape
torch.Size([2])
我们看见,只要当输入的数据还是一个python的array时候,输出的size还是一个中括号。
那么我们再来看下面这个例子:
a=torch.tensor([[1,2],[3,4]])
a
tensor([[1, 2],
[3, 4]])
a.shape
torch.Size([2, 2])
我们看见,这时候就有了两个中括号在这里,可以看见pytorch中的维度认为是垂直方向堆叠,同一个array里的处于同一个维度,而不同array里的处于不同维度。
那我们再看第一个例子:
tensor([[[0.1205, 0.1218],
[0.1326, 0.1112],
[0.1276, 0.1477],
[0.1228, 0.1192],
[0.1221, 0.1173],
[0.1243, 0.1268],
[0.1252, 0.1277],
[0.1250, 0.1283]]], grad_fn=<SoftmaxBackward>)
我们看到,这里是有明显的三个中括号,表明的我们的数据是3维而不是二维,我们可以一层一层的拨开这个矩阵,里层有八个size是2的tensor被一个中括号包住,说明了这个这个维度是8维的,而我们把第二个括号里的都看成一个tensor,则它是被第三个括号包裹的,那么对于第三个括号来说,这只是一维的。
而根据pytorch的size输出为:
torch.Size([1, 8, 2])
正好验证了这个剥开的过程,每个tensor是size2的张量,而一共有8个这样的张量,而这8个被一个中括号括起来后,成了一个[8,2]的tensor,随后又被一个中括号括起来,相当于成了第三个维度的第一个数据。
2.如果索引和操作多维数组
知道了多维张量的分布之后,我们就要正确的知道如何正确的索引我们想要的数据,相比于矩阵是从内向外一层层包裹,而矩阵索引就是从外到内一层层的拆开,对于这个数组,第一若我们要索引[8,2]的那个8维2列的正确的数据,我们因该怎么索引呢?
实际上按从外向内索引的办法,我们应该在第一步索引就可以做到:
a=torch.tensor([[[0.1205, 0.1218],
[0.1326, 0.1112],
[0.1276, 0.1477],
[0.1228, 0.1192],
[0.1221, 0.1173],
[0.1243, 0.1268],
[0.1252, 0.1277],
[0.1250, 0.1283]]])
a[0]
tensor([[0.1205, 0.1218],
[0.1326, 0.1112],
[0.1276, 0.1477],
[0.1228, 0.1192],
[0.1221, 0.1173],
[0.1243, 0.1268],
[0.1252, 0.1277],
[0.1250, 0.1283]])
可以看到我们正确的把我们想要的[8,2]的数据切了出来,证明了包裹是从内往外一层一层包裹,而索引则是从外到内一层一层索引。
继续索引,加入我们要取出[8,2]数据中的第一维度的第二个值0.1218,我们应该从外到内如下索引:
a[0][0][1]
tensor(0.1218)
这一看见我们正确的索引出来了结果。
3.函数在矩阵上的使用
使用pytorch函数时,我们经常要制定维度,比如softmax这种函数,我们要制定维度才能够正确在我们想要的维度上归一化,那么对于这个数据,在最里层基本都是相等的,所以我们应该出来的都是0.5左右,那我们要怎么去使用softmax来归一化呢,在初始数组上我们知道我们的矩阵维度是[1,8,2]的,这就提供给我们三个维度可以使用softmax,我们如何指定正确的维度呢。
先看下面:
a.softmax(dim=0)
a.shape
torch.Size([1, 8, 2])
tensor([[[1., 1.],
[1., 1.],
[1., 1.],
[1., 1.],
[1., 1.],
[1., 1.],
[1., 1.],
[1., 1.]]])
全部都是1,很奇怪吧,但是实际想想会觉得奇怪吗?
并不是这样,我们的数据的size是[1,8,2],那么我们在dim=0的维度索引,那么softmax也是在[1,8,2]中1这个方向索引,然后这个数组在三维上只有一个,所以softmax求出来必然是1.
在看下面:
a.softmax(dim=1)
tensor([[[0.1244, 0.1246],
[0.1260, 0.1233],
[0.1253, 0.1279],
[0.1247, 0.1243],
[0.1246, 0.1240],
[0.1249, 0.1252],
[0.1250, 0.1253],
[0.1250, 0.1254]]])
可以看见基本上每个都是1/8,不那么奇怪了对吧。但是实际上也是符合pytorch的逻辑的,我们在[1,8,2]的[8]上求softmax,它自然是按列求解了,所以得到这样一个结果也并不奇怪。
再看最后的结果:
a.softmax(dim=2)
tensor([[[0.4997, 0.5003],
[0.5053, 0.4947],
[0.4950, 0.5050],
[0.5009, 0.4991],
[0.5012, 0.4988],
[0.4994, 0.5006],
[0.4994, 0.5006],
[0.4992, 0.5008]]])
这我们就得到了我们想要的输出结果,我们在[1,8,2]的第三个维度在此时相当于[8,2]中的每个tensor求了,也就是我们包裹维度的最里层,可见pytroch的函数dim参数,从小到大分别对于了外层到里层的一个过程。
我们还可以做如下实验:
a=torch.tensor([[[0.1205, 0.1218],
[0.1326, 0.1112],
[0.1276, 0.1477],
[0.1228, 0.1192],
[0.1221, 0.1173],
[0.1243, 0.1268],
[0.1252, 0.1277],
[0.1250, 0.1283]],
[[0.1205, 0.1218],
[0.1326, 0.1112],
[0.1276, 0.1477],
[0.1228, 0.1192],
[0.1221, 0.1173],
[0.1243, 0.1268],
[0.1252, 0.1277],
[0.1250, 0.1283]]])
a.shape
torch.Size([2, 8, 2])
我们复制了一个相同的数据组成了[2,8,2],我们再用softmax算呢?
a.softmax(dim=0)
tensor([[[0.5000, 0.5000],
[0.5000, 0.5000],
[0.5000, 0.5000],
[0.5000, 0.5000],
[0.5000, 0.5000],
[0.5000, 0.5000],
[0.5000, 0.5000],
[0.5000, 0.5000]],
[[0.5000, 0.5000],
[0.5000, 0.5000],
[0.5000, 0.5000],
[0.5000, 0.5000],
[0.5000, 0.5000],
[0.5000, 0.5000],
[0.5000, 0.5000],
[0.5000, 0.5000]]])
这是一个并不意外的结果,在最外层上两个相同[1,8,2]组成的数据使用softmax必定都是0.5
a.softmax(dim=2)
tensor([[[0.4997, 0.5003],
[0.5053, 0.4947],
[0.4950, 0.5050],
[0.5009, 0.4991],
[0.5012, 0.4988],
[0.4994, 0.5006],
[0.4994, 0.5006],
[0.4992, 0.5008]],
[[0.4997, 0.5003],
[0.5053, 0.4947],
[0.4950, 0.5050],
[0.5009, 0.4991],
[0.5012, 0.4988],
[0.4994, 0.5006],
[0.4994, 0.5006],
[0.4992, 0.5008]]])
同样的在dim=2上求,也是对最里层的进行求解,得到正确的结果。