日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > windows >内容正文

windows

代码解析深度学习系统编程模型:TensorFlow vs. CNTK

發(fā)布時(shí)間:2025/7/25 windows 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 代码解析深度学习系统编程模型:TensorFlow vs. CNTK 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

from: http://geek.csdn.net/news/detail/62429

CNTK是微軟用于搭建深度神經(jīng)網(wǎng)絡(luò)的計(jì)算網(wǎng)絡(luò)工具包,此項(xiàng)目已在Github上開源。因?yàn)槲易罱鼘懥岁P(guān)于TensorFlow的文章,所以想比較一下這兩個(gè)系統(tǒng)的相似和差異之處。畢竟,CNTK也是許多圖像識(shí)別挑戰(zhàn)賽的衛(wèi)冕冠軍。為了內(nèi)容的完整性,我應(yīng)該也對(duì)比一下Theano、Torch和Caffe。后三者也是現(xiàn)在非常流行的框架。但是本文僅限于討論CNTK和TensorFlow,其余的框架將在今后討論。Kenneth Tran對(duì)這五個(gè)深度學(xué)習(xí)工具包做過一次高水平(以他個(gè)人觀點(diǎn))的分析。本文并不是一個(gè)CNTK或者TensorFlow的使用教程。我的目的在于從程序員的角度對(duì)它們做高層次的對(duì)比。本文也不屬于性能分析,而是編程模型分析。文中會(huì)夾雜著大量的代碼,如果你討厭閱讀代碼,請(qǐng)直接跳到結(jié)論部分。

CNTK有一套極度優(yōu)化的運(yùn)行系統(tǒng)來訓(xùn)練和測試神經(jīng)網(wǎng)絡(luò),它是以抽象的計(jì)算圖形式構(gòu)建。如此看來,CNTK和TensorFlow長得非常相似。但是,它們有一些本質(zhì)上的區(qū)別。為了演示這些特性和區(qū)別,我會(huì)用到兩個(gè)標(biāo)準(zhǔn)示例,它們分別包括了兩個(gè)系統(tǒng)及調(diào)用各自系統(tǒng)完成的任務(wù)。第一個(gè)例子是用較淺的卷積神經(jīng)網(wǎng)絡(luò)來解決標(biāo)準(zhǔn)的MNIST手寫數(shù)字集的識(shí)別任務(wù)。我會(huì)針對(duì)它們兩種遞歸神經(jīng)網(wǎng)絡(luò)方法的差異性做一些點(diǎn)評(píng)總結(jié)。

TensorFlow和CNTK都屬于腳本驅(qū)動(dòng)型的。我的意思是說神經(jīng)網(wǎng)絡(luò)構(gòu)建的流程圖都是在一個(gè)腳本里完成,并調(diào)用一些智能的自動(dòng)化步驟完成訓(xùn)練。TensorFlow的腳本是與Python語言捆綁的,Python操作符能夠用來控制計(jì)算圖的執(zhí)行過程。CNTK目前還沒有和Python或是C++綁定(盡管已經(jīng)承諾過),所以它目前訓(xùn)練和測試的流程控制還是需要精心編制設(shè)計(jì)的。等會(huì)我將展示,這個(gè)過程并不能算是一種限制。CNTK網(wǎng)絡(luò)需要用到兩個(gè)腳本:一個(gè)控制訓(xùn)練和測試參數(shù)的配置文件和一個(gè)用于構(gòu)建網(wǎng)絡(luò)的網(wǎng)絡(luò)定義語言(Network Definition Language, NDL)文件。

我會(huì)首先描述神經(jīng)網(wǎng)絡(luò)的流程圖,因?yàn)檫@是與TensorFlow最相似之處。CNTK支持兩種方式來定義網(wǎng)絡(luò)。一種是使用“Simple Network Builder”,只需設(shè)置幾個(gè)參數(shù)就能生成一個(gè)簡單的標(biāo)準(zhǔn)神經(jīng)網(wǎng)絡(luò)。另一種是使用網(wǎng)絡(luò)定義語言(NDL)。此處例子(直接從Github下載的)使用的是NDL。下面就是Convolution.ndl文件的縮略版本。(為了節(jié)省頁面空間,我把多行文件合并到同一行,并用逗號(hào)分隔)

