不知不覺寫到了第七篇,理一下思路:html
DNN/CNN都是目前機器學習領域最經常使用的算法,學習了這些例子,算是前進了一大步。
可是從產品的觀念看,咱們還有不少事情要作,好比如何從算法成爲一個程序?程序如何調試?技術如何產品化?
下面咱們就說一說這方面的問題。前端
TensorFlow是一個快速迭代中的產品,欣欣向榮的同時,做爲嚐鮮者,你須要忍受API不斷的變動。
有些朋友的作法是,下載一套就一直使用,輕易不升級。並且你看python的包管理工具pip也是推薦這種模式的,pip不多像brew/apt這些包管理工具同樣每次都提醒你升級新的軟件包。
兩種狀況下,我也會推薦這種方式:python
若是是以學習爲目的,我以爲按期關注官方網站、官方的文檔,根據本身的進度適時更新仍是很重要的。
從個人體驗上,TensorFlow對於版本的更新對API的影響控制的仍是很是好的。其實前面所講解的那些例子,不少來自於0.7.x版本的TensorFlow,基本上不加修改或者不多修改就順利運行在當前1.4.x的TensorFlow中。
這一點可比革命完全的swift強多了,網上有一個很著名的梗:曾經我覺得看一本《Swift 從入門到精通》就能夠了,誰知道我還得看《Swift 2.0 從入門到精通》、《Swift 3.0 從入門到精通》,《Swift 4.0 從入門到精通》。
從這一篇開始說這個問題主要緣由也是,其實TensorFlow在主要的算法部分在各版本保持了很好的一致性。而在周邊的功能部分變化仍是比較大的,好比說XLA、對GPU的支持、整合Keras等特徵。因此我建議開發使用的版本至少是選用TensorFlow 1.x以後的版原本入手。git
在第四篇中咱們介紹了一個最簡單的機器學習算法,主體是線性迴歸方程接softmax分類。
源碼來自於老版本的TensorFlow,在最新版本中這個源碼作了修改。增長了程序結構方面的考慮。
沒有在一開始就講解新的版本,你看了下面的源碼就知道了。跟算法無關的部分太多了,實質上提升了入門的門檻。而如今咱們掌握了基本的算法,再回頭來看外圍的結構部分,理解起來就很快了。算法
#!/usr/bin/env python # -*- coding=UTF-8 -*- # 注意以上部分官方本來是沒有的,其中直接執行是爲了使用更方便, # 否則須要用這種方式執行: python mnist_softmax.py # 字體編碼是由於須要添加中文註釋,不然執行時會報錯 # ============================================================================== # Copyright 2015 The TensorFlow Authors. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== """A very simple MNIST classifier. See extensive documentation at https://www.tensorflow.org/get_started/mnist/beginners """ #下面三行是爲了利用python3中的一些先進特徵而添加, #實際在本代碼中並無使用這些特徵,所以技術上說是能夠屏蔽的, #但無論是否使用,每次引入避免由於習慣引發的低級錯誤也是推薦的, #由於python大多當作腳運行,極可能執行到的時候纔會報錯。 from __future__ import absolute_import from __future__ import division from __future__ import print_function #引入命令行參數解析庫 import argparse import sys #全部的示例,若是使用官方推薦的方式安裝tensorflow包, #都應當已經已經安裝在本地,好比mac上路徑是: #/usr/local/lib/python2.7/site-packages/tensorflow/examples/tutorials/mnist #因此下面這個是直接從系統包中引用數據準備腳本 from tensorflow.examples.tutorials.mnist import input_data import tensorflow as tf #這個變量將用來保存命令行參數,首先清空 #在這裏聲明是爲了成爲全局變量,能夠直接在函數中調用 FLAGS = None #主程序,其實python做爲重要的腳本語言, #並非必須定義主函數,但顯然這種方式讓程序更規範 def main(_): # Import data #下面的算法部分基本不須要重複解釋,能夠看之前版本的註釋 #FLAGS.data_dir是從命令行傳遞來的參數, #表明數據目錄 mnist = input_data.read_data_sets(FLAGS.data_dir) # Create the model x = tf.placeholder(tf.float32, [None, 784]) W = tf.Variable(tf.zeros([784, 10])) b = tf.Variable(tf.zeros([10])) y = tf.matmul(x, W) + b # Define loss and optimizer y_ = tf.placeholder(tf.int64, [None]) # The raw formulation of cross-entropy, # # tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(tf.nn.softmax(y)), # reduction_indices=[1])) # # can be numerically unstable. # # So here we use tf.losses.sparse_softmax_cross_entropy on the raw # outputs of 'y', and then average across the batch. #交叉熵的計算,系統已經有內置的函數, #不須要本身計算了,對比原來的源碼能夠看一下, #上面英文也寫出了本身計算的缺陷 #注意參數:labels表示認爲標註的正確值,logits是模型計算出的值 cross_entropy = tf.losses.sparse_softmax_cross_entropy(labels=y_, logits=y) train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy) sess = tf.InteractiveSession() tf.global_variables_initializer().run() # Train for _ in range(1000): batch_xs, batch_ys = mnist.train.next_batch(100) sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys}) # Test trained model #注意這裏的比較,y_沒有跟之前版本同樣作argmax, #由於y_的值mnist.test.labes已是數字自己, #而不是原來表明10個分類的一維數組, #這個是由上面read_data_sets函數中one_hot=True參數決定的, #沒有這個參數表明直接讀數值數據,實際上在下載的數據包中就是數值 correct_prediction = tf.equal(tf.argmax(y, 1), y_) accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) print(sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels})) if __name__ == '__main__': #使用引入的argparse庫解析命令行參數 parser = argparse.ArgumentParser() #本腳本只支持一個參數,用於指定測試數據集文件夾 #默認的文件夾路徑作了修改,指向本地已有的數據集, #省得每次啓動時用參數從新指定 parser.add_argument('--data_dir', type=str, default='MNIST_data', help='Directory for storing input data') #解析的參數存入FLAGS,注意這是全局變量 FLAGS, unparsed = parser.parse_known_args() #tf的架構定義, #包裝了main主函數,熟悉c語言之類的用戶看起來確定更親切 tf.app.run(main=main, argv=[sys.argv[0]] + unparsed)
粗看起來源碼比咱們最先看到的複雜了不少是吧?其實有效的代碼並無多少,這裏作幾個解釋:express
前面的例子咱們體會很深了,隨着算法的複雜度提升,訓練所耗費的時間愈來愈長。這還只是10M左右的數據集和一個小的練習。在大型的系統中,可能須要一個集羣的工做環境作幾周的運算。
在真正投產的時候,實際就只是用訓練的數據配合預測部分的代碼完成工做便可。這就須要把訓練數據保存下來。
TensorFlow的保存、恢復很是容易,首先要生成一個保存器:apache
filesaver = tf.train.Saver()
隨後在要保存數據的位置,把數據保存到文件中:編程
datafile='data/softmax_data.tfdata' filesaver.save(sess,datafile)
在須要用到數據的位置,再把數據還原回來,一般產品化的時候,都是利用這個還原來替代大量長時間的運算,而且也保護了真正重要的數據:小程序
datafile='data/softmax_data.tfdata' filesaver.restore(sess,datafile)
應用到程序中,程序的主要變更通常是結構上的。或者訓練、預測分紅兩個程序。或者用開關判斷分紅兩個部分。下面就用本篇上面的代碼做爲例子添加上數據保存、恢復的功能:swift
#!/usr/bin/env python # -*- coding=UTF-8 -*- # ============================================================================== # Copyright 2015 The TensorFlow Authors. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== """A very simple MNIST classifier. See extensive documentation at https://www.tensorflow.org/get_started/mnist/beginners """ from __future__ import absolute_import from __future__ import division from __future__ import print_function import argparse import sys,os from tensorflow.examples.tutorials.mnist import input_data import tensorflow as tf FLAGS = None #數據文件保存的位置 datafile='data/softmax_data.tfdata' #增長一個函數用於判斷數據文件是否已經存在 def datafile_exist(): #tf寫出的數據文件實際是分了幾部分, #咱們判斷其中的索引文件是否存在 #假設索引文件存在,就有完整的數據文件 return os.path.exists(datafile+".index") def main(_): # Import data mnist = input_data.read_data_sets(FLAGS.data_dir) # Create the model x = tf.placeholder(tf.float32, [None, 784]) W = tf.Variable(tf.zeros([784, 10])) b = tf.Variable(tf.zeros([10])) y = tf.matmul(x, W) + b # Define loss and optimizer y_ = tf.placeholder(tf.int64, [None]) cross_entropy = tf.losses.sparse_softmax_cross_entropy(labels=y_, logits=y) train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy) filesaver = tf.train.Saver() sess = tf.InteractiveSession() tf.global_variables_initializer().run() if FLAGS.train or (not datafile_exist()): #若是命令行指定訓練模式,或者數據文件根本不存在則執行訓練流程 # Train for _ in range(1000): batch_xs, batch_ys = mnist.train.next_batch(100) sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys}) print("Training finished, data write to file.") #訓練結束,寫出數據 filesaver.save(sess,datafile) # Test trained model correct_prediction = tf.equal(tf.argmax(y, 1), y_) accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) print(sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels})) if (not FLAGS.train) and datafile_exist(): #若是已經存在數據文件,而且沒有要求強行從新訓練,則恢復文件 print("Restore data...") #恢復數據 filesaver.restore(sess,datafile) # Test trained model correct_prediction = tf.equal(tf.argmax(y, 1), y_) accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) print(sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels})) if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('--data_dir', type=str, default='MNIST_data', help='Directory for storing input data') #添加了一個參數,用於強制程序運行在訓練數據狀態 #使用時,若是添加-t或者--train參數執行,則運行在訓練模式 #若是沒有參數,則判斷數據文件是否存在,已經存在直接進入測試模式 #沒有存在,則先訓練,寫出數據集,再恢復數據集作一次測試 parser.add_argument('-t','--train', action='store_true',default=False, help='Force do train') FLAGS, unparsed = parser.parse_known_args() tf.app.run(main=main, argv=[sys.argv[0]] + unparsed)
在機器學習領域,學習算法作練習問題不大,實際應用,不管性能多強的電腦你都老是感受慢。
JIT XLA是TensorFlow推出的一項新特徵,就是運行時編譯器。主要功能是把數學模型部分進行運行時編譯,融合可組合的op以提升性能,內存需求也會下降不少。
XLA全稱:Accelerated Linear Algebra,也就是加速線性代數。
使用XLA很是容易,只要在Session初始化的時候添加以下代碼:
config = tf.ConfigProto() jit_level = tf.OptimizerOptions.ON_1 config.graph_options.optimizer_options.global_jit_level = jit_level with tf.Session(config=config) as sess: ...
Session的運行就會在XLA方式之下。實際上XLA的運行須要CPU或者GPU的支持。在個人老電腦上,雖然硬件不支持XLA的加速,但內存的需求也下降了大概一倍左右,可說效果明顯。
不一樣於傳統程序的DEBUG,機器學習類的程序,在程序邏輯上大多不會犯什麼大錯誤,畢竟整個流程很簡單。
大多的過程其實是在「調優」而不是「調試」。在調優中須要關注到的,主要是數據方面的問題。而數據無論原來是什麼格式,進入機器學習系統後,每每都已經變成了抽象的矩陣。因此整個調優過程每每充滿着痛苦和無力感。
TensorBoard是一個官方出品的優秀工具,能夠經過讀取事件文件的形式把數據圖形化的顯示出來,從而大幅下降調優難度。
使用TensorBoard自己很簡單,是一個B/S結構的程序,在瀏覽器中就能夠操做全部功能。比較麻煩的是生成事件文件,須要在程序代碼中嵌入不少用於監控的語句,用於輸出數據留待tensorboard分析。
官方有一個寫的很好的樣例:mnist_with_summaries.py
,可是這個源碼看上去偏於複雜。咱們仍是以上面的softmax分類識別的源碼爲例,對此作一個介紹。有了這個基礎,官方的樣例留給讀者在讀完這裏的內容以後,本身嘗試去分析。
生成事件數據文件只須要兩行語句:
#在Session創建後執行: train_writer = tf.summary.FileWriter('logs/train', sess.graph) #在Session關閉前執行: train_writer.close()
程序執行完成後,將在./logs/train/
目錄下生成事件數據文件。隨後能夠用tensorboard來分析和顯示數據文件:
> tensorboard --logdir=logs/ TensorBoard 0.4.0rc3 at http://127.0.0.1:6006 (Press CTRL+C to quit)
命令中指定數據文件路徑只須要指定到上一級便可,由於tensorbaord支持多組數據文件對比顯示,這個咱們後面再介紹。
查看圖形化的分析結果可使用瀏覽器訪問:http://127.0.0.1:6006。
可以顯示出來完整的數學模型,只是圖的可讀性感受仍是比較差,大多節點要思考一下才能明白表明的是什麼。並且除了圖基本也沒有提供其它參數,難以作更深刻的分析。
咱們還能夠繼續定義一些輸出來改善數學模型的顯示:
咱們能夠監控常量的狀態:
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) #第一個參數是指定顯示時圖表的名字,第二個參數是監控的值 tf.summary.scalar('accuracy', accuracy)
能夠監控變量的狀態:
cross_entropy = tf.losses.sparse_softmax_cross_entropy(labels=y_, logits=y) #第一個參數是指定顯示圖表的名字,第二個參數是監控的值 tf.summary.histogram('cost', cross_entropy)
能夠對節點命名以便更直觀的看到圖形化後模型的結構:
with tf.name_scope("pic_samples"): x = tf.placeholder(tf.float32, [None, 784]) with tf.name_scope("weight"): W = tf.Variable(tf.zeros([784, 10]))
注意:with tf.name_scope("pic_samples"):
這種形式,其實也是TensorFlow中的變量做用域的定義。
由於機器學習中的變量通常都佔用了比較大的空間,咱們確定但願儘量重複使用變量,因此若是在大系統中,會存在不少變量。這時候就須要有做用域在對變量作出管理。這方面的功能,初學確定用到不到,用到的時候看看手冊就夠了,這裏很少說。
咱們既然監控了變量、常量,必然須要tensorflow的運算才能獲得這些值,雖然這些值只是輸出到事件文件中的。因此記住一點,只要使用了任何的summary操做,咱們就須要在FileWriter定義的同時,定義一個運算操做,並在以後在Session.run中運算這個操做,隨後把返回的結果添加到事件文件中去,這樣才能真正把監控的值輸出到事件文件中去:
#注意這一行應當在全部須要監控的變量、常量、圖片等都設置好後,最後運行 #此語句以後定義的觀察器將都不會被輸出到事件文件。也就沒法被查看了 merged = tf.summary.merge_all() train_writer = tf.summary.FileWriter('logs/train', sess.graph) ... summary,_ = sess.run([merged,train_step], feed_dict={x: batch_xs, y_: batch_ys}) train_writer.add_summary(summary, i)
由於定義了placeholder,因此每次sess.run()都是須要喂數據的。即使咱們定義的merged這個操做並不須要數據。因此若是單獨運行這個操做,附帶喂入數據確定是很不經濟。所以一般的方法,都是跟主要的操做一塊兒運行。同時運行多個操做,而且同時獲得多個返回值的語法既是python的語言特點,也是TensorFlow支持的功能。
如今重複運行程序,獲得新的事件文件,再次啓動tensorboard而後用瀏覽器查看,咱們能夠看到更多的內容了:
請注意看兩個命名的節點,都已經有更友好的節點名了。
咱們監控的變量,能清晰的看到代價函數的值逐漸變小,表示逐漸趨於收斂。(請忽略這個粗糙示例中的抖動,這裏僅是爲了示例可視化的效果。)
其它監控的各類值基本相似,這裏就不一一貼出圖片了,建議你把源碼執行一下而後看看效果。
TensorBoard功能很是強大,不少功能超乎咱們的想象。前面咱們介紹過一個小程序,本身把輸入的數據圖形化,而後寫出到一個圖片文件。
這樣的功能,若是使用TensorBoard將更加容易,好比下面代碼監控輸入矩陣及計算的權重值,並以圖片的形式顯示出來:
x = tf.placeholder(tf.float32, [None, 784]) image_shaped_input = tf.reshape(x, [-1, 28, 28, 1]) tf.summary.image('input_images', image_shaped_input, 10) ... W = tf.Variable(tf.zeros([784, 10])) Wt=tf.transpose(W) #由於權重值跟圖片數據定義不一樣,要先轉置一下,再轉成圖片 image_shaped_weight = tf.reshape(Wt, [-1, 28, 28, 1]) tf.summary.image('weight_images', image_shaped_weight, 10)
最終生成圖片的樣子咱們前面的內容中見過,這裏也不貼圖了。
其中權重部分,由於不是[None,784]這樣的形式,而是[784,10],因此要先使用tf.transpose轉換成[10,784]的形式,再reshape成28x28的圖片,最後才能以圖片的方式顯示出來。
這一節的最後部分,把上面示例的完整代碼列出來,以供你參考實驗,由於上面講解都很詳細了,例子中就沒有加過多的註釋。請注意這個例子由於經歷了屢次的補丁和無邏輯意義的做用域定義,程序結構上比較混亂,你們在正式的項目中可千萬不要模仿。
#!/usr/bin/env python # -*- coding=UTF-8 -*- # ============================================================================== # Copyright 2015 The TensorFlow Authors. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== """A very simple MNIST classifier. See extensive documentation at https://www.tensorflow.org/get_started/mnist/beginners """ from __future__ import absolute_import from __future__ import division from __future__ import print_function import argparse import sys,os from tensorflow.examples.tutorials.mnist import input_data import tensorflow as tf FLAGS = None datafile='data/softmax_data.tfdata' def datafile_exist(): return os.path.exists(datafile+".index") def main(_): # Import data mnist = input_data.read_data_sets(FLAGS.data_dir) # Create the model with tf.name_scope("pic_samples"): x = tf.placeholder(tf.float32, [None, 784]) image_shaped_input = tf.reshape(x, [-1, 28, 28, 1]) tf.summary.image('input_images', image_shaped_input, 10) with tf.name_scope("weight"): W = tf.Variable(tf.zeros([784, 10])) Wt=tf.transpose(W) image_shaped_weight = tf.reshape(Wt, [-1, 28, 28, 1]) tf.summary.image('weight_images', image_shaped_weight, 10) b = tf.Variable(tf.zeros([10])) tf.summary.histogram('bias', b) y = tf.matmul(x, W) + b # Define loss and optimizer y_ = tf.placeholder(tf.int64, [None]) cross_entropy = tf.losses.sparse_softmax_cross_entropy(labels=y_, logits=y) tf.summary.histogram('cost', cross_entropy) train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy) filesaver = tf.train.Saver() sess = tf.InteractiveSession() tf.global_variables_initializer().run() correct_prediction = tf.equal(tf.argmax(y, 1), y_) accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) tf.summary.scalar('accuracy', accuracy) merged = tf.summary.merge_all() train_writer = tf.summary.FileWriter('logs/train', sess.graph) if FLAGS.train or (not datafile_exist()): # Train for i in range(1000): batch_xs, batch_ys = mnist.train.next_batch(100) sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys}) summary,_ = sess.run([merged,train_step], feed_dict={x: batch_xs, y_: batch_ys}) train_writer.add_summary(summary, i) # Test trained model if (i % 100 == 0): summary1,ac = sess.run([merged,accuracy], feed_dict={x: mnist.test.images, y_: mnist.test.labels}) train_writer.add_summary(summary1,i) print("Training finished, data write to file.") filesaver.save(sess,datafile) print(ac) train_writer.close() if (not FLAGS.train) and datafile_exist(): print("Restore data...") filesaver.restore(sess,datafile) # Test trained model correct_prediction = tf.equal(tf.argmax(y, 1), y_) accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) print(sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels})) if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('--data_dir', type=str, default='MNIST_data', help='Directory for storing input data') parser.add_argument('-t','--train', action='store_true',default=False, help='Force do train') FLAGS, unparsed = parser.parse_known_args() tf.app.run(main=main, argv=[sys.argv[0]] + unparsed)
注意程序運行的時候要使用-t參數,由於圖示部分的代碼主要加在了訓練環節。
讀完了這個程序,再去讀官方的示例應當容易不少,很是建議你以官方程序爲範例仔細閱讀。
做爲實踐環節的最後一部分,介紹一下一般機器學習項目的通常工做流程和團隊。
稍微成熟的公司,通常都已經有本身規範的研發流程和管理方式,此處列出的流程僅供參考。其實主要是強調算法專家的角色和數據收集的工做。這兩組人員在通常的項目中是沒有或者位置並非很重要的。可是在機器學習項目中,每每是核心部分。在圖像識別等監督學習領域,數據收集、標註成本每每佔了最主要的預算。
(待續...)
TensorBoard:可視化學習
tensorflow裏面name_scope, variable_scope等如何理解?
python argparse用法總結
谷歌官博詳解XLA:可在保留TensorFlow靈活性的同時提高效率
完整機器學習項目的工做流程
mnist_softmax_xla.py mnist_deep.py mnist_with_summaries.py