您好,歡迎來電子發(fā)燒友網(wǎng)! ,新用戶?[免費(fèi)注冊]

您的位置:電子發(fā)燒友網(wǎng)>源碼下載>數(shù)值算法/人工智能>

代碼分析CNTK和TensorFlow高層次的對比

大小:0.2 MB 人氣: 2017-10-12 需要積分:1
 本文從程序員的角度對CNTK和TensorFlow做高層次的對比。本文也不屬于性能分析,而是編程模型分析。文中會夾雜著大量的代碼。
  原標(biāo)題:當(dāng)TensorFlow遇見CNTK
  代碼分析CNTK和TensorFlow高層次的對比
  CNTK是微軟用于搭建深度神經(jīng)網(wǎng)絡(luò)的計(jì)算網(wǎng)絡(luò)工具包,此項(xiàng)目已在Github上開源。因?yàn)槲易罱鼘懥岁P(guān)于TensorFlow的文章,所以想比較一下這兩個系統(tǒng)的相似和差異之處。畢竟,CNTK也是許多圖像識別挑戰(zhàn)賽的衛(wèi)冕冠軍。為了內(nèi)容的完整性,我應(yīng)該也對比一下Theano、Torch和Caffe。后三者也是現(xiàn)在非常流行的框架。但是本文僅限于討論CNTK和TensorFlow,其余的框架將在今后討論。Kenneth Tran對這五個深度學(xué)習(xí)工具包做過一次高水平(以他個人觀點(diǎn))的分析。本文并不是一個CNTK或者TensorFlow的使用教程。我的目的在于從程序員的角度對它們做高層次的對比。本文也不屬于性能分析,而是編程模型分析。文中會夾雜著大量的代碼,如果你討厭閱讀代碼,請直接跳到結(jié)論部分。
  CNTK有一套極度優(yōu)化的運(yùn)行系統(tǒng)來訓(xùn)練和測試神經(jīng)網(wǎng)絡(luò),它是以抽象的計(jì)算圖形式構(gòu)建。如此看來,CNTK和TensorFlow長得非常相似。但是,它們有一些本質(zhì)上的區(qū)別。為了演示這些特性和區(qū)別,我會用到兩個標(biāo)準(zhǔn)示例,它們分別包括了兩個系統(tǒng)及調(diào)用各自系統(tǒng)完成的任務(wù)。第一個例子是用較淺的卷積神經(jīng)網(wǎng)絡(luò)來解決標(biāo)準(zhǔn)的MNIST手寫數(shù)字集的識別任務(wù)。我會針對它們兩種遞歸神經(jīng)網(wǎng)絡(luò)方法的差異性做一些點(diǎn)評總結(jié)。
  TensorFlow和CNTK都屬于腳本驅(qū)動型的。我的意思是說神經(jīng)網(wǎng)絡(luò)構(gòu)建的流程圖都是在一個腳本里完成,并調(diào)用一些智能的自動化步驟完成訓(xùn)練。TensorFlow的腳本是與Python語言捆綁的,Python操作符能夠用來控制計(jì)算圖的執(zhí)行過程。CNTK目前還沒有和Python或是C++綁定(盡管已經(jīng)承諾過),所以它目前訓(xùn)練和測試的流程控制還是需要精心編制設(shè)計(jì)的。等會我將展示,這個過程并不能算是一種限制。CNTK網(wǎng)絡(luò)需要用到兩個腳本:一個控制訓(xùn)練和測試參數(shù)的配置文件和一個用于構(gòu)建網(wǎng)絡(luò)的網(wǎng)絡(luò)定義語言(Network Definition Language, NDL)文件。
  我會首先描述神經(jīng)網(wǎng)絡(luò)的流程圖,因?yàn)檫@是與TensorFlow最相似之處。CNTK支持兩種方式來定義網(wǎng)絡(luò)。一種是使用“Simple Network Builder”,只需設(shè)置幾個參數(shù)就能生成一個簡單的標(biāo)準(zhǔn)神經(jīng)網(wǎng)絡(luò)。另一種是使用網(wǎng)絡(luò)定義語言(NDL)。此處例子(直接從Github下載的)使用的是NDL。下面就是Convolution.ndl文件的縮略版本。(為了節(jié)省頁面空間,我把多行文件合并到同一行,并用逗號分隔)
  CNTK網(wǎng)絡(luò)圖有一些特殊的節(jié)點(diǎn)。它們是描述輸入數(shù)據(jù)和訓(xùn)練標(biāo)簽的FeatureNodes和LabelNodes,用來評估訓(xùn)練結(jié)果的CriterionNodes和EvalNodes,和表示輸出的OutputNodes。當(dāng)我們在下文中遇到它們的時候我再具體解釋。在文件頂部還有一些用來加載數(shù)據(jù)(特征)和標(biāo)簽的宏定義。如下所示,我們將MNIST數(shù)據(jù)集的圖像作為特征讀入,經(jīng)過歸一化之后轉(zhuǎn)化為若干浮點(diǎn)數(shù)組。得到的數(shù)組“featScaled”將作為神經(jīng)網(wǎng)絡(luò)的輸入值。
  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小節(jié)定義了網(wǎng)絡(luò)的結(jié)構(gòu)。此神經(jīng)網(wǎng)絡(luò)包括了兩個卷積-最大池化層,接著是有一個128節(jié)點(diǎn)隱藏層的全連接標(biāo)準(zhǔn)網(wǎng)絡(luò)。
  在卷積層I 我們使用5x5的卷積核函數(shù),并且在參數(shù)空間定義了16個(cMap1)。操作符ConvReLULayer實(shí)際上是在宏文件中定義的另一個子網(wǎng)絡(luò)的縮寫。
  在計(jì)算時,我們想把卷積的參數(shù)用矩陣W和向量B來表示,那么如果輸入的是X,網(wǎng)絡(luò)的輸出將是f(op(W, X) + B)的形式。在這里操作符op就是卷積運(yùn)算,f是標(biāo)準(zhǔn)規(guī)則化函數(shù)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是模型的參數(shù),它們會被賦予一個初始值,并在訓(xùn)練的過程中不斷更新直到生成最終模型。這里,convW是一個16行25列的矩陣,B是長度為16的向量。Convolution是內(nèi)置的卷積函數(shù),默認(rèn)不使用補(bǔ)零的方法。也就是說對28x28的圖像做卷積運(yùn)算,實(shí)際上只是對24x24的中心區(qū)域操作,得到的結(jié)果是16個24x24的sudo-image。
  接著我們用2x2的區(qū)域應(yīng)用最大池化操作,最后得到的結(jié)果是16個12x12的矩陣。
  代碼分析CNTK和TensorFlow高層次的對比
  對于第二個卷積層,我們把卷積濾波器的個數(shù)由16個提升到32個。這一次我們有16通道的輸入數(shù)據(jù),因此W矩陣的尺寸為32行25×16 = 400列,向量B的長度為32。這次的卷積運(yùn)算針對12x12圖像幀的中心區(qū)域,所以得到的結(jié)果是32個8x8的矩陣。第二次池化操作的結(jié)果是32個4x4的幀,或者32x16=512。
  最后兩層,是由512個池化輸出結(jié)果經(jīng)過128個節(jié)點(diǎn)的隱藏層連接到10個輸出節(jié)點(diǎn),經(jīng)歷了兩次運(yùn)算操作。
  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) ]
  如你所見,這些運(yùn)算步驟都是標(biāo)準(zhǔn)的線性代數(shù)運(yùn)算形式W*x+b。
  圖定義的最后部分是交叉熵和誤差節(jié)點(diǎn),以及將它們綁定到特殊的節(jié)點(diǎn)名稱。
  我們接著要來定義訓(xùn)練的過程,但是先把它與用TensorFlow構(gòu)建相似的網(wǎng)絡(luò)模型做個比較。我們在之前的文章里討論過這部分內(nèi)容,這里再討論一次。你是否注意到我們使用了與CNTK相同的一組變量,只不過這里我們把它稱作變量,而在CNTK稱作參數(shù)。維度也略有不同。盡管卷積濾波器都是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”)
  網(wǎng)絡(luò)的構(gòu)建過程也大同小異。
  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)
  卷積運(yùn)算的唯一不同之處是這里定義了補(bǔ)零,因此第一次卷積運(yùn)算的輸出是28x28,經(jīng)過池化后,降為14x14。第二次卷積運(yùn)算和池化之后的結(jié)果降為了7x7,所以最后一層的輸入是7x7x64 = 3136維,有1024個隱藏節(jié)點(diǎn)(使用relu而不是sigmoid函數(shù))。(在訓(xùn)練時,最后一步用到了dropout函數(shù)將模型數(shù)值隨機(jī)地置零。如果keep_prob=1則忽略這步操作。)

非常好我支持^.^

(0) 0%

不好我反對

(0) 0%

      發(fā)表評論

      用戶評論
      評價(jià):好評中評差評

      發(fā)表評論,獲取積分! 請遵守相關(guān)規(guī)定!

      ?