CNTK網(wǎng)絡(luò)圖有一些特殊的節(jié)點(diǎn)。它們是描述輸入數(shù)據(jù)和訓(xùn)練標(biāo)簽的FeatureNodes和LabelNodes,用來評(píng)估訓(xùn)練結(jié)果的CriterionNodes和EvalNodes,和表示輸出的OutputNodes。當(dāng)我們?cè)谙挛闹杏龅剿鼈兊臅r(shí)候我再具體解釋。在文件頂部還有一些用來加載數(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 network run = DNNndlMnistMacros = [imageW = 28, imageH = 28labelDim = 10features = ImageInput(imageW, imageH, 1)featScale = Const(0.00390625)featScaled = Scale(featScale, features)labels = Input(labelDim) ]DNN=[# conv1kW1 = 5, kH1 = 5cMap1 = 16hStride1 = 1, vStride1 = 1conv1_act = ConvReLULayer(featScaled,cMap1,25,kW1,kH1,hStride1,vStride1,10, 1)# pool1pool1W = 2, pool1H = 2pool1hStride = 2, pool1vStride = 2pool1 = MaxPooling(conv1_act, pool1W, pool1H, pool1hStride, pool1vStride)# conv2kW2 = 5, kH2 = 5cMap2 = 32hStride2 = 1, vStride2 = 1conv2_act = ConvReLULayer(pool1,cMap2,400,kW2, kH2, hStride2, vStride2,10, 1)# pool2pool2W = 2, pool2H = 2pool2hStride = 2, pool2vStride = 2pool2 = MaxPooling(conv2_act, pool2W, pool2H, pool2hStride, pool2vStride)h1Dim = 128h1 = 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ò)包括了兩個(gè)卷積-最大池化層,接著是有一個(gè)128節(jié)點(diǎn)隱藏層的全連接標(biāo)準(zhǔn)網(wǎng)絡(luò)。

在卷積層I 我們使用5x5的卷積核函數(shù),并且在參數(shù)空間定義了16個(gè)(cMap1)。操作符ConvReLULayer實(shí)際上是在宏文件中定義的另一個(gè)子網(wǎng)絡(luò)的縮寫。

在計(jì)算時(shí),我們想把卷積的參數(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ù),它們會(huì)被賦予一個(gè)初始值,并在訓(xùn)練的過程中不斷更新直到生成最終模型。這里,convW是一個(gè)16行25列的矩陣,B是長度為16的向量。Convolution是內(nèi)置的卷積函數(shù),默認(rèn)不使用補(bǔ)零的方法。也就是說對(duì)28x28的圖像做卷積運(yùn)算,實(shí)際上只是對(duì)24x24的中心區(qū)域操作,得到的結(jié)果是16個(gè)24x24的sudo-image。

接著我們用2x2的區(qū)域應(yīng)用最大池化操作,最后得到的結(jié)果是16個(gè)12x12的矩陣。

對(duì)于第二個(gè)卷積層,我們把卷積濾波器的個(gè)數(shù)由16個(gè)提升到32個(gè)。這一次我們有16通道的輸入數(shù)據(jù),因此W矩陣的尺寸為32行25×16 = 400列,向量B的長度為32。這次的卷積運(yùn)算針對(duì)12x12圖像幀的中心區(qū)域,所以得到的結(jié)果是32個(gè)8x8的矩陣。第二次池化操作的結(jié)果是32個(gè)4x4的幀,或者32x16=512。

最后兩層,是由512個(gè)池化輸出結(jié)果經(jīng)過128個(gè)節(jié)點(diǎn)的隱藏層連接到10個(gè)輸出節(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ò)模型做個(gè)比較。我們?cè)谥暗奈恼吕镉懻撨^這部分內(nèi)容,這里再討論一次。你是否注意到我們使用了與CNTK相同的一組變量,只不過這里我們把它稱作變量,而在CNTK稱作參數(shù)。維度也略有不同。盡管卷積濾波器都是5x5,在CNTK我們前后兩級(jí)分別使用了16個(gè)和32個(gè)濾波器,但是在TensorFlow的例子里我們用的是32個(gè)和64個(gè)。

def weight_variable(shape, names):initial = tf.truncated_normal(shape, stddev=0.1)return tf.Variable(initial, name=names)def bias_variable(shape, names):initial = tf.constant(0.1, shape=shape)return tf.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)建過程也大同小異。

