【神经网络】(8) 卷积神经网络(Mobilenet_v1),案例:cifar图像10分类
各位同學大家好,今天和大家分享一下TensorFlow2.0中如何搭載Mobilenet_v1神經網絡。
1. 模型簡介
MobileNet系列是輕量級網絡的一個系列,是針對移動端以及嵌入式視覺的應用提出的一類有效的模型。詳細見:MobileNet_v1詳解 - 灰信網(軟件開發博客聚合) (freesion.com)
MobileNet是一種基于深度可分離卷積的模型,深度可分離卷積是一種將標準卷積分解成深度卷積以及一個1x1的卷積。對于MobileNet而言,深度卷積針對每個單個輸入通道應用單個濾波器進行濾波,然后逐點卷積應用1x1的卷積操作來結合所有深度卷積得到的輸出。而標準卷積一步即對所有的輸入進行結合得到新的一系列輸出。深度可分離卷積將其分成了兩步,針對每個單獨層進行濾波然后下一步即結合。這種分解能夠有效的大量減少計算量以及模型的大小。
深度可分離卷積和標準卷積的區別在于:標準卷積是將每個卷積核應用在所有通道上,而深度可分離卷積針對輸入中的每個通道應用不同的卷積核。假設輸入有M個通道,從下圖可以看到,標準卷積層有N個卷積核,每個卷積核尺寸為Dk * Dk,通道數為M;深度可分離卷積有M個卷積核,每個卷積核的通道數都是1。
但是深度可分離卷積的輸入和輸出維度都是一樣的,那怎么改變維度呢?這時候就輪到逐點卷積出場,其本質為1*1的標準卷積,主要作用就是對輸入進行升維和降維。
2. 數據加載
導入cifar10圖像數據,由于導入的標簽值y是二維的,需要將的shape=1的軸擠壓掉。使用tf.squeeze(),指定軸為axis=1,使用.map()對數據集中的所有數據采用函數中的方法預處理,原始圖像中每個像素值在[0,255]之間,歸一化處理將其映射到[0,1]之間。指定.batch(128),即每次迭代從數據集中取出128組樣本用于訓練。
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers, optimizers, Model, datasets# (1)數據獲取
(x,y), (x_test,y_test) = datasets.cifar10.load_data()# 查看數據
print('x.shape:',x.shape,'y.shape:',y.shape)
# x.shape: (50000, 32, 32, 3) y.shape: (50000, 1)
print('y[:5]:', y[:5])#(2)預處理
# 預處理函數類型轉換
def processing(x,y):x = tf.cast(x, dtype=tf.float32) / 255.0 y = tf.cast(y, dtype=tf.int32)return x,y# 創建訓練集數據集
y = tf.squeeze(y, axis=1) # 把目標值y維度為1的軸擠壓掉
train_db = tf.data.Dataset.from_tensor_slices((x,y))
train_db = train_db.map(processing).shuffle(10000).batch(128)# 創建測試集數據集
y_test = tf.squeeze(y_test, axis=1)
test_db = tf.data.Dataset.from_tensor_slices((x_test,y_test))
test_db = test_db.map(processing).batch(128)# 構造一個迭代器,檢查數據
sample = next(iter(train_db))
print('x_batch:', sample[0].shape,'y_batch', sample[1].shape)
為了能直觀地了解我們要預測的圖像,將圖像可視化。
#(2)顯示圖像
import matplotlib.pyplot as plt
for i in range(15):plt.subplot(3,5,i+1)plt.imshow(sample[0][i]) # sample[0]代表取出的一個batch的所有圖像信息plt.xticks([]) # 不顯示xy軸坐標刻度plt.yticks([])
plt.show()
3. 模型構建
網絡結構模型如下:
超參數:depth_multiplier 和 alpha。
所有層的?通道數?乘以?alpha?參數(四舍五入),模型大小近似下降到原來的 alpha^2?倍,計算量下降到原來的 alpha^2?倍,用于降低模型的寬度。
輸入層的 分辨率 乘以 depth_multiplier 參數 (四舍五入),等價于所有層的分辨率乘 depth_multiplier,模型大小不變,計算量下降到原來的 depth_multiplier^2?倍,用于降低輸入圖像的分辨率。
3.1 標準卷積
# 卷積+BN+激活函數
def conv_block(x, filter1, alpha, kernel, stride):# 寬度縮放因子filter1 = int(filter1 * alpha) # 卷積核--通道數# 卷積x = layers.Conv2D(filter1, kernel, stride, padding='same', use_bias=False)(x)# 標準化x = layers.BatchNormalization()(x)# 激活函數,使用relu6激活函數,保證移動端的精度x = layers.Activation('relu6')(x)return x
如果卷積層之后跟了BatchNormalization層,可以不用再加偏置了use_bias=False,對模型不起作用,還會占用內存。詳情見下文:偏置(bias)在什么情況下可以要,可以不要?
下圖是激活函數relu6和relu之間的關系;主要是為了在移動端float16的低精度的時候,也能有很好的數值分辨率,如果對reLu的輸出值不加限制,那么輸出范圍就是0到正無窮,而低精度的float16無法精確描述其數值,帶來精度損失。
3.2 深度分離卷積
# 深度可分離卷積塊代替普通的3*3卷積,減少參數提高網絡檢測效果
def depth_conv_block(x, filter1, alpha, stride, depth_multiplier):# 寬度縮放因子filter1 = int(filter1 * alpha)# 深度可分離卷積,輸入層的 分辨率 乘以 depth_multiplier 參數 x = layers.DepthwiseConv2D(kernel_size=(3,3), strides=stride, padding='same', depth_multiplier=depth_multiplier,use_bias=False)(x)# 標準化x = layers.BatchNormalization()(x)# 激活函數x = layers.Activation('relu6')(x)# 1*1普通卷積+BN+激活,調整通道數。注,卷積后有BN層沒必要加偏置,占用內存x = layers.Conv2D(filter1, kernel_size=(1,1), strides=(1,1), padding='same', use_bias=False)(x)x = layers.BatchNormalization()(x)x = layers.Activation('relu6')(x)return x
3.3 網絡主體
根據上面的網絡結構圖,一步一步堆疊下來
# 網絡主體部分
def mobilenet(classes=1000, input_shape=[224,224,3], alpha=1.0, depth_multiplier=1):# 輸入層input_tensor = keras.Input(shape=input_shape)# 普通卷積 [224,224,3]==>[112,112,32]x = conv_block(input_tensor, 32, alpha, kernel=(3,3), stride=(2,2))# 深度可分離卷積塊# [112,112,32] ==> [112,112,64]x = depth_conv_block(x, 64, alpha, (1,1), depth_multiplier) # [112,112,64] ==> [56,56,128]x = depth_conv_block(x, 128, alpha, (2,2), depth_multiplier) # [56,56,128]x = depth_conv_block(x, 128, alpha, (1,1), depth_multiplier) # [56,56,128]# [56,56,128] ==> [28,28,256]x = depth_conv_block(x, 256, alpha, (2,2), depth_multiplier) # [28,28,256]x = depth_conv_block(x, 256, alpha, (1,1), depth_multiplier) # [28,28,256]# [28,28,256] ==> [14,14,512]x = depth_conv_block(x, 512, alpha, (2,2), depth_multiplier) # [14,14,512]x = depth_conv_block(x, 512, alpha, (1,1), depth_multiplier) # [14,14,512] x = depth_conv_block(x, 512, alpha, (1,1), depth_multiplier) # [14,14,512] x = depth_conv_block(x, 512, alpha, (1,1), depth_multiplier) # [14,14,512] x = depth_conv_block(x, 512, alpha, (1,1), depth_multiplier) # [14,14,512] x = depth_conv_block(x, 512, alpha, (1,1), depth_multiplier) # [14,14,512] # [14,14,512] ==> [7,7,1024]x = depth_conv_block(x, 1024, alpha, (2,2), depth_multiplier) # [7,7,1024]x = depth_conv_block(x, 1024, alpha, (1,1), depth_multiplier) # [7,7,1024] # 全局平均池化層# [7, 7, 1024] -> [None, 1024]x = layers.GlobalAveragePooling2D()(x)shape = (1, 1, int(1024 * alpha)) #[1,1,1024]x = layers.Reshape(shape)(x) #[1,1,1024]x = layers.Dropout(0.5)(x)x = layers.Conv2D(classes, (1, 1), padding='same')(x) #[1,1,classes]x = layers.Activation('softmax')(x) # [1,1,1024]x = layers.Reshape((classes,))(x) # [None, classes]# 模型構建model = Model(input_tensor, x)return model# 模型
model = mobilenet()
# 查看網絡結構
model.summary()
4. 完整代碼
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers, optimizers, Model, 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])#(2)預處理
# 預處理函數類型轉換
def processing(x,y):x = tf.cast(x, dtype=tf.float32) / 255.0 y = tf.cast(y, dtype=tf.int32)return x,y# 創建訓練集數據集
y = tf.squeeze(y, axis=1) # 把目標值y維度為1的軸擠壓掉
y = tf.one_hot(y, depth=10)
train_db = tf.data.Dataset.from_tensor_slices((x,y))
train_db = train_db.map(processing).shuffle(10000).batch(128)# 創建測試集數據集
y_test = tf.squeeze(y_test, axis=1)
y_test = tf.one_hot(y_test, depth=10)
test_db = tf.data.Dataset.from_tensor_slices((x_test,y_test))
test_db = test_db.map(processing).batch(128)# 構造一個迭代器,檢查數據
sample = next(iter(train_db))
print('x_batch:', sample[0].shape,'y_batch', sample[1].shape)#(2)顯示圖像
import matplotlib.pyplot as plt
for i in range(15):plt.subplot(3,5,i+1)plt.imshow(sample[0][i]) # sample[0]代表取出的一個batch的所有圖像信息,映射到[0,1]之間顯示圖像plt.xticks([]) # 不顯示xy軸坐標刻度plt.yticks([])
plt.show()#(3)構造模型
# 卷積+BN+激活函數
def conv_block(x, filter1, alpha, kernel, stride):# 寬度縮放因子filter1 = int(filter1 * alpha)# 卷積x = layers.Conv2D(filter1, kernel, stride, padding='same', use_bias=False)(x)# 標準化x = layers.BatchNormalization()(x)# 激活函數,使用relu6激活函數,保證移動端的精度x = layers.Activation('relu6')(x)return x# 深度可分離卷積塊代替普通的3*3卷積,減少參數提高網絡檢測效果
def depth_conv_block(x, filter1, alpha, stride, depth_multiplier):# 寬度縮放因子filter1 = int(filter1 * alpha)# 深度可分離卷積x = layers.DepthwiseConv2D(kernel_size=(3,3), strides=stride, padding='same', depth_multiplier=depth_multiplier,use_bias=False)(x)# 標準化x = layers.BatchNormalization()(x)# 激活函數x = layers.Activation('relu6')(x)# 1*1普通卷積+BN+激活,調整通道數。注,卷積后有BN層沒必要加偏置,占用內存x = layers.Conv2D(filter1, kernel_size=(1,1), strides=(1,1), padding='same', use_bias=False)(x)x = layers.BatchNormalization()(x)x = layers.Activation('relu6')(x)return x# 網絡主體部分
# 4種分類,規定圖片輸入大小和之前讀入圖片相同
def mobilenet(classes=10, input_shape=[32, 32, 3], alpha=1.0, depth_multiplier=1):# 輸入層input_tensor = keras.Input(shape=input_shape)# 普通卷積 [224,224,3]==>[112,112,32]x = conv_block(input_tensor, 32, alpha, kernel=(3,3), stride=(2,2))# 深度可分離卷積塊# [112,112,32] ==> [112,112,64]x = depth_conv_block(x, 64, alpha, (1,1), depth_multiplier) # [112,112,64] ==> [56,56,128]x = depth_conv_block(x, 128, alpha, (2,2), depth_multiplier) # [56,56,128]x = depth_conv_block(x, 128, alpha, (1,1), depth_multiplier) # [56,56,128]# [56,56,128] ==> [28,28,256]x = depth_conv_block(x, 256, alpha, (2,2), depth_multiplier) # [28,28,256]x = depth_conv_block(x, 256, alpha, (1,1), depth_multiplier) # [28,28,256]# [28,28,256] ==> [14,14,512]x = depth_conv_block(x, 512, alpha, (2,2), depth_multiplier) # [14,14,512]x = depth_conv_block(x, 512, alpha, (1,1), depth_multiplier) # [14,14,512] x = depth_conv_block(x, 512, alpha, (1,1), depth_multiplier) # [14,14,512] x = depth_conv_block(x, 512, alpha, (1,1), depth_multiplier) # [14,14,512] x = depth_conv_block(x, 512, alpha, (1,1), depth_multiplier) # [14,14,512] x = depth_conv_block(x, 512, alpha, (1,1), depth_multiplier) # [14,14,512] # [14,14,512] ==> [7,7,1024]x = depth_conv_block(x, 1024, alpha, (2,2), depth_multiplier) # [7,7,1024]x = depth_conv_block(x, 1024, alpha, (1,1), depth_multiplier) # [7,7,1024] # 全局平均池化層[7,7,1024] ==> [b, 1024]x = layers.GlobalAveragePooling2D()(x)shape = (1, 1, int(1024 * alpha))x = layers.Reshape(shape)(x)x = layers.Dropout(0.5)(x)x = layers.Conv2D(classes, (1, 1), padding='same')(x)x = layers.Activation('softmax')(x)x = layers.Reshape((classes,))(x)# 模型構建model = Model(input_tensor, x)return model# 模型
model = mobilenet()
# 查看網絡結構
model.summary()#(4)網絡配置
# 設置學習率
opt = optimizers.Adam(learning_rate=1e-5)# 編譯
model.compile(optimizer=opt, # 學習率loss = tf.losses.CategoricalCrossentropy(), #損失函數metrics=['accuracy']) # 評價指標# 訓練
history = model.fit(train_db, # 訓練集validation_data=test_db, # 驗證集epochs=30) # 迭代次數#(5)模型評估
# 準確率
train_acc = history.history['acc']
val_acc = history.history['val_acc']
# 損失
train_loss = history.history['loss']
val_loss = history.history['val_loss']
# 繪圖
plt.figure(figsize=(10,5))
# 準確率
plt.subplot(1,2,1)
plt.plot(train_acc, label='train_acc')
plt.plot(val_acc, label='val_acc')
plt.legend()
# 損失曲線
plt.subplot(1,2,2)
plt.plot(train_loss, label='train_loss')
plt.plot(val_loss, label='val_loss')
plt.legend()
總結
以上是生活随笔為你收集整理的【神经网络】(8) 卷积神经网络(Mobilenet_v1),案例:cifar图像10分类的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【神经网络】(7) 迁移学习(CNN-M
- 下一篇: 【yolo目标检测】(1) yolov3