C++卷积神经网络实例:tiny_cnn代码详解(12)——从CNN中看多态性

  最近由于在准备论文的相关事宜,导致博客的更新速度有点缓慢,望大家见谅。不过该更新还是要更新的,所以今天我就挤出一点时间来更新一篇。由于之前的博文已经将tiny_cnn中相关的网络层结构介绍的差不多,接下来的博文中着重介绍卷积神经网络的训练流程和测试流程,重点就是前向传播算法和反向传播算法。不过我在研究CNN前向传播算法的流程时,发现作者在前向传播算法的调用过程中,很好的体现了C++的多态性特点,考虑到多态性是各个招聘单位面试时饱受青睐的一道考题,于是决定单独拿出一篇博文来从tiny_cnn的角度介绍作者是如何运用C++的多态性的。

  一、多态性

  关于C++的多态性归类问题网上的观点也是参差不齐。稍稍查了一下资料,有人热衷于将多态性分为两类:静多态和动多态。所谓静多态即是通过模板和重载实现,在程序的编译阶段即完成模板或重载函数的实参演绎,故称之为静多态。而动多态性则是通过继承和虚函数来实现,即程序在编译阶段只负责检查语法,并不能确定在运行的时候会具体实例化哪些函数对象,可能会实例化基类的虚函数,也可能会实例化各个子类中间相应的对虚函数进行重载之后的成员函数,这就是所谓的动多态。从这个角度看的话,和C++中的静态联编以及动态联编的概念比较相近。当然也有的人认为C++的多态性默认就是动多态,也就是说多态性只能通过继承和虚函数来实现。当然我们这里的重点不是争论谁对谁错,而是分析tiny_cnn中是如何实现多态性的,或者说是动多态。

  二、layers类容器结构

  tiny_cnn之所以能够表现出明显的实例多态性,主要是得益于layers这个类容器结构。这个容器中保存了组成tiny_cnn网络模型的各种网络结构:

  从上图中可以看出layers容器中保存的各个层结构通过指向layer_base类型的指针next_和prev_相互联系,并且通过head()成员函数返回第一个元素(Input_Layer),通过tail()成员函数返回最后一个元素(fully_connected_layer)。

  三、前向传播函数forward_propagation()的继承体系

  在分析forward_propagation()函数的调用以及实例化过程之前,首先需要明确都有哪些类定义了forward_propagation()或者其虚函数的形式,这里给出forward_propagation函数的继承体系结构图:

  首先,在基类layer_base中,定义了forward_propagation()的纯虚函数形式,供各个子类进行实例化:

  然后在各个子类中对其进行了重写(注意,纯虚函数必须在子类中进行重写),在input_layer中:

  可见在input_layer层中的前向传播算法只是起到一个“调用下一层前向传播算法”的作用,并未做实质性卷积运算。接下来是convolutional_layer和average_pooling_layer层的前向传播算法。由于这两层的前向传播操作相同,因此作者选择将其对应的forward_propagation函数封装在他们公共的基类partial_connected_layer中:

  可见卷积层和下采样层的前向传播函数承担了真正的前向传播任务,包括与映射核进行卷积、添加加性偏执、经过激活函数处理等等。接下来是全连接层中的forward_propagation函数:

  全连接层是卷积神经网络的最后一层,前向传播到此结束。

  四、实例化流程

  在完成以上继承结构的分析之后,接下里通过程序的实际运行来观察各个前向传播函数的实例化过程,进一步体会在程序运行中所体现出的多态性。首先需要在上面三处forward_propagation函数定义中加上断点,以便观察函数的实例化先后顺序。F5调试运行程序,发现程序首先命中Input_Layer中的forward_propagation函数,即先实例化Input_Layer中的函数对象:

  这是由于在训练函数中,调用了fprop函数作为前向传播算法的启动开关,而启动时以layers_.head()为入口,.head()函数返回的是指向layers容器中第一个元素的layer_base类型指针,即Input_Layer,因此程序选择先实例化Input_Layer类中对应的forward_propagation函数。继续F5运行,命中partial_connected_layer类中的forward_propagation函数:

  原因很明确,Input_Layer中的next_指针指向了layers容器中的下一个层结构,也就是卷积层,但编译器发现卷积层类中并没有实例化forward_propagation函数,因此继续搜索其基类,于是命中并实例化了partial_connected_layer类中的forward_propagation函数。接下里连按若干次F5继续调试,发现程序会连续命中partial_connected_layer的断点,这当然不是程序BUG了,而是因为接下来的几层均为卷积层或下采样层,而这两层中都没有实例化forward_propagation函数,因此程序都是毫不例外的选择实例化其基类中的重载虚函数。按到第4次或者第5次F5的时候(视程序的结构而定),命中全连接层fully_connected_layer中的断点,说明该轮到实例化fully_connected_layer类对应的forward_propagation函数了:

  对整个网络模型中的forward_propagation函数都完成实例化之后,程序将采用递归的方式返回。

  五、总结

  在这个实例化过程中,已经充分体现出了程序在执行过程中所表现出的多态性。通俗的讲,就是需要实例化那个子类层中对应的虚函数了,就去实例化那个虚函数,而这些在编译期肯定是无法确定的,编译期间编译器只能检查语法错误,但并不能知道用户是怎样安排网络结构,换句话说,卷积层之后一定就是均值下采样层吗?不一定,还可能是最大值下采样层。不过这种多态性依赖严谨的继承体系,并且指向各个层的指针必须是基类类型的,也就是layer_base*类型。

