【神经网络】(4) 卷积神经网络(CNN),自定义网络,案例:彩色图像10分类
各位同學大家好,今天和大家分享一下TensorFlow2.0中如何使用函數方法自定義卷積神經網絡。
1. 導入數據
獲取系統自帶的10分類圖像數據,50k張用于訓練,10k張用于測試。
# 10分類卷積神經網絡
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import Sequential,optimizers,layers,datasets#(1)數據獲取
(x,y), (x_test, y_test) = datasets.cifar10.load_data()# 查看數據
print('x.shape:',x.shape,'y.shape:',y.shape)
print('x_test.shape:',x_test.shape,'y_test.shape:',y_test.shape)
print('y[:5]:',y[:5])
# x.shape: (50000, 32, 32, 3) y.shape: (50000, 1)
# x_test.shape: (10000, 32, 32, 3) y_test.shape: (10000, 1)#(2)圖像可視化
import matplotlib.pyplot as plt
for i in range(10):plt.subplot(2,5,i+1)plt.imshow(x[i])plt.xticks([])plt.yticks([])
plt.show()
2. 數據預處理
將圖像的每個像素值從[0,255]映射到[-1,1]之間,由于目標值y的shape為[b,1]需要把shape為1的軸擠壓掉tf.squeeze(),留下分類數值。對所有的訓練和測試數據集重新洗牌.shuffle(),但不打亂x和y之間的對應關系。每次迭代從數據集中取出128個樣本,.batch(128)
#(3)數據預處理
def processing(x,y):x = 2 * tf.cast(x, tf.float32) / 255.0 - 1 # [-1,1]y = tf.cast(y, tf.int32)return(x,y)# 構造訓練集數據集
y = tf.squeeze(y, axis=1) # 擠壓掉shape=1的軸
train_ds = tf.data.Dataset.from_tensor_slices((x,y))
train_ds = train_ds.map(processing).shuffle(10000).batch(128) # 打亂數據集,每次迭代取128個數據# 構造測試集數據集,不需要onehot編碼,需要和預測結果比較
y_test = tf.squeeze(y_test, axis=1)
test_ds = tf.data.Dataset.from_tensor_slices((x_test,y_test))
test_ds = test_ds.map(processing).shuffle(10000).batch(128) # 構造迭代器,查看劃分是否正確
sample = next(iter(train_ds)) # 每次運行從數據集中取出一組xy
print('x_batch:', sample[0].shape, 'y_batch:', sample[1].shape)
# x_batch: (64, 128, 128, 3) y_batch: (64, 3)
3. 構造網絡
使用函數方法自定義網絡各層,layers.BatchNormalization()為batchnorm層,在把輸入送入至非線性函數之前,能有效的將數據控制在一定的范圍內,使網絡更穩定,收斂更快。如果使用該層,訓練集和測試集的使用的方法不同,訓練時需要指定net(x, training=True),BN層的參數在正反向傳播過程中會被優化更新。測試時指定net(x, training=False)。BN層的參數在正反向傳播過程中不會被更新。
#(4)網絡構造
# 自定義網絡層
def CNN(classes, input_shape):# 輸入層inputs = tf.keras.Input(shape=input_shape)# 卷積層x = layers.Conv2D(32, kernel_size=(3,3), strides=1, kernel_regularizer=keras.regularizers.l2(0.001), padding='same', activation='relu')(inputs)x = layers.Conv2D(32, kernel_size=(3,3), strides=1, padding='same')(x)# BN層x = layers.BatchNormalization()(x)# relu層x = layers.Activation('relu')(x) # 池化層x = layers.MaxPool2D(pool_size=(2,2), strides=2, padding='same')(x)# unit2x = layers.Conv2D(64, kernel_size=(3,3), strides=1, kernel_regularizer=keras.regularizers.l2(0.001), padding='same', activation='relu')(x)x = layers.Conv2D(64, kernel_size=(3,3), strides=1, padding='same')(x)# BN層x = layers.BatchNormalization()(x)# relu層x = layers.Activation('relu')(x) # 池化層x = layers.MaxPool2D(pool_size=(2,2), strides=2, padding='same')(x)# Flatten層 連接卷積層和全連接層x = layers.Flatten()(x)# 全連接層x = layers.Dense(128, activation='relu')(x)# Dropout層x = layers.Dropout(0.2)(x) # 全連接層x = layers.Dense(64, activation='relu')(x)# Dropout層x = layers.Dropout(0.2)(x)# logits層,不轉為概率outputs = layers.Dense(classes)(x)# 構建網絡model = keras.Model(inputs=inputs, outputs=outputs)# 返回網絡結構return model# 傳入函數所需參數
model = CNN(10, (32, 32, 3)) # 指定10個分類,輸入的一張圖片的shape為(32,32,3)# 查看網絡結構
model.summary()# 指定優化器
optimizer = optimizers.Adam(lr=0.0001, beta_1=0.9, beta_2=0.99)
?網絡結構如下圖所示,param代表每一層參數個數
Model: "model"
_________________________________________________________________Layer (type) Output Shape Param #
=================================================================input_1 (InputLayer) [(None, 32, 32, 3)] 0 conv2d (Conv2D) (None, 32, 32, 32) 896 conv2d_1 (Conv2D) (None, 32, 32, 32) 9248 batch_normalization (BatchN (None, 32, 32, 32) 128 ormalization) activation (Activation) (None, 32, 32, 32) 0 max_pooling2d (MaxPooling2D (None, 16, 16, 32) 0 ) conv2d_2 (Conv2D) (None, 16, 16, 64) 18496 conv2d_3 (Conv2D) (None, 16, 16, 64) 36928 batch_normalization_1 (Batc (None, 16, 16, 64) 256 hNormalization) activation_1 (Activation) (None, 16, 16, 64) 0 max_pooling2d_1 (MaxPooling (None, 8, 8, 64) 0 2D) flatten (Flatten) (None, 4096) 0 dense (Dense) (None, 128) 524416 dropout (Dropout) (None, 128) 0 dense_1 (Dense) (None, 64) 8256 dropout_1 (Dropout) (None, 64) 0 dense_2 (Dense) (None, 10) 650 =================================================================
Total params: 599,274
Trainable params: 599,082
Non-trainable params: 192
_________________________________________________________________
4. 網絡訓練
為了得到和model.fit()一樣的效果,訓練過程和測試過程都計算一下損失和準確率,并將每一次迭代的結果保存下來。指定model(x, trainin=True)更新BN層參數。計算交叉熵損失時,categorical_crossentropy(y, logits, from_logits=True),logits為每張圖片屬于各個分類的實數值,指定from_logits=True,logits會自動經過softmax函數變成概率再和真實值y比較,提高了網絡的穩定性。
# 保存每次循環的損失和準確率
train_loss_list = []
train_acc_list = []
test_loss_list = []
test_acc_list = []# 大循環
for epochs in range(10): # 循環10次print('---------------')print('epochs:', epochs)#(5)網絡訓練total_sum = 0total_correct = 0total_loss = 0 for step, (x,y) in enumerate(train_ds):# 梯度跟蹤with tf.GradientTape() as tape:# 前行傳播,[b,32,32,3]=>[b,10]logits = model(x, trainin=True) # 得到logits層輸出后的10分類數值# 計算準確率# 將logits層輸出結果轉概率prob = tf.nn.softmax(logits, axis=1)# 找到概率最大值對應的索引predict = tf.argmax(prob, axis=1, output_type=tf.int32)# 預測值和真實值是否相同correct = tf.cast(tf.equal(predict, y), tf.int32)# 計算預測對了幾個correct_sum = tf.reduce_sum(correct)# 計算總個數total_correct += correct_sum # 總預測對了幾個total_sum += x.shape[0] # 總共有多少參與# 計算損失y = tf.one_hot(y, depth=10) # 真實值y是onehot編碼后的# 計算交叉熵損失CE = tf.losses.categorical_crossentropy(y, logits, from_logits=True)# 計算每個batch的平均損失loss = tf.reduce_mean(CE)# 記錄batch總損失loss_sum = tf.reduce_sum(CE)total_loss += loss_sum# 梯度計算grads = tape.gradient(loss, model.trainable_variables) # 對所有的權重和偏置計算梯度# 更新梯度optimizer.apply_gradients(zip(grads, model.trainable_variables))# 每100個batch打印一次損失if step%100 == 0:print('step:', step, 'loss:', loss.numpy())# 保存訓練的準確率和損失# 計算每次循環的準確率train_acc = total_correct/total_sumtrain_acc_list.append(train_acc)# 計算每次循環的損失train_loss = total_loss/total_sumtrain_loss_list.append(train_loss)# 每次循環后打印一次print('train_acc:', train_acc.numpy(), 'train_loss:', train_loss.numpy())
5. 網絡測試
用和訓練過程相同的方法計算損失和準確率,但不需要更新梯度,將每一次迭代的結果保存下來。
#(6)網絡測試total_sum = 0total_correct = 0total_loss = 0for (x,y) in test_ds:# 前向傳播,得到輸出層結果logits = model(x, training=False) # 不更新BN層參數# 轉換成每張圖片屬于每個分類的概率prob = tf.nn.softmax(logits, axis=1)# 概率最大的值的索引predict = tf.argmax(prob, axis=1, output_type=tf.int32)# 預測值和真實值比較,相同返回1correct = tf.cast(tf.equal(predict, y), dtype=tf.int32)# 計算相同的個數之和correct_sum = tf.reduce_sum(correct)# 計算準確率total_correct += int(correct_sum) # 預測對了幾個total_sum += x.shape[0] # 一共有多少個參與預測# 計算測試集損失y = tf.one_hot(y, depth=10) # 測試集的目標進行onehot編碼后與預測得到的結果計算交叉熵損失loss = tf.reduce_sum(tf.losses.categorical_crossentropy(y, logits, from_logits=True)) # 每一個batch的總損失total_loss += loss # 記錄整個循環的損失和# 每個大循環計算一次損失及準確率test_loss = total_loss/total_sum test_acc = total_correct/total_sumprint('test_loss:', test_loss.numpy(),'test_acc:', test_acc)# 保存test_loss_list.append(test_loss)test_acc_list.append(test_acc)
6. 結果展示
網絡迭代10次之后,準確率提高了44%,沒有出現過擬合現象。
---------------------------------------
epochs: 0
step: 0 loss: 3.0574064
step: 100 loss: 1.9813004
step: 200 loss: 1.7280517
step: 300 loss: 1.6263434
train_acc: 0.33082 train_loss: 1.8415068
test_loss: 2.096799 test_acc: 0.2592
---------------
---------------------------------------
.......................................
---------------------------------------
epochs: 9
step: 0 loss: 0.78702605
step: 100 loss: 0.7967223
step: 200 loss: 0.82377315
step: 300 loss: 1.0987799
train_acc: 0.69934 train_loss: 0.8607509
test_loss: 0.8950623 test_acc: 0.6914
7. 模型評估
繪制訓練集和驗證集的準確率和損失的對比曲線,來對網絡優個直觀的感受。可見前10次迭代收斂平緩,效果較好,隨著迭代次數的增加可能會出現過擬合現象。
epochs_range = range(len(train_loss_list)) # 橫坐標,網絡循環了幾次# 準確率曲線
plt.figure(figsize=(10,5))
plt.subplot(1,2,1)
plt.plot(epochs_range, train_acc_list, label='Training_acc')
plt.plot(epochs_range, test_acc_list, label='validation_acc')
plt.legend()
plt.title('Accuracy')
# 損失曲線
plt.subplot(1,2,2)
plt.plot(epochs_range, train_loss_list, label='Training_loss')
plt.plot(epochs_range, test_loss_list, label='validation_loss')
plt.legend()
plt.title('Loss')
總結
以上是生活随笔為你收集整理的【神经网络】(4) 卷积神经网络(CNN),自定义网络,案例:彩色图像10分类的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【神经网络】(3) 卷积神经网络(CNN
- 下一篇: 【神经网络】(6) 卷积神经网络(VGG