深度学习-09-让函数更简单

深度学习-09-让函数更简单


本文是《深度学习入门2-自製框架》 的学习笔记,记录自己学习心得,以及对重点知识的理解。如果内容对你有帮助,请支持正版,去购买正版书籍,支持正版书籍不仅是尊重作者的辛勤劳动,也是鼓励更多优秀作品问世。

当前笔记内容主要为:步骤9 让函数更简单 章节的相关理解。

书籍总共分为5个阶段,每个阶段分很多步骤,最终是一步一步实现一个深度学习框架。例如前两个阶段为:

第 1 阶段共包括 10 个步骤 。 在这个阶段,将创建自动微分的机制
第 2 阶段,从步骤11-24,该阶段的主要目标是扩展当前的 DeZero ,使它能够执行更复杂的计算 ,使它能 够处理接收多个输入的函数和返回多个输出的函数


1.作为python 使用 


思考下我们之前的使用模式,在使用函数对象的时候,我们先要定义个出来,然后再使用,这里可以优化吗?

 x = Variable(np.array(0.5))
 A = Square()
 a = A(x)

是不是有点冗余,啰嗦,能否直接调用 a = f(x) 这种方式?

其实是可以的。我们对代码进行修改:

def square(x):
    f = Square()
    return f(x)

def exp(x):
    f = Exp()
    return f(x)

上面代码,更进一步优化

def square(x):
    return Square()(x)

def exp(x):
    return Exp()(x)

编写测试案例,看结果是否一致

    x = Variable(np.array(0.5))
    a= square(x)
    b = exp(a)
    y = square(b)

    y.grad = np.array(1.0)
    y.backward()
    print(x.grad)


输出结果还是  3.297442541400256  说明函数是等价的


2.简化backward方法

这里优化的目的是,简化用户在反向传播方面的工作。省略前面代码中的 y . grad = np.array(1 . 0),每次反向传播时,我们都要定义这个代码。代码改进:

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

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

    def backward(self):
        if self.grad is None:
            self.grad = np.ones_like(self.data)  # 初始化类型与data 一样

        funcs = [self.creator]
        while funcs:
            f = funcs.pop()
            x, y =f.input, f.output
            x.grad = f.backward(y.grad)

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


代码验证:

    # 优化ones_like 初始化后
    # 不需要定义 y.grad = np.array(1.0) 这个了
    x = Variable(np.array(0.5))
    y = square(exp(square(x)))
    y.backward()
    print(x.grad)

输出结果
3.297442541400256


3.仅支持ndarray 

为了减少用户误用,增加参数校验。我们的框架从开始就是只支持Variable ndarray 的示例。为了避免有些用户很可能会不小心使用 float 或 int 等数据类型。例如:Variable(1. 0) 和 Variable(3) 等 这些错误的使用,我们增加参数校验。

class Variable:
    def __init__(self, data):
        if data is not None:
            if not isinstance(data, np.ndarray):
                raise TypeError('{} is not supported'.format(type(data)))  #参数校验
        self.data = data
        self.grad = None
        self.creator = None

此外,由于NumPy 的特点,带来新的问题

考虑以下代码

    x = np.array([1.0])
    y = x ** 2
    print(type(x), x.ndim)

    print(type(y))


输出:

C:\Python\Python39-32\python.exe D:/pyworkspace/dezero-01/step09.py
<class 'numpy.ndarray'> 1
<class 'numpy.ndarray'>

这里执行时先注释掉其他case 案例


if __name__ == '__main__':
    # x = Variable(np.array(0.5))
    # a = square(x)
    # b = exp(a)
    # y = square(b)
    #
    # y.grad = np.array(1.0)
    # y.backward()
    # print(x.grad)
    #
    #
    # # 优化ones_like 初始化后
    # # 不需要定义 y.grad = np.array(1.0) 这个了
    # x = Variable(np.array(0.5))
    # y = square(exp(square(x)))
    # y.backward()
    # print(x.grad)
    #
    #
    # # 错误使用
    #
    # x = Variable(np.array(1.0))
    # x = Variable(None)
    # #x = Variable(1.0)  # 错误使用


    # Numpy 特性问题
    x = np.array([1.0])
    y = x ** 2
    print(type(x), x.ndim)

    print(type(y))

如果要上面的案例也可以执行,就要改造。考虑一种case ,由numpy 特性导致的问题。

    x= np.array(1.0)
    y = x ** 2
    print(type(x), x.ndim)
    print(type(y))