def conv2d(x, W):return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')def max_pool_2x2(x):return tf.nn.max_pool(x, ksize=[1, 2, 2, 1],strides=[1, 2, 2, 1], padding='SAME')#first convolutional layer x_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 layer h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2) h_pool2 = max_pool_2x2(h_conv2) #final layer h_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個(gè)隱藏節(jié)點(diǎn)(使用relu而不是sigmoid函數(shù))。(在訓(xùn)練時(shí),最后一步用到了dropout函數(shù)將模型數(shù)值隨機(jī)地置零。如果keep_prob=1則忽略這步操作。)

網(wǎng)絡(luò)訓(xùn)練

CNTK中設(shè)置網(wǎng)絡(luò)模型訓(xùn)練的方式與TensorFlow差別巨大。訓(xùn)練和測試步驟是在一個(gè)convolution.config的文件內(nèi)設(shè)置。CNTK和TensorFlow都是通過符號(hào)化分析流程圖來計(jì)算梯度下降訓(xùn)練算法中所用到的梯度值。CNTK組給出了一本非常贊的“書”來闡述梯度是如何計(jì)算的。現(xiàn)階段CNTK只支持一種學(xué)習(xí)方法:Mini-batch隨機(jī)梯度下降法,但他們承諾今后加入更多的算法。He, Zhang, Ren 和 Sun發(fā)表了一篇優(yōu)秀的論文介紹他們是如何用嵌套殘留還原法(nested residual reduction)來訓(xùn)練極度深層(深達(dá)1000層)的網(wǎng)絡(luò)模型,所以讓我們拭目以待這個(gè)方法被融入到CNTK中。配置文件的縮略版如下所示。

command = train:test modelPath = "$ModelDir$/02_Convolution" ndlMacros = "$ConfigDir$/Macros.ndl"train = [action = "train"NDLNetworkBuilder = [networkDescription = "$ConfigDir$/02_Convolution.ndl"]SGD = [epochSize = 60000minibatchSize = 32learningRatesPerMB = 0.5momentumPerMB = 0*10:0.7maxEpochs = 15]reader = [readerType = "UCIFastReader"file = "$DataDir$/Train-28x28.txt"features = [dim = 784start = 1]labels = [# details deleted]] ] test = […. ]

命令行顯示了執(zhí)行的順序:先訓(xùn)練后測試。先聲明了各種文件的路徑,然后訓(xùn)練模塊設(shè)置了待訓(xùn)練的網(wǎng)絡(luò)模型以及隨機(jī)梯度下降(SGD)的參數(shù)。讀取模塊根據(jù)NDL文件中的設(shè)置讀取了“特征”和“標(biāo)簽”數(shù)據(jù)。測試模塊設(shè)置了用于測試的參數(shù)。

16核(沒有GPU)的Linux VM需要消耗62.95分鐘來執(zhí)行訓(xùn)練和測試過程,999.01分鐘的用戶時(shí)間和4分鐘的系統(tǒng)時(shí)間。用戶時(shí)間指的是所有16個(gè)核都在滿負(fù)荷運(yùn)轉(zhuǎn)(999/63 = 15.85)。但這并不算什么,因?yàn)镃NTK是為并行計(jì)算而設(shè)計(jì)的,大規(guī)模GPU支持才是真正的設(shè)計(jì)點(diǎn)。

TensorFlow的訓(xùn)練步驟在Python控制流程中設(shè)置得更清晰。而使用的算法Adam也是基于梯度計(jì)算的,由Kingma和Ba發(fā)明。TensorFlow的函數(shù)庫里有大量基于梯度的優(yōu)化方法,但我沒有嘗試其它的方法。

如下所以,cross_entropy是按照標(biāo)準(zhǔn)形式定義的,然后傳入優(yōu)化器生成一個(gè) “train_step”對(duì)象。

y_ = tf.placeholder(tf.float32, [None, 10]) cross_entropy = -tf.reduce_sum(y_*tf.log(y_conv)) train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy) correct_prediction = tf.equal(tf.argmax(y_conv,1), tf.argmax(y_,1)) accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float")) sess.run(tf.initialize_all_variables()) for i in range(20000):batch = mnist.train.next_batch(50)train_step.run(feed_dict={x: batch[0], y_: batch[1], keep_prob: 0.5})print("test accuracy %g"%accuracy.eval(feed_dict={ x: mnist.test.images, y_: mnist.test.labels, keep_prob: 1.0}))

隨后Python腳本每批處理50條數(shù)據(jù),以50%的舍棄率執(zhí)行train_step,迭代20000次。測試步驟在整個(gè)測試集上評(píng)估準(zhǔn)確率。

除了巧妙的自動(dòng)求積分和Adam優(yōu)化器的構(gòu)建,一切都是直截了當(dāng)?shù)摹N以?6核的服務(wù)器上用CNTK例子中相同的數(shù)據(jù)集又跑了一遍。出乎我意料的是所需的時(shí)間與CNTK幾乎一模一樣。實(shí)際運(yùn)行時(shí)間是62.02分鐘,用戶時(shí)間為160.45分鐘,所以幾乎沒用利用并行運(yùn)算。我覺得這些數(shù)字不能說明什么。CNTK和TensorFlow都是為大規(guī)模GPU運(yùn)算而設(shè)計(jì)的,它們運(yùn)行的訓(xùn)練算法并不完全一致。

