分享

CNTK从入门到深入研究(6)

 imelee 2016-12-29

前言

本文将针对与CNTK中所提供用于构建网络结构的NDL语言进行简要的说明以及尝试的去讲解如何使用,可能并非十分的专业,但是希望能给共同研究CNTK的人带来对其入门的便利。

近期笔者在和同样研究CNTK的朋友交流的过程中发现,大家对目前CNTK的Guide都有一样的感受,认为CNTK运行提供好的样例以及基本的功能,感觉使用起来很方便,但是想自己扩展或者实现一些自己的例子,可能就感觉十分的麻烦以及不知道如何下手。

很多CNTK的样例的配置文件中发现的东西都找不到详细的官方说明,甚至在一些说明文档中说明的功能与实际使用中对应不上的情况。所以在这种情况下笔者个人建议应该去看看源代码来推测一下到底是怎样的,或者是去Github中问题,或者是找共同研究的小伙伴们讨论一下。也许这样,能够更快的获得答案。

NDL简介

CNTK中可以通过SimpleNetworkBuilder来构建通用的网络结构(CNTK预定义好的网络),但是具体定义出来的网络是什么情况,输入输出接点以及损失函数到底是什么我们可能很难去了解。虽然通过SimpleNetworkBuilder可以实现功能,但是如果详细的去究其根本,SimpleNetworkBuilder毕竟是缺少了对网络定义的控制,里面节点的名称,如果不查阅源码真的很难知道。

还好CNTK同时也提供了NDL语言用于定义网络结构(NDL就是NetworkDefineLangurage),使用NDL的好处在于,这个网络的结构情况完全可由自己掌控,这点对于拓展性来说十分重要。

NDL基础

本小结将对NDL脚本在编写上的一些基础进行讲解,下面的一段内容即为一段NDL示例的样子,

SDim=784  
HDim=256  
LDim=10  

B0=Parameter(HDim)  
W0=Parameter(HDim, SDim)  

features=Input(SDim)  
labels=Input(LDim)  

Times1=Times(W0, features)    
Plus1=Plus(Times1, B0)    
RL1=RectifiedLinear(Plus1)  
B1=Parameter(LDim, 1)  
W1=Parameter(LDim, HDim)  
Times2=Times(W1, RL1)  
Plus2=Plus(Times2, B1) 

CrossEntropy=CrossEntropyWithSoftmax(labels, Plus2)  
ErrPredict=ErrorPrediction(labels, Plus2)  

FeatureNodes=(features)  
LabelNodes=(labels)  
CriteriaNodes=(CrossEntropy)  
EvalNodes=(ErrPredict)  
OutputNodes=(Plus2)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

定义变量

现在任何一种主流的逻辑语言中都会有变量的概念,在NDL中的变量,本质上就是一个标示计算时用的符号。类似于脚本语言一样,NDL的符号是可以直接使用=赋值的,而之前不需要指定类型等特殊的语句。例如如下的脚本,就是定义了3个变量,分别指代3个维度分别是多少(这里只是一个符号而已,并没有涉及到除了变量名本身的意义外的任何东西)。

SDim=784  
HDim=256  
LDim=10  
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

定义向量或矩阵

这一部分在官方的文档中使用的是参数(Parameter)一词,本质上也是定义一个变量,只是这种变量是多维度的,可以是1维的向量,或者是2维的矩阵。
可以通过Parameter函数来定义向量或矩阵。Parameter的原型如下:

