TensorFlow入門和示例分析

  本文以TensorFlow源碼中自帶的手寫數字識別Example爲例,引出TensorFlow中的幾個主要概念。並結合Example源碼一步步分析該模型的實現過程。node

1、什麼是TensorFlow

  在這裏,引入TensorFlow中文社區首頁中的兩段描述。python

關於 TensorFlow
TensorFlow™ 是一個採用數據流圖(data flow
graphs),用於數值計算的開源軟件庫。節點(Nodes)在圖中表示數學操做,圖中的線(edges)則表示在節點間相互聯繫的多維數據數組,即張量(tensor)。它靈活的架構讓你能夠在多種平臺上展開計算,例如臺式計算機中的一個或多個CPU(或GPU),服務器,移動設備等等。TensorFlow
最初由Google大腦小組(隸屬於Google機器智能研究機構)的研究員和工程師們開發出來,用於機器學習和深度神經網絡方面的研究,但這個系統的通用性使其也可普遍用於其餘計算領域。git

什麼是數據流圖(Data Flow Graph)?
數據流圖用「結點」(nodes)和「線」(edges)的有向圖來描述數學計算。「節點」
通常用來表示施加的數學操做,但也能夠表示數據輸入(feed in)的起點/輸出(push
out)的終點,或者是讀取/寫入持久變量(persistent
variable)的終點。「線」表示「節點」之間的輸入/輸出關係。這些數據「線」能夠輸運「size可動態調整」的多維數據數組,即「張量」(tensor)。張量從圖中流過的直觀圖像是這個工具取名爲「Tensorflow」的緣由。一旦輸入端的全部張量準備好,節點將被分配到各類計算設備完成異步並行地執行運算。算法

2、示例

  接下來的示例中,主要使用到如下兩個文件。api

mnist.py
fully_connected_feed.py

  該示例的目的是創建一個手寫圖像識別模型,經過該模型,能夠準確識別輸入的28 * 28像素的手寫圖片是0~9這十個數字中的哪個。數組

一、運行文件準備

  須要下載好tensorflow源代碼,注意這裏的源代碼版本須要與安裝的TensorFlow版本保持一致。
  在/home/mlusr/files/tensorflow/下解壓縮該文件。進入示例文件路徑中,運行瀏覽器

cd ~/files/tensorflow/tensorflow-r0.11/tensorflow/examples/tutorials/mnist
python fully_connected_feed.py

  運行過程當中,須要聯網下載訓練數據,數據文件保存到~/files/tensorflow/tensorflow-r0.11/tensorflow/examples/tutorials/mnist/data路徑下,若是不能聯網的話,能夠手動到http://yann.lecun.com/exdb/mnist/,下載好如下四個文件,放入data目錄。bash

t10k-images-idx3-ubyte.gz
t10k-labels-idx1-ubyte.gz
train-images-idx3-ubyte.gz
train-labels-idx1-ubyte.gz

二、運行

  直接運行fully_connected_feed.py文件。服務器

python fully_connected_feed.py

  輸出信息以下:markdown

Extracting data/train-images-idx3-ubyte.gz
Extracting data/train-labels-idx1-ubyte.gz
Extracting data/t10k-images-idx3-ubyte.gz
Extracting data/t10k-labels-idx1-ubyte.gz
Step 0: loss = 2.30 (0.007 sec)
Step 100: loss = 2.13 (0.005 sec)
Step 200: loss = 1.87 (0.004 sec)
Step 300: loss = 1.55 (0.004 sec)
Step 400: loss = 1.26 (0.004 sec)
Step 500: loss = 0.87 (0.004 sec)
Step 600: loss = 0.87 (0.004 sec)
Step 700: loss = 0.65 (0.005 sec)
Step 800: loss = 0.43 (0.004 sec)
Step 900: loss = 0.65 (0.005 sec)
Training Data Eval:
  Num examples: 55000  Num correct: 47184  Precision @ 1: 0.8579
Validation Data Eval:
  Num examples: 5000  Num correct: 4349  Precision @ 1: 0.8698
Test Data Eval:
  Num examples: 10000  Num correct: 8663  Precision @ 1: 0.8663
Step 1000: loss = 0.47 (0.006 sec)
Step 1100: loss = 0.40 (0.051 sec)
Step 1200: loss = 0.55 (0.005 sec)
Step 1300: loss = 0.43 (0.004 sec)
Step 1400: loss = 0.39 (0.004 sec)
Step 1500: loss = 0.57 (0.005 sec)
Step 1600: loss = 0.50 (0.004 sec)
Step 1700: loss = 0.37 (0.005 sec)
Step 1800: loss = 0.38 (0.006 sec)
Step 1900: loss = 0.35 (0.004 sec)
Training Data Eval:
  Num examples: 55000  Num correct: 49292  Precision @ 1: 0.8962