遞歸神經(jīng)網(wǎng)絡(luò)在CNTK和TensorFlow的實(shí)現(xiàn)

遞歸神經(jīng)網(wǎng)絡(luò)(RNNs)在語言建模方面用途廣泛,例如打字時(shí)預(yù)測下一個(gè)輸入單詞,或是用于自動(dòng)翻譯系統(tǒng)。(更多例子請(qǐng)參見Andrej Karpathy的博客)真是個(gè)有趣的想法。系統(tǒng)的輸入是一個(gè)單詞(或者一組單詞)以及系統(tǒng)基于目前所出現(xiàn)單詞而更新的狀態(tài),輸出的是一個(gè)預(yù)測單詞列表和系統(tǒng)的新狀態(tài),如圖1所示。


圖1

當(dāng)然,RNN還有許多變種形式。其中一種常見的形式是長短期記憶模型(LSTM),其定義公式如下:


圖2:LSTM方程組(來源于CNTKBook)

此處 \sigma 表示sigmoid函數(shù)。

如果你有興趣讀一篇關(guān)于LSTM及其工作原理的博文,我推薦Christopher Olah所寫的這篇。他繪制了一張示意圖,使得上面的等式更容易理解。我把它稍作修改,使它符合CNTK版本的方程式,結(jié)果如下圖所示。


圖3:改編自Christopher Olah的優(yōu)秀文章

圖中使用了sigmoid和tanh函數(shù),并且級(jí)聯(lián)變量得到了下面的表達(dá)式:

其中W和b是學(xué)習(xí)得到的權(quán)重。

CNTK版本

下面是為LSTM模型設(shè)置的NDL。有兩件事需要注意。一個(gè)是網(wǎng)絡(luò)模型中使用了一個(gè)稱作“PastValue”的延遲操作符直接處理了遞歸的邏輯,它用到了維度和延遲時(shí)間兩個(gè)變量,返回該值的一個(gè)副本。第二件事情是注意W矩陣的處理方式,它與上面以及圖3中所示的級(jí)聯(lián)操作有何區(qū)別。在這里,它們把屬于x和h的所有W壓入堆棧,把所有b值也存入堆棧。然后計(jì)算一個(gè)W*x和一個(gè)W*h并求和,再加上b的值。然后再使用一個(gè)行切分操作符,分別用獨(dú)立的sigmoid函數(shù)處理它們。還需關(guān)注的是針對(duì)c的W矩陣都是對(duì)角陣。

