Tensorflow Layer指南-創(chuàng)建卷積神經(jīng)網(wǎng)絡(luò)

我們都知道,神經(jīng)網(wǎng)絡(luò)是由一層一層的神經(jīng)元組合而成的,每個層之間可以通過不同的方式來連接起來以構(gòu)成不同結(jié)構(gòu)的神經(jīng)網(wǎng)絡(luò)。Tensorflow的layer模塊為我們提供了一組抽象層級很高的API,讓我們可以輕松地構(gòu)建一個我們想要的神經(jīng)網(wǎng)絡(luò)。我們可以通過layer對象的方法來很方便的實(shí)現(xiàn)我們常見的一些對神經(jīng)網(wǎng)絡(luò)層操作,例如添加激活函數(shù),應(yīng)用dropout regularization減少過擬合等。在本教程中,您將學(xué)習(xí)如何使用layer對象構(gòu)建卷積神經(jīng)網(wǎng)絡(luò)模型來識別MNIST數(shù)據(jù)集中的手寫數(shù)字。

可能學(xué)過機(jī)器學(xué)習(xí)和神經(jīng)網(wǎng)絡(luò)教程的同學(xué)們對于MNIST數(shù)據(jù)集中手寫數(shù)字識別這個例子應(yīng)該很熟悉了,但是為了讓沒有學(xué)過的小白們能看懂這篇教程,還是有必要介紹一下什么是MNIST數(shù)據(jù)集:

MNIST數(shù)據(jù)集包含60,000個訓(xùn)練樣例和10,000個手寫數(shù)字0-9的測試示例,格式為28x28像素單色圖像。


MINIST數(shù)據(jù)集

卷積神經(jīng)網(wǎng)絡(luò)的介紹

卷積神經(jīng)網(wǎng)絡(luò)——Convolutional neural networks (通常縮寫為 CNN)是當(dāng)前用于執(zhí)行圖像分類任務(wù)的最先進(jìn)也是最常用的神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)。 CNN將一系列濾波器應(yīng)用于圖像的原始像素數(shù)據(jù)以提取和學(xué)習(xí)更高級別的特征,使得該模型能夠?qū)⑦@些特征用于分類。 CNN包含三個組件:

  1. 卷積層(Convolutional layers),將特定數(shù)量的卷積濾鏡(convolution filters)應(yīng)用于圖像。 對于每個子區(qū)域,圖層執(zhí)行一組數(shù)學(xué)運(yùn)算以在輸出特征映射中生成單個值。 卷積層通常將ReLU激活函數(shù)應(yīng)用于輸出以將非線性引入到模型中。
  2. 合并層(Pooling layers),負(fù)責(zé)對由卷積層提取的圖像數(shù)據(jù)進(jìn)行下采樣以減少特征映射的維度以提高處理效率。 常用的池化算法是最大池化(max polling),其提取特征地圖的子區(qū)域(例如,2×2像素的塊),保持它們的最大值并丟棄所有其他值。
  3. 密集層(Dence layers),對由卷積圖層提取的特征并由共用圖層進(jìn)行下采樣(downsampled)執(zhí)行分類。 密集層是全連接的神經(jīng)網(wǎng)絡(luò),在密集層中,圖層中的每個節(jié)點(diǎn)都連接到前一圖層中的每個節(jié)點(diǎn)。

通常,CNN由執(zhí)行特征提取的一組卷積模塊組成,每個模塊又由一個卷積層和一個合并層組成。 最后的卷積模塊之后是一個或多個執(zhí)行分類的密集層。 CNN中的最終密集層的節(jié)點(diǎn)數(shù)量是與所有目標(biāo)類型的數(shù)量一致的,即模型可能預(yù)測的所有可能的目標(biāo)類型,使用softmax激活函數(shù)為每個節(jié)點(diǎn)生成0-1之間的值(全部 這些softmax值等于1), 我們可以將給定圖像的softmax值解釋為圖像落入每個目標(biāo)類別的可能性的相對測量值。

用Tensorflow創(chuàng)建基于CNN的MNIST數(shù)據(jù)分類器