Validation Data Eval:
  Num examples: 5000  Num correct: 4525  Precision @ 1: 0.9050
Test Data Eval:
  Num examples: 10000  Num correct: 9027  Precision @ 1: 0.9027

三、啓動TensorBoard並查看

  在啓動TensorBoard時注意指定輸出log文件路徑,在本例中啓動命令以下

tensorboard --logdir /home/mlusr/files/tensorflow/tensorflow-r0.11/tensorflow/examples/tutorials/mnist/data

  啓動輸出信息以下所示:

Starting TensorBoard 29 on port 6006
(You can navigate to http://192.168.1.100:6006)

  瀏覽器訪問頁面指定ip和端口:
  這裏寫圖片描述

  在TensorBoard中還能夠查看該模型的更多信息。
  
  本文接下來的部分,將以mnist.pyfully_connected_feed.py兩個文件中的內容

2、數據下載和輸入

  MNIST的數據主要分紅如下三個部分,
  

數據集 做用
data_sets.train 55000條image和label數據,主要用於訓練模型
data_sets.validation 5000條image和label數據,用於在迭代過程當中肯定模型準確率
data_sets.test 10000條image和label數據,用於最終評估模型的準確率

1、概念一:Placeholder

  Placeholder的更多描述,請看這裏。使用Placeholder的地方,在構造Graph時並不包含實際的數據,只是在應用運行時纔會動態的用數據來替代。

  在fully_connected_feed.py文件中的placeholder_inputs方法中,經過調用tf.placeholder方法分別生成了表明imageslabelsplaceholder

IMAGE_SIZE = 28
IMAGE_PIXELS = IMAGE_SIZE * IMAGE_SIZE

images_placeholder = tf.placeholder(tf.float32, shape=(batch_size, mnist.IMAGE_PIXELS))
labels_placeholder = tf.placeholder(tf.int32, shape=(batch_size))

  在生成placeholder時,只須要指定其中的數據類型,以及維度。上面images_placeholder中的元素爲float類型,維度爲batch_size * IMAGE_PIXELSlagels_placeholder中的元素爲int類型,維度爲batch_sizebatch_size參數在程序調用placeholder_inputs時指定。

  看到這裏能夠發現images_placeholder和labels_placeholder僅僅只是指定了其中元素的類型和shape,具體數值是在後續程序運行時纔會填充進來的。因此叫作Placeholder。在這裏這兩個Placeholder表明了輸入的兩個數據源。

3、概念二:Graph

  
  Graph是TensorFlow中又一個重要概念。Graph能夠理解成TensorFlow中的一個調整好參數的執行計劃。構建好這個Graph以後,全部輸入數據,中間轉換過程,以及輸出數據的流程和格式便固定下來,數據進入Graph後按照特定的結構和參數,就能獲得對應的輸出結果。以下圖所示:
  這裏寫圖片描述

  構建一個Graph主要分紅如下三步。

一、肯定Graph結構

  inference方法,以images_placeholder做爲輸入,鏈接到維度爲(28 * 28, 128)的隱層1,隱層1鏈接到維度爲(128, 32)的隱層2,最後的輸出層logits爲10個節點。各層之間的激活函數爲Relu
  
  下面代碼中使用到的常量

IMAGE_SIZE = 28
IMAGE_PIXELS = IMAGE_SIZE * IMAGE_SIZE
flags.DEFINE_integer('hidden1', 128, 'Number of units in hidden layer 1.')
flags.DEFINE_integer('hidden2', 32, 'Number of units in hidden layer 2.')
NUM_CLASSES = 10

  構建隱層1,

with tf.name_scope('hidden1'):
 weights = tf.Variable(tf.truncated_normal([IMAGE_PIXELS, hidden1_units],
                       stddev=1.0 / math.sqrt(float(IMAGE_PIXELS))),
                       name='weights')
 biases = tf.Variable(tf.zeros([hidden1_units]),
                      name='biases')

  上面定義了兩個Variableweights表示鏈接權重,biases表示偏置量。
  biases比較簡單,定義了一個名爲biases的元素全爲0的變量,其長度爲hiden1_units,默認爲128
  weights的維度爲IMAGE_PIXELS * hidden1_units,其中的初始值爲標準差爲1 / math.sqrt(float(IMAGE_PIXELS)的截斷正態分佈值。

  構建隱層2,

with tf.name_scope('hidden2'):
  weights = tf.Variable(tf.truncated_normal([hidden1_units, hidden2_units],
                        stddev =1.0 / math.sqrt(float(hidden1_units))),
                        name = 'weights')
  biases = tf.Variable(tf.zeros([hidden2_units]),
                       name ='biases')

  構建輸出層,

with tf.name_scope('softmax_linear'):
  weights = tf.Variable(tf.truncated_normal([hidden2_units, NUM_CLASSES] ,
                        stddev =1.0 / math.sqrt(float(hidden2_units))) ,
                        name = 'weights')
  biases = tf.Variable(tf.zeros([NUM_CLASSES]),
                       name ='biases')

  基於上面的權重和偏置量值,使用relu激活函數鏈接各層,

hidden1 = tf.nn.relu(tf.matmul(images, weights) + biases)
hidden2 = tf.nn.relu(tf.matmul(hidden1, weights) + biases)
logits = tf.matmul(hidden2, weights) + biases

  前面的三組weightsbiases變量名都相同,區分的辦法是前面的with tf.name_scope('hidden1')。在hidden1命名空間下的wiehts參數的完整表示爲"hidden1/weights"

二、肯定損失函數

  上一步肯定好模型各層結構和參數後,接下來須要定義一個損失函數的計算邏輯。
  在mnist.py文件中有一個loss()方法,輸入兩個參數,第一個爲上面模型的輸出結果logits,第二個爲images對應的實際labels,在調用該方法時,傳入的是前面定義的labels_placeholder
  

def loss(logits, labels):
  labels = tf.to_int64(labels)
  cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(
      logits, labels , name = 'xentropy')
  loss = tf.reduce_mean(cross_entropy, name ='xentropy_mean')
  return loss

  上面的tf.nn.sparse_softmax_cross_entropy_with_logits會根據labels的內容自動生成1-hot編碼,而且計算與輸出logits的1-hot編碼的交叉熵[cross entropy][http://blog.csdn.net/rtygbwwwerr/article/details/50778098])
  最後,調用reduce_mean方法,計算交叉熵的平均值。

三、參數訓練

  
  調用training方法的調用形式爲,傳入上面的損失值和學習率。

train_op = mnist.training(loss, FLAGS.learning_rate)

  接下來,mnist.py文件中的training方法,將使用梯度降低法來計算使得損失值最小的模型參數。首先將損失值loss傳入tf.scala_summary中,這個操做主要是用於在後面使用SummaryWriter時向events file中生成求和值,將每一次獲得的損失值寫出到事件文tf.scalar_summary(loss.op.name, loss)後,調用tf.train.GradientDesecentOptimizer按指定的學習率實現梯度降低算法。
  

# Create the gradient descent optimizer with the given learning rate.
optimizer = tf.train.GradientDescentOptimizer(learning_rate)

  最後,使用一個名爲global_stepvariable來記錄每一次訓練的步長。optimizer.minimize操做用於更新系統的權重,同時增長步長。

# Create a variable to track the global step.
global_step = tf.Variable(0, name = 'global_step', trainable =False)
# Use the optimizer to apply the gradients that minimize the loss
# (and also increment the global step counter) as a single training step.
train_op = optimizer.minimize(loss, global_step=global_step)

4、訓練模型

  當第三步中的Graph構造完成以後,就能夠迭代的訓練和評估模型了。

一、Graph

  在run_training()方法的最前面,使用一個with命令代表全部的操做都要與tf.Graph的默認全局graph相關聯。

with tf.Graph().as_default():

  tf.Graph表示須要在一塊兒運行的操做集合。在大多數狀況下,TensorFlow使用一個默認的graph就已經夠用了。

二、Session

  接下來就須要爲應用運行準備環境了。在TensorFlow中使用的是Session

sess = tf.Session()

  另外,除了按上面這行代碼生成sess對象外,還可使用with命令生成,以下所示,

with tf.Session() as sess:

  在得到sess對象後,首先能夠將以前定義的variable進行初始化,

init = tf.initialize_all_variables()
sess.run(init)

三、循環訓練

  初始化以後就能夠開始循環訓練模型了。
  能夠經過以下代碼實現一個最簡單的訓練循環,在這個循環中能夠控制每次循環的步長。

for step in xrange(FLAGS.max_steps):
    sess.run(train_op)

  可是在本教程中的例子比較複雜。這是由於必須把輸入的數據根據每一步的狀況進行切分,替換到以前的placeholder處。具體能夠繼續看如下部分。

四、向Graph輸入數據

  TensorFlow的feed機制能夠在應用運行時向Graph輸入數據。在每一步訓練過程當中,首先會根據訓練數據生成一個feed dictionary,這裏面會包含本次循環中使用到的訓練數據集。

feed_dict = fill_feed_dict(data_sets.train, images_placeholder, labels_placeholder)

  fill_feed_dict方法以下,每次從訓練數據集中根據batch_size取出指定數量的images_feedlabels_feed,而後以images_pllabels_plkey存入字典中。

def fill_feed_dict (data_set, images_pl, labels_pl):
  images_feed, labels_feed = data_set.next_batch(FLAGS.batch_size,
                                                 FLAGS.fake_data)
  feed_dict = {
      images_pl: images_feed,
      labels_pl: labels_feed,
  }
  return feed_dict

五、檢查狀態

  接下來以上面獲取到的每一個batch的數據開始執行訓練過程。

for step in xrange(FLAGS.max_steps):
    feed_dict = fill_feed_dict(data_sets.train,
                              images_placeholder,
                              labels_placeholder)
    _, loss_value = sess.run([train_op, loss],
                            feed_dict=feed_dict)

  在這裏傳入train_oploss後,sess.run方法返回一個包含兩個Tensortuple對象。因爲train_op並無返回值,因此只記錄loss的返回值loss_value

  假設訓練過程很正常,那麼每過100次訓練將會打印一次當前的loss值,

if step % 100 == 0 :
  print ('Step %d: loss = %.2f (%.3f sec)' % (step, loss_value, duration))

六、狀態可視化

  在上面每隔100次打印一次loss值以外,還有兩個操做將當前的loss值寫入到事件文件中,供TensorBoard做展現用。

summary_str = sess.run(summary, feed_dict=feed_dict)
  summary_writer.add_summary(summary_str, step)
  summary_writer.flush()

七、設置檢查點

  在TensorFlow中使用tf.train.Saver將訓練好的模型進行保存。

saver = tf.train.Saver()

  在循環訓練過程當中,saver.save()方法會按期執行,用於將模型當前狀態寫入到檢查點文件中。

checkpoint_file = os.path.join(FLAGS.log_dir , 'model.ckpt')
saver.save(sess, checkpoint_file, global_step =step)

  若是須要使用到該檢查點文件中保存的模型時,可使用saver.restore()方法進行加載,

saver.restore(sess, FLAGS.train_dir)

5、評估模型

  在每次保存檢查點文件時,會同時計算此時模型在訓練數據集,檢驗數據集和測試數據集上的偏差。

print('Training Data Eval:')
do_eval(sess,
        eval_correct,
        images_placeholder,
        labels_placeholder,
        data_sets.train)
# Evaluate against the validation set.
print ('Validation Data Eval:')
do_eval(sess,
        eval_correct,
        images_placeholder,
        labels_placeholder,
        data_sets.validation)
# Evaluate against the test set.
print ('Test Data Eval:')
do_eval(sess,
        eval_correct,
        images_placeholder,
        labels_placeholder,
        data_sets.test)

一、創建評估Graph

  注意上面代碼中的do_eval方法,以及該方法的eval_correct參數。eval_correct操做是在循環開始前就已經定義好了的。

eval_correct = mnist.evaluation(logits, labels_placeholder)

  這個evaluation從參數上看是用於比較預測值與真實值直接的差別。

def evaluation (logits, labels):
  correct = tf.nn.in_top_k(logits, labels, 1)
  return tf.reduce_sum(tf.cast(correct, tf.int32))

  返回一個長度爲batch_sizetensor,若是預測值與真實值相同則爲true,不然爲false

二、評估模型輸出

  最後,在do_eval方法中,處理該偏差並輸出。相似於模型訓練過程當中,這裏也建立一個feed_dict對象,在給定的數據集上調用sess.run方法,計算預測值中有多少與實際值相一致。

for step in xrange(steps_per_epoch):
  feed_dict = fill_feed_dict(data_set,
                             images_placeholder,
                             labels_placeholder)
  true_count += sess.run(eval_correct, feed_dict =feed_dict)

  最後,將預測正確的記錄數與當前的總數據數進行比較,獲得本次的預測精度。

precision = float(true_count) / num_examples
print ('Num examples: %d Num correct: %d Precision @ 1: %0.04f' %
      (num_examples, true_count, precision))
相關文章
相關標籤/搜索