LSTMPComponent(inputDim, outputDim, cellDim, inputx, cellDimX2, cellDimX3, cellDimX4) = [wx = Parameter(cellDimX4, inputDim, init="uniform", initValueScale=1);b = Parameter(cellDimX4, 1, init="fixedValue", value=0.0);Wh = Parameter(cellDimX4, outputDim, init="uniform", initValueScale=1);Wci = Parameter(cellDim, init="uniform", initValueScale=1);Wcf = Parameter(cellDim, init="uniform", initValueScale=1);Wco = Parameter(cellDim, init="uniform", initValueScale=1);dh = PastValue(outputDim, output, timeStep=1);dc = PastValue(cellDim, ct, timeStep=1);wxx = Times(wx, inputx);wxxpb = Plus(wxx, b);whh = Times(wh, dh);wxxpbpwhh = Plus(wxxpb,whh)G1 = RowSlice(0, cellDim, wxxpbpwhh)G2 = RowSlice(cellDim, cellDim, wxxpbpwhh)G3 = RowSlice(cellDimX2, cellDim, wxxpbpwhh);G4 = RowSlice(cellDimX3, cellDim, wxxpbpwhh);Wcidc = DiagTimes(Wci, dc);it = Sigmoid (Plus ( G1, Wcidc));bit = ElementTimes(it, Tanh( G2 ));Wcfdc = DiagTimes(Wcf, dc);ft = Sigmoid( Plus (G3, Wcfdc));bft = ElementTimes(ft, dc);ct = Plus(bft, bit);Wcoct = DiagTimes(Wco, ct);ot = Sigmoid( Plus( G4, Wcoct));mt = ElementTimes(ot, Tanh(ct));Wmr = Parameter(outputDim, cellDim, init="uniform", initValueScale=1);output = Times(Wmr, mt); ]

TensorFlow版本

TensorFlow版本的LSTM遞歸神經(jīng)網(wǎng)絡(luò)模型與CNTK版本完全不同。盡管它們所執(zhí)行的操作符都一樣,但TensorFlow的表示方式充分發(fā)揮了Python控制流的作用。這個(gè)概念模型非常簡單。我們創(chuàng)建了一個(gè)LSTM單元,并且定義一個(gè)“狀態(tài)”作為此單元的輸入,同時(shí)也是此單元的輸出。偽代碼如下所示:

cell = rnn_cell.BasicLSTMCell(lstm_size) # Initial state of the LSTM memory. state = tf.zeros([batch_size, lstm.state_size])for current_batch_of_words in words_in_dataset:# The value of state is updated after processing each batch of words. output, state = cell(current_batch_of_words, state)

這段摘自教程的偽代碼版本很好地反映了圖1的內(nèi)容。折磨人的地方在于微妙細(xì)節(jié)的處理。記住大部分時(shí)間TensorFlow的python代碼是在搭建流程圖,所以我們需要下一點(diǎn)功夫來繪制用于訓(xùn)練和執(zhí)行的循環(huán)流程圖。

這里最大的挑戰(zhàn)在于如何在一個(gè)循環(huán)內(nèi)創(chuàng)建并重復(fù)使用權(quán)重矩陣和偏置向量。CNTK使用了“PastValue”操作符來創(chuàng)建所需的循環(huán)。TensorFlow則使用了上面提到的所謂遞歸機(jī)制,和一個(gè)非常聰明的變量保存和調(diào)用機(jī)制來完成同樣的任務(wù)。“PastValue”在TensorFlow中對(duì)應(yīng)的是一個(gè)函數(shù), tf.get_variable( “name”, size, initializer = None) ,它的行為取決于當(dāng)前變量域中的“reuse”這個(gè)標(biāo)志位。如果reuse==False而且在當(dāng)時(shí)不存在其它的同名變量,那么get_variable 用那個(gè)變量名返回一個(gè)新的變量,并用初始化器對(duì)其初始化。否則將會(huì)返回錯(cuò)誤。如果reuse == True,那么get_variable返回之前已經(jīng)存在的那個(gè)變量。如果不存在這樣的變量,則返回一個(gè)錯(cuò)誤。

