前言
随着tensorflow2的发布, 构建一个复杂的深度学习模型变得简单很多。与此同时,更多的原理及细节被隐藏起来,很容易让人知其然而不知其所以然。如果要了解那些隐藏在简单表象下的原理和细节,首先想到的是阅读它的代码。然而这并不是一件容易的事,核心原理相关代码淹没在大量工程优化代码中,让人很难抓住要点。例如tensorflow使用了GPU技术用来加速模型的训练和预测,然而GPU技术属于工程上的优化技术,虽然在实际应用中不可或缺(想象一下你点击一个链接几分钟页面才跳出来吧),但并不涉及深度学习的相关算法和原理。
本人被这个问题困扰许久之后,决定自己写一个轻量级的深度学习框架,通过在小规模数据集上和tensorflow对比,来加深相关原理的理解。
接下来首先给出这个框架的架构设计,然后一步一步实现一个小而美的框架。
核心概念
- 模型: 表示一个执行特定任务的神经网络,是神经网络层的集合。
- 神经网络层: 完成特定任务的神经元集合, 如卷积层用于捕捉数据的空间特征,循环层用于捕捉数据的序列特征。
- 损失函数: 训练模型的算法,它的作用是在训练时量化模型输出和任务目标之间的差距。
- 向前传播: 数据经过从模型的输入层进入,模型的每一层都使用上一层的输出作为输入计算出一个输出,直到最后一层(模型的输出层)输出结果,预测阶段向前传播过程到此结束。如果是训练阶段,最后还要把结果送入损失函数计算误差.
- (误差梯度)反向传播: 在模型的训练阶段, 损失函数计算误差之后, 即开始误差梯度的反向传播过程: 把梯度从模型的输出层送入,层层传递,直到模型的输入层.
- 学习率优化算法: 深度学习模型一般使用随机梯度的方法进行训练,这涉及到学习率的问题,学习率优化算法在模型训练的不同阶段给出合适的学习率,从而加快模型训练速度,提升模型最终的性能。
- 正则化: 修改学习算法的算法,可以在不影响模型训练误差的情况下降低泛化误差,从而提升模型性能。其本质是惩罚与任务目标相悖的行。
整体架构
主要功能
作为一个主要用来学习原理的框架,会不涉及GPU加速和分布式并行运算,这样整个框架的架构就会变得比较简单。框架的主要功能有:
- 定义层的接口, 使层在模型中具有统一的抽象行为.
- 定义层参数的数据结构和命名规范, 使任意层的参数可以被灵活地访问和修改.
- 定义激活函数的接口.
- 定义模型工作做方式, 模型可以以序列的方式把层组装在一起, 实现向前传播和向后传播.
- 定义损失函数的接口.
- 定义优化器接口.
- 维护训练上下文环境,实现模型的训练: 向前传播, 反向传播, 参数更新, 训练过程记录, 上下文环境的保存和加载.
核心类
框架主要包含以下几个抽象类:
- Layer: 神经网络中的层,所有层的实现都是它的子类。
- LayerParam: Layer的参数.
- Activation: 激活函数,所有激活函数实现都是它的子类。
- Model: 模型, 一个或多个层的集合。
- Optimizer: 优化器,定义了优化器的接口. 所有优化器实现都是它的子类。
- Loss: 损失函数, 定义的损失函数的接口。
- Session: 会话,集成了模型及训练模型所需要的所有对象,用于训练,保存,恢复模型和训练状态。
架构图
上图描述了训练模型的过程, 样本数据提交给Session, Session把data送入模型执行向前传播过程, 收到模型的输出后,把输出数据及label送入损失函数计算误差梯度, 然后把梯度反向