讓我們建立一個擁有以下結(jié)構(gòu)的CNN,來對MNIST數(shù)據(jù)集中的圖像進(jìn)行分類:

  1. 卷積層#1:包含32個5x5濾波器(提取5x5像素子區(qū)域),使用ReLU激活函數(shù)。
  2. 合并層#1:包含32個2x2濾鏡,并按照最大池化的策略提取的數(shù)據(jù)執(zhí)行步幅為2的池化操作。(其指定池區(qū)域不重疊)
  3. 卷積層#2:包含64個5x5濾波器,使用ReLU激活函數(shù)。
  4. 合并層#2:同樣,使用2x2濾波器和2步幅進(jìn)行最大池化。
  5. 密集層#1:包含1,024個神經(jīng)元,dropout regularization的比率為0.4。
  6. 密集層#2(Logits Layer):10個神經(jīng)元,每個數(shù)字目標(biāo)類別(0-9)一個。

下面讓我們隆重請出今天的主角tf.layer,該模塊包含創(chuàng)建上述三種圖層類型的方法:

  • conv2d() ,構(gòu)造一個二維卷積層。 采用過濾器數(shù)量,過濾內(nèi)核大小,填充和激活函數(shù)作為參數(shù)。
  • max_pooling2d() ,使用max-pooling算法構(gòu)造一個二維池化層。 參數(shù)為過濾器大小和步幅。
  • dense() 構(gòu)建一個密集層。 以神經(jīng)元數(shù)量和激活函數(shù)作為參數(shù)。

這些方法中的每一個都接受tensor作為輸入,并將變換后的tensor作為輸出返回。 這樣可以很容易地將一個神經(jīng)層連接到另一個神經(jīng)層:只需從一個神經(jīng)層創(chuàng)建方法中獲取輸出并將其作為輸入提供給另一個神經(jīng)層。
現(xiàn)在我們添加以下cnn_model_fn函數(shù),該函數(shù)符合TensorFlow的Estimator API預(yù)期的接口。 cnn_mnist.py將MNIST特征數(shù)據(jù),標(biāo)簽和模型模式(TRAIN,EVAL,PREDICT)作為參數(shù); 配置CNN; 并返回預(yù)測,損失和培訓(xùn)操作:

def cnn_model_fn(features, labels, mode):
  """Model function for CNN."""
  # Input Layer
  input_layer = tf.reshape(features["x"], [-1, 28, 28, 1])

  # Convolutional Layer #1
  conv1 = tf.layers.conv2d(
      inputs=input_layer,
      filters=32,
      kernel_size=[5, 5],
      padding="same",
      activation=tf.nn.relu)

  # Pooling Layer #1
  pool1 = tf.layers.max_pooling2d(inputs=conv1, pool_size=[2, 2], strides=2)

  # Convolutional Layer #2 and Pooling Layer #2
  conv2 = tf.layers.conv2d(
      inputs=pool1,
      filters=64,
      kernel_size=[5, 5],
      padding="same",
      activation=tf.nn.relu)
  pool2 = tf.layers.max_pooling2d(inputs=conv2, pool_size=[2, 2], strides=2)

  # Dense Layer
  pool2_flat = tf.reshape(pool2, [-1, 7 * 7 * 64])
  dense = tf.layers.dense(inputs=pool2_flat, units=1024, activation=tf.nn.relu)
  dropout = tf.layers.dropout(
      inputs=dense, rate=0.4, training=mode == tf.estimator.ModeKeys.TRAIN)

  # Logits Layer
  logits = tf.layers.dense(inputs=dropout, units=10)

  predictions = {
      # Generate predictions (for PREDICT and EVAL mode)
      "classes": tf.argmax(input=logits, axis=1),
      # Add `softmax_tensor` to the graph. It is used for PREDICT and by the
      # `logging_hook`.
      "probabilities": tf.nn.softmax(logits, name="softmax_tensor")
  }

  if mode == tf.estimator.ModeKeys.PREDICT:
    return tf.estimator.EstimatorSpec(mode=mode, predictions=predictions)

  # Calculate Loss (for both TRAIN and EVAL modes)
  loss = tf.losses.sparse_softmax_cross_entropy(labels=labels, logits=logits)

  # Configure the Training Op (for TRAIN mode)
  if mode == tf.estimator.ModeKeys.TRAIN:
    optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.001)
    train_op = optimizer.minimize(
        loss=loss,
        global_step=tf.train.get_global_step())
    return tf.estimator.EstimatorSpec(mode=mode, loss=loss, train_op=train_op)

  # Add evaluation metrics (for EVAL mode)
  eval_metric_ops = {
      "accuracy": tf.metrics.accuracy(
          labels=labels, predictions=predictions["classes"])}
  return tf.estimator.EstimatorSpec(
      mode=mode, loss=loss, eval_metric_ops=eval_metric_ops)

