我的Keras使用總結(5)——Keras指定顯示卡且限制視訊記憶體用量,常見函式的用法及其習題練習

戰爭熱誠發表於2020-10-06

  Keras 是一個高層神經網路API,Keras是由純Python編寫而成並基於TensorFlow,Theano以及CNTK後端。Keras為支援快速實驗而生,能夠將我們的idea迅速轉換為結果。好了不吹了,下面繼續學習Keras的一些用法,其中這篇部落格包括了Keras如何指定顯示卡且限制視訊記憶體用量,還有一些常見函式的用法及其問題,最後是使用Keras進行的練習。

Keras如何指定顯示卡且限制視訊記憶體用量

  Keras在使用GPU的時候有個特點,就是預設全部佔滿視訊記憶體。若單核GPU也無所謂,若是伺服器GPU較多,效能較好,全部沾滿就太浪費了。

  於是有以下五種情況:

  • 1,指定GPU
  • 2,使用固定視訊記憶體的GPU
  • 3,指定GPU+固定視訊記憶體
  • 4,GPU動態增長
  • 5,CPU充分佔用
  • 6,tf.keras 使用多 GPU

1,固定 GPU 的視訊記憶體

  本節來自:深度學習 tehano/tensorflow 多顯示卡多人使用問題集(參見:Limit the resource usage for tensorflow backend · Issue #1538 · fchollet/keras · GitHub)

  在使用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))

  需要注意的是,雖然程式碼或配置層面設定了對視訊記憶體佔用百分比閾值,但在實際執行中如果達到了這個閾值,程式有需要的話還是會突破這個閾值。換而言之如果跑在一個大資料集上還是會用到更多的視訊記憶體。以上的視訊記憶體限制僅僅為了在跑小資料集時避免對視訊記憶體的浪費而已。

 

2,使用指定的GPU

  比如下面程式碼:

import os

os.environ["CUDA_VISIBLE_DEVICES"] = "2"

  此時的程式碼為選擇了編號為2的GPU。

  下面程式碼我們設定了8個GPU,(當然這是假的哈)

# python設定系統變數的方法
os.environ["CUDA_VISIBLE_DEVICES"] = "8,9,10,11,12,13,14,15"

  注意,在程式碼中指定裝置時,重新從 0 開始計,而不是從8開始。

 

3,指定GPU+固定視訊記憶體

  上面兩個連在一起用就OK:

import os
import tensorflow as tf
os.environ["CUDA_VISIBLE_DEVICES"] = "2"
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))

  那麼在命令列,可以使用:https://github.com/tensorflow/nmt/issues/60

CUDA_VISIBLE_DEVICES=0 python -m nmt.nmt 

  

4,GPU動態增長

import keras.backend.tensorflow_backend as KTF
import tensorflow as tf
import os


os.environ["CUDA_VISIBLE_DEVICES"] = "1"

config = tf.ConfigProto()
config.gpu_options.allow_growth=True   #不全部佔滿視訊記憶體, 按需分配
sess = tf.Session(config=config)

KTF.set_session(sess)

  os.environ 指的時佔用的 GPU編號;allow_growth 為動態申請視訊記憶體佔用。

 

5,CPU充分佔用

  來自部落格:http://nooverfit.com/wp/tensorflow%E5%A6%82%E4%BD%95%E5%85%85%E5%88%86%E4%BD%BF%E7%94%A8%E6%89%80%E6%9C%89cpu%E6%A0%B8%E6%95%B0%EF%BC%8C%E6%8F%90%E9%AB%98tensorflow%E7%9A%84cpu%E4%BD%BF%E7%94%A8%E7%8E%87%EF%BC%8C%E4%BB%A5/

num_cores = 4

config = tf.ConfigProto(intra_op_parallelism_threads=num_cores, inter_op_parallelism_threads=num_cores,
                        allow_soft_placement=True, device_count={'CPU': 4})
session = tf.Session(config=config)
K.set_session(session)

  其中:

  • device_count, 告訴tf Session使用CPU數量上限,如果你的CPU數量較多,可以適當加大這個值
  • inter_op_parallelism_threads和intra_op_parallelism_threads告訴session操作的執行緒並行程度,如果值越小,執行緒的複用就越少,越可能使用較多的CPU核數。如果值為0,TF會自動選擇一個合適的值。
  • allow_soft_placement=True, 有時候,不同的裝置,它的cpu和gpu是不同的,如果將這個選項設定成True,那麼當執行裝置不滿足要求時,會自動分配GPU或者CPU。

 

6,tf.keras 使用多 GPU

  DistributionStrategy API是構建多裝置/機器訓練的簡單方式,開發者只需要在現有模型上做少量的修改,就可以用它們進行分散式訓練。另外,DistributionStrategy在設計時考慮了同時相容動態圖(eager)和靜態圖。
參考:TensorFlow 1.11.0釋出,一鍵多GPU(訓練、預測和評價tf.keras模型)

  目前TensorFlow支援三種DistributionStrategy:

  • MirroredStrategy
  • CollectiveAllReduceStrategy
  • ParameterServerStrategy

  在tf.keras中直接使用DistributionStrategy

  最新的TensorFlow Github中給出了在tf.keras中直接使用DistributionStrategy的例子。

  用tf.keras構建一個單層網路:

inputs = tf.keras.layers.Input(shape=(1,))
predictions = tf.keras.layers.Dense(1)(inputs)
model = tf.keras.models.Model(inputs=inputs, outputs=predictions)

  目前,使用DistributionStrategy需要使用tf.data.Dataset來作為資料輸入:

