《深度学习入门——自制框架》读书笔记 第二章 用自然的代码表达

2. 用自然的代码表达

step12 可变长参数(正向)

主要是解决多输入多输出问题
例如Add函数:

class Add(Function):
	def forward(self, x0, x1):
		y = x0 + x1
		return y

def add(x0, x1):
	return Add()(x0, x1)

对Function类的改造如下:

class Function:
	def __call__(self, *inputs):
		xs = [x.data for x in inputs]
		ys = self.forward(*xs)  # 使用*号解包
		if not isinstance(ys, tuple):  # 对非元组情况的额外处理
			ys = (ys,)
		outputs = [Variable(as_array(y)) for y in ys]

		for output in outputs:
			output.set_creator(self)

		self.inputs = inputs  # 保存输入的变量
		self.outputs = outputs

		return outputs if len(outputs) > 1 else outputs[0]
		# 若输出是标量,则直接返回标量,否则以列表形式返回

step13 可变长参数(反向)

多输入输出函数backward的实现

\[\begin{align} & z = x + y \nonumber \\ \nonumber \\ & \frac{\partial z}{\partial x} = 1, \frac{\partial z}{\partial y} = 1 \nonumber \end{align} \]

所以对于add函数,反向传播给自变量的导数值就是因变量的导数值

class Add(Function):
	def forward(self, x0, x1):
		y = x0 + x1
		return y
# ======== 添加backward代码 ==========
	def backward(self, gy):
		return gy, gy

让Variable类支持多输入输出的backward

class Variable: 
    ...
	def backward(self):
		if self.grad is None:
			self.grad = np.ones_like(self.data)
			# ones_like创建一个ndarray实例,其形状和数据类型与self.data相同,数据全为1
			# 即若self=[0.2 0.22],则创建实例[1, 1]

		funcs = [self.creator]  # 1. 获取函数
		while funcs:
			f = funcs.pop()  # 获取函数
            # =================== 修改部分 ============================
			gys = [output.grad for output in f.outputs]
			gxs = f.backward(*gys)
			if not isinstance(gxs, tuple):
				gxs = (gxs,)

			for x,gxs in zip(f.inputs, gxs):
				x.grad = gxs

				if x.creator is not None:
					funcs.append(x.creator)
            # ========================================================

为单输入函数(Square和Exp)打个补丁

class Square(Function):
    ...
	def backward(self, gy):
		x = self.inputs[0].data  # 修改前为 x =self.input.data
		gx = 2 * x * gy
		return gx
posted @ 2023-12-02 00:13  码鸽  阅读(47)  评论(0编辑  收藏  举报