準備數據
!wget --no-check-certificate \
https://storage.googleapis.com/laurencemoroney-blog.appspot.com/horse-or-human.zip \
-O /tmp/horse-or-human.zip
該目錄下又分別包含horses
和humans
子目錄。
簡而言之:訓練集就是用來告訴神經網絡模型"這就是馬的樣子"、"這就是人的樣子"等數據。
這裏需要注意的是,我們並沒有明確地將圖像標註爲馬或人。如果還記得之前的手寫數字例子,它的訓練數據已經標註了"這是一個1","這是一個7"等等。 稍後,我們使用一個叫做ImageGenerator的類--用它從子目錄中讀取圖像,並根據子目錄的名稱自動給圖像貼上標籤。所以,會有一個"訓練"目錄,其中包含一個"馬匹"目錄和一個"人類"目錄。ImageGenerator將爲你適當地標註圖片,從而減少一個編碼步驟。(不僅編程上更方便,而且可以避免一次性把所有訓練數據載入內存,而導致內存不夠等問題。)
讓我們分別定義這些目錄。
# Directory with our training horse pictures
train_horse_dir = os.path.join('/tmp/horse-or-human/horses')
# Directory with our training human pictures
train_human_dir = os.path.join('/tmp/horse-or-human/humans')
建模
像前面的例子一樣添加捲積層CNN,並將最終結果扁平化,以輸送到全連接的層去。
最後我們添加全連接層。
需要注意的是,由於我們面對的是一個兩類分類問題,即二類分類問題,所以我們會用sigmoid激活函數
作爲模型的最後一層,這樣我們網絡的輸出將是一個介於0和1之間的有理數,即當前圖像是1類(而不是0類)的概率。
BTW, 如果是是多個分類,比如前面提到的0~9個分類用的softmax激活函數。
import tensorflow as tf
model = tf.keras.models.Sequential([
tf.keras.layers.Conv2D(16, (3,3), activation='relu', input_shape=(150, 150,3)),
tf.keras.layers.MaxPooling2D(2,2),
tf.keras.layers.Conv2D(32, (3,3), activation='relu'),
tf.keras.layers.MaxPooling2D(2,2),
tf.keras.layers.Conv2D(64, (3,3), activation='relu'),
tf.keras.layers.MaxPooling2D(2,2),
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(512, activation='relu'),
tf.keras.layers.Dense(1, activation='sigmoid')
])
調用model.summary()方法打印出神經元網絡模型的結構信息
接下來,我們將配置模型訓練的參數。我們將用 "binary_crossentropy(二元交叉熵)"衡量損失,因爲這是一個二元分類問題,最終的激活函數是一個sigmoid。關於損失度量的複習,請參見機器學習速成班。我們將使用rmsprop
作爲優化器,學習率爲0.001
。在訓練過程中,我們將希望監控分類精度。
NOTE.我們將使用學習率爲0.001
的rmsprop
優化器。在這種情況下,使用RMSprop優化算法比隨機梯度下降(SGD)更可取,因爲RMSprop可以爲我們自動調整學習率。(其他優化器,如Adam和Adagrad,也會在訓練過程中自動調整學習率,在這裏也同樣有效。)
from tensorflow.keras.optimizers import RMSprop
model.compile(loss=tf.keras.losses.BinaryCrossentropy(),
optimizer=tf.keras.optimizers.RMSprop(lr=0.001),
metrics=['acc'])
數據預處理
讓我們設置訓練數據生成器(ImageDataGenerator
),它將讀取源文件夾中的圖片,將它們轉換爲float32多維數組,並將圖像數據(連同它們的標籤)反饋給神經元網絡。總共需要兩個生成器,有用於產生訓練圖像,一個用於產生驗證圖像。生成器將產生一批大小爲300x300的圖像及其標籤(0或1)。
前面的課中我們已經知道如何對訓練數據做歸一化,進入神經網絡的數據通常應該以某種方式進行歸一化,以使其更容易被網絡處理。在這個例子中,我們將通過將像素值歸一化到[0, 1]範圍
內(最初所有的值都在[0, 255]範圍內)來對圖像進行預處理。
在Keras中,可以通過keras.preprocessing.image.ImageDataGenerator類使用rescale參數來實現歸一化。通過ImageDataGenerator類的.flow
(data, labels)或.flow_from_directory
(directory),可以創建生成器
。然後,這些生成器可以作爲輸入Keras方法的參數,如fit_generator、evaluate_generator和predict_generator都可接收生成器實例爲參數。
from tensorflow.keras.preprocessing.image import ImageDataGenerator
train_datagen = ImageDataGenerator(rescale=1/255)
train_generator = train_datagen.flow_from_directory(
'/tmp/horse-or-human/',
target_size=(150, 150),
batch_size=32,
class_mode='binary'
)
訓練
history = model.fit(
train_generator,
steps_per_epoch=10,
epochs=10,
verbose=1
)
調參
構造神經元網絡模型時,
- 一定會考慮
需要幾個卷積層
? 過濾器應該幾個
?全連接層需要幾個神經元
?
最先想到的肯定是手動修改那些參數,然後觀察訓練的效果(損失和準確度),從而判斷參數的設置是否合理。但是那樣很繁瑣,因爲參數組合會有很多,訓練時間很長
。再進一步,可以手動編寫一些循環,通過遍歷來搜索合適的參數。但是最好利用專門的框架來搜索參數
,不太容易出錯,效果也比前兩種方法更好。
Kerastuner
就是一個可以自動搜索模型訓練參數
的庫。它的基本思路是在需要調整參數的地方插入一個特殊的對象(可指定參數範圍),然後調用類似訓練那樣的search
方法即可。
接下來首先準備訓練數據和需要加載的庫。
如果沒有這個庫先安裝
pip3 install -U keras-tuner
,不然會提示錯誤。ModuleNotFoundError: No module named 'kerastuner
import os
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.optimizers import RMSprop
train_datagen = ImageDataGenerator(rescale=1/255)
validation_datagen = ImageDataGenerator(rescale=1/255)
train_generator = train_datagen.flow_from_directory('/tmp/horse-or-human/',
target_size=(150, 150),batch_size=32,class_mode='binary')
# validation_generator = validation_datagen.flow_from_directory('/tmp/validation-horse-or-human/',
# target_size=(150, 150), batch_size=32,class_mode='binary')
from kerastuner.tuners import Hyperband
from kerastuner.engine.hyperparameters import HyperParameters
import tensorflow as tf
接着創建HyperParameters對象,然後在模型中插入Choice、Int等調參用的對象。
hp=HyperParameters()
def build_model(hp):
model = tf.keras.models.Sequential()
model.add(tf.keras.layers.Conv2D(hp.Choice('num_filters_top_layer',values=[16,64],default=16), (3,3),
activation='relu', input_shape=(150, 150, 3)))
model.add(tf.keras.layers.MaxPooling2D(2, 2))
for i in range(hp.Int("num_conv_layers",1,3)):
model.add(tf.keras.layers.Conv2D(hp.Choice(f'num_filters_layer{i}',values=[16,64],default=16), (3,3), activation='relu'))
model.add(tf.keras.layers.MaxPooling2D(2,2))
model.add(tf.keras.layers.Flatten())
model.add(tf.keras.layers.Dense(hp.Int("hidden_units",128,512,step=32), activation='relu'))
model.add(tf.keras.layers.Dense(1, activation='sigmoid'))
model.compile(loss='binary_crossentropy',optimizer=RMSprop(lr=0.001),metrics=['acc'])
return model
- 第一個參數Choice是CNN的過濾器,範圍是16~64,最好是32的倍數,默認是16
- 第二個參數是Int,設置幾個CNN,13個,且每個CNN卷積神經網絡的過濾器個數爲1664,默認16
- 第三個全連接需要幾個神經元,128~512,步長爲32。即:128、128+32....
他們的第一個參數是name,隨意命名,最好知道且表達出來即可。
然後創建Hyperband對象,這是Kerastuner支持的四種方法的其中一種,可以輕易的限定搜索空間去優化部分參數
。具體資料可以到Kerastuner的網站獲取。關於其他三種tuner:RandomSearch、 BayesianOptimization 和 Sklearn
最後調用search方法。
tuner=Hyperband(
build_model,
objective='val_acc',
max_epochs=10,
directory='horse_human_params',
hyperparameters=hp,
project_name='my_horse_human_project'
)
tuner.search(train_generator,epochs=10,validation_data=validation_generator)
搜索到最優參數後,可以通過下面的程序,用tuner對象提取最優參數構建神經元網絡模型。並調用summary方法觀察優化後的網絡結構。
best_hps=tuner.get_best_hyperparameters(1)[0]
print(best_hps.values)
model=tuner.hypermodel.build(best_hps)
model.summary()