输出:

<class 'numpy.ndarray'> 0
<class 'numpy.float64'>

发现 0维的变成了 numpy.float64 、 numpy.float32

这意 味着 DeZero 函数的输山 Variable 可能是 numpy. float64 或 numpy. float32 类哑 的数据。我们在数据输出,输入过程中需要检查,强制转换下。


首先引入辅助函数 

def as_array(x):
    if np.isscalar(x):  # 使用 np.isscalar 函数来检查 numpy.float64 等属于标量
        return np.array(x)
    return x

修改 Function 类的输出,增加强制转换

class Function:
    def __call__(self, input):
        x = input.data
        y = self.forward(x)
        output = Variable(as_array(y))   # 转成 ndarray 类型
        output.set_creator(self)  # 输出者保存创造者对象
        self.input = input
        self.output = output  # 保存输出者。我是创造者的信息,这是动态建立 "连接"这 一 机制的核心
        return output

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

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

最后执行,发现所有的案例都可以跑通了:

if __name__ == '__main__':
    x = Variable(np.array(0.5))
    a = square(x)
    b = exp(a)
    y = square(b)

    y.grad = np.array(1.0)
    y.backward()
    print(x.grad)


    # 优化ones_like 初始化后
    # 不需要定义 y.grad = np.array(1.0) 这个了
    x = Variable(np.array(0.5))
    y = square(exp(square(x)))
    y.backward()
    print(x.grad)


    # 错误使用

    x = Variable(np.array(1.0))
    x = Variable(None)
    #x = Variable(1.0)  # 错误使用


    # Numpy 特性问题
    x = np.array([1.0])
    y = x ** 2
    print(type(x), x.ndim)

    print(type(y))


    x= np.array(1.0)
    y = x ** 2
    print(type(x), x.ndim)
    print(type(y))


输出结果:

C:\Python\Python39-32\python.exe D:/pyworkspace/dezero-01/step09.py
3.297442541400256
3.297442541400256
<class 'numpy.ndarray'> 1
<class 'numpy.ndarray'>
<class 'numpy.ndarray'> 0
<class 'numpy.float64'>

进程已结束,退出代码0

4.代码总结

到此相关优化以及完成,本节所有代码如下:

'''
step09.py
优化-函数更易于使用

'''

import numpy as np

class Variable:
    def __init__(self, data):
        if data is not None:   # 新增
            if not isinstance(data, np.ndarray):
                raise TypeError('{} is not supported'.format(type(data)))
        self.data = data
        self.grad = None
        self.creator = None

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

    def backward(self):
        if self.grad is None:
            self.grad = np.ones_like(self.data)

        funcs = [self.creator]
        while funcs:
            f = funcs.pop()
            x, y =f.input, f.output
            x.grad = f.backward(y.grad)

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

class Function:
    def __call__(self, input):
        x = input.data
        y = self.forward(x)                # 新增
        output = Variable(as_array(y))   # 转成 ndarray 类型  
        output.set_creator(self)  # 输出者保存创造者对象
        self.input = 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     #方法的参数 gy 是 一个 ndarray 实例 , 它是从输出传播而来的导数 。
        return gx


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

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


def square(x):
    f = Square()
    return f(x)

def exp(x):
    f = Exp()
    return f(x)

def as_array(x):        # 新增
    if np.isscalar(x):  # 使用 np.isscalar 函数来检查 numpy.float64 等属于标量
        return np.array(x)
    return x

if __name__ == '__main__':
    x = Variable(np.array(0.5))
    a = square(x)
    b = exp(a)
    y = square(b)

    y.grad = np.array(1.0)
    y.backward()
    print(x.grad)


    # 优化ones_like 初始化后
    # 不需要定义 y.grad = np.array(1.0) 这个了
    x = Variable(np.array(0.5))
    y = square(exp(square(x)))
    y.backward()
    print(x.grad)


    # 错误使用

    x = Variable(np.array(1.0))
    x = Variable(None)
    #x = Variable(1.0)  # 错误使用


    # Numpy 特性问题
    x = np.array([1.0])
    y = x ** 2
    print(type(x), x.ndim)

    print(type(y))


    x= np.array(1.0)
    y = x ** 2
    print(type(x), x.ndim)
    print(type(y))

  • 16
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值