感謝作者分享-http://bjbsair.com/2020-04-07/tech-info/30654.html
Keras基本的使用都已經清楚了,那么這篇主要學習如何使用Keras進行訓練模型,訓練訓練,主要就是“練”,所以多做幾個案例就知道怎么做了。
在本文中,我們將提供一些面向小數據集(幾百張到幾千張圖片)構造高效,實用的圖像分類器的方法。
1,熱身練習——CIFAR10 小圖片分類示例(Sequential式)
示例中CIFAR10采用的是Sequential式來編譯網絡結構。代碼如下:
要訓練模型,首先得知道數據長啥樣
from future import print_function
import keras
from keras.datasets import cifar10
from keras.preprocessing.image import ImageDataGenerator
from keras.models import Sequential
from keras.layers import Dense, Dropout, Activation, Flatten
from keras.layers import Conv2D, MaxPooling2D
batch_size = 32
num_classes = 10
epochs = 100
data_augmentation = True
數據載入
(x_train, y_train), (x_test, y_test) = cifar10.load_data()
多分類標簽生成,我們將其由單個標簽,生成一個熱編碼的形式
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)
網絡結構配置
model = Sequential()
model.add(Conv2D(32, (3, 3), padding='same',
input_shape=x_train.shape[1:])) # (32, 32, 3)
model.add(Activation('relu'))
model.add(Conv2D(32, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Conv2D(64, (3, 3), padding='same'))
model.add(Activation('relu'))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(512))
model.add(Activation('relu'))
model.add(Dropout(num_classes))
model.add(Activation('softmax'))
訓練參數設置
initiate RMSprop optimizer
opt = keras.optimizers.rmsprop(lr=0.0001, decay=1e-6)
Let's train the model using RMSprop
model.compile(loss='categorical_crossentropy',
optimizer=opt,
metrics=['accuracy'])
生成訓練數據
x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
x_train /= 255
x_test /= 255
if not data_augmentation:
print("Not using data augmentation")
model.fit(x_train, y_train,
batch_size=batch_size,
epochs=epochs,
validation_data=(x_test, y_test),
shuffle=True)
else:
print("Using real-time data augmentation")
this will do preprocessing and realtime data augmentation
datagen = ImageDataGenerator(
featurewise_center=False, # set input mean to 0 over the dataset
samplewise_center=False, # set each sample mean to 0
featurewise_std_normalization=False, # divide inputs by std of the dataset
samplewise_std_normalization=False, # divide each input by its std
zca_whitening=False, # apply ZCA whitening
rotation_range=0, # randomly rotate images in the range (degrees, 0 to 180)
width_shift_range=0.1, # randomly shift images horizontally (fraction of total width)
height_shift_range=0.1, # randomly shift images vertically (fraction of total height)
horizontal_flip=True, # randomly flip images
vertical_flip=False) # randomly flip images
Compute quantities required for feature-wise normalization
(std, mean, and principal components if ZCA whitening is applied).
datagen.fit(x_train)
fit訓練
fit the model on batches generated by datagen.flow()
model.fit_generator(datagen.flow(x_train, y_train,
batch_size=batch_size),
epochs=epochs,
validation_data=(x_test, y_test))
截取部分epoch的運行結果:
49056/50000 [============================>.] - ETA: 4s - loss: 0.6400 - acc: 0.7855
49088/50000 [============================>.] - ETA: 4s - loss: 0.6399 - acc: 0.7855
49120/50000 [============================>.] - ETA: 3s - loss: 0.6401 - acc: 0.7855
49152/50000 [============================>.] - ETA: 3s - loss: 0.6399 - acc: 0.7855
49184/50000 [============================>.] - ETA: 3s - loss: 0.6398 - acc: 0.7856
49216/50000 [============================>.] - ETA: 3s - loss: 0.6397 - acc: 0.7856
49248/50000 [============================>.] - ETA: 3s - loss: 0.6395 - acc: 0.7856
49280/50000 [============================>.] - ETA: 3s - loss: 0.6396 - acc: 0.7857
49312/50000 [============================>.] - ETA: 3s - loss: 0.6398 - acc: 0.7856
49344/50000 [============================>.] - ETA: 2s - loss: 0.6404 - acc: 0.7856
49376/50000 [============================>.] - ETA: 2s - loss: 0.6404 - acc: 0.7856
49408/50000 [============================>.] - ETA: 2s - loss: 0.6403 - acc: 0.7856
49440/50000 [============================>.] - ETA: 2s - loss: 0.6404 - acc: 0.7856
49472/50000 [============================>.] - ETA: 2s - loss: 0.6404 - acc: 0.7856
49504/50000 [============================>.] - ETA: 2s - loss: 0.6405 - acc: 0.7855
49536/50000 [============================>.] - ETA: 2s - loss: 0.6406 - acc: 0.7855
49568/50000 [============================>.] - ETA: 1s - loss: 0.6407 - acc: 0.7855
49600/50000 [============================>.] - ETA: 1s - loss: 0.6407 - acc: 0.7854
49632/50000 [============================>.] - ETA: 1s - loss: 0.6410 - acc: 0.7854
49664/50000 [============================>.] - ETA: 1s - loss: 0.6409 - acc: 0.7853
49696/50000 [============================>.] - ETA: 1s - loss: 0.6410 - acc: 0.7853
49728/50000 [============================>.] - ETA: 1s - loss: 0.6412 - acc: 0.7852
49760/50000 [============================>.] - ETA: 1s - loss: 0.6413 - acc: 0.7852
49792/50000 [============================>.] - ETA: 0s - loss: 0.6413 - acc: 0.7852
49824/50000 [============================>.] - ETA: 0s - loss: 0.6413 - acc: 0.7852
49856/50000 [============================>.] - ETA: 0s - loss: 0.6414 - acc: 0.7851
49888/50000 [============================>.] - ETA: 0s - loss: 0.6415 - acc: 0.7851
49920/50000 [============================>.] - ETA: 0s - loss: 0.6415 - acc: 0.7851
49952/50000 [============================>.] - ETA: 0s - loss: 0.6415 - acc: 0.7850
49984/50000 [============================>.] - ETA: 0s - loss: 0.6415 - acc: 0.7850
50000/50000 [==============================] - 228s 5ms/step - loss: 0.6414 - acc: 0.7851 - val_loss: 0.6509 - val_acc: 0.7836
Epoch 55/200
其實跑到第55個epoch,准確率已經達到了 0.785了,后面肯定能達到八九十,我這里就暫停了,讓我電腦歇一歇,跑一天了。其實跑這個就是證明模型沒有問題,而且我的代碼可以運行,僅此而已。
2,針對小數據集的深度學習
本節實驗基於如下配置:
- 2000張訓練圖片構成的數據集,一共有兩個類別,每類1000張
- 安裝有Keras,Scipy,PIL的機器,如果有GPU就更好的了,但是因為我們面對的是小數據集,沒有也可以
- 數據集存放格式如下:
這份數據集來自於Kaggle,元數據集有12500只貓和12500只狗,我們只取了各個類的前500張圖片,另外還從各個類中取了1000張額外圖片用於測試。
下面是數據集的一些示例圖片,圖片的數量非常少,這對於圖像分類來說是個大麻煩。但現實是,很多真實世界圖片獲取是很困難的,我們能得到的樣本數目確實很有限(比如醫學圖像,每張正樣本都意味着一個承受痛苦的病人)對數據科學家而言,我們應該有能夠榨取少量數據的全部價值的能力,而不是簡單的伸手要更多的數據。
在Kaggle的貓狗大戰競賽中,參賽者通過使用現代的深度學習技術達到了98%的正確率,我們只使用了全部數據的8%,因此這個問題對我們來說更難。
經常聽說的一種做法是,深度學習只有在你擁有海量數據的時候才有意義。雖然這種說法並不是完全不對,但卻具有較強的誤導性。當然,深度學習強調從數據中自動學習特征的能力,沒有足夠的訓練樣本,這幾乎是不可能的。尤其是當輸入的數據維度很高(如圖片)時。然而,卷積神經網絡作為深度學習的支柱,被設計為針對“感知”問題最好的模型之一(如圖像分類問題),即使只有很少的數據,網絡也能把特征學的不錯。針對小數據集的神經網絡依然能夠得到合理的結果,並不需要任何手工的特征工程。一言以蔽之,卷積神經網絡大法好!另一方面,深度學習模型天然就具有可重用的特性:比方說,你可以把一個在大規模數據上訓練好的圖像分類或語音識別的模型重用在另一個很不一樣的問題上,而只需要做有限的一點改動。尤其是在計算機視覺領域,許多預訓練的模型現在都被公開下載,並重用在其他問題上以提升在小數據集上的性能。
2.1 數據預處理與數據提升
為了盡量利用我們有限的訓練數據,我們將通過一系列變換堆數據進行提升,這樣我們的模型將看不到任何兩張完全相同的圖片,這有利於我們抑制過擬合,使得模型的泛化能力更好。
在Keras中,這個步驟可以通過keras.preprocessing.image.ImageDataGenerator來實現,也就是圖片預處理生成器,這個類使你可以:
- 在訓練過程中,設置要施行的隨機變換。
- 通過 .flow 或者 .flow_from_directory(directory) 方法實例化一個針對圖像 batch 的生成器,這些生成器可以被用作 Keras模型相關方法的輸入,如 fit_generator,evaluate_generator 和 predict_generator。
datagen = ImageDataGenerator()
datagen.fit(x_train)
生成器初始化 datagen,生成 datagen.fit,計算依賴於數據的變化所需要的統計信息。
最終把數據需要按照每個batch進行划分,這樣就可以送到模型進行訓練了
datagen.flow(x_train, y_train, batch_size=batch_size)
接收numpy數組和標簽為參數,生成經過數據提升或標准化后的batch數據,並在一個無限循環中不斷的返回batch數據。
具體的圖片生成器函數ImageDataGenerator:(他可以用以生成一個 batch的圖像數據,支持實時數據提升,訓練時該函數會無限生成數據,直到達到規定的 epoch次數為止。)
參數意思:
方法有三個,分別是 fit() flow() flow_from_directory() 下面繼續截圖Keras官網的內容:
現在我們看一個例子:
coding:utf-8
'''
使用ImageDataGenerator 來生成圖片,並將其保存在一個臨時文件夾中
下面感受一下數據提升究竟做了什么事情。
'''
import os
from keras.preprocessing.image import ImageDataGenerator, array_to_img, img_to_array, load_img
datagen = ImageDataGenerator(
rotation_range=40, # 是一個0~180的度數,用來指定隨機選擇圖片的角度
width_shift_range=0.2, # 水平方向的隨機移動程度
height_shift_range=0.2, # 豎直方向的隨機移動程度
rescale=1./255, #將在執行其他處理前乘到整個圖像上
shear_range=0.2, # 用來進行剪切變換的程度,參考剪切變換
zoom_range=0.2, # 用來進行隨機的放大
horizontal_flip=True, # 隨機的對圖片進行水平翻轉,此參數用於水平翻轉不影響圖片語義的時候
fill_mode='nearest' # 用來指定當需要進行像素填充,如旋轉,水平和豎直位移的時候
)
pho_path = 'timg.jpg'
img = load_img(pho_path) # this is a PIL image
x = img_to_array(img) # this is a Numpy array with shape (3, 150, 150)
x = x.reshape((1, ) + x.shape) # this is a Numpy array with shape (1, 3, 150, 150)
if not os.path.exists('preview'):
os.mkdir('preview')
i = 0
for batch in datagen.flow(x, batch_size=1,
save_to_dir='preview', save_prefix='durant', save_format='jpg'):
i += 1
if i > 20:
break # otherwise the generator would loop indefitely
這是原圖:
下面是一張圖片被提升以后得到的多個結果:
下面粘貼三個官網的例子:
1,使用.flow() 的例子
2,使用.flow_from_directory(directory)
3,同時變換圖像和 mask
2.2 在小數據集上訓練神經網絡:40行代碼達到80%的准確率
進行圖像分類的正確工具是卷積網絡,所以我們來試試用卷積神經網絡搭建一個初級的模型。因為我們的樣本數很少,所以我們應該對過擬合的問題多加注意。當一個模型從很少的樣本中學習到不能推廣到新數據的模式時,我們稱為出現了過擬合的問題。過擬合發生時,模型試圖使用不相關的特征來進行預測。例如,你有三張伐木工人的照片,有三張水手的照片。六張照片中只有一個伐木工人戴了帽子,如果你認為戴帽子是能將伐木工人與水手區別開的特征,那么此時你就是一個差勁的分類器。
數據提升是對抗過擬合問題的一個武器,但還不夠,因為提升過的數據讓然是高度相關的。對抗過擬合的你應該主要關注的時模型的“熵容量”——模型允許存儲的信息量。能夠存儲更多信息的模型能夠利用更多的特征取得更好的性能,但也有存儲不相關特征的風險。另一方面,只能存儲少量信息的模型會將存儲的特征主要集中在真正相關的特征上,並有更好的泛華性能。
有很多不同的方法來調整模型的“熵容量”,常見的一種選擇是調整模型的參數數目,即模型的層數和每層的規模。另一種方法時對權重進行正則化約束,如L1或L2這種約束會使模型的權重偏向較小的值。
在我們的模型里,我們使用了很小的卷積網絡,只有很少的幾層,每層的濾波器數目也不多。再加上數據提升和Dropout,就差不多了。Dropout通過防止一層看到兩次完全一樣的模式來防止過擬合,相當於也是數據提升的方法。(你可以說Dropout和數據提升都在隨機擾亂數據的相關性)
下面展示的代碼是我們的第一個模型,一個很簡單的3層卷積加上ReLU激活函數,再接max-pooling層,這個結構和Yann LeCun 在1990 年發布的圖像分類器很相似(除了ReLU)
這個實驗的代碼如下:
coding:utf-8
from keras.models import Sequential
from keras.layers import Convolution2D, MaxPooling2D
from keras.layers import Activation, Dropout, Flatten, Dense
from keras.preprocessing.image import ImageDataGenerator
from keras import backend as K
K.set_image_dim_ordering('th')
簡單的三層卷積加上ReLU激活函數,再接一個max-pooling層
model = Sequential()
model.add(Convolution2D(32, 3, 3, input_shape=(3, 150, 150)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Convolution2D(32, 3, 3))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Convolution2D(64, 3, 3))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
the model so far outputs 3D feature maps (height, width, features)
然后我們接了兩個全連接網絡,並以單個神經元和Sigmoid激活結束模型
這種選擇會產生一個二分類的結果,與這種配置項適應,損失函數選擇binary_crossentropy
this converts our 3D feature maps to 1D feature vectors
model.add(Flatten())
添加隱藏層神經元的數量和激活函數
model.add(Dense(64))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(1))
model.add(Activation('sigmoid'))
model.compile(loss='binary_crossentropy',
optimizer='rmsprop',
metrics=['accuracy'])
next read data
使用 .flow_from_directory() 來從我們的jpgs圖片中直接產生數據和標簽
this is the augmentation configuration we will use for training
train_datagen = ImageDataGenerator(
rescale=1./255,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True
)
this is the augmentation configuration we will use for testing only rescaling
test_datagen = ImageDataGenerator(rescale=1./255)
this is a generator that will read pictures found in subfliders
of 'data/train'. and indefinitely generate batches of augmented image data
train_generator = train_datagen.flow_from_directory(
'DogsVSCats/train', # this is the target directory
target_size=(150, 150), # all image will be resize to 150*150
batch_size=32,
class_mode='binary'
) # since we use binary_crossentropy loss,we need binary labels
this is a similar generator, for validation data
validation_generator = test_datagen.flow_from_directory(
'DogsVSCats/valid', # this is the target directory
target_size=(150, 150), # all image will be resize to 150*150
batch_size=32,
class_mode='binary'
)
然后我們可以用這個生成器來訓練網絡了。
model.fit_generator(
train_generator,
samples_per_epoch=2000,
nb_epoch=50,
validation_data=validation_generator,
nb_val_samples=800
)
model.save_weights('first_try.h5') #always save your weights after training or duraing trianing
部分結果如下:
Epoch 1/50
62/62 [==============================] - 66s 1s/step - loss: 0.7043 - acc: 0.5238 - val_loss: 0.6798 - val_acc: 0.5015
Epoch 2/50
62/62 [==============================] - 63s 1s/step - loss: 0.6837 - acc: 0.5762 - val_loss: 0.6481 - val_acc: 0.6837
Epoch 3/50
62/62 [==============================] - 63s 1s/step - loss: 0.6465 - acc: 0.6503 - val_loss: 0.5826 - val_acc: 0.6827
Epoch 4/50
62/62 [==============================] - 63s 1s/step - loss: 0.6077 - acc: 0.6884 - val_loss: 0.5512 - val_acc: 0.7447
Epoch 5/50
62/62 [==============================] - 63s 1s/step - loss: 0.5568 - acc: 0.7088 - val_loss: 0.5127 - val_acc: 0.7357
Epoch 6/50
62/62 [==============================] - 63s 1s/step - loss: 0.5469 - acc: 0.7241 - val_loss: 0.4962 - val_acc: 0.7578
Epoch 7/50
62/62 [==============================] - 63s 1s/step - loss: 0.5236 - acc: 0.7446 - val_loss: 0.4325 - val_acc: 0.8028
Epoch 8/50
62/62 [==============================] - 63s 1s/step - loss: 0.4842 - acc: 0.7751 - val_loss: 0.4710 - val_acc: 0.7758
Epoch 9/50
62/62 [==============================] - 63s 1s/step - loss: 0.4693 - acc: 0.7726 - val_loss: 0.4383 - val_acc: 0.7808
Epoch 10/50
62/62 [==============================] - 63s 1s/step - loss: 0.4545 - acc: 0.7952 - val_loss: 0.3806 - val_acc: 0.8298
Epoch 11/50
62/62 [==============================] - 63s 1s/step - loss: 0.4331 - acc: 0.8031 - val_loss: 0.3781 - val_acc: 0.8248
Epoch 12/50
62/62 [==============================] - 63s 1s/step - loss: 0.4178 - acc: 0.8162 - val_loss: 0.3146 - val_acc: 0.8799
Epoch 13/50
62/62 [==============================] - 63s 1s/step - loss: 0.3926 - acc: 0.8275 - val_loss: 0.3030 - val_acc: 0.8739
Epoch 14/50
62/62 [==============================] - 63s 1s/step - loss: 0.3854 - acc: 0.8295 - val_loss: 0.2835 - val_acc: 0.8929
Epoch 15/50
62/62 [==============================] - 63s 1s/step - loss: 0.3714 - acc: 0.8303 - val_loss: 0.2882 - val_acc: 0.8879
Epoch 16/50
62/62 [==============================] - 63s 1s/step - loss: 0.3596 - acc: 0.8517 - val_loss: 0.3727 - val_acc: 0.8228
Epoch 17/50
62/62 [==============================] - 63s 1s/step - loss: 0.3369 - acc: 0.8568 - val_loss: 0.3638 - val_acc: 0.8328
Epoch 18/50
62/62 [==============================] - 63s 1s/step - loss: 0.3249 - acc: 0.8608 - val_loss: 0.2589 - val_acc: 0.8819
Epoch 19/50
62/62 [==============================] - 63s 1s/step - loss: 0.3348 - acc: 0.8548 - val_loss: 0.2273 - val_acc: 0.9079
Epoch 20/50
62/62 [==============================] - 63s 1s/step - loss: 0.2979 - acc: 0.8754 - val_loss: 0.1737 - val_acc: 0.9389
Epoch 21/50
62/62 [==============================] - 63s 1s/step - loss: 0.2980 - acc: 0.8686 - val_loss: 0.2198 - val_acc: 0.9189
Epoch 22/50
62/62 [==============================] - 63s 1s/step - loss: 0.2789 - acc: 0.8815 - val_loss: 0.2040 - val_acc: 0.9109
Epoch 23/50
62/62 [==============================] - 63s 1s/step - loss: 0.2793 - acc: 0.8891 - val_loss: 0.1388 - val_acc: 0.9479
Epoch 24/50
62/62 [==============================] - 63s 1s/step - loss: 0.2799 - acc: 0.8865 - val_loss: 0.1565 - val_acc: 0.9419
Epoch 25/50
62/62 [==============================] - 63s 1s/step - loss: 0.2513 - acc: 0.8949 - val_loss: 0.1467 - val_acc: 0.9510
Epoch 26/50
62/62 [==============================] - 63s 1s/step - loss: 0.2551 - acc: 0.9029 - val_loss: 0.1281 - val_acc: 0.9520
Epoch 27/50
62/62 [==============================] - 63s 1s/step - loss: 0.2387 - acc: 0.8961 - val_loss: 0.1590 - val_acc: 0.9409
Epoch 28/50
62/62 [==============================] - 63s 1s/step - loss: 0.2449 - acc: 0.9054 - val_loss: 0.1250 - val_acc: 0.9580
Epoch 29/50
62/62 [==============================] - 63s 1s/step - loss: 0.2158 - acc: 0.9218 - val_loss: 0.0881 - val_acc: 0.9780
Epoch 30/50
62/62 [==============================] - 63s 1s/step - loss: 0.2286 - acc: 0.9158 - val_loss: 0.1012 - val_acc: 0.9660
Epoch 31/50
62/62 [==============================] - 63s 1s/step - loss: 0.2017 - acc: 0.9181 - val_loss: 0.1109 - val_acc: 0.9570
Epoch 32/50
62/62 [==============================] - 63s 1s/step - loss: 0.1957 - acc: 0.9213 - val_loss: 0.1160 - val_acc: 0.9560
Epoch 33/50
62/62 [==============================] - 63s 1s/step - loss: 0.2046 - acc: 0.9249 - val_loss: 0.0600 - val_acc: 0.9840
Epoch 34/50
62/62 [==============================] - 63s 1s/step - loss: 0.1967 - acc: 0.9206 - val_loss: 0.0713 - val_acc: 0.9790
Epoch 35/50
62/62 [==============================] - 63s 1s/step - loss: 0.2238 - acc: 0.9153 - val_loss: 0.3123 - val_acc: 0.8929
Epoch 36/50
62/62 [==============================] - 63s 1s/step - loss: 0.1841 - acc: 0.9317 - val_loss: 0.0751 - val_acc: 0.9740
Epoch 37/50
62/62 [==============================] - 63s 1s/step - loss: 0.1890 - acc: 0.9279 - val_loss: 0.1030 - val_acc: 0.9700
這個模型在50個epoch后的准確率為 79%~81%。(但是我的准確率在38個epoch卻達到了驚人的92%,也是恐怖)沒有做模型和超參數的優化。
注意這個准確率的變化可能會比較大,因為准確率本來就是一個變化較高的評估參數,而且我們的訓練樣本比較少,所以比較好的驗證方法就是使用K折交叉驗證,但每輪驗證中我們都要訓練一個模型。
2.3 Keras報錯:ValueError: Negative dimension size caused by subtracting 2 ...
使用Keras時遇到如下錯誤:
1
2
ValueError: Negative dimension size caused by subtracting 2 from 1
for 'block2_pool/MaxPool' (op: 'MaxPool') with input shapes: [?,1,75,128].
解決方法:這個是圖片的通道順序問題。
以128*128的RGB圖像為例 channels_last應該將數據組織為(128, 128, 3),而channels_first將數據組織為(3, 128, 128)。
通過查看函數 set_image_dim_ording():
def set_image_dim_ordering(dim_ordering):
"""Legacy setter for image_data_format
.
Arguments
dim_ordering: string. tf
or th
.
Raises
ValueError: if dim_ordering
is invalid.
"""
global _IMAGE_DATA_FORMAT
if dim_ordering not in {'tf', 'th'}:
raise ValueError('Unknown dim_ordering:', dim_ordering)
if dim_ordering == 'th':
data_format = 'channels_first'
else:
data_format = 'channels_last'
_IMAGE_DATA_FORMAT = data_format
可知,tf對應原本的 channels_last,th對應 channels_first,因此添加下面代碼即可解決問題:
from keras import backend as K
K.set_image_dim_ordering('th')
這樣保證要使用的通道順序和配置的通道順序一致即可。
3,多分類簡易網絡結構(Sequential)
數據描述:
共有500張圖片,分為大巴車、恐龍、大象、鮮花和馬五個類,每個類100張。下載地址:http://pan.baidu.com/s/1nuqlTnN
編號分別以3,4,5,6,7開頭,各為一類。我從其中每類選出20張作為測試,其余80張作為訓練。因此最終訓練圖片400張,測試圖片100張,共5類。如下圖:
(注意,這里需要自己將圖片分為五類,包括訓練集和測試集)
3.1 載入與模型網絡構建
代碼如下:
載入與模型網絡構建
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D
from keras.layers import Activation, Dropout, Flatten, Dense
def built_model():
model = Sequential()
model.add(Conv2D(32, (3, 3), input_shape=(150, 150, 3)))
filter大小為3*3 數量為32個,原始圖像大小3,150 150
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(32, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
this converts ours 3D feature maps to 1D feature vector
model.add(Dense(64))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(5)) # 幾個分類就幾個dense
model.add(Activation('softmax')) # 多分類
3.2 圖像預處理
下面我們開始准備數據,使用 .flow_from_directory() 來從我們的jpgs圖片中直接產生數據和標簽。
其中值得留意的是:
- ImageDataGenerate:用以生成一個 batch 的圖像數據,支持實時數據提升。訓練時該函數會無限生成數據,直到達到規定的epoch次數為止。
- flow_from_directory(directory):以文件夾路徑為參數,生成經過數據提升/歸一化后的數據,在一個無限循環中無限產生batch數據。
def generate_data():
'''
flow_from_directory是計算數據的一些屬性值,之后再訓練階段直接丟進去這些生成器。
通過這個函數來准確數據,可以讓我們的jpgs圖片中直接產生數據和標簽
:return:
'''
train_datagen = ImageDataGenerator(
rescale=1. / 255,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True
)
test_datagen = ImageDataGenerator(rescale=1. / 255)
train_generator = train_datagen.flow_from_directory(
'data/mytrain',
target_size=(150, 150), # all images will be resized to 150*150
batch_size=32,
class_mode='categorical' # 多分類
)
validation_generator = test_datagen.flow_from_directory(
'data/mytest',
target_size=(150, 150),
batch_size=32,
class_mode='categorical' # 多分類
)
return train_generator, validation_generator
3.3 載入與模型網絡構建
代碼如下:(和上面兩分類的沒多少差別)
def built_model():
載入與模型網絡構建
model = Sequential()
model.add(Conv2D(32, (3, 3), input_shape=(150, 150, 3)))
filter大小為3*3 數量為32個,原始圖像大小3,150 150
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(32, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
this converts ours 3D feature maps to 1D feature vector
model.add(Dense(64))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(5)) # 幾個分類就幾個dense
model.add(Activation('softmax')) # 多分類
model.compile(loss='binary_corssentropy',
optimizer='rmsprop',
metrics=['accuracy'])
優化器rmsprop:除學習率可調整外,建議保持優化器的其他默認參數不變
model.compile(loss='categorical_crossentropy',
optimizer='rmsprop',
metrics=['accuracy'])
model.summary()
return model
3.4 訓練
代碼如下:
def train_model(model=None):
if model is None:
model = built_model()
model.fit_generator(
train_generator,
sampels_per_epoch 相當於每個epoch數據量峰值,
每個epoch以經過模型的樣本數達到samples_per_epoch時,記一個epoch結束
samples_per_epoch=2000,
nb_epoch=50,
validation_data=validation_generator,
nb_val_samples=800
)
model.save_weights('first_try_animal.h5')
3.5 部分結果展示和所有源碼
部分結果如下(這里僅展示一個完整的epoch訓練數據):
Epoch 50/50
1/62 [..............................] - ETA: 1:10 - loss: 0.4921 - acc: 0.9062
2/62 [..............................] - ETA: 1:13 - loss: 0.2460 - acc: 0.9531
3/62 [>.............................] - ETA: 1:12 - loss: 0.1640 - acc: 0.9688
4/62 [>.............................] - ETA: 1:02 - loss: 0.1230 - acc: 0.9766
5/62 [=>............................] - ETA: 1:02 - loss: 0.0985 - acc: 0.9812
6/62 [=>............................] - ETA: 1:02 - loss: 0.0821 - acc: 0.9844
7/62 [==>...........................] - ETA: 1:02 - loss: 0.0704 - acc: 0.9866
8/62 [==>...........................] - ETA: 1:00 - loss: 0.0616 - acc: 0.9883
9/62 [===>..........................] - ETA: 59s - loss: 0.0547 - acc: 0.9896
10/62 [===>..........................] - ETA: 59s - loss: 0.0493 - acc: 0.9906
11/62 [====>.........................] - ETA: 59s - loss: 0.0448 - acc: 0.9915
12/62 [====>.........................] - ETA: 58s - loss: 0.0412 - acc: 0.9922
13/62 [=====>........................] - ETA: 57s - loss: 0.0380 - acc: 0.9928
14/62 [=====>........................] - ETA: 55s - loss: 0.0363 - acc: 0.9933
15/62 [======>.......................] - ETA: 54s - loss: 0.0339 - acc: 0.9938
16/62 [======>.......................] - ETA: 53s - loss: 0.0318 - acc: 0.9941
17/62 [=======>......................] - ETA: 51s - loss: 0.0316 - acc: 0.9945
18/62 [=======>......................] - ETA: 50s - loss: 0.0298 - acc: 0.9948
19/62 [========>.....................] - ETA: 49s - loss: 0.0283 - acc: 0.9951
20/62 [========>.....................] - ETA: 48s - loss: 0.0268 - acc: 0.9953
21/62 [=========>....................] - ETA: 47s - loss: 0.0259 - acc: 0.9955
22/62 [=========>....................] - ETA: 46s - loss: 0.0247 - acc: 0.9957
23/62 [==========>...................] - ETA: 45s - loss: 0.0236 - acc: 0.9959
24/62 [==========>...................] - ETA: 44s - loss: 0.0227 - acc: 0.9961
25/62 [===========>..................] - ETA: 42s - loss: 0.0218 - acc: 0.9962
26/62 [===========>..................] - ETA: 42s - loss: 0.0209 - acc: 0.9964
27/62 [============>.................] - ETA: 41s - loss: 0.0202 - acc: 0.9965
28/62 [============>.................] - ETA: 40s - loss: 0.0194 - acc: 0.9967
29/62 [=============>................] - ETA: 39s - loss: 0.0188 - acc: 0.9968
30/62 [=============>................] - ETA: 37s - loss: 0.0181 - acc: 0.9969
31/62 [==============>...............] - ETA: 36s - loss: 0.0176 - acc: 0.9970
32/62 [==============>...............] - ETA: 34s - loss: 0.0170 - acc: 0.9971
33/62 [==============>...............] - ETA: 33s - loss: 0.0165 - acc: 0.9972
34/62 [===============>..............] - ETA: 32s - loss: 0.0160 - acc: 0.9972
35/62 [===============>..............] - ETA: 31s - loss: 0.0156 - acc: 0.9973
36/62 [================>.............] - ETA: 30s - loss: 0.0151 - acc: 0.9974
37/62 [================>.............] - ETA: 29s - loss: 0.0147 - acc: 0.9975
38/62 [=================>............] - ETA: 28s - loss: 0.0146 - acc: 0.9975
39/62 [=================>............] - ETA: 27s - loss: 0.0142 - acc: 0.9976
40/62 [==================>...........] - ETA: 26s - loss: 0.0139 - acc: 0.9977
41/62 [==================>...........] - ETA: 24s - loss: 0.0135 - acc: 0.9977
42/62 [===================>..........] - ETA: 23s - loss: 0.0132 - acc: 0.9978
43/62 [===================>..........] - ETA: 22s - loss: 0.0129 - acc: 0.9978
44/62 [====================>.........] - ETA: 21s - loss: 0.0126 - acc: 0.9979
45/62 [====================>.........] - ETA: 20s - loss: 0.0123 - acc: 0.9979
46/62 [=====================>........] - ETA: 19s - loss: 0.0135 - acc: 0.9973
47/62 [=====================>........] - ETA: 17s - loss: 0.0153 - acc: 0.9967
48/62 [======================>.......] - ETA: 16s - loss: 0.0254 - acc: 0.9961
49/62 [======================>.......] - ETA: 15s - loss: 0.0249 - acc: 0.9962
50/62 [=======================>......] - ETA: 14s - loss: 0.0244 - acc: 0.9962
51/62 [=======================>......] - ETA: 13s - loss: 0.0338 - acc: 0.9957
52/62 [========================>.....] - ETA: 11s - loss: 0.0332 - acc: 0.9958
53/62 [========================>.....] - ETA: 10s - loss: 0.0329 - acc: 0.9959
54/62 [=========================>....] - ETA: 9s - loss: 0.0323 - acc: 0.9959
55/62 [=========================>....] - ETA: 8s - loss: 0.0317 - acc: 0.9960
56/62 [==========================>...] - ETA: 7s - loss: 0.0393 - acc: 0.9950
57/62 [==========================>...] - ETA: 5s - loss: 0.0511 - acc: 0.9940
58/62 [===========================>..] - ETA: 4s - loss: 0.0502 - acc: 0.9941
59/62 [===========================>..] - ETA: 3s - loss: 0.0494 - acc: 0.9942
60/62 [============================>.] - ETA: 2s - loss: 0.0518 - acc: 0.9938
61/62 [============================>.] - ETA: 1s - loss: 0.0535 - acc: 0.9933
62/62 [==============================] - 271s 4s/step - loss: 0.0607 - acc: 0.9929 - val_loss: 0.7166 - val_acc: 0.9300
源碼如下:
載入與模型網絡構建
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D
from keras.layers import Activation, Dropout, Flatten, Dense
from keras.preprocessing.image import ImageDataGenerator
import numpy as np
import os
def built_model():
載入與模型網絡構建
model = Sequential()
model.add(Conv2D(32, (3, 3), input_shape=(150, 150, 3)))
filter大小為3*3 數量為32個,原始圖像大小3,150 150
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(32, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
this converts ours 3D feature maps to 1D feature vector
model.add(Dense(64))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(5)) # 幾個分類就幾個dense
model.add(Activation('softmax')) # 多分類
model.compile(loss='binary_corssentropy',
optimizer='rmsprop',
metrics=['accuracy'])
優化器rmsprop:除學習率可調整外,建議保持優化器的其他默認參數不變
model.compile(loss='categorical_crossentropy',
optimizer='rmsprop',
metrics=['accuracy'])
model.summary()
return model
def generate_data():
'''
flow_from_directory是計算數據的一些屬性值,之后再訓練階段直接丟進去這些生成器。
通過這個函數來准確數據,可以讓我們的jpgs圖片中直接產生數據和標簽
:return:
'''
train_datagen = ImageDataGenerator(
rescale=1. / 255,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True
)
test_datagen = ImageDataGenerator(rescale=1. / 255)
train_generator = train_datagen.flow_from_directory(
'data/mytrain',
target_size=(150, 150), # all images will be resized to 150*150
batch_size=32,
class_mode='categorical' # 多分類
)
validation_generator = test_datagen.flow_from_directory(
'data/mytest',
target_size=(150, 150),
batch_size=32,
class_mode='categorical' # 多分類
)
return train_generator, validation_generator
def train_model(model=None):
if model is None:
model = built_model()
model.fit_generator(
train_generator,
sampels_per_epoch 相當於每個epoch數據量峰值,
每個epoch以經過模型的樣本數達到samples_per_epoch時,記一個epoch結束
samples_per_epoch=2000,
nb_epoch=50,
validation_data=validation_generator,
nb_val_samples=800
)
model.save_weights('first_try_animal.h5')
if name == 'main':
train_generator, validation_generator = generate_data()
train_model()
當loss出現負數,肯定是之前多分類的標簽哪些設置的不對,
注意上面的 steps_per_epoch和validation_steps的值(應該是這樣計算出來的):
model=build_model(input_shape=(IMG_W,IMG_H,IMG_CH)) # 輸入的圖片維度
模型的訓練
model.fit_generator(train_generator, # 數據流
steps_per_epoch=train_samples_num // batch_size,
epochs=epochs,
validation_data=val_generator,
validation_steps=val_samples_num // batch_size)
3.6 畫圖展示和使用模型預測
最后我們可以通過圖直觀的查看訓練過程中的 loss 和 acc ,看看其變化趨勢。下面是代碼:
畫圖,將訓練時的acc和loss都繪制到圖上
import matplotlib.pyplot as plt
def plot_training(history):
plt.figure(12)
plt.subplot(121)
train_acc = history.history['acc']
val_acc = history.history['val_acc']
epochs = range(len(train_acc))
plt.plot(epochs, train_acc, 'b',label='train_acc')
plt.plot(epochs, val_acc, 'r',label='test_acc')
plt.title('Train and Test accuracy')
plt.legend()
plt.subplot(122)
train_loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(len(train_loss))
plt.plot(epochs, train_loss, 'b',label='train_loss')
plt.plot(epochs, val_loss, 'r',label='test_loss')
plt.title('Train and Test loss')
plt.legend()
plt.show()
我們也可以通過訓練好的模型去預測新的樣本。
單張樣本的預測代碼如下:
用訓練好的模型來預測新樣本
from PIL import Image
from keras.preprocessing import image
def predict(model, img_path, target_size):
img=Image.open(img_path) # 加載圖片
if img.size != target_size:
img = img.resize(target_size)
x = image.img_to_array(img)
x *=1./255 # 相當於ImageDataGenerator(rescale=1. / 255)
x = np.expand_dims(x, axis=0) # 調整圖片維度
preds = model.predict(x) # 預測
return preds[0]
批量預測(一個文件夾中的所有文件):
預測一個文件夾中的所有圖片
new_sample_gen=ImageDataGenerator(rescale=1. / 255)
newsample_generator=new_sample_gen.flow_from_directory(
'E:\PyProjects\DataSet\FireAI\DeepLearning',
target_size=(IMG_W, IMG_H),
batch_size=16,
class_mode=None,
shuffle=False)
predicted=model.predict_generator(newsample_generator)
print(predicted)
注意我們上面保存模型是保存的權重,而不是模型,保存模型的代碼如下:
模型的加載,預測
from keras.models import load_model
saved_model=load_model('animal.h5')
predicted=saved_model.predict_generator(newsample_generator)
print(predicted) # saved_model的結果和前面的model結果一致,表面模型正確保存和加載
如果保存的是權重,直接加載,會報錯:
最后,我自己的預測代碼:
用訓練好的模型來預測新的樣本
from keras.preprocessing import image
import cv2
import numpy as np
from keras.models import load_model
def predict(model, img_path, target_size):
img = cv2.imread(img_path)
if img.shape != target_size:
img = cv2.resize(img, target_size)
print(img.shape)
x = image.img_to_array(img)
x *= 1. / 255 # 相當於ImageDataGenerator(rescale=1. / 255)
x = np.expand_dims(x, axis=0) # 調整圖片維度
preds = model.predict(x)
return preds[0]
if name == 'main':
model_path = 'animal.h5'
model = load_model(model_path)
target_size = (150, 150)
img_path = 'data/test/300.jpg'
res = predict(model, img_path, target_size)
print(res)感謝作者分享-http://bjbsair.com/2020-04-07/tech-info/30654.html
Keras基本的使用都已經清楚了,那么這篇主要學習如何使用Keras進行訓練模型,訓練訓練,主要就是“練”,所以多做幾個案例就知道怎么做了。
在本文中,我們將提供一些面向小數據集(幾百張到幾千張圖片)構造高效,實用的圖像分類器的方法。
1,熱身練習——CIFAR10 小圖片分類示例(Sequential式)
示例中CIFAR10采用的是Sequential式來編譯網絡結構。代碼如下:
要訓練模型,首先得知道數據長啥樣
from future import print_function
import keras
from keras.datasets import cifar10
from keras.preprocessing.image import ImageDataGenerator
from keras.models import Sequential
from keras.layers import Dense, Dropout, Activation, Flatten
from keras.layers import Conv2D, MaxPooling2D
batch_size = 32
num_classes = 10
epochs = 100
data_augmentation = True
數據載入
(x_train, y_train), (x_test, y_test) = cifar10.load_data()
多分類標簽生成,我們將其由單個標簽,生成一個熱編碼的形式
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)
網絡結構配置
model = Sequential()
model.add(Conv2D(32, (3, 3), padding='same',
input_shape=x_train.shape[1:])) # (32, 32, 3)
model.add(Activation('relu'))
model.add(Conv2D(32, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Conv2D(64, (3, 3), padding='same'))
model.add(Activation('relu'))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(512))
model.add(Activation('relu'))
model.add(Dropout(num_classes))
model.add(Activation('softmax'))
訓練參數設置
initiate RMSprop optimizer
opt = keras.optimizers.rmsprop(lr=0.0001, decay=1e-6)
Let's train the model using RMSprop
model.compile(loss='categorical_crossentropy',
optimizer=opt,
metrics=['accuracy'])
生成訓練數據
x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
x_train /= 255
x_test /= 255
if not data_augmentation:
print("Not using data augmentation")
model.fit(x_train, y_train,
batch_size=batch_size,
epochs=epochs,
validation_data=(x_test, y_test),
shuffle=True)
else:
print("Using real-time data augmentation")
this will do preprocessing and realtime data augmentation
datagen = ImageDataGenerator(
featurewise_center=False, # set input mean to 0 over the dataset
samplewise_center=False, # set each sample mean to 0
featurewise_std_normalization=False, # divide inputs by std of the dataset
samplewise_std_normalization=False, # divide each input by its std
zca_whitening=False, # apply ZCA whitening
rotation_range=0, # randomly rotate images in the range (degrees, 0 to 180)
width_shift_range=0.1, # randomly shift images horizontally (fraction of total width)
height_shift_range=0.1, # randomly shift images vertically (fraction of total height)
horizontal_flip=True, # randomly flip images
vertical_flip=False) # randomly flip images
Compute quantities required for feature-wise normalization
(std, mean, and principal components if ZCA whitening is applied).
datagen.fit(x_train)
fit訓練
fit the model on batches generated by datagen.flow()
model.fit_generator(datagen.flow(x_train, y_train,
batch_size=batch_size),
epochs=epochs,
validation_data=(x_test, y_test))
截取部分epoch的運行結果:
49056/50000 [============================>.] - ETA: 4s - loss: 0.6400 - acc: 0.7855
49088/50000 [============================>.] - ETA: 4s - loss: 0.6399 - acc: 0.7855
49120/50000 [============================>.] - ETA: 3s - loss: 0.6401 - acc: 0.7855
49152/50000 [============================>.] - ETA: 3s - loss: 0.6399 - acc: 0.7855
49184/50000 [============================>.] - ETA: 3s - loss: 0.6398 - acc: 0.7856
49216/50000 [============================>.] - ETA: 3s - loss: 0.6397 - acc: 0.7856
49248/50000 [============================>.] - ETA: 3s - loss: 0.6395 - acc: 0.7856
49280/50000 [============================>.] - ETA: 3s - loss: 0.6396 - acc: 0.7857
49312/50000 [============================>.] - ETA: 3s - loss: 0.6398 - acc: 0.7856
49344/50000 [============================>.] - ETA: 2s - loss: 0.6404 - acc: 0.7856
49376/50000 [============================>.] - ETA: 2s - loss: 0.6404 - acc: 0.7856
49408/50000 [============================>.] - ETA: 2s - loss: 0.6403 - acc: 0.7856
49440/50000 [============================>.] - ETA: 2s - loss: 0.6404 - acc: 0.7856
49472/50000 [============================>.] - ETA: 2s - loss: 0.6404 - acc: 0.7856
49504/50000 [============================>.] - ETA: 2s - loss: 0.6405 - acc: 0.7855
49536/50000 [============================>.] - ETA: 2s - loss: 0.6406 - acc: 0.7855
49568/50000 [============================>.] - ETA: 1s - loss: 0.6407 - acc: 0.7855
49600/50000 [============================>.] - ETA: 1s - loss: 0.6407 - acc: 0.7854
49632/50000 [============================>.] - ETA: 1s - loss: 0.6410 - acc: 0.7854
49664/50000 [============================>.] - ETA: 1s - loss: 0.6409 - acc: 0.7853
49696/50000 [============================>.] - ETA: 1s - loss: 0.6410 - acc: 0.7853
49728/50000 [============================>.] - ETA: 1s - loss: 0.6412 - acc: 0.7852
49760/50000 [============================>.] - ETA: 1s - loss: 0.6413 - acc: 0.7852
49792/50000 [============================>.] - ETA: 0s - loss: 0.6413 - acc: 0.7852
49824/50000 [============================>.] - ETA: 0s - loss: 0.6413 - acc: 0.7852
49856/50000 [============================>.] - ETA: 0s - loss: 0.6414 - acc: 0.7851
49888/50000 [============================>.] - ETA: 0s - loss: 0.6415 - acc: 0.7851
49920/50000 [============================>.] - ETA: 0s - loss: 0.6415 - acc: 0.7851
49952/50000 [============================>.] - ETA: 0s - loss: 0.6415 - acc: 0.7850
49984/50000 [============================>.] - ETA: 0s - loss: 0.6415 - acc: 0.7850
50000/50000 [==============================] - 228s 5ms/step - loss: 0.6414 - acc: 0.7851 - val_loss: 0.6509 - val_acc: 0.7836
Epoch 55/200
其實跑到第55個epoch,准確率已經達到了 0.785了,后面肯定能達到八九十,我這里就暫停了,讓我電腦歇一歇,跑一天了。其實跑這個就是證明模型沒有問題,而且我的代碼可以運行,僅此而已。
2,針對小數據集的深度學習
本節實驗基於如下配置:
- 2000張訓練圖片構成的數據集,一共有兩個類別,每類1000張
- 安裝有Keras,Scipy,PIL的機器,如果有GPU就更好的了,但是因為我們面對的是小數據集,沒有也可以
- 數據集存放格式如下:
這份數據集來自於Kaggle,元數據集有12500只貓和12500只狗,我們只取了各個類的前500張圖片,另外還從各個類中取了1000張額外圖片用於測試。
下面是數據集的一些示例圖片,圖片的數量非常少,這對於圖像分類來說是個大麻煩。但現實是,很多真實世界圖片獲取是很困難的,我們能得到的樣本數目確實很有限(比如醫學圖像,每張正樣本都意味着一個承受痛苦的病人)對數據科學家而言,我們應該有能夠榨取少量數據的全部價值的能力,而不是簡單的伸手要更多的數據。
在Kaggle的貓狗大戰競賽中,參賽者通過使用現代的深度學習技術達到了98%的正確率,我們只使用了全部數據的8%,因此這個問題對我們來說更難。
經常聽說的一種做法是,深度學習只有在你擁有海量數據的時候才有意義。雖然這種說法並不是完全不對,但卻具有較強的誤導性。當然,深度學習強調從數據中自動學習特征的能力,沒有足夠的訓練樣本,這幾乎是不可能的。尤其是當輸入的數據維度很高(如圖片)時。然而,卷積神經網絡作為深度學習的支柱,被設計為針對“感知”問題最好的模型之一(如圖像分類問題),即使只有很少的數據,網絡也能把特征學的不錯。針對小數據集的神經網絡依然能夠得到合理的結果,並不需要任何手工的特征工程。一言以蔽之,卷積神經網絡大法好!另一方面,深度學習模型天然就具有可重用的特性:比方說,你可以把一個在大規模數據上訓練好的圖像分類或語音識別的模型重用在另一個很不一樣的問題上,而只需要做有限的一點改動。尤其是在計算機視覺領域,許多預訓練的模型現在都被公開下載,並重用在其他問題上以提升在小數據集上的性能。
2.1 數據預處理與數據提升
為了盡量利用我們有限的訓練數據,我們將通過一系列變換堆數據進行提升,這樣我們的模型將看不到任何兩張完全相同的圖片,這有利於我們抑制過擬合,使得模型的泛化能力更好。
在Keras中,這個步驟可以通過keras.preprocessing.image.ImageDataGenerator來實現,也就是圖片預處理生成器,這個類使你可以:
- 在訓練過程中,設置要施行的隨機變換。
- 通過 .flow 或者 .flow_from_directory(directory) 方法實例化一個針對圖像 batch 的生成器,這些生成器可以被用作 Keras模型相關方法的輸入,如 fit_generator,evaluate_generator 和 predict_generator。
datagen = ImageDataGenerator()
datagen.fit(x_train)
生成器初始化 datagen,生成 datagen.fit,計算依賴於數據的變化所需要的統計信息。
最終把數據需要按照每個batch進行划分,這樣就可以送到模型進行訓練了
datagen.flow(x_train, y_train, batch_size=batch_size)
接收numpy數組和標簽為參數,生成經過數據提升或標准化后的batch數據,並在一個無限循環中不斷的返回batch數據。
具體的圖片生成器函數ImageDataGenerator:(他可以用以生成一個 batch的圖像數據,支持實時數據提升,訓練時該函數會無限生成數據,直到達到規定的 epoch次數為止。)
參數意思:
方法有三個,分別是 fit() flow() flow_from_directory() 下面繼續截圖Keras官網的內容:
現在我們看一個例子:
coding:utf-8
'''
使用ImageDataGenerator 來生成圖片,並將其保存在一個臨時文件夾中
下面感受一下數據提升究竟做了什么事情。
'''
import os
from keras.preprocessing.image import ImageDataGenerator, array_to_img, img_to_array, load_img
datagen = ImageDataGenerator(
rotation_range=40, # 是一個0~180的度數,用來指定隨機選擇圖片的角度
width_shift_range=0.2, # 水平方向的隨機移動程度
height_shift_range=0.2, # 豎直方向的隨機移動程度
rescale=1./255, #將在執行其他處理前乘到整個圖像上
shear_range=0.2, # 用來進行剪切變換的程度,參考剪切變換
zoom_range=0.2, # 用來進行隨機的放大
horizontal_flip=True, # 隨機的對圖片進行水平翻轉,此參數用於水平翻轉不影響圖片語義的時候
fill_mode='nearest' # 用來指定當需要進行像素填充,如旋轉,水平和豎直位移的時候
)
pho_path = 'timg.jpg'
img = load_img(pho_path) # this is a PIL image
x = img_to_array(img) # this is a Numpy array with shape (3, 150, 150)
x = x.reshape((1, ) + x.shape) # this is a Numpy array with shape (1, 3, 150, 150)
if not os.path.exists('preview'):
os.mkdir('preview')
i = 0
for batch in datagen.flow(x, batch_size=1,
save_to_dir='preview', save_prefix='durant', save_format='jpg'):
i += 1
if i > 20:
break # otherwise the generator would loop indefitely
這是原圖:
下面是一張圖片被提升以后得到的多個結果:
下面粘貼三個官網的例子:
1,使用.flow() 的例子
2,使用.flow_from_directory(directory)
3,同時變換圖像和 mask
2.2 在小數據集上訓練神經網絡:40行代碼達到80%的准確率
進行圖像分類的正確工具是卷積網絡,所以我們來試試用卷積神經網絡搭建一個初級的模型。因為我們的樣本數很少,所以我們應該對過擬合的問題多加注意。當一個模型從很少的樣本中學習到不能推廣到新數據的模式時,我們稱為出現了過擬合的問題。過擬合發生時,模型試圖使用不相關的特征來進行預測。例如,你有三張伐木工人的照片,有三張水手的照片。六張照片中只有一個伐木工人戴了帽子,如果你認為戴帽子是能將伐木工人與水手區別開的特征,那么此時你就是一個差勁的分類器。
數據提升是對抗過擬合問題的一個武器,但還不夠,因為提升過的數據讓然是高度相關的。對抗過擬合的你應該主要關注的時模型的“熵容量”——模型允許存儲的信息量。能夠存儲更多信息的模型能夠利用更多的特征取得更好的性能,但也有存儲不相關特征的風險。另一方面,只能存儲少量信息的模型會將存儲的特征主要集中在真正相關的特征上,並有更好的泛華性能。
有很多不同的方法來調整模型的“熵容量”,常見的一種選擇是調整模型的參數數目,即模型的層數和每層的規模。另一種方法時對權重進行正則化約束,如L1或L2這種約束會使模型的權重偏向較小的值。
在我們的模型里,我們使用了很小的卷積網絡,只有很少的幾層,每層的濾波器數目也不多。再加上數據提升和Dropout,就差不多了。Dropout通過防止一層看到兩次完全一樣的模式來防止過擬合,相當於也是數據提升的方法。(你可以說Dropout和數據提升都在隨機擾亂數據的相關性)
下面展示的代碼是我們的第一個模型,一個很簡單的3層卷積加上ReLU激活函數,再接max-pooling層,這個結構和Yann LeCun 在1990 年發布的圖像分類器很相似(除了ReLU)
這個實驗的代碼如下:
coding:utf-8
from keras.models import Sequential
from keras.layers import Convolution2D, MaxPooling2D
from keras.layers import Activation, Dropout, Flatten, Dense
from keras.preprocessing.image import ImageDataGenerator
from keras import backend as K
K.set_image_dim_ordering('th')
簡單的三層卷積加上ReLU激活函數,再接一個max-pooling層
model = Sequential()
model.add(Convolution2D(32, 3, 3, input_shape=(3, 150, 150)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Convolution2D(32, 3, 3))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Convolution2D(64, 3, 3))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
the model so far outputs 3D feature maps (height, width, features)
然后我們接了兩個全連接網絡,並以單個神經元和Sigmoid激活結束模型
這種選擇會產生一個二分類的結果,與這種配置項適應,損失函數選擇binary_crossentropy
this converts our 3D feature maps to 1D feature vectors
model.add(Flatten())
添加隱藏層神經元的數量和激活函數
model.add(Dense(64))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(1))
model.add(Activation('sigmoid'))
model.compile(loss='binary_crossentropy',
optimizer='rmsprop',
metrics=['accuracy'])
next read data
使用 .flow_from_directory() 來從我們的jpgs圖片中直接產生數據和標簽
this is the augmentation configuration we will use for training
train_datagen = ImageDataGenerator(
rescale=1./255,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True
)
this is the augmentation configuration we will use for testing only rescaling
test_datagen = ImageDataGenerator(rescale=1./255)
this is a generator that will read pictures found in subfliders
of 'data/train'. and indefinitely generate batches of augmented image data
train_generator = train_datagen.flow_from_directory(
'DogsVSCats/train', # this is the target directory
target_size=(150, 150), # all image will be resize to 150*150
batch_size=32,
class_mode='binary'
) # since we use binary_crossentropy loss,we need binary labels
this is a similar generator, for validation data
validation_generator = test_datagen.flow_from_directory(
'DogsVSCats/valid', # this is the target directory
target_size=(150, 150), # all image will be resize to 150*150
batch_size=32,
class_mode='binary'
)
然后我們可以用這個生成器來訓練網絡了。
model.fit_generator(
train_generator,
samples_per_epoch=2000,
nb_epoch=50,
validation_data=validation_generator,
nb_val_samples=800
)
model.save_weights('first_try.h5') #always save your weights after training or duraing trianing
部分結果如下:
Epoch 1/50
62/62 [==============================] - 66s 1s/step - loss: 0.7043 - acc: 0.5238 - val_loss: 0.6798 - val_acc: 0.5015
Epoch 2/50
62/62 [==============================] - 63s 1s/step - loss: 0.6837 - acc: 0.5762 - val_loss: 0.6481 - val_acc: 0.6837
Epoch 3/50
62/62 [==============================] - 63s 1s/step - loss: 0.6465 - acc: 0.6503 - val_loss: 0.5826 - val_acc: 0.6827
Epoch 4/50
62/62 [==============================] - 63s 1s/step - loss: 0.6077 - acc: 0.6884 - val_loss: 0.5512 - val_acc: 0.7447
Epoch 5/50
62/62 [==============================] - 63s 1s/step - loss: 0.5568 - acc: 0.7088 - val_loss: 0.5127 - val_acc: 0.7357
Epoch 6/50
62/62 [==============================] - 63s 1s/step - loss: 0.5469 - acc: 0.7241 - val_loss: 0.4962 - val_acc: 0.7578
Epoch 7/50
62/62 [==============================] - 63s 1s/step - loss: 0.5236 - acc: 0.7446 - val_loss: 0.4325 - val_acc: 0.8028
Epoch 8/50
62/62 [==============================] - 63s 1s/step - loss: 0.4842 - acc: 0.7751 - val_loss: 0.4710 - val_acc: 0.7758
Epoch 9/50
62/62 [==============================] - 63s 1s/step - loss: 0.4693 - acc: 0.7726 - val_loss: 0.4383 - val_acc: 0.7808
Epoch 10/50
62/62 [==============================] - 63s 1s/step - loss: 0.4545 - acc: 0.7952 - val_loss: 0.3806 - val_acc: 0.8298
Epoch 11/50
62/62 [==============================] - 63s 1s/step - loss: 0.4331 - acc: 0.8031 - val_loss: 0.3781 - val_acc: 0.8248
Epoch 12/50
62/62 [==============================] - 63s 1s/step - loss: 0.4178 - acc: 0.8162 - val_loss: 0.3146 - val_acc: 0.8799
Epoch 13/50
62/62 [==============================] - 63s 1s/step - loss: 0.3926 - acc: 0.8275 - val_loss: 0.3030 - val_acc: 0.8739
Epoch 14/50
62/62 [==============================] - 63s 1s/step - loss: 0.3854 - acc: 0.8295 - val_loss: 0.2835 - val_acc: 0.8929
Epoch 15/50
62/62 [==============================] - 63s 1s/step - loss: 0.3714 - acc: 0.8303 - val_loss: 0.2882 - val_acc: 0.8879
Epoch 16/50
62/62 [==============================] - 63s 1s/step - loss: 0.3596 - acc: 0.8517 - val_loss: 0.3727 - val_acc: 0.8228
Epoch 17/50
62/62 [==============================] - 63s 1s/step - loss: 0.3369 - acc: 0.8568 - val_loss: 0.3638 - val_acc: 0.8328
Epoch 18/50
62/62 [==============================] - 63s 1s/step - loss: 0.3249 - acc: 0.8608 - val_loss: 0.2589 - val_acc: 0.8819
Epoch 19/50
62/62 [==============================] - 63s 1s/step - loss: 0.3348 - acc: 0.8548 - val_loss: 0.2273 - val_acc: 0.9079
Epoch 20/50
62/62 [==============================] - 63s 1s/step - loss: 0.2979 - acc: 0.8754 - val_loss: 0.1737 - val_acc: 0.9389
Epoch 21/50
62/62 [==============================] - 63s 1s/step - loss: 0.2980 - acc: 0.8686 - val_loss: 0.2198 - val_acc: 0.9189
Epoch 22/50
62/62 [==============================] - 63s 1s/step - loss: 0.2789 - acc: 0.8815 - val_loss: 0.2040 - val_acc: 0.9109
Epoch 23/50
62/62 [==============================] - 63s 1s/step - loss: 0.2793 - acc: 0.8891 - val_loss: 0.1388 - val_acc: 0.9479
Epoch 24/50
62/62 [==============================] - 63s 1s/step - loss: 0.2799 - acc: 0.8865 - val_loss: 0.1565 - val_acc: 0.9419
Epoch 25/50
62/62 [==============================] - 63s 1s/step - loss: 0.2513 - acc: 0.8949 - val_loss: 0.1467 - val_acc: 0.9510
Epoch 26/50
62/62 [==============================] - 63s 1s/step - loss: 0.2551 - acc: 0.9029 - val_loss: 0.1281 - val_acc: 0.9520
Epoch 27/50
62/62 [==============================] - 63s 1s/step - loss: 0.2387 - acc: 0.8961 - val_loss: 0.1590 - val_acc: 0.9409
Epoch 28/50
62/62 [==============================] - 63s 1s/step - loss: 0.2449 - acc: 0.9054 - val_loss: 0.1250 - val_acc: 0.9580
Epoch 29/50
62/62 [==============================] - 63s 1s/step - loss: 0.2158 - acc: 0.9218 - val_loss: 0.0881 - val_acc: 0.9780
Epoch 30/50
62/62 [==============================] - 63s 1s/step - loss: 0.2286 - acc: 0.9158 - val_loss: 0.1012 - val_acc: 0.9660
Epoch 31/50
62/62 [==============================] - 63s 1s/step - loss: 0.2017 - acc: 0.9181 - val_loss: 0.1109 - val_acc: 0.9570
Epoch 32/50
62/62 [==============================] - 63s 1s/step - loss: 0.1957 - acc: 0.9213 - val_loss: 0.1160 - val_acc: 0.9560
Epoch 33/50
62/62 [==============================] - 63s 1s/step - loss: 0.2046 - acc: 0.9249 - val_loss: 0.0600 - val_acc: 0.9840
Epoch 34/50
62/62 [==============================] - 63s 1s/step - loss: 0.1967 - acc: 0.9206 - val_loss: 0.0713 - val_acc: 0.9790
Epoch 35/50
62/62 [==============================] - 63s 1s/step - loss: 0.2238 - acc: 0.9153 - val_loss: 0.3123 - val_acc: 0.8929
Epoch 36/50
62/62 [==============================] - 63s 1s/step - loss: 0.1841 - acc: 0.9317 - val_loss: 0.0751 - val_acc: 0.9740
Epoch 37/50
62/62 [==============================] - 63s 1s/step - loss: 0.1890 - acc: 0.9279 - val_loss: 0.1030 - val_acc: 0.9700
這個模型在50個epoch后的准確率為 79%~81%。(但是我的准確率在38個epoch卻達到了驚人的92%,也是恐怖)沒有做模型和超參數的優化。
注意這個准確率的變化可能會比較大,因為准確率本來就是一個變化較高的評估參數,而且我們的訓練樣本比較少,所以比較好的驗證方法就是使用K折交叉驗證,但每輪驗證中我們都要訓練一個模型。
2.3 Keras報錯:ValueError: Negative dimension size caused by subtracting 2 ...
使用Keras時遇到如下錯誤:
1
2
ValueError: Negative dimension size caused by subtracting 2 from 1
for 'block2_pool/MaxPool' (op: 'MaxPool') with input shapes: [?,1,75,128].
解決方法:這個是圖片的通道順序問題。
以128*128的RGB圖像為例 channels_last應該將數據組織為(128, 128, 3),而channels_first將數據組織為(3, 128, 128)。
通過查看函數 set_image_dim_ording():
def set_image_dim_ordering(dim_ordering):
"""Legacy setter for image_data_format
.
Arguments
dim_ordering: string. tf
or th
.
Raises
ValueError: if dim_ordering
is invalid.
"""
global _IMAGE_DATA_FORMAT
if dim_ordering not in {'tf', 'th'}:
raise ValueError('Unknown dim_ordering:', dim_ordering)
if dim_ordering == 'th':
data_format = 'channels_first'
else:
data_format = 'channels_last'
_IMAGE_DATA_FORMAT = data_format
可知,tf對應原本的 channels_last,th對應 channels_first,因此添加下面代碼即可解決問題:
from keras import backend as K
K.set_image_dim_ordering('th')
這樣保證要使用的通道順序和配置的通道順序一致即可。
3,多分類簡易網絡結構(Sequential)
數據描述:
共有500張圖片,分為大巴車、恐龍、大象、鮮花和馬五個類,每個類100張。下載地址:http://pan.baidu.com/s/1nuqlTnN
編號分別以3,4,5,6,7開頭,各為一類。我從其中每類選出20張作為測試,其余80張作為訓練。因此最終訓練圖片400張,測試圖片100張,共5類。如下圖:
(注意,這里需要自己將圖片分為五類,包括訓練集和測試集)
3.1 載入與模型網絡構建
代碼如下:
載入與模型網絡構建
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D
from keras.layers import Activation, Dropout, Flatten, Dense
def built_model():
model = Sequential()
model.add(Conv2D(32, (3, 3), input_shape=(150, 150, 3)))
filter大小為3*3 數量為32個,原始圖像大小3,150 150
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(32, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
this converts ours 3D feature maps to 1D feature vector
model.add(Dense(64))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(5)) # 幾個分類就幾個dense
model.add(Activation('softmax')) # 多分類
3.2 圖像預處理
下面我們開始准備數據,使用 .flow_from_directory() 來從我們的jpgs圖片中直接產生數據和標簽。
其中值得留意的是:
- ImageDataGenerate:用以生成一個 batch 的圖像數據,支持實時數據提升。訓練時該函數會無限生成數據,直到達到規定的epoch次數為止。
- flow_from_directory(directory):以文件夾路徑為參數,生成經過數據提升/歸一化后的數據,在一個無限循環中無限產生batch數據。
def generate_data():
'''
flow_from_directory是計算數據的一些屬性值,之后再訓練階段直接丟進去這些生成器。
通過這個函數來准確數據,可以讓我們的jpgs圖片中直接產生數據和標簽
:return:
'''
train_datagen = ImageDataGenerator(
rescale=1. / 255,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True
)
test_datagen = ImageDataGenerator(rescale=1. / 255)
train_generator = train_datagen.flow_from_directory(
'data/mytrain',
target_size=(150, 150), # all images will be resized to 150*150
batch_size=32,
class_mode='categorical' # 多分類
)
validation_generator = test_datagen.flow_from_directory(
'data/mytest',
target_size=(150, 150),
batch_size=32,
class_mode='categorical' # 多分類
)
return train_generator, validation_generator
3.3 載入與模型網絡構建
代碼如下:(和上面兩分類的沒多少差別)
def built_model():
載入與模型網絡構建
model = Sequential()
model.add(Conv2D(32, (3, 3), input_shape=(150, 150, 3)))
filter大小為3*3 數量為32個,原始圖像大小3,150 150
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(32, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
this converts ours 3D feature maps to 1D feature vector
model.add(Dense(64))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(5)) # 幾個分類就幾個dense
model.add(Activation('softmax')) # 多分類
model.compile(loss='binary_corssentropy',
optimizer='rmsprop',
metrics=['accuracy'])
優化器rmsprop:除學習率可調整外,建議保持優化器的其他默認參數不變
model.compile(loss='categorical_crossentropy',
optimizer='rmsprop',
metrics=['accuracy'])
model.summary()
return model
3.4 訓練
代碼如下:
def train_model(model=None):
if model is None:
model = built_model()
model.fit_generator(
train_generator,
sampels_per_epoch 相當於每個epoch數據量峰值,
每個epoch以經過模型的樣本數達到samples_per_epoch時,記一個epoch結束
samples_per_epoch=2000,
nb_epoch=50,
validation_data=validation_generator,
nb_val_samples=800
)
model.save_weights('first_try_animal.h5')
3.5 部分結果展示和所有源碼
部分結果如下(這里僅展示一個完整的epoch訓練數據):
Epoch 50/50
1/62 [..............................] - ETA: 1:10 - loss: 0.4921 - acc: 0.9062
2/62 [..............................] - ETA: 1:13 - loss: 0.2460 - acc: 0.9531
3/62 [>.............................] - ETA: 1:12 - loss: 0.1640 - acc: 0.9688
4/62 [>.............................] - ETA: 1:02 - loss: 0.1230 - acc: 0.9766
5/62 [=>............................] - ETA: 1:02 - loss: 0.0985 - acc: 0.9812
6/62 [=>............................] - ETA: 1:02 - loss: 0.0821 - acc: 0.9844
7/62 [==>...........................] - ETA: 1:02 - loss: 0.0704 - acc: 0.9866
8/62 [==>...........................] - ETA: 1:00 - loss: 0.0616 - acc: 0.9883
9/62 [===>..........................] - ETA: 59s - loss: 0.0547 - acc: 0.9896
10/62 [===>..........................] - ETA: 59s - loss: 0.0493 - acc: 0.9906
11/62 [====>.........................] - ETA: 59s - loss: 0.0448 - acc: 0.9915
12/62 [====>.........................] - ETA: 58s - loss: 0.0412 - acc: 0.9922
13/62 [=====>........................] - ETA: 57s - loss: 0.0380 - acc: 0.9928
14/62 [=====>........................] - ETA: 55s - loss: 0.0363 - acc: 0.9933
15/62 [======>.......................] - ETA: 54s - loss: 0.0339 - acc: 0.9938
16/62 [======>.......................] - ETA: 53s - loss: 0.0318 - acc: 0.9941
17/62 [=======>......................] - ETA: 51s - loss: 0.0316 - acc: 0.9945
18/62 [=======>......................] - ETA: 50s - loss: 0.0298 - acc: 0.9948
19/62 [========>.....................] - ETA: 49s - loss: 0.0283 - acc: 0.9951
20/62 [========>.....................] - ETA: 48s - loss: 0.0268 - acc: 0.9953
21/62 [=========>....................] - ETA: 47s - loss: 0.0259 - acc: 0.9955
22/62 [=========>....................] - ETA: 46s - loss: 0.0247 - acc: 0.9957
23/62 [==========>...................] - ETA: 45s - loss: 0.0236 - acc: 0.9959
24/62 [==========>...................] - ETA: 44s - loss: 0.0227 - acc: 0.9961
25/62 [===========>..................] - ETA: 42s - loss: 0.0218 - acc: 0.9962
26/62 [===========>..................] - ETA: 42s - loss: 0.0209 - acc: 0.9964
27/62 [============>.................] - ETA: 41s - loss: 0.0202 - acc: 0.9965
28/62 [============>.................] - ETA: 40s - loss: 0.0194 - acc: 0.9967
29/62 [=============>................] - ETA: 39s - loss: 0.0188 - acc: 0.9968
30/62 [=============>................] - ETA: 37s - loss: 0.0181 - acc: 0.9969
31/62 [==============>...............] - ETA: 36s - loss: 0.0176 - acc: 0.9970
32/62 [==============>...............] - ETA: 34s - loss: 0.0170 - acc: 0.9971
33/62 [==============>...............] - ETA: 33s - loss: 0.0165 - acc: 0.9972
34/62 [===============>..............] - ETA: 32s - loss: 0.0160 - acc: 0.9972
35/62 [===============>..............] - ETA: 31s - loss: 0.0156 - acc: 0.9973
36/62 [================>.............] - ETA: 30s - loss: 0.0151 - acc: 0.9974
37/62 [================>.............] - ETA: 29s - loss: 0.0147 - acc: 0.9975
38/62 [=================>............] - ETA: 28s - loss: 0.0146 - acc: 0.9975
39/62 [=================>............] - ETA: 27s - loss: 0.0142 - acc: 0.9976
40/62 [==================>...........] - ETA: 26s - loss: 0.0139 - acc: 0.9977
41/62 [==================>...........] - ETA: 24s - loss: 0.0135 - acc: 0.9977
42/62 [===================>..........] - ETA: 23s - loss: 0.0132 - acc: 0.9978
43/62 [===================>..........] - ETA: 22s - loss: 0.0129 - acc: 0.9978
44/62 [====================>.........] - ETA: 21s - loss: 0.0126 - acc: 0.9979
45/62 [====================>.........] - ETA: 20s - loss: 0.0123 - acc: 0.9979
46/62 [=====================>........] - ETA: 19s - loss: 0.0135 - acc: 0.9973
47/62 [=====================>........] - ETA: 17s - loss: 0.0153 - acc: 0.9967
48/62 [======================>.......] - ETA: 16s - loss: 0.0254 - acc: 0.9961
49/62 [======================>.......] - ETA: 15s - loss: 0.0249 - acc: 0.9962
50/62 [=======================>......] - ETA: 14s - loss: 0.0244 - acc: 0.9962
51/62 [=======================>......] - ETA: 13s - loss: 0.0338 - acc: 0.9957
52/62 [========================>.....] - ETA: 11s - loss: 0.0332 - acc: 0.9958
53/62 [========================>.....] - ETA: 10s - loss: 0.0329 - acc: 0.9959
54/62 [=========================>....] - ETA: 9s - loss: 0.0323 - acc: 0.9959
55/62 [=========================>....] - ETA: 8s - loss: 0.0317 - acc: 0.9960
56/62 [==========================>...] - ETA: 7s - loss: 0.0393 - acc: 0.9950
57/62 [==========================>...] - ETA: 5s - loss: 0.0511 - acc: 0.9940
58/62 [===========================>..] - ETA: 4s - loss: 0.0502 - acc: 0.9941
59/62 [===========================>..] - ETA: 3s - loss: 0.0494 - acc: 0.9942
60/62 [============================>.] - ETA: 2s - loss: 0.0518 - acc: 0.9938
61/62 [============================>.] - ETA: 1s - loss: 0.0535 - acc: 0.9933
62/62 [==============================] - 271s 4s/step - loss: 0.0607 - acc: 0.9929 - val_loss: 0.7166 - val_acc: 0.9300
源碼如下:
載入與模型網絡構建
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D
from keras.layers import Activation, Dropout, Flatten, Dense
from keras.preprocessing.image import ImageDataGenerator
import numpy as np
import os
def built_model():
載入與模型網絡構建
model = Sequential()
model.add(Conv2D(32, (3, 3), input_shape=(150, 150, 3)))
filter大小為3*3 數量為32個,原始圖像大小3,150 150
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(32, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
this converts ours 3D feature maps to 1D feature vector
model.add(Dense(64))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(5)) # 幾個分類就幾個dense
model.add(Activation('softmax')) # 多分類
model.compile(loss='binary_corssentropy',
optimizer='rmsprop',
metrics=['accuracy'])
優化器rmsprop:除學習率可調整外,建議保持優化器的其他默認參數不變
model.compile(loss='categorical_crossentropy',
optimizer='rmsprop',
metrics=['accuracy'])
model.summary()
return model
def generate_data():
'''
flow_from_directory是計算數據的一些屬性值,之后再訓練階段直接丟進去這些生成器。
通過這個函數來准確數據,可以讓我們的jpgs圖片中直接產生數據和標簽
:return:
'''
train_datagen = ImageDataGenerator(
rescale=1. / 255,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True
)
test_datagen = ImageDataGenerator(rescale=1. / 255)
train_generator = train_datagen.flow_from_directory(
'data/mytrain',
target_size=(150, 150), # all images will be resized to 150*150
batch_size=32,
class_mode='categorical' # 多分類
)
validation_generator = test_datagen.flow_from_directory(
'data/mytest',
target_size=(150, 150),
batch_size=32,
class_mode='categorical' # 多分類
)
return train_generator, validation_generator
def train_model(model=None):
if model is None:
model = built_model()
model.fit_generator(
train_generator,
sampels_per_epoch 相當於每個epoch數據量峰值,
每個epoch以經過模型的樣本數達到samples_per_epoch時,記一個epoch結束
samples_per_epoch=2000,
nb_epoch=50,
validation_data=validation_generator,
nb_val_samples=800
)
model.save_weights('first_try_animal.h5')
if name == 'main':
train_generator, validation_generator = generate_data()
train_model()
當loss出現負數,肯定是之前多分類的標簽哪些設置的不對,
注意上面的 steps_per_epoch和validation_steps的值(應該是這樣計算出來的):
model=build_model(input_shape=(IMG_W,IMG_H,IMG_CH)) # 輸入的圖片維度
模型的訓練
model.fit_generator(train_generator, # 數據流
steps_per_epoch=train_samples_num // batch_size,
epochs=epochs,
validation_data=val_generator,
validation_steps=val_samples_num // batch_size)
3.6 畫圖展示和使用模型預測
最后我們可以通過圖直觀的查看訓練過程中的 loss 和 acc ,看看其變化趨勢。下面是代碼:
畫圖,將訓練時的acc和loss都繪制到圖上
import matplotlib.pyplot as plt
def plot_training(history):
plt.figure(12)
plt.subplot(121)
train_acc = history.history['acc']
val_acc = history.history['val_acc']
epochs = range(len(train_acc))
plt.plot(epochs, train_acc, 'b',label='train_acc')
plt.plot(epochs, val_acc, 'r',label='test_acc')
plt.title('Train and Test accuracy')
plt.legend()
plt.subplot(122)
train_loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(len(train_loss))
plt.plot(epochs, train_loss, 'b',label='train_loss')
plt.plot(epochs, val_loss, 'r',label='test_loss')
plt.title('Train and Test loss')
plt.legend()
plt.show()
我們也可以通過訓練好的模型去預測新的樣本。
單張樣本的預測代碼如下:
用訓練好的模型來預測新樣本
from PIL import Image
from keras.preprocessing import image
def predict(model, img_path, target_size):
img=Image.open(img_path) # 加載圖片
if img.size != target_size:
img = img.resize(target_size)
x = image.img_to_array(img)
x *=1./255 # 相當於ImageDataGenerator(rescale=1. / 255)
x = np.expand_dims(x, axis=0) # 調整圖片維度
preds = model.predict(x) # 預測
return preds[0]
批量預測(一個文件夾中的所有文件):
預測一個文件夾中的所有圖片
new_sample_gen=ImageDataGenerator(rescale=1. / 255)
newsample_generator=new_sample_gen.flow_from_directory(
'E:\PyProjects\DataSet\FireAI\DeepLearning',
target_size=(IMG_W, IMG_H),
batch_size=16,
class_mode=None,
shuffle=False)
predicted=model.predict_generator(newsample_generator)
print(predicted)
注意我們上面保存模型是保存的權重,而不是模型,保存模型的代碼如下:
模型的加載,預測
from keras.models import load_model
saved_model=load_model('animal.h5')
predicted=saved_model.predict_generator(newsample_generator)
print(predicted) # saved_model的結果和前面的model結果一致,表面模型正確保存和加載
如果保存的是權重,直接加載,會報錯:
最后,我自己的預測代碼:
用訓練好的模型來預測新的樣本
from keras.preprocessing import image
import cv2
import numpy as np
from keras.models import load_model
def predict(model, img_path, target_size):
img = cv2.imread(img_path)
if img.shape != target_size:
img = cv2.resize(img, target_size)
print(img.shape)
x = image.img_to_array(img)
x *= 1. / 255 # 相當於ImageDataGenerator(rescale=1. / 255)
x = np.expand_dims(x, axis=0) # 調整圖片維度
preds = model.predict(x)
return preds[0]
if name == 'main':
model_path = 'animal.h5'
model = load_model(model_path)
target_size = (150, 150)
img_path = 'data/test/300.jpg'
res = predict(model, img_path, target_size)
print(res)感謝作者分享-http://bjbsair.com/2020-04-07/tech-info/30654.html
Keras基本的使用都已經清楚了,那么這篇主要學習如何使用Keras進行訓練模型,訓練訓練,主要就是“練”,所以多做幾個案例就知道怎么做了。
在本文中,我們將提供一些面向小數據集(幾百張到幾千張圖片)構造高效,實用的圖像分類器的方法。
1,熱身練習——CIFAR10 小圖片分類示例(Sequential式)
示例中CIFAR10采用的是Sequential式來編譯網絡結構。代碼如下:
要訓練模型,首先得知道數據長啥樣
from future import print_function
import keras
from keras.datasets import cifar10
from keras.preprocessing.image import ImageDataGenerator
from keras.models import Sequential
from keras.layers import Dense, Dropout, Activation, Flatten
from keras.layers import Conv2D, MaxPooling2D
batch_size = 32
num_classes = 10
epochs = 100
data_augmentation = True
數據載入
(x_train, y_train), (x_test, y_test) = cifar10.load_data()
多分類標簽生成,我們將其由單個標簽,生成一個熱編碼的形式
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)
網絡結構配置
model = Sequential()
model.add(Conv2D(32, (3, 3), padding='same',
input_shape=x_train.shape[1:])) # (32, 32, 3)
model.add(Activation('relu'))
model.add(Conv2D(32, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Conv2D(64, (3, 3), padding='same'))
model.add(Activation('relu'))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(512))
model.add(Activation('relu'))
model.add(Dropout(num_classes))
model.add(Activation('softmax'))
訓練參數設置
initiate RMSprop optimizer
opt = keras.optimizers.rmsprop(lr=0.0001, decay=1e-6)
Let's train the model using RMSprop
model.compile(loss='categorical_crossentropy',
optimizer=opt,
metrics=['accuracy'])
生成訓練數據
x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
x_train /= 255
x_test /= 255
if not data_augmentation:
print("Not using data augmentation")
model.fit(x_train, y_train,
batch_size=batch_size,
epochs=epochs,
validation_data=(x_test, y_test),
shuffle=True)
else:
print("Using real-time data augmentation")
this will do preprocessing and realtime data augmentation
datagen = ImageDataGenerator(
featurewise_center=False, # set input mean to 0 over the dataset
samplewise_center=False, # set each sample mean to 0
featurewise_std_normalization=False, # divide inputs by std of the dataset
samplewise_std_normalization=False, # divide each input by its std
zca_whitening=False, # apply ZCA whitening
rotation_range=0, # randomly rotate images in the range (degrees, 0 to 180)
width_shift_range=0.1, # randomly shift images horizontally (fraction of total width)
height_shift_range=0.1, # randomly shift images vertically (fraction of total height)
horizontal_flip=True, # randomly flip images
vertical_flip=False) # randomly flip images
Compute quantities required for feature-wise normalization
(std, mean, and principal components if ZCA whitening is applied).
datagen.fit(x_train)
fit訓練
fit the model on batches generated by datagen.flow()
model.fit_generator(datagen.flow(x_train, y_train,
batch_size=batch_size),
epochs=epochs,
validation_data=(x_test, y_test))
截取部分epoch的運行結果:
49056/50000 [============================>.] - ETA: 4s - loss: 0.6400 - acc: 0.7855
49088/50000 [============================>.] - ETA: 4s - loss: 0.6399 - acc: 0.7855
49120/50000 [============================>.] - ETA: 3s - loss: 0.6401 - acc: 0.7855
49152/50000 [============================>.] - ETA: 3s - loss: 0.6399 - acc: 0.7855
49184/50000 [============================>.] - ETA: 3s - loss: 0.6398 - acc: 0.7856
49216/50000 [============================>.] - ETA: 3s - loss: 0.6397 - acc: 0.7856
49248/50000 [============================>.] - ETA: 3s - loss: 0.6395 - acc: 0.7856
49280/50000 [============================>.] - ETA: 3s - loss: 0.6396 - acc: 0.7857
49312/50000 [============================>.] - ETA: 3s - loss: 0.6398 - acc: 0.7856
49344/50000 [============================>.] - ETA: 2s - loss: 0.6404 - acc: 0.7856
49376/50000 [============================>.] - ETA: 2s - loss: 0.6404 - acc: 0.7856
49408/50000 [============================>.] - ETA: 2s - loss: 0.6403 - acc: 0.7856
49440/50000 [============================>.] - ETA: 2s - loss: 0.6404 - acc: 0.7856
49472/50000 [============================>.] - ETA: 2s - loss: 0.6404 - acc: 0.7856
49504/50000 [============================>.] - ETA: 2s - loss: 0.6405 - acc: 0.7855
49536/50000 [============================>.] - ETA: 2s - loss: 0.6406 - acc: 0.7855
49568/50000 [============================>.] - ETA: 1s - loss: 0.6407 - acc: 0.7855
49600/50000 [============================>.] - ETA: 1s - loss: 0.6407 - acc: 0.7854
49632/50000 [============================>.] - ETA: 1s - loss: 0.6410 - acc: 0.7854
49664/50000 [============================>.] - ETA: 1s - loss: 0.6409 - acc: 0.7853
49696/50000 [============================>.] - ETA: 1s - loss: 0.6410 - acc: 0.7853
49728/50000 [============================>.] - ETA: 1s - loss: 0.6412 - acc: 0.7852
49760/50000 [============================>.] - ETA: 1s - loss: 0.6413 - acc: 0.7852
49792/50000 [============================>.] - ETA: 0s - loss: 0.6413 - acc: 0.7852
49824/50000 [============================>.] - ETA: 0s - loss: 0.6413 - acc: 0.7852
49856/50000 [============================>.] - ETA: 0s - loss: 0.6414 - acc: 0.7851
49888/50000 [============================>.] - ETA: 0s - loss: 0.6415 - acc: 0.7851
49920/50000 [============================>.] - ETA: 0s - loss: 0.6415 - acc: 0.7851
49952/50000 [============================>.] - ETA: 0s - loss: 0.6415 - acc: 0.7850
49984/50000 [============================>.] - ETA: 0s - loss: 0.6415 - acc: 0.7850
50000/50000 [==============================] - 228s 5ms/step - loss: 0.6414 - acc: 0.7851 - val_loss: 0.6509 - val_acc: 0.7836
Epoch 55/200
其實跑到第55個epoch,准確率已經達到了 0.785了,后面肯定能達到八九十,我這里就暫停了,讓我電腦歇一歇,跑一天了。其實跑這個就是證明模型沒有問題,而且我的代碼可以運行,僅此而已。
2,針對小數據集的深度學習
本節實驗基於如下配置:
- 2000張訓練圖片構成的數據集,一共有兩個類別,每類1000張
- 安裝有Keras,Scipy,PIL的機器,如果有GPU就更好的了,但是因為我們面對的是小數據集,沒有也可以
- 數據集存放格式如下:
這份數據集來自於Kaggle,元數據集有12500只貓和12500只狗,我們只取了各個類的前500張圖片,另外還從各個類中取了1000張額外圖片用於測試。
下面是數據集的一些示例圖片,圖片的數量非常少,這對於圖像分類來說是個大麻煩。但現實是,很多真實世界圖片獲取是很困難的,我們能得到的樣本數目確實很有限(比如醫學圖像,每張正樣本都意味着一個承受痛苦的病人)對數據科學家而言,我們應該有能夠榨取少量數據的全部價值的能力,而不是簡單的伸手要更多的數據。
在Kaggle的貓狗大戰競賽中,參賽者通過使用現代的深度學習技術達到了98%的正確率,我們只使用了全部數據的8%,因此這個問題對我們來說更難。
經常聽說的一種做法是,深度學習只有在你擁有海量數據的時候才有意義。雖然這種說法並不是完全不對,但卻具有較強的誤導性。當然,深度學習強調從數據中自動學習特征的能力,沒有足夠的訓練樣本,這幾乎是不可能的。尤其是當輸入的數據維度很高(如圖片)時。然而,卷積神經網絡作為深度學習的支柱,被設計為針對“感知”問題最好的模型之一(如圖像分類問題),即使只有很少的數據,網絡也能把特征學的不錯。針對小數據集的神經網絡依然能夠得到合理的結果,並不需要任何手工的特征工程。一言以蔽之,卷積神經網絡大法好!另一方面,深度學習模型天然就具有可重用的特性:比方說,你可以把一個在大規模數據上訓練好的圖像分類或語音識別的模型重用在另一個很不一樣的問題上,而只需要做有限的一點改動。尤其是在計算機視覺領域,許多預訓練的模型現在都被公開下載,並重用在其他問題上以提升在小數據集上的性能。
2.1 數據預處理與數據提升
為了盡量利用我們有限的訓練數據,我們將通過一系列變換堆數據進行提升,這樣我們的模型將看不到任何兩張完全相同的圖片,這有利於我們抑制過擬合,使得模型的泛化能力更好。
在Keras中,這個步驟可以通過keras.preprocessing.image.ImageDataGenerator來實現,也就是圖片預處理生成器,這個類使你可以:
- 在訓練過程中,設置要施行的隨機變換。
- 通過 .flow 或者 .flow_from_directory(directory) 方法實例化一個針對圖像 batch 的生成器,這些生成器可以被用作 Keras模型相關方法的輸入,如 fit_generator,evaluate_generator 和 predict_generator。
datagen = ImageDataGenerator()
datagen.fit(x_train)
生成器初始化 datagen,生成 datagen.fit,計算依賴於數據的變化所需要的統計信息。
最終把數據需要按照每個batch進行划分,這樣就可以送到模型進行訓練了
datagen.flow(x_train, y_train, batch_size=batch_size)
接收numpy數組和標簽為參數,生成經過數據提升或標准化后的batch數據,並在一個無限循環中不斷的返回batch數據。
具體的圖片生成器函數ImageDataGenerator:(他可以用以生成一個 batch的圖像數據,支持實時數據提升,訓練時該函數會無限生成數據,直到達到規定的 epoch次數為止。)
參數意思:
方法有三個,分別是 fit() flow() flow_from_directory() 下面繼續截圖Keras官網的內容:
現在我們看一個例子:
coding:utf-8
'''
使用ImageDataGenerator 來生成圖片,並將其保存在一個臨時文件夾中
下面感受一下數據提升究竟做了什么事情。
'''
import os
from keras.preprocessing.image import ImageDataGenerator, array_to_img, img_to_array, load_img
datagen = ImageDataGenerator(
rotation_range=40, # 是一個0~180的度數,用來指定隨機選擇圖片的角度
width_shift_range=0.2, # 水平方向的隨機移動程度
height_shift_range=0.2, # 豎直方向的隨機移動程度
rescale=1./255, #將在執行其他處理前乘到整個圖像上
shear_range=0.2, # 用來進行剪切變換的程度,參考剪切變換
zoom_range=0.2, # 用來進行隨機的放大
horizontal_flip=True, # 隨機的對圖片進行水平翻轉,此參數用於水平翻轉不影響圖片語義的時候
fill_mode='nearest' # 用來指定當需要進行像素填充,如旋轉,水平和豎直位移的時候
)
pho_path = 'timg.jpg'
img = load_img(pho_path) # this is a PIL image
x = img_to_array(img) # this is a Numpy array with shape (3, 150, 150)
x = x.reshape((1, ) + x.shape) # this is a Numpy array with shape (1, 3, 150, 150)
if not os.path.exists('preview'):
os.mkdir('preview')
i = 0
for batch in datagen.flow(x, batch_size=1,
save_to_dir='preview', save_prefix='durant', save_format='jpg'):
i += 1
if i > 20:
break # otherwise the generator would loop indefitely
這是原圖:
下面是一張圖片被提升以后得到的多個結果:
下面粘貼三個官網的例子:
1,使用.flow() 的例子
2,使用.flow_from_directory(directory)
3,同時變換圖像和 mask
2.2 在小數據集上訓練神經網絡:40行代碼達到80%的准確率
進行圖像分類的正確工具是卷積網絡,所以我們來試試用卷積神經網絡搭建一個初級的模型。因為我們的樣本數很少,所以我們應該對過擬合的問題多加注意。當一個模型從很少的樣本中學習到不能推廣到新數據的模式時,我們稱為出現了過擬合的問題。過擬合發生時,模型試圖使用不相關的特征來進行預測。例如,你有三張伐木工人的照片,有三張水手的照片。六張照片中只有一個伐木工人戴了帽子,如果你認為戴帽子是能將伐木工人與水手區別開的特征,那么此時你就是一個差勁的分類器。
數據提升是對抗過擬合問題的一個武器,但還不夠,因為提升過的數據讓然是高度相關的。對抗過擬合的你應該主要關注的時模型的“熵容量”——模型允許存儲的信息量。能夠存儲更多信息的模型能夠利用更多的特征取得更好的性能,但也有存儲不相關特征的風險。另一方面,只能存儲少量信息的模型會將存儲的特征主要集中在真正相關的特征上,並有更好的泛華性能。
有很多不同的方法來調整模型的“熵容量”,常見的一種選擇是調整模型的參數數目,即模型的層數和每層的規模。另一種方法時對權重進行正則化約束,如L1或L2這種約束會使模型的權重偏向較小的值。
在我們的模型里,我們使用了很小的卷積網絡,只有很少的幾層,每層的濾波器數目也不多。再加上數據提升和Dropout,就差不多了。Dropout通過防止一層看到兩次完全一樣的模式來防止過擬合,相當於也是數據提升的方法。(你可以說Dropout和數據提升都在隨機擾亂數據的相關性)
下面展示的代碼是我們的第一個模型,一個很簡單的3層卷積加上ReLU激活函數,再接max-pooling層,這個結構和Yann LeCun 在1990 年發布的圖像分類器很相似(除了ReLU)
這個實驗的代碼如下:
coding:utf-8
from keras.models import Sequential
from keras.layers import Convolution2D, MaxPooling2D
from keras.layers import Activation, Dropout, Flatten, Dense
from keras.preprocessing.image import ImageDataGenerator
from keras import backend as K
K.set_image_dim_ordering('th')
簡單的三層卷積加上ReLU激活函數,再接一個max-pooling層
model = Sequential()
model.add(Convolution2D(32, 3, 3, input_shape=(3, 150, 150)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Convolution2D(32, 3, 3))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Convolution2D(64, 3, 3))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
the model so far outputs 3D feature maps (height, width, features)
然后我們接了兩個全連接網絡,並以單個神經元和Sigmoid激活結束模型
這種選擇會產生一個二分類的結果,與這種配置項適應,損失函數選擇binary_crossentropy
this converts our 3D feature maps to 1D feature vectors
model.add(Flatten())
添加隱藏層神經元的數量和激活函數
model.add(Dense(64))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(1))
model.add(Activation('sigmoid'))
model.compile(loss='binary_crossentropy',
optimizer='rmsprop',
metrics=['accuracy'])
next read data
使用 .flow_from_directory() 來從我們的jpgs圖片中直接產生數據和標簽
this is the augmentation configuration we will use for training
train_datagen = ImageDataGenerator(
rescale=1./255,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True
)
this is the augmentation configuration we will use for testing only rescaling
test_datagen = ImageDataGenerator(rescale=1./255)
this is a generator that will read pictures found in subfliders
of 'data/train'. and indefinitely generate batches of augmented image data
train_generator = train_datagen.flow_from_directory(
'DogsVSCats/train', # this is the target directory
target_size=(150, 150), # all image will be resize to 150*150
batch_size=32,
class_mode='binary'
) # since we use binary_crossentropy loss,we need binary labels
this is a similar generator, for validation data
validation_generator = test_datagen.flow_from_directory(
'DogsVSCats/valid', # this is the target directory
target_size=(150, 150), # all image will be resize to 150*150
batch_size=32,
class_mode='binary'
)
然后我們可以用這個生成器來訓練網絡了。
model.fit_generator(
train_generator,
samples_per_epoch=2000,
nb_epoch=50,
validation_data=validation_generator,
nb_val_samples=800
)
model.save_weights('first_try.h5') #always save your weights after training or duraing trianing
部分結果如下:
Epoch 1/50
62/62 [==============================] - 66s 1s/step - loss: 0.7043 - acc: 0.5238 - val_loss: 0.6798 - val_acc: 0.5015
Epoch 2/50
62/62 [==============================] - 63s 1s/step - loss: 0.6837 - acc: 0.5762 - val_loss: 0.6481 - val_acc: 0.6837
Epoch 3/50
62/62 [==============================] - 63s 1s/step - loss: 0.6465 - acc: 0.6503 - val_loss: 0.5826 - val_acc: 0.6827
Epoch 4/50
62/62 [==============================] - 63s 1s/step - loss: 0.6077 - acc: 0.6884 - val_loss: 0.5512 - val_acc: 0.7447
Epoch 5/50
62/62 [==============================] - 63s 1s/step - loss: 0.5568 - acc: 0.7088 - val_loss: 0.5127 - val_acc: 0.7357
Epoch 6/50
62/62 [==============================] - 63s 1s/step - loss: 0.5469 - acc: 0.7241 - val_loss: 0.4962 - val_acc: 0.7578
Epoch 7/50
62/62 [==============================] - 63s 1s/step - loss: 0.5236 - acc: 0.7446 - val_loss: 0.4325 - val_acc: 0.8028
Epoch 8/50
62/62 [==============================] - 63s 1s/step - loss: 0.4842 - acc: 0.7751 - val_loss: 0.4710 - val_acc: 0.7758
Epoch 9/50
62/62 [==============================] - 63s 1s/step - loss: 0.4693 - acc: 0.7726 - val_loss: 0.4383 - val_acc: 0.7808
Epoch 10/50
62/62 [==============================] - 63s 1s/step - loss: 0.4545 - acc: 0.7952 - val_loss: 0.3806 - val_acc: 0.8298
Epoch 11/50
62/62 [==============================] - 63s 1s/step - loss: 0.4331 - acc: 0.8031 - val_loss: 0.3781 - val_acc: 0.8248
Epoch 12/50
62/62 [==============================] - 63s 1s/step - loss: 0.4178 - acc: 0.8162 - val_loss: 0.3146 - val_acc: 0.8799
Epoch 13/50
62/62 [==============================] - 63s 1s/step - loss: 0.3926 - acc: 0.8275 - val_loss: 0.3030 - val_acc: 0.8739
Epoch 14/50
62/62 [==============================] - 63s 1s/step - loss: 0.3854 - acc: 0.8295 - val_loss: 0.2835 - val_acc: 0.8929
Epoch 15/50
62/62 [==============================] - 63s 1s/step - loss: 0.3714 - acc: 0.8303 - val_loss: 0.2882 - val_acc: 0.8879
Epoch 16/50
62/62 [==============================] - 63s 1s/step - loss: 0.3596 - acc: 0.8517 - val_loss: 0.3727 - val_acc: 0.8228
Epoch 17/50
62/62 [==============================] - 63s 1s/step - loss: 0.3369 - acc: 0.8568 - val_loss: 0.3638 - val_acc: 0.8328
Epoch 18/50
62/62 [==============================] - 63s 1s/step - loss: 0.3249 - acc: 0.8608 - val_loss: 0.2589 - val_acc: 0.8819
Epoch 19/50
62/62 [==============================] - 63s 1s/step - loss: 0.3348 - acc: 0.8548 - val_loss: 0.2273 - val_acc: 0.9079
Epoch 20/50
62/62 [==============================] - 63s 1s/step - loss: 0.2979 - acc: 0.8754 - val_loss: 0.1737 - val_acc: 0.9389
Epoch 21/50
62/62 [==============================] - 63s 1s/step - loss: 0.2980 - acc: 0.8686 - val_loss: 0.2198 - val_acc: 0.9189
Epoch 22/50
62/62 [==============================] - 63s 1s/step - loss: 0.2789 - acc: 0.8815 - val_loss: 0.2040 - val_acc: 0.9109
Epoch 23/50
62/62 [==============================] - 63s 1s/step - loss: 0.2793 - acc: 0.8891 - val_loss: 0.1388 - val_acc: 0.9479
Epoch 24/50
62/62 [==============================] - 63s 1s/step - loss: 0.2799 - acc: 0.8865 - val_loss: 0.1565 - val_acc: 0.9419
Epoch 25/50
62/62 [==============================] - 63s 1s/step - loss: 0.2513 - acc: 0.8949 - val_loss: 0.1467 - val_acc: 0.9510
Epoch 26/50
62/62 [==============================] - 63s 1s/step - loss: 0.2551 - acc: 0.9029 - val_loss: 0.1281 - val_acc: 0.9520
Epoch 27/50
62/62 [==============================] - 63s 1s/step - loss: 0.2387 - acc: 0.8961 - val_loss: 0.1590 - val_acc: 0.9409
Epoch 28/50
62/62 [==============================] - 63s 1s/step - loss: 0.2449 - acc: 0.9054 - val_loss: 0.1250 - val_acc: 0.9580
Epoch 29/50
62/62 [==============================] - 63s 1s/step - loss: 0.2158 - acc: 0.9218 - val_loss: 0.0881 - val_acc: 0.9780
Epoch 30/50
62/62 [==============================] - 63s 1s/step - loss: 0.2286 - acc: 0.9158 - val_loss: 0.1012 - val_acc: 0.9660
Epoch 31/50
62/62 [==============================] - 63s 1s/step - loss: 0.2017 - acc: 0.9181 - val_loss: 0.1109 - val_acc: 0.9570
Epoch 32/50
62/62 [==============================] - 63s 1s/step - loss: 0.1957 - acc: 0.9213 - val_loss: 0.1160 - val_acc: 0.9560
Epoch 33/50
62/62 [==============================] - 63s 1s/step - loss: 0.2046 - acc: 0.9249 - val_loss: 0.0600 - val_acc: 0.9840
Epoch 34/50
62/62 [==============================] - 63s 1s/step - loss: 0.1967 - acc: 0.9206 - val_loss: 0.0713 - val_acc: 0.9790
Epoch 35/50
62/62 [==============================] - 63s 1s/step - loss: 0.2238 - acc: 0.9153 - val_loss: 0.3123 - val_acc: 0.8929
Epoch 36/50
62/62 [==============================] - 63s 1s/step - loss: 0.1841 - acc: 0.9317 - val_loss: 0.0751 - val_acc: 0.9740
Epoch 37/50
62/62 [==============================] - 63s 1s/step - loss: 0.1890 - acc: 0.9279 - val_loss: 0.1030 - val_acc: 0.9700
這個模型在50個epoch后的准確率為 79%~81%。(但是我的准確率在38個epoch卻達到了驚人的92%,也是恐怖)沒有做模型和超參數的優化。
注意這個准確率的變化可能會比較大,因為准確率本來就是一個變化較高的評估參數,而且我們的訓練樣本比較少,所以比較好的驗證方法就是使用K折交叉驗證,但每輪驗證中我們都要訓練一個模型。
2.3 Keras報錯:ValueError: Negative dimension size caused by subtracting 2 ...
使用Keras時遇到如下錯誤:
1
2
ValueError: Negative dimension size caused by subtracting 2 from 1
for 'block2_pool/MaxPool' (op: 'MaxPool') with input shapes: [?,1,75,128].
解決方法:這個是圖片的通道順序問題。
以128*128的RGB圖像為例 channels_last應該將數據組織為(128, 128, 3),而channels_first將數據組織為(3, 128, 128)。
通過查看函數 set_image_dim_ording():
def set_image_dim_ordering(dim_ordering):
"""Legacy setter for image_data_format
.
Arguments
dim_ordering: string. tf
or th
.
Raises
ValueError: if dim_ordering
is invalid.
"""
global _IMAGE_DATA_FORMAT
if dim_ordering not in {'tf', 'th'}:
raise ValueError('Unknown dim_ordering:', dim_ordering)
if dim_ordering == 'th':
data_format = 'channels_first'
else:
data_format = 'channels_last'
_IMAGE_DATA_FORMAT = data_format
可知,tf對應原本的 channels_last,th對應 channels_first,因此添加下面代碼即可解決問題:
from keras import backend as K
K.set_image_dim_ordering('th')
這樣保證要使用的通道順序和配置的通道順序一致即可。
3,多分類簡易網絡結構(Sequential)
數據描述:
共有500張圖片,分為大巴車、恐龍、大象、鮮花和馬五個類,每個類100張。下載地址:http://pan.baidu.com/s/1nuqlTnN
編號分別以3,4,5,6,7開頭,各為一類。我從其中每類選出20張作為測試,其余80張作為訓練。因此最終訓練圖片400張,測試圖片100張,共5類。如下圖:
(注意,這里需要自己將圖片分為五類,包括訓練集和測試集)
3.1 載入與模型網絡構建
代碼如下:
載入與模型網絡構建
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D
from keras.layers import Activation, Dropout, Flatten, Dense
def built_model():
model = Sequential()
model.add(Conv2D(32, (3, 3), input_shape=(150, 150, 3)))
filter大小為3*3 數量為32個,原始圖像大小3,150 150
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(32, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
this converts ours 3D feature maps to 1D feature vector
model.add(Dense(64))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(5)) # 幾個分類就幾個dense
model.add(Activation('softmax')) # 多分類
3.2 圖像預處理
下面我們開始准備數據,使用 .flow_from_directory() 來從我們的jpgs圖片中直接產生數據和標簽。
其中值得留意的是:
- ImageDataGenerate:用以生成一個 batch 的圖像數據,支持實時數據提升。訓練時該函數會無限生成數據,直到達到規定的epoch次數為止。
- flow_from_directory(directory):以文件夾路徑為參數,生成經過數據提升/歸一化后的數據,在一個無限循環中無限產生batch數據。
def generate_data():
'''
flow_from_directory是計算數據的一些屬性值,之后再訓練階段直接丟進去這些生成器。
通過這個函數來准確數據,可以讓我們的jpgs圖片中直接產生數據和標簽
:return:
'''
train_datagen = ImageDataGenerator(
rescale=1. / 255,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True
)
test_datagen = ImageDataGenerator(rescale=1. / 255)
train_generator = train_datagen.flow_from_directory(
'data/mytrain',
target_size=(150, 150), # all images will be resized to 150*150
batch_size=32,
class_mode='categorical' # 多分類
)
validation_generator = test_datagen.flow_from_directory(
'data/mytest',
target_size=(150, 150),
batch_size=32,
class_mode='categorical' # 多分類
)
return train_generator, validation_generator
3.3 載入與模型網絡構建
代碼如下:(和上面兩分類的沒多少差別)
def built_model():
載入與模型網絡構建
model = Sequential()
model.add(Conv2D(32, (3, 3), input_shape=(150, 150, 3)))
filter大小為3*3 數量為32個,原始圖像大小3,150 150
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(32, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
this converts ours 3D feature maps to 1D feature vector
model.add(Dense(64))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(5)) # 幾個分類就幾個dense
model.add(Activation('softmax')) # 多分類
model.compile(loss='binary_corssentropy',
optimizer='rmsprop',
metrics=['accuracy'])
優化器rmsprop:除學習率可調整外,建議保持優化器的其他默認參數不變
model.compile(loss='categorical_crossentropy',
optimizer='rmsprop',
metrics=['accuracy'])
model.summary()
return model
3.4 訓練
代碼如下:
def train_model(model=None):
if model is None:
model = built_model()
model.fit_generator(
train_generator,
sampels_per_epoch 相當於每個epoch數據量峰值,
每個epoch以經過模型的樣本數達到samples_per_epoch時,記一個epoch結束
samples_per_epoch=2000,
nb_epoch=50,
validation_data=validation_generator,
nb_val_samples=800
)
model.save_weights('first_try_animal.h5')
3.5 部分結果展示和所有源碼
部分結果如下(這里僅展示一個完整的epoch訓練數據):
Epoch 50/50
1/62 [..............................] - ETA: 1:10 - loss: 0.4921 - acc: 0.9062
2/62 [..............................] - ETA: 1:13 - loss: 0.2460 - acc: 0.9531
3/62 [>.............................] - ETA: 1:12 - loss: 0.1640 - acc: 0.9688
4/62 [>.............................] - ETA: 1:02 - loss: 0.1230 - acc: 0.9766
5/62 [=>............................] - ETA: 1:02 - loss: 0.0985 - acc: 0.9812
6/62 [=>............................] - ETA: 1:02 - loss: 0.0821 - acc: 0.9844
7/62 [==>...........................] - ETA: 1:02 - loss: 0.0704 - acc: 0.9866
8/62 [==>...........................] - ETA: 1:00 - loss: 0.0616 - acc: 0.9883
9/62 [===>..........................] - ETA: 59s - loss: 0.0547 - acc: 0.9896
10/62 [===>..........................] - ETA: 59s - loss: 0.0493 - acc: 0.9906
11/62 [====>.........................] - ETA: 59s - loss: 0.0448 - acc: 0.9915
12/62 [====>.........................] - ETA: 58s - loss: 0.0412 - acc: 0.9922
13/62 [=====>........................] - ETA: 57s - loss: 0.0380 - acc: 0.9928
14/62 [=====>........................] - ETA: 55s - loss: 0.0363 - acc: 0.9933
15/62 [======>.......................] - ETA: 54s - loss: 0.0339 - acc: 0.9938
16/62 [======>.......................] - ETA: 53s - loss: 0.0318 - acc: 0.9941
17/62 [=======>......................] - ETA: 51s - loss: 0.0316 - acc: 0.9945
18/62 [=======>......................] - ETA: 50s - loss: 0.0298 - acc: 0.9948
19/62 [========>.....................] - ETA: 49s - loss: 0.0283 - acc: 0.9951
20/62 [========>.....................] - ETA: 48s - loss: 0.0268 - acc: 0.9953
21/62 [=========>....................] - ETA: 47s - loss: 0.0259 - acc: 0.9955
22/62 [=========>....................] - ETA: 46s - loss: 0.0247 - acc: 0.9957
23/62 [==========>...................] - ETA: 45s - loss: 0.0236 - acc: 0.9959
24/62 [==========>...................] - ETA: 44s - loss: 0.0227 - acc: 0.9961
25/62 [===========>..................] - ETA: 42s - loss: 0.0218 - acc: 0.9962
26/62 [===========>..................] - ETA: 42s - loss: 0.0209 - acc: 0.9964
27/62 [============>.................] - ETA: 41s - loss: 0.0202 - acc: 0.9965
28/62 [============>.................] - ETA: 40s - loss: 0.0194 - acc: 0.9967
29/62 [=============>................] - ETA: 39s - loss: 0.0188 - acc: 0.9968
30/62 [=============>................] - ETA: 37s - loss: 0.0181 - acc: 0.9969
31/62 [==============>...............] - ETA: 36s - loss: 0.0176 - acc: 0.9970
32/62 [==============>...............] - ETA: 34s - loss: 0.0170 - acc: 0.9971
33/62 [==============>...............] - ETA: 33s - loss: 0.0165 - acc: 0.9972
34/62 [===============>..............] - ETA: 32s - loss: 0.0160 - acc: 0.9972
35/62 [===============>..............] - ETA: 31s - loss: 0.0156 - acc: 0.9973
36/62 [================>.............] - ETA: 30s - loss: 0.0151 - acc: 0.9974
37/62 [================>.............] - ETA: 29s - loss: 0.0147 - acc: 0.9975
38/62 [=================>............] - ETA: 28s - loss: 0.0146 - acc: 0.9975
39/62 [=================>............] - ETA: 27s - loss: 0.0142 - acc: 0.9976
40/62 [==================>...........] - ETA: 26s - loss: 0.0139 - acc: 0.9977
41/62 [==================>...........] - ETA: 24s - loss: 0.0135 - acc: 0.9977
42/62 [===================>..........] - ETA: 23s - loss: 0.0132 - acc: 0.9978
43/62 [===================>..........] - ETA: 22s - loss: 0.0129 - acc: 0.9978
44/62 [====================>.........] - ETA: 21s - loss: 0.0126 - acc: 0.9979
45/62 [====================>.........] - ETA: 20s - loss: 0.0123 - acc: 0.9979
46/62 [=====================>........] - ETA: 19s - loss: 0.0135 - acc: 0.9973
47/62 [=====================>........] - ETA: 17s - loss: 0.0153 - acc: 0.9967
48/62 [======================>.......] - ETA: 16s - loss: 0.0254 - acc: 0.9961
49/62 [======================>.......] - ETA: 15s - loss: 0.0249 - acc: 0.9962
50/62 [=======================>......] - ETA: 14s - loss: 0.0244 - acc: 0.9962
51/62 [=======================>......] - ETA: 13s - loss: 0.0338 - acc: 0.9957
52/62 [========================>.....] - ETA: 11s - loss: 0.0332 - acc: 0.9958
53/62 [========================>.....] - ETA: 10s - loss: 0.0329 - acc: 0.9959
54/62 [=========================>....] - ETA: 9s - loss: 0.0323 - acc: 0.9959
55/62 [=========================>....] - ETA: 8s - loss: 0.0317 - acc: 0.9960
56/62 [==========================>...] - ETA: 7s - loss: 0.0393 - acc: 0.9950
57/62 [==========================>...] - ETA: 5s - loss: 0.0511 - acc: 0.9940
58/62 [===========================>..] - ETA: 4s - loss: 0.0502 - acc: 0.9941
59/62 [===========================>..] - ETA: 3s - loss: 0.0494 - acc: 0.9942
60/62 [============================>.] - ETA: 2s - loss: 0.0518 - acc: 0.9938
61/62 [============================>.] - ETA: 1s - loss: 0.0535 - acc: 0.9933
62/62 [==============================] - 271s 4s/step - loss: 0.0607 - acc: 0.9929 - val_loss: 0.7166 - val_acc: 0.9300
源碼如下:
載入與模型網絡構建
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D
from keras.layers import Activation, Dropout, Flatten, Dense
from keras.preprocessing.image import ImageDataGenerator
import numpy as np
import os
def built_model():
載入與模型網絡構建
model = Sequential()
model.add(Conv2D(32, (3, 3), input_shape=(150, 150, 3)))
filter大小為3*3 數量為32個,原始圖像大小3,150 150
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(32, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
this converts ours 3D feature maps to 1D feature vector
model.add(Dense(64))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(5)) # 幾個分類就幾個dense
model.add(Activation('softmax')) # 多分類
model.compile(loss='binary_corssentropy',
optimizer='rmsprop',
metrics=['accuracy'])
優化器rmsprop:除學習率可調整外,建議保持優化器的其他默認參數不變
model.compile(loss='categorical_crossentropy',
optimizer='rmsprop',
metrics=['accuracy'])
model.summary()
return model
def generate_data():
'''
flow_from_directory是計算數據的一些屬性值,之后再訓練階段直接丟進去這些生成器。
通過這個函數來准確數據,可以讓我們的jpgs圖片中直接產生數據和標簽
:return:
'''
train_datagen = ImageDataGenerator(
rescale=1. / 255,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True
)
test_datagen = ImageDataGenerator(rescale=1. / 255)
train_generator = train_datagen.flow_from_directory(
'data/mytrain',
target_size=(150, 150), # all images will be resized to 150*150
batch_size=32,
class_mode='categorical' # 多分類
)
validation_generator = test_datagen.flow_from_directory(
'data/mytest',
target_size=(150, 150),
batch_size=32,
class_mode='categorical' # 多分類
)
return train_generator, validation_generator
def train_model(model=None):
if model is None:
model = built_model()
model.fit_generator(
train_generator,
sampels_per_epoch 相當於每個epoch數據量峰值,
每個epoch以經過模型的樣本數達到samples_per_epoch時,記一個epoch結束
samples_per_epoch=2000,
nb_epoch=50,
validation_data=validation_generator,
nb_val_samples=800
)
model.save_weights('first_try_animal.h5')
if name == 'main':
train_generator, validation_generator = generate_data()
train_model()
當loss出現負數,肯定是之前多分類的標簽哪些設置的不對,
注意上面的 steps_per_epoch和validation_steps的值(應該是這樣計算出來的):
model=build_model(input_shape=(IMG_W,IMG_H,IMG_CH)) # 輸入的圖片維度
模型的訓練
model.fit_generator(train_generator, # 數據流
steps_per_epoch=train_samples_num // batch_size,
epochs=epochs,
validation_data=val_generator,
validation_steps=val_samples_num // batch_size)
3.6 畫圖展示和使用模型預測
最后我們可以通過圖直觀的查看訓練過程中的 loss 和 acc ,看看其變化趨勢。下面是代碼:
畫圖,將訓練時的acc和loss都繪制到圖上
import matplotlib.pyplot as plt
def plot_training(history):
plt.figure(12)
plt.subplot(121)
train_acc = history.history['acc']
val_acc = history.history['val_acc']
epochs = range(len(train_acc))
plt.plot(epochs, train_acc, 'b',label='train_acc')
plt.plot(epochs, val_acc, 'r',label='test_acc')
plt.title('Train and Test accuracy')
plt.legend()
plt.subplot(122)
train_loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(len(train_loss))
plt.plot(epochs, train_loss, 'b',label='train_loss')
plt.plot(epochs, val_loss, 'r',label='test_loss')
plt.title('Train and Test loss')
plt.legend()
plt.show()
我們也可以通過訓練好的模型去預測新的樣本。
單張樣本的預測代碼如下:
用訓練好的模型來預測新樣本
from PIL import Image
from keras.preprocessing import image
def predict(model, img_path, target_size):
img=Image.open(img_path) # 加載圖片
if img.size != target_size:
img = img.resize(target_size)
x = image.img_to_array(img)
x *=1./255 # 相當於ImageDataGenerator(rescale=1. / 255)
x = np.expand_dims(x, axis=0) # 調整圖片維度
preds = model.predict(x) # 預測
return preds[0]
批量預測(一個文件夾中的所有文件):
預測一個文件夾中的所有圖片
new_sample_gen=ImageDataGenerator(rescale=1. / 255)
newsample_generator=new_sample_gen.flow_from_directory(
'E:\PyProjects\DataSet\FireAI\DeepLearning',
target_size=(IMG_W, IMG_H),
batch_size=16,
class_mode=None,
shuffle=False)
predicted=model.predict_generator(newsample_generator)
print(predicted)
注意我們上面保存模型是保存的權重,而不是模型,保存模型的代碼如下:
模型的加載,預測
from keras.models import load_model
saved_model=load_model('animal.h5')
predicted=saved_model.predict_generator(newsample_generator)
print(predicted) # saved_model的結果和前面的model結果一致,表面模型正確保存和加載
如果保存的是權重,直接加載,會報錯:
最后,我自己的預測代碼:
用訓練好的模型來預測新的樣本
from keras.preprocessing import image
import cv2
import numpy as np
from keras.models import load_model
def predict(model, img_path, target_size):
img = cv2.imread(img_path)
if img.shape != target_size:
img = cv2.resize(img, target_size)
print(img.shape)
x = image.img_to_array(img)
x *= 1. / 255 # 相當於ImageDataGenerator(rescale=1. / 255)
x = np.expand_dims(x, axis=0) # 調整圖片維度
preds = model.predict(x)
return preds[0]
if name == 'main':
model_path = 'animal.h5'
model = load_model(model_path)
target_size = (150, 150)
img_path = 'data/test/300.jpg'
res = predict(model, img_path, target_size)
print(res)感謝作者分享-http://bjbsair.com/2020-04-07/tech-info/30654.html
Keras基本的使用都已經清楚了,那么這篇主要學習如何使用Keras進行訓練模型,訓練訓練,主要就是“練”,所以多做幾個案例就知道怎么做了。
在本文中,我們將提供一些面向小數據集(幾百張到幾千張圖片)構造高效,實用的圖像分類器的方法。
1,熱身練習——CIFAR10 小圖片分類示例(Sequential式)
示例中CIFAR10采用的是Sequential式來編譯網絡結構。代碼如下:
要訓練模型,首先得知道數據長啥樣
from future import print_function
import keras
from keras.datasets import cifar10
from keras.preprocessing.image import ImageDataGenerator
from keras.models import Sequential
from keras.layers import Dense, Dropout, Activation, Flatten
from keras.layers import Conv2D, MaxPooling2D
batch_size = 32
num_classes = 10
epochs = 100
data_augmentation = True
數據載入
(x_train, y_train), (x_test, y_test) = cifar10.load_data()
多分類標簽生成,我們將其由單個標簽,生成一個熱編碼的形式
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)
網絡結構配置
model = Sequential()
model.add(Conv2D(32, (3, 3), padding='same',
input_shape=x_train.shape[1:])) # (32, 32, 3)
model.add(Activation('relu'))
model.add(Conv2D(32, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Conv2D(64, (3, 3), padding='same'))
model.add(Activation('relu'))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(512))
model.add(Activation('relu'))
model.add(Dropout(num_classes))
model.add(Activation('softmax'))
訓練參數設置
initiate RMSprop optimizer
opt = keras.optimizers.rmsprop(lr=0.0001, decay=1e-6)
Let's train the model using RMSprop
model.compile(loss='categorical_crossentropy',
optimizer=opt,
metrics=['accuracy'])
生成訓練數據
x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
x_train /= 255
x_test /= 255
if not data_augmentation:
print("Not using data augmentation")
model.fit(x_train, y_train,
batch_size=batch_size,
epochs=epochs,
validation_data=(x_test, y_test),
shuffle=True)
else:
print("Using real-time data augmentation")
this will do preprocessing and realtime data augmentation
datagen = ImageDataGenerator(
featurewise_center=False, # set input mean to 0 over the dataset
samplewise_center=False, # set each sample mean to 0
featurewise_std_normalization=False, # divide inputs by std of the dataset
samplewise_std_normalization=False, # divide each input by its std
zca_whitening=False, # apply ZCA whitening
rotation_range=0, # randomly rotate images in the range (degrees, 0 to 180)
width_shift_range=0.1, # randomly shift images horizontally (fraction of total width)
height_shift_range=0.1, # randomly shift images vertically (fraction of total height)
horizontal_flip=True, # randomly flip images
vertical_flip=False) # randomly flip images
Compute quantities required for feature-wise normalization
(std, mean, and principal components if ZCA whitening is applied).
datagen.fit(x_train)
fit訓練
fit the model on batches generated by datagen.flow()
model.fit_generator(datagen.flow(x_train, y_train,
batch_size=batch_size),
epochs=epochs,
validation_data=(x_test, y_test))
截取部分epoch的運行結果:
49056/50000 [============================>.] - ETA: 4s - loss: 0.6400 - acc: 0.7855
49088/50000 [============================>.] - ETA: 4s - loss: 0.6399 - acc: 0.7855
49120/50000 [============================>.] - ETA: 3s - loss: 0.6401 - acc: 0.7855
49152/50000 [============================>.] - ETA: 3s - loss: 0.6399 - acc: 0.7855
49184/50000 [============================>.] - ETA: 3s - loss: 0.6398 - acc: 0.7856
49216/50000 [============================>.] - ETA: 3s - loss: 0.6397 - acc: 0.7856
49248/50000 [============================>.] - ETA: 3s - loss: 0.6395 - acc: 0.7856
49280/50000 [============================>.] - ETA: 3s - loss: 0.6396 - acc: 0.7857
49312/50000 [============================>.] - ETA: 3s - loss: 0.6398 - acc: 0.7856
49344/50000 [============================>.] - ETA: 2s - loss: 0.6404 - acc: 0.7856
49376/50000 [============================>.] - ETA: 2s - loss: 0.6404 - acc: 0.7856
49408/50000 [============================>.] - ETA: 2s - loss: 0.6403 - acc: 0.7856
49440/50000 [============================>.] - ETA: 2s - loss: 0.6404 - acc: 0.7856
49472/50000 [============================>.] - ETA: 2s - loss: 0.6404 - acc: 0.7856
49504/50000 [============================>.] - ETA: 2s - loss: 0.6405 - acc: 0.7855
49536/50000 [============================>.] - ETA: 2s - loss: 0.6406 - acc: 0.7855
49568/50000 [============================>.] - ETA: 1s - loss: 0.6407 - acc: 0.7855
49600/50000 [============================>.] - ETA: 1s - loss: 0.6407 - acc: 0.7854
49632/50000 [============================>.] - ETA: 1s - loss: 0.6410 - acc: 0.7854
49664/50000 [============================>.] - ETA: 1s - loss: 0.6409 - acc: 0.7853
49696/50000 [============================>.] - ETA: 1s - loss: 0.6410 - acc: 0.7853
49728/50000 [============================>.] - ETA: 1s - loss: 0.6412 - acc: 0.7852
49760/50000 [============================>.] - ETA: 1s - loss: 0.6413 - acc: 0.7852
49792/50000 [============================>.] - ETA: 0s - loss: 0.6413 - acc: 0.7852
49824/50000 [============================>.] - ETA: 0s - loss: 0.6413 - acc: 0.7852
49856/50000 [============================>.] - ETA: 0s - loss: 0.6414 - acc: 0.7851
49888/50000 [============================>.] - ETA: 0s - loss: 0.6415 - acc: 0.7851
49920/50000 [============================>.] - ETA: 0s - loss: 0.6415 - acc: 0.7851
49952/50000 [============================>.] - ETA: 0s - loss: 0.6415 - acc: 0.7850
49984/50000 [============================>.] - ETA: 0s - loss: 0.6415 - acc: 0.7850
50000/50000 [==============================] - 228s 5ms/step - loss: 0.6414 - acc: 0.7851 - val_loss: 0.6509 - val_acc: 0.7836
Epoch 55/200
其實跑到第55個epoch,准確率已經達到了 0.785了,后面肯定能達到八九十,我這里就暫停了,讓我電腦歇一歇,跑一天了。其實跑這個就是證明模型沒有問題,而且我的代碼可以運行,僅此而已。
2,針對小數據集的深度學習
本節實驗基於如下配置:
- 2000張訓練圖片構成的數據集,一共有兩個類別,每類1000張
- 安裝有Keras,Scipy,PIL的機器,如果有GPU就更好的了,但是因為我們面對的是小數據集,沒有也可以
- 數據集存放格式如下:
這份數據集來自於Kaggle,元數據集有12500只貓和12500只狗,我們只取了各個類的前500張圖片,另外還從各個類中取了1000張額外圖片用於測試。
下面是數據集的一些示例圖片,圖片的數量非常少,這對於圖像分類來說是個大麻煩。但現實是,很多真實世界圖片獲取是很困難的,我們能得到的樣本數目確實很有限(比如醫學圖像,每張正樣本都意味着一個承受痛苦的病人)對數據科學家而言,我們應該有能夠榨取少量數據的全部價值的能力,而不是簡單的伸手要更多的數據。
在Kaggle的貓狗大戰競賽中,參賽者通過使用現代的深度學習技術達到了98%的正確率,我們只使用了全部數據的8%,因此這個問題對我們來說更難。
經常聽說的一種做法是,深度學習只有在你擁有海量數據的時候才有意義。雖然這種說法並不是完全不對,但卻具有較強的誤導性。當然,深度學習強調從數據中自動學習特征的能力,沒有足夠的訓練樣本,這幾乎是不可能的。尤其是當輸入的數據維度很高(如圖片)時。然而,卷積神經網絡作為深度學習的支柱,被設計為針對“感知”問題最好的模型之一(如圖像分類問題),即使只有很少的數據,網絡也能把特征學的不錯。針對小數據集的神經網絡依然能夠得到合理的結果,並不需要任何手工的特征工程。一言以蔽之,卷積神經網絡大法好!另一方面,深度學習模型天然就具有可重用的特性:比方說,你可以把一個在大規模數據上訓練好的圖像分類或語音識別的模型重用在另一個很不一樣的問題上,而只需要做有限的一點改動。尤其是在計算機視覺領域,許多預訓練的模型現在都被公開下載,並重用在其他問題上以提升在小數據集上的性能。
2.1 數據預處理與數據提升
為了盡量利用我們有限的訓練數據,我們將通過一系列變換堆數據進行提升,這樣我們的模型將看不到任何兩張完全相同的圖片,這有利於我們抑制過擬合,使得模型的泛化能力更好。
在Keras中,這個步驟可以通過keras.preprocessing.image.ImageDataGenerator來實現,也就是圖片預處理生成器,這個類使你可以:
- 在訓練過程中,設置要施行的隨機變換。
- 通過 .flow 或者 .flow_from_directory(directory) 方法實例化一個針對圖像 batch 的生成器,這些生成器可以被用作 Keras模型相關方法的輸入,如 fit_generator,evaluate_generator 和 predict_generator。
datagen = ImageDataGenerator()
datagen.fit(x_train)
生成器初始化 datagen,生成 datagen.fit,計算依賴於數據的變化所需要的統計信息。
最終把數據需要按照每個batch進行划分,這樣就可以送到模型進行訓練了
datagen.flow(x_train, y_train, batch_size=batch_size)
接收numpy數組和標簽為參數,生成經過數據提升或標准化后的batch數據,並在一個無限循環中不斷的返回batch數據。
具體的圖片生成器函數ImageDataGenerator:(他可以用以生成一個 batch的圖像數據,支持實時數據提升,訓練時該函數會無限生成數據,直到達到規定的 epoch次數為止。)
參數意思:
方法有三個,分別是 fit() flow() flow_from_directory() 下面繼續截圖Keras官網的內容:
現在我們看一個例子:
coding:utf-8
'''
使用ImageDataGenerator 來生成圖片,並將其保存在一個臨時文件夾中
下面感受一下數據提升究竟做了什么事情。
'''
import os
from keras.preprocessing.image import ImageDataGenerator, array_to_img, img_to_array, load_img
datagen = ImageDataGenerator(
rotation_range=40, # 是一個0~180的度數,用來指定隨機選擇圖片的角度
width_shift_range=0.2, # 水平方向的隨機移動程度
height_shift_range=0.2, # 豎直方向的隨機移動程度
rescale=1./255, #將在執行其他處理前乘到整個圖像上
shear_range=0.2, # 用來進行剪切變換的程度,參考剪切變換
zoom_range=0.2, # 用來進行隨機的放大
horizontal_flip=True, # 隨機的對圖片進行水平翻轉,此參數用於水平翻轉不影響圖片語義的時候
fill_mode='nearest' # 用來指定當需要進行像素填充,如旋轉,水平和豎直位移的時候
)
pho_path = 'timg.jpg'
img = load_img(pho_path) # this is a PIL image
x = img_to_array(img) # this is a Numpy array with shape (3, 150, 150)
x = x.reshape((1, ) + x.shape) # this is a Numpy array with shape (1, 3, 150, 150)
if not os.path.exists('preview'):
os.mkdir('preview')
i = 0
for batch in datagen.flow(x, batch_size=1,
save_to_dir='preview', save_prefix='durant', save_format='jpg'):
i += 1
if i > 20:
break # otherwise the generator would loop indefitely
這是原圖:
下面是一張圖片被提升以后得到的多個結果:
下面粘貼三個官網的例子:
1,使用.flow() 的例子
2,使用.flow_from_directory(directory)
3,同時變換圖像和 mask
2.2 在小數據集上訓練神經網絡:40行代碼達到80%的准確率
進行圖像分類的正確工具是卷積網絡,所以我們來試試用卷積神經網絡搭建一個初級的模型。因為我們的樣本數很少,所以我們應該對過擬合的問題多加注意。當一個模型從很少的樣本中學習到不能推廣到新數據的模式時,我們稱為出現了過擬合的問題。過擬合發生時,模型試圖使用不相關的特征來進行預測。例如,你有三張伐木工人的照片,有三張水手的照片。六張照片中只有一個伐木工人戴了帽子,如果你認為戴帽子是能將伐木工人與水手區別開的特征,那么此時你就是一個差勁的分類器。
數據提升是對抗過擬合問題的一個武器,但還不夠,因為提升過的數據讓然是高度相關的。對抗過擬合的你應該主要關注的時模型的“熵容量”——模型允許存儲的信息量。能夠存儲更多信息的模型能夠利用更多的特征取得更好的性能,但也有存儲不相關特征的風險。另一方面,只能存儲少量信息的模型會將存儲的特征主要集中在真正相關的特征上,並有更好的泛華性能。
有很多不同的方法來調整模型的“熵容量”,常見的一種選擇是調整模型的參數數目,即模型的層數和每層的規模。另一種方法時對權重進行正則化約束,如L1或L2這種約束會使模型的權重偏向較小的值。
在我們的模型里,我們使用了很小的卷積網絡,只有很少的幾層,每層的濾波器數目也不多。再加上數據提升和Dropout,就差不多了。Dropout通過防止一層看到兩次完全一樣的模式來防止過擬合,相當於也是數據提升的方法。(你可以說Dropout和數據提升都在隨機擾亂數據的相關性)
下面展示的代碼是我們的第一個模型,一個很簡單的3層卷積加上ReLU激活函數,再接max-pooling層,這個結構和Yann LeCun 在1990 年發布的圖像分類器很相似(除了ReLU)
這個實驗的代碼如下:
coding:utf-8
from keras.models import Sequential
from keras.layers import Convolution2D, MaxPooling2D
from keras.layers import Activation, Dropout, Flatten, Dense
from keras.preprocessing.image import ImageDataGenerator
from keras import backend as K
K.set_image_dim_ordering('th')
簡單的三層卷積加上ReLU激活函數,再接一個max-pooling層
model = Sequential()
model.add(Convolution2D(32, 3, 3, input_shape=(3, 150, 150)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Convolution2D(32, 3, 3))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Convolution2D(64, 3, 3))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
the model so far outputs 3D feature maps (height, width, features)
然后我們接了兩個全連接網絡,並以單個神經元和Sigmoid激活結束模型
這種選擇會產生一個二分類的結果,與這種配置項適應,損失函數選擇binary_crossentropy
this converts our 3D feature maps to 1D feature vectors
model.add(Flatten())
添加隱藏層神經元的數量和激活函數
model.add(Dense(64))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(1))
model.add(Activation('sigmoid'))
model.compile(loss='binary_crossentropy',
optimizer='rmsprop',
metrics=['accuracy'])
next read data
使用 .flow_from_directory() 來從我們的jpgs圖片中直接產生數據和標簽
this is the augmentation configuration we will use for training
train_datagen = ImageDataGenerator(
rescale=1./255,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True
)
this is the augmentation configuration we will use for testing only rescaling
test_datagen = ImageDataGenerator(rescale=1./255)
this is a generator that will read pictures found in subfliders
of 'data/train'. and indefinitely generate batches of augmented image data
train_generator = train_datagen.flow_from_directory(
'DogsVSCats/train', # this is the target directory
target_size=(150, 150), # all image will be resize to 150*150
batch_size=32,
class_mode='binary'
) # since we use binary_crossentropy loss,we need binary labels
this is a similar generator, for validation data
validation_generator = test_datagen.flow_from_directory(
'DogsVSCats/valid', # this is the target directory
target_size=(150, 150), # all image will be resize to 150*150
batch_size=32,
class_mode='binary'
)
然后我們可以用這個生成器來訓練網絡了。
model.fit_generator(
train_generator,
samples_per_epoch=2000,
nb_epoch=50,
validation_data=validation_generator,
nb_val_samples=800
)
model.save_weights('first_try.h5') #always save your weights after training or duraing trianing
部分結果如下:
Epoch 1/50
62/62 [==============================] - 66s 1s/step - loss: 0.7043 - acc: 0.5238 - val_loss: 0.6798 - val_acc: 0.5015
Epoch 2/50
62/62 [==============================] - 63s 1s/step - loss: 0.6837 - acc: 0.5762 - val_loss: 0.6481 - val_acc: 0.6837
Epoch 3/50
62/62 [==============================] - 63s 1s/step - loss: 0.6465 - acc: 0.6503 - val_loss: 0.5826 - val_acc: 0.6827
Epoch 4/50
62/62 [==============================] - 63s 1s/step - loss: 0.6077 - acc: 0.6884 - val_loss: 0.5512 - val_acc: 0.7447
Epoch 5/50
62/62 [==============================] - 63s 1s/step - loss: 0.5568 - acc: 0.7088 - val_loss: 0.5127 - val_acc: 0.7357
Epoch 6/50
62/62 [==============================] - 63s 1s/step - loss: 0.5469 - acc: 0.7241 - val_loss: 0.4962 - val_acc: 0.7578
Epoch 7/50
62/62 [==============================] - 63s 1s/step - loss: 0.5236 - acc: 0.7446 - val_loss: 0.4325 - val_acc: 0.8028
Epoch 8/50
62/62 [==============================] - 63s 1s/step - loss: 0.4842 - acc: 0.7751 - val_loss: 0.4710 - val_acc: 0.7758
Epoch 9/50
62/62 [==============================] - 63s 1s/step - loss: 0.4693 - acc: 0.7726 - val_loss: 0.4383 - val_acc: 0.7808
Epoch 10/50
62/62 [==============================] - 63s 1s/step - loss: 0.4545 - acc: 0.7952 - val_loss: 0.3806 - val_acc: 0.8298
Epoch 11/50
62/62 [==============================] - 63s 1s/step - loss: 0.4331 - acc: 0.8031 - val_loss: 0.3781 - val_acc: 0.8248
Epoch 12/50
62/62 [==============================] - 63s 1s/step - loss: 0.4178 - acc: 0.8162 - val_loss: 0.3146 - val_acc: 0.8799
Epoch 13/50
62/62 [==============================] - 63s 1s/step - loss: 0.3926 - acc: 0.8275 - val_loss: 0.3030 - val_acc: 0.8739
Epoch 14/50
62/62 [==============================] - 63s 1s/step - loss: 0.3854 - acc: 0.8295 - val_loss: 0.2835 - val_acc: 0.8929
Epoch 15/50
62/62 [==============================] - 63s 1s/step - loss: 0.3714 - acc: 0.8303 - val_loss: 0.2882 - val_acc: 0.8879
Epoch 16/50
62/62 [==============================] - 63s 1s/step - loss: 0.3596 - acc: 0.8517 - val_loss: 0.3727 - val_acc: 0.8228
Epoch 17/50
62/62 [==============================] - 63s 1s/step - loss: 0.3369 - acc: 0.8568 - val_loss: 0.3638 - val_acc: 0.8328
Epoch 18/50
62/62 [==============================] - 63s 1s/step - loss: 0.3249 - acc: 0.8608 - val_loss: 0.2589 - val_acc: 0.8819
Epoch 19/50
62/62 [==============================] - 63s 1s/step - loss: 0.3348 - acc: 0.8548 - val_loss: 0.2273 - val_acc: 0.9079
Epoch 20/50
62/62 [==============================] - 63s 1s/step - loss: 0.2979 - acc: 0.8754 - val_loss: 0.1737 - val_acc: 0.9389
Epoch 21/50
62/62 [==============================] - 63s 1s/step - loss: 0.2980 - acc: 0.8686 - val_loss: 0.2198 - val_acc: 0.9189
Epoch 22/50
62/62 [==============================] - 63s 1s/step - loss: 0.2789 - acc: 0.8815 - val_loss: 0.2040 - val_acc: 0.9109
Epoch 23/50
62/62 [==============================] - 63s 1s/step - loss: 0.2793 - acc: 0.8891 - val_loss: 0.1388 - val_acc: 0.9479
Epoch 24/50
62/62 [==============================] - 63s 1s/step - loss: 0.2799 - acc: 0.8865 - val_loss: 0.1565 - val_acc: 0.9419
Epoch 25/50
62/62 [==============================] - 63s 1s/step - loss: 0.2513 - acc: 0.8949 - val_loss: 0.1467 - val_acc: 0.9510
Epoch 26/50
62/62 [==============================] - 63s 1s/step - loss: 0.2551 - acc: 0.9029 - val_loss: 0.1281 - val_acc: 0.9520
Epoch 27/50
62/62 [==============================] - 63s 1s/step - loss: 0.2387 - acc: 0.8961 - val_loss: 0.1590 - val_acc: 0.9409
Epoch 28/50
62/62 [==============================] - 63s 1s/step - loss: 0.2449 - acc: 0.9054 - val_loss: 0.1250 - val_acc: 0.9580
Epoch 29/50
62/62 [==============================] - 63s 1s/step - loss: 0.2158 - acc: 0.9218 - val_loss: 0.0881 - val_acc: 0.9780
Epoch 30/50
62/62 [==============================] - 63s 1s/step - loss: 0.2286 - acc: 0.9158 - val_loss: 0.1012 - val_acc: 0.9660
Epoch 31/50
62/62 [==============================] - 63s 1s/step - loss: 0.2017 - acc: 0.9181 - val_loss: 0.1109 - val_acc: 0.9570
Epoch 32/50
62/62 [==============================] - 63s 1s/step - loss: 0.1957 - acc: 0.9213 - val_loss: 0.1160 - val_acc: 0.9560
Epoch 33/50
62/62 [==============================] - 63s 1s/step - loss: 0.2046 - acc: 0.9249 - val_loss: 0.0600 - val_acc: 0.9840
Epoch 34/50
62/62 [==============================] - 63s 1s/step - loss: 0.1967 - acc: 0.9206 - val_loss: 0.0713 - val_acc: 0.9790
Epoch 35/50
62/62 [==============================] - 63s 1s/step - loss: 0.2238 - acc: 0.9153 - val_loss: 0.3123 - val_acc: 0.8929
Epoch 36/50
62/62 [==============================] - 63s 1s/step - loss: 0.1841 - acc: 0.9317 - val_loss: 0.0751 - val_acc: 0.9740
Epoch 37/50
62/62 [==============================] - 63s 1s/step - loss: 0.1890 - acc: 0.9279 - val_loss: 0.1030 - val_acc: 0.9700
這個模型在50個epoch后的准確率為 79%~81%。(但是我的准確率在38個epoch卻達到了驚人的92%,也是恐怖)沒有做模型和超參數的優化。
注意這個准確率的變化可能會比較大,因為准確率本來就是一個變化較高的評估參數,而且我們的訓練樣本比較少,所以比較好的驗證方法就是使用K折交叉驗證,但每輪驗證中我們都要訓練一個模型。
2.3 Keras報錯:ValueError: Negative dimension size caused by subtracting 2 ...
使用Keras時遇到如下錯誤:
1
2
ValueError: Negative dimension size caused by subtracting 2 from 1
for 'block2_pool/MaxPool' (op: 'MaxPool') with input shapes: [?,1,75,128].
解決方法:這個是圖片的通道順序問題。
以128*128的RGB圖像為例 channels_last應該將數據組織為(128, 128, 3),而channels_first將數據組織為(3, 128, 128)。
通過查看函數 set_image_dim_ording():
def set_image_dim_ordering(dim_ordering):
"""Legacy setter for image_data_format
.
Arguments
dim_ordering: string. tf
or th
.
Raises
ValueError: if dim_ordering
is invalid.
"""
global _IMAGE_DATA_FORMAT
if dim_ordering not in {'tf', 'th'}:
raise ValueError('Unknown dim_ordering:', dim_ordering)
if dim_ordering == 'th':
data_format = 'channels_first'
else:
data_format = 'channels_last'
_IMAGE_DATA_FORMAT = data_format
可知,tf對應原本的 channels_last,th對應 channels_first,因此添加下面代碼即可解決問題:
from keras import backend as K
K.set_image_dim_ordering('th')
這樣保證要使用的通道順序和配置的通道順序一致即可。
3,多分類簡易網絡結構(Sequential)
數據描述:
共有500張圖片,分為大巴車、恐龍、大象、鮮花和馬五個類,每個類100張。下載地址:http://pan.baidu.com/s/1nuqlTnN
編號分別以3,4,5,6,7開頭,各為一類。我從其中每類選出20張作為測試,其余80張作為訓練。因此最終訓練圖片400張,測試圖片100張,共5類。如下圖:
(注意,這里需要自己將圖片分為五類,包括訓練集和測試集)
3.1 載入與模型網絡構建
代碼如下:
載入與模型網絡構建
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D
from keras.layers import Activation, Dropout, Flatten, Dense
def built_model():
model = Sequential()
model.add(Conv2D(32, (3, 3), input_shape=(150, 150, 3)))
filter大小為3*3 數量為32個,原始圖像大小3,150 150
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(32, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
this converts ours 3D feature maps to 1D feature vector
model.add(Dense(64))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(5)) # 幾個分類就幾個dense
model.add(Activation('softmax')) # 多分類
3.2 圖像預處理
下面我們開始准備數據,使用 .flow_from_directory() 來從我們的jpgs圖片中直接產生數據和標簽。
其中值得留意的是:
- ImageDataGenerate:用以生成一個 batch 的圖像數據,支持實時數據提升。訓練時該函數會無限生成數據,直到達到規定的epoch次數為止。
- flow_from_directory(directory):以文件夾路徑為參數,生成經過數據提升/歸一化后的數據,在一個無限循環中無限產生batch數據。
def generate_data():
'''
flow_from_directory是計算數據的一些屬性值,之后再訓練階段直接丟進去這些生成器。
通過這個函數來准確數據,可以讓我們的jpgs圖片中直接產生數據和標簽
:return:
'''
train_datagen = ImageDataGenerator(
rescale=1. / 255,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True
)
test_datagen = ImageDataGenerator(rescale=1. / 255)
train_generator = train_datagen.flow_from_directory(
'data/mytrain',
target_size=(150, 150), # all images will be resized to 150*150
batch_size=32,
class_mode='categorical' # 多分類
)
validation_generator = test_datagen.flow_from_directory(
'data/mytest',
target_size=(150, 150),
batch_size=32,
class_mode='categorical' # 多分類
)
return train_generator, validation_generator
3.3 載入與模型網絡構建
代碼如下:(和上面兩分類的沒多少差別)
def built_model():
載入與模型網絡構建
model = Sequential()
model.add(Conv2D(32, (3, 3), input_shape=(150, 150, 3)))
filter大小為3*3 數量為32個,原始圖像大小3,150 150
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(32, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
this converts ours 3D feature maps to 1D feature vector
model.add(Dense(64))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(5)) # 幾個分類就幾個dense
model.add(Activation('softmax')) # 多分類
model.compile(loss='binary_corssentropy',
optimizer='rmsprop',
metrics=['accuracy'])
優化器rmsprop:除學習率可調整外,建議保持優化器的其他默認參數不變
model.compile(loss='categorical_crossentropy',
optimizer='rmsprop',
metrics=['accuracy'])
model.summary()
return model
3.4 訓練
代碼如下:
def train_model(model=None):
if model is None:
model = built_model()
model.fit_generator(
train_generator,
sampels_per_epoch 相當於每個epoch數據量峰值,
每個epoch以經過模型的樣本數達到samples_per_epoch時,記一個epoch結束
samples_per_epoch=2000,
nb_epoch=50,
validation_data=validation_generator,
nb_val_samples=800
)
model.save_weights('first_try_animal.h5')
3.5 部分結果展示和所有源碼
部分結果如下(這里僅展示一個完整的epoch訓練數據):
Epoch 50/50
1/62 [..............................] - ETA: 1:10 - loss: 0.4921 - acc: 0.9062
2/62 [..............................] - ETA: 1:13 - loss: 0.2460 - acc: 0.9531
3/62 [>.............................] - ETA: 1:12 - loss: 0.1640 - acc: 0.9688
4/62 [>.............................] - ETA: 1:02 - loss: 0.1230 - acc: 0.9766
5/62 [=>............................] - ETA: 1:02 - loss: 0.0985 - acc: 0.9812
6/62 [=>............................] - ETA: 1:02 - loss: 0.0821 - acc: 0.9844
7/62 [==>...........................] - ETA: 1:02 - loss: 0.0704 - acc: 0.9866
8/62 [==>...........................] - ETA: 1:00 - loss: 0.0616 - acc: 0.9883
9/62 [===>..........................] - ETA: 59s - loss: 0.0547 - acc: 0.9896
10/62 [===>..........................] - ETA: 59s - loss: 0.0493 - acc: 0.9906
11/62 [====>.........................] - ETA: 59s - loss: 0.0448 - acc: 0.9915
12/62 [====>.........................] - ETA: 58s - loss: 0.0412 - acc: 0.9922
13/62 [=====>........................] - ETA: 57s - loss: 0.0380 - acc: 0.9928
14/62 [=====>........................] - ETA: 55s - loss: 0.0363 - acc: 0.9933
15/62 [======>.......................] - ETA: 54s - loss: 0.0339 - acc: 0.9938
16/62 [======>.......................] - ETA: 53s - loss: 0.0318 - acc: 0.9941
17/62 [=======>......................] - ETA: 51s - loss: 0.0316 - acc: 0.9945
18/62 [=======>......................] - ETA: 50s - loss: 0.0298 - acc: 0.9948
19/62 [========>.....................] - ETA: 49s - loss: 0.0283 - acc: 0.9951
20/62 [========>.....................] - ETA: 48s - loss: 0.0268 - acc: 0.9953
21/62 [=========>....................] - ETA: 47s - loss: 0.0259 - acc: 0.9955
22/62 [=========>....................] - ETA: 46s - loss: 0.0247 - acc: 0.9957
23/62 [==========>...................] - ETA: 45s - loss: 0.0236 - acc: 0.9959
24/62 [==========>...................] - ETA: 44s - loss: 0.0227 - acc: 0.9961
25/62 [===========>..................] - ETA: 42s - loss: 0.0218 - acc: 0.9962
26/62 [===========>..................] - ETA: 42s - loss: 0.0209 - acc: 0.9964
27/62 [============>.................] - ETA: 41s - loss: 0.0202 - acc: 0.9965
28/62 [============>.................] - ETA: 40s - loss: 0.0194 - acc: 0.9967
29/62 [=============>................] - ETA: 39s - loss: 0.0188 - acc: 0.9968
30/62 [=============>................] - ETA: 37s - loss: 0.0181 - acc: 0.9969
31/62 [==============>...............] - ETA: 36s - loss: 0.0176 - acc: 0.9970
32/62 [==============>...............] - ETA: 34s - loss: 0.0170 - acc: 0.9971
33/62 [==============>...............] - ETA: 33s - loss: 0.0165 - acc: 0.9972
34/62 [===============>..............] - ETA: 32s - loss: 0.0160 - acc: 0.9972
35/62 [===============>..............] - ETA: 31s - loss: 0.0156 - acc: 0.9973
36/62 [================>.............] - ETA: 30s - loss: 0.0151 - acc: 0.9974
37/62 [================>.............] - ETA: 29s - loss: 0.0147 - acc: 0.9975
38/62 [=================>............] - ETA: 28s - loss: 0.0146 - acc: 0.9975
39/62 [=================>............] - ETA: 27s - loss: 0.0142 - acc: 0.9976
40/62 [==================>...........] - ETA: 26s - loss: 0.0139 - acc: 0.9977
41/62 [==================>...........] - ETA: 24s - loss: 0.0135 - acc: 0.9977
42/62 [===================>..........] - ETA: 23s - loss: 0.0132 - acc: 0.9978
43/62 [===================>..........] - ETA: 22s - loss: 0.0129 - acc: 0.9978
44/62 [====================>.........] - ETA: 21s - loss: 0.0126 - acc: 0.9979
45/62 [====================>.........] - ETA: 20s - loss: 0.0123 - acc: 0.9979
46/62 [=====================>........] - ETA: 19s - loss: 0.0135 - acc: 0.9973
47/62 [=====================>........] - ETA: 17s - loss: 0.0153 - acc: 0.9967
48/62 [======================>.......] - ETA: 16s - loss: 0.0254 - acc: 0.9961
49/62 [======================>.......] - ETA: 15s - loss: 0.0249 - acc: 0.9962
50/62 [=======================>......] - ETA: 14s - loss: 0.0244 - acc: 0.9962
51/62 [=======================>......] - ETA: 13s - loss: 0.0338 - acc: 0.9957
52/62 [========================>.....] - ETA: 11s - loss: 0.0332 - acc: 0.9958
53/62 [========================>.....] - ETA: 10s - loss: 0.0329 - acc: 0.9959
54/62 [=========================>....] - ETA: 9s - loss: 0.0323 - acc: 0.9959
55/62 [=========================>....] - ETA: 8s - loss: 0.0317 - acc: 0.9960
56/62 [==========================>...] - ETA: 7s - loss: 0.0393 - acc: 0.9950
57/62 [==========================>...] - ETA: 5s - loss: 0.0511 - acc: 0.9940
58/62 [===========================>..] - ETA: 4s - loss: 0.0502 - acc: 0.9941
59/62 [===========================>..] - ETA: 3s - loss: 0.0494 - acc: 0.9942
60/62 [============================>.] - ETA: 2s - loss: 0.0518 - acc: 0.9938
61/62 [============================>.] - ETA: 1s - loss: 0.0535 - acc: 0.9933
62/62 [==============================] - 271s 4s/step - loss: 0.0607 - acc: 0.9929 - val_loss: 0.7166 - val_acc: 0.9300
源碼如下:
載入與模型網絡構建
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D
from keras.layers import Activation, Dropout, Flatten, Dense
from keras.preprocessing.image import ImageDataGenerator
import numpy as np
import os
def built_model():
載入與模型網絡構建
model = Sequential()
model.add(Conv2D(32, (3, 3), input_shape=(150, 150, 3)))
filter大小為3*3 數量為32個,原始圖像大小3,150 150
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(32, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
this converts ours 3D feature maps to 1D feature vector
model.add(Dense(64))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(5)) # 幾個分類就幾個dense
model.add(Activation('softmax')) # 多分類
model.compile(loss='binary_corssentropy',
optimizer='rmsprop',
metrics=['accuracy'])
優化器rmsprop:除學習率可調整外,建議保持優化器的其他默認參數不變
model.compile(loss='categorical_crossentropy',
optimizer='rmsprop',
metrics=['accuracy'])
model.summary()
return model
def generate_data():
'''
flow_from_directory是計算數據的一些屬性值,之后再訓練階段直接丟進去這些生成器。
通過這個函數來准確數據,可以讓我們的jpgs圖片中直接產生數據和標簽
:return:
'''
train_datagen = ImageDataGenerator(
rescale=1. / 255,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True
)
test_datagen = ImageDataGenerator(rescale=1. / 255)
train_generator = train_datagen.flow_from_directory(
'data/mytrain',
target_size=(150, 150), # all images will be resized to 150*150
batch_size=32,
class_mode='categorical' # 多分類
)
validation_generator = test_datagen.flow_from_directory(
'data/mytest',
target_size=(150, 150),
batch_size=32,
class_mode='categorical' # 多分類
)
return train_generator, validation_generator
def train_model(model=None):
if model is None:
model = built_model()
model.fit_generator(
train_generator,
sampels_per_epoch 相當於每個epoch數據量峰值,
每個epoch以經過模型的樣本數達到samples_per_epoch時,記一個epoch結束
samples_per_epoch=2000,
nb_epoch=50,
validation_data=validation_generator,
nb_val_samples=800
)
model.save_weights('first_try_animal.h5')
if name == 'main':
train_generator, validation_generator = generate_data()
train_model()
當loss出現負數,肯定是之前多分類的標簽哪些設置的不對,
注意上面的 steps_per_epoch和validation_steps的值(應該是這樣計算出來的):
model=build_model(input_shape=(IMG_W,IMG_H,IMG_CH)) # 輸入的圖片維度
模型的訓練
model.fit_generator(train_generator, # 數據流
steps_per_epoch=train_samples_num // batch_size,
epochs=epochs,
validation_data=val_generator,
validation_steps=val_samples_num // batch_size)
3.6 畫圖展示和使用模型預測
最后我們可以通過圖直觀的查看訓練過程中的 loss 和 acc ,看看其變化趨勢。下面是代碼:
畫圖,將訓練時的acc和loss都繪制到圖上
import matplotlib.pyplot as plt
def plot_training(history):
plt.figure(12)
plt.subplot(121)
train_acc = history.history['acc']
val_acc = history.history['val_acc']
epochs = range(len(train_acc))
plt.plot(epochs, train_acc, 'b',label='train_acc')
plt.plot(epochs, val_acc, 'r',label='test_acc')
plt.title('Train and Test accuracy')
plt.legend()
plt.subplot(122)
train_loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(len(train_loss))
plt.plot(epochs, train_loss, 'b',label='train_loss')
plt.plot(epochs, val_loss, 'r',label='test_loss')
plt.title('Train and Test loss')
plt.legend()
plt.show()
我們也可以通過訓練好的模型去預測新的樣本。
單張樣本的預測代碼如下:
用訓練好的模型來預測新樣本
from PIL import Image
from keras.preprocessing import image
def predict(model, img_path, target_size):
img=Image.open(img_path) # 加載圖片
if img.size != target_size:
img = img.resize(target_size)
x = image.img_to_array(img)
x *=1./255 # 相當於ImageDataGenerator(rescale=1. / 255)
x = np.expand_dims(x, axis=0) # 調整圖片維度
preds = model.predict(x) # 預測
return preds[0]
批量預測(一個文件夾中的所有文件):
預測一個文件夾中的所有圖片
new_sample_gen=ImageDataGenerator(rescale=1. / 255)
newsample_generator=new_sample_gen.flow_from_directory(
'E:\PyProjects\DataSet\FireAI\DeepLearning',
target_size=(IMG_W, IMG_H),
batch_size=16,
class_mode=None,
shuffle=False)
predicted=model.predict_generator(newsample_generator)
print(predicted)
注意我們上面保存模型是保存的權重,而不是模型,保存模型的代碼如下:
模型的加載,預測
from keras.models import load_model
saved_model=load_model('animal.h5')
predicted=saved_model.predict_generator(newsample_generator)
print(predicted) # saved_model的結果和前面的model結果一致,表面模型正確保存和加載
如果保存的是權重,直接加載,會報錯:
最后,我自己的預測代碼:
用訓練好的模型來預測新的樣本
from keras.preprocessing import image
import cv2
import numpy as np
from keras.models import load_model
def predict(model, img_path, target_size):
img = cv2.imread(img_path)
if img.shape != target_size:
img = cv2.resize(img, target_size)
print(img.shape)
x = image.img_to_array(img)
x *= 1. / 255 # 相當於ImageDataGenerator(rescale=1. / 255)
x = np.expand_dims(x, axis=0) # 調整圖片維度
preds = model.predict(x)
return preds[0]
if name == 'main':
model_path = 'animal.h5'
model = load_model(model_path)
target_size = (150, 150)
img_path = 'data/test/300.jpg'
res = predict(model, img_path, target_size)
print(res)感謝作者分享-http://bjbsair.com/2020-04-07/tech-info/30654.html
Keras基本的使用都已經清楚了,那么這篇主要學習如何使用Keras進行訓練模型,訓練訓練,主要就是“練”,所以多做幾個案例就知道怎么做了。
在本文中,我們將提供一些面向小數據集(幾百張到幾千張圖片)構造高效,實用的圖像分類器的方法。
1,熱身練習——CIFAR10 小圖片分類示例(Sequential式)
示例中CIFAR10采用的是Sequential式來編譯網絡結構。代碼如下:
要訓練模型,首先得知道數據長啥樣
from future import print_function
import keras
from keras.datasets import cifar10
from keras.preprocessing.image import ImageDataGenerator
from keras.models import Sequential
from keras.layers import Dense, Dropout, Activation, Flatten
from keras.layers import Conv2D, MaxPooling2D
batch_size = 32
num_classes = 10
epochs = 100
data_augmentation = True
數據載入
(x_train, y_train), (x_test, y_test) = cifar10.load_data()
多分類標簽生成,我們將其由單個標簽,生成一個熱編碼的形式
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)
網絡結構配置
model = Sequential()
model.add(Conv2D(32, (3, 3), padding='same',
input_shape=x_train.shape[1:])) # (32, 32, 3)
model.add(Activation('relu'))
model.add(Conv2D(32, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Conv2D(64, (3, 3), padding='same'))
model.add(Activation('relu'))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(512))
model.add(Activation('relu'))
model.add(Dropout(num_classes))
model.add(Activation('softmax'))
訓練參數設置
initiate RMSprop optimizer
opt = keras.optimizers.rmsprop(lr=0.0001, decay=1e-6)
Let's train the model using RMSprop
model.compile(loss='categorical_crossentropy',
optimizer=opt,
metrics=['accuracy'])
生成訓練數據
x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
x_train /= 255
x_test /= 255
if not data_augmentation:
print("Not using data augmentation")
model.fit(x_train, y_train,
batch_size=batch_size,
epochs=epochs,
validation_data=(x_test, y_test),
shuffle=True)
else:
print("Using real-time data augmentation")
this will do preprocessing and realtime data augmentation
datagen = ImageDataGenerator(
featurewise_center=False, # set input mean to 0 over the dataset
samplewise_center=False, # set each sample mean to 0
featurewise_std_normalization=False, # divide inputs by std of the dataset
samplewise_std_normalization=False, # divide each input by its std
zca_whitening=False, # apply ZCA whitening
rotation_range=0, # randomly rotate images in the range (degrees, 0 to 180)
width_shift_range=0.1, # randomly shift images horizontally (fraction of total width)
height_shift_range=0.1, # randomly shift images vertically (fraction of total height)
horizontal_flip=True, # randomly flip images
vertical_flip=False) # randomly flip images
Compute quantities required for feature-wise normalization
(std, mean, and principal components if ZCA whitening is applied).
datagen.fit(x_train)
fit訓練
fit the model on batches generated by datagen.flow()
model.fit_generator(datagen.flow(x_train, y_train,
batch_size=batch_size),
epochs=epochs,
validation_data=(x_test, y_test))
截取部分epoch的運行結果:
49056/50000 [============================>.] - ETA: 4s - loss: 0.6400 - acc: 0.7855
49088/50000 [============================>.] - ETA: 4s - loss: 0.6399 - acc: 0.7855
49120/50000 [============================>.] - ETA: 3s - loss: 0.6401 - acc: 0.7855
49152/50000 [============================>.] - ETA: 3s - loss: 0.6399 - acc: 0.7855
49184/50000 [============================>.] - ETA: 3s - loss: 0.6398 - acc: 0.7856
49216/50000 [============================>.] - ETA: 3s - loss: 0.6397 - acc: 0.7856
49248/50000 [============================>.] - ETA: 3s - loss: 0.6395 - acc: 0.7856
49280/50000 [============================>.] - ETA: 3s - loss: 0.6396 - acc: 0.7857
49312/50000 [============================>.] - ETA: 3s - loss: 0.6398 - acc: 0.7856
49344/50000 [============================>.] - ETA: 2s - loss: 0.6404 - acc: 0.7856
49376/50000 [============================>.] - ETA: 2s - loss: 0.6404 - acc: 0.7856
49408/50000 [============================>.] - ETA: 2s - loss: 0.6403 - acc: 0.7856
49440/50000 [============================>.] - ETA: 2s - loss: 0.6404 - acc: 0.7856
49472/50000 [============================>.] - ETA: 2s - loss: 0.6404 - acc: 0.7856
49504/50000 [============================>.] - ETA: 2s - loss: 0.6405 - acc: 0.7855
49536/50000 [============================>.] - ETA: 2s - loss: 0.6406 - acc: 0.7855
49568/50000 [============================>.] - ETA: 1s - loss: 0.6407 - acc: 0.7855
49600/50000 [============================>.] - ETA: 1s - loss: 0.6407 - acc: 0.7854
49632/50000 [============================>.] - ETA: 1s - loss: 0.6410 - acc: 0.7854
49664/50000 [============================>.] - ETA: 1s - loss: 0.6409 - acc: 0.7853
49696/50000 [============================>.] - ETA: 1s - loss: 0.6410 - acc: 0.7853
49728/50000 [============================>.] - ETA: 1s - loss: 0.6412 - acc: 0.7852
49760/50000 [============================>.] - ETA: 1s - loss: 0.6413 - acc: 0.7852
49792/50000 [============================>.] - ETA: 0s - loss: 0.6413 - acc: 0.7852
49824/50000 [============================>.] - ETA: 0s - loss: 0.6413 - acc: 0.7852
49856/50000 [============================>.] - ETA: 0s - loss: 0.6414 - acc: 0.7851
49888/50000 [============================>.] - ETA: 0s - loss: 0.6415 - acc: 0.7851
49920/50000 [============================>.] - ETA: 0s - loss: 0.6415 - acc: 0.7851
49952/50000 [============================>.] - ETA: 0s - loss: 0.6415 - acc: 0.7850
49984/50000 [============================>.] - ETA: 0s - loss: 0.6415 - acc: 0.7850
50000/50000 [==============================] - 228s 5ms/step - loss: 0.6414 - acc: 0.7851 - val_loss: 0.6509 - val_acc: 0.7836
Epoch 55/200
其實跑到第55個epoch,准確率已經達到了 0.785了,后面肯定能達到八九十,我這里就暫停了,讓我電腦歇一歇,跑一天了。其實跑這個就是證明模型沒有問題,而且我的代碼可以運行,僅此而已。
2,針對小數據集的深度學習
本節實驗基於如下配置:
- 2000張訓練圖片構成的數據集,一共有兩個類別,每類1000張
- 安裝有Keras,Scipy,PIL的機器,如果有GPU就更好的了,但是因為我們面對的是小數據集,沒有也可以
- 數據集存放格式如下:
這份數據集來自於Kaggle,元數據集有12500只貓和12500只狗,我們只取了各個類的前500張圖片,另外還從各個類中取了1000張額外圖片用於測試。
下面是數據集的一些示例圖片,圖片的數量非常少,這對於圖像分類來說是個大麻煩。但現實是,很多真實世界圖片獲取是很困難的,我們能得到的樣本數目確實很有限(比如醫學圖像,每張正樣本都意味着一個承受痛苦的病人)對數據科學家而言,我們應該有能夠榨取少量數據的全部價值的能力,而不是簡單的伸手要更多的數據。
在Kaggle的貓狗大戰競賽中,參賽者通過使用現代的深度學習技術達到了98%的正確率,我們只使用了全部數據的8%,因此這個問題對我們來說更難。
經常聽說的一種做法是,深度學習只有在你擁有海量數據的時候才有意義。雖然這種說法並不是完全不對,但卻具有較強的誤導性。當然,深度學習強調從數據中自動學習特征的能力,沒有足夠的訓練樣本,這幾乎是不可能的。尤其是當輸入的數據維度很高(如圖片)時。然而,卷積神經網絡作為深度學習的支柱,被設計為針對“感知”問題最好的模型之一(如圖像分類問題),即使只有很少的數據,網絡也能把特征學的不錯。針對小數據集的神經網絡依然能夠得到合理的結果,並不需要任何手工的特征工程。一言以蔽之,卷積神經網絡大法好!另一方面,深度學習模型天然就具有可重用的特性:比方說,你可以把一個在大規模數據上訓練好的圖像分類或語音識別的模型重用在另一個很不一樣的問題上,而只需要做有限的一點改動。尤其是在計算機視覺領域,許多預訓練的模型現在都被公開下載,並重用在其他問題上以提升在小數據集上的性能。
2.1 數據預處理與數據提升
為了盡量利用我們有限的訓練數據,我們將通過一系列變換堆數據進行提升,這樣我們的模型將看不到任何兩張完全相同的圖片,這有利於我們抑制過擬合,使得模型的泛化能力更好。
在Keras中,這個步驟可以通過keras.preprocessing.image.ImageDataGenerator來實現,也就是圖片預處理生成器,這個類使你可以:
- 在訓練過程中,設置要施行的隨機變換。
- 通過 .flow 或者 .flow_from_directory(directory) 方法實例化一個針對圖像 batch 的生成器,這些生成器可以被用作 Keras模型相關方法的輸入,如 fit_generator,evaluate_generator 和 predict_generator。
datagen = ImageDataGenerator()
datagen.fit(x_train)
生成器初始化 datagen,生成 datagen.fit,計算依賴於數據的變化所需要的統計信息。
最終把數據需要按照每個batch進行划分,這樣就可以送到模型進行訓練了
datagen.flow(x_train, y_train, batch_size=batch_size)
接收numpy數組和標簽為參數,生成經過數據提升或標准化后的batch數據,並在一個無限循環中不斷的返回batch數據。
具體的圖片生成器函數ImageDataGenerator:(他可以用以生成一個 batch的圖像數據,支持實時數據提升,訓練時該函數會無限生成數據,直到達到規定的 epoch次數為止。)
參數意思:
方法有三個,分別是 fit() flow() flow_from_directory() 下面繼續截圖Keras官網的內容:
現在我們看一個例子:
coding:utf-8
'''
使用ImageDataGenerator 來生成圖片,並將其保存在一個臨時文件夾中
下面感受一下數據提升究竟做了什么事情。
'''
import os
from keras.preprocessing.image import ImageDataGenerator, array_to_img, img_to_array, load_img
datagen = ImageDataGenerator(
rotation_range=40, # 是一個0~180的度數,用來指定隨機選擇圖片的角度
width_shift_range=0.2, # 水平方向的隨機移動程度
height_shift_range=0.2, # 豎直方向的隨機移動程度
rescale=1./255, #將在執行其他處理前乘到整個圖像上
shear_range=0.2, # 用來進行剪切變換的程度,參考剪切變換
zoom_range=0.2, # 用來進行隨機的放大
horizontal_flip=True, # 隨機的對圖片進行水平翻轉,此參數用於水平翻轉不影響圖片語義的時候
fill_mode='nearest' # 用來指定當需要進行像素填充,如旋轉,水平和豎直位移的時候
)
pho_path = 'timg.jpg'
img = load_img(pho_path) # this is a PIL image
x = img_to_array(img) # this is a Numpy array with shape (3, 150, 150)
x = x.reshape((1, ) + x.shape) # this is a Numpy array with shape (1, 3, 150, 150)
if not os.path.exists('preview'):
os.mkdir('preview')
i = 0
for batch in datagen.flow(x, batch_size=1,
save_to_dir='preview', save_prefix='durant', save_format='jpg'):
i += 1
if i > 20:
break # otherwise the generator would loop indefitely
這是原圖:
下面是一張圖片被提升以后得到的多個結果:
下面粘貼三個官網的例子:
1,使用.flow() 的例子
2,使用.flow_from_directory(directory)
3,同時變換圖像和 mask
2.2 在小數據集上訓練神經網絡:40行代碼達到80%的准確率
進行圖像分類的正確工具是卷積網絡,所以我們來試試用卷積神經網絡搭建一個初級的模型。因為我們的樣本數很少,所以我們應該對過擬合的問題多加注意。當一個模型從很少的樣本中學習到不能推廣到新數據的模式時,我們稱為出現了過擬合的問題。過擬合發生時,模型試圖使用不相關的特征來進行預測。例如,你有三張伐木工人的照片,有三張水手的照片。六張照片中只有一個伐木工人戴了帽子,如果你認為戴帽子是能將伐木工人與水手區別開的特征,那么此時你就是一個差勁的分類器。
數據提升是對抗過擬合問題的一個武器,但還不夠,因為提升過的數據讓然是高度相關的。對抗過擬合的你應該主要關注的時模型的“熵容量”——模型允許存儲的信息量。能夠存儲更多信息的模型能夠利用更多的特征取得更好的性能,但也有存儲不相關特征的風險。另一方面,只能存儲少量信息的模型會將存儲的特征主要集中在真正相關的特征上,並有更好的泛華性能。
有很多不同的方法來調整模型的“熵容量”,常見的一種選擇是調整模型的參數數目,即模型的層數和每層的規模。另一種方法時對權重進行正則化約束,如L1或L2這種約束會使模型的權重偏向較小的值。
在我們的模型里,我們使用了很小的卷積網絡,只有很少的幾層,每層的濾波器數目也不多。再加上數據提升和Dropout,就差不多了。Dropout通過防止一層看到兩次完全一樣的模式來防止過擬合,相當於也是數據提升的方法。(你可以說Dropout和數據提升都在隨機擾亂數據的相關性)
下面展示的代碼是我們的第一個模型,一個很簡單的3層卷積加上ReLU激活函數,再接max-pooling層,這個結構和Yann LeCun 在1990 年發布的圖像分類器很相似(除了ReLU)
這個實驗的代碼如下:
coding:utf-8
from keras.models import Sequential
from keras.layers import Convolution2D, MaxPooling2D
from keras.layers import Activation, Dropout, Flatten, Dense
from keras.preprocessing.image import ImageDataGenerator
from keras import backend as K
K.set_image_dim_ordering('th')
簡單的三層卷積加上ReLU激活函數,再接一個max-pooling層
model = Sequential()
model.add(Convolution2D(32, 3, 3, input_shape=(3, 150, 150)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Convolution2D(32, 3, 3))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Convolution2D(64, 3, 3))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
the model so far outputs 3D feature maps (height, width, features)
然后我們接了兩個全連接網絡,並以單個神經元和Sigmoid激活結束模型
這種選擇會產生一個二分類的結果,與這種配置項適應,損失函數選擇binary_crossentropy
this converts our 3D feature maps to 1D feature vectors
model.add(Flatten())
添加隱藏層神經元的數量和激活函數
model.add(Dense(64))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(1))
model.add(Activation('sigmoid'))
model.compile(loss='binary_crossentropy',
optimizer='rmsprop',
metrics=['accuracy'])
next read data
使用 .flow_from_directory() 來從我們的jpgs圖片中直接產生數據和標簽
this is the augmentation configuration we will use for training
train_datagen = ImageDataGenerator(
rescale=1./255,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True
)
this is the augmentation configuration we will use for testing only rescaling
test_datagen = ImageDataGenerator(rescale=1./255)
this is a generator that will read pictures found in subfliders
of 'data/train'. and indefinitely generate batches of augmented image data
train_generator = train_datagen.flow_from_directory(
'DogsVSCats/train', # this is the target directory
target_size=(150, 150), # all image will be resize to 150*150
batch_size=32,
class_mode='binary'
) # since we use binary_crossentropy loss,we need binary labels
this is a similar generator, for validation data
validation_generator = test_datagen.flow_from_directory(
'DogsVSCats/valid', # this is the target directory
target_size=(150, 150), # all image will be resize to 150*150
batch_size=32,
class_mode='binary'
)
然后我們可以用這個生成器來訓練網絡了。
model.fit_generator(
train_generator,
samples_per_epoch=2000,
nb_epoch=50,
validation_data=validation_generator,
nb_val_samples=800
)
model.save_weights('first_try.h5') #always save your weights after training or duraing trianing
部分結果如下:
Epoch 1/50
62/62 [==============================] - 66s 1s/step - loss: 0.7043 - acc: 0.5238 - val_loss: 0.6798 - val_acc: 0.5015
Epoch 2/50
62/62 [==============================] - 63s 1s/step - loss: 0.6837 - acc: 0.5762 - val_loss: 0.6481 - val_acc: 0.6837
Epoch 3/50
62/62 [==============================] - 63s 1s/step - loss: 0.6465 - acc: 0.6503 - val_loss: 0.5826 - val_acc: 0.6827
Epoch 4/50
62/62 [==============================] - 63s 1s/step - loss: 0.6077 - acc: 0.6884 - val_loss: 0.5512 - val_acc: 0.7447
Epoch 5/50
62/62 [==============================] - 63s 1s/step - loss: 0.5568 - acc: 0.7088 - val_loss: 0.5127 - val_acc: 0.7357
Epoch 6/50
62/62 [==============================] - 63s 1s/step - loss: 0.5469 - acc: 0.7241 - val_loss: 0.4962 - val_acc: 0.7578
Epoch 7/50
62/62 [==============================] - 63s 1s/step - loss: 0.5236 - acc: 0.7446 - val_loss: 0.4325 - val_acc: 0.8028
Epoch 8/50
62/62 [==============================] - 63s 1s/step - loss: 0.4842 - acc: 0.7751 - val_loss: 0.4710 - val_acc: 0.7758
Epoch 9/50
62/62 [==============================] - 63s 1s/step - loss: 0.4693 - acc: 0.7726 - val_loss: 0.4383 - val_acc: 0.7808
Epoch 10/50
62/62 [==============================] - 63s 1s/step - loss: 0.4545 - acc: 0.7952 - val_loss: 0.3806 - val_acc: 0.8298
Epoch 11/50
62/62 [==============================] - 63s 1s/step - loss: 0.4331 - acc: 0.8031 - val_loss: 0.3781 - val_acc: 0.8248
Epoch 12/50
62/62 [==============================] - 63s 1s/step - loss: 0.4178 - acc: 0.8162 - val_loss: 0.3146 - val_acc: 0.8799
Epoch 13/50
62/62 [==============================] - 63s 1s/step - loss: 0.3926 - acc: 0.8275 - val_loss: 0.3030 - val_acc: 0.8739
Epoch 14/50
62/62 [==============================] - 63s 1s/step - loss: 0.3854 - acc: 0.8295 - val_loss: 0.2835 - val_acc: 0.8929
Epoch 15/50
62/62 [==============================] - 63s 1s/step - loss: 0.3714 - acc: 0.8303 - val_loss: 0.2882 - val_acc: 0.8879
Epoch 16/50
62/62 [==============================] - 63s 1s/step - loss: 0.3596 - acc: 0.8517 - val_loss: 0.3727 - val_acc: 0.8228
Epoch 17/50
62/62 [==============================] - 63s 1s/step - loss: 0.3369 - acc: 0.8568 - val_loss: 0.3638 - val_acc: 0.8328
Epoch 18/50
62/62 [==============================] - 63s 1s/step - loss: 0.3249 - acc: 0.8608 - val_loss: 0.2589 - val_acc: 0.8819
Epoch 19/50
62/62 [==============================] - 63s 1s/step - loss: 0.3348 - acc: 0.8548 - val_loss: 0.2273 - val_acc: 0.9079
Epoch 20/50
62/62 [==============================] - 63s 1s/step - loss: 0.2979 - acc: 0.8754 - val_loss: 0.1737 - val_acc: 0.9389
Epoch 21/50
62/62 [==============================] - 63s 1s/step - loss: 0.2980 - acc: 0.8686 - val_loss: 0.2198 - val_acc: 0.9189
Epoch 22/50
62/62 [==============================] - 63s 1s/step - loss: 0.2789 - acc: 0.8815 - val_loss: 0.2040 - val_acc: 0.9109
Epoch 23/50
62/62 [==============================] - 63s 1s/step - loss: 0.2793 - acc: 0.8891 - val_loss: 0.1388 - val_acc: 0.9479
Epoch 24/50
62/62 [==============================] - 63s 1s/step - loss: 0.2799 - acc: 0.8865 - val_loss: 0.1565 - val_acc: 0.9419
Epoch 25/50
62/62 [==============================] - 63s 1s/step - loss: 0.2513 - acc: 0.8949 - val_loss: 0.1467 - val_acc: 0.9510
Epoch 26/50
62/62 [==============================] - 63s 1s/step - loss: 0.2551 - acc: 0.9029 - val_loss: 0.1281 - val_acc: 0.9520
Epoch 27/50
62/62 [==============================] - 63s 1s/step - loss: 0.2387 - acc: 0.8961 - val_loss: 0.1590 - val_acc: 0.9409
Epoch 28/50
62/62 [==============================] - 63s 1s/step - loss: 0.2449 - acc: 0.9054 - val_loss: 0.1250 - val_acc: 0.9580
Epoch 29/50
62/62 [==============================] - 63s 1s/step - loss: 0.2158 - acc: 0.9218 - val_loss: 0.0881 - val_acc: 0.9780
Epoch 30/50
62/62 [==============================] - 63s 1s/step - loss: 0.2286 - acc: 0.9158 - val_loss: 0.1012 - val_acc: 0.9660
Epoch 31/50
62/62 [==============================] - 63s 1s/step - loss: 0.2017 - acc: 0.9181 - val_loss: 0.1109 - val_acc: 0.9570
Epoch 32/50
62/62 [==============================] - 63s 1s/step - loss: 0.1957 - acc: 0.9213 - val_loss: 0.1160 - val_acc: 0.9560
Epoch 33/50
62/62 [==============================] - 63s 1s/step - loss: 0.2046 - acc: 0.9249 - val_loss: 0.0600 - val_acc: 0.9840
Epoch 34/50
62/62 [==============================] - 63s 1s/step - loss: 0.1967 - acc: 0.9206 - val_loss: 0.0713 - val_acc: 0.9790
Epoch 35/50
62/62 [==============================] - 63s 1s/step - loss: 0.2238 - acc: 0.9153 - val_loss: 0.3123 - val_acc: 0.8929
Epoch 36/50
62/62 [==============================] - 63s 1s/step - loss: 0.1841 - acc: 0.9317 - val_loss: 0.0751 - val_acc: 0.9740
Epoch 37/50
62/62 [==============================] - 63s 1s/step - loss: 0.1890 - acc: 0.9279 - val_loss: 0.1030 - val_acc: 0.9700
這個模型在50個epoch后的准確率為 79%~81%。(但是我的准確率在38個epoch卻達到了驚人的92%,也是恐怖)沒有做模型和超參數的優化。
注意這個准確率的變化可能會比較大,因為准確率本來就是一個變化較高的評估參數,而且我們的訓練樣本比較少,所以比較好的驗證方法就是使用K折交叉驗證,但每輪驗證中我們都要訓練一個模型。
2.3 Keras報錯:ValueError: Negative dimension size caused by subtracting 2 ...
使用Keras時遇到如下錯誤:
1
2
ValueError: Negative dimension size caused by subtracting 2 from 1
for 'block2_pool/MaxPool' (op: 'MaxPool') with input shapes: [?,1,75,128].
解決方法:這個是圖片的通道順序問題。
以128*128的RGB圖像為例 channels_last應該將數據組織為(128, 128, 3),而channels_first將數據組織為(3, 128, 128)。
通過查看函數 set_image_dim_ording():
def set_image_dim_ordering(dim_ordering):
"""Legacy setter for image_data_format
.
Arguments
dim_ordering: string. tf
or th
.
Raises
ValueError: if dim_ordering
is invalid.
"""
global _IMAGE_DATA_FORMAT
if dim_ordering not in {'tf', 'th'}:
raise ValueError('Unknown dim_ordering:', dim_ordering)
if dim_ordering == 'th':
data_format = 'channels_first'
else:
data_format = 'channels_last'
_IMAGE_DATA_FORMAT = data_format
可知,tf對應原本的 channels_last,th對應 channels_first,因此添加下面代碼即可解決問題:
from keras import backend as K
K.set_image_dim_ordering('th')
這樣保證要使用的通道順序和配置的通道順序一致即可。
3,多分類簡易網絡結構(Sequential)
數據描述:
共有500張圖片,分為大巴車、恐龍、大象、鮮花和馬五個類,每個類100張。下載地址:http://pan.baidu.com/s/1nuqlTnN
編號分別以3,4,5,6,7開頭,各為一類。我從其中每類選出20張作為測試,其余80張作為訓練。因此最終訓練圖片400張,測試圖片100張,共5類。如下圖:
(注意,這里需要自己將圖片分為五類,包括訓練集和測試集)
3.1 載入與模型網絡構建
代碼如下:
載入與模型網絡構建
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D
from keras.layers import Activation, Dropout, Flatten, Dense
def built_model():
model = Sequential()
model.add(Conv2D(32, (3, 3), input_shape=(150, 150, 3)))
filter大小為3*3 數量為32個,原始圖像大小3,150 150
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(32, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
this converts ours 3D feature maps to 1D feature vector
model.add(Dense(64))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(5)) # 幾個分類就幾個dense
model.add(Activation('softmax')) # 多分類
3.2 圖像預處理
下面我們開始准備數據,使用 .flow_from_directory() 來從我們的jpgs圖片中直接產生數據和標簽。
其中值得留意的是:
- ImageDataGenerate:用以生成一個 batch 的圖像數據,支持實時數據提升。訓練時該函數會無限生成數據,直到達到規定的epoch次數為止。
- flow_from_directory(directory):以文件夾路徑為參數,生成經過數據提升/歸一化后的數據,在一個無限循環中無限產生batch數據。
def generate_data():
'''
flow_from_directory是計算數據的一些屬性值,之后再訓練階段直接丟進去這些生成器。
通過這個函數來准確數據,可以讓我們的jpgs圖片中直接產生數據和標簽
:return:
'''
train_datagen = ImageDataGenerator(
rescale=1. / 255,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True
)
test_datagen = ImageDataGenerator(rescale=1. / 255)
train_generator = train_datagen.flow_from_directory(
'data/mytrain',
target_size=(150, 150), # all images will be resized to 150*150
batch_size=32,
class_mode='categorical' # 多分類
)
validation_generator = test_datagen.flow_from_directory(
'data/mytest',
target_size=(150, 150),
batch_size=32,
class_mode='categorical' # 多分類
)
return train_generator, validation_generator
3.3 載入與模型網絡構建
代碼如下:(和上面兩分類的沒多少差別)
def built_model():
載入與模型網絡構建
model = Sequential()
model.add(Conv2D(32, (3, 3), input_shape=(150, 150, 3)))
filter大小為3*3 數量為32個,原始圖像大小3,150 150
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(32, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
this converts ours 3D feature maps to 1D feature vector
model.add(Dense(64))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(5)) # 幾個分類就幾個dense
model.add(Activation('softmax')) # 多分類
model.compile(loss='binary_corssentropy',
optimizer='rmsprop',
metrics=['accuracy'])
優化器rmsprop:除學習率可調整外,建議保持優化器的其他默認參數不變
model.compile(loss='categorical_crossentropy',
optimizer='rmsprop',
metrics=['accuracy'])
model.summary()
return model
3.4 訓練
代碼如下:
def train_model(model=None):
if model is None:
model = built_model()
model.fit_generator(
train_generator,
sampels_per_epoch 相當於每個epoch數據量峰值,
每個epoch以經過模型的樣本數達到samples_per_epoch時,記一個epoch結束
samples_per_epoch=2000,
nb_epoch=50,
validation_data=validation_generator,
nb_val_samples=800
)
model.save_weights('first_try_animal.h5')
3.5 部分結果展示和所有源碼
部分結果如下(這里僅展示一個完整的epoch訓練數據):
Epoch 50/50
1/62 [..............................] - ETA: 1:10 - loss: 0.4921 - acc: 0.9062
2/62 [..............................] - ETA: 1:13 - loss: 0.2460 - acc: 0.9531
3/62 [>.............................] - ETA: 1:12 - loss: 0.1640 - acc: 0.9688
4/62 [>.............................] - ETA: 1:02 - loss: 0.1230 - acc: 0.9766
5/62 [=>............................] - ETA: 1:02 - loss: 0.0985 - acc: 0.9812
6/62 [=>............................] - ETA: 1:02 - loss: 0.0821 - acc: 0.9844
7/62 [==>...........................] - ETA: 1:02 - loss: 0.0704 - acc: 0.9866
8/62 [==>...........................] - ETA: 1:00 - loss: 0.0616 - acc: 0.9883
9/62 [===>..........................] - ETA: 59s - loss: 0.0547 - acc: 0.9896
10/62 [===>..........................] - ETA: 59s - loss: 0.0493 - acc: 0.9906
11/62 [====>.........................] - ETA: 59s - loss: 0.0448 - acc: 0.9915
12/62 [====>.........................] - ETA: 58s - loss: 0.0412 - acc: 0.9922
13/62 [=====>........................] - ETA: 57s - loss: 0.0380 - acc: 0.9928
14/62 [=====>........................] - ETA: 55s - loss: 0.0363 - acc: 0.9933
15/62 [======>.......................] - ETA: 54s - loss: 0.0339 - acc: 0.9938
16/62 [======>.......................] - ETA: 53s - loss: 0.0318 - acc: 0.9941
17/62 [=======>......................] - ETA: 51s - loss: 0.0316 - acc: 0.9945
18/62 [=======>......................] - ETA: 50s - loss: 0.0298 - acc: 0.9948
19/62 [========>.....................] - ETA: 49s - loss: 0.0283 - acc: 0.9951
20/62 [========>.....................] - ETA: 48s - loss: 0.0268 - acc: 0.9953
21/62 [=========>....................] - ETA: 47s - loss: 0.0259 - acc: 0.9955
22/62 [=========>....................] - ETA: 46s - loss: 0.0247 - acc: 0.9957
23/62 [==========>...................] - ETA: 45s - loss: 0.0236 - acc: 0.9959
24/62 [==========>...................] - ETA: 44s - loss: 0.0227 - acc: 0.9961
25/62 [===========>..................] - ETA: 42s - loss: 0.0218 - acc: 0.9962
26/62 [===========>..................] - ETA: 42s - loss: 0.0209 - acc: 0.9964
27/62 [============>.................] - ETA: 41s - loss: 0.0202 - acc: 0.9965
28/62 [============>.................] - ETA: 40s - loss: 0.0194 - acc: 0.9967
29/62 [=============>................] - ETA: 39s - loss: 0.0188 - acc: 0.9968
30/62 [=============>................] - ETA: 37s - loss: 0.0181 - acc: 0.9969
31/62 [==============>...............] - ETA: 36s - loss: 0.0176 - acc: 0.9970
32/62 [==============>...............] - ETA: 34s - loss: 0.0170 - acc: 0.9971
33/62 [==============>...............] - ETA: 33s - loss: 0.0165 - acc: 0.9972
34/62 [===============>..............] - ETA: 32s - loss: 0.0160 - acc: 0.9972
35/62 [===============>..............] - ETA: 31s - loss: 0.0156 - acc: 0.9973
36/62 [================>.............] - ETA: 30s - loss: 0.0151 - acc: 0.9974
37/62 [================>.............] - ETA: 29s - loss: 0.0147 - acc: 0.9975
38/62 [=================>............] - ETA: 28s - loss: 0.0146 - acc: 0.9975
39/62 [=================>............] - ETA: 27s - loss: 0.0142 - acc: 0.9976
40/62 [==================>...........] - ETA: 26s - loss: 0.0139 - acc: 0.9977
41/62 [==================>...........] - ETA: 24s - loss: 0.0135 - acc: 0.9977
42/62 [===================>..........] - ETA: 23s - loss: 0.0132 - acc: 0.9978
43/62 [===================>..........] - ETA: 22s - loss: 0.0129 - acc: 0.9978
44/62 [====================>.........] - ETA: 21s - loss: 0.0126 - acc: 0.9979
45/62 [====================>.........] - ETA: 20s - loss: 0.0123 - acc: 0.9979
46/62 [=====================>........] - ETA: 19s - loss: 0.0135 - acc: 0.9973
47/62 [=====================>........] - ETA: 17s - loss: 0.0153 - acc: 0.9967
48/62 [======================>.......] - ETA: 16s - loss: 0.0254 - acc: 0.9961
49/62 [======================>.......] - ETA: 15s - loss: 0.0249 - acc: 0.9962
50/62 [=======================>......] - ETA: 14s - loss: 0.0244 - acc: 0.9962
51/62 [=======================>......] - ETA: 13s - loss: 0.0338 - acc: 0.9957
52/62 [========================>.....] - ETA: 11s - loss: 0.0332 - acc: 0.9958
53/62 [========================>.....] - ETA: 10s - loss: 0.0329 - acc: 0.9959
54/62 [=========================>....] - ETA: 9s - loss: 0.0323 - acc: 0.9959
55/62 [=========================>....] - ETA: 8s - loss: 0.0317 - acc: 0.9960
56/62 [==========================>...] - ETA: 7s - loss: 0.0393 - acc: 0.9950
57/62 [==========================>...] - ETA: 5s - loss: 0.0511 - acc: 0.9940
58/62 [===========================>..] - ETA: 4s - loss: 0.0502 - acc: 0.9941
59/62 [===========================>..] - ETA: 3s - loss: 0.0494 - acc: 0.9942
60/62 [============================>.] - ETA: 2s - loss: 0.0518 - acc: 0.9938
61/62 [============================>.] - ETA: 1s - loss: 0.0535 - acc: 0.9933
62/62 [==============================] - 271s 4s/step - loss: 0.0607 - acc: 0.9929 - val_loss: 0.7166 - val_acc: 0.9300
源碼如下:
載入與模型網絡構建
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D
from keras.layers import Activation, Dropout, Flatten, Dense
from keras.preprocessing.image import ImageDataGenerator
import numpy as np
import os
def built_model():
載入與模型網絡構建
model = Sequential()
model.add(Conv2D(32, (3, 3), input_shape=(150, 150, 3)))
filter大小為3*3 數量為32個,原始圖像大小3,150 150
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(32, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
this converts ours 3D feature maps to 1D feature vector
model.add(Dense(64))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(5)) # 幾個分類就幾個dense
model.add(Activation('softmax')) # 多分類
model.compile(loss='binary_corssentropy',
optimizer='rmsprop',
metrics=['accuracy'])
優化器rmsprop:除學習率可調整外,建議保持優化器的其他默認參數不變
model.compile(loss='categorical_crossentropy',
optimizer='rmsprop',
metrics=['accuracy'])
model.summary()
return model
def generate_data():
'''
flow_from_directory是計算數據的一些屬性值,之后再訓練階段直接丟進去這些生成器。
通過這個函數來准確數據,可以讓我們的jpgs圖片中直接產生數據和標簽
:return:
'''
train_datagen = ImageDataGenerator(
rescale=1. / 255,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True
)
test_datagen = ImageDataGenerator(rescale=1. / 255)
train_generator = train_datagen.flow_from_directory(
'data/mytrain',
target_size=(150, 150), # all images will be resized to 150*150
batch_size=32,
class_mode='categorical' # 多分類
)
validation_generator = test_datagen.flow_from_directory(
'data/mytest',
target_size=(150, 150),
batch_size=32,
class_mode='categorical' # 多分類
)
return train_generator, validation_generator
def train_model(model=None):
if model is None:
model = built_model()
model.fit_generator(
train_generator,
sampels_per_epoch 相當於每個epoch數據量峰值,
每個epoch以經過模型的樣本數達到samples_per_epoch時,記一個epoch結束
samples_per_epoch=2000,
nb_epoch=50,
validation_data=validation_generator,
nb_val_samples=800
)
model.save_weights('first_try_animal.h5')
if name == 'main':
train_generator, validation_generator = generate_data()
train_model()
當loss出現負數,肯定是之前多分類的標簽哪些設置的不對,
注意上面的 steps_per_epoch和validation_steps的值(應該是這樣計算出來的):
model=build_model(input_shape=(IMG_W,IMG_H,IMG_CH)) # 輸入的圖片維度
模型的訓練
model.fit_generator(train_generator, # 數據流
steps_per_epoch=train_samples_num // batch_size,
epochs=epochs,
validation_data=val_generator,
validation_steps=val_samples_num // batch_size)
3.6 畫圖展示和使用模型預測
最后我們可以通過圖直觀的查看訓練過程中的 loss 和 acc ,看看其變化趨勢。下面是代碼:
畫圖,將訓練時的acc和loss都繪制到圖上
import matplotlib.pyplot as plt
def plot_training(history):
plt.figure(12)
plt.subplot(121)
train_acc = history.history['acc']
val_acc = history.history['val_acc']
epochs = range(len(train_acc))
plt.plot(epochs, train_acc, 'b',label='train_acc')
plt.plot(epochs, val_acc, 'r',label='test_acc')
plt.title('Train and Test accuracy')
plt.legend()
plt.subplot(122)
train_loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(len(train_loss))
plt.plot(epochs, train_loss, 'b',label='train_loss')
plt.plot(epochs, val_loss, 'r',label='test_loss')
plt.title('Train and Test loss')
plt.legend()
plt.show()
我們也可以通過訓練好的模型去預測新的樣本。
單張樣本的預測代碼如下:
用訓練好的模型來預測新樣本
from PIL import Image
from keras.preprocessing import image
def predict(model, img_path, target_size):
img=Image.open(img_path) # 加載圖片
if img.size != target_size:
img = img.resize(target_size)
x = image.img_to_array(img)
x *=1./255 # 相當於ImageDataGenerator(rescale=1. / 255)
x = np.expand_dims(x, axis=0) # 調整圖片維度
preds = model.predict(x) # 預測
return preds[0]
批量預測(一個文件夾中的所有文件):
預測一個文件夾中的所有圖片
new_sample_gen=ImageDataGenerator(rescale=1. / 255)
newsample_generator=new_sample_gen.flow_from_directory(
'E:\PyProjects\DataSet\FireAI\DeepLearning',
target_size=(IMG_W, IMG_H),
batch_size=16,
class_mode=None,
shuffle=False)
predicted=model.predict_generator(newsample_generator)
print(predicted)
注意我們上面保存模型是保存的權重,而不是模型,保存模型的代碼如下:
模型的加載,預測
from keras.models import load_model
saved_model=load_model('animal.h5')
predicted=saved_model.predict_generator(newsample_generator)
print(predicted) # saved_model的結果和前面的model結果一致,表面模型正確保存和加載
如果保存的是權重,直接加載,會報錯:
最后,我自己的預測代碼:
用訓練好的模型來預測新的樣本
from keras.preprocessing import image
import cv2
import numpy as np
from keras.models import load_model
def predict(model, img_path, target_size):
img = cv2.imread(img_path)
if img.shape != target_size:
img = cv2.resize(img, target_size)
print(img.shape)
x = image.img_to_array(img)
x *= 1. / 255 # 相當於ImageDataGenerator(rescale=1. / 255)
x = np.expand_dims(x, axis=0) # 調整圖片維度
preds = model.predict(x)
return preds[0]
if name == 'main':
model_path = 'animal.h5'
model = load_model(model_path)
target_size = (150, 150)
img_path = 'data/test/300.jpg'
res = predict(model, img_path, target_size)
print(res)感謝作者分享-http://bjbsair.com/2020-04-07/tech-info/30654.html
Keras基本的使用都已經清楚了,那么這篇主要學習如何使用Keras進行訓練模型,訓練訓練,主要就是“練”,所以多做幾個案例就知道怎么做了。
在本文中,我們將提供一些面向小數據集(幾百張到幾千張圖片)構造高效,實用的圖像分類器的方法。
1,熱身練習——CIFAR10 小圖片分類示例(Sequential式)
示例中CIFAR10采用的是Sequential式來編譯網絡結構。代碼如下:
要訓練模型,首先得知道數據長啥樣
from future import print_function
import keras
from keras.datasets import cifar10
from keras.preprocessing.image import ImageDataGenerator
from keras.models import Sequential
from keras.layers import Dense, Dropout, Activation, Flatten
from keras.layers import Conv2D, MaxPooling2D
batch_size = 32
num_classes = 10
epochs = 100
data_augmentation = True
數據載入
(x_train, y_train), (x_test, y_test) = cifar10.load_data()
多分類標簽生成,我們將其由單個標簽,生成一個熱編碼的形式
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)
網絡結構配置
model = Sequential()
model.add(Conv2D(32, (3, 3), padding='same',
input_shape=x_train.shape[1:])) # (32, 32, 3)
model.add(Activation('relu'))
model.add(Conv2D(32, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Conv2D(64, (3, 3), padding='same'))
model.add(Activation('relu'))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(512))
model.add(Activation('relu'))
model.add(Dropout(num_classes))
model.add(Activation('softmax'))
訓練參數設置
initiate RMSprop optimizer
opt = keras.optimizers.rmsprop(lr=0.0001, decay=1e-6)
Let's train the model using RMSprop
model.compile(loss='categorical_crossentropy',
optimizer=opt,
metrics=['accuracy'])
生成訓練數據
x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
x_train /= 255
x_test /= 255
if not data_augmentation:
print("Not using data augmentation")
model.fit(x_train, y_train,
batch_size=batch_size,
epochs=epochs,
validation_data=(x_test, y_test),
shuffle=True)
else:
print("Using real-time data augmentation")
this will do preprocessing and realtime data augmentation
datagen = ImageDataGenerator(
featurewise_center=False, # set input mean to 0 over the dataset
samplewise_center=False, # set each sample mean to 0
featurewise_std_normalization=False, # divide inputs by std of the dataset
samplewise_std_normalization=False, # divide each input by its std
zca_whitening=False, # apply ZCA whitening
rotation_range=0, # randomly rotate images in the range (degrees, 0 to 180)
width_shift_range=0.1, # randomly shift images horizontally (fraction of total width)
height_shift_range=0.1, # randomly shift images vertically (fraction of total height)
horizontal_flip=True, # randomly flip images
vertical_flip=False) # randomly flip images
Compute quantities required for feature-wise normalization
(std, mean, and principal components if ZCA whitening is applied).
datagen.fit(x_train)
fit訓練
fit the model on batches generated by datagen.flow()
model.fit_generator(datagen.flow(x_train, y_train,
batch_size=batch_size),
epochs=epochs,
validation_data=(x_test, y_test))
截取部分epoch的運行結果:
49056/50000 [============================>.] - ETA: 4s - loss: 0.6400 - acc: 0.7855
49088/50000 [============================>.] - ETA: 4s - loss: 0.6399 - acc: 0.7855
49120/50000 [============================>.] - ETA: 3s - loss: 0.6401 - acc: 0.7855
49152/50000 [============================>.] - ETA: 3s - loss: 0.6399 - acc: 0.7855
49184/50000 [============================>.] - ETA: 3s - loss: 0.6398 - acc: 0.7856
49216/50000 [============================>.] - ETA: 3s - loss: 0.6397 - acc: 0.7856
49248/50000 [============================>.] - ETA: 3s - loss: 0.6395 - acc: 0.7856
49280/50000 [============================>.] - ETA: 3s - loss: 0.6396 - acc: 0.7857
49312/50000 [============================>.] - ETA: 3s - loss: 0.6398 - acc: 0.7856
49344/50000 [============================>.] - ETA: 2s - loss: 0.6404 - acc: 0.7856
49376/50000 [============================>.] - ETA: 2s - loss: 0.6404 - acc: 0.7856
49408/50000 [============================>.] - ETA: 2s - loss: 0.6403 - acc: 0.7856
49440/50000 [============================>.] - ETA: 2s - loss: 0.6404 - acc: 0.7856
49472/50000 [============================>.] - ETA: 2s - loss: 0.6404 - acc: 0.7856
49504/50000 [============================>.] - ETA: 2s - loss: 0.6405 - acc: 0.7855
49536/50000 [============================>.] - ETA: 2s - loss: 0.6406 - acc: 0.7855
49568/50000 [============================>.] - ETA: 1s - loss: 0.6407 - acc: 0.7855
49600/50000 [============================>.] - ETA: 1s - loss: 0.6407 - acc: 0.7854
49632/50000 [============================>.] - ETA: 1s - loss: 0.6410 - acc: 0.7854
49664/50000 [============================>.] - ETA: 1s - loss: 0.6409 - acc: 0.7853
49696/50000 [============================>.] - ETA: 1s - loss: 0.6410 - acc: 0.7853
49728/50000 [============================>.] - ETA: 1s - loss: 0.6412 - acc: 0.7852
49760/50000 [============================>.] - ETA: 1s - loss: 0.6413 - acc: 0.7852
49792/50000 [============================>.] - ETA: 0s - loss: 0.6413 - acc: 0.7852
49824/50000 [============================>.] - ETA: 0s - loss: 0.6413 - acc: 0.7852
49856/50000 [============================>.] - ETA: 0s - loss: 0.6414 - acc: 0.7851
49888/50000 [============================>.] - ETA: 0s - loss: 0.6415 - acc: 0.7851
49920/50000 [============================>.] - ETA: 0s - loss: 0.6415 - acc: 0.7851
49952/50000 [============================>.] - ETA: 0s - loss: 0.6415 - acc: 0.7850
49984/50000 [============================>.] - ETA: 0s - loss: 0.6415 - acc: 0.7850
50000/50000 [==============================] - 228s 5ms/step - loss: 0.6414 - acc: 0.7851 - val_loss: 0.6509 - val_acc: 0.7836
Epoch 55/200
其實跑到第55個epoch,准確率已經達到了 0.785了,后面肯定能達到八九十,我這里就暫停了,讓我電腦歇一歇,跑一天了。其實跑這個就是證明模型沒有問題,而且我的代碼可以運行,僅此而已。
2,針對小數據集的深度學習
本節實驗基於如下配置:
- 2000張訓練圖片構成的數據集,一共有兩個類別,每類1000張
- 安裝有Keras,Scipy,PIL的機器,如果有GPU就更好的了,但是因為我們面對的是小數據集,沒有也可以
- 數據集存放格式如下:
這份數據集來自於Kaggle,元數據集有12500只貓和12500只狗,我們只取了各個類的前500張圖片,另外還從各個類中取了1000張額外圖片用於測試。
下面是數據集的一些示例圖片,圖片的數量非常少,這對於圖像分類來說是個大麻煩。但現實是,很多真實世界圖片獲取是很困難的,我們能得到的樣本數目確實很有限(比如醫學圖像,每張正樣本都意味着一個承受痛苦的病人)對數據科學家而言,我們應該有能夠榨取少量數據的全部價值的能力,而不是簡單的伸手要更多的數據。
在Kaggle的貓狗大戰競賽中,參賽者通過使用現代的深度學習技術達到了98%的正確率,我們只使用了全部數據的8%,因此這個問題對我們來說更難。
經常聽說的一種做法是,深度學習只有在你擁有海量數據的時候才有意義。雖然這種說法並不是完全不對,但卻具有較強的誤導性。當然,深度學習強調從數據中自動學習特征的能力,沒有足夠的訓練樣本,這幾乎是不可能的。尤其是當輸入的數據維度很高(如圖片)時。然而,卷積神經網絡作為深度學習的支柱,被設計為針對“感知”問題最好的模型之一(如圖像分類問題),即使只有很少的數據,網絡也能把特征學的不錯。針對小數據集的神經網絡依然能夠得到合理的結果,並不需要任何手工的特征工程。一言以蔽之,卷積神經網絡大法好!另一方面,深度學習模型天然就具有可重用的特性:比方說,你可以把一個在大規模數據上訓練好的圖像分類或語音識別的模型重用在另一個很不一樣的問題上,而只需要做有限的一點改動。尤其是在計算機視覺領域,許多預訓練的模型現在都被公開下載,並重用在其他問題上以提升在小數據集上的性能。
2.1 數據預處理與數據提升
為了盡量利用我們有限的訓練數據,我們將通過一系列變換堆數據進行提升,這樣我們的模型將看不到任何兩張完全相同的圖片,這有利於我們抑制過擬合,使得模型的泛化能力更好。
在Keras中,這個步驟可以通過keras.preprocessing.image.ImageDataGenerator來實現,也就是圖片預處理生成器,這個類使你可以:
- 在訓練過程中,設置要施行的隨機變換。
- 通過 .flow 或者 .flow_from_directory(directory) 方法實例化一個針對圖像 batch 的生成器,這些生成器可以被用作 Keras模型相關方法的輸入,如 fit_generator,evaluate_generator 和 predict_generator。
datagen = ImageDataGenerator()
datagen.fit(x_train)
生成器初始化 datagen,生成 datagen.fit,計算依賴於數據的變化所需要的統計信息。
最終把數據需要按照每個batch進行划分,這樣就可以送到模型進行訓練了
datagen.flow(x_train, y_train, batch_size=batch_size)
接收numpy數組和標簽為參數,生成經過數據提升或標准化后的batch數據,並在一個無限循環中不斷的返回batch數據。
具體的圖片生成器函數ImageDataGenerator:(他可以用以生成一個 batch的圖像數據,支持實時數據提升,訓練時該函數會無限生成數據,直到達到規定的 epoch次數為止。)
參數意思:
方法有三個,分別是 fit() flow() flow_from_directory() 下面繼續截圖Keras官網的內容:
現在我們看一個例子:
coding:utf-8
'''
使用ImageDataGenerator 來生成圖片,並將其保存在一個臨時文件夾中
下面感受一下數據提升究竟做了什么事情。
'''
import os
from keras.preprocessing.image import ImageDataGenerator, array_to_img, img_to_array, load_img
datagen = ImageDataGenerator(
rotation_range=40, # 是一個0~180的度數,用來指定隨機選擇圖片的角度
width_shift_range=0.2, # 水平方向的隨機移動程度
height_shift_range=0.2, # 豎直方向的隨機移動程度
rescale=1./255, #將在執行其他處理前乘到整個圖像上
shear_range=0.2, # 用來進行剪切變換的程度,參考剪切變換
zoom_range=0.2, # 用來進行隨機的放大
horizontal_flip=True, # 隨機的對圖片進行水平翻轉,此參數用於水平翻轉不影響圖片語義的時候
fill_mode='nearest' # 用來指定當需要進行像素填充,如旋轉,水平和豎直位移的時候
)
pho_path = 'timg.jpg'
img = load_img(pho_path) # this is a PIL image
x = img_to_array(img) # this is a Numpy array with shape (3, 150, 150)
x = x.reshape((1, ) + x.shape) # this is a Numpy array with shape (1, 3, 150, 150)
if not os.path.exists('preview'):
os.mkdir('preview')
i = 0
for batch in datagen.flow(x, batch_size=1,
save_to_dir='preview', save_prefix='durant', save_format='jpg'):
i += 1
if i > 20:
break # otherwise the generator would loop indefitely
這是原圖:
下面是一張圖片被提升以后得到的多個結果:
下面粘貼三個官網的例子:
1,使用.flow() 的例子
2,使用.flow_from_directory(directory)
3,同時變換圖像和 mask
2.2 在小數據集上訓練神經網絡:40行代碼達到80%的准確率
進行圖像分類的正確工具是卷積網絡,所以我們來試試用卷積神經網絡搭建一個初級的模型。因為我們的樣本數很少,所以我們應該對過擬合的問題多加注意。當一個模型從很少的樣本中學習到不能推廣到新數據的模式時,我們稱為出現了過擬合的問題。過擬合發生時,模型試圖使用不相關的特征來進行預測。例如,你有三張伐木工人的照片,有三張水手的照片。六張照片中只有一個伐木工人戴了帽子,如果你認為戴帽子是能將伐木工人與水手區別開的特征,那么此時你就是一個差勁的分類器。
數據提升是對抗過擬合問題的一個武器,但還不夠,因為提升過的數據讓然是高度相關的。對抗過擬合的你應該主要關注的時模型的“熵容量”——模型允許存儲的信息量。能夠存儲更多信息的模型能夠利用更多的特征取得更好的性能,但也有存儲不相關特征的風險。另一方面,只能存儲少量信息的模型會將存儲的特征主要集中在真正相關的特征上,並有更好的泛華性能。
有很多不同的方法來調整模型的“熵容量”,常見的一種選擇是調整模型的參數數目,即模型的層數和每層的規模。另一種方法時對權重進行正則化約束,如L1或L2這種約束會使模型的權重偏向較小的值。
在我們的模型里,我們使用了很小的卷積網絡,只有很少的幾層,每層的濾波器數目也不多。再加上數據提升和Dropout,就差不多了。Dropout通過防止一層看到兩次完全一樣的模式來防止過擬合,相當於也是數據提升的方法。(你可以說Dropout和數據提升都在隨機擾亂數據的相關性)
下面展示的代碼是我們的第一個模型,一個很簡單的3層卷積加上ReLU激活函數,再接max-pooling層,這個結構和Yann LeCun 在1990 年發布的圖像分類器很相似(除了ReLU)
這個實驗的代碼如下:
coding:utf-8
from keras.models import Sequential
from keras.layers import Convolution2D, MaxPooling2D
from keras.layers import Activation, Dropout, Flatten, Dense
from keras.preprocessing.image import ImageDataGenerator
from keras import backend as K
K.set_image_dim_ordering('th')
簡單的三層卷積加上ReLU激活函數,再接一個max-pooling層
model = Sequential()
model.add(Convolution2D(32, 3, 3, input_shape=(3, 150, 150)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Convolution2D(32, 3, 3))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Convolution2D(64, 3, 3))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
the model so far outputs 3D feature maps (height, width, features)
然后我們接了兩個全連接網絡,並以單個神經元和Sigmoid激活結束模型
這種選擇會產生一個二分類的結果,與這種配置項適應,損失函數選擇binary_crossentropy
this converts our 3D feature maps to 1D feature vectors
model.add(Flatten())
添加隱藏層神經元的數量和激活函數
model.add(Dense(64))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(1))
model.add(Activation('sigmoid'))
model.compile(loss='binary_crossentropy',
optimizer='rmsprop',
metrics=['accuracy'])
next read data
使用 .flow_from_directory() 來從我們的jpgs圖片中直接產生數據和標簽
this is the augmentation configuration we will use for training
train_datagen = ImageDataGenerator(
rescale=1./255,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True
)
this is the augmentation configuration we will use for testing only rescaling
test_datagen = ImageDataGenerator(rescale=1./255)
this is a generator that will read pictures found in subfliders
of 'data/train'. and indefinitely generate batches of augmented image data
train_generator = train_datagen.flow_from_directory(
'DogsVSCats/train', # this is the target directory
target_size=(150, 150), # all image will be resize to 150*150
batch_size=32,
class_mode='binary'
) # since we use binary_crossentropy loss,we need binary labels
this is a similar generator, for validation data
validation_generator = test_datagen.flow_from_directory(
'DogsVSCats/valid', # this is the target directory
target_size=(150, 150), # all image will be resize to 150*150
batch_size=32,
class_mode='binary'
)
然后我們可以用這個生成器來訓練網絡了。
model.fit_generator(
train_generator,
samples_per_epoch=2000,
nb_epoch=50,
validation_data=validation_generator,
nb_val_samples=800
)
model.save_weights('first_try.h5') #always save your weights after training or duraing trianing
部分結果如下:
Epoch 1/50
62/62 [==============================] - 66s 1s/step - loss: 0.7043 - acc: 0.5238 - val_loss: 0.6798 - val_acc: 0.5015
Epoch 2/50
62/62 [==============================] - 63s 1s/step - loss: 0.6837 - acc: 0.5762 - val_loss: 0.6481 - val_acc: 0.6837
Epoch 3/50
62/62 [==============================] - 63s 1s/step - loss: 0.6465 - acc: 0.6503 - val_loss: 0.5826 - val_acc: 0.6827
Epoch 4/50
62/62 [==============================] - 63s 1s/step - loss: 0.6077 - acc: 0.6884 - val_loss: 0.5512 - val_acc: 0.7447
Epoch 5/50
62/62 [==============================] - 63s 1s/step - loss: 0.5568 - acc: 0.7088 - val_loss: 0.5127 - val_acc: 0.7357
Epoch 6/50
62/62 [==============================] - 63s 1s/step - loss: 0.5469 - acc: 0.7241 - val_loss: 0.4962 - val_acc: 0.7578
Epoch 7/50
62/62 [==============================] - 63s 1s/step - loss: 0.5236 - acc: 0.7446 - val_loss: 0.4325 - val_acc: 0.8028
Epoch 8/50
62/62 [==============================] - 63s 1s/step - loss: 0.4842 - acc: 0.7751 - val_loss: 0.4710 - val_acc: 0.7758
Epoch 9/50
62/62 [==============================] - 63s 1s/step - loss: 0.4693 - acc: 0.7726 - val_loss: 0.4383 - val_acc: 0.7808
Epoch 10/50
62/62 [==============================] - 63s 1s/step - loss: 0.4545 - acc: 0.7952 - val_loss: 0.3806 - val_acc: 0.8298
Epoch 11/50
62/62 [==============================] - 63s 1s/step - loss: 0.4331 - acc: 0.8031 - val_loss: 0.3781 - val_acc: 0.8248
Epoch 12/50
62/62 [==============================] - 63s 1s/step - loss: 0.4178 - acc: 0.8162 - val_loss: 0.3146 - val_acc: 0.8799
Epoch 13/50
62/62 [==============================] - 63s 1s/step - loss: 0.3926 - acc: 0.8275 - val_loss: 0.3030 - val_acc: 0.8739
Epoch 14/50
62/62 [==============================] - 63s 1s/step - loss: 0.3854 - acc: 0.8295 - val_loss: 0.2835 - val_acc: 0.8929
Epoch 15/50
62/62 [==============================] - 63s 1s/step - loss: 0.3714 - acc: 0.8303 - val_loss: 0.2882 - val_acc: 0.8879
Epoch 16/50
62/62 [==============================] - 63s 1s/step - loss: 0.3596 - acc: 0.8517 - val_loss: 0.3727 - val_acc: 0.8228
Epoch 17/50
62/62 [==============================] - 63s 1s/step - loss: 0.3369 - acc: 0.8568 - val_loss: 0.3638 - val_acc: 0.8328
Epoch 18/50
62/62 [==============================] - 63s 1s/step - loss: 0.3249 - acc: 0.8608 - val_loss: 0.2589 - val_acc: 0.8819
Epoch 19/50
62/62 [==============================] - 63s 1s/step - loss: 0.3348 - acc: 0.8548 - val_loss: 0.2273 - val_acc: 0.9079
Epoch 20/50
62/62 [==============================] - 63s 1s/step - loss: 0.2979 - acc: 0.8754 - val_loss: 0.1737 - val_acc: 0.9389
Epoch 21/50
62/62 [==============================] - 63s 1s/step - loss: 0.2980 - acc: 0.8686 - val_loss: 0.2198 - val_acc: 0.9189
Epoch 22/50
62/62 [==============================] - 63s 1s/step - loss: 0.2789 - acc: 0.8815 - val_loss: 0.2040 - val_acc: 0.9109
Epoch 23/50
62/62 [==============================] - 63s 1s/step - loss: 0.2793 - acc: 0.8891 - val_loss: 0.1388 - val_acc: 0.9479
Epoch 24/50
62/62 [==============================] - 63s 1s/step - loss: 0.2799 - acc: 0.8865 - val_loss: 0.1565 - val_acc: 0.9419
Epoch 25/50
62/62 [==============================] - 63s 1s/step - loss: 0.2513 - acc: 0.8949 - val_loss: 0.1467 - val_acc: 0.9510
Epoch 26/50
62/62 [==============================] - 63s 1s/step - loss: 0.2551 - acc: 0.9029 - val_loss: 0.1281 - val_acc: 0.9520
Epoch 27/50
62/62 [==============================] - 63s 1s/step - loss: 0.2387 - acc: 0.8961 - val_loss: 0.1590 - val_acc: 0.9409
Epoch 28/50
62/62 [==============================] - 63s 1s/step - loss: 0.2449 - acc: 0.9054 - val_loss: 0.1250 - val_acc: 0.9580
Epoch 29/50
62/62 [==============================] - 63s 1s/step - loss: 0.2158 - acc: 0.9218 - val_loss: 0.0881 - val_acc: 0.9780
Epoch 30/50
62/62 [==============================] - 63s 1s/step - loss: 0.2286 - acc: 0.9158 - val_loss: 0.1012 - val_acc: 0.9660
Epoch 31/50
62/62 [==============================] - 63s 1s/step - loss: 0.2017 - acc: 0.9181 - val_loss: 0.1109 - val_acc: 0.9570
Epoch 32/50
62/62 [==============================] - 63s 1s/step - loss: 0.1957 - acc: 0.9213 - val_loss: 0.1160 - val_acc: 0.9560
Epoch 33/50
62/62 [==============================] - 63s 1s/step - loss: 0.2046 - acc: 0.9249 - val_loss: 0.0600 - val_acc: 0.9840
Epoch 34/50
62/62 [==============================] - 63s 1s/step - loss: 0.1967 - acc: 0.9206 - val_loss: 0.0713 - val_acc: 0.9790
Epoch 35/50
62/62 [==============================] - 63s 1s/step - loss: 0.2238 - acc: 0.9153 - val_loss: 0.3123 - val_acc: 0.8929
Epoch 36/50
62/62 [==============================] - 63s 1s/step - loss: 0.1841 - acc: 0.9317 - val_loss: 0.0751 - val_acc: 0.9740
Epoch 37/50
62/62 [==============================] - 63s 1s/step - loss: 0.1890 - acc: 0.9279 - val_loss: 0.1030 - val_acc: 0.9700
這個模型在50個epoch后的准確率為 79%~81%。(但是我的准確率在38個epoch卻達到了驚人的92%,也是恐怖)沒有做模型和超參數的優化。
注意這個准確率的變化可能會比較大,因為准確率本來就是一個變化較高的評估參數,而且我們的訓練樣本比較少,所以比較好的驗證方法就是使用K折交叉驗證,但每輪驗證中我們都要訓練一個模型。
2.3 Keras報錯:ValueError: Negative dimension size caused by subtracting 2 ...
使用Keras時遇到如下錯誤:
1
2
ValueError: Negative dimension size caused by subtracting 2 from 1
for 'block2_pool/MaxPool' (op: 'MaxPool') with input shapes: [?,1,75,128].
解決方法:這個是圖片的通道順序問題。
以128*128的RGB圖像為例 channels_last應該將數據組織為(128, 128, 3),而channels_first將數據組織為(3, 128, 128)。
通過查看函數 set_image_dim_ording():
def set_image_dim_ordering(dim_ordering):
"""Legacy setter for image_data_format
.
Arguments
dim_ordering: string. tf
or th
.
Raises
ValueError: if dim_ordering
is invalid.
"""
global _IMAGE_DATA_FORMAT
if dim_ordering not in {'tf', 'th'}:
raise ValueError('Unknown dim_ordering:', dim_ordering)
if dim_ordering == 'th':
data_format = 'channels_first'
else:
data_format = 'channels_last'
_IMAGE_DATA_FORMAT = data_format
可知,tf對應原本的 channels_last,th對應 channels_first,因此添加下面代碼即可解決問題:
from keras import backend as K
K.set_image_dim_ordering('th')
這樣保證要使用的通道順序和配置的通道順序一致即可。
3,多分類簡易網絡結構(Sequential)
數據描述:
共有500張圖片,分為大巴車、恐龍、大象、鮮花和馬五個類,每個類100張。下載地址:http://pan.baidu.com/s/1nuqlTnN
編號分別以3,4,5,6,7開頭,各為一類。我從其中每類選出20張作為測試,其余80張作為訓練。因此最終訓練圖片400張,測試圖片100張,共5類。如下圖:
(注意,這里需要自己將圖片分為五類,包括訓練集和測試集)
3.1 載入與模型網絡構建
代碼如下:
載入與模型網絡構建
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D
from keras.layers import Activation, Dropout, Flatten, Dense
def built_model():
model = Sequential()
model.add(Conv2D(32, (3, 3), input_shape=(150, 150, 3)))
filter大小為3*3 數量為32個,原始圖像大小3,150 150
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(32, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
this converts ours 3D feature maps to 1D feature vector
model.add(Dense(64))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(5)) # 幾個分類就幾個dense
model.add(Activation('softmax')) # 多分類
3.2 圖像預處理
下面我們開始准備數據,使用 .flow_from_directory() 來從我們的jpgs圖片中直接產生數據和標簽。
其中值得留意的是:
- ImageDataGenerate:用以生成一個 batch 的圖像數據,支持實時數據提升。訓練時該函數會無限生成數據,直到達到規定的epoch次數為止。
- flow_from_directory(directory):以文件夾路徑為參數,生成經過數據提升/歸一化后的數據,在一個無限循環中無限產生batch數據。
def generate_data():
'''
flow_from_directory是計算數據的一些屬性值,之后再訓練階段直接丟進去這些生成器。
通過這個函數來准確數據,可以讓我們的jpgs圖片中直接產生數據和標簽
:return:
'''
train_datagen = ImageDataGenerator(
rescale=1. / 255,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True
)
test_datagen = ImageDataGenerator(rescale=1. / 255)
train_generator = train_datagen.flow_from_directory(
'data/mytrain',
target_size=(150, 150), # all images will be resized to 150*150
batch_size=32,
class_mode='categorical' # 多分類
)
validation_generator = test_datagen.flow_from_directory(
'data/mytest',
target_size=(150, 150),
batch_size=32,
class_mode='categorical' # 多分類
)
return train_generator, validation_generator
3.3 載入與模型網絡構建
代碼如下:(和上面兩分類的沒多少差別)
def built_model():
載入與模型網絡構建
model = Sequential()
model.add(Conv2D(32, (3, 3), input_shape=(150, 150, 3)))
filter大小為3*3 數量為32個,原始圖像大小3,150 150
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(32, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
this converts ours 3D feature maps to 1D feature vector
model.add(Dense(64))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(5)) # 幾個分類就幾個dense
model.add(Activation('softmax')) # 多分類
model.compile(loss='binary_corssentropy',
optimizer='rmsprop',
metrics=['accuracy'])
優化器rmsprop:除學習率可調整外,建議保持優化器的其他默認參數不變
model.compile(loss='categorical_crossentropy',
optimizer='rmsprop',
metrics=['accuracy'])
model.summary()
return model
3.4 訓練
代碼如下:
def train_model(model=None):
if model is None:
model = built_model()
model.fit_generator(
train_generator,
sampels_per_epoch 相當於每個epoch數據量峰值,
每個epoch以經過模型的樣本數達到samples_per_epoch時,記一個epoch結束
samples_per_epoch=2000,
nb_epoch=50,
validation_data=validation_generator,
nb_val_samples=800
)
model.save_weights('first_try_animal.h5')
3.5 部分結果展示和所有源碼
部分結果如下(這里僅展示一個完整的epoch訓練數據):
Epoch 50/50
1/62 [..............................] - ETA: 1:10 - loss: 0.4921 - acc: 0.9062
2/62 [..............................] - ETA: 1:13 - loss: 0.2460 - acc: 0.9531
3/62 [>.............................] - ETA: 1:12 - loss: 0.1640 - acc: 0.9688
4/62 [>.............................] - ETA: 1:02 - loss: 0.1230 - acc: 0.9766
5/62 [=>............................] - ETA: 1:02 - loss: 0.0985 - acc: 0.9812
6/62 [=>............................] - ETA: 1:02 - loss: 0.0821 - acc: 0.9844
7/62 [==>...........................] - ETA: 1:02 - loss: 0.0704 - acc: 0.9866
8/62 [==>...........................] - ETA: 1:00 - loss: 0.0616 - acc: 0.9883
9/62 [===>..........................] - ETA: 59s - loss: 0.0547 - acc: 0.9896
10/62 [===>..........................] - ETA: 59s - loss: 0.0493 - acc: 0.9906
11/62 [====>.........................] - ETA: 59s - loss: 0.0448 - acc: 0.9915
12/62 [====>.........................] - ETA: 58s - loss: 0.0412 - acc: 0.9922
13/62 [=====>........................] - ETA: 57s - loss: 0.0380 - acc: 0.9928
14/62 [=====>........................] - ETA: 55s - loss: 0.0363 - acc: 0.9933
15/62 [======>.......................] - ETA: 54s - loss: 0.0339 - acc: 0.9938
16/62 [======>.......................] - ETA: 53s - loss: 0.0318 - acc: 0.9941
17/62 [=======>......................] - ETA: 51s - loss: 0.0316 - acc: 0.9945
18/62 [=======>......................] - ETA: 50s - loss: 0.0298 - acc: 0.9948
19/62 [========>.....................] - ETA: 49s - loss: 0.0283 - acc: 0.9951
20/62 [========>.....................] - ETA: 48s - loss: 0.0268 - acc: 0.9953
21/62 [=========>....................] - ETA: 47s - loss: 0.0259 - acc: 0.9955
22/62 [=========>....................] - ETA: 46s - loss: 0.0247 - acc: 0.9957
23/62 [==========>...................] - ETA: 45s - loss: 0.0236 - acc: 0.9959
24/62 [==========>...................] - ETA: 44s - loss: 0.0227 - acc: 0.9961
25/62 [===========>..................] - ETA: 42s - loss: 0.0218 - acc: 0.9962
26/62 [===========>..................] - ETA: 42s - loss: 0.0209 - acc: 0.9964
27/62 [============>.................] - ETA: 41s - loss: 0.0202 - acc: 0.9965
28/62 [============>.................] - ETA: 40s - loss: 0.0194 - acc: 0.9967
29/62 [=============>................] - ETA: 39s - loss: 0.0188 - acc: 0.9968
30/62 [=============>................] - ETA: 37s - loss: 0.0181 - acc: 0.9969
31/62 [==============>...............] - ETA: 36s - loss: 0.0176 - acc: 0.9970
32/62 [==============>...............] - ETA: 34s - loss: 0.0170 - acc: 0.9971
33/62 [==============>...............] - ETA: 33s - loss: 0.0165 - acc: 0.9972
34/62 [===============>..............] - ETA: 32s - loss: 0.0160 - acc: 0.9972
35/62 [===============>..............] - ETA: 31s - loss: 0.0156 - acc: 0.9973
36/62 [================>.............] - ETA: 30s - loss: 0.0151 - acc: 0.9974
37/62 [================>.............] - ETA: 29s - loss: 0.0147 - acc: 0.9975
38/62 [=================>............] - ETA: 28s - loss: 0.0146 - acc: 0.9975
39/62 [=================>............] - ETA: 27s - loss: 0.0142 - acc: 0.9976
40/62 [==================>...........] - ETA: 26s - loss: 0.0139 - acc: 0.9977
41/62 [==================>...........] - ETA: 24s - loss: 0.0135 - acc: 0.9977
42/62 [===================>..........] - ETA: 23s - loss: 0.0132 - acc: 0.9978
43/62 [===================>..........] - ETA: 22s - loss: 0.0129 - acc: 0.9978
44/62 [====================>.........] - ETA: 21s - loss: 0.0126 - acc: 0.9979
45/62 [====================>.........] - ETA: 20s - loss: 0.0123 - acc: 0.9979
46/62 [=====================>........] - ETA: 19s - loss: 0.0135 - acc: 0.9973
47/62 [=====================>........] - ETA: 17s - loss: 0.0153 - acc: 0.9967
48/62 [======================>.......] - ETA: 16s - loss: 0.0254 - acc: 0.9961
49/62 [======================>.......] - ETA: 15s - loss: 0.0249 - acc: 0.9962
50/62 [=======================>......] - ETA: 14s - loss: 0.0244 - acc: 0.9962
51/62 [=======================>......] - ETA: 13s - loss: 0.0338 - acc: 0.9957
52/62 [========================>.....] - ETA: 11s - loss: 0.0332 - acc: 0.9958
53/62 [========================>.....] - ETA: 10s - loss: 0.0329 - acc: 0.9959
54/62 [=========================>....] - ETA: 9s - loss: 0.0323 - acc: 0.9959
55/62 [=========================>....] - ETA: 8s - loss: 0.0317 - acc: 0.9960
56/62 [==========================>...] - ETA: 7s - loss: 0.0393 - acc: 0.9950
57/62 [==========================>...] - ETA: 5s - loss: 0.0511 - acc: 0.9940
58/62 [===========================>..] - ETA: 4s - loss: 0.0502 - acc: 0.9941
59/62 [===========================>..] - ETA: 3s - loss: 0.0494 - acc: 0.9942
60/62 [============================>.] - ETA: 2s - loss: 0.0518 - acc: 0.9938
61/62 [============================>.] - ETA: 1s - loss: 0.0535 - acc: 0.9933
62/62 [==============================] - 271s 4s/step - loss: 0.0607 - acc: 0.9929 - val_loss: 0.7166 - val_acc: 0.9300
源碼如下:
載入與模型網絡構建
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D
from keras.layers import Activation, Dropout, Flatten, Dense
from keras.preprocessing.image import ImageDataGenerator
import numpy as np
import os
def built_model():
載入與模型網絡構建
model = Sequential()
model.add(Conv2D(32, (3, 3), input_shape=(150, 150, 3)))
filter大小為3*3 數量為32個,原始圖像大小3,150 150
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(32, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
this converts ours 3D feature maps to 1D feature vector
model.add(Dense(64))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(5)) # 幾個分類就幾個dense
model.add(Activation('softmax')) # 多分類
model.compile(loss='binary_corssentropy',
optimizer='rmsprop',
metrics=['accuracy'])
優化器rmsprop:除學習率可調整外,建議保持優化器的其他默認參數不變
model.compile(loss='categorical_crossentropy',
optimizer='rmsprop',
metrics=['accuracy'])
model.summary()
return model
def generate_data():
'''
flow_from_directory是計算數據的一些屬性值,之后再訓練階段直接丟進去這些生成器。
通過這個函數來准確數據,可以讓我們的jpgs圖片中直接產生數據和標簽
:return:
'''
train_datagen = ImageDataGenerator(
rescale=1. / 255,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True
)
test_datagen = ImageDataGenerator(rescale=1. / 255)
train_generator = train_datagen.flow_from_directory(
'data/mytrain',
target_size=(150, 150), # all images will be resized to 150*150
batch_size=32,
class_mode='categorical' # 多分類
)
validation_generator = test_datagen.flow_from_directory(
'data/mytest',
target_size=(150, 150),
batch_size=32,
class_mode='categorical' # 多分類
)
return train_generator, validation_generator
def train_model(model=None):
if model is None:
model = built_model()
model.fit_generator(
train_generator,
sampels_per_epoch 相當於每個epoch數據量峰值,
每個epoch以經過模型的樣本數達到samples_per_epoch時,記一個epoch結束
samples_per_epoch=2000,
nb_epoch=50,
validation_data=validation_generator,
nb_val_samples=800
)
model.save_weights('first_try_animal.h5')
if name == 'main':
train_generator, validation_generator = generate_data()
train_model()
當loss出現負數,肯定是之前多分類的標簽哪些設置的不對,
注意上面的 steps_per_epoch和validation_steps的值(應該是這樣計算出來的):
model=build_model(input_shape=(IMG_W,IMG_H,IMG_CH)) # 輸入的圖片維度
模型的訓練
model.fit_generator(train_generator, # 數據流
steps_per_epoch=train_samples_num // batch_size,
epochs=epochs,
validation_data=val_generator,
validation_steps=val_samples_num // batch_size)
3.6 畫圖展示和使用模型預測
最后我們可以通過圖直觀的查看訓練過程中的 loss 和 acc ,看看其變化趨勢。下面是代碼:
畫圖,將訓練時的acc和loss都繪制到圖上
import matplotlib.pyplot as plt
def plot_training(history):
plt.figure(12)
plt.subplot(121)
train_acc = history.history['acc']
val_acc = history.history['val_acc']
epochs = range(len(train_acc))
plt.plot(epochs, train_acc, 'b',label='train_acc')
plt.plot(epochs, val_acc, 'r',label='test_acc')
plt.title('Train and Test accuracy')
plt.legend()
plt.subplot(122)
train_loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(len(train_loss))
plt.plot(epochs, train_loss, 'b',label='train_loss')
plt.plot(epochs, val_loss, 'r',label='test_loss')
plt.title('Train and Test loss')
plt.legend()
plt.show()
我們也可以通過訓練好的模型去預測新的樣本。
單張樣本的預測代碼如下:
用訓練好的模型來預測新樣本
from PIL import Image
from keras.preprocessing import image
def predict(model, img_path, target_size):
img=Image.open(img_path) # 加載圖片
if img.size != target_size:
img = img.resize(target_size)
x = image.img_to_array(img)
x *=1./255 # 相當於ImageDataGenerator(rescale=1. / 255)
x = np.expand_dims(x, axis=0) # 調整圖片維度
preds = model.predict(x) # 預測
return preds[0]
批量預測(一個文件夾中的所有文件):
預測一個文件夾中的所有圖片
new_sample_gen=ImageDataGenerator(rescale=1. / 255)
newsample_generator=new_sample_gen.flow_from_directory(
'E:\PyProjects\DataSet\FireAI\DeepLearning',
target_size=(IMG_W, IMG_H),
batch_size=16,
class_mode=None,
shuffle=False)
predicted=model.predict_generator(newsample_generator)
print(predicted)
注意我們上面保存模型是保存的權重,而不是模型,保存模型的代碼如下:
模型的加載,預測
from keras.models import load_model
saved_model=load_model('animal.h5')
predicted=saved_model.predict_generator(newsample_generator)
print(predicted) # saved_model的結果和前面的model結果一致,表面模型正確保存和加載
如果保存的是權重,直接加載,會報錯:
最后,我自己的預測代碼:
用訓練好的模型來預測新的樣本
from keras.preprocessing import image
import cv2
import numpy as np
from keras.models import load_model
def predict(model, img_path, target_size):
img = cv2.imread(img_path)
if img.shape != target_size:
img = cv2.resize(img, target_size)
print(img.shape)
x = image.img_to_array(img)
x *= 1. / 255 # 相當於ImageDataGenerator(rescale=1. / 255)
x = np.expand_dims(x, axis=0) # 調整圖片維度
preds = model.predict(x)
return preds[0]
if name == 'main':
model_path = 'animal.h5'
model = load_model(model_path)
target_size = (150, 150)
img_path = 'data/test/300.jpg'
res = predict(model, img_path, target_size)
print(res)感謝作者分享-http://bjbsair.com/2020-04-07/tech-info/30654.html
Keras基本的使用都已經清楚了,那么這篇主要學習如何使用Keras進行訓練模型,訓練訓練,主要就是“練”,所以多做幾個案例就知道怎么做了。
在本文中,我們將提供一些面向小數據集(幾百張到幾千張圖片)構造高效,實用的圖像分類器的方法。
1,熱身練習——CIFAR10 小圖片分類示例(Sequential式)
示例中CIFAR10采用的是Sequential式來編譯網絡結構。代碼如下:
要訓練模型,首先得知道數據長啥樣
from future import print_function
import keras
from keras.datasets import cifar10
from keras.preprocessing.image import ImageDataGenerator
from keras.models import Sequential
from keras.layers import Dense, Dropout, Activation, Flatten
from keras.layers import Conv2D, MaxPooling2D
batch_size = 32
num_classes = 10
epochs = 100
data_augmentation = True
數據載入
(x_train, y_train), (x_test, y_test) = cifar10.load_data()
多分類標簽生成,我們將其由單個標簽,生成一個熱編碼的形式
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)
網絡結構配置
model = Sequential()
model.add(Conv2D(32, (3, 3), padding='same',
input_shape=x_train.shape[1:])) # (32, 32, 3)
model.add(Activation('relu'))
model.add(Conv2D(32, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Conv2D(64, (3, 3), padding='same'))
model.add(Activation('relu'))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(512))
model.add(Activation('relu'))
model.add(Dropout(num_classes))
model.add(Activation('softmax'))
訓練參數設置
initiate RMSprop optimizer
opt = keras.optimizers.rmsprop(lr=0.0001, decay=1e-6)
Let's train the model using RMSprop
model.compile(loss='categorical_crossentropy',
optimizer=opt,
metrics=['accuracy'])
生成訓練數據
x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
x_train /= 255
x_test /= 255
if not data_augmentation:
print("Not using data augmentation")
model.fit(x_train, y_train,
batch_size=batch_size,
epochs=epochs,
validation_data=(x_test, y_test),
shuffle=True)
else:
print("Using real-time data augmentation")
this will do preprocessing and realtime data augmentation
datagen = ImageDataGenerator(
featurewise_center=False, # set input mean to 0 over the dataset
samplewise_center=False, # set each sample mean to 0
featurewise_std_normalization=False, # divide inputs by std of the dataset
samplewise_std_normalization=False, # divide each input by its std
zca_whitening=False, # apply ZCA whitening
rotation_range=0, # randomly rotate images in the range (degrees, 0 to 180)
width_shift_range=0.1, # randomly shift images horizontally (fraction of total width)
height_shift_range=0.1, # randomly shift images vertically (fraction of total height)
horizontal_flip=True, # randomly flip images
vertical_flip=False) # randomly flip images
Compute quantities required for feature-wise normalization
(std, mean, and principal components if ZCA whitening is applied).
datagen.fit(x_train)
fit訓練
fit the model on batches generated by datagen.flow()
model.fit_generator(datagen.flow(x_train, y_train,
batch_size=batch_size),
epochs=epochs,
validation_data=(x_test, y_test))
截取部分epoch的運行結果:
49056/50000 [============================>.] - ETA: 4s - loss: 0.6400 - acc: 0.7855
49088/50000 [============================>.] - ETA: 4s - loss: 0.6399 - acc: 0.7855
49120/50000 [============================>.] - ETA: 3s - loss: 0.6401 - acc: 0.7855
49152/50000 [============================>.] - ETA: 3s - loss: 0.6399 - acc: 0.7855
49184/50000 [============================>.] - ETA: 3s - loss: 0.6398 - acc: 0.7856
49216/50000 [============================>.] - ETA: 3s - loss: 0.6397 - acc: 0.7856
49248/50000 [============================>.] - ETA: 3s - loss: 0.6395 - acc: 0.7856
49280/50000 [============================>.] - ETA: 3s - loss: 0.6396 - acc: 0.7857
49312/50000 [============================>.] - ETA: 3s - loss: 0.6398 - acc: 0.7856
49344/50000 [============================>.] - ETA: 2s - loss: 0.6404 - acc: 0.7856
49376/50000 [============================>.] - ETA: 2s - loss: 0.6404 - acc: 0.7856
49408/50000 [============================>.] - ETA: 2s - loss: 0.6403 - acc: 0.7856
49440/50000 [============================>.] - ETA: 2s - loss: 0.6404 - acc: 0.7856
49472/50000 [============================>.] - ETA: 2s - loss: 0.6404 - acc: 0.7856
49504/50000 [============================>.] - ETA: 2s - loss: 0.6405 - acc: 0.7855
49536/50000 [============================>.] - ETA: 2s - loss: 0.6406 - acc: 0.7855
49568/50000 [============================>.] - ETA: 1s - loss: 0.6407 - acc: 0.7855
49600/50000 [============================>.] - ETA: 1s - loss: 0.6407 - acc: 0.7854
49632/50000 [============================>.] - ETA: 1s - loss: 0.6410 - acc: 0.7854
49664/50000 [============================>.] - ETA: 1s - loss: 0.6409 - acc: 0.7853
49696/50000 [============================>.] - ETA: 1s - loss: 0.6410 - acc: 0.7853
49728/50000 [============================>.] - ETA: 1s - loss: 0.6412 - acc: 0.7852
49760/50000 [============================>.] - ETA: 1s - loss: 0.6413 - acc: 0.7852
49792/50000 [============================>.] - ETA: 0s - loss: 0.6413 - acc: 0.7852
49824/50000 [============================>.] - ETA: 0s - loss: 0.6413 - acc: 0.7852
49856/50000 [============================>.] - ETA: 0s - loss: 0.6414 - acc: 0.7851
49888/50000 [============================>.] - ETA: 0s - loss: 0.6415 - acc: 0.7851
49920/50000 [============================>.] - ETA: 0s - loss: 0.6415 - acc: 0.7851
49952/50000 [============================>.] - ETA: 0s - loss: 0.6415 - acc: 0.7850
49984/50000 [============================>.] - ETA: 0s - loss: 0.6415 - acc: 0.7850
50000/50000 [==============================] - 228s 5ms/step - loss: 0.6414 - acc: 0.7851 - val_loss: 0.6509 - val_acc: 0.7836
Epoch 55/200
其實跑到第55個epoch,准確率已經達到了 0.785了,后面肯定能達到八九十,我這里就暫停了,讓我電腦歇一歇,跑一天了。其實跑這個就是證明模型沒有問題,而且我的代碼可以運行,僅此而已。
2,針對小數據集的深度學習
本節實驗基於如下配置:
- 2000張訓練圖片構成的數據集,一共有兩個類別,每類1000張
- 安裝有Keras,Scipy,PIL的機器,如果有GPU就更好的了,但是因為我們面對的是小數據集,沒有也可以
- 數據集存放格式如下:
這份數據集來自於Kaggle,元數據集有12500只貓和12500只狗,我們只取了各個類的前500張圖片,另外還從各個類中取了1000張額外圖片用於測試。
下面是數據集的一些示例圖片,圖片的數量非常少,這對於圖像分類來說是個大麻煩。但現實是,很多真實世界圖片獲取是很困難的,我們能得到的樣本數目確實很有限(比如醫學圖像,每張正樣本都意味着一個承受痛苦的病人)對數據科學家而言,我們應該有能夠榨取少量數據的全部價值的能力,而不是簡單的伸手要更多的數據。
在Kaggle的貓狗大戰競賽中,參賽者通過使用現代的深度學習技術達到了98%的正確率,我們只使用了全部數據的8%,因此這個問題對我們來說更難。
經常聽說的一種做法是,深度學習只有在你擁有海量數據的時候才有意義。雖然這種說法並不是完全不對,但卻具有較強的誤導性。當然,深度學習強調從數據中自動學習特征的能力,沒有足夠的訓練樣本,這幾乎是不可能的。尤其是當輸入的數據維度很高(如圖片)時。然而,卷積神經網絡作為深度學習的支柱,被設計為針對“感知”問題最好的模型之一(如圖像分類問題),即使只有很少的數據,網絡也能把特征學的不錯。針對小數據集的神經網絡依然能夠得到合理的結果,並不需要任何手工的特征工程。一言以蔽之,卷積神經網絡大法好!另一方面,深度學習模型天然就具有可重用的特性:比方說,你可以把一個在大規模數據上訓練好的圖像分類或語音識別的模型重用在另一個很不一樣的問題上,而只需要做有限的一點改動。尤其是在計算機視覺領域,許多預訓練的模型現在都被公開下載,並重用在其他問題上以提升在小數據集上的性能。
2.1 數據預處理與數據提升
為了盡量利用我們有限的訓練數據,我們將通過一系列變換堆數據進行提升,這樣我們的模型將看不到任何兩張完全相同的圖片,這有利於我們抑制過擬合,使得模型的泛化能力更好。
在Keras中,這個步驟可以通過keras.preprocessing.image.ImageDataGenerator來實現,也就是圖片預處理生成器,這個類使你可以:
- 在訓練過程中,設置要施行的隨機變換。
- 通過 .flow 或者 .flow_from_directory(directory) 方法實例化一個針對圖像 batch 的生成器,這些生成器可以被用作 Keras模型相關方法的輸入,如 fit_generator,evaluate_generator 和 predict_generator。
datagen = ImageDataGenerator()
datagen.fit(x_train)
生成器初始化 datagen,生成 datagen.fit,計算依賴於數據的變化所需要的統計信息。
最終把數據需要按照每個batch進行划分,這樣就可以送到模型進行訓練了
datagen.flow(x_train, y_train, batch_size=batch_size)
接收numpy數組和標簽為參數,生成經過數據提升或標准化后的batch數據,並在一個無限循環中不斷的返回batch數據。
具體的圖片生成器函數ImageDataGenerator:(他可以用以生成一個 batch的圖像數據,支持實時數據提升,訓練時該函數會無限生成數據,直到達到規定的 epoch次數為止。)
參數意思:
方法有三個,分別是 fit() flow() flow_from_directory() 下面繼續截圖Keras官網的內容:
現在我們看一個例子:
coding:utf-8
'''
使用ImageDataGenerator 來生成圖片,並將其保存在一個臨時文件夾中
下面感受一下數據提升究竟做了什么事情。
'''
import os
from keras.preprocessing.image import ImageDataGenerator, array_to_img, img_to_array, load_img
datagen = ImageDataGenerator(
rotation_range=40, # 是一個0~180的度數,用來指定隨機選擇圖片的角度
width_shift_range=0.2, # 水平方向的隨機移動程度
height_shift_range=0.2, # 豎直方向的隨機移動程度
rescale=1./255, #將在執行其他處理前乘到整個圖像上
shear_range=0.2, # 用來進行剪切變換的程度,參考剪切變換
zoom_range=0.2, # 用來進行隨機的放大
horizontal_flip=True, # 隨機的對圖片進行水平翻轉,此參數用於水平翻轉不影響圖片語義的時候
fill_mode='nearest' # 用來指定當需要進行像素填充,如旋轉,水平和豎直位移的時候
)
pho_path = 'timg.jpg'
img = load_img(pho_path) # this is a PIL image
x = img_to_array(img) # this is a Numpy array with shape (3, 150, 150)
x = x.reshape((1, ) + x.shape) # this is a Numpy array with shape (1, 3, 150, 150)
if not os.path.exists('preview'):
os.mkdir('preview')
i = 0
for batch in datagen.flow(x, batch_size=1,
save_to_dir='preview', save_prefix='durant', save_format='jpg'):
i += 1
if i > 20:
break # otherwise the generator would loop indefitely
這是原圖:
下面是一張圖片被提升以后得到的多個結果:
下面粘貼三個官網的例子:
1,使用.flow() 的例子
2,使用.flow_from_directory(directory)
3,同時變換圖像和 mask
2.2 在小數據集上訓練神經網絡:40行代碼達到80%的准確率
進行圖像分類的正確工具是卷積網絡,所以我們來試試用卷積神經網絡搭建一個初級的模型。因為我們的樣本數很少,所以我們應該對過擬合的問題多加注意。當一個模型從很少的樣本中學習到不能推廣到新數據的模式時,我們稱為出現了過擬合的問題。過擬合發生時,模型試圖使用不相關的特征來進行預測。例如,你有三張伐木工人的照片,有三張水手的照片。六張照片中只有一個伐木工人戴了帽子,如果你認為戴帽子是能將伐木工人與水手區別開的特征,那么此時你就是一個差勁的分類器。
數據提升是對抗過擬合問題的一個武器,但還不夠,因為提升過的數據讓然是高度相關的。對抗過擬合的你應該主要關注的時模型的“熵容量”——模型允許存儲的信息量。能夠存儲更多信息的模型能夠利用更多的特征取得更好的性能,但也有存儲不相關特征的風險。另一方面,只能存儲少量信息的模型會將存儲的特征主要集中在真正相關的特征上,並有更好的泛華性能。
有很多不同的方法來調整模型的“熵容量”,常見的一種選擇是調整模型的參數數目,即模型的層數和每層的規模。另一種方法時對權重進行正則化約束,如L1或L2這種約束會使模型的權重偏向較小的值。
在我們的模型里,我們使用了很小的卷積網絡,只有很少的幾層,每層的濾波器數目也不多。再加上數據提升和Dropout,就差不多了。Dropout通過防止一層看到兩次完全一樣的模式來防止過擬合,相當於也是數據提升的方法。(你可以說Dropout和數據提升都在隨機擾亂數據的相關性)
下面展示的代碼是我們的第一個模型,一個很簡單的3層卷積加上ReLU激活函數,再接max-pooling層,這個結構和Yann LeCun 在1990 年發布的圖像分類器很相似(除了ReLU)
這個實驗的代碼如下:
coding:utf-8
from keras.models import Sequential
from keras.layers import Convolution2D, MaxPooling2D
from keras.layers import Activation, Dropout, Flatten, Dense
from keras.preprocessing.image import ImageDataGenerator
from keras import backend as K
K.set_image_dim_ordering('th')
簡單的三層卷積加上ReLU激活函數,再接一個max-pooling層
model = Sequential()
model.add(Convolution2D(32, 3, 3, input_shape=(3, 150, 150)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Convolution2D(32, 3, 3))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Convolution2D(64, 3, 3))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
the model so far outputs 3D feature maps (height, width, features)
然后我們接了兩個全連接網絡,並以單個神經元和Sigmoid激活結束模型
這種選擇會產生一個二分類的結果,與這種配置項適應,損失函數選擇binary_crossentropy
this converts our 3D feature maps to 1D feature vectors
model.add(Flatten())
添加隱藏層神經元的數量和激活函數
model.add(Dense(64))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(1))
model.add(Activation('sigmoid'))
model.compile(loss='binary_crossentropy',
optimizer='rmsprop',
metrics=['accuracy'])
next read data
使用 .flow_from_directory() 來從我們的jpgs圖片中直接產生數據和標簽
this is the augmentation configuration we will use for training
train_datagen = ImageDataGenerator(
rescale=1./255,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True
)
this is the augmentation configuration we will use for testing only rescaling
test_datagen = ImageDataGenerator(rescale=1./255)
this is a generator that will read pictures found in subfliders
of 'data/train'. and indefinitely generate batches of augmented image data
train_generator = train_datagen.flow_from_directory(
'DogsVSCats/train', # this is the target directory
target_size=(150, 150), # all image will be resize to 150*150
batch_size=32,
class_mode='binary'
) # since we use binary_crossentropy loss,we need binary labels
this is a similar generator, for validation data
validation_generator = test_datagen.flow_from_directory(
'DogsVSCats/valid', # this is the target directory
target_size=(150, 150), # all image will be resize to 150*150
batch_size=32,
class_mode='binary'
)
然后我們可以用這個生成器來訓練網絡了。
model.fit_generator(
train_generator,
samples_per_epoch=2000,
nb_epoch=50,
validation_data=validation_generator,
nb_val_samples=800
)
model.save_weights('first_try.h5') #always save your weights after training or duraing trianing
部分結果如下:
Epoch 1/50
62/62 [==============================] - 66s 1s/step - loss: 0.7043 - acc: 0.5238 - val_loss: 0.6798 - val_acc: 0.5015
Epoch 2/50
62/62 [==============================] - 63s 1s/step - loss: 0.6837 - acc: 0.5762 - val_loss: 0.6481 - val_acc: 0.6837
Epoch 3/50
62/62 [==============================] - 63s 1s/step - loss: 0.6465 - acc: 0.6503 - val_loss: 0.5826 - val_acc: 0.6827
Epoch 4/50
62/62 [==============================] - 63s 1s/step - loss: 0.6077 - acc: 0.6884 - val_loss: 0.5512 - val_acc: 0.7447
Epoch 5/50
62/62 [==============================] - 63s 1s/step - loss: 0.5568 - acc: 0.7088 - val_loss: 0.5127 - val_acc: 0.7357
Epoch 6/50
62/62 [==============================] - 63s 1s/step - loss: 0.5469 - acc: 0.7241 - val_loss: 0.4962 - val_acc: 0.7578
Epoch 7/50
62/62 [==============================] - 63s 1s/step - loss: 0.5236 - acc: 0.7446 - val_loss: 0.4325 - val_acc: 0.8028
Epoch 8/50
62/62 [==============================] - 63s 1s/step - loss: 0.4842 - acc: 0.7751 - val_loss: 0.4710 - val_acc: 0.7758
Epoch 9/50
62/62 [==============================] - 63s 1s/step - loss: 0.4693 - acc: 0.7726 - val_loss: 0.4383 - val_acc: 0.7808
Epoch 10/50
62/62 [==============================] - 63s 1s/step - loss: 0.4545 - acc: 0.7952 - val_loss: 0.3806 - val_acc: 0.8298
Epoch 11/50
62/62 [==============================] - 63s 1s/step - loss: 0.4331 - acc: 0.8031 - val_loss: 0.3781 - val_acc: 0.8248
Epoch 12/50
62/62 [==============================] - 63s 1s/step - loss: 0.4178 - acc: 0.8162 - val_loss: 0.3146 - val_acc: 0.8799
Epoch 13/50
62/62 [==============================] - 63s 1s/step - loss: 0.3926 - acc: 0.8275 - val_loss: 0.3030 - val_acc: 0.8739
Epoch 14/50
62/62 [==============================] - 63s 1s/step - loss: 0.3854 - acc: 0.8295 - val_loss: 0.2835 - val_acc: 0.8929
Epoch 15/50
62/62 [==============================] - 63s 1s/step - loss: 0.3714 - acc: 0.8303 - val_loss: 0.2882 - val_acc: 0.8879
Epoch 16/50
62/62 [==============================] - 63s 1s/step - loss: 0.3596 - acc: 0.8517 - val_loss: 0.3727 - val_acc: 0.8228
Epoch 17/50
62/62 [==============================] - 63s 1s/step - loss: 0.3369 - acc: 0.8568 - val_loss: 0.3638 - val_acc: 0.8328
Epoch 18/50
62/62 [==============================] - 63s 1s/step - loss: 0.3249 - acc: 0.8608 - val_loss: 0.2589 - val_acc: 0.8819
Epoch 19/50
62/62 [==============================] - 63s 1s/step - loss: 0.3348 - acc: 0.8548 - val_loss: 0.2273 - val_acc: 0.9079
Epoch 20/50
62/62 [==============================] - 63s 1s/step - loss: 0.2979 - acc: 0.8754 - val_loss: 0.1737 - val_acc: 0.9389
Epoch 21/50
62/62 [==============================] - 63s 1s/step - loss: 0.2980 - acc: 0.8686 - val_loss: 0.2198 - val_acc: 0.9189
Epoch 22/50
62/62 [==============================] - 63s 1s/step - loss: 0.2789 - acc: 0.8815 - val_loss: 0.2040 - val_acc: 0.9109
Epoch 23/50
62/62 [==============================] - 63s 1s/step - loss: 0.2793 - acc: 0.8891 - val_loss: 0.1388 - val_acc: 0.9479
Epoch 24/50
62/62 [==============================] - 63s 1s/step - loss: 0.2799 - acc: 0.8865 - val_loss: 0.1565 - val_acc: 0.9419
Epoch 25/50
62/62 [==============================] - 63s 1s/step - loss: 0.2513 - acc: 0.8949 - val_loss: 0.1467 - val_acc: 0.9510
Epoch 26/50
62/62 [==============================] - 63s 1s/step - loss: 0.2551 - acc: 0.9029 - val_loss: 0.1281 - val_acc: 0.9520
Epoch 27/50
62/62 [==============================] - 63s 1s/step - loss: 0.2387 - acc: 0.8961 - val_loss: 0.1590 - val_acc: 0.9409
Epoch 28/50
62/62 [==============================] - 63s 1s/step - loss: 0.2449 - acc: 0.9054 - val_loss: 0.1250 - val_acc: 0.9580
Epoch 29/50
62/62 [==============================] - 63s 1s/step - loss: 0.2158 - acc: 0.9218 - val_loss: 0.0881 - val_acc: 0.9780
Epoch 30/50
62/62 [==============================] - 63s 1s/step - loss: 0.2286 - acc: 0.9158 - val_loss: 0.1012 - val_acc: 0.9660
Epoch 31/50
62/62 [==============================] - 63s 1s/step - loss: 0.2017 - acc: 0.9181 - val_loss: 0.1109 - val_acc: 0.9570
Epoch 32/50
62/62 [==============================] - 63s 1s/step - loss: 0.1957 - acc: 0.9213 - val_loss: 0.1160 - val_acc: 0.9560
Epoch 33/50
62/62 [==============================] - 63s 1s/step - loss: 0.2046 - acc: 0.9249 - val_loss: 0.0600 - val_acc: 0.9840
Epoch 34/50
62/62 [==============================] - 63s 1s/step - loss: 0.1967 - acc: 0.9206 - val_loss: 0.0713 - val_acc: 0.9790
Epoch 35/50
62/62 [==============================] - 63s 1s/step - loss: 0.2238 - acc: 0.9153 - val_loss: 0.3123 - val_acc: 0.8929
Epoch 36/50
62/62 [==============================] - 63s 1s/step - loss: 0.1841 - acc: 0.9317 - val_loss: 0.0751 - val_acc: 0.9740
Epoch 37/50
62/62 [==============================] - 63s 1s/step - loss: 0.1890 - acc: 0.9279 - val_loss: 0.1030 - val_acc: 0.9700
這個模型在50個epoch后的准確率為 79%~81%。(但是我的准確率在38個epoch卻達到了驚人的92%,也是恐怖)沒有做模型和超參數的優化。
注意這個准確率的變化可能會比較大,因為准確率本來就是一個變化較高的評估參數,而且我們的訓練樣本比較少,所以比較好的驗證方法就是使用K折交叉驗證,但每輪驗證中我們都要訓練一個模型。
2.3 Keras報錯:ValueError: Negative dimension size caused by subtracting 2 ...
使用Keras時遇到如下錯誤:
1
2
ValueError: Negative dimension size caused by subtracting 2 from 1
for 'block2_pool/MaxPool' (op: 'MaxPool') with input shapes: [?,1,75,128].
解決方法:這個是圖片的通道順序問題。
以128*128的RGB圖像為例 channels_last應該將數據組織為(128, 128, 3),而channels_first將數據組織為(3, 128, 128)。
通過查看函數 set_image_dim_ording():
def set_image_dim_ordering(dim_ordering):
"""Legacy setter for image_data_format
.
Arguments
dim_ordering: string. tf
or th
.
Raises
ValueError: if dim_ordering
is invalid.
"""
global _IMAGE_DATA_FORMAT
if dim_ordering not in {'tf', 'th'}:
raise ValueError('Unknown dim_ordering:', dim_ordering)
if dim_ordering == 'th':
data_format = 'channels_first'
else:
data_format = 'channels_last'
_IMAGE_DATA_FORMAT = data_format
可知,tf對應原本的 channels_last,th對應 channels_first,因此添加下面代碼即可解決問題:
from keras import backend as K
K.set_image_dim_ordering('th')
這樣保證要使用的通道順序和配置的通道順序一致即可。
3,多分類簡易網絡結構(Sequential)
數據描述:
共有500張圖片,分為大巴車、恐龍、大象、鮮花和馬五個類,每個類100張。下載地址:http://pan.baidu.com/s/1nuqlTnN
編號分別以3,4,5,6,7開頭,各為一類。我從其中每類選出20張作為測試,其余80張作為訓練。因此最終訓練圖片400張,測試圖片100張,共5類。如下圖:
(注意,這里需要自己將圖片分為五類,包括訓練集和測試集)
3.1 載入與模型網絡構建
代碼如下:
載入與模型網絡構建
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D
from keras.layers import Activation, Dropout, Flatten, Dense
def built_model():
model = Sequential()
model.add(Conv2D(32, (3, 3), input_shape=(150, 150, 3)))
filter大小為3*3 數量為32個,原始圖像大小3,150 150
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(32, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
this converts ours 3D feature maps to 1D feature vector
model.add(Dense(64))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(5)) # 幾個分類就幾個dense
model.add(Activation('softmax')) # 多分類
3.2 圖像預處理
下面我們開始准備數據,使用 .flow_from_directory() 來從我們的jpgs圖片中直接產生數據和標簽。
其中值得留意的是:
- ImageDataGenerate:用以生成一個 batch 的圖像數據,支持實時數據提升。訓練時該函數會無限生成數據,直到達到規定的epoch次數為止。
- flow_from_directory(directory):以文件夾路徑為參數,生成經過數據提升/歸一化后的數據,在一個無限循環中無限產生batch數據。
def generate_data():
'''
flow_from_directory是計算數據的一些屬性值,之后再訓練階段直接丟進去這些生成器。
通過這個函數來准確數據,可以讓我們的jpgs圖片中直接產生數據和標簽
:return:
'''
train_datagen = ImageDataGenerator(
rescale=1. / 255,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True
)
test_datagen = ImageDataGenerator(rescale=1. / 255)
train_generator = train_datagen.flow_from_directory(
'data/mytrain',
target_size=(150, 150), # all images will be resized to 150*150
batch_size=32,
class_mode='categorical' # 多分類
)
validation_generator = test_datagen.flow_from_directory(
'data/mytest',
target_size=(150, 150),
batch_size=32,
class_mode='categorical' # 多分類
)
return train_generator, validation_generator
3.3 載入與模型網絡構建
代碼如下:(和上面兩分類的沒多少差別)
def built_model():
載入與模型網絡構建
model = Sequential()
model.add(Conv2D(32, (3, 3), input_shape=(150, 150, 3)))
filter大小為3*3 數量為32個,原始圖像大小3,150 150
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(32, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
this converts ours 3D feature maps to 1D feature vector
model.add(Dense(64))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(5)) # 幾個分類就幾個dense
model.add(Activation('softmax')) # 多分類
model.compile(loss='binary_corssentropy',
optimizer='rmsprop',
metrics=['accuracy'])
優化器rmsprop:除學習率可調整外,建議保持優化器的其他默認參數不變
model.compile(loss='categorical_crossentropy',
optimizer='rmsprop',
metrics=['accuracy'])
model.summary()
return model
3.4 訓練
代碼如下:
def train_model(model=None):
if model is None:
model = built_model()
model.fit_generator(
train_generator,
sampels_per_epoch 相當於每個epoch數據量峰值,
每個epoch以經過模型的樣本數達到samples_per_epoch時,記一個epoch結束
samples_per_epoch=2000,
nb_epoch=50,
validation_data=validation_generator,
nb_val_samples=800
)
model.save_weights('first_try_animal.h5')
3.5 部分結果展示和所有源碼
部分結果如下(這里僅展示一個完整的epoch訓練數據):
Epoch 50/50
1/62 [..............................] - ETA: 1:10 - loss: 0.4921 - acc: 0.9062
2/62 [..............................] - ETA: 1:13 - loss: 0.2460 - acc: 0.9531
3/62 [>.............................] - ETA: 1:12 - loss: 0.1640 - acc: 0.9688
4/62 [>.............................] - ETA: 1:02 - loss: 0.1230 - acc: 0.9766
5/62 [=>............................] - ETA: 1:02 - loss: 0.0985 - acc: 0.9812
6/62 [=>............................] - ETA: 1:02 - loss: 0.0821 - acc: 0.9844
7/62 [==>...........................] - ETA: 1:02 - loss: 0.0704 - acc: 0.9866
8/62 [==>...........................] - ETA: 1:00 - loss: 0.0616 - acc: 0.9883
9/62 [===>..........................] - ETA: 59s - loss: 0.0547 - acc: 0.9896
10/62 [===>..........................] - ETA: 59s - loss: 0.0493 - acc: 0.9906
11/62 [====>.........................] - ETA: 59s - loss: 0.0448 - acc: 0.9915
12/62 [====>.........................] - ETA: 58s - loss: 0.0412 - acc: 0.9922
13/62 [=====>........................] - ETA: 57s - loss: 0.0380 - acc: 0.9928
14/62 [=====>........................] - ETA: 55s - loss: 0.0363 - acc: 0.9933
15/62 [======>.......................] - ETA: 54s - loss: 0.0339 - acc: 0.9938
16/62 [======>.......................] - ETA: 53s - loss: 0.0318 - acc: 0.9941
17/62 [=======>......................] - ETA: 51s - loss: 0.0316 - acc: 0.9945
18/62 [=======>......................] - ETA: 50s - loss: 0.0298 - acc: 0.9948
19/62 [========>.....................] - ETA: 49s - loss: 0.0283 - acc: 0.9951
20/62 [========>.....................] - ETA: 48s - loss: 0.0268 - acc: 0.9953
21/62 [=========>....................] - ETA: 47s - loss: 0.0259 - acc: 0.9955
22/62 [=========>....................] - ETA: 46s - loss: 0.0247 - acc: 0.9957
23/62 [==========>...................] - ETA: 45s - loss: 0.0236 - acc: 0.9959
24/62 [==========>...................] - ETA: 44s - loss: 0.0227 - acc: 0.9961
25/62 [===========>..................] - ETA: 42s - loss: 0.0218 - acc: 0.9962
26/62 [===========>..................] - ETA: 42s - loss: 0.0209 - acc: 0.9964
27/62 [============>.................] - ETA: 41s - loss: 0.0202 - acc: 0.9965
28/62 [============>.................] - ETA: 40s - loss: 0.0194 - acc: 0.9967
29/62 [=============>................] - ETA: 39s - loss: 0.0188 - acc: 0.9968
30/62 [=============>................] - ETA: 37s - loss: 0.0181 - acc: 0.9969
31/62 [==============>...............] - ETA: 36s - loss: 0.0176 - acc: 0.9970
32/62 [==============>...............] - ETA: 34s - loss: 0.0170 - acc: 0.9971
33/62 [==============>...............] - ETA: 33s - loss: 0.0165 - acc: 0.9972
34/62 [===============>..............] - ETA: 32s - loss: 0.0160 - acc: 0.9972
35/62 [===============>..............] - ETA: 31s - loss: 0.0156 - acc: 0.9973
36/62 [================>.............] - ETA: 30s - loss: 0.0151 - acc: 0.9974
37/62 [================>.............] - ETA: 29s - loss: 0.0147 - acc: 0.9975
38/62 [=================>............] - ETA: 28s - loss: 0.0146 - acc: 0.9975
39/62 [=================>............] - ETA: 27s - loss: 0.0142 - acc: 0.9976
40/62 [==================>...........] - ETA: 26s - loss: 0.0139 - acc: 0.9977
41/62 [==================>...........] - ETA: 24s - loss: 0.0135 - acc: 0.9977
42/62 [===================>..........] - ETA: 23s - loss: 0.0132 - acc: 0.9978
43/62 [===================>..........] - ETA: 22s - loss: 0.0129 - acc: 0.9978
44/62 [====================>.........] - ETA: 21s - loss: 0.0126 - acc: 0.9979
45/62 [====================>.........] - ETA: 20s - loss: 0.0123 - acc: 0.9979
46/62 [=====================>........] - ETA: 19s - loss: 0.0135 - acc: 0.9973
47/62 [=====================>........] - ETA: 17s - loss: 0.0153 - acc: 0.9967
48/62 [======================>.......] - ETA: 16s - loss: 0.0254 - acc: 0.9961
49/62 [======================>.......] - ETA: 15s - loss: 0.0249 - acc: 0.9962
50/62 [=======================>......] - ETA: 14s - loss: 0.0244 - acc: 0.9962
51/62 [=======================>......] - ETA: 13s - loss: 0.0338 - acc: 0.9957
52/62 [========================>.....] - ETA: 11s - loss: 0.0332 - acc: 0.9958
53/62 [========================>.....] - ETA: 10s - loss: 0.0329 - acc: 0.9959
54/62 [=========================>....] - ETA: 9s - loss: 0.0323 - acc: 0.9959
55/62 [=========================>....] - ETA: 8s - loss: 0.0317 - acc: 0.9960
56/62 [==========================>...] - ETA: 7s - loss: 0.0393 - acc: 0.9950
57/62 [==========================>...] - ETA: 5s - loss: 0.0511 - acc: 0.9940
58/62 [===========================>..] - ETA: 4s - loss: 0.0502 - acc: 0.9941
59/62 [===========================>..] - ETA: 3s - loss: 0.0494 - acc: 0.9942
60/62 [============================>.] - ETA: 2s - loss: 0.0518 - acc: 0.9938
61/62 [============================>.] - ETA: 1s - loss: 0.0535 - acc: 0.9933
62/62 [==============================] - 271s 4s/step - loss: 0.0607 - acc: 0.9929 - val_loss: 0.7166 - val_acc: 0.9300
源碼如下:
載入與模型網絡構建
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D
from keras.layers import Activation, Dropout, Flatten, Dense
from keras.preprocessing.image import ImageDataGenerator
import numpy as np
import os
def built_model():
載入與模型網絡構建
model = Sequential()
model.add(Conv2D(32, (3, 3), input_shape=(150, 150, 3)))
filter大小為3*3 數量為32個,原始圖像大小3,150 150
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(32, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
this converts ours 3D feature maps to 1D feature vector
model.add(Dense(64))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(5)) # 幾個分類就幾個dense
model.add(Activation('softmax')) # 多分類
model.compile(loss='binary_corssentropy',
optimizer='rmsprop',
metrics=['accuracy'])
優化器rmsprop:除學習率可調整外,建議保持優化器的其他默認參數不變
model.compile(loss='categorical_crossentropy',
optimizer='rmsprop',
metrics=['accuracy'])
model.summary()
return model
def generate_data():
'''
flow_from_directory是計算數據的一些屬性值,之后再訓練階段直接丟進去這些生成器。
通過這個函數來准確數據,可以讓我們的jpgs圖片中直接產生數據和標簽
:return:
'''
train_datagen = ImageDataGenerator(
rescale=1. / 255,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True
)
test_datagen = ImageDataGenerator(rescale=1. / 255)
train_generator = train_datagen.flow_from_directory(
'data/mytrain',
target_size=(150, 150), # all images will be resized to 150*150
batch_size=32,
class_mode='categorical' # 多分類
)
validation_generator = test_datagen.flow_from_directory(
'data/mytest',
target_size=(150, 150),
batch_size=32,
class_mode='categorical' # 多分類
)
return train_generator, validation_generator
def train_model(model=None):
if model is None:
model = built_model()
model.fit_generator(
train_generator,
sampels_per_epoch 相當於每個epoch數據量峰值,
每個epoch以經過模型的樣本數達到samples_per_epoch時,記一個epoch結束
samples_per_epoch=2000,
nb_epoch=50,
validation_data=validation_generator,
nb_val_samples=800
)
model.save_weights('first_try_animal.h5')
if name == 'main':
train_generator, validation_generator = generate_data()
train_model()
當loss出現負數,肯定是之前多分類的標簽哪些設置的不對,
注意上面的 steps_per_epoch和validation_steps的值(應該是這樣計算出來的):
model=build_model(input_shape=(IMG_W,IMG_H,IMG_CH)) # 輸入的圖片維度
模型的訓練
model.fit_generator(train_generator, # 數據流
steps_per_epoch=train_samples_num // batch_size,
epochs=epochs,
validation_data=val_generator,
validation_steps=val_samples_num // batch_size)
3.6 畫圖展示和使用模型預測
最后我們可以通過圖直觀的查看訓練過程中的 loss 和 acc ,看看其變化趨勢。下面是代碼:
畫圖,將訓練時的acc和loss都繪制到圖上
import matplotlib.pyplot as plt
def plot_training(history):
plt.figure(12)
plt.subplot(121)
train_acc = history.history['acc']
val_acc = history.history['val_acc']
epochs = range(len(train_acc))
plt.plot(epochs, train_acc, 'b',label='train_acc')
plt.plot(epochs, val_acc, 'r',label='test_acc')
plt.title('Train and Test accuracy')
plt.legend()
plt.subplot(122)
train_loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(len(train_loss))
plt.plot(epochs, train_loss, 'b',label='train_loss')
plt.plot(epochs, val_loss, 'r',label='test_loss')
plt.title('Train and Test loss')
plt.legend()
plt.show()
我們也可以通過訓練好的模型去預測新的樣本。
單張樣本的預測代碼如下:
用訓練好的模型來預測新樣本
from PIL import Image
from keras.preprocessing import image
def predict(model, img_path, target_size):
img=Image.open(img_path) # 加載圖片
if img.size != target_size:
img = img.resize(target_size)
x = image.img_to_array(img)
x *=1./255 # 相當於ImageDataGenerator(rescale=1. / 255)
x = np.expand_dims(x, axis=0) # 調整圖片維度
preds = model.predict(x) # 預測
return preds[0]
批量預測(一個文件夾中的所有文件):
預測一個文件夾中的所有圖片
new_sample_gen=ImageDataGenerator(rescale=1. / 255)
newsample_generator=new_sample_gen.flow_from_directory(
'E:\PyProjects\DataSet\FireAI\DeepLearning',
target_size=(IMG_W, IMG_H),
batch_size=16,
class_mode=None,
shuffle=False)
predicted=model.predict_generator(newsample_generator)
print(predicted)
注意我們上面保存模型是保存的權重,而不是模型,保存模型的代碼如下:
模型的加載,預測
from keras.models import load_model
saved_model=load_model('animal.h5')
predicted=saved_model.predict_generator(newsample_generator)
print(predicted) # saved_model的結果和前面的model結果一致,表面模型正確保存和加載
如果保存的是權重,直接加載,會報錯:
最后,我自己的預測代碼:
用訓練好的模型來預測新的樣本
from keras.preprocessing import image
import cv2
import numpy as np
from keras.models import load_model
def predict(model, img_path, target_size):
img = cv2.imread(img_path)
if img.shape != target_size:
img = cv2.resize(img, target_size)
print(img.shape)
x = image.img_to_array(img)
x *= 1. / 255 # 相當於ImageDataGenerator(rescale=1. / 255)
x = np.expand_dims(x, axis=0) # 調整圖片維度
preds = model.predict(x)
return preds[0]
if name == 'main':
model_path = 'animal.h5'
model = load_model(model_path)
target_size = (150, 150)
img_path = 'data/test/300.jpg'
res = predict(model, img_path, target_size)
print(res)