以下部分(與上面每個代碼塊對應(yīng)的標(biāo)題)深入介紹用于創(chuàng)建每個神經(jīng)層的tf.layers代碼,以及如何計算損失,配置訓(xùn)練操作并生成預(yù)測。

輸入層

對于處理2D圖像數(shù)據(jù)的CNN,Tensorflow的Layer對象中用于創(chuàng)建卷積層和合并層的方法需要輸入一個結(jié)構(gòu)為[batch_size, image_width, image_height, channels]的4維tensor,各個參數(shù)定義如下:

  • batch_size: 在訓(xùn)練期間執(zhí)行梯度下降時要使用的示例子集的大小。
  • image_width:示例圖像寬度。
  • image_height: 示例圖像高度。
  • channels: 示例圖像中的顏色通道數(shù)量。 對于彩色圖像,通道數(shù)量是3(紅色,綠色,藍(lán)色),對于單色圖像,只有1個通道(黑色)。

這里,我們的MNIST數(shù)據(jù)集由單色的28x28像素圖像組成,因此我們輸入圖層的所需形狀為[batch_size,28,28,1]。
為了將我們的輸入特征數(shù)據(jù)映射(特征)轉(zhuǎn)換為這種形狀,我們可以執(zhí)行下面的整形操作:

input_layer = tf.reshape(features["x"], [-1, 28, 28, 1])

值得注意的是,我們已經(jīng)為batch_size賦值為-1,意味著此維度大小應(yīng)該根據(jù)feature["x"]中輸入值的數(shù)量動態(tài)計算,并保持所有其他維度的大小不變。 這使我們可以將batch_size視為我們可以調(diào)整的超參數(shù)。 例如,如果我們將示例以5批次的形式提供給我們的模型,則feature["x"]將包含3,920個值(每個圖像中每個像素的一個值),并且input_layer將具有[5,28,28,1]. 同樣,如果我們以100個批次的形式提供示例,則feature["x"]將包含78,400個值,而input_layer將具有[100,28,28,1]的形狀。

卷積層 #1

在我們的第一個卷積層中,我們希望將32個5x5濾波器應(yīng)用于輸入層,并使用ReLU作為激活函數(shù)。 我們可以在圖層模塊中使用conv2d()方法來創(chuàng)建該圖層,如下所示:

conv1 = tf.layers.conv2d(
    inputs=input_layer,
    filters=32,
    kernel_size=[5, 5],
    padding="same",
    activation=tf.nn.relu)
  • filter參數(shù)表示filter的數(shù)量(這里是32)。
  • kernel_size表示filter的維度(這里是[5,5])。
  • padding參數(shù)為兩個枚舉值中的一個(不區(qū)分大小寫):valid(缺省值)或+ same。 我們在此處設(shè)置padding=same,表示輸出tensor應(yīng)該與輸入tensor具有相同的寬度和高度值。此時TensorFlow將0值添加到輸入tensor的邊緣以保持寬度和高度為28。(如果沒有設(shè)置padding屬性,則將在28x28tensor上進(jìn)行5x5卷積生成24x24tensor,因為有24x24個位置從28x28網(wǎng)格中提取5x5瓦片。)
  • activation參數(shù)表示用于卷積層輸出的激活函數(shù)。這里我們選取了Relu函數(shù) tf.nn.relu

我們的用于輸出的tensor由conv2d()函數(shù)生成,tensor的結(jié)構(gòu)是[batch_size, 28, 28, 32]。這里前三個維度大小和input_layer輸出的大小一致,最后一個32表示了有32個通道保存每個過濾器的輸出。

匯聚層 #1

