Reading Note「7」Deep Forest: Towards an Alternative to Deep Neural Networks

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Reading Note「7」Deep Forest: Towards an Alternative to Deep Neural Networks相关的知识,希望对你有一定的参考价值。

参考技术A 在听完WAIC上周志华大佬的分享之后,就决定阅读一下他提出的「Deep Forest」,他认为这是一个取代「Deep Neural Network」的思路。

首先在他提出了当前深度神经网络架构的很多不足之处,

所以为了解决神经网络上述的问题,作者提出了「Deep Forest」的概念,也就是将很多个森林组合在一起(森林就是树的组合,Deep Forest中若干森林组成一层,然后通过若干层的叠加实现效果)。在这个概念下作者提出了「multi-Grained Cascade Forest, gcForest」。

首先先来说一下「Forest」
对于一个已经训练好的Forest,输入一个sample我们就可以得到这个sample的class distribution。首先对于一个Forest中的每一颗树,它的叶子结点一定是一种类别分布(即在训练过程中落到这个叶子中的各个类别的sample所占的比例,然后用这个比例来作为预测的时候的结果),然后对整个Forest输入一个input时就是将它放入每一棵树,观察它会落到哪一个叶子结点,即可以得到对应的class distribution,(其实训练的时候,每一个sample也可以得到其对应的class distribution,就是这个sample所在的叶子结点的sample分布)然后只需要对Forest中每一颗树的预测结果进行平均,就可以得到这棵树对这个sample的预测结果。

「Cascade Forest」
作者通过四颗树实现了「Cascade Forest」,作为深度森林的一层,四颗树中分别为两颗「completely-random tree forests」以及两颗「random forests 」(具体的差别我也不知道,但是结构应该是类似的),然后通过训练四颗不同的森林,构成了一层,如下图所示,然后将这一层的输出(每一个森林都输出一个class distribution)作为下一层的输入,最后构成了如下的深度森林模型。

「Training Process」
那么有这样一颗「Cascade Forest」之后如何训练呢,或者说如何让prediction逼近ground truth呢?在深度学习中采用的是「Back Propagation」的方法迭代求解整个网络的最优参数结构,但是在「Cascade Forest」中很明显是不能够进行这样的训练的,因此作者提出的训练方式就是叠加深度森林的层数(四颗森林为一层),通过将上一层的输出作为该层的输入来不断地迭代,一直到新增加层数对整个网络表现的提升不再明显为止。

「Multi-Grained」
在图像识别以及语音识别中,深度学习具有能够充分挖掘特征之间的位置关系的特点,例如利用二维卷积核对图像进行特征提取的时候,像素之间的关系会被反应在kernel weight上,所以为了使得「Cascade Forest」能够对特征之间的关系同样敏感,作者利用了一个「sliding window」来提取输入特征的局部信息,从而建立位置关系,如下如所示。

从图中我们可以发现,无论是sample属于「序列型」还是「图像型」,都可以通过滑动窗口将其转化为多个instance,将每个instance单独输入到一个预处理森林(与深度森林的每一层有所不同,它仅仅只由两个森林构成,算是对数据的预处理吧),然后将每个instance预处理的结果结合起来作为深度森林的输出。

同时通过调整sliding window的大小以及stride的大小,可以实现「multi-scale」这个功能。因此整体的网络架构如下图所示。

「Experiment」
实验部分,主要是可以聊一聊究竟怎么来训练这个新型深度结构。正如前面所说,训练过程是一个森林层增加的过程,但是究竟怎样才算是训练结束呢,这个思路有一点像决策树剪枝的想法。

首先我们有一个「Training Set」,然后我们将它划分为80%的「Growing Set」以及20%的「Estimating Set」,然后用「Growing Set」来指导整个网络中每一棵树的生成,从而确定深度结构,而用「Estimating」来判断当前的结构的表现,当表现变化不大的时候则停止继续叠加森林层。最终,再用整个「Training Set」来重新训练一遍深度结构(我认为在最后这个训练中,并不会再调整网络结构,即每一个树的结构,只会调整每一个叶子结点的分布估计,用Training Set在每个叶子结点中sample的类别分布来作为每一个叶子结点的分布估计)。

thinking in java -----reading note

# thinking in java 4th
# reading note
# victor
# 2016.02.16

chapter 2 一切都是对象

2.1 用句柄操纵对象
    使用句柄作为标识符指向一个对象.但拥有句柄并不意味着有一个对象同它连接.
    例如,创建一个String句柄: String s;
    此时,这里创建的是句柄,并不是对象.如果向s发一条消息,就会获得一个错误.因此,较为安全的做法是:创建一个句柄时,无论如何都进行初始化.

2.2 创建对象
    通常使用 new 关键字在创建句柄时将其与一个对象相连.例如:String s = new String("hello world!");