features = tf.data.Dataset.from_tensors([1.]).repeat(10000).batch(10)
labels = tf.data.Dataset.from_tensors([1.]).repeat(10000).batch(10)
train_dataset = tf.data.Dataset.zip((features, labels))

  這裡我們為模型指定使用MirroredStrategy進行多GPU訓練,程式碼非常簡單:

distribution = tf.contrib.distribute.MirroredStrategy()
model.compile(loss='mean_squared_error',
              optimizer=tf.train.GradientDescentOptimizer(learning_rate=0.2),
              distribute=distribution)

  使用常規的訓練、評價和預測方法會自動在多GPU上進行:

model.fit(train_dataset, epochs=5, steps_per_epoch=10)
model.evaluate(eval_dataset)
model.predict(predict_dataset)

  將tf.keras模型遷移到多GPU上執行只需要上面這些程式碼,它會自動切分輸入、在每個裝置(GPU)上覆制層和變數、合併和更新梯度。

 

7,OpenBLASblas_thread_initpthread_creatResourcetemporarilyunavailable問題分析與解決

7.1 報錯情況

  我直接執行我的程式碼會報錯如下: 

   問題太多了,但是解決方法好像很簡單

7.2 解決方法

   參考文獻:https://zhuanlan.zhihu.com/p/23250782

   TensorFlow 如果單純使用 TensorFlow的話,可以用程式碼控制:

config = tf.ConfigProto()
config.gpu_options.per_process_gpu_memory_fraction = 0.4
session = tf.Session(config=config, ...)

  如果使用Keras作為前端,也可以用程式碼控制:

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))

  這樣一來,就可以讓同一塊顯示卡同時執行多程式了,cuda流處理器也可以和多核CPU一樣滿足多程式執行。

  需要注意的是,雖然程式碼或配置層面設定了對視訊記憶體佔用百分比閾值,但在實際執行中如果達到了這個閾值,程式有需要的話還是會突破這個閾值(用theano後端會如此,tensorflow可能會報資源耗盡錯,2020年7月20日補充)。換而言之如果跑在一個大資料集上還是會用到更多的視訊記憶體。以上的視訊記憶體限制僅僅為了在跑小資料集時避免對視訊記憶體的浪費而已。

 

常見函式的用法

1,fit() 和 fit_generator() 區別以及引數的坑

  參考地址:https://blog.csdn.net/mlp750303040/article/details/89207658    https://blog.csdn.net/learning_tortosie/article/details/85243310

  首先Keras中的 fit() 函式傳入的 x_train 和 y_train 是被完整的載入進記憶體的,當然用起來很方便,但是如果我們資料量很大,那麼是不可能將所有資料載入記憶體的,必將導致記憶體洩露,這時候我們可以用 fit_generator 函式來進行訓練。

1.1  fit() 函式

  下面是 fit 傳參的例子:

history = model.fit(x_train, y_train, epochs=10,batch_size=32, 
                    validation_split=0.2)

  在這裡我們看到提供的訓練資料(trainX)和訓練標籤(trainY),然後這裡需要給出 epochs 和 batch_size,epoch是這個資料集要被訓練多少次,batch_size 是這個資料集被分成多少個 batch 進行處理。最後給出交叉驗證集的大小,這裡的 0.2 是指在訓練集上佔比 20%。

  使用 .fit() 函式,這裡需要做兩個假設:

  • 1,我們的整個訓練集可以放在 RAM
  • 2,沒有資料增強(即不需要Keras生成器)

  相反,我們的網路將在原始資料上訓練,原始資料本身將適合記憶體,我們無需將舊批量資料從 RAM 中移出並將新批量資料移入RAM。此外,我們不會使用資料增強動態操縱訓練資料。

1.2 fit_generator() 函式

  對於小型,簡單化的資料集,使用Keras的  .fit 函式是完全可以接受的。這些小型資料集通常不是很具有挑戰性,不需要任何資料增強

  但是,真實世界的資料集很少這麼簡單:

  • 真實世界的資料結構通常太大而無法放入記憶體中
  • 他們也往往具有挑戰性,要求我們執行資料增強以避免過擬合併增加我們模型的泛化能力

  在這些情況下,我們需要利用 Keras的 .fit_generator() 函式。

  fit_generator() 函式必須傳入一個生成器,我們的訓練資料也是通過生成器產生的,下面給出一個簡單的生成器例子:

# initialize the number of epochs and batch size
EPOCHS = 100
BS = 32

# construct the training image generator for data augmentation
aug = ImageDataGenerator(rotation_range=20, zoom_range=0.15,
    width_shift_range=0.2, height_shift_range=0.2, shear_range=0.15,
    horizontal_flip=True, fill_mode="nearest")

  這裡的生成器函式我生成的是一個 batch_size 為 32大小的資料,這裡只是一個demo,如果在生成器裡沒有規定 batch_size 的大小,就是每次產生一個資料,那麼在用  fit_generator 的時候裡面的引數 steps_per_epoch 是不一樣的(這個問題後面講,這裡不再贅述)

  下面是 fit_generator() 函式的傳參:

