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

前言

        随着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对象。

posted on 2020-04-10 17:26  自带buff  阅读(1993)  评论(0编辑  收藏  举报

导航