2.2.1 保存位置
    1.寄存器.寄存器一般由编译器分配,我们没有直接控制权.
    2.堆栈.驻留于常规RAM区域,但可以通过"堆栈指针"获得处理的直接支持.指针下移和上移会创建和释放内存(push or pop),栈的基地址一定大于或等于栈顶地址.其中JAVA的对象句柄就保存在堆栈里,但对象并不放到其中.
    3.堆.一种常规用途的内存池(也在RAM区域),其中保存了JAVA的对象.和堆栈不同,编译器不需要知道从堆中分配多少存储空间,也不必知道存储的数据要停留多长时间.用堆保存数据会更加灵活.
    4.静态存储.静态存储(static)的数据将位于固定位置.
    5.常数存储.
    6.非RAM存储.

2.2.2 基本类型
    基本类型不是用new创建变量,而是创建非句柄的"自动变量",且存放在堆栈中.
    基本数据类型            包装类
    byte                    Byte
    boolean                 Boolean
    short                   Short
    char                    Character
    int                     Integer
    long                    Long
    float                   Float
    double                  Double
    包装类便于涉及对象的操作,且包含相关操作方法.
    java的两个类:BigInteger和BigDecimal,是用于高精度计算的,但它们没有对应的基本类型,都是"包装类".

2.2.3 java的数组
    创建对象数组时,实际创建的是一个句柄数组.每个句柄都会自动初始化成一个特殊值,并带有自己的关键字:null.正式使用前,必须为每个句柄都分配一个对象,否则会报错.JAVA也可以创建基本类型数组,同样,编译器会对它初始化.

2.3 变量的清除
    JAVA会帮助我们完成变量清除工作,不需要自己考虑变量存在时间.

2.3.1 作用域
    {
        int x =12;
        {
            int y = 13;
        }
    }
    java中作用域由花括号的位置决定.
    作用域的变量,只有在作用域结束前才可以使用.
    值得一提的是java不能像下面这样书写代码:
    {
        int x =12;
        {
            int x =123;/*illegal*/
        }
    }

2.3.2 对象的作用域
    在JAVA中,用new创建的对象,只要愿意,它们会一直保留下去.同时,JAVA有"垃圾收集器",会查找用new创建的所有对象,并辨别出哪些不再被引用,随后会自动释放闲置对象所占据的内存,以便新对象的使用.

2.4 类
    新建类:
    class Atypename {/*class body is here */}
    实例化对象:
    Atypename a = new Atypename();

2.4.1 字段和方法
    定义一个类的时候,可以在类里设置两种元素:数据成员(字段)和成员函数(方法).
    字段可以是对象,也可以是基本类型.如果某个基本类型属于类成员,那么它们不显式初始化,也可以获得一个默认值,但这不适用于局部变量.

2.5 方法,自变量和返回值
    方法的结构:
    返回类型 方法名(/*自变量列表*/){/*方法主体*/}
    
    一般的方法只能通过对象调用,如:
        Example e = new Example();
        e.getdetails();
    而静态方法可以通过类调用:
        Example.getmessage();// getmessage() is a static method

2.5.1 自变量
    除了基本类型外,自变量实际传递的都是对象的"句柄".

2.6 构建JAVA程序
    JAVA的类存放在自己的命名空间中(package).一个文件的每个类都获得独一无二的标识符.

2.6.1 使用其它组件    
    import java.util.vector;
    # 导入util下的vector类
    
    import java.util.*;
    # 导入util下的所有类

2.6.2 Static 关键字
    一旦将什么东西设为 static,数据或方法就不会同那个类的任何对象实例联系到一起。所以尽管从未创建那个类的一个对象,仍能调用一个 static 方法,或访问一些 static 数据。
    而在这之前,对于非 static 数据和方法,我们必须创建一个对象,并用那个对象访问数据或方法。这是由于非static 数据和方法必须知道它们操作的具体对象。
    但是值得一提的是,在对象创建前,static 方法不可以调用其它对象成员.

    静态变量的例子:
    class StaticTest{
        Static int i = 47;
    }
    StaticTest st1 = new StaticTest();
    StaticTest st2 = new StaticTest();
    
    尽管,此处我们创建了两个对象,但是StaticTest.i 仍然只有一个,即这两个对象共享同样的i。
    无论st1.i 还是st2.i 都有同样的值47,因为它们引用的是同样的内存区域。所以,若是执行StaticTest.i ++, 可以得到st1.i = st2.i =48.
    
    静态方法的例子:
    class StaticFun{
        Static void incr{StaticTest.i++;}
    }
    静态方法既可以通过对象调用,又可以通过它的类直接调用.

    注意:
        Static  一项重要的用途就是帮助我们在不必创建对象的前提下调用那个方法。正如以后会看到的那样,这一点是至关重要的——特别是在定义程序运行入口方法 main()的时候。
 
2.7 注释和嵌入文档
    java 有两种类型的注释。
    第一种继承自C++,进行编译时,/*和*/之间的所有东西都会被忽略:
        /* 这是
        *     一段注释
        *  它跨越多行
        */
    第二种注释是单行注释: //从此向后的都是注释(单行注释)