# train the network
H = model.fit_generator(aug.flow(trainX, trainY, batch_size=BS),
    validation_data=(testX, testY), steps_per_epoch=len(trainX) // BS,
    epochs=EPOCHS)

   我們首先會初始化將要訓練的網路的 epoch和batch size,然後我們初始化 aug,這是一個 Keras ImageDataGenerator 物件,用於影像的資料增強,隨機平移,旋轉,調整大小等。

  執行資料增強是正則化的一種形式,使我們的模型能夠更好的被泛化。但是應用資料增強意味著我們的訓練資料不再是“靜態的”,而資料不斷在變換。根據提供給ImageDataGenerator的引數隨機調整每批新資料,因此我們需要利用Keras的 .fit_generator 函式來訓練我們的模型。顧名思義, .fit_generator() 函式假定存在一個為其生成資料的基礎函式。該函式本身是一個 Python生成器。

  所以Keras在使用 .fit_generator() 訓練模型的過程中:

  • Keras呼叫提供給 .fit_generator 的生成器函式(在本例為 aug.flow)
  • 生成器函式為 .fit_generator() 函式生成一大批為 batch size 的資料
  • .fit_generator() 函式接受批量資料,執行反向傳播,並更新模型中的權重
  • 重複該過程直到達到期望的 epoch 數量

  下面說一下為什麼我們需要  steps_per_epoch

  請記住,Keras資料生成器意味著無限迴圈,它永遠不會返回或退出。

  而steps_per_epoch:是在宣告一個epoch完成並開始下一個epoch之前從發生器產生的步驟(樣本批次)的總數,它通常應該等於資料集的唯一樣本數除以批量大小

  由於該函式旨在無限迴圈,因此 Keras無法確定一個 epoch何時開始,並且新的 epoch何時開始。因此我們將訓練資料的總數除以批量大小的結果作為 steps_per_epoch 的值,一旦Keras到達這一步,它就會知道這是一個新的 epoch。所以當使用 fit_generator 增加 batch_size時,如果希望訓練時間保持不變或者更低,則應將 steps_per_epochs 減少相同的因子。

  所以我們使用fit_generator() 函式的時候,一般需要將 steps_per_epoch 和 validation_steps寫成活參,如下:

    model.fit_generator(
        train_generator,
        steps_per_epoch=nb_train_samples // batch_size,
        epochs=epochs,
        validation_data=validation_generator,
        validation_steps=nb_validation_samples // batch_size
    )

   注意這裡的  nb_train_samples 和 nb_validation_samples 需要我們自己找一下,看看自己的訓練集和驗證集的資料總共有多少個。

 

2,回撥函式callback

  官方文件:https://keras.io/zh/callbacks/

  回撥函式是一組在訓練的特定階段被呼叫的函式集,你可以使用回撥函式來觀察訓練過程中網路內部的狀態和統計資訊。通過傳遞迴調函式列表到模型的 .fit() 中,即可在給定的訓練階段呼叫該函式集中的函式。

  Tips:雖然我們稱之為“回撥函式”,事實上Keras的回撥函式是一個類,回撥函式只是習慣性稱呼。

  callback模組中常用的類和函式有12個,但是下面只學習幾個常用的類。

2.1,Callback

keras.callbacks.Callback()

   這是回撥函式的抽象類,定義新的回撥函式必須繼承該類。

類屬性

  • params:字典,訓練引數集(如資訊顯示方法 verbosity,batch大小,epoch數)
  • model:keras.models.Model物件,為正在訓練的模型的引用

  回撥函式以字典 logs 為引數,該字典包含了一系列與當前 batch 或 epoch相關的資訊。

  目前,模型的 .fit() 中有下列引數會被記錄到logs中:

  • 在每個epoch的結尾處(on_epoch_end),logs將包含訓練的正確率和誤差,acc和loss,如果指定了驗證集,還會包含驗證集正確率和誤差 val_acc 和 val_loss,val_acc 還額外需要在 .compile中啟用 metrics=['accuracy']。
  • 在每個 batch 的開始處(on_batch_begin):logs包含size,即當前batch的樣本數
  • 在每個batch的結尾處(on_batch_end):logs包含loss,若啟用 accuracy則還包含acc
on_epoch_begin  #在每輪開始時被呼叫
on_epoch_end   #在每輪結束時被呼叫
 
on_batch_begin #在處理每個批量之前被呼叫
on_batch_end  #在處理每個批量之後被呼叫
 
on_train_begin  #在訓練開始時被呼叫
on_train_end  #在訓練結束時被呼叫

 

2.2  EarlyStopping

  earlystopping 是Callbacks 的一種,callbacks 用於指定在每個 epoch 開始和結束的時候進行哪種特定的操作。Callbacks中有一些設定好的介面,可以直接使用,如'acc','val_acc', 'loss','val_loss'等等。EarlyStopping則是用於提前停止訓練的 callbacks。具體的,可以達到當訓練集上的 loss 不再減少(即減小的程度小於某個閾值)的時候停止訓練。

為什麼要使用 earlystopping?

  當我們訓練深度學習神經網路的時候通常希望能夠獲得最好的泛化效能(generalization performance,即可以很好的擬合資料),但是所有的標準深度網路結構如全連線多層感知機都很容易過擬合。常用的防止過擬合的方法是對模型加正則項,如L1,L2,dropout,但深度神經網路希望通過加深網路層次減少優化的引數,同時可以得到更好的優化結果,Early stopping 的使用可以在模型訓練整個過程中擷取儲存結構最優的引數模型,防止過擬合。

  earlystopping 旨在解決 epoch 數量需要手動設定的問題。它也可以被視為一種能夠避免網路發生過擬合的正則化方法(與L1,L2權重衰減和丟棄法類似)。根本原因就是因為繼續訓練會導致測試集上的準確率下降。那麼繼續訓練導致測試準確率下降的原因猜測可能是:1,過擬合;2,學習率過大導致不收斂;3,使用正則項的時候,loss的減少可能不是因為準確率增加導致的,而是因為權重大小的降低。