接下來,我們將第一個匯聚層與剛才創(chuàng)建的卷積層連接起來。我們可以利用layermax_pooling2d()方法構(gòu)建層執(zhí)行max_pooling策略的一個2x2的過濾器:

pool1 = tf.layers.max_pooling2d(inputs=conv1, pool_size=[2, 2], strides=2)

這里同樣,inputs表示輸入的結(jié)構(gòu)為[batch_size, image_width, image_height, channels]的tensor。這里,我們的輸入tensor是conv1,它是我們的第一個卷積層的輸出,它的結(jié)構(gòu)為[batch_size, 28, 28, 32] 。

  • pool_size表示這個max pooling filter的大小為[width,height](這里是[2,2])。
  • strides參數(shù)表示步幅大小,在這里,我們設(shè)置了長度為2的步幅,這表明由濾波器提取的子區(qū)域應(yīng)該在寬和高上間隔2個像素(對于2x2濾波器,這意味著沒有提取的區(qū)域?qū)⒅丿B), 如果要為寬度和高度設(shè)置不同的跨度值,則可以改為指定元組或列表(例如,stride = [3,6])。

我們的輸出tensor是由max_pooling2d()方法生成的,#pool1輸出格式為:[batch_size,14,14,32],2X2的filter使原始數(shù)據(jù)的長度和高度都減少50%。

卷積層#2以及合并層#2

我們可以繼續(xù)將第二個卷積層和合并層的組合連接到我們的CNN中,這里我們依然使用conv2d()max_pooling2d()方法。第二個卷積層中,我們將filter的數(shù)量增加到64個,依然使用ReLU函數(shù)作為激活函數(shù),而對于第二個合并層,我們將采用和一個合并層相同的結(jié)構(gòu)(一個長寬和步幅都為2的max pooling filter):

conv2 = tf.layers.conv2d(
    inputs=pool1,
    filters=64,
    kernel_size=[5, 5],
    padding="same",
    activation=tf.nn.relu)
pool2 = tf.layers.max_pooling2d(inputs=conv2, pool_size=[2, 2], strides=2)

值得注意的是,卷積層#2使用的是合并層#1的輸出作為輸入tensor,并通過conv2d()方法生成結(jié)構(gòu)為[batch_size,14,14,64]的tensor作為輸出。其中widthheight由于設(shè)置了參數(shù)padding="same",和pool1的輸出寬高是一致的,channels 則表示64個filter輸出的64個channel。
合并層#2采用conv2作為輸入,并以pool2作為輸出,輸出格式為:[batch_size, 7, 7, 64],可以看出,數(shù)據(jù)寬高大小再一次減半。

密集層

接下來,我們需要添加一個由1024個采用ReLU激活函數(shù)的神經(jīng)元組成的密集層到我們的神經(jīng)網(wǎng)絡(luò)中,來為我們從前面的卷積層和合并層中提取出來的圖像特征做分類。在我們將這個神經(jīng)層連接到神經(jīng)網(wǎng)絡(luò)之前,我們需要將我們的pool2輸出的tensor扁平化(flatten)一下,讓其結(jié)構(gòu)變成[batch_size,features]只有兩個維度,代碼如下:

pool2_flat = tf.reshape(pool2, [-1, 7 * 7 * 64])

在上面的reshape()操作中,-1表示batch_size由輸入數(shù)據(jù)的實(shí)例數(shù)量動態(tài)計算。每個實(shí)例具有7x7x64=3136個特征,這里每個數(shù)字分別對應(yīng)pool2的寬、高以及通道數(shù)量,所以我們的pool2_flat的被“壓扁”后的大小為:[batch_size, 3136]
Now, we can use the dense() method in layers to connect our dense layer as follows:
現(xiàn)在 ,我們可以使用layerdense()方法去將我們的dense層創(chuàng)建出來:

dense = tf.layers.dense(inputs=pool2_flat, units=1024, activation=tf.nn.relu)

其中inputs、activation參數(shù)的意義和tf.layers.conv2d()方法中一樣,分別表示輸入tensor和激活函數(shù),而units參數(shù)則表示該層中神經(jīng)元的數(shù)量。

為了防止過擬合,我們可以使用tf.layers.dropout()方法,在我們的dense層輸出后面加上dropout regularization:

