Skip to content

3.自动微分的计算图实现

Plain
难度
难度

:简单

经过第一题和第二题,想必您已经熟悉了深度学习中的基础概念,在完成本节的任务前,请确保您已经了解了前向传播,反向传播,链式法则等概念。

知识准备

  • 计算图是深度学习领域非常重要的基础概念,几乎我们现在使用的所有的深度学习框架都是基于计算图构建的,最为流行的TensorFlow和Pytorch就是其中的典型代表。
  • 深度学习框架中的计算都是用计算图表示的,几乎所有的框架都在计算图的基础上实现了自动微分机制。

您需要查阅相关资料,回答以下基础问题:

  • 简述计算图的概念,重点区分“静态图”和“动态图”两种构图方式。
  • 阐述自动微分的概念、

进行实践

现在,您应该已经了解了计算图、前向传播、反向传播、自动微分等基础概念,并具有python面向对象编程的能力。现在让我们来实现一个_可自动微分计算图_。

您的计算图应该至少包含以下内容:

  • 数值节点

在基于计算图搭建的深度学习框架如Pytorch、TensorFlow中,都定义了一个计算和存储数据的基本单元,如在Pytorch中,是tensor。它也就是每个计算图中的节点。为此,我们需要实现定义一个计算和存储数据的基本单元,可以用类来实现它,它的名字自定义,在此我们暂且称为"Tensor"。

您需要完整地定义并实现这个"Tensor"节点。该节点的具体数据值我们可以用numpy.array来表示,也就是说,我们的"Tensor"是通过包装"numpy"实现的。

Plain
class Tensor:
    def __init__(self, data, ...):
        self.data = np.array(data)
        ...

    ...

    def backward(self, ...):
        ...
    ...
class Tensor:
    def __init__(self, data, ...):
        self.data = np.array(data)
        ...

    ...

    def backward(self, ...):
        ...
    ...

对于"Tensor"的属性值设置,您至少需要设置完成自动微分所需要的那几个属性值。(可参考Pytorch tensor的定义)

函数backward()定义了在"Tensor"节点上实现反向传播的计算

  • 计算

我们现在定义的"Tensor"是类,实例化后的两个对象之间是不能直接运算的,但是像在Pytorch中两个tensor是可以直接运算的:torch.tensor(a)+torch.tensor(b)。为此,我们需要使用Python魔法方法实现运算符重载来定义我们自定义的"Tensor"的相关计算。

Plain
class Tensor:
    def __init__(self, ...):
        ...
    def __add__(self, op2):
        return add(self, op2)

    ...
class Tensor:
    def __init__(self, ...):
        ...
    def __add__(self, op2):
        return add(self, op2)

    ...
  • 操作符节点

运算符重载仅仅是使我们实例化"Tensor"对象能够像普通的数值变量一样可以直接进行计算,但是具体的计算实现操作仍需要我们另外定义,在上面的示例代码中,__add__方法内的add才是具体实现“加”操作的地方。实际上,该add就是计算图中的操作符节点

我们将操作符节点实现为一个类,它继承自数值节点"Tensor":

Plain
class add(Tensor):
    def __init__(self, ...):
        ...

    def forward(self, ...):
            ...

    def grad_fn(self, ...):
        ...
class add(Tensor):
    def __init__(self, ...):
        ...

    def forward(self, ...):
            ...

    def grad_fn(self, ...):
        ...

forward()方法实现的是计算图中前向传播时的计算

grad\_fn()方法实现的是计算图中反向传播时该操作符节点的计算

  • 计算图类

上面我们定义了计算图的节点,现在我们应该实现一个计算图类

Plain
class Graph:
    ...
class Graph:
    ...

Graph类应该具有哪些方法和属性?请自行完成它的定义和实现。

最终,使用您实现的可自动微分计算图实现一个具体的计算操作:

Example

Plain
x = Tensor(np.random.rand(1), requires_grad=True)
y = exp(x) + x
z = x * y
t = sum(z)
t.backward()
print(x.grad)
x = Tensor(np.random.rand(1), requires_grad=True)
y = exp(x) + x
z = x * y
t = sum(z)
t.backward()
print(x.grad)

您可以将使用您实现的代码计算出的梯度值与使用如Pytorch计算出的梯度作比较以验证正确性。

需要提交部分

  1. 上述完整的可运行代码,做好相关注释
  2. 最终的使用您实现的可自动微分计算图实现的一个具体的计算操作的运行结果截图

提交方式

将题目中要求的提交的总结内容(截图等)利用 Markdown 格式进行编辑,并存为 PDF 文件。将其与你的源代码一起提交至邮箱:glimmerml@163.com

文件名要求:姓名-学号-中档题03.pdf 或 姓名-学号-中档题03.py (若有)

出题人

Khalil(学长)

QQ: 2053296645