【神经网络】(3) 卷积神经网络(CNN),案例:动物三分类,附python完整代码
各位同學(xué)好,今天和大家分享一下TensorFlow2.0深度學(xué)習(xí)中卷積神經(jīng)網(wǎng)絡(luò)的案例。現(xiàn)在有貓、狗、熊貓圖片一千張,構(gòu)建卷積神經(jīng)網(wǎng)絡(luò)實(shí)現(xiàn)圖像的分類預(yù)測。
1. 數(shù)據(jù)加載
將訓(xùn)練測試數(shù)據(jù)劃分好后放在同一個文件目錄下,使用tf.keras.preprocessing.image_dataset_from_directory()函數(shù)構(gòu)造數(shù)據(jù)集。函數(shù)的具體用法見:tf.keras.preprocessing.image_dataset_from_directory_自在獨(dú)行的博客-CSDN博客_image_dataset_from_directory
對訓(xùn)練數(shù)據(jù)和驗(yàn)證數(shù)據(jù)進(jìn)行one-hot編碼,便于計(jì)算損失,讀入圖像時統(tǒng)一圖片大小size為128*128。batch為64,每次迭代從中取64個樣本。class_names中保存的是根據(jù)文件夾名稱生成的標(biāo)簽。
# 三分類,卷積神經(jīng)網(wǎng)絡(luò)
import numpy as np
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers,optimizers,datasets,Sequential
import os # 設(shè)置一下輸出框打印的內(nèi)容
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' # '2'輸出欄只打印error信息,其他亂七八糟的信息不打印#(1)數(shù)據(jù)獲取
# 加載訓(xùn)練集數(shù)據(jù)
filepath1 = 'C:/Users/admin/.../train'
train_ds = tf.keras.preprocessing.image_dataset_from_directory(filepath1,label_mode='categorical', # "int", "categorical"表示onehot, "binary", or Noneseed=123,image_size=(128, 128), # resize圖片大小batch_size=64)# 加載驗(yàn)證集數(shù)據(jù)
filepath2 = 'C:/Users/admin/.../new_data/val'
val_ds = tf.keras.preprocessing.image_dataset_from_directory(filepath2,label_mode='categorical',seed=123,image_size=(128, 128),batch_size=64)# 加載測試集數(shù)據(jù)
filepath3 = 'C:/Users/.../new_data/test'
test_ds = tf.keras.preprocessing.image_dataset_from_directory(filepath3,label_mode='int',seed=123,image_size=(128, 128),batch_size=64) # 類別名稱
class_names = train_ds.class_names
print('類別有:',class_names)
# 類別有: ['cats', 'dogs', 'panda']
2. 數(shù)據(jù)預(yù)處理
使用.map()對dataset中的數(shù)據(jù)進(jìn)行processing函數(shù)中的操作,對每個點(diǎn)的像素值從[0,255]變成[-1,1],.shuffle()對數(shù)據(jù)集重新洗牌打散,但不改變x和y之間的對應(yīng)關(guān)系。
train_ds.take(1) 是指從訓(xùn)練集數(shù)據(jù)集中取出1個batch的數(shù)據(jù),返回值img存放圖像數(shù)據(jù),label存放圖像的標(biāo)簽。
#(2)數(shù)據(jù)預(yù)處理
def processing(image, label): image = 2 * tf.cast(image, tf.float32) / 255.0 - 1 #[-1,1]之間label = tf.cast(label, tf.int32) # 修改數(shù)據(jù)類型return (image, label)train_ds = train_ds.map(processing).shuffle(10000) #洗牌
val_ds = val_ds.map(processing).shuffle(10000)
test_ds = test_ds.map(processing).shuffle(10000)#(2)數(shù)據(jù)檢查
for img, label in train_ds.take(1): # 取出一個batch的數(shù)據(jù),一個batch有64個樣本print('img.shape:', img.shape) # img.shape: (64, 128, 128, 3)print('label.shape:', label.shape) # label.shape: (64, 3)# 數(shù)據(jù)集展示
import matplotlib.pyplot as plt
for img,label in train_ds.take(1): #取一個batchfor i in range(15):plt.subplot(3,5,i+1)plt.imshow(img[i]) # 每張圖像的shape為(4, 256, 256, 3) plt.xticks([]) # 不顯示xy軸坐標(biāo)刻度plt.yticks([])
plt.show()
預(yù)處理后的圖像展示結(jié)果如下:
3. 網(wǎng)絡(luò)構(gòu)建
這里構(gòu)造一個6層的神經(jīng)網(wǎng)絡(luò),使用Sequential()容器堆疊網(wǎng)絡(luò)各層,layers.Conv2D()構(gòu)造卷積層,卷積核size為3*3。layers.MaxPool2D()構(gòu)造池化層,采用最大池化方法。指定padding='same'填充圖像,在傳播過程中保證生成的特征圖的size不變,只改變其channel。指定layers.Dropout(0.2)每次迭代該層每個神經(jīng)元都有20%的概率被殺死,防止網(wǎng)絡(luò)出現(xiàn)過擬合現(xiàn)象。
在卷積池化層和全連接層之間需要指定一個Flatten層layers.Flatten(),輸入至全連接層的圖像需要是一個二維tensor,假設(shè)卷積池化層輸出的shape為[b,1,1,64],那么傳入全連接層的shape需要w是[b,64]
#(3)網(wǎng)絡(luò)構(gòu)建
# ==1== 卷積和池化層,2次卷積1次池化
network = Sequential()
# unit1
network.add(layers.Conv2D(32, kernel_size=[3,3], strides=1, padding='same', activation=tf.nn.relu))
network.add(layers.Conv2D(32, kernel_size=[3,3], strides=1, padding='same', activation=tf.nn.relu))
network.add(layers.MaxPool2D(pool_size=[2,2], strides=2, padding='same'))
# unit2
network.add(layers.Conv2D(64, kernel_size=[3,3], strides=1, padding='same', activation=tf.nn.relu))
network.add(layers.Conv2D(64, kernel_size=[3,3], strides=1, padding='same', activation=tf.nn.relu))
network.add(layers.MaxPool2D(pool_size=[2,2], strides=2, padding='same'))
# dropout層
network.add(layers.Dropout(0.2)) #每個神經(jīng)元都有0.2的概率被殺死# ==2== Flatten層,連接卷積池化層和全連接層
network.add(layers.Flatten())# ==3== 全連接層
network.add(layers.Dense(128, activation=tf.nn.relu))
network.add(layers.Dense(3)) # 輸出層logits層# ==4== 指定輸入層
network.build(input_shape=[None, 128, 128, 3])# ==5== 查看網(wǎng)絡(luò)結(jié)構(gòu)
network.summary()
網(wǎng)絡(luò)結(jié)構(gòu)如下,param代表該層網(wǎng)絡(luò)擁有的參數(shù)個數(shù)
Model: "sequential"
_________________________________________________________________Layer (type) Output Shape Param #
=================================================================conv2d_1 (Conv2D) (None, 128, 128, 32) 896 conv2d_2 (Conv2D) (None, 128, 128, 32) 9248 max_pooling2d_1 (MaxPoolin (None, 64, 64, 32) 0 g2D) conv2d_3 (Conv2D) (None, 64, 64, 64) 18496 conv2d_4 (Conv2D) (None, 64, 64, 64) 36928 max_pooling2d_2 (MaxPoolin (None, 32, 32, 64) 0 g2D) dropout_1 (Dropout) (None, 32, 32, 64) 0 flatten_1 (Flatten) (None, 65536) 0 dense_1 (Dense) (None, 128) 8388736 dense_2 (Dense) (None, 3) 387 =================================================================
Total params: 8,454,691
Trainable params: 8,454,691
Non-trainable params: 0
_________________________________________________________________
4. 網(wǎng)絡(luò)配置
采用學(xué)習(xí)率指數(shù)衰減的方法方法,tf.keras.optimizers.schedules.ExponentialDecay(),起初梯度變化大一點(diǎn)能更快接近目標(biāo),后續(xù)梯度變化不斷減小,越來越逼近最優(yōu)點(diǎn)。設(shè)置早停策略,因?yàn)樯窠?jīng)網(wǎng)絡(luò)在不斷迭代的過程中,準(zhǔn)確率不會一直在上升,如果找到某一極值點(diǎn),且后續(xù)多次迭代過程中,網(wǎng)絡(luò)效果沒有變的更優(yōu)的跡象,就使用之前的極值點(diǎn)的結(jié)果作為最優(yōu)解。
#(4)網(wǎng)絡(luò)配置
# 設(shè)置動態(tài)學(xué)習(xí)率指數(shù)衰減
exponential_decay = tf.keras.optimizers.schedules.ExponentialDecay(initial_learning_rate=0.001, #初始學(xué)習(xí)率decay_steps=2, # 衰減步長decay_rate=0.96) # 衰減率# 編譯
network.compile(optimizer=optimizers.Adam(learning_rate=exponential_decay), loss=tf.losses.CategoricalCrossentropy(from_logits=True), # 交叉熵?fù)p失metrics=['accuracy']) # 準(zhǔn)確率指標(biāo)# 早停策略
early_stopping = keras.callbacks.EarlyStopping(monitor = 'val_acc', # 驗(yàn)證集的準(zhǔn)確率作為指標(biāo)patience = 10, # 最多忍受多少個次循環(huán)沒有改進(jìn)restore_best_weights = True) # 發(fā)生早停時,自動尋找最優(yōu)的monitor參數(shù)
5. 模型訓(xùn)練
#(5)網(wǎng)絡(luò)訓(xùn)練
# 指定訓(xùn)練集、驗(yàn)證集、迭代次數(shù)
# 訓(xùn)練目標(biāo)和驗(yàn)證目標(biāo)需要時one_hot編碼后的
model = network.fit(train_ds, # 訓(xùn)練集validation_data=val_ds, # 驗(yàn)證集 epochs=10, # 迭代多少次callbacks= early_stopping, # 回調(diào)函數(shù),在訓(xùn)練過程中的適當(dāng)時機(jī)被調(diào)用shuffle = True, # 每輪迭代之前洗牌verbose = 1 # 0為不在標(biāo)準(zhǔn)輸出流輸出日志信息,1:顯示進(jìn)度條,2:每個epoch輸出一行記錄)
由于時間關(guān)系,這里就簡單循環(huán)10次,效果如下
Epoch 1/10
33/33 [==============================] - ETA: 0s - loss: 1.1605 - accuracy: 0.4527 WARNING:tensorflow:Early stopping conditioned on metric `val_acc` which is not available. Available metrics are: loss,accuracy,val_loss,val_accuracy
33/33 [==============================] - 75s 2s/step - loss: 1.1605 - accuracy: 0.4527 - val_loss: 0.8374 - val_accuracy: 0.5796
#.................#
#.................#
Epoch 10/10
33/33 [==============================] - ETA: 0s - loss: 0.6362 - accuracy: 0.6985 WARNING:tensorflow:Early stopping conditioned on metric `val_acc` which is not available. Available metrics are: loss,accuracy,val_loss,val_accuracy
33/33 [==============================] - 96s 3s/step - loss: 0.6362 - accuracy: 0.6985 - val_loss: 0.6821 - val_accuracy: 0.6415
6. 模型評估
比較網(wǎng)絡(luò)訓(xùn)練集和驗(yàn)證集上的準(zhǔn)確率和損失,繪圖比較
#(6)模型評估
# ==1== 計(jì)算準(zhǔn)確率
train_acc = model.history['accuracy']
val_acc = model.history['val_accuracy']# ==2== 損失
train_loss = model.history['loss']
val_loss = model.history['val_loss']# ==3== 曲線圖
epochs_range = range(len(train_acc)) # 橫坐標(biāo),網(wǎng)絡(luò)循環(huán)了幾次# 準(zhǔn)確率曲線
plt.figure(figsize=(10,5))
plt.subplot(1,2,1)
plt.plot(epochs_range, train_acc, label='Training_acc')
plt.plot(epochs_range, val_acc, label='validation_acc')
plt.legend()
plt.title('Accuracy')# 損失曲線
plt.subplot(1,2,2)
plt.plot(epochs_range, train_loss, label='Training_loss')
plt.plot(epochs_range, val_loss, label='validation_loss')
plt.legend()
plt.title('Loss')
如圖所示訓(xùn)練集和驗(yàn)證集的損失都逐漸下降,隨著迭代次數(shù)的增加,效果會更好,但要防止過擬合的現(xiàn)象出現(xiàn)。
7. 預(yù)測
采用測試集中的圖像數(shù)據(jù)對網(wǎng)絡(luò)進(jìn)行預(yù)測,network.predict()得到輸入圖像分別屬于三個分類的數(shù)值,返回numpy類型,使用np.argmax()找到最大值的索引,該索引對應(yīng)的class_names標(biāo)簽就是預(yù)測得到的該圖像所屬的分類。
#(7)預(yù)測
test_pred = []
test_target = []for images, targets in test_ds: #取一個batch的測試集生成混淆矩陣for image, label in zip(images, targets): # 每次從batch中取出一組# 需要給圖片增加一個維度img_array = tf.expand_dims(image, axis=0) # 使用模型預(yù)測圖片中的動物prediction = network.predict(img_array)# 預(yù)測結(jié)果是預(yù)測值最大值索引對應(yīng)的位置test_pred.append(class_names[np.argmax(prediction)])# 保存真實(shí)值的標(biāo)簽test_target.append(class_names[label]) # label沒有做onehot編碼print('測試結(jié)果:',test_pred[:10])
print('真實(shí)結(jié)果:',test_target[:10])#(8)混淆矩陣
from sklearn.metrics import confusion_matrix
import seaborn as sns
import pandas as pd
plt.rcParams['font.sans-serif'] = ['SimSun'] #宋體
plt.rcParams['font.size'] = 15 #設(shè)置字體大小# 生成混淆矩陣
conf_numpy = confusion_matrix(test_target, test_pred)
# 將矩陣轉(zhuǎn)化為 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('混淆矩陣')
plt.ylabel('真實(shí)值')
plt.xlabel('預(yù)測值')
輸出的前10張圖片的預(yù)測結(jié)果如下:
測試結(jié)果: ['panda', 'dogs', 'cats', 'cats', 'panda', 'panda', 'panda', 'panda', 'dogs', 'panda']
真實(shí)結(jié)果: ['panda', 'dogs', 'dogs', 'cats', 'panda', 'panda', 'panda', 'panda', 'cats', 'panda']
為了更加清晰的展示預(yù)測值和真實(shí)值的關(guān)系,構(gòu)建混淆矩陣,如圖。可見一個6層的卷積網(wǎng)絡(luò)經(jīng)過10次循環(huán)后,對熊貓的預(yù)測精度最高,對狗的預(yù)測精度較低,需要增加網(wǎng)絡(luò)層數(shù)和循環(huán)次數(shù)。
總結(jié)
以上是生活随笔為你收集整理的【神经网络】(3) 卷积神经网络(CNN),案例:动物三分类,附python完整代码的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【神经网络】(2) 网络优化,案例:服装
- 下一篇: 【神经网络】(4) 卷积神经网络(CNN