时间: 2024-11-13 12:25:57

C++卷积神经网络实例:tiny_cnn代码详解(12)——从CNN中看多态性的相关文章

DeepLearning tutorial(4)CNN卷积神经网络原理简介+代码详解

DeepLearning tutorial(4)CNN卷积神经网络原理简介+代码详解 @author:wepon @blog:http://blog.csdn.net/u012162613/article/details/43225445 本文介绍多层感知机算法,特别是详细解读其代码实现,基于python theano,代码来自:Convolutional Neural Networks (LeNet).经详细注释的代码和原始代码:放在我的github地址上,可下载. 一.CNN卷积神经网络原理

tiny_cnn代码详解(3)——层间继承关系

在上一篇博文中我们顺利将tiny_cnn的程序调试通过,在这篇博文中我们尝试从整体角度给出对tiny_cnn这个深度学习框架的解读,重点论述一下其各个层直接类封装的继承关系. 一.卷积神经网络快速入门 tiny_cnn作为卷积神经网络的一种实现形式,在探讨其框架结构之前,首先需要简要介绍一些卷积神经网络相关的知识.首先,给出经典卷积神经网络的网络结构: 这个是经典的LeNet-5的网络结构图,五层网络.最早用于支票上的手写数字识别,也是最早的商业化的深度学习模型.从上图中可以看出,卷积神经网络主

C++卷积神经网络实例:tiny_cnn代码详解(7)——fully_connected_layer层结构类分析

之前的博文中已经将卷积层.下采样层进行了分析,在这篇博文中我们对最后一个顶层层结构fully_connected_layer类(全连接层)进行分析: 一.卷积神经网路中的全连接层 在卷积神经网络中全连接层位于网络模型的最后部分,负责对网络最终输出的特征进行分类预测,得出分类结果: LeNet-5模型中的全连接层分为全连接和高斯连接,该层的最终输出结果即为预测标签,例如这里我们需要对MNIST数据库中的数据进行分类预测,其中的数据一共有10类(数字0~9),因此全全连接层的最终输出就是一个10维的

C++卷积神经网络实例:tiny_cnn代码详解(11)——层结构容器layers类源码分析

在这篇博文中我们对tiny_cnn卷积神经网络模型中的最后一个网络结构方面的类--layers做简要分析. 首先,layers通俗的讲可以被称为是层结构的vector,即层结构容器.由于卷积神经网络是一个多层的网络模型,因此有必要将网络中各个层进行统一管理,这便引出了本篇博文中所要介绍的layers类.layers类是一个vector类型的变量,其中压入的元素就是网络中的各个层模型,这里给出一个简单的结构图,一目了然: 从上图中可以清晰的看到layers的vector结构,说白了就是一个层结构类

C++卷积神经网络实例:tiny_cnn代码详解(9)——partial_connected_layer层结构类分析(下)

在上一篇博文中我们着重分析了partial_connected_layer类的成员变量的结构,在这篇博文中我们将继续对partial_connected_layer类中的其他成员函数做一下简要介绍. 一.构造函数 由于partial_connected_layer类是继承自基类layer,因此在构造函数中同样分为两部分,即调用基类构造函数以及初始化自身成员变量: partial_connected_layer(layer_size_t in_dim, layer_size_t out_dim,

正则表达式表单验证实例代码详解

正则表达式表单验证实例代码详解 这篇文章主要介绍了正则表达式表单验证实例详解的相关资料,大家可以参考下.正则表达式表单验证具体内容如下: 首先给大家解释一些符号相关的意义 * 匹配前面的子表达式零次或多次: ^ 匹配输入字符串的开始位置:$匹配输入字符串的结束位置 1. /^$/ 这个是个通用的格式. 2. 里面输入需要实现的功能. \d 匹配一个数字字符,等价于[0-9] + 匹配前面的子表达式一次或多次: ?匹配前面的子表达式零次或一次: 下面通过一段代码给大家分析表单验证正则表达式,具体代

jQuery选择器代码详解(五)——实例说明tokenize的解析过程

原创文章,转载请写明出处,多谢! 以下分析基于jQuery-1.10.2.js版本. 下面将以$("div:not(.class:contain('span')):eq(3)")为例,说明tokenize和preFilter各段代码是如何协调完成解析的.若想了解tokenize方法和preFilter类的每行代码的详细解释,请参看如下两篇文章: jQuery选择器代码详解(三)--tokenize方法 jQuery选择器代码详解(四)--Expr.preFilter 下面是tokeni

DeepLearning tutorial(3)MLP多层感知机原理简介+代码详解

DeepLearning tutorial(3)MLP多层感知机原理简介+代码详解 @author:wepon @blog:http://blog.csdn.net/u012162613/article/details/43221829 本文介绍多层感知机算法,特别是详细解读其代码实现,基于python theano,代码来自:Multilayer Perceptron,如果你想详细了解多层感知机算法,可以参考:UFLDL教程,或者参考本文第一部分的算法简介. 经详细注释的代码:放在我的gith

JQuery选择器代码详解(三)——tokenize方法

原创文章,转载请注明出处,多谢! /* * tokenize函数是选择器解析的核心函数,它将选择器转换成两级数组groups * 举例: * 若选择器为"div.class,span",则解析后的结果为: * group[0][0] = {type:'TAG',value:'div',matches:match} * group[0][1] = {type:'CLASS',value:'.class',matches:match} * group[1][0] = {type:'TAG'