手寫數字識別——利用keras高層API快速搭建並優化網絡模型

《手寫數字識別——手動搭建全鏈接層》一文中,咱們經過機器學習的基本公式構建出了一個網絡模型,其實現過程毫無疑問是過於複雜了——不得不考慮諸如數據類型匹配、梯度計算、準確度的統計等問題,可是這樣的實踐對機器學習的理解是大有裨益的。在大多數狀況下,咱們仍是但願能多簡單就多簡單地去搭建網絡模型,這同時也算對得起TensorFlow這個強大的工具了。本節,仍是以手寫數據集MNIST爲例,利用TensorFlow2.0的keras高層API重現以前的網絡。html

 

1、數據的導入與預處理

 


 關於這個過程,與上節講過的相似,就再也不贅述了。須要提一點的就是,爲了程序的整潔,將數據類型的轉換過程單獨寫成一個預處理函數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)

 

 

 

 

2、模型構建

 


 對於全鏈接層,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()

 

 

 

 

3、模型的訓練

 


模型的訓練最重要的就是權重更新和準確度統計。keras提供了多種優化器(optimizer)用於更新權重。優化器實際就是不一樣的梯度降低算法,緩解了傳統梯度降低可能沒法收斂到全局最小值的問題。在上一節中就稍加討論了三種。這裏就簡單對比一下一些優化器,至於詳細的區別從此有時間再寫篇隨筆專門討論:算法

  1. SGD:TensorFlow2.0 SGD實際是隨機梯度降低+動量的綜合優化器。隨機梯度降低是每次更新隨機選取一個樣本計算梯度,這樣計算梯度快不少,但怕大噪聲;動量是在梯度降低的基礎上,累計歷史梯度信息加速梯度降低,這是由於一方面它想水稀釋牛奶同樣,能減少隨機梯度降低對噪聲的敏感度,另外一方面動量賦予降低以慣性,能夠預見梯度變化。這優化器實話說讓我聯想到了PID控制。SGD須要指定學習率和動量大小。通常地,動量大小設置爲0.9。
  2. Adagrad:採用自適應梯度的優化器。所謂自適應梯度,就是根據參數的頻率,對每一個參數應用不一樣的學習速率。可是該算法在迭代次數變得很大時,學習速率會變得很小,致使不能繼續更新。Adagrad要求指定初始化的學習速率、累加器初始值和防止分母爲0的偏置值。
  3. Adadelta:採用自適應增量的優化器。解決了adagrad算法學習速率消失的問題。Adagrad要求指定初始化的學習速率、衰減率和防止分母爲0的偏置值。這個衰減率跟動量差很少,通常也指定爲0.9。
  4. RMSprop:相似於Adadelta。
  5. Adam:採用梯度的一階和二階矩來估計更新參數。它結合了Adadelta和RMSprop的優勢。能夠說,深度學習一般都會選擇Adam優化器。TensorFlow中,Adam優化器須要指定4個參數,但經驗證實,它的默認參數能表現出很好的效果。

鑑於以上對比,此處選用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!而經過上一節的方式,要達到這樣的準確度,起碼得訓練半個小時。這其中最主要的差異就在於梯度降低算法的優化。工具

 

 

 

 

4、完整代碼

 


 

 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)
相關文章
相關標籤/搜索