×

代码分析CNTK和TensorFlow高层次的对比

消耗积分:1 | 格式:rar | 大小:0.2 MB | 2017-10-12

分享资料个

 本文从程序员的角度对CNTK和TensorFlow做高层次的对比。本文也不属于性能分析,而是编程模型分析。文中会夹杂着大量的代码。
  原标题:当TensorFlow遇见CNTK
  代码分析CNTK和TensorFlow高层次的对比
  CNTK是微软用于搭建深度神经网络的计算网络工具包,此项目已在Github上开源。因为我最近写了关于TensorFlow的文章,所以想比较一下这两个系统的相似和差异之处。毕竟,CNTK也是许多图像识别挑战赛的卫冕冠军。为了内容的完整性,我应该也对比一下Theano、Torch和Caffe。后三者也是现在非常流行的框架。但是本文仅限于讨论CNTK和TensorFlow,其余的框架将在今后讨论。Kenneth Tran对这五个深度学习工具包做过一次高水平(以他个人观点)的分析。本文并不是一个CNTK或者TensorFlow的使用教程。我的目的在于从程序员的角度对它们做高层次的对比。本文也不属于性能分析,而是编程模型分析。文中会夹杂着大量的代码,如果你讨厌阅读代码,请直接跳到结论部分。
  CNTK有一套极度优化的运行系统来训练和测试神经网络,它是以抽象的计算图形式构建。如此看来,CNTK和TensorFlow长得非常相似。但是,它们有一些本质上的区别。为了演示这些特性和区别,我会用到两个标准示例,它们分别包括了两个系统及调用各自系统完成的任务。第一个例子是用较浅的卷积神经网络来解决标准的MNIST手写数字集的识别任务。我会针对它们两种递归神经网络方法的差异性做一些点评总结。
  TensorFlow和CNTK都属于脚本驱动型的。我的意思是说神经网络构建的流程图都是在一个脚本里完成,并调用一些智能的自动化步骤完成训练。TensorFlow的脚本是与Python语言捆绑的,Python操作符能够用来控制计算图的执行过程。CNTK目前还没有和Python或是C++绑定(尽管已经承诺过),所以它目前训练和测试的流程控制还是需要精心编制设计的。等会我将展示,这个过程并不能算是一种限制。CNTK网络需要用到两个脚本:一个控制训练和测试参数的配置文件和一个用于构建网络的网络定义语言(Network Definition Language, NDL)文件。
  我会首先描述神经网络的流程图,因为这是与TensorFlow最相似之处。CNTK支持两种方式来定义网络。一种是使用“Simple Network Builder”,只需设置几个参数就能生成一个简单的标准神经网络。另一种是使用网络定义语言(NDL)。此处例子(直接从Github下载的)使用的是NDL。下面就是Convolution.ndl文件的缩略版本。(为了节省页面空间,我把多行文件合并到同一行,并用逗号分隔)
  CNTK网络图有一些特殊的节点。它们是描述输入数据和训练标签的FeatureNodes和LabelNodes,用来评估训练结果的CriterionNodes和EvalNodes,和表示输出的OutputNodes。当我们在下文中遇到它们的时候我再具体解释。在文件顶部还有一些用来加载数据(特征)和标签的宏定义。如下所示,我们将MNIST数据集的图像作为特征读入,经过归一化之后转化为若干浮点数组。得到的数组“featScaled”将作为神经网络的输入值。
  load= ndlMnistMacros # the actual NDL that defines the networkrun= DNN ndlMnistMacros= [ imageW = 28, imageH = 28 labelDim = 10 features = ImageInput(imageW, imageH, 1) featScale = Const(0.00390625) featScaled = Scale(featScale, features) labels = Input(labelDim) ] DNN=[ # conv1kW1 = 5, kH1 = 5 cMap1 = 16 hStride1 = 1, vStride1 = 1 conv1_act = ConvReLULayer(featScaled,cMap1,25,kW1,kH1,hStride1,vStride1,10, 1) # pool1pool1W = 2, pool1H = 2 pool1hStride = 2, pool1vStride = 2 pool1 = MaxPooling(conv1_act, pool1W, pool1H, pool1hStride, pool1vStride) # conv2kW2 = 5, kH2 = 5 cMap2 = 32 hStride2 = 1, vStride2 = 1 conv2_act = ConvReLULayer(pool1,cMap2,400,kW2, kH2, hStride2, vStride2,10, 1) # pool2pool2W = 2, pool2H = 2 pool2hStride = 2, pool2vStride = 2 pool2 = MaxPooling(conv2_act, pool2W, pool2H, pool2hStride, pool2vStride) h1Dim = 128 h1 = DNNSigmoidLayer(512, h1Dim, pool2, 1) ol = DNNLayer(h1Dim, labelDim, h1, 1) ce = CrossEntropyWithSoftmax(labels, ol) err = ErrorPrediction(labels, ol) # Special NodesFeatureNodes = (features) LabelNodes = (labels) CriterionNodes = (ce) EvalNodes = (err) OutputNodes = (ol) ]
  DNN小节定义了网络的结构。此神经网络包括了两个卷积-最大池化层,接着是有一个128节点隐藏层的全连接标准网络。
  在卷积层I 我们使用5x5的卷积核函数,并且在参数空间定义了16个(cMap1)。操作符ConvReLULayer实际上是在宏文件中定义的另一个子网络的缩写。
  在计算时,我们想把卷积的参数用矩阵W和向量B来表示,那么如果输入的是X,网络的输出将是f(op(W, X) + B)的形式。在这里操作符op就是卷积运算,f是标准规则化函数relu(x)=max(x,0)。
  ConvReLULayer的NDL代码如下图所示:
  ConvReLULayer(inp, outMap, inWCount, kW, kH, hStride, vStride, wScale, bValue) = [ convW = Parameter(outMap, inWCount, init=“uniform”, initValueScale=wScale) convB = Parameter(outMap, 1, init=“fixedValue”, value=bValue) conv = Convolution(convW, inp, kW, kH, outMap, hStride,vStride, zeroPadding=false) convPlusB = Plus(conv, convB); act = RectifiedLinear(convPlusB); ]
  矩阵W和向量B是模型的参数,它们会被赋予一个初始值,并在训练的过程中不断更新直到生成最终模型。这里,convW是一个16行25列的矩阵,B是长度为16的向量。Convolution是内置的卷积函数,默认不使用补零的方法。也就是说对28x28的图像做卷积运算,实际上只是对24x24的中心区域操作,得到的结果是16个24x24的sudo-image。
  接着我们用2x2的区域应用最大池化操作,最后得到的结果是16个12x12的矩阵。
  代码分析CNTK和TensorFlow高层次的对比
  对于第二个卷积层,我们把卷积滤波器的个数由16个提升到32个。这一次我们有16通道的输入数据,因此W矩阵的尺寸为32行25×16 = 400列,向量B的长度为32。这次的卷积运算针对12x12图像帧的中心区域,所以得到的结果是32个8x8的矩阵。第二次池化操作的结果是32个4x4的帧,或者32x16=512。
  最后两层,是由512个池化输出结果经过128个节点的隐藏层连接到10个输出节点,经历了两次运算操作。
  DNNSigmoidLayer(inDim, outDim, x, parmScale) = [ W = Parameter(outDim, inDim, init=“uniform”, initValueScale=parmScale) b = Parameter(outDim, 1, init=“uniform”, initValueScale=parmScale) t = Times(W, x) z = Plus(t, b) y = Sigmoid(z) ] DNNLayer(inDim, outDim, x, parmScale) = [ W = Parameter(outDim, inDim, init=“uniform”, initValueScale=parmScale) b = Parameter(outDim, 1, init=“uniform”, initValueScale=parmScale) t = Times(W, x) z = Plus(t, b) ]
  如你所见,这些运算步骤都是标准的线性代数运算形式W*x+b。
  图定义的最后部分是交叉熵和误差节点,以及将它们绑定到特殊的节点名称。
  我们接着要来定义训练的过程,但是先把它与用TensorFlow构建相似的网络模型做个比较。我们在之前的文章里讨论过这部分内容,这里再讨论一次。你是否注意到我们使用了与CNTK相同的一组变量,只不过这里我们把它称作变量,而在CNTK称作参数。维度也略有不同。尽管卷积滤波器都是5x5,在CNTK我们前后两级分别使用了16个和32个滤波器,但是在TensorFlow的例子里我们用的是32个和64个。
  defweight_variable(shape, names):initial = tf.truncated_normal(shape, stddev=0.1) returntf.Variable(initial, name=names) defbias_variable(shape, names):initial = tf.constant(0.1, shape=shape) returntf.Variable(initial, name=names) x = tf.placeholder(tf.float32, [None, 784], name=“x”) sess = tf.InteractiveSession() W_conv1 = weight_variable([5, 5, 1, 32], “wconv”) b_conv1 = bias_variable([32], “bconv”) W_conv2 = weight_variable([5, 5, 32, 64], “wconv2”) b_conv2 = bias_variable([64], “bconv2”) W_fc1 = weight_variable([7* 7* 64, 1024], “wfc1”) b_fc1 = bias_variable([1024], “bfcl”) W_fc2 = weight_variable([1024, 10], “wfc2”) b_fc2 = bias_variable([10], “bfc2”)
  网络的构建过程也大同小异。
  defconv2d(x, W):returntf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding=‘SAME’) defmax_pool_2x2(x):returntf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding=‘SAME’) #first convolutional layerx_image = tf.reshape(x, [-1,28,28,1]) h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1) h_pool1 = max_pool_2x2(h_conv1) #second convolutional layerh_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2) h_pool2 = max_pool_2x2(h_conv2) #final layerh_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64]) h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1) h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob) y_conv=tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)
  卷积运算的唯一不同之处是这里定义了补零,因此第一次卷积运算的输出是28x28,经过池化后,降为14x14。第二次卷积运算和池化之后的结果降为了7x7,所以最后一层的输入是7x7x64 = 3136维,有1024个隐藏节点(使用relu而不是sigmoid函数)。(在训练时,最后一步用到了dropout函数将模型数值随机地置零。如果keep_prob=1则忽略这步操作。)

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