earlystopping 的原理

  1,將資料分為訓練集和驗證集

  2,每個 epoch結束後(或者每N個epoch後):在驗證集上獲取測試結果,隨著epoch的增加,如果在驗證集上發現測試誤差上升,則停止訓練

  3,將停止之後的權重作為網路的最終引數

  這種做法很符合直觀感受,因為精度都不再提高了,在繼續訓練也是無益的,只會提高訓練的時間。那麼該做法的一個重點便是怎樣才認為驗證集精度不再提高了,因為可能經過這個Epoch後,精度降低了,但是隨後的Epoch又讓精度又上去了,所以不能根據一兩次的連續降低就判斷不再提高。一般的做法是,在訓練的過程中,記錄到目前為止最好的驗證集精度,當連續10次Epoch(或者更多次)沒達到最佳精度時,則可以認為精度不再提高了。

keras.callbacks.EarlyStopping(monitor='val_loss', patience=0, verbose=0, mode='auto')

   當監測值不再改善時,該回撥函式將終止訓練。

引數:

  • monitor:需要監視的量,有’acc’,’val_acc’,’loss’,’val_loss’等等。正常情況下如果有驗證集,就用’val_acc’或者’val_loss’。但是如果沒有單設驗證集,就只能用’acc’了
  • patience:能夠容忍多少個 epoch 內都沒有improvement,這個設定其實是在抖動和真正的準確率下降之間做 trade off。如果 patience設定的大,那麼最終得到的準確率要略低於模型可以達到的最高準確率;如果patience設定的小,那麼模型很可能在前期抖動,還在全圖搜尋的階段就停止了,準確率一般很差。patience的大小和 learning rate 直接相關。當 early stop被啟用(如果發現loss相比上一個epoch訓練沒有下降),則經過patience個epoch後停止訓練
  • verbose:資訊展示模型
  • model:‘auto’,‘min’,‘max’之一,在 min模式下,如果檢測值停止下降則終止訓練。在 max模式下,當檢測值不再上升則停止訓練。例如,當監測值為 val_acc 時,模式應該為 max,當監測值為 val_loss 時,模式應為 min。在auto模式下,評價準則由被監測值的名字自動推斷

 

2.3  LearningRateScheduler

keras.callbacks.LearningRateScheduler(schedule)

   該回撥函式是學習率排程器

引數

  • schedule:函式,該函式以 epoch號為引數(從0算起的整數),返回一個新的學習率(浮點數)

 

2.4  ModelCheckpoint

  Keras中的模型主要包括model和weight兩個部分,儲存Keras的model檔案和載入Keras檔案的方法有很多,這裡分別學習一下。

  儲存model部分的主要方法:

  1,通過 json 檔案:

# serialize model to JSON
model_json = model.to_json()
with open("model.json", "w") as json_file:
    json_file.write(model_json)

# load json and create model
json_file = open('model.json', 'r')
loaded_model_json = json_file.read()
json_file.close()
loaded_model = model_from_json(loaded_model_json)

   2,通過Yaml檔案:

# save as YAML
yaml_string = model.to_yaml()

   3,通過 hdf5檔案:

# 儲存權重係數
# serialize weights to HDF5
model.save_weights("model.h5")
print("Saved model to disk")

# 同時儲存 model 和權重的方法
model.save('model_weight.h5')  # creates a HDF5 file 'my_model.h5'

# 載入權重和載入模型
from keras.models import load_model
 
model = load_model('model.h5') 
loaded_model.load_weights("model.h5")

   但是這裡主要學習一下ModelCheckpoint方法:

keras.callbacks.ModelCheckpoint(filepath, monitor='val_loss', verbose=0,
 save_best_only=False, save_weights_only=False, mode='auto', period=1)

   在每個訓練器之後儲存模型。

  引數說明:

  • filepath:字串,儲存模型的路徑
  • monitor:需要監視的值,val_acc 或者 val_loss
  • verbose:資訊展示模式,0或者1(checkpoint 的儲存資訊,類似於Epoch 000001:saving model to...)
  • save_best_only:當設定為True時,監測值有改進時才會儲存當前的模型(the lastest best model according to the quantity monitored will not be overweitten)
  • model:'auto', ‘min’,‘max’之一,在 save_best_only=True時決定效能最佳模型的評判準則,例如,當監測值為 val_acc 時,模式應該為 max,當監測值為 val_loss 時,模式應為 min。在auto模式下,評價準則由被監測值的名字自動推斷
  • save_weights_only:若設定為True,則只儲存模型權重,否則將儲存整個模型(包括模型結構,配置資訊等)
  • period:checkpoint之間的間隔的 epoch數

  注意1:filepath 可以包括命名格式選項,可以由 epoch的值和 logs的鍵(由on_epoch_end 引數傳遞)來填充。

  例如:如果 filepath 是 weights.{epoch:02d}-{val_loss:.2f}.hdf5,那麼模型被儲存的檔名就會有訓練輪數和驗證損失。

  注意2:我們需要在 model.fit 新增 callbacks = [checkpoint] 實現回撥。

  舉一個我實際的例子:

# 訓練引數設定
logging = TensorBoard(log_dir=log_dir)
checkpoint = ModelCheckpoint(log_dir + 'ep{epoch:03d}-loss{loss:.3f}-val_loss{val_loss:.3f}.h5',
    monitor='val_loss', save_weights_only=True, save_best_only=True, period=1)
reduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=2, verbose=1)
early_stopping = EarlyStopping(monitor='val_loss', min_delta=0, patience=6, verbose=1)