dropout = tf.layers.dropout(
    inputs=dense, rate=0.4, training=mode == tf.estimator.ModeKeys.TRAIN)

inputs參數(shù)不用多說,表示輸入的tensor,rate參數(shù)表示我們droout的比率,這里我們使用0.4,意味著在訓(xùn)練時,40%的數(shù)據(jù)會被隨機(jī)丟棄。 train參數(shù)是個布爾值,用于控制dropout是否啟用,這里我們將只在TRAIN模式中采用dropout,dropout的大小為:[batch_size, 1024]。

Logits層

我們的神經(jīng)網(wǎng)絡(luò)中的最后一層是logits層,它會返回我們預(yù)測的原始值。 最終我們創(chuàng)建了一個結(jié)構(gòu)為[batch_size, 10]包含10個神經(jīng)元(分別對應(yīng)0-9這10個目標(biāo)類)的密集層,并使用線性激活函數(shù)(默認(rèn)值):

logits = tf.layers.dense(inputs=dropout, units=10)

生成預(yù)測值

我們的模型為我們返回的[batch_size, 10]-維的tensor中包含預(yù)測結(jié)果的原始值,讓我們將這些原始值轉(zhuǎn)換成一些比較直觀的格式來作為我們模型的返回值,例如:

  • 每個示例的預(yù)測類別:直接根據(jù)預(yù)測值返回一個0-9的數(shù)字。
  • 每個示例的每個可能目標(biāo)類的概率:返回預(yù)測值為0,為1,為2...的概率。

回到我們的代碼,我們采用了一個tf.argmax()方法來找到返回的tensor中每一行數(shù)據(jù)中的最大值的下標(biāo):

tf.argmax(input=logits, axis=1)

input參數(shù)表示輸入tensor,axis參數(shù)表示我們是對哪個維度求最大值下標(biāo),這里給1表示對行求最大值下標(biāo),而我們的輸入logit的結(jié)構(gòu)是[batch_size,10],所以我們這里表示是對數(shù)字10所代表的維度求最大值下標(biāo),而這10個下標(biāo)分別對應(yīng)我們所預(yù)測的0-9這9個數(shù)字,而最大值所對應(yīng)的下標(biāo)就是我們預(yù)測的結(jié)果。這樣講可能有點(diǎn)抽象,我們來舉個例子:

[...[1,1,1,1,1000,1,1,1,1,1]...]

這里可以看到,我們輸出tensor的某一行中最大值1000所對應(yīng)的下標(biāo)為4(從0開始),表示我們對于這一組數(shù)據(jù)的預(yù)測結(jié)果為4,即這幅圖片上面寫的是阿拉伯?dāng)?shù)字4。
然后,我們將我們的預(yù)測值轉(zhuǎn)換成兩種輸出格式再組合成一個dict后輸出一個EstimatorSpec對象:

predictions = {
    "classes": tf.argmax(input=logits, axis=1),
    "probabilities": tf.nn.softmax(logits, name="softmax_tensor")
}
if mode == tf.estimator.ModeKeys.PREDICT:
  return tf.estimator.EstimatorSpec(mode=mode, predictions=predictions)

計算損失

對于培訓(xùn)和評估,我們需要定義一個損失函數(shù)來衡量模型的預(yù)測與目標(biāo)類別的匹配程度。 對于像MNIST這樣的多類分類問題,通常使用交叉熵來度量損失。 以下代碼計算模型在TRAIN或EVAL模式下運(yùn)行時的交叉熵:

onehot_labels = tf.one_hot(indices=tf.cast(labels, tf.int32), depth=10)
loss = tf.losses.softmax_cross_entropy(
    onehot_labels=onehot_labels, logits=logits)

先看第一行代碼,label這個tensor中包含了我們用于訓(xùn)練的預(yù)測值列表,例如, [1,9,...]。 為了計算交叉熵,首先我們需要將標(biāo)簽轉(zhuǎn)換為相應(yīng)的單熱編碼 (one-hot encoding)

