38.3_矩阵的转置

38.3 矩阵的转置

接下来实现进行矩阵转置的函数。矩阵的转置是对矩阵进行图38-3这种变形处理。

x=(x11x12x13x21x22x23)\boldsymbol {x} = \left( \begin{array}{c c c} x _ {1 1} & x _ {1 2} & x _ {1 3} \\ x _ {2 1} & x _ {2 2} & x _ {2 3} \end{array} \right)
xT=(x11x21x12x22x13x23)\boldsymbol {x} ^ {\mathrm {T}} = \left( \begin{array}{l l} x _ {1 1} & x _ {2 1} \\ x _ {1 2} & x _ {2 2} \\ x _ {1 3} & x _ {2 3} \end{array} \right)

如图38-3所示,转置改变了矩阵的形状。下面在DeZero中实现执行转置操作的函数。


图38-3 矩阵转置的例子

本节中实现的转置函数 transpose 只支持输入变量是矩阵(二阶张量)的情况。实际的 DeZero 的 transpose 函数的实现更为通用,它支持轴数据的替换。这部分内容将在本步骤的最后一节介绍。

我们可以使用NumPy的transpose函数执行转置操作,示例如下。

$\mathbf{x} = \mathbf{np}$  .array([[1,2,3],[4,5,6]])  
y  $=$  np.transpose(x)  
print(y)

运行结果

[1

[25]

[36]]

上面的代码使 xx 的形状由 (2, 3) 变为 (3, 2)。张量的元素本身没有发生改变,改变的是张量的形状。因此,它的反向传播只改变从输出端传播的梯度的形状。形状的改变方式正好是正向传播的逆向变化。基于以上内容,我们按如下方式实现 DeZero 的 transpose 函数。

dezero/functions.py

class Transpose(Function): def forward(self, x):
$\mathsf{y} = \mathsf{np}$  .transpose(x) return y def backward(self, gy):  $\mathtt{gx} =$  transpose(gy) return gx def transpose(x): return Transpose() (x)

上面的代码在正向传播的过程中使用np.transpose函数进行转置,在反向传播的过程中使用正在实现的transpose函数对从输出端传来的梯度进行转置。因此,反向传播中所做的转置是正向传播的逆转置。下面我们来实际使用一下transpose函数。

steps/step38.py

$\mathbf{x} =$  Variable(np.array([[1,2,3],[4,5,6]]))   
y  $=$  F.transpose(x)   
y.backup()   
print(x.grad)

运行结果

variable([[1 1 1] [1 1 1]])

从上面的代码可以看出,transpose函数可以用于计算,也可以顺利实现反向传播。接下来,为了能够从Variable变量调用transpose函数,我们来添加以下代码。

dezero/core.py

class Variable: def transpose(self): returndezero-functions.transpose(self) @property defT(self): returndezero-functions.transpose(self)

上面的代码添加了两个方法。第一个方法可以作为transpose方法使用,第二个方法通过添加@property,可以让自己作为实例变量来使用。由此,我们可以编写如下代码。

$\begin{array}{rl} & {\mathrm{x} = \mathrm{Variable(np.random.randint(2,3))}}\\ & {\mathrm{y} = \mathrm{x}.}\end{array}$  transpose()   
 $\mathbf{y} = \mathbf{x}.\mathbf{T}$

到这里我们就实现了执行转置操作的transpose函数。本节实现的transpose函数只支持矩阵,实际的DeZero的transpose函数在本节内容的基础上增加了一些代码。下面笔者对此进行补充说明。

38.4 实际的transpose函数(补充内容)

NumPy的np.transpose函数有更为通用的用法,这个用法就是改变轴的数据顺序。下面是一个实际的例子。

A,B,C,D=1,2,3,4  
x=np.random.randint(A,B,C,D)  
y=x.transpose(1,0,3,2)

上面的代码中有形状为 (A,B,C,D)(A, B, C, D) 的数据,代码中使用 np.transpose 函数改变了形状的轴(为了方便大家理解,这里使用 A 等变量来表示形状的值)。代码中的参数是变换后的轴的顺序。图 38-4 可以帮助我们理解这些内容。


图38-4 np.transpose函数的具体例子

如上图所示,如果指定了轴的顺序,数据的轴就会按照指示重新排序。如果参数为 None,轴就会以相反的顺序重新排序。默认参数是 None。因此,如果 xx 是矩阵,那么 x.transpose() 可以使轴 0 和轴 1 的数据按照轴 1、轴 0 的顺序排列,这正是矩阵的转置操作。

DeZero的transpose函数也支持对轴的数据进行调换。它的反向传播只执行轴的反向调换。这里没有展示相关代码。感兴趣的读者可以参考dezero/functions.py中Transpose类的代码。