BATCH_SIZE = 32
gen = Generator(bbox_util, BATCH_SIZE, lines[:num_train], lines[num_train:],
                (input_shape[0], input_shape[1]),NUM_CLASSES, do_crop=True)
    
model.compile(optimizer=Adam(lr=1e-4),loss=MultiboxLoss(NUM_CLASSES, neg_pos_ratio=5.0).compute_loss)

model.fit_generator(gen.generate(True), 
        steps_per_epoch=num_train//BATCH_SIZE,
        validation_data=gen.generate(False),
        validation_steps=num_val//BATCH_SIZE,
        epochs=100, 
        initial_epoch=0,
        callbacks=[logging, checkpoint, reduce_lr, early_stopping])

 

2.5  ReduceLROnPlateau

  在訓練過程中如果出現了損失平臺(loss plateau),即損失率不怎麼變化時,改變學習率。

callbacks_list = [
    keras.callbacks.ReduceLROnPlateau(
        monitor='val_loss'   ←------ 監控模型的驗證損失
        factor=0.1,   ←------ 觸發時將學習率除以10
        patience=10,   ←------ 如果驗證損失在10輪內都沒有改善,那麼就觸發這個回撥函式
    )
]

 

2.6  官網例子:記錄損失歷史

  程式碼如下:

class LossHistory(keras.callbacks.Callback):
    def on_train_begin(self, logs={}):
        self.losses = []

    def on_batch_end(self, batch, logs={}):
        self.losses.append(logs.get('loss'))

model = Sequential()
model.add(Dense(10, input_dim=784, kernel_initializer='uniform'))
model.add(Activation('softmax'))
model.compile(loss='categorical_crossentropy', optimizer='rmsprop')

history = LossHistory()
model.fit(x_train, y_train, batch_size=128, epochs=20, verbose=0, callbacks=[history])

print(history.losses)
# 輸出
'''
[0.66047596406559383, 0.3547245744908703, ..., 0.25953155204159617, 0.25901699725311789]
'''

 

2.7  官網例子:模型檢查點

  程式碼如下:

from keras.callbacks import ModelCheckpoint

model = Sequential()
model.add(Dense(10, input_dim=784, kernel_initializer='uniform'))
model.add(Activation('softmax'))
model.compile(loss='categorical_crossentropy', optimizer='rmsprop')

'''
如果驗證損失下降, 那麼在每個訓練輪之後儲存模型。
'''
checkpointer = ModelCheckpoint(filepath='/tmp/weights.hdf5', verbose=1, save_best_only=True)
model.fit(x_train, y_train, batch_size=128, epochs=20, verbose=0,
 validation_data=(X_test, Y_test), callbacks=[checkpointer])

 

2.8  例子:提前終止訓練

  程式碼如下:

    train_generator, validation_generator, count1, count2 = generate(batch, size)

    model = MobileNetv2((size, size, 3), num_classes)

    opt = Adam()
    earlystop = EarlyStopping(monitor='val_acc', patience=30, verbose=0, mode='auto')
    model.compile(loss='categorical_crossentropy', optimizer=opt, metrics=['accuracy'])

    history = model.fit_generator(
        train_generator,
        validation_data=validation_generator,
        steps_per_epoch=count1 // batch,
        validation_steps=count2 // batch,
        epochs=epochs,
        callbacks=[earlystop]
    )

    model.save('model/model.h5')