评论(0)
发评论

下载排行榜

全部0条评论

快来发表一下你的评论吧 !

'+ '

'+ '

'+ ''+ '
'+ ''+ ''+ '
'+ ''+ '' ); $.get('/article/vipdownload/aid/'+webid,function(data){ if(data.code ==5){ $(pop_this).attr('href',"/login/index.html"); return false } if(data.code == 2){ //跳转到VIP升级页面 window.location.href="//m.obk20.com/vip/index?aid=" + webid return false } //是会员 if (data.code > 0) { $('body').append(htmlSetNormalDownload); var getWidth=$("#poplayer").width(); $("#poplayer").css("margin-left","-"+getWidth/2+"px"); $('#tips').html(data.msg) $('.download_confirm').click(function(){ $('#dialog').remove(); }) } else { var down_url = $('#vipdownload').attr('data-url'); isBindAnalysisForm(pop_this, down_url, 1) } }); }); //是否开通VIP $.get('/article/vipdownload/aid/'+webid,function(data){ if(data.code == 2 || data.code ==5){ //跳转到VIP升级页面 $('#vipdownload>span').text("开通VIP 免费下载") return false }else{ // 待续费 if(data.code == 3) { vipExpiredInfo.ifVipExpired = true vipExpiredInfo.vipExpiredDate = data.data.endoftime } $('#vipdownload .icon-vip-tips').remove() $('#vipdownload>span').text("VIP免积分下载") } }); }).on("click",".download_cancel",function(){ $('#dialog').remove(); }) var setWeixinShare={};//定义默认的微信分享信息,页面如果要自定义分享,直接更改此变量即可 if(window.navigator.userAgent.toLowerCase().match(/MicroMessenger/i) == 'micromessenger'){ var d={ title:'代码分析CNTK和TensorFlow高层次的对比',//标题 desc:$('[name=description]').attr("content"), //描述 imgUrl:'https://'+location.host+'/static/images/ele-logo.png',// 分享图标,默认是logo link:'',//链接 type:'',// 分享类型,music、video或link,不填默认为link dataUrl:'',//如果type是music或video,则要提供数据链接,默认为空 success:'', // 用户确认分享后执行的回调函数 cancel:''// 用户取消分享后执行的回调函数 } setWeixinShare=$.extend(d,setWeixinShare); $.ajax({ url:"//www.obk20.com/app/wechat/index.php?s=Home/ShareConfig/index", data:"share_url="+encodeURIComponent(location.href)+"&format=jsonp&domain=m", type:'get', dataType:'jsonp', success:function(res){ if(res.status!="successed"){ return false; } $.getScript('https://res.wx.qq.com/open/js/jweixin-1.0.0.js',function(result,status){ if(status!="success"){ return false; } var getWxCfg=res.data; wx.config({ //debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。 appId:getWxCfg.appId, // 必填,公众号的唯一标识 timestamp:getWxCfg.timestamp, // 必填,生成签名的时间戳 nonceStr:getWxCfg.nonceStr, // 必填,生成签名的随机串 signature:getWxCfg.signature,// 必填,签名,见附录1 jsApiList:['onMenuShareTimeline','onMenuShareAppMessage','onMenuShareQQ','onMenuShareWeibo','onMenuShareQZone'] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2 }); wx.ready(function(){ //获取“分享到朋友圈”按钮点击状态及自定义分享内容接口 wx.onMenuShareTimeline({ title: setWeixinShare.title, // 分享标题 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享给朋友”按钮点击状态及自定义分享内容接口 wx.onMenuShareAppMessage({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 type: setWeixinShare.type, // 分享类型,music、video或link,不填默认为link dataUrl: setWeixinShare.dataUrl, // 如果type是music或video,则要提供数据链接,默认为空 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到QQ”按钮点击状态及自定义分享内容接口 wx.onMenuShareQQ({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到腾讯微博”按钮点击状态及自定义分享内容接口 wx.onMenuShareWeibo({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到QQ空间”按钮点击状态及自定义分享内容接口 wx.onMenuShareQZone({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); }); }); } }); } function openX_ad(posterid, htmlid, width, height) { if ($(htmlid).length > 0) { var randomnumber = Math.random(); var now_url = encodeURIComponent(window.location.href); var ga = document.createElement('iframe'); ga.src = 'https://www1.elecfans.com/www/delivery/myafr.php?target=_blank&cb=' + randomnumber + '&zoneid=' + posterid+'&prefer='+now_url; ga.width = width; ga.height = height; ga.frameBorder = 0; ga.scrolling = 'no'; var s = $(htmlid).append(ga); } } openX_ad(828, '#berry-300', 300, 250);