提高模型效能,你可以嘗試這幾招
在 ofollow,noindex">EZDL到底怎樣,試試看… 一文中,我嘗試了百度推出的線上人工智慧設計平臺EZDL,其願景是 任何人不用編寫一行程式碼就可以輕鬆地構建、設計和部署人工智慧(AI)模型 。從試用效果上看,確實不需要編寫一行程式碼,也不需要什麼人工智慧知識。但對於一名程式員而言,將人工智慧包裝到一個黑盒子中,而自己毫無掌控感,總有那麼一點不踏實。
如果是自己動手構建模型、訓練、預測,哪些地方是我們可以掌控的呢?本文延續 EZDL到底怎樣,試試看… 一文中最後提出的一個問題: 模型的準確率為88.82%,我要提高準確率該怎麼做? ,來談談提高模型效能,我們能夠採取哪些措施。在 一步步提高手寫數字的識別率 系列文章中,我有簡單的談到如何優化模型,這篇文章將更進一步探討優化模型的方法。
我們還是以手寫數字識別為例,程式碼改為使用keras框架實現,這裡不貼程式碼,有興趣的話請至我的github: https://github.com/mogoweb/aiexamples 檢視,本文的示例程式碼位於keras/multi_layer_perceptron目錄下。
手寫數字識別最簡單的實現演算法是採用邏輯迴歸,因為是多分類問題,最後的輸出使用softmax代替sigmoid。當然,你也可以把它看做僅有一層的簡單神經網路,程式碼請檢視如下連結:
https://github.com/mogoweb/aiexamples/blob/master/keras/multi_layer_perceptron/mlp.py
經過200次迭代,訓練結束後,模型在訓練集上的準確率為92.36%,驗證集上的準確率為92.27%,測試集上的準確率為92.22%。這意味著10個手寫數字中只有不到一個沒有被正確識別,一個不錯的起點。
下面以此作為基線精度,比較不同的優化方法對效能提升的效果。
增加隱藏層
很自然的,我們可以想到第一個改進方法,為模型新增更多的層:
NB_CLASSES = 10# 輸出類別數量 N_HIDDEN = 128 # X_train是60000個28*28的資料,窄化為60000*784 RESHAPE = 784 model = Sequential() model.add(Dense(N_HIDDEN, input_shape=(RESHAPE, ))) model.add(Activation('relu')) model.add(Dense(N_HIDDEN)) model.add(Activation('relu')) model.add(Dense(NB_CLASSES)) model.add(Activation('softmax'))
完整程式碼請參閱:
https://github.com/mogoweb/aiexamples/blob/master/keras/multi_layer_perceptron/mlp_v2.py
增加的中間層稱為隱藏層(hidden layer),這裡只添加了一個具有N_HIDDEN個神經元並使用ReLU啟用函式的全連線層(Dense)。增加隱藏層,迭代20次之後,訓練集上的準確率即可達到94.50%,驗證集上為94.63%,測試集上為94.41%。雖然從準確率上看只提高了2.2%,但迭代次數可以大大減少。實際上如果同樣迭代200次,準確率還可以提升。
那是不是我們增加更多的層,得到的準確率就會更高呢?事實上並非如此,經過嘗試,比如在隱藏層數為5時,在訓練集、驗證集和測試集上的準確率分別為96.5%、95.99%、96.05%,而隱藏層數增加到10時的準確率依次為95.41%、95.47%、95.14%,準確率反而有所下降。所以神經網路的層數並非越多越好,層數過多,對提升準確率並沒有什麼幫助,由此還有可能帶來模型複雜、訓練時間增加等不良後果。
增加神經元的數量
從上面可以知道,適當增加隱藏層可以提升準確率,那增加神經元的數量,是否可以提升準確率?讓我們以資料說話:
從圖中可以看出,神經元數量從32增加到128,準確率有非常明顯的提升,但再往上增加神經元的數量,對準確率的提升就不那麼明顯了。與此同時,我們也需要了解到,增加模型的複雜性,執行時間也顯著增加,因為有更多的引數需要優化。
這幅圖顯示了神經元數量與訓練引數數量之間的關係。
從上圖可以看到,隨著神經元的增多,每次迭代所需的時間大幅增長。
小結一下,適當增加神經元的數量,對準確率提升有幫助,但也不是越大越好。
使用dropout策略
簡單說,dropout策略就是隨機丟棄一些神經元節點,不參與計算,為什麼這種策略能夠奏效,在Andrew NG的 改善深層神經網路:超引數除錯、正則化以及優化 課程中有很清晰的講解:
在keras中實現dropout策略非常簡單,只需在隱藏層後面增加一個Dropout層:
model = Sequential() model.add(Dense(N_HIDDEN, input_shape=(RESHAPE, ))) model.add(Activation('relu')) model.add(Dropout(DROPOUT)) model.add(Dense(N_HIDDEN)) model.add(Activation('relu')) model.add(Dropout(DROPOUT)) model.add(Dense(NB_CLASSES)) model.add(Activation('softmax'))
進行20次迭代,訓練集上的準確率91.54%,驗證集上為94.48%,測試集上為94.25%。注意這裡訓練集上的準確率低於測試集上的,說明訓練的輪次不夠。將訓練輪次增加至250,準確率資料依次為98.1%、97.73%和97.7%。
由於引入了dropout策略,需要增加訓練輪次,當然我們不能無限增加訓練輪次,因為訓練輪次增加,意味著訓練時間的增加,還是用資料說話:
從圖中可以看到,兩條曲線在約250輪時相交,而這一點之後就有必要進一步訓練了。
選擇不同的優化器
在上面的程式碼中,我們使用了SGD優化器,SGD稱為隨機梯度下降(Stochastic Gradient Descent,SGD)。除了SGD,還有RMSprop和Adam這兩種更先進的優化技術,它們引入了動量(速度分量)的概念,當然實現上更加複雜。不過在keras中,只是一行程式碼的事情:
OPTIMIZER = RMSprop()
或
OPTIMIZER = Adam()
這是使用RMSprop優化器的曲線圖,可以看到RMSprop比SGD快,在20次迭代後,在訓練集上達到97.97%的準確率,驗證集上97.59%,測試集上為97.84%。
這是使用Adam優化器的曲線圖,效果更好一些,訓練20輪之後,在訓練集上的準確率達到了98.28%,驗證集上達到了98.03%,測試集上達到了97.93%。
調整批次大小(BATCH_SIZE)
讓我們修改一下BATCH_SIZE的大小,然後看看對準確率有和影響:
如圖所示,BATCH_SIZE需要選擇一個合適的值,對於本例而言,最優的準確率在BATCH_SIZE=128時取得。
採用更合適的模型結構
在 一步步提高手寫數字的識別率(3) 中,我們提到了一種提升手寫數字識別率的模型:卷積神經網路CNN。對於影象相關的神經網路,通常卷積神經網路可以取得比全連線網路更好的效果,而對於文字處理、語音識別等,則迴圈神經網路RNN更加有效。
總結
本文僅僅從工程的角度探討了如何提高模型效能,並給出了示例程式碼,在實際專案中,關於模型調優是一個很複雜的工程,需要從很多方面考量。本文也沒有對其中涉及的理論有過多的深入,有興趣的朋友推薦大家看一看Andrew NG的深度學習課程 改善深層神經網路:超引數除錯、正則化以及優化 ,在網易雲課堂上是免費的課程。
參考
-
Keras深度學習實戰,Antonio Gulli/Sujit Pal,人民郵電出版社
-
改善深層神經網路:超引數除錯、正則化以及優化,Andrew NG
往期回顧