我的Keras使用總結(1)——Keras概述與常見問題整理


完整代碼及其數據,請移步小編的GitHub

  傳送門:請點擊我

  如果點擊有誤:https://github.com/LeBron-Jian/DeepLearningNote

  今天整理了自己所寫的關於Keras的博客,有沒發布的,有發布的,但是整體來說是有點亂的。上周有空,認真看了一周Keras的中文文檔,稍有心得,整理於此。這里附上Keras官網地址:

  Keras英文文檔:https://keras.io/#installationKeras

  Keras中文文檔:https://keras.io/zh/

  下面回顧一下自己以前寫的有關Keras的博客:

Python機器學習筆記:利用Keras進行分類預測

Python機器學習筆記:使用Keras進行回歸預測

  首先是上面兩篇博客,這兩篇寫出來,其實是整理筆記,單純的學習一下基礎的回歸和分類,使用Keras創建一個簡單的回歸問題的神經網絡模型和多類分類的模型。使用了最經典的回歸數據波士頓房價數據集和分類數據鳶尾花數據集。創建一個簡單的模型進行訓練。其重點是學習基礎的分類回歸思想,順便使用別人的Keras代碼進行實現。

深入學習Keras中Sequential模型及方法

  其次是這篇博客,當時學習Keras的時候對序貫模型不懂,就做了一個簡單的筆記對此進行整理。主要整理了序貫模型的函數及其參數的意義,包括使用Keras進行模型訓練的部分簡單的過程,最后整理了Keras官網文檔的幾個例子。此時也只是對Keras有個大概的了解。

Keras深度學習之卷積神經網絡(CNN)

  然后上面這篇博客還是學習卷積神經網絡,為什么上面這篇博客起這個一個怪異的名字呢,我當時知道自己不太懂,所以我希望自己懂了后回顧一下。而且這篇文章我沒有發布,我將其從隨筆改為文章,就是說是自己摘抄的筆記,希望懂后,回首一下,也算是做了個交代。

  這篇雜文主要是以學習卷積神經網絡為主,回顧了典型的CNN,常用的CNN框架,和Keras如何實現CNN等。主要實現了兩件事情,一個是使用Keras搭建了一個卷積神經網絡,一個是學習了卷積神經網絡實現的過程。內容以摘抄為主,但是摘抄的確實是好文,講解細膩,循序漸進,值得去看。

