• 自己动手实现深度学习框架-1 架构设计


    TOC

    前言

            随着tensorflow2的发布, 构建一个复杂的深度学习模型变得简单很多。与此同时,更多的原理及细节被隐藏起来,很容易让人知其然而不知其所以然。如果要了解那些隐藏在简单表象下的原理和细节,首先想到的是阅读它的代码。然而这并不是一件容易的事,核心原理相关代码淹没在大量工程优化代码中,让人很难抓住要点。例如tensorflow使用了GPU技术用来加速模型的训练和预测,然而GPU技术属于工程上的优化技术,虽然在实际应用中不可或缺(想象一下你点击一个链接几分钟页面才跳出来吧),但并不涉及深度学习的相关算法和原理。         本人被这个问题困扰许久之后,决定自己写一个轻量级的深度学习框架,通过在小规模数据集上和tensorflow对比,来加深相关原理的理解。         接下来首先给出这个框架的架构设计,然后一步一步实现一个小而美的框架。

    核心概念

    • 模型: 表示一个执行特定任务的神经网络,是神经网络层的集合。
    • 神经网络层: 完成特定任务的神经元集合, 如卷积层用于捕捉数据的空间特征,循环层用于捕捉数据的序列特征。
    • 损失函数: 训练模型的算法,它的作用是在训练时量化模型输出和任务目标之间的差距。
    • 向前传播: 数据经过从模型的输入层进入,模型的每一层都使用上一层的输出作为输入计算出一个输出,直到最后一层(模型的输出层)输出结果,预测阶段向前传播过程到此结束。如果是训练阶段,最后还要把结果送入损失函数计算误差.
    • (误差梯度)反向传播: 在模型的训练阶段, 损失函数计算误差之后, 即开始误差梯度的反向传播过程: 把梯度从模型的输出层送入,层层传递,直到模型的输入层.
    • 学习率优化算法: 深度学习模型一般使用随机梯度的方法进行训练,这涉及到学习率的问题,学习率优化算法在模型训练的不同阶段给出合适的学习率,从而加快模型训练速度,提升模型最终的性能。
    • 正则化: 修改学习算法的算法,可以在不影响模型训练误差的情况下降低泛化误差,从而提升模型性能。其本质是惩罚与任务目标相悖的行。

    整体架构

    主要功能

            作为一个主要用来学习原理的框架,会不涉及GPU加速和分布式并行运算,这样整个框架的架构就会变得比较简单。框架的主要功能有:

    1. 定义层的接口, 使层在模型中具有统一的抽象行为.
    2. 定义层参数的数据结构和命名规范, 使任意层的参数可以被灵活地访问和修改.
    3. 定义激活函数的接口.
    4. 定义模型工作做方式, 模型可以以序列的方式把层组装在一起, 实现向前传播和向后传播.
    5. 定义损失函数的接口.
    6. 定义优化器接口.
    7. 维护训练上下文环境,实现模型的训练: 向前传播, 反向传播, 参数更新, 训练过程记录, 上下文环境的保存和加载.

    核心类

            框架主要包含以下几个抽象类:

    • Layer: 神经网络中的层,所有层的实现都是它的子类。
    • LayerParam: Layer的参数.
    • Activation: 激活函数,所有激活函数实现都是它的子类。
    • Model: 模型, 一个或多个层的集合。
    • Optimizer: 优化器,定义了优化器的接口. 所有优化器实现都是它的子类。
    • Loss: 损失函数, 定义的损失函数的接口。
    • Session: 会话,集成了模型及训练模型所需要的所有对象,用于训练,保存,恢复模型和训练状态。

    架构图

    在这里插入图片描述         上图描述了训练模型的过程, 样本数据提交给Session, Session把data送入模型执行向前传播过程, 收到模型的输出后,把输出数据及label送入损失函数计算误差梯度, 然后把梯度反向送入模型执行反向传播过程。待反向传播过程完成,调用优化器执行更新模型的参数。上图中GenOptimizer是广义优化器, Optimizer是学习率专用优化器, 广义优化器指其他所有用来更新参数的算法如: L2正则化优化算法优化.

    设计约束

    LayerParam

    属性:

    名称 读/写 类型 说明
    name r string 参数名称,必须全局唯一
    value rw darray 参数值
    gradient rw darray 参数值的梯度
    udt rw int 记录参数被跟新的次数

    另外, 优化器可以根据需要添加属性。

    name格式: /layerName/layerName/.../paramName. 参数名使用树形结构, layerName是参数所属层的名字, 因此只要保证layerName全局唯一, paramName只需要保证层范围内唯一就能满足要求。

    Layer

    属性

    名称 读/写 类型 说明
    layer_id r int 层的全局唯一id
    name r string 层的名字, 全局唯一
    params r list 层的参数列表
    inshape r tuple 向前传播时输入数据的形状
    outshape r tuple 向前传播时输出数据的形状
    activation r Activation 激活函数对象

    方法

    __ init __(self, *outshape, **kargs)

    outshape: 指定输出形状, 可以是tuple或in. kargs: 可选参数

    • activation: 激活函数的名字。
    • inshape: 输入形状。除了第一个层外, 其他层都不用在这个方法传入inshape。

    join(self, pre_layer, *inshape=None)

    把当前层和前一个层连接在一起. 使用前一个层的outshape作为当前层的inshape, 如果设置了inshape参数, 则使用inshape参数作为当前层的inshape。如果是第一个层这个方法不会被调用。

    • pre_layer: 前一个层。
    • inshape: 指定当前层的输入形状。

    init_params(self)

    初始化参数,由子类实现。当Layer发现inshape和outshape都已经设置了, 才会调用这个方法。

    forward(self, in_batch, training=False)

    向前传播的方法。由子类实现。

    • in_batch: 输入数据.
    • training: 是否处于训练状态.

    backward(self, gradient)

    反向传播方法。由子类实现。

    • gradient: 反向传回来的梯度。

    reset(self)

    重置当前层的状态。由子类实现。

    Model

    方法

    __init __(self, layers=None)

    • layers: Layer对象list.

    add(self, layer)

    向模型中添加一个层.

    get_layer(self, index)

    使用索引从model中得到一个Layer对象。Model中需要维护一个layer list, index是layer在这个list中的索引。

    layer_iterator(self)

    得到层的迭代器.

    assemble(self)

    组装层。检查输入层的inshape参数是否设置。然后调用layer的join方法把layer连接在一起。

    predict(self, in_batch, training=False)

    向前传播输出模型的预测值。参数的含义和Layer的forward相同。

    backward(self, gradient)

    反向传播。参数的含义和Layer的forward相同。

    reset(self)

    重置模型状态。

    Activation

    方法

    __call __(self, in_batch)

    激活函数的调用方法,由子类实现。

    • in_batch: 向前传播时输入的数据。

    grad(self, gradient)

    反向传播时计算并返回激活函数的梯度。

    Loss

    属性

    名称 读/写 类型 说明
    gradient r darray 损失函数的梯度值

    方法

    __call __(self, y_true, y_pred)

    损失函数调用方法。

    • y_true: 数据的标签值。
    • y_pred: 由模型输出的数据的预测值。

    Optimizer

    方法

    __ call__(self, model)

    优化器函数的调用方法.

    • model: Model对象.

    Session

    属性

    名称 读/写 类型 说明
    model r Model 训练的目标模型
    loss r Loss 训练模型使用的损失函数
    history r dict 训练历史记录数据

    方法

    __init __(self, model, loss, optimizer, genoptimizer=None)

    • model: Model对象。
    • loss: Loss对象。
    • optimizer: optimizer对象。
    • genoptimizer: 广义优化器。

    batch_train(self, data, label)

    分批训练。返回损失值。

    • data: 输入数据, darray。
    • label: 数据标签, darray。

    save(self, fpath)

    把模型和训练上下文保存到fpath指定的位置。

    load(cls, fpath)

    这是一个类方法。从fpath指定位置加载模型和训练上下文,返回已经准备就绪的Session对象。

  • 相关阅读:
    JavaScript对象继承的实现
    Redis资料
    Difference between LINQ to SQL and the Entity Framework
    闭包,懂不懂由你,反正我是懂了
    Castle资料
    csu 1242 碱基配对
    csu 1242 碱基配对——一个错误的解答
    [转载]zoj 分类
    计算素数
    魔方阵
  • 原文地址:https://www.cnblogs.com/brandonli/p/12674764.html
Copyright © 2020-2023  润新知