40.3_支持广播
40.3 支持广播
在本步骤实现sum_to函数是为了支持NumPy的广播。广播是NumPy的一个功能,它使不同形状的多维数组之间的运算成为可能。下面是广播的示例代码。
$\mathbf{x0} = \mathbf{np}$ .array([1,2,3])
x1 $=$ np.array([10])
y $=$ x0 + x1
print(y)运行结果
array([11, 12, 13])在上面的代码中, 和 的形状是不同的。在进行上面的计算时,元素会被复制,以使 与 的形状相匹配。这里比较重要的一点是 NumPy 的广播功能是在幕后进行的。DeZero 也会实现这样的广播。我们来看下面的代码。
$\begin{array}{l}\mathrm{x0} = \mathrm{Variable(np.array([1,2,3]))}\\ \mathrm{x1} = \mathrm{Variable(np.array([10]))}\\ \mathrm{y} = \mathrm{x0} + \mathrm{x1}\\ \mathrm{print(y)} \end{array}$运行结果
variable([11, 12, 13])上面的代码在正向传播时会进行广播,这是因为代码是基于ndarray实例实现的。当然,如果在正向传播中进行了广播,那么在反向传播时就必须进行广播的反向传播,但是目前的DeZero不会对广播的反向传播做任何处理。
NumPy的广播是在broadcast_to函数中进行的,broadcast_to函数的反向传播对应的是sum_to函数。考虑到这一点,我们将DeZero的Add类修改成下面这样。
dezero/core.py
class Add(Function): def forward(self, x0, x1): self.x0_shape, self.x1_shape = x0.shape, x1.shape y = x0 + x1 return y def backward(self, gy): $\mathtt{gx0}$ , $\mathtt{gx1} = \mathtt{gy}$ ,gy if self.x0_shape != self.x1_shape: $\mathtt{gx0} =$ dezero-functions.sum_to(gx0,self.x0_shape) $\mathtt{gx1} =$ dezero-functions,sum_to(gx1,self.x1_shape) return gx0,gx1如果在正向传播中进行了广播,就说明输入的 和 在形状上是不同的。此时应进行广播的反向传播计算。为此需要求梯度 的和,使 变为 的形状,还要求梯度 的和,使 变为 的形状。
以上修改是针对dezero/core.py中的Add类进行的。Mul、Sub、Div等所有进行四则运算的类都要完成相同的修改。由此便可实现广播功能。经过以上修改,我们可以编写以下代码。
steps/step40.py
import numpy as np
fromdezero import Variable
$\mathrm{x0} =$ Variable(np.array([1,2,3]))
x1 $=$ Variable(np.array([10]))
y $=$ x0+x1
print(y)
y.backup()
print(x1.grad)运行结果
variable([11 12 13])
variable([3])上面的代码在 时进行了广播。不过,这次广播的反向传播在DeZero函数中被正确执行了。实际得到的 的梯度是3,这是正确的结果。通过以上操作,DeZero实现了广播功能。
步骤41
矩阵的乘积
本步骤的主题是向量的内积和矩阵的乘积。这里会先介绍这两种计算方法,然后将它们实现为DeZero函数。完成本步骤后,我们就有了能够处理张量的最低限度的函数集,由此可以开始解决实际问题了。