為了演示這種用法,以下是TensorFlow的一個(gè)函數(shù)的簡化版本,用來創(chuàng)建上面等式一的sigmoid函數(shù)。它只是W*x+b 的一個(gè)演化版本,其中x是一個(gè)list[a,b,c,…]

def linear(args, output_size, scope=None):#Linear map: sum_i(args[i] * W[i]), where W[i] is a variable.with vs.variable_scope(scope):matrix = vs.get_variable("Matrix", [total_arg_size, output_size])res = math_ops.matmul(array_ops.concat(1, args), matrix)bias_term = vs.get_variable("Bias", [output_size],initializer=init_ops.constant_initializer(1.))return res + bias_term

接下來定義BasicLSTMCell,大致的寫法如下所示。(想要查看這些函數(shù)的完整版本,請(qǐng)前往TensorFlow Github代碼庫里的rnn_cell.py腳本。)

class BasicLSTMCell(RNNCell):def __call__(self, inputs, state, scope=None):with vs.variable_scope(scope): c, h = array_ops.split(1, 2, state)concat = linear([inputs, h], 4 * self._num_units)i, j, f, o = array_ops.split(1, 4, concat)new_c = c * sigmoid(f) + sigmoid(i) * tanh(j)new_h = tanh(new_c) * sigmoid(o)return new_h, array_ops.concat(1, [new_c, new_h])

你可以看到,這里相當(dāng)準(zhǔn)確地再現(xiàn)圖3中的圖示。你會(huì)注意到上面的split操作符正是對(duì)應(yīng)于CNTK的row slice操作符。

現(xiàn)在我們可以創(chuàng)建一個(gè)可以用于訓(xùn)練的遞歸神經(jīng)網(wǎng)絡(luò)模型,在同樣的變量域我們能用共享的W和b變量創(chuàng)建另一個(gè)網(wǎng)絡(luò)模型用于測試。具體的做法在TensorFlow的遞歸神經(jīng)網(wǎng)絡(luò)教程ptb_word_lm.py腳本中有介紹。還有兩點(diǎn)值得留意。(應(yīng)該說它們對(duì)于我理解這個(gè)例子,有著至關(guān)重要的作用)他們創(chuàng)建一個(gè)lstmModel類來訓(xùn)練和測試網(wǎng)絡(luò)模型。

class lstmModel:def __init__(self, is_training, num_steps):self._input_data = tf.placeholder(tf.int32, [batch_size, num_steps])self._targets = tf.placeholder(tf.int32, [batch_size, num_steps])cell = rnn_cell.BasicLSTMCell(size, forget_bias=0.0)outputs = []states = []state = self._initial_statewith tf.variable_scope("RNN"):for time_step in range(num_steps):if time_step > 0: tf.get_variable_scope().reuse_variables()(cell_output, state) = cell(inputs[:, time_step, :], state)outputs.append(cell_output)states.append(state)… many details omitted …

我們?cè)谥鞒绦蛑袆?chuàng)建一個(gè)訓(xùn)練實(shí)例和一個(gè)測試實(shí)例,并調(diào)用它們(事實(shí)上還要?jiǎng)?chuàng)建一個(gè)實(shí)例,為了簡化過程我暫時(shí)先把它忽略)。

with tf.variable_scope("model", reuse=None, initializer=initializer):m = PTBModel(is_training=True, 20) with tf.variable_scope("model", reuse=True, initializer=initializer):mtest = PTBModel(is_training=False, 1)

在上述代碼中,創(chuàng)建了實(shí)例m,初始化設(shè)置20步且不用reuse。從初始化這一步你能觀察到,在計(jì)算流程圖中該單元被展開成20個(gè)副本,并且在首次迭代后reuse標(biāo)志置為True,此時(shí)所有的實(shí)例都將共享同一組W和b。訓(xùn)練過程在這個(gè)展開的版本上完成。第二個(gè)版本mtest設(shè)置reuse=True,且在圖中只有該單元的一個(gè)實(shí)例。但是變量域和m相同,因此它與m共享同一組訓(xùn)練得到的變量。

