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

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

  那麼在命令行,可以使用://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充分佔用

  來自部落格://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 解決方法

   參考文獻://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() 區別以及參數的坑

  參考地址://blog.csdn.net/mlp750303040/article/details/89207658    //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

  官方文檔://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  例子:編寫自己的回調函數

  (參考文獻://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

 

習題練習

  (參考地址://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

 

 

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