pytorch的tensor同理。
先说结论:
None实际上是增加了一个维度,它不是原维度的索引。
为什么引入None
说一下我找原文档的心路历程:
在numpy的官方文档里搜索“slice”,也就是切片,很容易就能找到关于slice的介绍:
Basic slicing extends Python’s basic concept of slicing to N dimensions. Basic slicing occurs when obj is aslice
object (constructed bystart:stop:step
notation inside of brackets), an integer, or a tuple ofsliceobjects and integers.Ellipsis
andnewaxis
objects can be interspersed with these as well.
简单来说就是numpy的切片扩展了python的切片。当索引是切片对象(由括号内的start:stop:step语法构造)、整数、切片对象的元组或整数的元组,切片操作就会发生。后面一句话特别重要:省略号和newaxis对象也可以穿插其中。
省略号就是python语法的“...”,那么newaxis是什么呢?直觉告诉我它和None有关。找到newaxis的文档,里面第一句话就是:
A convenient alias for None, useful for indexing arrays.
也就是说,numpy.newaxis是None的别名,在索引数组时有用。而文档紧接着给的例子也特别直接:
第一句newaxis is None , is None 。。。
官方这么直白,这下不用我多说,你也知道None是什么意思了吧?None就是newaxis,也就是建立一个新的索引维度。其实就是为了写法上的方便,本来新建一个维度用reshape函数、unsqueeze函数也可以做到。其实文档后面也给出了解释[3]:
This can be handy to combine two arrays in a way that otherwise would require explicit reshaping operations.
这种写法很方便地把两个数组结合起来,否则,还需要明确的reshape操作。
那么,怎么用呢?
以一维为例
x = np.arange(3) # array([0, 1, 2])
( 注意,这个一维数组的shape是(3,),而不是(1,3),初学者很容易犯错。)
如果想把x的shape变成(1,3),只需要把None放在第一个维度的位置,以下两种写法等价:
x[None,:]
x[None]
结果如下:
array([[0, 1, 2]])
如果想把x的shape变成(3,1),只需要把None放在第二个维度的位置:
x[:,None]
结果如下:
array([[0],
[1],
[2]])
其实,None可以任意添加和组合,例如下面的写法:
x[None,:,None,None]
结果如下:
array([[[[0]],
[[1]],
[[2]]]])
这个数组的shape是(1,3,1,1)。
以二维为例
x = np.arange(6).reshape((2,3))
x如下:
array([[0, 1, 2],
[3, 4, 5]])
在第一个维度插入,以下三种写法等价:
x[None]
x[None,:]
x[None,:,:]
输出结果如下,shape为(1, 2, 3):
array([[[0, 1, 2],
[3, 4, 5]]])
在第二个维度插入,以下两种写法等价:
x[:,None]
x[:,None,:]
输出结果如下,shape为(2, 1, 3):
array([[[0, 1, 2]],
[[3, 4, 5]]])
在第三个维度插入:
x[:,:,None]
输出结果如下,shape为(2, 3, 1):
array([[[0],
[1],
[2]],
[[3],
[4],
[5]]])
更高维的情况以此类推。
这种写法一般在进行矩阵运算的时候会用到。比如:
x = np.arange(5)
x[:, None] + x[None, :]
这样可以很优雅地获得 列向量+行向量 的结果(划重点:优雅~):
array([[0, 1, 2, 3, 4],
[1, 2, 3, 4, 5],
[2, 3, 4, 5, 6],
[3, 4, 5, 6, 7],
[4, 5, 6, 7, 8]])