一旦訓(xùn)練完成,我們可以用一個(gè)內(nèi)核來調(diào)用這個(gè)網(wǎng)絡(luò)模型。

cost, state = sess.run([mtest.cost, mtest.final_state],{mtest.input_data: x,mtest.targets: y,mtest.initial_state: state})

x和y是輸入變量。這和教程示例中的完整過程相去甚遠(yuǎn)。舉個(gè)例子,我完全沒有深入到訓(xùn)練過程的細(xì)節(jié)中去,完整的示例使用了stacked LSTM并設(shè)置了dropout的比例。我的希望是,我在此羅列的細(xì)節(jié)將有助于讀者了解代碼的最基本結(jié)構(gòu)。

總結(jié)

我對(duì)兩個(gè)系統(tǒng)的編程模型做了比較。這里是一些頂層的想法。

  • TensorFlow和CNTK在卷積神經(jīng)網(wǎng)絡(luò)那個(gè)簡單例子中的做法非常相似。然而,我發(fā)現(xiàn)tensorflow版本更容易進(jìn)行實(shí)驗(yàn),因?yàn)樗怯蒔ython驅(qū)動(dòng)的。我能用IPython notebook加載它并做一些其它嘗試。而CNTK則需要用戶完全理解如何用配置文件表達(dá)想法。我覺得這很困難。我用TensorFlow能很容易寫一個(gè)簡單的k-means聚類算法(詳見我之前關(guān)于TensorFlow的文章)。我卻無法用CNTK來實(shí)現(xiàn),不過這可能是由于我的無知,而不是CNTK的局限性。如果有人能提示我該怎么做,我會(huì)很感激的)。

  • 在LSTM遞歸神經(jīng)網(wǎng)絡(luò)的例子里,我發(fā)現(xiàn)CNTK的版本相當(dāng)?shù)耐该鳌N野l(fā)現(xiàn)TensorFlow版本的頂層想法非常優(yōu)雅,但我也發(fā)現(xiàn)想了解它的所有細(xì)節(jié)卻非常困難,因?yàn)樯婕暗搅俗兞孔饔糜蚝妥兞抗蚕淼那擅钣梅āN也坏貌簧钊氲亓私馑墓ぷ髟怼5浆F(xiàn)在我也不是十分清楚!我在TensorFlow版本里確實(shí)發(fā)現(xiàn)了一個(gè)微小但很容易修復(fù)的bug,而且我不相信變量作用域和reuse標(biāo)志是解決封裝問題的最好方法。但是TensorFlow的好處在于我能很容易地修改實(shí)驗(yàn)。

  • 我也必須說CNTK書和TensorFlow教程都是優(yōu)秀入門級(jí)讀物。我相信有更多詳細(xì)的、深入的書馬上就會(huì)面世。

  • 我也相信,隨著兩個(gè)系統(tǒng)的不斷成熟,它們都會(huì)有改進(jìn),并且能更容易地使用。我在此不討論性能問題,但CNTK目前在解決某些挑戰(zhàn)難題的速度方面略勝一籌。但隨著這些系統(tǒng)的快速發(fā)展,我希望看到競爭也隨之升溫。

    原文鏈接:TensorFlow Meets Microsoft’s CNTK?
    作者:Dennis Gannon(MSR退休數(shù)據(jù)科學(xué)家,印第安納大學(xué)計(jì)算機(jī)科學(xué)榮譽(yù)退休教授)?
    譯者:趙屹華 審校:劉翔宇?
    責(zé)編:周建丁(投稿請(qǐng)聯(lián)系z(mì)houjd@csdn.net,優(yōu)稿優(yōu)酬)

    延伸閱讀:MXNet設(shè)計(jì)筆記之:深度學(xué)習(xí)的編程模式比較

    總結(jié)

    以上是生活随笔為你收集整理的代码解析深度学习系统编程模型:TensorFlow vs. CNTK的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。