[variable] Parameter(DimX [, DimY] [,init=initOp)

Parameter函数中可以输入3个参数,分别为,维度X,维度Y以及一个初始化的方法。
第一个参数维度X是必须的,因为至少我们定义的也是一个1维的向量。

Vec0 = Parameter(10)

第二个参数维度Y是可选的,当我们定义的是一个1为向量时,我们就忽略这个参数,当我们希望定义一个2维的矩阵时,我们通过这个参数来指定矩阵的第二个维度数。

Mat0 = Parameter(256, 10)

第三个参数初始化是可选的,我们可以设置init为zero或者为uniform。

定义输入

NDL中使用Input函数来定义输入,笔者在使用过程中,其实针对输入这个函数的必要性一直有一定怀疑性质,因为笔者认为,Input就是1维的Parameter而已,为什么非要另外使用一个单独的Input函数来定义呢?
既然存在,也许会有一定道理,下面是Input函数的原型,

[input] Input(Dim [,tag = “tagName”])

Input函数的目的就是定义一个1维输入向量,其输入参数有两个,
第一个参数Dim用于指定输入的维度
第二个参数tag用于指定该输入的tag的名称

定义函数(宏函数)

上述所说的内容都是如何定义变量,变量可以是一个参数值,也可以是1维或者2维的矩阵。在定义输入或者向量或者矩阵时,我们用到了宏函数。NDL中提供了一一套机制可以使用户定义自己的宏函数,这样就能大幅度的降低用户在定义网络时的重复工作。

FF(X1, W1, B1)
{
    T=Times(W1,X1);
    FF=Plus(T, B1);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

宏函数就是先定义一个宏函数的名称,然后再后面的括号中指定参数即可,然后再之后的花括号中实现其内部的计算。

在定义完成宏函数后,即可在后面使用它。使用方式就是调用它并将其返回值赋值给一个变量。
这个变量可以用.符号来访问宏函数中的内部变量。

CE = SMBFF(L1, LDim, HDim, labels)
Err=ErrorPrediction(labels, CE.F)
  • 1
  • 2
  • 1
  • 2

NDL对外接口

NDL作为定义网络的语言,定义出来的网络需要被CNTK使用,所以NDL与CNTK需要制定一种规约,定义特殊的节点(输入、输出、评估等)、以及基础的计算等。

NDL特殊变量

CNTK为NDL约定了一些变量名称的特殊意义,这些变量用于指明哪些是输入节点等,

FeatureNodes = (features)
LabelNodes = (labels)
CriterionNodes = (CrossEntropy)
EvalNodes = (ErrPredict)
OutputNodes = (Plus2)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

在前文中有说明过,在定义输入或者计算的过程中,可以针对返回值设定tag,特殊的tag名称可以被CNTK识别出来并当做对应的特殊节点处理。下表为一个特殊的tag及其功能的对照表。

Tag name Meaning
feature feature input
label label input
criterion criterion node, top level node
eval evaluation node
Output output node

另外,CNTK中还有些变量名称用于指定一些有关算法的特殊功能,例如评估等,

CrossEntropy=CrossEntropyWithSoftmax(labels, Plus2)  
ErrPredict=ErrorPrediction(labels, Plus2)  
  • 1
  • 2
  • 1
  • 2

NDL基础函数

大家发现笔者在讲述NDL语言的过程中,并没有说明NDL中如何计算基本的“加减乘数”等操作,是因为NDL作为网络定义语言,同时也承担着网络可以求梯度等操作的支持。
所以为了实现支持各种其他功能的需要,NDL在计算方面使用的比较麻烦,需要使用其内定的函数实现。

下面列出的就是NDL内部支持的基本函数,函数名已经代表了其意义,就不在此做过多说明了。

Input, InputValue
ImageInput
Parameter, LearnableParameter
Sum
Scale
Times
Plus
Minus
Negate
RectifiedLinear
Sigmoid
Tanh
Log
Softmax
SquareError
CrossEntropyWithSoftmax, CEWithSM
MatrixL1Reg, L1Reg
MatrixL2Reg, L2Reg
PerDimMeanVarNormalization, PerDimMVNorm
ErrorPrediction
Dropout
Mean
Convolution
Pooling
Delay
BatchNormalization
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

总结

本文中对CNTK中所提供的NDL进行了简单的说明,讲解了NDL使用上的一些知识。现在大家应该可以理解如下这段NDL脚本所定义网络的意义了。

SDim=784  
HDim=256  
LDim=10  

B0=Parameter(HDim)  
W0=Parameter(HDim, SDim)  

features=Input(SDim)  
labels=Input(LDim)  

Times1=Times(W0, features)    
Plus1=Plus(Times1, B0)    
RL1=RectifiedLinear(Plus1)  
B1=Parameter(LDim, 1)  
W1=Parameter(LDim, HDim)  
Times2=Times(W1, RL1)  
Plus2=Plus(Times2, B1) 

CrossEntropy=CrossEntropyWithSoftmax(labels, Plus2)  
ErrPredict=ErrorPrediction(labels, Plus2)  

FeatureNodes=(features)  
LabelNodes=(labels)  
CriteriaNodes=(CrossEntropy)  
EvalNodes=(ErrPredict)  
OutputNodes=(Plus2)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

上述脚本的内容就是为了识别手写数字所准备的一个标准的3层网络结构。

虽然CNTK中提供的SimpleNetworkBuilder很好用也很方便,但是其网络的细节都被隐藏了起来,对于深层的理解其中的运作原理可能会造成困扰,所以笔者建议遇到这种情况,尝试自己去实现一个NDL脚本将CNTK配置中的SimpleNetworkBuilder给替换掉,这样就能更好的理解CNTK如何实现的深度网络了。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多