2.7.1 注释文档
    用于提取注释的工具叫作 javadoc。它采用了部分来自 Java 编译器的技术,查找我们置入程序的特殊注释标记。它不仅提取由这些标记指示的信息,也将毗邻注释的类名或方法名提取出来。这样一来,我们就可用最轻的工作量,生成十分专业的程序文档。
    javadoc 输出的是一个 HTML 文件,可用自己的 Web 浏览器查看。该工具允许我们创建和管理单个源文件,并生动生成有用的文档。由于有了 jvadoc,所以我们能够用标准的方法创建文档。而且由于它非常方便,所以我们能轻松获得所有 Java 库的文档。

2.7.2 具体语法
    所有 javadoc 命令都只能出现于“/**”注释中。但和平常一样,注释结束于一个“*/”。主要通过两种方式
来使用 javadoc:嵌入的 HTML,或使用“文档标记”。其中,“文档标记”(Doc tags)是一些以“@”开头
的命令,置于注释行的起始处(但前导的“*”会被忽略)。
有三种类型的注释文档,它们对应于位于注释后面的元素:类、变量或者方法。也就是说,一个类注释正好位于一个类定义之前;变量注释正好位于变量定义之前;而一个方法定义正好位于一个方法定义的前面。如下面这个简单的例子所示:
 
/** 一个类注释 */
public class docTest {
/** 一个变量注释 */
public int i;
/** 一个方法注释 */
public void f() {}  
}
 
注意 javadoc 只能为"public"(公共)和"protected"(受保护)成员处理注释文档。"private"(私有)和"friendly"(友好)成员的注释会被忽略,我们看不到任何输出(也可以用-private 标记包括 private 成员)。这样做是有道理的,因为只有 public 和 protected 成员才可在文件之外使用。然而,所有类注释都会包含到输出结果里。

2.7.3 嵌入HTML
    可以在文档注释中使用HTML标签来对普通文本格式化,使其更美观。

2.7.4 @see: 引用其他类
    所有三种类型的注释文档都可包含@see 标记,它允许我们引用其他类里的文档。对于这个标记,javadoc 会生成相应的 HTML,将其直接链接到其他文档。格式如下:
 
    @see 类名
    @see 完整类名
    @see 完整类名#方法名

2.7.5 类文档标记
    随同嵌入 HTML 和@see 引用,类文档还可以包括用于版本信息以及作者姓名的标记。类文档亦可用于“接口”目的。

    1. @version
    格式如下:
    @version 版本信息
    其中,“版本信息”代表任何适合作为版本说明的资料。若在 javadoc 命令行使用了“-version”标记,就会从生成的 HTML 文档里提取出版本信息。
 
    2. @author
    格式如下:
    @author 作者信息
    其中,“作者信息”包括您的姓名、电子函件地址或者其他任何适宜的资料。若在 javadoc 命令行使用了“-author”标记,就会专门从生成的 HTML 文档里提取出作者信息。

2.7.6 变量文档标记
    变量文档只能包括嵌入的 HTML 以及@see 引用。

2.7.7 方法文档标记
    除嵌入 HTML 和@see 引用之外,方法还允许使用针对参数、返回值以及违例的文档标记。  

    1. @param
    格式如下:
    @param 参数名 说明
    其中,"参数名"是指参数列表内的标识符,而"说明"代表一些可延续到后续行内的说明文字。一旦遇到一个新文档标记,就认为前一个说明结束。可使用任意数量的说明,每个参数一个。
 
    2. @return
    格式如下:
    @return 说明
    其中,"说明"是指返回值的含义。它可延续到后面的行内。
 
    3. @exception
    "异常"(Exception)是一些特殊的对象,若某个方法失败,就可将它们“扔出”对象。调用一个方法时,尽管只有一个异常对象出现,但一些特殊的方法也许能产生任意数量的、不同类型的异常。所有这些异常都需要说明。所以,异常标记的格式如下:
    exception 完整类名 说明
    其中,"完整类名"明确指定了一个异常类的名字,它是在其他某个地方定义好的。而"说明"告诉我们为什么这种特殊类型的违例会在方法调用中出现。
 
    4. @deprecated
    该标记用于指出一些旧功能已由改进过的新功能取代。该标记的作用是建议用户不必再使用一种特定的功能,因为未来改版时可能摒弃这一功能。若将一个方法标记为@deprecated,则使用该方法时会收到编译器的警告。

以上是关于Reading Note「7」Deep Forest: Towards an Alternative to Deep Neural Networks的主要内容,如果未能解决你的问题,请参考以下文章

深度学习阅读列表 Deep Learning Reading List

论文笔记Reading Scene Text in Deep Convolutional Sequences

编译原理--reading note

paper reading----Xception: Deep Learning with Depthwise Separable Convolutions

Paper Reading 4:Massively Parallel Methods for Deep Reinforcement Learning

Paper Reading 1 - Playing Atari with Deep Reinforcement Learning