[[0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
 ...]

我們使用tf.one_hot()函數(shù)來執(zhí)行此轉(zhuǎn)換,這個函數(shù)有兩個必需的參數(shù):

  • indices: 單熱t(yī)ensor中"on value"所處的位置,即上述tensor中的"1"值的位置。
  • depth: 單熱t(yī)ensor的深度,即目標(biāo)類別的數(shù)量。 這里,深度是10(0-9)。

經(jīng)過這一步驟之后我們的label的值從[0,1...]這樣的由0-9數(shù)字組成的列表變?yōu)?code>onehot_labels這樣由[1,0,0,0,0,0,0,0,0],[0,1,0,0,0,0,0,0,0]...等單熱編碼所組成的列表,數(shù)字1所在的下標(biāo)表示原來的數(shù)值。

接下來在看第二行代碼,我們利用tf.losses.softmax_cross_entropy()方法來計算onehot_labelslogits層輸出預(yù)測值的交叉熵。 在計算時,會在logits上執(zhí)行softmax激活,再將onehot_labels和softmax激活后的logits作為參數(shù)計算交叉熵,并將loss作為一個標(biāo)量tensor返回。

配置訓(xùn)練操作

在我們將CNN的損失定義為logits層和我們label的softmax交叉熵后,我們將配置我們的模型以在訓(xùn)練期間優(yōu)化這個損失值。 我們將使用學(xué)習(xí)率為0.001的隨機(jī)梯度下降作為優(yōu)化算法:

if mode == tf.estimator.ModeKeys.TRAIN:
  optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.001)
  train_op = optimizer.minimize(
      loss=loss,
      global_step=tf.train.get_global_step())
  return tf.estimator.EstimatorSpec(mode=mode, loss=loss, train_op=train_op)

添加評估指標(biāo)

要評估我們模型的預(yù)測準(zhǔn)確性,我們需要在EVAL模式中定義eval_metric_ops字典,如下所示:

eval_metric_ops = {
    "accuracy": tf.metrics.accuracy(
        labels=labels, predictions=predictions["classes"])
}
return tf.estimator.EstimatorSpec(
    mode=mode, loss=loss, eval_metric_ops=eval_metric_ops)

訓(xùn)練和評估我們的 CNN MNIST 分類器

我們已經(jīng)完成 CNN MNIST 模型的創(chuàng)建,現(xiàn)在我們要訓(xùn)練并評估它

加載訓(xùn)練集和測試集

First, let's load our training and test data. Add a main() function to cnn_mnist.py with the following code:

首先,讓我們加載我們的訓(xùn)練集和測試集。首先為我們的工程添加main()函數(shù):

def main(unused_argv):
  # Load training and eval data
  mnist = tf.contrib.learn.datasets.load_dataset("mnist")
  train_data = mnist.train.images # Returns np.array
  train_labels = np.asarray(mnist.train.labels, dtype=np.int32)
  eval_data = mnist.test.images # Returns np.array
  eval_labels = np.asarray(mnist.test.labels, dtype=np.int32)

我們將train_data和train_labels中的訓(xùn)練特征數(shù)據(jù)(手繪數(shù)字的55,000個圖像的原始像素值)和訓(xùn)練label(每個圖像的0-9的對應(yīng)值)分別存儲為numpy數(shù)組。 同樣,我們將評估特征數(shù)據(jù)(10,000個圖像)和評估label分別存儲在eval_data和eval_labels中。

創(chuàng)建Estimator

接下來,讓我們?yōu)槲覀兊哪P蛣?chuàng)建一個Estimator(一個TensorFlow類,用于執(zhí)行高級模型訓(xùn)練,評估和推理)。 將下面的代碼添加到main()中:

# Create the Estimator
mnist_classifier = tf.estimator.Estimator(
    model_fn=cnn_model_fn, model_dir="/tmp/mnist_convnet_model")

model_fun就是我們前面所編寫的創(chuàng)建模型的cnn_model_fn,model_dir表示我們保存模型數(shù)據(jù)的路徑。

設(shè)置日志鉤子

由于CNN的訓(xùn)練需要一段時間,因此我們需要在訓(xùn)練期間建立一些日志記錄,以便跟蹤訓(xùn)練進(jìn)度。 我們可以使用TensorFlow的tf.train.SessionRunHook創(chuàng)建一個tf.train.LoggingTensorHook,它將記錄來自CNN的softmax層的概率值:

  # Set up logging for predictions
  tensors_to_log = {"probabilities": "softmax_tensor"}
  logging_hook = tf.train.LoggingTensorHook(
      tensors=tensors_to_log, every_n_iter=50)