2.9  例子:編寫自己的回撥函式

  (參考文獻:https://www.kancloud.cn/mikl_maple/python/1726322)

  調函式中,那麼可以編寫你自己的回撥函式,回撥函式的實現方法是建立 Keras.callbacks.Callback 類的子類。然後你可以實現下面這些方法(從名稱中即可看出這些方法的作用),他們分別在訓練過程中的不同時間段被呼叫。

n_epoch_begin   ←------ 在每輪開始時被呼叫
on_epoch_end   ←------ 在每輪結束時被呼叫
 
on_batch_begin   ←------ 在處理每個批量之前被呼叫
on_batch_end   ←------ 在處理每個批量之後被呼叫
 
on_train_begin   ←------ 在訓練開始時被呼叫
on_train_end   ←------ 在訓練結束時被呼叫

   這些方法被呼叫時都有一個 logs 引數,這個引數是一個字典,裡面包含前一個批量,前一個輪次或者前一次訓練的資訊,即訓練指標和驗證指標等。此外,回撥函式還可以訪問下列屬性。

  • self.model:呼叫回撥函式的模型例項。
  • self.validation_data:傳入fit作為驗證資料的值。

  下面是一個自定義回撥函式的簡單示例,它可以在每輪結束後將模型每層的啟用儲存到硬碟(格式為 Numpy 陣列),這個啟用是對驗證集的第一個樣本計算得到的。

import keras
import numpy as np

class ActivationLogger(keras.callbacks.Callback):
    def set_model(self, model):
        self.model = model  #在訓練之前由父模型呼叫,告訴回撥函式是哪個模型在呼叫它
        layer_outputs = [layer.output for layer in model.layers]
        self.activations_model = keras.models.Model(model.input,
                                                    layer_outputs)  #模型例項,返回每層的啟用
    def on_epoch_end(self, epoch, logs=None):
        if self.validation_data is None:
            raise RuntimeError('Requires validation_data.')
        validation_sample = self.validation_data[0][0:1]   #獲取驗證資料的第一個輸入樣本
        activations = self.activations_model.predict(validation_sample)
        f = open('activations_at_epoch_' + str(epoch) + '.npz', 'w')   #(以下3行)將陣列儲存到硬碟
        np.savez(f, activations)          
        f.close()  

  我的回撥程式碼如下:

class myCallback(keras.callbacks.Callback):
    def on_epoch_end(self, epoch, logs=None):
        if (logs.get('acc') > 0.95):
            print("\nReached 95% accuracy so cancelling training !")
            self.model.stop_training = True

 

習題練習

  (參考地址:https://zhuanlan.zhihu.com/p/103049619)

1,匯入

1.1,匯入Keras庫,並列印版本資訊

import keras

print(keras.__version__)
#  2.2.4

 

2,一個簡單的例子

  使用MLP模型實現手寫數字影像MNIST的分類

2.1 選擇模型

  Keras中的模型分為序貫模型和函式式模型,我們這裡初始化一個順序模型(Sequential)

model = Sequential()

 

2.2 構建網路層

  網路層分為輸入層,隱藏層,輸出層。我們為模型model加入一個784輸入,784輸出的隱藏層,啟用函式使用relu。

model.add(Dense(units=784, activation='relu', input_dim=784))

   在上面的基礎上,我們為model加入10個輸出的輸出層,啟用函式使用softmax。

model.add(Dense(units=10, activation='softmax'))

   最後可以通過 .summary()  檢視模型引數情況

model.summary()

 

2.3 編譯模型

  編譯模型的過程主要分為三個,分別是優化函式的選擇,損失函式的選擇,效能評估指標的選擇。

  我們使用.compile() 來配置學習過程,代價函式 loss 使用 categorical_crossentropy,優化演算法 optimizer使用 sgd,效能的指標使用 accuracy。

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

 

2.4 訓練模型

  首先讀入資料

from keras.datasets import mnist
 
(X_train, y_train), (X_test, y_test) = mnist.load_data()

   然後將y值進行one-hot編碼

y_train, y_test = to_categorical(y_train), to_categorical(y_test)

   將資料送入模型訓練

model.fit(X_train, y_train, epochs=5, batch_size=32)

   評估模型效能

score = model.evaluate(X_test, y_test, batch_size=128)
print('loss:', score[0])
print('accu:', score[1])

 

2.5 模型預測

  使用模型進行預測

model.predict_classes(X_test, batch_size=128)

 

2.6 完整程式碼

  我們這裡完整程式碼有資料預處理,我們可以很清楚的看到我們特意將資料reshape成一維資料,從最簡單的開始,我們是將28*28的灰度圖轉化為 784的一維資料。

  完整的程式碼如下:

import keras
from keras.models import Sequential
from keras.layers import Dense
from keras.datasets import mnist
from keras.utils import to_categorical
import numpy as np

(X_train, y_train), (X_test, y_test) = mnist.load_data()
# print(X_train.shape[0])
X_train, X_test = X_train.reshape(X_train.shape[0], 784), X_test.reshape(X_test.shape[0], 784)
X_train, X_test = X_train.astype('float32'), X_test.astype('float32')
X_train /= 255
X_test /= 255

y_train, y_test = to_categorical(y_train, num_classes=10), to_categorical(y_test, num_classes=10)

model = Sequential()
model.add(Dense(units=784, activation='relu', input_dim=784))
model.add(Dense(units=10, activation='softmax'))
model.summary()
model.compile(loss='categorical_crossentropy',
              optimizer='sgd',
              metrics=['accuracy'])

model.fit(X_train, y_train, epochs=5, batch_size=32)

score = model.evaluate(X_test, y_test, batch_size=128)
print('loss:', score[0])
print('accu:', score[1])

model.predict_classes(X_test, batch_size=128)

   結果如下:

  128/10000 [..............................] - ETA: 3s
 2176/10000 [=====>........................] - ETA: 0s
 4224/10000 [===========>..................] - ETA: 0s
 6528/10000 [==================>...........] - ETA: 0s
 8704/10000 [=========================>....] - ETA: 0s
10000/10000 [==============================] - 0s 28us/step
loss: 0.20326514495611192
accu: 0.9421

 

3,一個稍微複雜的例子

  使用LeNet5 實現CIFAR10資料集的分類

3.1 選擇模型

  我們這裡仍然初始化一個順序模型(Sequential)

model = Sequential()

 

3.2 構建網路層

  完成input_c1:新增一個二維卷積層,輸入為32*32*3,卷積核大小為5*5,核種類6個,並且假設我們漏了relu

model.add(Conv2D(6, (5, 5), input_shape=(32, 32, 3)))

   剛剛漏了relu,現在可以另外加上

model.add(Activation('relu'))

   完成C1-S1:2*2 下采樣層

model.add(MaxPooling2D(pool_size=(2, 2)))

   完成S2-C3:二維卷積,16個核心,5*5的大小,別忘記relu

model.add(Conv2D(16, (5, 5), activation='relu'))

   完成C3-S4:2*2下采樣層

model.add(MaxPooling2D(pool_size=(2, 2)))

   完成S4-C5:先新增平坦層,(也就是碾平資料),再新增全連線層,輸入120維,啟用函式relu

model.add(Flatten())
model.add(Dense(120, activation='relu'))

   完成C5-F6:新增全連線層,84個輸出,啟用函式relu

model.add(Dense(84, activation='relu'))

   完成F6-OUTPUT:新增全連線層,10個輸出,啟用函式 softmax

model.add(Dense(10, activation='softmax'))

  最後可以通過 .summary()  檢視模型引數情況

model.summary()

 

3.3 編譯模型

  編譯模型的過程主要分為三個,分別是優化函式的選擇,損失函式的選擇,效能評估指標的選擇。

  首先我們設定隨機梯度下降SGD優化演算法的引數。我們learning_rate=0.01, epoch=25, decay=learning_rate/epoch, momentum=0.9, nesterov=False

from keras.optimizers import SGD

learning_rate = 0.01
epoch = 10
decay = learning_rate / epoch
sgd = SGD(lr=learning_rate, momentum=0.9, decay=decay, nesterov=False)

   編譯模型,代價函式loss使用categorical_crossentropy,優化演算法前面已經定義了,效能指標使用accuracy。

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

 

3.4 訓練模型

  首先讀入資料

from keras.datasets import cifar
 
(X_train, y_train), (X_test, y_test) = cifar.load_data()

   然後將y值進行one-hot編碼(預處理)

y_train, y_test = to_categorical(y_train), to_categorical(y_test)

   將資料送入模型訓練,並且設定20%為驗證集

history = model.fit(X_train, y_train, validation_split=0.2, epochs=10, batch_size=32, verbose=1)

   然後可以視覺化歷史訓練的訓練集及驗證集的準確率值,以及視覺化歷史訓練的訓練集及驗證集的損失值。

plt.plot(history.history['accuracy'])
plt.plot(history.history['val_accuracy'])
plt.title('Model accuracy')
plt.ylabel('Accuracy')
plt.xlabel('Epochs')
plt.legend(['Train', 'val'], loc='upper left')
plt.show()

plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.title('Model loss')
plt.ylabel('Loss')
plt.xlabel('Epochs')
plt.legend(['Train', 'val'], loc='upper left')
plt.show()

   模型評估

score = model.evaluate(X_test, y_test, verbose=0)
print(model.metrics_names)
print('loss:', score[0])
print('accu:', score[1])

 

3.5 模型預測

  使用模型進行預測

prediction = model.predict_classes(X_test)
print(prediction[:10])

  顯示混淆矩陣

# 顯示混淆矩陣
import pandas as pd
print(classes)
pd.crosstab(y_gt.reshape(-1),prediction,rownames=['label'],colnames=['predict'])

 

3.6 完整程式碼

   程式碼如下:

from keras.models import Sequential
from keras.layers import Conv2D, Activation, MaxPooling2D, Flatten, Dense
from keras.datasets import cifar10
from keras.utils import to_categorical
import numpy as np
import matplotlib.pyplot as plt
from keras.optimizers import SGD

(X_train, y_train), (X_test, y_test) = cifar10.load_data()
X_train, X_test = X_train.astype('float32'), X_test.astype('float32')
X_train /= 255.0
X_test /= 255.0
y_train, y_test = to_categorical(y_train, num_classes=10), to_categorical(y_test, num_classes=10)

learning_rate = 0.001
epoch = 10
decay = learning_rate / epoch
sgd = SGD(lr=learning_rate, momentum=0.9, decay=decay, nesterov=False)

model = Sequential()
model.add(Conv2D(6, (5, 5), input_shape=(32, 32, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(16, (5, 5), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
model.add(Dense(120, activation='relu'))
model.add(Dense(84, activation='relu'))
model.add(Dense(10, activation='softmax'))
model.summary()
model.compile(loss='categorical_crossentropy',
              optimizer=sgd,
              metrics=['accuracy'])

history = model.fit(X_train, y_train,
                    validation_split=0.2,
                    epochs=20, batch_size=32, verbose=1)
plt.figure(12)
plt.subplot(121)
plt.plot(history.history['acc'])
plt.plot(history.history['val_acc'])
plt.title('Model accuracy')
plt.ylabel('Accuracy')
plt.xlabel('Epochs')
plt.legend(['Train', 'val'], loc='upper left')
plt.subplot(122)
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.title('Model loss')
plt.ylabel('Loss')
plt.xlabel('Epochs')
plt.legend(['Train', 'val'], loc='upper left')
plt.show()

score = model.evaluate(X_test, y_test, verbose=1)
print(model.metrics_names)
print('loss:', score[0])
print('accu:', score[1])

prediction = model.predict_classes(X_test)
print(prediction[:10])

 

   結果如下:

['loss', 'acc']
loss: 1.262941250228882
accu: 0.5561
[3 1 8 0 4 6 1 2 4 1]

  視覺化預測結果如下:

   我們可以看到準確率才達到55%左右,當我們增加epochs的時候,這裡準確率就上去了,這裡不多做嘗試。

 

4,Model式模型

  這部分會實現一個多輸入多輸出的模型

4.1 構建網路

  這裡我們選擇函式式模型(model),所以不需要提前例項化,先將網路結構實現。

  定義1,主要輸入層,接受新聞標題本身,即一個整數序列(每個整數編碼一個詞)。這些整數在1到10000之間(10000個詞的詞彙表),且序列長度為100個詞,命名 main_input

main_input = Input(shape=(100,), dtype='int32', name='main_input')

   定義2,將輸入序列編碼為一個稠密向量的序列,輸出每個向量維度為 512。

x = Embedding(output_dim=512, input_dim=10000, input_length=100)(main_input)

   定義3,LSTM層把向量序列轉換成單個向量,它包含整個序列的上下文資訊,輸出維度為32

lstm_out = LSTM(32)(x)

   定義10,其作為輔助損失,使得即使在模型主損失很高的情況下,LSTM層和 Embedding層都能被平穩地訓練。輸出維度1,啟用函式Sigmoid,命名為aux_output

auxiliary_output = Dense(1, activation='sigmoid', name='aux_output')(lstm_out)

   定義9,輸入輔助資料,五維向量,命名為 aux_input

auxiliary_input = Input(shape=(5,), name='aux_input')

   定義4,將輔助輸入資料與LSTM層的輸出連線起來,輸入到模型中

x = keras.layers.concatenate([lstm_out, auxiliary_input])

   定義5,6,7, 堆疊多個全連線網路層,輸出均為 64維

x = Dense(64, activation='relu')(x)
x = Dense(64, activation='relu')(x)
x = Dense(64, activation='relu')(x)

   定義8,輸出層,啟用函式Sigmoid,命名 main_output

main_output = Dense(1, activation='sigmoid', name='main_output')(x)

 

4.2 定義模型

  定義一個具有兩個輸入和輸出的模型

model = Model(inputs=[main_input, auxiliary_input], outputs=[main_output, auxiliary_output])

 

4.3 編譯模型

  編譯模型,給輔助損失分配 0.2 的權重

model.compile(optimizer='rmsprop',
              loss={'main_output': 'binary_crossentropy', 'aux_output': 'binary_crossentropy'},
              loss_weights={'main_output': 1., 'aux_output': 0.2})

 

4.4 訓練模型

  讀取資料

 

  把資料送入模型訓練

model.fit({'main_input': headline_data, 'aux_input': additional_data},
          {'main_output': headline_labels, 'aux_output': additional_labels},
          epochs=50, batch_size=32,verbose=0)

 

4.5 預測

model.predict({'main_input': headline_data, 'aux_input': additional_data})

 

4.6 完整程式碼

  程式碼如下(這個差點東西):

from keras.models import Sequential, Input, Model
from keras.layers import Conv2D, Activation, MaxPooling2D, Flatten, Dense
from keras.datasets import imdb
from keras.utils import to_categorical
import numpy as np
import matplotlib.pyplot as plt
from keras.optimizers import SGD
import keras
from keras.layers import Embedding, LSTM

max_features = 10000
# 該資料庫含有IMDB的25000條影評,被標記為正面/負面兩種評價,影評已被預處理為詞下標構成的序列
# y_train和y_test  序列的標籤,是一個二值 list
(X_train, y_train), (X_test, y_test) = imdb.load_data(num_words=max_features)
print(X_train.shape, y_train.shape)  # (25000,) (25000,)

main_input = Input(shape=(100,), dtype='int32', name='main_input')
x = Embedding(output_dim=512, input_dim=10000, input_length=100)(main_input)
lstm_out = LSTM(32)(x)
auxiliary_output = Dense(1, activation='sigmoid', name='aux_output')(lstm_out)
auxiliary_input = Input(shape=(5,), name='aux_input')
x = keras.layers.concatenate([lstm_out, auxiliary_input])

x = Dense(64, activation='relu')(x)
x = Dense(64, activation='relu')(x)
x = Dense(64, activation='relu')(x)

main_output = Dense(1, activation='sigmoid', name='main_output')(x)
model = Model(inputs=[main_input, auxiliary_input],
              outputs=[main_output, auxiliary_output])

model.compile(optimizer='rmsprop',
              loss={'main_output': 'binary_crossentropy',
                    'aux_output': 'binary_crossentropy'},
              loss_weights={'main_output': 1, 'aux_output': 0.2})

model.fit({'main_input': headline_data, 'aux_input': additional_data},
          {'main_output': headline_labels, 'aux_input': additional_label},
          epochs=50, batch_size=32, verbose=0)

model.predict({'main_input': headline_data, 'aux_input': additional_data})

 

5,LSTM官網例子

  程式碼:

from __future__ import print_function
from keras.preprocessing import sequence
from keras.models import Sequential
from keras.layers import Dense, Embedding
from keras.layers import LSTM
from keras.datasets import imdb

max_features = 20000
maxlen = 80
batch_size = 32
print('loading data...')
(x_train, y_train), (x_test, y_test) = imdb.load_data(num_words=max_features)
print(len(x_train), 'train sequences')  # 25000 train sequences
print(len(x_test), 'test sequences')  # 25000 test sequences

print('Pad sequences (samples x time)')
x_train = sequence.pad_sequences(x_train, maxlen=maxlen)
x_test = sequence.pad_sequences(x_test, maxlen=maxlen)
print('x_train shape:', x_train.shape)  # x_train shape: (25000, 80)
print('x_test shape:', x_test.shape)  # x_test shape: (25000, 80)

print('Build model...')
model = Sequential()
model.add(Embedding(max_features, 128))
model.add(LSTM(128, dropout=0.2, recurrent_dropout=0.2))
model.add(Dense(1, activation='sigmoid'))

# try using different optimizers and different optimizer configs
model.compile(loss='binary_crossentropy',
              optimizer='adam',
              metrics=['accuracy'])
print("Train...")
model.fit(x_train, y_train,
          batch_size=batch_size,
          epochs=15,
          validation_data=(x_test, y_test))
score, acc = model.evaluate(x_test, y_test,
                            batch_size=batch_size)
print('Test score:', score)
print('Test accuracy:', acc)

   結果:

  672/25000 [..............................] - ETA: 2:12 - loss: 0.0880 - acc: 0.9702
  704/25000 [..............................] - ETA: 2:12 - loss: 0.0866 - acc: 0.9702
  736/25000 [..............................] - ETA: 2:11 - loss: 0.0846 - acc: 0.9715
  768/25000 [..............................] - ETA: 2:11 - loss: 0.0825 - acc: 0.9727
  800/25000 [..............................] - ETA: 2:12 - loss: 0.0823 - acc: 0.9725
  832/25000 [..............................] - ETA: 2:12 - loss: 0.0796 - acc: 0.9736
  864/25000 [>.............................] - ETA: 2:11 - loss: 0.0798 - acc: 0.9722

 

 

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

相關文章