CNTK从入门到深入研究(8) - CNTK工程结构(CNTK Core)

来源:互联网 发布:b站mac客户端下载视频 编辑:程序博客网 时间:2024/05/16 12:43

前言

CNTK对于使用者来说是用于神经网络的一套平台工具,但是对于程序员来说,更多的可拓展性以及定制性才能将CNTK使用在各种应用场合中。本文将针对CNTK的工程结构做一些研究,并根据官方给出的一些资料对其目前可以进行修改的地方进行初探。

工程

这里写图片描述

大家通过Visual Studio打开CNTK工程后,可发现解决方案视图中主要分为了几个部分,
1. CNTK Core
2. Extensibility
3. Reader Plugins
4. Documentation
5. Examples
6. Tests
7. Tools

通过名字已经知道每个部分的功能和存在的意义,本文只讲针对代码部分进行讲解,所以,只会针对前三部分进行简单的探究性说明。

CNTK Core

这里写图片描述

CNTK Core部分包含了8个项目,其中CNTK就是大家之前调用cntk.exe的入口工程。但是其本身为一个空的盒子(其实也实际包含了些内容)只是对其他项目的一个调用入口。下面将一一进行介绍。

CNTK

标题的名字看起来略微显得有点大,但是这部分只是cntk.exe部分,但是在这个子工程中,其的确扮演者很重要的角色(不仅仅是因为他是入口exe)。

这里写图片描述

通过工程文件可以看出,CNTK工程中,包含了3个主要的部分,
1. Common
2. Model Editing
3. BrainScript

Common

首先是Common这部分(笔者暂且将cntk.cpp也包括在内),首先第一部分是是包包括了配置文件的解析(Config.cpp)、DataReader、DataWriter等基础接口。

CNTK中基于数据块的配置文件就是有Config.cpp这部分进行解析的,当然,cntk.exe调用时传入的参数也由其一并处理。

DataReader和DataWritter这两部分主要是定义了CNTK中的Data Reader和Writer的接口,前面有提到过CNTK支持第三方通过插件的方式实现其自定义的Data Reader,这部分其实本质就是实现一个集成自IDataReader的类去实现这部分功能。

// Data Reader interface// implemented by DataReader and underlying classesclass DATAREADER_API IDataReader// Data Writer interface// implemented by some DataWritersclass DATAWRITER_API IDataWriter

其他的在Common部分的文件包括File、fileutil、TimerUtility之类的这些的都是一些一些便捷工具相关的类,这里特别的推荐大家留意一下ExceptionWithCallStack有关异常处理的一个类,十分简单,但是也十分的实用,CNTK是MIT License的项目,所以日后在一些自己的工程中,完全可以借鉴其实现方法。

/// <summary>This function collects the stack tracke and writes it through the provided write function/// <param name="write">Function for writing the text associated to a the callstack</param>/// <param name="newline">Function for writing and "end-of-line" / "newline"</param>/// </summary>static void CollectCallStack(size_t skipLevels, bool makeFunctionNamesStandOut, const function<void(string)>& write);// base class that we can catch, independent of the type parameterstruct /*interface*/ IExceptionWithCallStackBase{    virtual const char * CallStack() const = 0;    virtual ~IExceptionWithCallStackBase() throw() {}};// Exception wrapper to include native call stack stringtemplate <class E>class ExceptionWithCallStack : public E, public IExceptionWithCallStackBase{public:    ExceptionWithCallStack(const std::string& msg, const std::string& callstack) :        E(msg), m_callStack(callstack)    { }    virtual const char * CallStack() const override { return m_callStack.c_str(); }    static void      PrintCallStack(size_t skipLevels = 0, bool makeFunctionNamesStandOut = false);    static std::string GetCallStack(size_t skipLevels = 0, bool makeFunctionNamesStandOut = false); // generate call stack as a string, which should then be passed to the constructor of this  --TODO: Why not generate it directly in the constructor?protected:    std::string m_callStack;};

上面的类型就是ExceptionWithCallStack的声明,具体请详见ExceptionWithCallStack.cpp中CollectCallStack的实现,CollectCallStack目前看情况是支持Windows以及Linux下的堆栈信息收集,移植到其他项目上看起来很容易。可以使用下面的语句将异常和堆栈信息一同打印。大家可将其使用在自己的项目中,作为一个小技巧进行分享。

ExceptionWithCallStack<std::runtime_error>::PrintCallStack(0, false);

Model Editing

之前有提到过目前CNTK中对网络做构建定义的是通过NDL来进行定义,通过CNTK也提供一种叫做MEL的可以对已经构建完成的网络进行修改。这里的ModelEditing部分就是这个功能。

这里写图片描述

笔者尚未对其进行研究,只是暂时简单知道这部分做什么即可。

BrainScript

这里写图片描述

笔者之前在CNTK中已经发现BrainScript的痕迹,但是始终没有找到具体的某个Sample中使用过BrainScript。在工程中BrainScript部分留下了一个PPT和一个Note,PPT主要是汇报了BrianScript到底是什么他做了什么,什么没有完成,而Note则对BrainScript的使用做了一部分讲解。大家可以尝试使用下,但是笔者目前仍未实际用过(毕竟目前所提供的NDL之类的已经满足需求),还不能确切的知道如何使用。

ActionsLib

ActionsLib这个库其实是有cntk.exe工程直接调用,cntk.exe工程中common部分对传入的参数以及配置文件进行解析,获取相关参数后,根据数据块中的任务去调用ActionsLib的实际函数去执行相关的操作。

这里写图片描述

通过工程文件可以看出,CNTK工程中,包含了4个主要的部分(其实是3个),其中的Common在哪个工程里都同cntk.exe中一样是共享过来的文件,主要提供了对File等的一些工具,笔者在后面的内容中将不会重复说明。

  1. Actions
  2. Model Building - NDL
  3. Model Building - Standard Models
  4. Common

Actions

之前在CNTK的Config文件中,有使用过command = Simple_Demo_Train:Simple_Demo_Test:Simple_Demo_Output这句语句, 并且后面所接的内容是具体需要制定的任务块,而任务块中使用action = "train" 来标记该任务块在做什么。其中的action参数的执行,就是在Actions中进行实现。

Model Building - NDL

CNTK支持多种方式去定义网络模型,其中高级别的使用方法是通过NDL方法去定义完全非标准的网络结构供一些特殊的应用场景去使用。其中NDL脚本的解析与构建就是在这里实现的。
这里面包含两部分,第一部分是NDL语言本身的解析,另外一部分是通过NDL的Network Builder。

Model Building - Standard Models

可能Standard Models很容易使人感觉迷惑,但是这部分所对应于我们所知的CNTK的功能十分的贴近,目前网络模型除了一定要使用NDL进行定义的以外,普通场景都可以使用最基本或者通用的网络结构进行实现,这种情况下就完全没必要使用复杂的NDL来定义网络。而是使用前面一些文章中介绍的Simple Network Builder来通过参数的方式定义网络。

ActionsLib中的这部分中只有一个类,即SimpleNetworkBuilder它完全对应用配置文件中SimpleNetworkBuilder类型的网络模型构建。其实现了通过参数的方式配置网络(包括RNN等)。

SequenceTrainingLib

上一小节中提到过cntk中出现的action的实际实现以及执行都在ActionsLib中,其实也不一定是这样,因为存在2个比较大的部分,“training”以及“eval”这两部分的实现相对较为复杂,所以被单独的拉出来作为一个Lib进行实现。

这里写图片描述

这里的SequenceTrainingLib就是对Training部分进行实现的部分。这部分没有分成小结而是一同的进行了实现,主要涉及几方面内容,第一点是如何如何实现的神经网络算法中的向前传导,另外一点是如何实现的并行计算。

EvalDll

这里写图片描述

eval是另外一部分action不在ActionsLib中实现的内容。EvalDll中包含了eval的实现以及eval的reader和writer(笔者目前仍未知道哪里使用了他们),他被独立出来还有另外的原因是CNTK的拓展行,该dll在经由C++\CLI封装后,可由.net相关的程序直接调用。

针对于拓展性,EvalDLL中定义了一个接口,可在引用或封装其的工程中使用:

// IEvaluateModel - interface used by decoders and other components that need just evaluator functionality in DLL formtemplate <class ElemType>class IEvaluateModel // Evaluate Model Interface{public:    virtual void Init(const std::string& config) = 0;    virtual void Destroy() = 0;    virtual void CreateNetwork(const std::string& networkDescription) = 0;    virtual void GetNodeDimensions(std::map<std::wstring, size_t>& dimensions, NodeGroup nodeGroup) = 0;    virtual void StartEvaluateMinibatchLoop(const std::wstring& outputNodeName) = 0;    virtual void Evaluate(std::map<std::wstring, std::vector<ElemType>*>& inputs, std::map<std::wstring, std::vector<ElemType>*>& outputs) = 0;    virtual void Evaluate(std::map<std::wstring, std::vector<ElemType>*>& outputs) = 0;    virtual void ResetState() = 0;};// GetEval - get a evaluator type from the DLL// since we have 2 evaluator types based on template parameters, exposes 2 exports// could be done directly with the templated name, but that requires mangled C++ namestemplate <class ElemType>void EVAL_API GetEval(IEvaluateModel<ElemType>** peval);extern "C" EVAL_API void GetEvalF(IEvaluateModel<float>** peval);extern "C" EVAL_API void GetEvalD(IEvaluateModel<double>** peval);

通过 GetEval方法即可获取到一个IEvaluateModel的指针,然后调用其方法即可。

Math\MathCUDA

这里写图片描述

神经网络毕竟是数学相关的工程,所以一定会实现一些数学方法,Math以及MathCUDA则是这部分的实现。里面包含了常用的矩阵计算的实现,并且根据编译选项的不同,分为CPU与GPU两种版本,MathCUDA为GPU的版本,其中使用了CUDA的很多东西对计算进行加速,并且用了cuDNN针对深度神经网络的训练过程有直接利用Nvidia所提供的专用方法。

SGDLib

这里写图片描述

CNTK中目前所支持的神经网络训练方法为SGD(随机梯度下降)。SGDLib则为其实现,微软也提供了1bitSGD的库去实现这部分功能,只是License不同。
SDG本身算法实现起来不难,麻烦的是如何将其可以应用到多线程上,多设备上,如何解读如何分配是个问题。SGDLib在CNTK中主要实现具体的网络的训练。

ComputationNetworkLib

这里写图片描述

ComputationNetworkLib其实就是网络模型的数据结构的实现,其中定义了network以及其中的node。整个CNTK都是围绕ComputationNetwork展开的,CNTK通过训练ComputationNetwork并且到最后的使用ComputationNetwork,都是通过这个数据结构进行的。

总结

本篇文章中针对CNTK的工程的结构进行的解读,并针对计划所研究的三个部分中的第一部分CNTK Core的每个子工程进行了简单的描述,讲述了其在CNTK中所承载功能的位置,希望这篇文章可以对读者在了解CNTK整个工程的实现部分有所帮助。

下篇文章中将会针对工程中Extensibility以及Reader Plugins部分在进行讲解,已完成整体的CNTK工程结构的探究。

2 0
原创粉丝点击