我在研究CS231的作业时发现了一个令人困惑的问题。在计算梯度时,我先重塑x然后再获取其转置,这样就能得到正确的结果。
x_r=x.reshape(x.shape[0],-1)dw= x_r.T.dot(dout)
然而,当我直接按照X.T的形状重塑时,得到的结果就不正确了。
dw = x.reshape(-1,x.shape[0]).dot(dout)
有人能解释以下问题吗?
使用np.reshape()获取元素的顺序是如何变化的?将形状为(N,d1,d2..dn)的数组重塑成N,D数组与将其重塑成(D,N)并获取其转置的数组有什么不同?
回答:
虽然你的两种方法都会得到相同形状的数组,但由于numpy读取/写入元素的方式,元素的顺序会有不同。默认情况下,reshape
使用类似C语言的索引顺序,这意味着元素是以最后一个轴的索引变化最快的方式读取/写入的,而第一个轴的索引变化最慢(摘自文档)。
下面是一个实际例子来说明这一点。假设我们有以下数组x
:
x = np.asarray([[[1, 2], [3, 4], [5, 6]], [[7, 8], [9, 10], [11, 12]]])print(x.shape) # (2, 3, 2)print(x)# output[[[ 1 2] [ 3 4] [ 5 6]] [[ 7 8] [ 9 10] [11 12]]]
现在让我们以以下两种方式重塑这个数组:
opt1 = x.reshape(x.shape[0], -1)opt2 = x.reshape(-1, x.shape[0])print(opt1.shape) # outptu: (2, 6)print(opt2.shape) # output: (6, 2)print(opt1)# output:[[ 1 2 3 4 5 6] [ 7 8 9 10 11 12]]print(opt2)# output:[[ 1 2] [ 3 4] [ 5 6] [ 7 8] [ 9 10] [11 12]]
reshape
首先推断新数组的形状,然后以C语言索引顺序读取元素并返回一个视图。
以opt1
为例:由于原始数组x
有12个元素,它推断新数组opt1
必须具有(2, 6)
的形状(因为2*6=12)。现在,reshape
返回一个视图,其中:
opt1[0][0] == x[0][0][0] opt1[0][1] == x[0][0][1]opt1[0][2] == x[0][1][0]opt1[0][3] == x[0][1][1]opt1[0][4] == x[0][2][0]opt1[0][5] == x[0][2][1]opt1[1][0] == x[1][0][0]...opt1[1][5] == x[1][2][1]
如上所述,最后一个轴的索引变化最快,第一个轴的索引变化最慢。同样,opt2
的输出将以相同的方式计算。
现在你可以验证,第一种方法的转置将导致相同形状但元素顺序不同的结果:
opt1 = opt1.Tprint(opt1.shape) # output: (6, 2)print(opt1)# output: [[ 1 7] [ 2 8] [ 3 9] [ 4 10] [ 5 11] [ 6 12]]
显然,由于元素顺序的不同,这两种方法不会得到相同的数组,尽管它们会有相同的形状。