《深度学习入门——自制框架》读书笔记

发布时间 2023-12-01 19:54:18作者: 码鸽

1. 自动微分

step2 创建变量的函数

# 箱子类,存放一个变量数据
class Variable: 
	def __init__(self, data):
		self.data = data

# 函数类的基类
class Function:
    # __call__方法是一个特殊的Python方法。
    # 定义了这个方法后,当f = Function()时,就可以通过编写f(...)来调用__call__方法了
	def __call__(self, input):
		x = input.data
		y = self.forward(x)  # 具体的计算在forward方法中进行
		output = Variable(y)
		return output

	def forward(self, x):
		raise NotImplementedError()  # 告诉使用了Function类forward方法的人这个方法应该通过继承来实现

# 函数类的具体实现类
class Square(Function):
	def forward(self, x):
		return x ** 2


if __name__ == "__main__":
	x = Variable(10)
	f = Square()
	y = f(x)
	print(type(y))
	print(y.data)

step4 数值微分

利用中心差分近似来计算导数:

\[f'(x) \approx \frac{f(x+ \epsilon)-f(x- \epsilon)}{2 \epsilon} \]

设计numerical_diff函数:

class Variable: 
	def __init__(self, data):
		self.data = data


class Function:
	def __call__(self, input):
		x = input.data
		y = self.forward(x)  # 具体的计算在forward方法中进行
		output = Variable(y)
		return output

	def forward(self, x):
		raise NotImplementedError()  # 告诉使用了Function类forward方法的人这个方法应该通过继承来实现


class Square(Function):
	def forward(self, x):
		return x ** 2


def numerical_diff(f, x, eps=1e-4):
	x0 = Variable(x.data - eps)
	x1 = Variable(x.data + eps)
	y0 = f(x0)
	y1 = f(x1)
	return (y1.data - y0.data) / (2 * eps)


if __name__ == "__main__":
	f = Square()
	x = Variable(2.0)
	dy = numerical_diff(f, x)
	print(dy)

复合函数的导数

def f(x):
    A = Square()
    B = Exp()
    C = Square()
    return C(B(A(x)))


if __name__ == "__main__":
	x = Variable(np.array(0.5))
	dy = numerical_diff(f, x)
	print(dy)

step6 手动进行反向传播

用反向传播计算导数

import numpy as np


class Variable: 
	def __init__(self, data):
		self.data = data
		self.grad = None  # gradient梯度的缩写

class Function:
	def __call__(self, input):
		x = input.data
		y = self.forward(x)  # 具体的计算在forward方法中进行
		output = Variable(y)
		self.input = input  # 保存输入的变量
		# 将input设置为实例变量。
		# 这样一来,当调用backward方法时,向函数输入的Variable实例就可以作为self.input使用

		return output

	def forward(self, x):
		raise NotImplementedError()  # 告诉使用了Function类forward方法的人这个方法应该通过继承来实现

	def backward(self, gy):
		raise NotImplementedError()


class Square(Function):
	def forward(self, x):
		y = x ** 2
		return y

	def backward(self, gy):
		x = self.input.data
		gx = 2 * x * gy
		return gx


class Exp(Function):
	def forward(self, x):
		return np.exp(x)

	def backward(self, gy):
		x = self.input.data
		gx = np.exp(x) * gy
		return gx


if __name__ == "__main__":
	A = Square()
	B = Exp()
	C = Square()

	x = Variable(np.array(0.5))
	a = A(x)
	b = B(a)
	y = C(b)

	y.grad = np.array(1.0)  # dy/dy = 1
	b.grad = C.backward(y.grad)
	a.grad = B.backward(b.grad)
	x.grad = A.backward(a.grad)

	print(x.grad)

step7 反向传播的自动化

import numpy as np


class Variable: 
	def __init__(self, data):
		self.data = data
		self.grad = None  # gradient梯度的缩写
		self.creator = None

	def set_creator(self, func):
		self.creator = func

	def backward(self):
		f = self.creator  # 1. 获取函数
		if f is not None:
			x = f.input  # 2. 获取函数的输入
			x.grad = f.backward(self.grad)  # 3. 调用函数的backward方法
			x.backward()  # 调用自己前面那个变量的backward方法(递归)


class Function:
	def __call__(self, input):
		x = input.data
		y = self.forward(x)  # 具体的计算在forward方法中进行
		output = Variable(y)
		output.set_creator(self)  # 让输出变量保存创造者信息
		self.input = input  # 保存输入的变量
		# 将input设置为实例变量。
		# 这样一来,当调用backward方法时,向函数输入的Variable实例就可以作为self.input使用

		self.output = output  # 也保存输出变量
		return output

	def forward(self, x):
		raise NotImplementedError()  # 告诉使用了Function类forward方法的人这个方法应该通过继承来实现

	def backward(self, gy):
		raise NotImplementedError()


class Square(Function):
	def forward(self, x):
		y = x ** 2
		return y

	def backward(self, gy):
		x = self.input.data
		gx = 2 * x * gy
		return gx


class Exp(Function):
	def forward(self, x):
		return np.exp(x)

	def backward(self, gy):
		x = self.input.data
		gx = np.exp(x) * gy
		return gx


if __name__ == "__main__":
	A = Square()
	B = Exp()
	C = Square()

	x = Variable(np.array(0.5))
	a = A(x)
	b = B(a)
	y = C(b)

	y.grad = np.array(1.0)  # dy/dy = 1
	y.backward()

	print(x.grad)