我們在tensors_to_log字典中存儲了我們想要進(jìn)行日志跟蹤的tensor,字典的key是我們輸出日志的標(biāo)簽,而對應(yīng)的value是我們的tensor在tensorflow的graph中的名稱。在這里,我們的概率可以在softmax_tensor中找到,這是我們在cnn_model_fn中生成概率時早先給出的softmax操作的名稱。
接下來,我們創(chuàng)建LoggingTensorHook,將tensors_to_log傳遞給tensors參數(shù),我們設(shè)置every_n_iter = 50,表示每50步記錄一次日志。

訓(xùn)練我們的模型

現(xiàn)在我們準(zhǔn)備訓(xùn)練我們的模型,我們可以通過在mnist_classifier上創(chuàng)建train_input_fn并調(diào)用train()來完成這個模型的訓(xùn)練。 將以下內(nèi)容添加到main()

# Train the model
train_input_fn = tf.estimator.inputs.numpy_input_fn(
    x={"x": train_data},
    y=train_labels,
    batch_size=100,
    num_epochs=None,
    shuffle=True)
mnist_classifier.train(
    input_fn=train_input_fn,
    steps=20000,
    hooks=[logging_hook])

在numpy_input_fn調(diào)用中,我們將訓(xùn)練特征數(shù)據(jù)和label分別傳遞給x和y,并設(shè)置了100的batch_size(這意味著模型將在每個步驟以數(shù)量為100的minibatches進(jìn)行訓(xùn)練)。num_epochs = None表示模型將訓(xùn)練到達(dá)到指定的步數(shù)。 我們還設(shè)置shuffle = True來洗牌訓(xùn)練數(shù)據(jù)。 在調(diào)用train()時,我們設(shè)置了steps= 20000(這意味著模型將訓(xùn)練總共20000步)。 我們將logging_hook傳遞給hooks參數(shù),以便在訓(xùn)練過程中觸發(fā)它。

評估我們的模型

一旦訓(xùn)練完成,我們要評估我們的模型以確定其在MNIST測試集上的準(zhǔn)確性。 我們將測試集中的eval_dataeval_label傳入numpy_input_fn的x和y參數(shù),并調(diào)用evaluate()方法來評估我們在model_fn中的eval_metric_ops參數(shù)中指定指標(biāo):

# Evaluate the model and print results
eval_input_fn = tf.estimator.inputs.numpy_input_fn(
    x={"x": eval_data},
    y=eval_labels,
    num_epochs=1,
    shuffle=False)
eval_results = mnist_classifier.evaluate(input_fn=eval_input_fn)
print(eval_results)

為了創(chuàng)建eval_input_fn,我們設(shè)置num_epochs = 1,以便模型評估一個歷元數(shù)據(jù)上的度量并返回結(jié)果。 我們還設(shè)置shuffle = False來循環(huán)遍歷數(shù)據(jù)。

Run the Model

我們編寫了CNN模型函數(shù)、Estimator和訓(xùn)練/評估邏輯; 現(xiàn)在讓我們看看結(jié)果。 運(yùn)行cnn_mnist.py得到以下輸出:

INFO:tensorflow:loss = 2.36026, step = 1
INFO:tensorflow:probabilities = [[ 0.07722801  0.08618255  0.09256398, ...]]
...
INFO:tensorflow:loss = 2.13119, step = 101
INFO:tensorflow:global_step/sec: 5.44132
...
INFO:tensorflow:Loss for final step: 0.553216.

INFO:tensorflow:Restored model from /tmp/mnist_convnet_model
INFO:tensorflow:Eval steps [0,inf) for training step 20000.
INFO:tensorflow:Input iterator is exhausted.
INFO:tensorflow:Saving evaluation summary for step 20000: accuracy = 0.9733, loss = 0.0902271
{'loss': 0.090227105, 'global_step': 20000, 'accuracy': 0.97329998}

可以看到我們的模型擁有高達(dá)97.3%的準(zhǔn)確率,是不是很酷?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容