在《手寫數字識別——手動搭建全連接層》一文中,我們通過機器學習的基本公式構建出了一個網路模型,其實作程序毫無疑問是過于復雜了——不得不考慮諸如資料型別匹配、梯度計算、準確度的統計等問題,但是這樣的實踐對機器學習的理解是大有裨益的,在大多數情況下,我們還是希望能多簡單就多簡單地去搭建網路模型,這同時也算對得起TensorFlow這個強大的工具了,本節,還是以手寫資料集MNIST為例,利用TensorFlow2.0的keras高層API重現之前的網路,
一、資料的匯入與預處理
關于這個程序,與上節講過的類似,就不再贅述了,需要提一點的就是,為了程式的整潔,將資料型別的轉換程序單獨寫成一個預處理函式preprocess,通過Dataset物件的map方法應用該預處理函式,整個資料匯入與預處理代碼如下:
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方法可以列印網路資訊,
#網路模型 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)用于更新權重,優化器實際就是不同的梯度下降演算法,緩解了傳統梯度下降可能無法收斂到全域最小值的問題,在上一節中就稍加討論了三種,這里就簡單對比一下一些優化器,至于詳細的區別今后有時間再寫篇隨筆專門討論:
- SGD:TensorFlow2.0 SGD實際是隨機梯度下降+動量的綜合優化器,隨機梯度下降是每次更新隨機選取一個樣本計算梯度,這樣計算梯度快很多,但怕大噪聲;動量是在梯度下降的基礎上,累計歷史梯度資訊加速梯度下降,這是因為一方面它想水稀釋牛奶一樣,能減小隨機梯度下降對噪聲的敏感度,另一方面動量賦予下降以慣性,可以預見梯度變化,這優化器實話說讓我聯想到了PID控制,SGD需要指定學習率和動量大小,一般地,動量大小設定為0.9,
- Adagrad:采用自適應梯度的優化器,所謂自適應梯度,就是根據引數的頻率,對每個引數應用不同的學習速率,但是該演算法在迭代次數變得很大時,學習速率會變得很小,導致不能繼續更新,Adagrad要求指定初始化的學習速率、累加器初始值和防止分母為0的偏置值,
- Adadelta:采用自適應增量的優化器,解決了adagrad演算法學習速率消失的問題,Adagrad要求指定初始化的學習速率、衰減率和防止分母為0的偏置值,這個衰減率跟動量差不多,一般也指定為0.9,
- RMSprop:類似于Adadelta,
- Adam:采用梯度的一階和二階矩來估計更新引數,它結合了Adadelta和RMSprop的優點,可以說,深度學習通常都會選擇Adam優化器,TensorFlow中,Adam優化器需要指定4個引數,但經驗證明,它的默認引數能表現出很好的效果,
鑒于以上對比,此處選用Adam作為優化器,并采用其默認引數,
除了梯度下降,還需要考慮的是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=https://www.cnblogs.com/kensporger/p/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=https://www.cnblogs.com/kensporger/p/val_db,validation_freq=5)
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/51609.html
標籤:其他
