【图像分类案例】(1) ResNeXt 交通标志四分类,附Tensorflow完整代码
各位同學好,今天和大家分享一下如何使用 Tensorflow 構建 ResNeXt 神經網絡模型,通過案例實戰 ResNeXt 的訓練以及預測過程。每個小節的末尾有網絡、訓練、預測的完整代碼。想要數據集的私聊我就行。
ResNeXt 是 ResNet 的改進版,在 bottleneck卷積塊 結構上進行了較小的改動,其他都和 ResNet 模塊相近,如下圖所示,ResNeXt 比 ResNet 的精度更好。
本節只介紹 ResNeXt 的改進部分,想了解 ResNet 網絡的,可以看我的這篇文章:https://blog.csdn.net/dgvv4/article/details/122396424
1.?ResNeXt 模型結構
1.1 分組卷積
(1)在標準卷積中,輸入有多少個通道,卷積核就有多少個通道。
舉個例子:若輸入的圖像shape為5x5x6,一個卷積核的shape為3x3x6,使用3個卷積核,得到的特征圖shape為3x3x3。參數量 = 5x5x6x3 = 450
(2)在分組卷積中,每個卷積核只處理部分通道。如下圖,紅色的卷積核只處理輸入圖像中紅色的兩個通道,綠色的卷積核只處理輸入圖像中間的兩個綠色的通道,第三個卷積核只處理黃色的兩個通道。此時,每個卷積核有兩個通道,每個卷積核生成一個特征圖。
舉個例子:若輸入的圖像shape為5x5x6,一個分組卷積核的shape為3x3x2,使用3個分組卷積核,得到的特征圖shape為3x3x3。參數量 = 5x5x(6/3)x(3/3)x3 = 5x5x2x1x3 = 150 。可見,分成三組,參數量為原來的三分之一。
因此,分組卷積能夠有效地降低參數量和計算量。
代碼實現:
#(1)分組卷積塊
def group_conv(inputs, filters, stride, num_groups):'''inputs為輸入特征圖filters為每個分組卷積的輸出通道數stride為分組卷積的步長num_groups為分幾組'''# 用來保存每個分組卷積的輸出特征圖groupList = []for i in range(num_groups): # 遍歷每一組# 均勻取出需要卷積的特征圖inputs.shape=[b,h,w,c]x = inputs[:, :, :, i*filters: (i+1)*filters]# 分別對每一組卷積使用3*3卷積x = layers.Conv2D(filters, kernel_size=3, strides=stride, padding='same', use_bias=False)(x)# 將每個分組卷積結果保存起來groupList.append(x)# 將每個分組卷積的輸出特征圖在通道維度上堆疊x = layers.Concatenate()(groupList)x = layers.BatchNormalization()(x) # 批標準化x = layers.Activation('relu')(x) # 激活函數return x
1.2 殘差結構單元
論文中的殘差單元結構圖如下,它們在數學計算上完全等價。
如圖c,首先經過1x1卷積下降通道數 [h,w,256]==>[h,w,128];然后經過3x3分組卷積提取特征;再經過1x1卷積上升通道數 [h,w,128]==>[h,w,256];最后,如果輸入和輸出的shape相同,通過殘差連接輸入和輸出。
如圖b,可以理解為,第一層的32個1x1卷積相當于圖c的第一個1x1卷積;第二三層的將分組卷積的結果在通道上堆疊,就是圖c的3x3分組卷積
代碼實現
#(2)一個殘差單元
def res_block(inputs, out_channel, stride, shortcut, num_groups=32):'''inputs輸入特征圖out_channel最后一個1*1卷積的輸出通道數stride=2下采樣, 圖像長寬減半, 殘差邊對輸入卷積后再連接輸出stride=1基本模塊, size不變, 殘差連接輸入和輸出num_groups代表3*3分組卷積分了幾組shortcut判斷是否要調整通道數'''# 殘差邊if shortcut is False: # 直接使用參加連接輸入和輸出residual = inputselif shortcut is True: # 調整通道數# 1*1卷積調整通道數,使輸入輸出的size和通道數相同residual = layers.Conv2D(out_channel, kernel_size=1, strides=stride, padding='same', use_bias=False)(inputs)# 有BN層就不需要偏置residual = layers.BatchNormalization()(residual)# 1*1卷積,輸出通道數是最后一個1*1卷積層輸出通道數的一半x = layers.Conv2D(filters = out_channel//2, kernel_size=1, strides=1,padding = 'same', use_bias = False)(inputs)x = layers.BatchNormalization()(x)x = layers.Activation('relu')(x)# 3*3分組卷積group_filters = (out_channel//2) // num_groups # 每一組卷積的輸出通道數x = group_conv(x, filters = group_filters, stride = stride, num_groups = num_groups)# 1*1卷積上升通道x = layers.Conv2D(filters = out_channel, kernel_size = 1, strides = 1,padding = 'same', use_bias = False)(x)x = layers.BatchNormalization()(x)# 殘差連接,保證x和殘差邊的shape相同x = layers.Add()([x, residual])x = layers.Activation('relu')(x)return x
1.3 網絡結構
下圖是 ResNet 和 ResNeXt 網絡結構對比圖,接下來就一層一層堆疊網絡就可以了
完整代碼展示:
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers, Model#(1)分組卷積塊
def group_conv(inputs, filters, stride, num_groups):'''inputs為輸入特征圖filters為每個分組卷積的輸出通道數stride為分組卷積的步長num_groups為分幾組'''# 用來保存每個分組卷積的輸出特征圖groupList = []for i in range(num_groups): # 遍歷每一組# 均勻取出需要卷積的特征圖inputs.shape=[b,h,w,c]x = inputs[:, :, :, i*filters: (i+1)*filters]# 分別對每一組卷積使用3*3卷積x = layers.Conv2D(filters, kernel_size=3, strides=stride, padding='same', use_bias=False)(x)# 將每個分組卷積結果保存起來groupList.append(x)# 將每個分組卷積的輸出特征圖在通道維度上堆疊x = layers.Concatenate()(groupList)x = layers.BatchNormalization()(x) # 批標準化x = layers.Activation('relu')(x) # 激活函數return x#(2)一個殘差單元
def res_block(inputs, out_channel, stride, shortcut, num_groups=32):'''inputs輸入特征圖out_channel最后一個1*1卷積的輸出通道數stride=2下采樣, 圖像長寬減半, 殘差邊對輸入卷積后再連接輸出stride=1基本模塊, size不變, 殘差連接輸入和輸出num_groups代表3*3分組卷積分了幾組shortcut判斷是否要調整通道數'''# 殘差邊if shortcut is False: # 直接使用參加連接輸入和輸出residual = inputselif shortcut is True: # 調整通道數# 1*1卷積調整通道數,使輸入輸出的size和通道數相同residual = layers.Conv2D(out_channel, kernel_size=1, strides=stride, padding='same', use_bias=False)(inputs)# 有BN層就不需要偏置residual = layers.BatchNormalization()(residual)# 1*1卷積,輸出通道數是最后一個1*1卷積層輸出通道數的一半x = layers.Conv2D(filters = out_channel//2, kernel_size=1, strides=1,padding = 'same', use_bias = False)(inputs)x = layers.BatchNormalization()(x)x = layers.Activation('relu')(x)# 3*3分組卷積group_filters = (out_channel//2) // num_groups # 每一組卷積的輸出通道數x = group_conv(x, filters = group_filters, stride = stride, num_groups = num_groups)# 1*1卷積上升通道x = layers.Conv2D(filters = out_channel, kernel_size = 1, strides = 1,padding = 'same', use_bias = False)(x)x = layers.BatchNormalization()(x)# 殘差連接,保證x和殘差邊的shape相同x = layers.Add()([x, residual])x = layers.Activation('relu')(x)return x#(3)一個殘差塊
def stage(x, num, out_channel, first_stride):# 第一個殘差單元下采樣步長可能是1也可能是2,第一個殘差單元需要調整殘差邊通道數x = res_block(x, out_channel, stride=first_stride, shortcut=True)# 其他的都是基本模塊strides=1for _ in range(num-1):x = res_block(x, out_channel, stride=1, shortcut=False)# 殘差塊輸出結果return x#(4)網絡骨架
def resnext(input_shape, classes):'''input_shape代表輸入圖像的shapeclasses代表分類類別的數量'''# 構造輸入層inputs = keras.Input(shape=input_shape)# 7*7標準卷積[224,224,3]==>[112,112,64]x = layers.Conv2D(filters=64, kernel_size=7, strides=2,padding='same', use_bias=False)(inputs)x = layers.BatchNormalization()(x)x = layers.Activation('relu')(x)# 最大池化[112,112,64]==>[56,56,64]x = layers.MaxPooling2D(pool_size=(3,3), strides=2, padding='same')(x)# [56,56,64]==>[56,56,256]x = stage(x, num=3, out_channel=256, first_stride=1)# [56,56,256]==>[28,28,512]x = stage(x, num=4, out_channel=512, first_stride=2)# [28,28,512]==>[14,14,1024]x = stage(x, num=6, out_channel=1024, first_stride=2)# [14,14,1024]==>[7,7,2048]x = stage(x, num=3, out_channel=2048, first_stride=2)# [7,7,2048]==>[None,2048]x = layers.GlobalAveragePooling2D()(x)# [None,2048]==>[None,classes]logits = layers.Dense(classes)(x) # 輸出不經過softmax激活函數# 構建模型model = Model(inputs, logits)# 返回模型return model#(5)接收網絡模型
if __name__ == '__main__':model = resnext(input_shape = [224,224,3], # 輸入圖像shapeclasses = 1000) # 分類數model.summary() # 查看網絡架構
通過model.summary()查看網絡參數量
==================================================================================================
Total params: 25,097,128
Trainable params: 25,028,904
Non-trainable params: 68,224
__________________________________________________________________________________________________
2. 模型訓練
我是用的GPU訓練網絡,先將各種包導入進來,并設置GPU內存占用,防止內存爆炸。
from tensorflow.keras.preprocessing.image import ImageDataGenerator # 預處理
import matplotlib.pyplot as plt
import tensorflow as tf
from tensorflow import keras
from ResNeXt import resnext # 網絡模型
import json
import osos.environ['CUDA_DEVICE_ORDER'] = 'PCI_BUS_ID' # 調用GPU訓練
os.environ['CUDA_VISIBLE_DEVICES'] = '0' # 使用當前設備的第一塊GPUgpus = tf.config.experimental.list_physical_devices("GPU")# 設置GPU內存占用,根據網絡模型大小占用相應的內存
if gpus:try:for gpu in gpus:tf.config.experimental.set_memory_growth(gpu, True)except RuntimeError as e:print(e)exit(-1)
2.1 數據集加載
接下來,在文件夾中圖片數據以測試集、驗證集、測試集分類。
函數?tf.keras.preprocessing.image_dataset_from_directory() 構造數據集,
分批次讀取圖片數據,參數 img_size 會對讀進來的圖片resize成指定大小;參數 label_mode 中,'int'代表目標值y是數值類型索引,即0, 1, 2, 3等;'categorical'代表onehot類型,對應正確類別的索引的值為1,如圖像屬于第二類則表示為0,1,0,0,0;'binary'代表二分類。
#(1)加載數據集
def get_data(height, width, batchsz):# 訓練集數據train_ds = keras.preprocessing.image_dataset_from_directory(directory = filepath + 'train', # 訓練集圖片所在文件夾label_mode = 'categorical', # onehot編碼image_size = (height, width), # 輸入圖象的sizebatch_size = batchsz) # 每批次訓練32張圖片# 驗證集數據val_ds = keras.preprocessing.image_dataset_from_directory(directory = filepath + 'val', label_mode = 'categorical', image_size = (height, width), batch_size = batchsz) # 返回劃分好的數據集return train_ds, val_ds# 讀取數據集
train_ds, val_ds = get_data(height, width, batchsz)
2.2 顯示圖像信息
接下來繪圖查看圖像信息,iter()生成迭代器,配合next()每次運行取出訓練集中的一個batch數據。
def plot_show(train_ds):# 生成迭代器,每次取出一個batch的數據sample = next(iter(train_ds)) # sample[0]圖像信息, sample[1]標簽信息# 顯示前5張圖for i in range(5):plt.subplot(1,5,i+1) # 在一塊畫板的子畫板上繪制1行5列plt.imshow(sample[0][i]/255.0) # 圖像的像素值壓縮到0-1plt.xticks([]) # 不顯示xy坐標刻度plt.yticks([])plt.show()# 是否展示圖像信息
if plotShow is True:plot_show(train_ds)
顯示圖像如下:
2.3 數據預處理
使用.map()函數轉換數據集中所有x和y的類型,并將每張圖象的像素值映射到[0,1]之間,打亂訓練集數據的順序.shuffle()
def processing(x,y): # 定義預處理函數x = tf.cast(x, dtype=tf.float32) / 255.0 # 圖片轉換為tensor類型,并歸一化y = tf.cast(y, dtype=tf.int32) # 分類標簽轉換成tensor類型return x,y# 對所有數據預處理
train_ds = train_ds.map(processing).shuffle(10000) # map調用自定義預處理函數, shuffle打亂數據集
val_ds = val_ds.map(processing)
2.4 網絡訓練
在網絡編譯.compile(),指定損失loss采用交叉熵損失。設置參數from_logits=True,由于網絡的輸出層沒有使用softmax函數將輸出的實數轉為概率,參數設置為True時,會自動將logits的實數轉為概率值,再和真實值計算損失,這里的真實值y是經過onehot編碼之后的結果。
#(7)保存權重文件
if not os.path.exists(weights_dir): # 判斷當前文件夾下有沒有一個叫save_weights的文件夾os.makedirs(weights_dir) # 如果沒有就創建一個#(8)模型編譯
opt = optimizers.Adam(learning_rate=learning_rate) # 設置Adam優化器model.compile(optimizer=opt, #學習率loss=keras.losses.CategoricalCrossentropy(from_logits=True), # 交叉熵損失,logits層先經過softmaxmetrics=['accuracy']) #評價指標#(9)定義回調函數,一個列表
# 保存模型參數
callbacks = [keras.callbacks.ModelCheckpoint(filepath = 'save_weights/resnext.h5', # 參數保存的位置save_best_only = True, # 保存最佳參數save_weights_only = True, # 只保存權重文件monitor = 'val_loss')] # 通過驗證集損失判斷是否是最佳參數#(10)模型訓練,history保存訓練信息
history = model.fit(x = train_ds, # 訓練集validation_data = val_ds, # 驗證集epochs = epochs, #迭代30次callbacks = callbacks)
訓練過程中的損失值和準確率如下:
Epoch 1/10
556/556 [==============================] - 247s 370ms/step - loss: 1.3545 - accuracy: 0.5662 - val_loss: 0.1252 - val_accuracy: 0.9664
Epoch 2/10
556/556 [==============================] - 175s 305ms/step - loss: 0.1186 - accuracy: 0.9622 - val_loss: 0.1337 - val_accuracy: 0.9724
Epoch 3/10
556/556 [==============================] - 176s 307ms/step - loss: 0.0499 - accuracy: 0.9859 - val_loss: 3.8282 - val_accuracy: 0.6735
Epoch 4/10
556/556 [==============================] - 176s 305ms/step - loss: 0.0697 - accuracy: 0.9816 - val_loss: 0.0783 - val_accuracy: 0.9796
Epoch 5/10
556/556 [==============================] - 176s 306ms/step - loss: 0.1167 - accuracy: 0.9661 - val_loss: 0.0843 - val_accuracy: 0.9844
Epoch 6/10
556/556 [==============================] - 177s 308ms/step - loss: 0.0703 - accuracy: 0.9841 - val_loss: 0.0096 - val_accuracy: 0.9964
Epoch 7/10
556/556 [==============================] - 176s 306ms/step - loss: 0.0267 - accuracy: 0.9920 - val_loss: 0.0295 - val_accuracy: 0.9940
Epoch 8/10
556/556 [==============================] - 176s 306ms/step - loss: 0.0339 - accuracy: 0.9925 - val_loss: 0.0870 - val_accuracy: 0.9712
Epoch 9/10
556/556 [==============================] - 174s 302ms/step - loss: 0.0622 - accuracy: 0.9851 - val_loss: 0.0588 - val_accuracy: 0.9904
Epoch 10/10
556/556 [==============================] - 176s 306ms/step - loss: 0.0384 - accuracy: 0.9889 - val_loss: 3.3135 - val_accuracy: 0.6591
2.5 繪制訓練曲線
history 中保存了本輪訓練的信息,由于沒有使用預訓練權重,模型的訓練損失和準確率波動比較大,但準確率還是可以的。并且訓練時設置了回調函數callbacks,只保存驗證集損失最小時的權重參數。
#(11)獲取訓練信息
history_dict = history.history # 獲取訓練的數據字典
train_loss = history_dict['loss'] # 訓練集損失
train_accuracy = history_dict['accuracy'] # 訓練集準確率
val_loss = history_dict['val_loss'] # 驗證集損失
val_accuracy = history_dict['val_accuracy'] # 驗證集準確率#(12)繪制訓練損失和驗證損失
plt.figure()
plt.plot(range(epochs), train_loss, label='train_loss') # 訓練集損失
plt.plot(range(epochs), val_loss, label='val_loss') # 驗證集損失
plt.legend() # 顯示標簽
plt.xlabel('epochs')
plt.ylabel('loss')#(13)繪制訓練集和驗證集準確率
plt.figure()
plt.plot(range(epochs), train_accuracy, label='train_accuracy') # 訓練集準確率
plt.plot(range(epochs), val_accuracy, label='val_accuracy') # 驗證集準確率
plt.legend()
plt.xlabel('epochs')
plt.ylabel('accuracy')
繪制損失曲線和準確率曲線
2.6 訓練過程完整代碼
訓練階段一定要注意 batch_size 的大小,batch_size 設置的越大,越容易導致顯存爆炸,要改的話最好設置2的n次方
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import optimizers
from ResNeXt import resnext # 導入模型
import os
import matplotlib.pyplot as pltos.environ['CUDA_DEVICE_ORDER'] = 'PCI_BUS_ID' # 調用GPU訓練
os.environ['CUDA_VISIBLE_DEVICES'] = '0' # 使用當前設備的第一塊GPUgpus = tf.config.experimental.list_physical_devices("GPU")# 設置GPU內存占用,根據網絡模型大小占用相應的內存
if gpus:try:for gpu in gpus:tf.config.experimental.set_memory_growth(gpu, True)except RuntimeError as e:print(e)exit(-1)# ------------------------------------- #
# 加載數據集,batchsz太大會導致顯存爆炸
# ------------------------------------- #
filepath = 'D:/deeplearning/test/數據集/交通標志/new_data/' # 數據集所在文件夾
height = 224 # 輸入圖象的高
width = 224 # 輸入圖象的寬
batchsz = 8 # 每個batch處理32張圖片
checkData = False # 查看數據集劃分的信息
plotShow = True # 繪制圖像
checkDataAgain = False # 預處理之后是否再次查看數據集信息
# ------------------------------------- #
# 網絡模型結構
# ------------------------------------- #
input_shape = (224,224,3) # 輸入圖象的shape
classes = 4 # 圖像分類的類別數
checkNet = False # 是否查看網絡架構
# ------------------------------------- #
# 網絡訓練
# ------------------------------------- #
weights_dir = 'save_weights' # 權重文件保存的文件夾路徑
learning_rate = 0.0005 # adam優化器的學習率
epochs = 10 # 訓練迭代次數#(1)加載數據集
def get_data(height, width, batchsz):# 訓練集數據train_ds = keras.preprocessing.image_dataset_from_directory(directory = filepath + 'train', # 訓練集圖片所在文件夾label_mode = 'categorical', # onehot編碼image_size = (height, width), # 輸入圖象的sizebatch_size = batchsz) # 每批次訓練32張圖片# 驗證集數據val_ds = keras.preprocessing.image_dataset_from_directory(directory = filepath + 'val', label_mode = 'categorical', image_size = (height, width), batch_size = batchsz) # 返回劃分好的數據集return train_ds, val_ds# 讀取數據集
train_ds, val_ds = get_data(height, width, batchsz) #(2)查看數據集信息
def check_data(train_ds): # 傳入訓練集數據集# 查看數據集有幾個分類類別class_names = train_ds.class_namesprint('classNames:', class_names)# 查看數據集的shape, x代表圖片數據, y代表分類類別數據sample = next(iter(train_ds)) # 生成迭代器,每次取出一個batch的數據print('x_batch.shape:', sample[0].shape, 'y_batch.shape:', sample[1].shape)print('前五個目標值:', sample[1][:5])# 是否查看數據集信息
if checkData is True:check_data(train_ds)#(3)查看圖像
def plot_show(train_ds):# 生成迭代器,每次取出一個batch的數據sample = next(iter(train_ds)) # sample[0]圖像信息, sample[1]標簽信息# 顯示前5張圖for i in range(5):plt.subplot(1,5,i+1) # 在一塊畫板的子畫板上繪制1行5列plt.imshow(sample[0][i]) # 圖像的像素值壓縮到0-1plt.xticks([]) # 不顯示xy坐標刻度plt.yticks([])plt.show()# 是否展示圖像信息
if plotShow is True:plot_show(train_ds)#(4)數據預處理
def processing(x,y): # 定義預處理函數x = tf.cast(x, dtype=tf.float32) / 255.0 # 圖片轉換為tensor類型,并歸一化y = tf.cast(y, dtype=tf.int32) # 分類標簽轉換成tensor類型return x,y# 對所有數據預處理
train_ds = train_ds.map(processing).shuffle(10000) # map調用自定義預處理函數, shuffle打亂數據集
val_ds = val_ds.map(processing)#(5)查看預處理后的數據是否正確
def check_data_again(train_ds): # 傳入訓練集數據集sample = next(iter(train_ds)) # 生成迭代器,每次取出一個batch的數據print('-------after preprocessing-------')print('x_batch.shape:', sample[0].shape, 'y_batch.shape:', sample[1].shape)print('前五個目標值:', sample[1][:5])# 是否查看數據集信息
if checkDataAgain is True:check_data_again(train_ds)#(6)導入網絡模型
model = resnext(input_shape=input_shape, # 網絡的輸入圖像的sizeclasses=classes) # 分類數# 查看網絡構架
if checkNet is True:model.summary()#(7)保存權重文件
if not os.path.exists(weights_dir): # 判斷當前文件夾下有沒有一個叫save_weights的文件夾os.makedirs(weights_dir) # 如果沒有就創建一個#(8)模型編譯
opt = optimizers.Adam(learning_rate=learning_rate) # 設置Adam優化器model.compile(optimizer=opt, #學習率loss=keras.losses.CategoricalCrossentropy(from_logits=True), # 交叉熵損失,logits層先經過softmaxmetrics=['accuracy']) #評價指標#(9)定義回調函數,一個列表
# 保存模型參數
callbacks = [keras.callbacks.ModelCheckpoint(filepath = 'save_weights/resnext.h5', # 參數保存的位置save_best_only = True, # 保存最佳參數save_weights_only = True, # 只保存權重文件monitor = 'val_loss')] # 通過驗證集損失判斷是否是最佳參數#(10)模型訓練,history保存訓練信息
history = model.fit(x = train_ds, # 訓練集validation_data = val_ds, # 驗證集epochs = epochs, #迭代30次callbacks = callbacks) #(11)獲取訓練信息
history_dict = history.history # 獲取訓練的數據字典
train_loss = history_dict['loss'] # 訓練集損失
train_accuracy = history_dict['accuracy'] # 訓練集準確率
val_loss = history_dict['val_loss'] # 驗證集損失
val_accuracy = history_dict['val_accuracy'] # 驗證集準確率#(12)繪制訓練損失和驗證損失
plt.figure()
plt.plot(range(epochs), train_loss, label='train_loss') # 訓練集損失
plt.plot(range(epochs), val_loss, label='val_loss') # 驗證集損失
plt.legend() # 顯示標簽
plt.xlabel('epochs')
plt.ylabel('loss')#(13)繪制訓練集和驗證集準確率
plt.figure()
plt.plot(range(epochs), train_accuracy, label='train_accuracy') # 訓練集準確率
plt.plot(range(epochs), val_accuracy, label='val_accuracy') # 驗證集準確率
plt.legend()
plt.xlabel('epochs')
plt.ylabel('accuracy')
3. 預測階段
以對整個測試集的圖片預測為例,test_ds 存放測試集的圖片和類別標簽,對測試集進行和訓練集相同的預處理方法,將像素值映射到0-1之間。
model.predict(img) 返回的是每張圖片屬于每個類別的概率,需要找到概率最大值所對應的索引 np.argmax(result),該索引對應的分類名稱就是最終預測結果。
?打印前10組預測結果
真實值: ['forbiden', 'forbiden', 'slow', 'forbiden', 'goahead', 'slow', 'goahead', 'slow', 'slow', 'forbiden']
預測值: ['forbiden', 'forbiden', 'slow', 'forbiden', 'goahead', 'slow', 'goahead', 'slow', 'slow', 'forbiden']
生成真實值和預測值的對比熱力圖可以觀察整個測試集的預測情況
完整代碼如下:
import tensorflow as tf
from tensorflow import keras
from ResNeXt import resnext
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt# 報錯解決:NotFoundError: No algorithm worked! when using Conv2D
from tensorflow.compat.v1 import ConfigProto
from tensorflow.compat.v1 import InteractiveSession
config = ConfigProto()
config.gpu_options.allow_growth = True
session = InteractiveSession(config=config)# ------------------------------------ #
# 預測參數設置
# ------------------------------------ #
im_height = 224 # 輸入圖像的高
im_width = 224 # 輸入圖像的高
# 分類名稱
class_names = ['forbiden', 'warning', 'goahead', 'slow']
# 權重路徑
weight_dir = 'save_weights/resnext.h5'# ------------------------------------ #
# 單張圖片預測
# ------------------------------------ #
# 是否只預測一張圖
single_pic = False
# 圖像所在文件夾的路徑
single_filepath = 'D:/deeplearning/test/數據集/交通標志/new_data/test/禁令標志/'
# 指定某張圖片
picture = single_filepath + '010_0001.png'# ------------------------------------ #
# 對測試集圖片預測
# ------------------------------------ #
test_pack = True
# 驗證集文件夾路徑
test_filepath = 'D:/deeplearning/test/數據集/交通標志/new_data/test/'#(1)載入模型
model = resnext(input_shape=[224,224,3], classes=4) # 模型的輸入shape和輸出分類數
print('model is loaded')#(2)載入權重.h文件
model.load_weights(weight_dir)
print('weights is loaded')#(3)只對單張圖像預測
if single_pic is True:# 加載圖片img = Image.open(picture)# 改變圖片sizeimg = img.resize((im_height, im_width))# 展示圖像plt.figure()plt.imshow(img)plt.xticks([])plt.yticks([])# 圖像像素值歸一化處理img = np.array(img) / 255.0# 輸入網絡的要求,給圖像增加一個batch維度, [h,w,c]==>[b,h,w,c]img = np.expand_dims(img, axis=0)# 預測圖片,返回結果包含batch維度[b,n]result = model.predict(img)# 轉換成一維,擠壓掉batch維度result = np.squeeze(result)# 找到概率最大值對應的索引predict_class = np.argmax(result)# 打印預測類別及概率print('class:', class_names[predict_class], 'prob:', result[predict_class])plt.title(f'{class_names[predict_class]}')plt.show()#(4)對測試集圖像預測
if test_pack is True:# 載入測試集test_ds = keras.preprocessing.image_dataset_from_directory(directory = test_filepath, label_mode = 'int', # 不經過ont編碼, 1、2、3、4、、、 image_size = (im_height, im_width), # 測試集的圖像resizebatch_size = 32) # 每批次32張圖# 測試機預處理#(2)數據預處理def processing(image, label): image = tf.cast(image, tf.float32) / 255.0 #[0,1]之間label = tf.cast(label, tf.int32) # 修改數據類型return (image, label)test_ds = test_ds.map(processing) # 預處理test_true = [] # 存放真實值test_pred = [] # 存放預測值# 遍歷測試集所有的batchfor imgs, labels in test_ds:# 每次每次取出一個batch的一張圖像和一個標簽for img, label in zip(imgs, labels):# 網絡輸入的要求,給圖像增加一個維度[h,w,c]==>[b,h,w,c]image_array = tf.expand_dims(img, axis=0)# 預測某一張圖片,返回圖片屬于許多類別的概率prediction = model.predict(image_array)# 找到預測概率最大的索引對應的類別test_pred.append(class_names[np.argmax(prediction)])# label是真實標簽索引test_true.append(class_names[label])# 展示結果print('真實值: ', test_true[:10])print('預測值: ', test_pred[:10])# 繪制混淆矩陣from sklearn.metrics import confusion_matriximport seaborn as snsimport pandas as pdplt.rcParams['font.sans-serif'] = ['SimSun'] #宋體plt.rcParams['font.size'] = 15 #設置字體大小# 生成混淆矩陣conf_numpy = confusion_matrix(test_true, test_pred)# 轉換成DataFrame表格類型,設置行列標簽conf_df = pd.DataFrame(conf_numpy, index=class_names, columns=class_names)# 創建繪圖區plt.figure(figsize=(8,7))# 生成熱力圖sns.heatmap(conf_df, annot=True, fmt="d", cmap="BuPu")# 設置標簽plt.title('Confusion_Matrix')plt.xlabel('Predict')plt.ylabel('True')
總結
以上是生活随笔為你收集整理的【图像分类案例】(1) ResNeXt 交通标志四分类,附Tensorflow完整代码的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【神经网络】(18) Efficient
- 下一篇: 【图像分类案例】(2) DenseNet