在《手寫數字識別——手動搭建全鏈接層》一文中,咱們經過機器學習的基本公式構建出了一個網絡模型,其實現過程毫無疑問是過於複雜了——不得不考慮諸如數據類型匹配、梯度計算、準確度的統計等問題,可是這樣的實踐對機器學習的理解是大有裨益的。在大多數狀況下,咱們仍是但願能多簡單就多簡單地去搭建網絡模型,這同時也算對得起TensorFlow這個強大的工具了。本節,仍是以手寫數據集MNIST爲例,利用TensorFlow2.0的keras高層API重現以前的網絡。html
關於這個過程,與上節講過的相似,就再也不贅述了。須要提一點的就是,爲了程序的整潔,將數據類型的轉換過程單獨寫成一個預處理函數preprocess,經過Dataset對象的map方法應用該預處理函數。整個數據導入與預處理代碼以下:python
import tensorflow as tf from tensorflow.keras import datasets,optimizers,Sequential,metrics,layers # 改變數據類型 def preprocess(x,y): x = tf.cast(x,dtype=tf.float32)/255-0.5 x = tf.reshape(x,[-1,28*28]) y = tf.one_hot(y, depth=10) y = tf.cast(y, dtype=tf.int32) return x,y #60k 28*28 (train_x,train_y),(val_x,val_y) = datasets.mnist.load_data() #生成Dataset對象 train_db = tf.data.Dataset.from_tensor_slices((train_x,train_y)).shuffle(10000).batch(256) val_db = tf.data.Dataset.from_tensor_slices((val_x,val_y)).shuffle(10000).batch(256) #預處理,對每一個數據應用preprocess train_db = train_db.map(preprocess) val_db = val_db.map(preprocess)
對於全鏈接層,keras提供了layers.Dense(units,activation)接口,利用它能夠創建一層layer,多層堆疊放入keras提供的Sequential容器中,就造成了一個網絡模型。在Dense的參數中,units決定了這層layer含有的神經元數量,activation是激活函數的選擇。同以前的網絡同樣,咱們的網絡傳播能夠看作是:input(784 units)->layer1(256 units)->ReLu->layer2(128 units)->ReLu->output(10 units)。所以,在Sequential容器中定義後三層,activation指定爲ReLu,而輸入層須要經過build時候指定input_shape來告訴網絡輸入層的神經元數量。構建的代碼以下,經過summary方法能夠打印網絡信息。git
#網絡模型 model = Sequential([ layers.Dense(256,activation=tf.nn.relu), layers.Dense(128,activation=tf.nn.relu), layers.Dense(10), ]) #input_shape=(batch_size,input_dims) model.build(input_shape=(None,28*28)) model.summary()
模型的訓練最重要的就是權重更新和準確度統計。keras提供了多種優化器(optimizer)用於更新權重。優化器實際就是不一樣的梯度降低算法,緩解了傳統梯度降低可能沒法收斂到全局最小值的問題。在上一節中就稍加討論了三種。這裏就簡單對比一下一些優化器,至於詳細的區別從此有時間再寫篇隨筆專門討論:算法
鑑於以上對比,此處選用Adam做爲優化器,並採用其默認參數。api
除了梯度降低,還須要考慮的是Loss的計算方法。以前,咱們採用的是預測機率與實際值的差平方的均值,專業名稱應該是歐幾里得損失函數。其實,這是個錯誤,歐幾里得損失函數適用於二元分類,多元分類應該採用交叉熵損失函數。有時候針對多元函數,咱們會很不自覺地想把輸出層歸一化,因而會在輸出層以後,交叉熵計算前先softmax一下。可是因爲softmax是採用指數形式進行計算的,若是輸出各種機率相差較大,則大機率在歸一化後幾乎爲1,小几率歸一化以後幾乎爲0。爲了不這一問題,一般是去掉softmax,在交叉熵函數tf.losses.CategoricalCrossentropy的參數中指from_logits=True。網絡
Loss函數和優化器配置均可以經過compile方法指定,同時,還能夠指定metrics列表來決定須要自動計算的信息,如準確度。機器學習
經過fit方法能夠傳入訓練數據和測試數據。代碼以下:函數
#配合Adam優化器、交叉熵Loss函數、metrics列表 model.compile(optimizer=optimizers.Adam(), loss=tf.losses.CategoricalCrossentropy(from_logits=True), metrics=['accuracy']) #數據傳入,迭代10次train_db,每迭代1次,計算一次測試數據集準確度 model.fit(train_db,epochs=10,validation_data=val_db,validation_freq=1)
以上創建的網絡模型在第一次train_db迭代完後就能夠達到0.8以上的準確度,並且這個迭代每次僅花費3秒左右。通過大約50次迭代,準確度就能夠高達0.98!而經過上一節的方式,要達到這樣的準確度,起碼得訓練半個小時。這其中最主要的差異就在於梯度降低算法的優化。工具
1 import tensorflow as tf 2 from tensorflow.keras import datasets,optimizers,Sequential,metrics,layers 3 4 # 改變數據類型 5 def preprocess(x,y): 6 x = tf.cast(x,dtype=tf.float32)/255-0.5 7 x = tf.reshape(x,[-1,28*28]) 8 y = tf.one_hot(y, depth=10) 9 y = tf.cast(y, dtype=tf.int32) 10 return x,y 11 12 #60k 28*28 13 (train_x,train_y),(val_x,val_y) = datasets.mnist.load_data() 14 15 #生成Dataset對象 16 train_db = tf.data.Dataset.from_tensor_slices((train_x,train_y)).shuffle(10000).batch(256) 17 val_db = tf.data.Dataset.from_tensor_slices((val_x,val_y)).shuffle(10000).batch(256) 18 19 #預處理,對每一個數據應用preprocess 20 train_db = train_db.map(preprocess) 21 val_db = val_db.map(preprocess) 22 23 #網絡模型 24 model = Sequential([ 25 layers.Dense(256,activation=tf.nn.relu), 26 layers.Dense(128,activation=tf.nn.relu), 27 layers.Dense(10), 28 ]) 29 #input_shape=(batch_size,input_dims) 30 model.build(input_shape=(None,28*28)) 31 model.summary() 32 33 #配合Adam優化器、交叉熵Loss函數、metrics列表 34 model.compile(optimizer=optimizers.Adam(), 35 loss=tf.losses.CategoricalCrossentropy(from_logits=True), 36 metrics=['accuracy']) 37 #數據傳入,迭代10次train_db,每迭代1次,計算一次測試數據集準確度 38 model.fit(train_db,epochs=100,validation_data=val_db,validation_freq=5)