zoukankan      html  css  js  c++  java
  • nnet3配置中的“编译”

    编译概述

    编译流程将NnetComputationRequest作为输入,输出NnetComputationComputationRequest包含可用的输入索引 以及 请求的输出索引。

    不提供输出索引并让编译器找出需要的输入索引的原因是,某些网络,比如RNNs,为输出给定输出,可能会消耗任意数量的输入特征

       

    struct ComputationRequest包含除Nnet之外计算所需的所有数据。该结构体用于创建Computation

    ComputationRequest中最重要的信息是:

    1. 各个输入结点处提供的可用索引;
    2. 各个输出节点处请求的索引;
    3. 是否要执行反向传播;

    相同的输入结点不能在IoSpecification数组input中出现两次,输出结点亦然;

       

    创建计算图

    详述ComputationGraph

       

    为了提高效率,ComputationGraph会将Cindex映射为cindex_id,反之亦然。

       

    ComputationGraph类的定义如下:

    struct ComputationGraph {

    //cindex_idCindex的映射

    std::vector<Cindex> cindexes;

       

    //is_input[cindex_id]表示cindex_id对应的Cindex是否在输入中是可用的

    std::vector<bool> is_input;

       

    // dependencies[cindex_id]表示计算cindex_id所依赖的其他cindex_id列表

    std::vector<std::vector<int32> > dependencies;

    private:

    //Cindexcindex_id的映射

    // 必须通过GetCindexId()函数来调用

    unordered_map<Cindex, int32, CindexHasher> cindex_to_cindex_id_;

    };

       

    dependencies成员的确切意义依赖于编译阶段。

    在编译的初始阶段,dependencies包含函数Descriptor::GetDependencies()返回的Cindexes对应的所有cindex_ids

    之后,对dependencies进行修剪,只保留在实际计算中使用到的cindex_ids

       

    注意,与Descriptor类似的,Component也有GetDependencies()以及IsComputable()函数。然而,当且仅当ComponentGeneralComponent时才有用。

       

    创建计算图——ComputationGraph

    详述计算图

       

    ComputationGraphBuilder用于构建ComputationGraph。对于一个最简单的例子,从网络请求的输出开始,并沿着网络向前计算其依赖,并添加到ComputationGraph中,直至计算到输入结点。

    判断ComputationRequest中是否包含了所依赖的输入结点,如果不足,则无法进行计算。(在841研究所实习时,为现有DNN添加一输出层后,脚本得出无法计算的结论,就是依赖于此)。

       

    基础算法

    本算法不实际使用。

    构建计算图时,需要使用如下算法来确定每个Cindex是否可以从提供的输入计算得到:

    • 调用Descriptor::GetDependencies()得到输出层处的所有依赖项;
    • 调用IsComputable()确定输入中哪些Cindexes可以用于计算,对实际不参与计算的依赖关系进行修剪
    • 检查所有请求的输出都是可计算的
    • 修脚掉所有不需要参与计算的cindex_ids

       

    将计算组织为多步

    根据拓扑顺序对Cindex排序并分组,使同组Cindexes可同时计算。

       

    使用该算法的动机

    ComputationGraphBuilder接口

       

       

    将网络计算组织为一系列的步骤

    介绍计算步骤序列

    一旦有了计算图,那么原则上就有足够的信息来执行计算了。可以对计算图中的Cindexes以拓扑顺序排序,可以依赖关系作为输入,以单独计算每个Cindex。不幸的是,这并不十分有效,因为进行的矩阵操作不能发挥全部效率,除非矩阵相当大;使用GPU时尤其如此。所以需要将多个Cindexes合为一个批次(batch),这样同一batch中的Cindex可以同时计算。batch被称为一个步骤step),大致对应于NnetComputation中的一个命令。

       

    接下来将计算中所有的cindex_ids划分为一个step序列

    step序列有以下属性:

    1. 给定step中所有cindex_ids对应于计算图中的一个节点(component-node
    2. 给定step中所有cindex_ids的所有依赖以及在之前的step中计算完成了

       

    step序列还需要满足一些额外的、模糊的属性:

    1. ComputationRequest中的任何输入Cindex或输出Cindex必须在一个step中,step序列中索引的顺序与ComputationRequest中指定的顺序相同。(注意:输入可以是kComponentkInput类型的节点)。
    2. 如果一个step对应于kComponent类型的节点(并且不对应于ComputationRequest中的输入),那么紧接着的下一个step必须对应于kDescriptor类型的节点,并且这两个step中的索引序列必须是相同的。
    3. 如果一个step对应于kDimRange类型的节点,则必须有另一个对应与源节点的step,这两个step中的索引和顺序完全相同。(这允许我们为kDimRange节点使用子矩阵)

       

    规则b是确保Component可以直接以Descriptor的输出作为其输入,而无需任何额外的重排或重组(因为按照设计,这样的重排或重组需要Descriptor来完成)。由于这个规则,原则上,一个来自于kDescriptor节点的cindex_id可以出现在多个step中,尽管这只有在使用非简单组件时才会发生。此外,为了确保满足规则c,我们偶尔可能需要在计算图中添加新的cindex_ids

       

    创建计算步骤序列(基础算法)

    这里,介绍一个用于创建step序列的基本算法(但不实际使用),只用于为稍后介绍的实际算法做铺垫。这个基本算法分为以下几步:

    • 首先保留对应于输入和输出节点的Cindex;将它们按节点索引分开;将这些step按照ComputationRequests相同顺序排序。
    • 接下来处理非输入或输出的中间Cindex
      • 将中间Cindex按神经网络层的依赖关系划分为多个"阶段"(phase,其中第一个phase包含仅依赖于输入的所有Cindex并且通常第n阶段包含仅依赖于小于第nphaseCindex
      • 从每个phase移除所有不对应于kComponent节点的Cindexes(这些Cindexes将在稍后处理)。
      • 使用struct Index的排序运算符对step进行排序
      • 按如下方式创建component-input组件输入)节点的step
        • 对于kComponent类型的每个step,使用ComputationGraphdependencies成员计算其所依赖的Cindex集合。
        • 使用struct Index的排序运算符对上述每个step的集合进行排序。(对于简单的组件,这确保它们与组件的输出的顺序相同)。
        • (模糊特征):此时非简单组件的输入进行重排(如果需要);具体请参阅Component::ReorderIndexes()
        • 将该组件输入节点的step防止在Component的对应step之前
      • 按如下方式创建dim-range节点的step
        • 从网络图中取对应于dim-range节点的所有Cindex,并得出其输入所属的step
        • 注意,对于每个现有step,拥有Cindexdim-range节点的集合从该step中获取输入。
        • 对于每个现有步骤s,对于拥有从该step获取输入的现有Cindex的每个dim-range节点,创建包含与step s相同的索引序列的step,并将新step放在step s之后。
    • 所有step排序:输入step中间step输出step

       

    上述算法最终将将它最终将Cindexes分成许多step

    例如,假设我们有一个RNN层,后接一个标准的前馈层。RNN层必须将Cindexs分成与时间索引一样多step,但是上述算法也会将完连接层的计算分成多个step,因为RNN输出Cindexes后,全连接层对应的Cindexes立即可计算。我们希望能够先完成RNN层的所有计算,然后再完成全连接层的计算。

       

    创建计算步骤序列(实际算法)

    class Compiler

    介绍class Compiler

    创建网络计算

    设定位置信息

    检查是否需要梯度

    计算StepInfo

    计算input_output_info

    分配矩阵空间

    前向计算

    反向计算

    释放矩阵空间

    添加调试信息

    快捷编译

    Kaldi版本5.1提供的功能,并默认启用。

    条件是:

    1. ComputationRequest中有两个以上不同的"n"索引;
    2. 对于每个索引,请求的"t""x"索引是相同且有序的

       

    快捷方式根据原始计算请求,生成mini_request,即只包含两个不同的"n"索引(01);

    编译mini_request,得到mini_computation

    根据mini_computation将所有原始计算请求的编译结果推导出来;

       

    快捷方式编译能显着缩短编译时间。

  • 相关阅读:
    Java String字符串补0或空格
    oracle查看当前用户权限
    plsql developer 导出导入存储过程和函数
    扩展jQuery easyui datagrid增加动态改变列编辑的类型
    jQueryEasyUI Messager基本使用
    combox源码解析
    Solr -- Solr Facet 2
    Solr -- Solr Facet 1
    nginx -- 安装配置Nginx
    DOS
  • 原文地址:https://www.cnblogs.com/JarvanWang/p/10145901.html
Copyright © 2011-2022 走看看