Python機器學習筆記:深入理解Keras中序貫模型和函數模型

  然后就是這篇博客了,我是先學習的sklearn,然后再學習的tensorflow,Keras。所以對照着sklearn的機器學習流程學習Keras的使用流程。然后對序貫模型和函數模型進行詳細學習和對比,最后整理了Keras如何保存模型。相對來說我的思路還是比較清晰了。會使用Keras搭建模型了。

  那么說完了這么多,為什么還要寫呢?

  其實從18年九月開始,自己的機器學習之路就開始了,當時前途一片迷茫,學到哪里算哪里。到19年一月又寫了幾篇博客,當然后面也寫了只是沒有發布而已,到現在已經是20年三月了。啰嗦這么多,就是感覺自己走了彎路,讓看到博客的人能少走點彎路,而且最主要的是自己整理回顧。

  首先說起Keras,都知道它是基於Theano和TensorFlow的深度學習庫,所以這里我們先說一說TensorFlow,上面聊過,我是先學習sklearn,再學習的TensorFlow的。通過對比sklearn的訓練過程,學習Keras的訓練過程。其實大同小異,那么同在哪里,異又在哪里?

  上面博客只是簡單的學習了一下其兩者的機器學習使用流程和區別,這里從根上回顧一下機器學習和深度學習的區別。這里內容我就不寫自己的拙見了,直接拿網友現成的東西(https://www.zhihu.com/question/53740695/answer/284428668)

1,機器學習和深度學習的總結

  首先我們看sklearn和TensorFlow的區別。 這個問題其實等價於:現在深度學習那么火,那么是否還有必要學習傳統的機器學習方法。

  理論上來說,深度學習技術也是機器學習的一個組成部分,學習其他傳統的機器學習方法對深入理解深度學習技術有很大的幫助,知道模型凸的條件,才能更好的理解神經網絡的非凸,知道傳統模型的優點,才能更好的理解深度學習並不是萬能的,也有很多問題和場景直接使用深度學習方法會遇到瓶頸和問題,需要傳統方法來解決。

  從實踐上來說,深度學習方法一般需要大量的GPU機器,工業界哪怕大公司的GPU資源也是有限的,一般只有深度學習方法效果遠好於傳統方法並且對業務提升很大的情況下,才會考慮使用深度學習方法,例如語音識別,圖像識別等任務現在深度學習方法用的比較多,而NPL領域除了機器翻譯以外,其他大部分任務仍然更常使用傳統方法,傳統方法一般有着更好的可解釋性,這對檢查調試模型也是非常有幫助的。工業上一般喜歡招能解決問題的人,而不是掌握最火技術的人,因此在了解深度學習技術的同時,學習一下傳統的方法是很有好處的。

1.1 Sklearn和TensorFlow的區別

  Scikit-learn(Sklearn)的定位是通過機器學習庫,而TensorFlow(tf)的定位主要是深度學習庫。一個顯而易見的不同:tf並未提供sklearn那種強大的特征工程,如維度壓縮,特征選擇等。

  傳統機器學習:利用特征工程(feature enginerring),人為對數據進行提煉清洗

  深度學習:利用表示學習(representation learning),機器學習模型自身對數據進行提煉

  sklearn更傾向於使用者可以自行對數據進行處理,比如選擇特征,壓縮維度,轉換格式,是傳統機器學庫,而以tf為代表的深度學習庫會自動從數據中抽取有效特征,而不需要人為的來做這件事情,因此並未提供類似的功能。

   上面這幅圖直觀的對比了我們提到的兩種對於數據的學習方法,傳統的機器學習方法主要依賴人工特征處理與提取,而深度學習依賴模型自身去學習數據的表示。

1.2  模型封裝的抽象化程度不同,給予使用者自由度不同

  sklearn中的模塊都是高度抽象化,所有的分類器基本都可以在3~5行內完成,所有的轉換器(如scaler和transformer)也有固定的格式,這種抽象化限制了使用者的自由度,但是增加了模型的效率,降低了批量化,標准化的難度。

  比如svm分類器:

clf = svm.SVC() # 初始化一個分類器
clf.fit(X_train, y_train) # 訓練分類器
y_predict = clf.predict(X_test) # 使用訓練好的分類器進行預測

  而tf不同,雖然是深度學習庫,但是它由很高的自由度。你依然可以使用它做傳統機器學習所做的事情,代碼是你需要自己實現算法。因此用tf類比sklearn不適合,封裝在tf等工具庫的Keras更像深度學習界的sklearn。

  從自由度來看,tf更高;而從抽象化,封裝程度來看,sklearn更高;從易用性角度來看,sklearn更高。

1.3 深度的群體,項目不同

    sklearn主要適合中小型的,實用機器學習項目,尤其是那種數據量不大且需要使用者手動對數據進行處理,並選擇合適模型的項目,這類項目往往在CPU上就可以完成,對硬件要求低。

  tf主要適合已經明確了解需要用深度學習,且數據處理需求不高的項目。這類項目往往數據量較大,且最終需要的精度更高,一般都需要GPU加速運算。對於深度學習做“小樣”可以在采樣的小數據集上用Keras做快速的實驗。

  下面我們看一個使用Keras搭建的網絡,代碼如下:

model = Sequential() # 定義模型
model.add(Dense(units=64, activation='relu', input_dim=100)) # 定義網絡結構
model.add(Dense(units=10, activation='softmax')) # 定義網絡結構
model.compile(loss='categorical_crossentropy', # 定義loss函數、優化方法、評估標准
              optimizer='sgd',
              metrics=['accuracy'])
model.fit(x_train, y_train, epochs=5, batch_size=32) # 訓練模型
loss_and_metrics = model.evaluate(x_test, y_test, batch_size=128) # 評估模型
classes = model.predict(x_test, batch_size=128) # 使用訓練好的數據進行預測

  不難看出,sklearn和tf還是有很大的區別,雖然sklearn中也有神經網絡模塊,但是做大型的深度學習是不可能依靠sklearn的。

1.4  sklearn和TensorFlow結合使用

  更常見的情況,可以把sklearn和tf,甚至Keras結合起來使用。sklearn肩負基本的數據清理任務,Keras用於對問題進行小規模試驗驗證想法,而tf用於在完整的數據上進行嚴肅的調參任務。

  而單獨把sklearn拿出來看的話,它的文檔做的特別好,初學者跟着看一遍sklearn支持的功能就可以大概對機器學習包括的很多內容有了基本的了解。舉個簡單的例子,sklearn很多時候對單獨的知識點有概述,比如簡單的異常檢測。因此,sklearn不僅僅是簡單的工具庫,它的文檔更像是一份簡單的新手入門指南。

  因此,以sklearn為代表的傳統機器學習庫和以tf為代表的自由靈活更具有針對性的深度學習庫都是機器學習者必須要了解的工具。

  那么如何結合sklearn庫和Keras模型做機器學習任務呢?

   Keras是Python中比較流行的深度學習庫,但是Keras本身關注的是深度學習。而Python中的scikit-learn庫是建立在Scipy上的,有着比較有效的數值計算能力。Sklearn是一個具有全特征的通用性機器學習庫,它提供了很多在深度學習中可以用到的工具,舉個例子:

  • 1,可以用sklearn中的 K-fold 交叉驗證方法來對模型進行評估
  • 2,模型參數的評估和尋找

     Keras提供了深度學習模型的簡便包裝,可以在Sklearn中被用來做分類和回歸,在本文中我們舉這么一個例子:使用Keras建立神經網絡分類器——KerasClassifier,並在scikit-learn庫中使用這個分類器對UCI的Pima Indians數據集進行分類。

  利用Keras進行分類或者回歸,主要利用Keras中兩個類,一個是KerasClassifier,另一個是KerasRegressor。這兩個類有參數build_fn。build_fn是你創建的Keras名稱,在創建一個Keras模型時,務必要把完成模型的定義,編譯和返回。在這里我們假設建立的模型叫做create_model(),則:

def create_model():
    # create model
    model = Sequential()
    model.add(Dense(12, input_dim=8, init='uniform', activation='relu'))
    model.add(Dense(8, init='uniform', activation='relu'))
    model.add(Dense(1, init='uniform', activation='sigmoid'))
    # Compile model
    model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
    return model

  將建立好的模型通過參數build_fn傳遞到KerasClassifier中,並且定義其他的參數選項nb_epoch = 150,batch_size = 10 。KerasClassifier會自動調用fit方法。

  在Sklearn中,我們使用它cross_validation的包中的StratifiedKFold來進行10折交叉驗證,使用cross_val_score來對模型進行評價。

kfold = StratifiedKFold(y=Y, n_folds=10, shuffle=True, random_state=seed)
results = cross_val_score(model, X, Y, cv=kfold)

  總結來說,機器學習和深度學習均需要學習,只會調用工具包的程序員不是好的機器學習者。

  扯了這么多,我們明白sklearn和keras結合使用,相得益彰。

2,Keras的概述

  而我之前的筆記主要的針對點就是序貫模型和卷積神經網絡這塊,那今天我們不學習這些東西。從架構上整體的了解一下Keras。

     Keras是Python中一個以CNTK、TensorFlow或者Theano為計算后台的深度學習建模環境。相對於常見的幾種深度學習計算軟件,比如TensorFlow、Theano、Caffe、CNTK、Torch等,Keras在實際應用中有如下幾個顯著的優點。Keras在設計時以人為本,強調快速建模,用戶能快速地將所需模型的結構映射到Keras代碼中,盡可能減少編寫代碼的工作量,特別是對於成熟的模型類型,從而加快開發速度。支持現有的常見結構,比如卷積神經網絡、時間遞歸神經網絡等,足以應對大量的常見應用場景。高度模塊化,用戶幾乎能夠任意組合各個模塊來構造所需的模型。

  在Keras中,任何神經網絡模型都可以被描述為一個圖模型或者序列模型,其中的部件被划分為以下模塊:神經網絡層、損失函數、激活函數、初始化方法、正則化方法、優化引擎。這些模塊可以以任意合理地方式放入圖模型或者序列模型中來構造所需的模型,用戶並不需要知道每個模塊后面的細節。這種方式相比其他軟件需要用戶編寫大量代碼或者用特定語言來描述神經網絡結構的方法效率高很多,也不容易出錯。基於Python,用戶也可以使用Python代碼來描述模型,因此易用性、可擴展性都非常高。用戶可以非常容易地編寫自己的定制模塊,或者對已有模塊進行修改或者擴展,因此可以非常方便地開發和應用新的模型與方法,加快迭代速度。能在CPU和GPU之間無縫切換,適用於不同的應用環境。當然,我們強烈推薦GPU環境。

   首先這個思維導圖是對Keras中文文檔的整體概述,也可以叫做目錄:

  上面從上面導圖我們可以直觀的看到Keras官網文檔主要分為五個方面寫:模型,后端,網絡層,網絡配置,數據預處理。模型分為序貫模型和函數式模型,我們之前學習過就不贅述了;下面利用三個思維導圖展示一下網絡配置,網絡層,數據預處理。(原圖地址:https://blog.csdn.net/sinat_26917383/article/details/72857454?locationNum=1&fps=1)

  注意:回調函數 callbacks應該是Keras的精髓。。

3,一個簡單的Keras訓練模型過程

  使用Keras訓練模型的步驟圖示如下:

  Keras的核心數據結構是model,一種組織網絡層的方式,最簡單的模型是Sequential順序模型,它由多個網絡層線性堆疊。對於更復雜的結構,你應該使用Keras函數式API,它允許構建任意的神經網絡圖。

3.1,選擇模型

  Sequential順序模型如下所示:

from keras.models import Sequential

model = Sequential()

3.2,構建網絡層

  可以簡單地使用.add()來堆疊模型:

from keras.layers import Dense

model.add(Dense(units=64, activation='relu', input_dim=100))
model.add(Dense(units=10, activation='softmax'))

3.3,編譯

  在完成了模型的構建后,可以使用.compile()來配置學習過程。編譯模型時必須指明損失函數和優化器,如果有需要的話也可以自己定制損失函數。

model.compile(loss='categorical_crossentropy',
              optimizer='sgd',
              metrics=['accuracy'])

  如果需要,我們還可以進一步的配置我們的優化器,Keras的核心原則是使事情變得相當簡單,同時又允許用戶在需要的時候能夠進行完全的控制(終控的控制是源代碼的易擴展性)。

model.compile(loss=keras.losses.categorical_crossentropy,
              optimizer=keras.optimizers.SGD(lr=0.01, momentum=0.9, nesterov=True))

3.4 訓練

  現在我們可以批量的在訓練數據上進行迭代了:

# x_train 和 y_train 是 Numpy 數組 -- 就像在 Scikit-Learn API 中一樣。
model.fit(x_train, y_train, epochs=5, batch_size=32)

  或者,你可以手動地將批次的數據提供給模型:

model.train_on_batch(x_batch, y_batch)

  只需一行代碼就能評估模型性能:

loss_and_metrics = model.evaluate(x_test, y_test, batch_size=128)

3.5 預測

  或者對新的數據生成預測:

classes = model.predict(x_test, batch_size=128)

  構建一個問答系統,一個圖形分類模型,一個神經圖靈機,或者其他的任何模型,就是這么的快。我們利用一個小的代碼展示了一個Keras完整的訓練過程,后面就不再贅述了。

3.6  保存模型結構

  如果想打印看一下模型結果圖,比如下圖:

 

   則需要使用 plot_model()函數,代碼如下:

from keras.utils.vis_utils import plot_model


# write model image
plot_model(model, to_file='lenet.jpg', show_shapes=True, show_layer_names=False)

 

4,一些Keras中常見的問題

4.1  為什么訓練誤差比測試誤差高很多?

  一個Keras的模型有兩個模式:訓練模式和測試模式。一些正則機制,如Dropout,L1/L2正則項在測試模式下將不被啟用。

  另外,訓練誤差是訓練數據每個batch的誤差的平均。在訓練過程中,每個 epoch 起始時的batch的誤差要大一些,而后面的batch的誤差要小一些。另一方面,每個 epoch 結束時計算的測試誤差是由模型在 epoch結束時的狀態決定的,這時候的網絡將產生較小的誤差。

  Tips:可以通過定義回調函數將每個 epoch的訓練誤差和測試誤差並作圖,如果訓練誤差曲線和測試誤差曲線之間有很大的空隙,說明你的模型可能有過擬合的問題。當然,這個問題與Keras無關。

4.2  在Theano和TensorFlow中如何表示一組彩色圖片的尺寸?

  Keras提供了兩套后端,Theano和TensorFlow,這是一件幸福的事,就像手里拿着面包,想蘸紅糖蘸紅糖,想蘸白糖蘸白糖。如果你從無到有搭建自己的一套網絡,則大可放心。但是如果你想使用一個已有的網絡,或把一個用 th/tf 訓練的網絡以另一種后端應用,在載入的時候你就應該特別小心了。

  Theano和TensorFlow在表示一組彩色圖片的問題上有分歧,“th”模式,也就是Theano模式會把100張 RGB 三通道的16*32(高為16 寬為32)彩色圖表示為下面這張形式(100, 3, 16, 32),Caffe采取的也是這種形式。第0個維度為樣本維,代表樣本的樹木,第一個維度是通道維,代表顏色通道數。后面兩個就是高和寬了。這張Theano 風格的數據組織方式,稱為“channels_first”, 即通道維靠前。

  而TensorFlow 的表達形式(100, 16, 32,3),即把通道維放在了最后,這張數據組織形式稱為“channels_last”。

  注意:卷積核與所使用的后端不匹配,不會報任何錯誤,因為他們的shape是完全一致的,沒有辦法能夠檢測出這種錯誤。所以在使用預訓練模型的時候,一個建議是首先找一些測試樣本,看看模型的表現是否與預計的一致,如需對卷積核進行轉換,可以使用 utils.convert_call_kernels_in_model 對模型的所有卷積核進行轉換。

4.3,模型的節點信息提取

# 節點信息提取
config = model.get_config() 
 # 把model中的信息,solver.prototxt和train.prototxt信息提取出來
model = Model.from_config(config) 
# 還回去
# or, for Sequential:
model = Sequential.from_config(config) 
# 重構一個新的Model模型,用去其他訓練,fine-tuning比較好用

  

4.4  模型概況查詢(包括權重查詢)

# 1、模型概括打印
model.summary()

# 2、返回代表模型的JSON字符串,僅包含網絡結構,不包含權值。可以從JSON字符串中重構原模型:
from models import model_from_json

json_string = model.to_json()
model = model_from_json(json_string)

# 3、model.to_yaml:與model.to_json類似,同樣可以從產生的YAML字符串中重構模型
from models import model_from_yaml

yaml_string = model.to_yaml()
model = model_from_yaml(yaml_string)

# 4、權重獲取
model.get_layer()      #依據層名或下標獲得層對象
model.get_weights()    #返回模型權重張量的列表,類型為numpy array
model.set_weights()    #從numpy array里將權重載入給模型,要求數組具有與model.get_weights()相同的形狀。

# 查看model中Layer的信息
model.layers 查看layer信息

  

4.5  當驗證集的 loss 不再下降時,如何中斷訓練?

  可以定義 EarlyStopping 來提前終止訓練。

from keras.callbacks import EarlyStopping

early_stopping = EarlyStopping(monitor='val_loss', patience=2)
model.fit(X, y, validation_split=0.2, callbacks=[early_stopping])

  可以參考 官網:回調函數

4.6  如何在每個 epoch后記錄訓練/測試的loss和正確率?

  model.fit 在運行結束后返回一個 History 對象,其中含有的 history 屬性包含了訓練過程中損失函數的值以及其他度量指標。

hist = model.fit(X, y, validation_split=0.2)
print(hist.history)

  

4.7 如何在keras中設定GPU使用的大小

  如果采用TensorFlow作為后端,當機器上有可用的GPU時,代碼會自動調用GPU進行並行計算,但是在使用keras時候會出現總是占滿GPU顯存的情況,可以通過重設backend的GPU占用情況來進行調節。

import tensorflow as tf
from keras.backend.tensorflow_backend import set_session
config = tf.ConfigProto()
config.gpu_options.per_process_gpu_memory_fraction = 0.3
set_session(tf.Session(config=config))

  需要注意的是,雖然代碼或配置層面設置了對顯存占用百分比閾值,但在實際運行中如果達到了這個閾值,程序有需要的話還是會突破這個閾值。換而言之如果跑在一個大數據集上還是會用到更多的顯存。以上的顯存限制僅僅為了在跑小數據集時避免對顯存的浪費而已。

 

4.8 如何更科學的模型訓練與模型保存

filepath = 'model-ep{epoch:03d}-loss{loss:.3f}-val_loss{val_loss:.3f}.h5'
checkpoint = ModelCheckpoint(filepath, monitor='val_loss', verbose=1, save_best_only=True, mode='min')
# fit model
model.fit(x, y, epochs=20, verbose=2, callbacks=[checkpoint], validation_data=(x, y))

  save_best_only打開之后,會如下:

ETA: 3s - loss: 0.5820Epoch 00017: val_loss did not improve

  如果val_loss 提高了就會保存,沒有提高就不會保存。

4.9,如何在keras中使用tensorboard

RUN = RUN + 1 if 'RUN' in locals() else 1   # locals() 函數會以字典類型返回當前位置的全部局部變量。

    LOG_DIR = model_save_path + '/training_logs/run{}'.format(RUN)
    LOG_FILE_PATH = LOG_DIR + '/checkpoint-{epoch:02d}-{val_loss:.4f}.hdf5'   # 模型Log文件以及.h5模型文件存放地址

    tensorboard = TensorBoard(log_dir=LOG_DIR, write_images=True)
    checkpoint = ModelCheckpoint(filepath=LOG_FILE_PATH, monitor='val_loss', verbose=1, save_best_only=True)
    early_stopping = EarlyStopping(monitor='val_loss', patience=5, verbose=1)

    history = model.fit_generator(generator=gen.generate(True), steps_per_epoch=int(gen.train_batches / 4),
                                  validation_data=gen.generate(False), validation_steps=int(gen.val_batches / 4),
                                  epochs=EPOCHS, verbose=1, callbacks=[tensorboard, checkpoint, early_stopping])

  都是在回調函數中起作用:

  • EarlyStopping patience:當early 
    (1)stop被激活(如發現loss相比上一個epoch訓練沒有下降),則經過patience個epoch后停止訓練。 
    (2)mode:‘auto’,‘min’,‘max’之一,在min模式下,如果檢測值停止下降則中止訓練。在max模式下,當檢測值不再上升則停止訓練。

  • 模型檢查點ModelCheckpoint 
    (1)save_best_only:當設置為True時,將只保存在驗證集上性能最好的模型 
    (2) mode:‘auto’,‘min’,‘max’之一,在save_best_only=True時決定性能最佳模型的評判准則,例如,當監測值為val_acc時,模式應為max,當檢測值為val_loss時,模式應為min。在auto模式下,評價准則由被監測值的名字自動推斷。 
    (3)save_weights_only:若設置為True,則只保存模型權重,否則將保存整個模型(包括模型結構,配置信息等) 
    (4)period:CheckPoint之間的間隔的epoch數

  • 可視化tensorboard write_images: 是否將模型權重以圖片的形式可視化

4.10 模型概況查詢(包括權重查詢)

# 1、模型概括打印
model.summary()

# 2、返回代表模型的JSON字符串,僅包含網絡結構,不包含權值。可以從JSON字符串中重構原模型:
from models import model_from_json

json_string = model.to_json()
model = model_from_json(json_string)

# 3、model.to_yaml:與model.to_json類似,同樣可以從產生的YAML字符串中重構模型
from models import model_from_yaml

yaml_string = model.to_yaml()
model = model_from_yaml(yaml_string)

# 4、權重獲取
model.get_layer()      #依據層名或下標獲得層對象
model.get_weights()    #返回模型權重張量的列表,類型為numpy array
model.set_weights()    #從numpy array里將權重載入給模型,要求數組具有與model.get_weights()相同的形狀。

# 查看model中Layer的信息
model.layers 

 

4.11  二分類和多分類編譯模型的參數設置

  二分類編譯模型的參數與多分類設置還是有區別的,具體如下:

# 二分類
#model.compile(loss='binary_crossentropy',
#              optimizer='rmsprop',
#              metrics=['accuracy'])

# 多分類
model.compile(loss='categorical_crossentropy',                    # matt,多分類,不是binary_crossentropy
              optimizer='rmsprop',
              metrics=['accuracy'])
# 優化器rmsprop:除學習率可調整外,建議保持優化器的其他默認參數不變

  

4.12  Keras卷積補零相關的 border_mode 的選擇以及 padding的操作

  我們先來看看 Keras卷積操作中 border_mode 的實現:

# apply a 3x3 convolution with 64 output filters on a 256x256 image:
model = Sequential()
model.add(Convolution2D(64, 3, 3, border_mode='same', input_shape=(3, 256, 256))) # now model.output_shape == (None, 64, 256, 256)

>> definition in keras:
def conv_output_length(input_length, filter_size, border_mode, stride):  
    if input_length is None:  
        return None  
    assert border_mode in {'same', 'valid'}  
    if border_mode == 'same':  
        output_length = input_length  
    elif border_mode == 'valid':  
        output_length = input_length - filter_size + 1  
    return (output_length + stride - 1) // stride

   總結:如果卷積的方式選擇為 same,那么卷積操作的輸入和輸出尺寸會保持一致。如果選擇方式為 valid,那么卷積過后,尺寸會變小。

  而TensorFlow中padding有兩種方式,其中SAME是填充零,而VALID是不做填充。

  (具體區別見:https://www.cnblogs.com/bugxch/p/14190955.html)

5,Keras中常用數據庫Datasets

5.1  CIFAR10 小圖片分類數據集

  該數據庫具有 50000個32*32 的彩色圖片作為訓練集,10000個圖片作為測試集,圖片一共有10個類別。

  使用方法:

from keras.datasets import cifar10

(X_train, y_train), (X_test, y_test) = cifar10.load_data()

  返回值是兩個Tuple。

  X_train和X_test 是形如 (nb_samples, 3, 32, 32)的RGB三通道圖像數據,數據類型是無符號8位整形(uint8)

  Y_train和Y_test 是形如(nb_samples, )標簽數據,標簽的范圍是0-9

5.2  CIFAR100 小圖片分類數據集

  該數據庫具有 50000個32*32 的彩色圖片作為訓練集,10000個圖片作為測試集,圖片一共有100個類別,每個類別有600張圖片。這100個類別又分為20個大類。

  使用方法:

from keras.datasets import cifar100

(X_train, y_train), (X_test, y_test) = cifar100.load_data(lebel_mode='fine')

  參數 label_model:為 fine 或 coarse,控制標簽的精細度,‘fine’ 獲得的標簽是100個小類的標簽;coarse獲得的標簽是大類的標簽。

  返回值是兩個Tuple。

  X_train和X_test 是形如 (nb_samples, 3, 32, 32)的RGB三通道圖像數據,數據類型是無符號8位整形(uint8)

  Y_train和Y_test 是形如(nb_samples, )標簽數據,標簽的范圍是0-9

 

5.3  MNIST 手寫數字識別

  該數據庫具有 60000個28*28 的灰度手寫數字圖片作為訓練集,10000個圖片作為測試集

  使用方法:

from keras.datasets import mnist

(X_train, y_train), (X_test, y_test) = mnist.load_data()

  參數 path:如果你本機上已經有此數據集(位於'~/.keras/datasets/'+path),則載入,否則數據將下載到該目錄下。

  返回值是兩個Tuple。

  X_train和X_test 是形如 (nb_samples, 28, 28)的RGB三通道圖像數據,數據類型是無符號8位整形(uint8)

  Y_train和Y_test 是形如(nb_samples, )標簽數據,標簽的范圍是0-9

  數據庫會被下載到 ~/.keras/datasets/'+path

5.4  Boston 房屋價格回歸數據庫

  該數據庫由StatLib庫取得,由CMU維護,每個樣本都是 1970s晚期波士頓郊區的不同位置,每條數據含有13個屬性,目標值是該位置房子的房價中位數(千 dollar)。

  使用方法:

from keras.datasets import boston_housing

(X_train, y_train), (X_test, y_test) = boston_housing.load_data()

  參數 path:如果你本機上已經有此數據集(位於'~/.keras/datasets/'+path),則載入,否則數據將下載到該目錄下。

  參數 seed:隨機數種子

  參數 test_split:分割測試集的比例

  返回值是兩個Tuple。

  X_train和X_test   Y_train和Y_test 

  數據庫會被下載到 ~/.keras/datasets/'+path

5.5  IMDB 影評傾向分類

  本數據庫含有來自 IMDB 的 25000 條影評,被標記為正面/負面兩種評價。影評已被預處理為詞下標構成的序列。方便起見,單詞的下標基於它在數據集中出現的頻率標定,例如整數3所編碼的詞為數據中第三常出現的詞。這樣的組織方式使得用戶可以快速完成諸如“只考慮最常出現的10000個詞,但不考慮最常出現的20個詞”這樣的操作。

  按照慣例,0不代表任何特定的詞,而用來編碼任何未知單詞。

  使用方法

from keras.datasets import imdb

(X_train, y_train), (X_test, y_test) = imdb.load_data(
    path='imdb.npz', num_words=None, skip_top=0,
    maxlen=None, seed=113,
    start_char=1, oov_char=2, index_from=3, )

  參數path:如果你本機上已經有此數據集(位於'~/.keras/datasets/'+path),則載入,否則數據將下載到該目錄下。

  參數 nb_words:整數或None,要考慮的最常見的單詞數,序列中任何出現頻率更低的單詞將會被編碼為 oov_char 的值。

  參數skip_top:整數,忽略最常出現的若干單詞,這些單詞將會被編碼為 oov_char的值。

  參數maxlen:整數,最大序列長度,任何長度大於此值的序列將會被截斷。

  參數 seed:整數,用於數據重排的隨機數種子

  參數start_char:字符,序列的起始將以該字符標記,默認為1 因為0通常用作padding

  參數oov_char:整數,因 nb_words或 skip_top 限制而 cut 掉的單詞將被該字符代替

  參數index_form:整數,真實的單詞(而不是類似於 start_char的特殊占位符)將從這個下標開始

  返回值是兩個Tuple。

  X_train和X_test  序列的列表,每個序列都是詞下標的列表,如果指定了 nb_words,則序列中可能的最大下標為 nb_word-1.如果指定了 maxlen,則序列的最大可能長度為 maxlen

  y_train和y_test  序列的標簽,是一個二值 list

5.6  路透社新聞主題分類

  該數據庫包含來自路透社的11228條新聞,分為了46個主題,與IMDB庫一樣,每條新聞被編碼為一個詞下標的序列。

  使用方法:

from keras.datasets import reuters

(X_train, y_train), (X_test, y_test) = reuters.load_data(
    path='reuters.npz', num_words=None, skip_top=0,
    maxlen=None, test_split=0.2, seed=113,
    start_char=1, oov_char=2, index_from=3,
)

  參數的含義與 IMDB同名參數相同,唯一多的參數是:test_split,用於指定從原數據中分割出作為測試集的比例。該數據庫支持獲取用於編碼序列的詞的下標:

word_index = reuters.get_word_index(path='reuters_word_index.json')

  上面的代碼的返回值是一個以單詞為關鍵字,以其下標為值的字典,例如 word_index['giraffe'] 的值可能是1234.

  參數path:如果你在本機上有此數據集(位於 ~/.keras/datasets/'+path),則載入。否則數據將下載到該目錄下

5.7  多分類標簽指定Keras格式

  數據集的載入上面都說了,而下面要強調的是Keras對多分類的標簽需要一種固定格式,所以需要按照以下的方式進行轉換,num_classes 為分類數量,假設此時有五類:

y_train = keras.utils.to_categorical(y_train, num_classes)

  最終輸出的格式應該是(100, 5)

     to_categorical 函數如下:

to_categorical(y,  num_classes = None)

  將類別向量(從0到 nb_classes的整數向量)映射為二值類別矩陣,用於應用到以 categorical_crossentropy 為目標函數的模型中。  其中y表示類別向量,num_classes表示總共類別數。

6,延伸

6.1  fine-tuning 時如何加載 No_top 的權重

  如果你需要加載權重到不同的網絡結構(有些層一樣)中,例如 fine-tune或transfer-learning,你可以通過層名字來加載模型:

model.load_weights(‘my_model_weights.h5’, by_name=True)

  例如,假設元模型為:

    model = Sequential()
    model.add(Dense(2, input_dim=3, name="dense_1"))
    model.add(Dense(3, name="dense_2"))
    ...
    model.save_weights(fname)

  新模型如下:

# new model
model = Sequential()
model.add(Dense(2, input_dim=3, name="dense_1"))  # will be loaded
model.add(Dense(10, name="new_dense"))  # will not be loaded

# load weights from first model; will only affect the first layer, dense_1.
model.load_weights(fname, by_name=True)

  

6.2  應對不均衡樣本的情況

  使用 class_weight,  sample_weight

  兩者的區別為

  1. class_weight 主要針對的時數據不均衡問題,比如:異常檢測的二項分類問題,異常數據僅占 1%,正常數據占 99 %;此時就要設置不同類對 loss 的影響。
  2. sample_weight  主要解決的時樣本質量不同的問題,比如前 1000 個樣本的可信度,那么他的權重就要高,后 1000個樣本可能有錯,不可信,那么權重就要調低。

  class-weight 的使用:

cw = {0: 1, 1: 50}
model.fit(x_train, y_train,batch_size=batch_size,epochs=epochs,verbose=1,callbacks=cbks,
validation_data=(x_test, y_test), shuffle=True,class_weight=cw)

  sample_weight 的使用:

from sklearn.utils import class_weight

list_classes = ["toxic", "severe_toxic", "obscene", "threat", "insult", "identity_hate"]
y = train[list_classes].values
sample_weights = class_weight.compute_sample_weight('balanced', y)

model.fit(X_t, y, batch_size=batch_size, epochs=epochs,validation_split=0.1,sample_weight=sample_weights, callbacks=callbacks_list)

  來源:https://www.kaggle.com/c/jigsaw-toxic-comment-classification-challenge/discussion/46673

6.3  h5模型轉pb模型,h5模型轉 tflite模型

  這里記錄一下關於自己的h5模型轉pb模型的代碼,注意:這里有基於Keras寫的自定義損失函數如何保存到模型中

  首先,我們看一下,如何將自定義損失函數保存到模型中:

from keras.models import load_model, 

def contrastive_loss(y_true, y_pred):
    '''Contrastive loss from Hadsell-et-al.'06
        http://yann.lecun.com/exdb/publis/pdf/hadsell-chopra-lecun-06.pdf
        '''
    margin = 1
    square_pred = K.square(y_pred)
    margin_square = K.square(K.maximum(margin - y_pred, 0))
    return K.mean(y_true * square_pred + (1 - y_true) * margin_square)

h5_file = 'sample_model.h5'

h5_model = model.save(h5_file, custom_objects={'contrastive_loss': contrastive_loss})

   保存為h5模型后,我們轉pb模型(直接上代碼):

from keras.models import load_model
from tensorflow.python.framework import graph_util
from keras import backend as K
import tensorflow as tf
import os


def h5_to_pb(h5_file, output_dir, model_name, out_prefix="output_"):
    h5_model = load_model(h5_file, custom_objects={'contrastive_loss': contrastive_loss})
    out_nodes = []
    for i in range(len(h5_model.outputs)):
        out_nodes.append(out_prefix + str(i + 1))
        # print(out_nodes)  # ['output_1']
        tf.identity(h5_model.output[i], out_prefix + str(i + 1))
    sess = K.get_session()
    init_graph = sess.graph.as_graph_def()
    main_graph = graph_util.convert_variables_to_constants(sess, init_graph, out_nodes)
    with tf.gfile.GFile(os.path.join(output_dir, model_name), "wb") as filemodel:
        filemodel.write(main_graph.SerializeToString())
    print("pb model: ", {os.path.join(output_dir, model_name)})

   這樣轉pb 是沒有任何問題的,over。

  我們用Keras訓練模型后,通常保存的模型格式類型為 hdf5格式,也就是 .h5文件。但是如果如果我們想要移植到移動端,特別是基於TensorFlow支持的移動端,那就需要轉化為tflite格式。

  在TensorFlow高版本中支持通過命令行方式進行轉換,如下:

tflite_convert --output_file=/my_model.tflite --keras_model_file=/my_model.h5

   如果在程序中轉換,加上自定義的損失函數,則代碼如下:

from tensorflow import lite

def h5_to_tflite(h5_file, tflite_file):
    converter = lite.TFLiteConverter.from_keras_model_file(h5_file,
                                                           custom_objects={'contrastive_loss': contrastive_loss})
    tflite_model = converter.convert()
    with open(tflite_file, 'wb') as f:
        f.write(tflite_model)

   但是會出現問題,就是報錯,轉不了。。。。原因還未找到,如果不加損失函數,則沒有問題,代碼可以用。

  還有一個 pb轉 tflite的代碼,我沒有嘗試。。。但是因為小改就可以用。

  所有的代碼如下:

import tensorflow as tf
# print(tensorflow.__version__)  # 1.14.0
from keras.models import load_model
from tensorflow.python.framework import graph_util
from tensorflow import lite
from keras import backend as K
import os


def h5_to_pb(h5_file, output_dir, model_name, out_prefix="output_"):
    h5_model = load_model(h5_file, custom_objects={'contrastive_loss': contrastive_loss})
    print(h5_model.input)
    # [<tf.Tensor 'input_2:0' shape=(?, 80, 80) dtype=float32>, <tf.Tensor 'input_3:0' shape=(?, 80, 80) dtype=float32>]
    print(h5_model.output)  # [<tf.Tensor 'lambda_1/Sqrt:0' shape=(?, 1) dtype=float32>]
    print(len(h5_model.outputs))  # 1
    out_nodes = []
    for i in range(len(h5_model.outputs)):
        out_nodes.append(out_prefix + str(i + 1))
        # print(out_nodes)  # ['output_1']
        tf.identity(h5_model.output[i], out_prefix + str(i + 1))
    sess = K.get_session()
    init_graph = sess.graph.as_graph_def()
    main_graph = graph_util.convert_variables_to_constants(sess, init_graph, out_nodes)
    with tf.gfile.GFile(os.path.join(output_dir, model_name), "wb") as filemodel:
        filemodel.write(main_graph.SerializeToString())
    print("pb model: ", {os.path.join(output_dir, model_name)})


def pb_to_tflite(pb_file, tflite_file):
    inputs = ["input_1"]  # 模型文件的輸入節點名稱
    classes = ["output_1"]  # 模型文件的輸出節點名稱
    converter = tf.lite.TocoConverter.from_frozen_graph(pb_file, inputs, classes)
    tflite_model = converter.convert()
    with open(tflite_file, "wb") as f:
        f.write(tflite_model)


def contrastive_loss(y_true, y_pred):
    '''Contrastive loss from Hadsell-et-al.'06
        http://yann.lecun.com/exdb/publis/pdf/hadsell-chopra-lecun-06.pdf
        '''
    margin = 1
    square_pred = K.square(y_pred)
    margin_square = K.square(K.maximum(margin - y_pred, 0))
    return K.mean(y_true * square_pred + (1 - y_true) * margin_square)


def h5_to_tflite(h5_file, tflite_file):
    converter = lite.TFLiteConverter.from_keras_model_file(h5_file,
                                                           custom_objects={'contrastive_loss': contrastive_loss})
    tflite_model = converter.convert()
    with open(tflite_file, 'wb') as f:
        f.write(tflite_model)


if __name__ == '__main__':
    h5_file = 'screw_10.h5'
    tflite_file = 'screw_10.tflite'
    pb_file = 'screw_10.pb'
    # h5_to_tflite(h5_file, tflite_file)
    h5_to_pb(h5_file=h5_file, model_name=pb_file, output_dir='', )
    # pb_to_tflite(pb_file, tflite_file)

   保存為pb可以用在安卓端,這里使用pb來預測,和h5預測稍有不同,這里寫的是使用pb來預測孿生網絡的代碼,和上面保持一致哈(其他的照着改就行):

import tensorflow as tf
from tensorflow.python.platform import gfile
import cv2


def predict_pb(pb_model_path, image_path1, image_path2, target_size):
    sess = tf.Session()
    with gfile.FastGFile(pb_model_path, 'rb') as f:
        graph_def = tf.compat.v1.GraphDef()
        graph_def.ParseFromString(f.read())
        sess.graph.as_default()
        tf.import_graph_def(graph_def, name='')
    # 輸入  這里有兩個輸入
    input_x = sess.graph.get_tensor_by_name('input_2:0')
    input_y = sess.graph.get_tensor_by_name('input_3:0')
    # 輸出
    op = sess.graph.get_tensor_by_name('lambda_1/Sqrt:0')

    image1 = cv2.imread(image_path1)
    image2 = cv2.imread(image_path2)
    # 灰度化,並調整尺寸
    image1 = cv2.cvtColor(image1, cv2.COLOR_BGR2GRAY)
    image1 = cv2.resize(image1, target_size)
    image2 = cv2.cvtColor(image2, cv2.COLOR_BGR2GRAY)
    image2 = cv2.resize(image2, target_size)
    data1 = np.array([image1], dtype='float') / 255.0
    data2 = np.array([image2], dtype='float') / 255.0
    y_pred = sess.run(op, {input_x: data1, input_y: data2})
    print(y_pred)

 

 

 參考文獻:https://blog.csdn.net/sinat_26917383/article/details/72859145


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM