tensorflow2學(xué)習(xí)筆記

第五章卷積神經(jīng)網(wǎng)絡(luò)

5.1卷積計(jì)算過程

因?yàn)槿B接網(wǎng)絡(luò)在實(shí)際應(yīng)用中,數(shù)據(jù)參數(shù)量過大導(dǎo)致過擬合,所以不會(huì)將原始圖像直接輸入,而是先對(duì)特征進(jìn)行提取,再將提取到到特征輸入全連接網(wǎng)絡(luò),因此提出卷積神經(jīng)網(wǎng)絡(luò)。
卷積到概念:積可以認(rèn)為是一種有效提取圖像特征的方法。一般會(huì)用一個(gè)正方形的卷積核,按指定步長,在輸入特征圖上滑動(dòng),遍歷輸入特征圖中的每個(gè)像素點(diǎn)。每一個(gè)步長, 卷積核會(huì)與輸入特征圖出現(xiàn)重合區(qū)域,重合區(qū)域?qū)?yīng)元素相乘、求和再加上偏置項(xiàng)得到輸出特征的一個(gè)像素點(diǎn)。如圖所示:


卷積計(jì)算

輸入特征圖的深度(channel數(shù)),決定了當(dāng)前層卷積核的深度;
當(dāng)前層卷積核的個(gè)數(shù),決定了當(dāng)前層輸出特征圖的深度。

5.2感受野(Receptive Field)

感受野的概念:卷積神經(jīng)網(wǎng)絡(luò)各輸出層每個(gè)像素點(diǎn)在原始圖像上
的映射區(qū)域大小,如圖所示:


感受野

上圖中兩種方法感受野都為5。
當(dāng)采用尺寸不同的卷積核時(shí),最大的區(qū)別就是感受野的大小不同,所以經(jīng)常會(huì)采用多層小卷積核來替換一層大卷積核,在保持感受野相同的情況下減少參數(shù)量和計(jì)算量,例如十分常見的用2層3 * 3卷積核來替換1層5 * 5卷積核的方法,如圖所示:


計(jì)算量區(qū)別

從上圖中可以直接看出參數(shù)量的大小,下面給出計(jì)算量如何計(jì)算出來
先給出輸出特征尺寸計(jì)算公式:
輸出特征尺寸計(jì)算公式

根據(jù)公式 ,5 * 5 卷積核輸出特征圖共有(x – 5 + 1)^2 個(gè)像素點(diǎn),每個(gè)像素點(diǎn)需要進(jìn)行 5 * 5 = 25 次乘加運(yùn)算,則總計(jì)算量 為 25 * (x – 5 + 1)^2 = 25x^2 – 200x + 400;
兩個(gè)3*3卷積核,第一個(gè) 3 * 3 卷積核輸出特征圖共有(x – 3 + 1)^2 個(gè)像素點(diǎn), 每個(gè)像素點(diǎn)需要進(jìn)行3 * 3 = 9次乘加運(yùn)算,第二個(gè)3 * 3卷積核輸出特征圖共有(x – 3 + 1 – 3 + 1)^2 個(gè)像素點(diǎn)(此時(shí)圖片邊長為x-3+1),每個(gè)像素點(diǎn)同樣需要進(jìn)行 9 次乘加運(yùn)算,則總計(jì)算量為 9 * (x – 3 + 1)^2 + 9 * (x – 3 + 1 – 3 + 1)^2 = 18 x^2 – 108x +180;

5.3全零填充

全零填充概念:為了保持輸出圖像尺寸與輸入圖像一致,經(jīng)常會(huì)在輸入圖像
周圍進(jìn)行全零填充,如圖所示,在 5×5 的輸入圖像周圍填 0,則輸出特征尺寸同為 5×5。


全零填充

對(duì)上一小節(jié)中的輸出特征尺寸公式進(jìn)行更新:


輸出特征尺寸公式

TF描述全零填充 用參數(shù)padding = ‘SAME’或 padding = ‘VALID’表示

5.4TF描述卷積計(jì)算層

tf.keras.layers.Conv2D (
filters = 卷積核個(gè)數(shù),
kernel_size = 卷積核尺寸, #正方形寫核長整數(shù),或(核高h(yuǎn),核寬w)
strides = 滑動(dòng)步長, #橫縱向相同寫步長整數(shù),或(縱向步長h,橫向步長w),默認(rèn)1
padding = “same” or “valid”, #使用全零填充是“same”,不使用是“valid”(默認(rèn))
activation = “ relu ” or “ sigmoid ” or “ tanh ” or “ softmax”等 , #如有BN此處不寫 input_shape = (高, 寬 , 通道數(shù)) #輸入特征圖維度,可省略
)

model = tf.keras.models.Sequential([
  Conv2D(6, 5, padding='valid', activation='sigmoid'),
  Conv2D(6, (5, 5), padding='valid', activation='sigmoid'),
  Conv2D(filters=6, kernel_size=(5, 5), padding='valid', activation='sigmoid'),
])

5.5批標(biāo)準(zhǔn)化

標(biāo)準(zhǔn)化:使數(shù)據(jù)符合0均值,1為標(biāo)準(zhǔn)差的分布。
批標(biāo)準(zhǔn)化:對(duì)一小批數(shù)據(jù)(batch),做標(biāo)準(zhǔn)化處理 。
BN操作使進(jìn)入激活函數(shù)的數(shù)據(jù)分布在激活函數(shù)線性區(qū),提升了激活函數(shù)對(duì)輸入數(shù)據(jù)對(duì)區(qū)分力,通過縮放因子和偏移因子,保證了網(wǎng)絡(luò)的非線性表達(dá)力。



BN 操作通常位于卷積層之后,激活層之前,在 Tensorflow 框架中,通常使用 Keras 中的 tf.keras.layers.BatchNormalization 函數(shù)來構(gòu)建 BN 層。

model = tf.keras.models.Sequential([
  Conv2D(filters=6, kernel_size=(5, 5), padding='valid'),
  BatchNormalization()
])

5.6池化

池化用于減少特征數(shù)據(jù)量。 最大值池化可提取圖片紋理,均值池化可保留背景特征。


池化

TF描述池化
tf.keras.layers.MaxPool2D(
pool_size=池化核尺寸,#正方形寫核長整數(shù),或(核高h(yuǎn),核寬w)
strides=池化步長,#步長整數(shù), 或(縱向步長h,橫向步長w),默認(rèn)為pool_size padding=‘valid’or‘same’ #使用全零填充是“same”,不使用是“valid”(默認(rèn)))
tf.keras.layers.AveragePooling2D(
pool_size=池化核尺寸,#正方形寫核長整數(shù),或(核高h(yuǎn),核寬w)
strides=池化步長,#步長整數(shù), 或(縱向步長h,橫向步長w),默認(rèn)為pool_size padding=‘valid’or‘same’ #使用全零填充是“same”,不使用是“valid”(默認(rèn)))

model = tf.keras.models.Sequential([
  Conv2D(filters=6, kernel_size=(5, 5), padding='valid'),  # 卷積層
  BatchNormalization()  # BN層
  Activation('relu'), # 激活層
  MaxPool2D(pool_size=(2, 2), strides=2, padding='same'), # 池化層
])

5.7舍棄

在神經(jīng)網(wǎng)絡(luò)訓(xùn)練時(shí),將一部分神經(jīng)元按照一定概率從神經(jīng)網(wǎng)絡(luò) 中暫時(shí)舍棄。神經(jīng)網(wǎng)絡(luò)使用時(shí),被舍棄的神經(jīng)元恢復(fù)鏈接。
TF描述舍棄 tf.keras.layers.Dropout(舍棄的概率)

model = tf.keras.models.Sequential([
  Conv2D(filters=6, kernel_size=(5, 5), padding='same'), # 卷積層             
  BatchNormalization(), # BN層
  Activation('relu'), # 激活層
  MaxPool2D(pool_size=(2, 2), strides=2, padding='same'), # 池化層   
  Dropout(0.2), # dropout層
])

5.8卷積神經(jīng)網(wǎng)絡(luò)

卷積神經(jīng)網(wǎng)絡(luò)

5.9cifar0數(shù)據(jù)集

如果數(shù)據(jù)集下載太慢,可以自己通過百度云下載(https://blog.csdn.net/baidu_35113561/article/details/79375701),然后存入~/.keras/datasets/中。

5.10卷積神經(jīng)網(wǎng)絡(luò)搭建

import tensorflow as tf
import os

os.environ['KMP_DUPLICATE_LIB_OK'] = 'True'
import numpy as np
from matplotlib import pyplot as plt
from tensorflow.keras.layers import Conv2D, BatchNormalization, Activation, MaxPool2D, Dropout, Flatten, Dense
from tensorflow.keras import Model  # 導(dǎo)入相關(guān)的包

np.set_printoptions(threshold=np.inf)  # 打印參數(shù)

cifar10 = tf.keras.datasets.cifar10
(x_train, y_train), (x_test, y_test) = cifar10.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0  # 讀入數(shù)據(jù),歸一化


class Baseline(Model):
    def __init__(self):
        super(Baseline, self).__init__()
        self.c1 = Conv2D(filters=6, kernel_size=(5, 5), padding='same')  # 卷積層
        self.b1 = BatchNormalization()  # BN層
        self.a1 = Activation('relu')  # 激活層
        self.p1 = MaxPool2D(pool_size=(2, 2), strides=2, padding='same')  # 池化層
        self.d1 = Dropout(0.2)  # dropout層,以上就是卷積神經(jīng)網(wǎng)絡(luò)的五層

        self.flatten = Flatten()
        self.f1 = Dense(128, activation='relu')
        self.d2 = Dropout(0.2)
        self.f2 = Dense(10, activation='softmax')

    def call(self, x):
        x = self.c1(x)
        x = self.b1(x)
        x = self.a1(x)
        x = self.p1(x)
        x = self.d1(x)

        x = self.flatten(x)
        x = self.f1(x)
        x = self.d2(x)
        y = self.f2(x)
        return y


model = Baseline()

model.compile(optimizer='adam',
              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False),
              metrics=['sparse_categorical_accuracy'])

checkpoint_save_path = "./checkpoint/Baseline.ckpt"
if os.path.exists(checkpoint_save_path + '.index'):
    print('-------------load the model-----------------')
    model.load_weights(checkpoint_save_path)

cp_callback = tf.keras.callbacks.ModelCheckpoint(filepath=checkpoint_save_path,
                                                 save_weights_only=True,
                                                 save_best_only=True)

history = model.fit(x_train, y_train, batch_size=32, epochs=5, validation_data=(x_test, y_test), validation_freq=1,
                    callbacks=[cp_callback])
model.summary()

# print(model.trainable_variables)
file = open('./weights.txt', 'w')
for v in model.trainable_variables:
    file.write(str(v.name) + '\n')
    file.write(str(v.shape) + '\n')
    file.write(str(v.numpy()) + '\n')
file.close()

###############################################    show   ###############################################

# 顯示訓(xùn)練集和驗(yàn)證集的acc和loss曲線
acc = history.history['sparse_categorical_accuracy']
val_acc = history.history['val_sparse_categorical_accuracy']
loss = history.history['loss']
val_loss = history.history['val_loss']

plt.subplot(1, 2, 1)
plt.plot(acc, label='Training Accuracy')
plt.plot(val_acc, label='Validation Accuracy')
plt.title('Training and Validation Accuracy')
plt.legend()

plt.subplot(1, 2, 2)
plt.plot(loss, label='Training Loss')
plt.plot(val_loss, label='Validation Loss')
plt.title('Training and Validation Loss')
plt.legend()
plt.show()

5.11LeNet

共享卷積核,減少網(wǎng)絡(luò)參數(shù)。


LeNet結(jié)構(gòu)示意圖

一共5層,兩層卷積層,三層全連接層

class LeNet5(Model):
    def __init__(self):
        super(LeNet5, self).__init__()
        self.c1 = Conv2D(filters=6, kernel_size=(5, 5),
                         activation='sigmoid')
        self.p1 = MaxPool2D(pool_size=(2, 2), strides=2)

        self.c2 = Conv2D(filters=16, kernel_size=(5, 5),
                         activation='sigmoid')
        self.p2 = MaxPool2D(pool_size=(2, 2), strides=2)

        self.flatten = Flatten()
        self.f1 = Dense(120, activation='sigmoid')
        self.f2 = Dense(84, activation='sigmoid')
        self.f3 = Dense(10, activation='softmax')

    def call(self, x):
        x = self.c1(x)
        x = self.p1(x)

        x = self.c2(x)
        x = self.p2(x)

        x = self.flatten(x)
        x = self.f1(x)
        x = self.f2(x)
        y = self.f3(x)
        return y

5.12AlexNet

激活函數(shù)使用 Relu,提升訓(xùn)練速度;Dropout 防止過擬合。


AlexNet

一共8層,5層卷積層,3層全連接層

class AlexNet8(Model):
    def __init__(self):
        super(AlexNet8, self).__init__()
        self.c1 = Conv2D(filters=96, kernel_size=(3, 3))
        self.b1 = BatchNormalization()
        self.a1 = Activation('relu')
        self.p1 = MaxPool2D(pool_size=(3, 3), strides=2)

        self.c2 = Conv2D(filters=256, kernel_size=(3, 3))
        self.b2 = BatchNormalization()
        self.a2 = Activation('relu')
        self.p2 = MaxPool2D(pool_size=(3, 3), strides=2)

        self.c3 = Conv2D(filters=384, kernel_size=(3, 3), padding='same',
                         activation='relu')
                         
        self.c4 = Conv2D(filters=384, kernel_size=(3, 3), padding='same',
                         activation='relu')
                         
        self.c5 = Conv2D(filters=256, kernel_size=(3, 3), padding='same',
                         activation='relu')
        self.p3 = MaxPool2D(pool_size=(3, 3), strides=2)

        self.flatten = Flatten()
        self.f1 = Dense(2048, activation='relu')
        self.d1 = Dropout(0.5)
        self.f2 = Dense(2048, activation='relu')
        self.d2 = Dropout(0.5)
        self.f3 = Dense(10, activation='softmax')

5.13VGGNet

小卷積核減少參數(shù)的同時(shí),提高識(shí)別準(zhǔn)確率;網(wǎng)絡(luò)結(jié)構(gòu)規(guī)整,適合并行加速。

class VGG16(Model):
    def __init__(self):
        super(VGG16, self).__init__()
        self.c1 = Conv2D(filters=64, kernel_size=(3, 3), padding='same')  # 卷積層1
        self.b1 = BatchNormalization()  # BN層1
        self.a1 = Activation('relu')  # 激活層1
        self.c2 = Conv2D(filters=64, kernel_size=(3, 3), padding='same', )
        self.b2 = BatchNormalization()  # BN層1
        self.a2 = Activation('relu')  # 激活層1
        self.p1 = MaxPool2D(pool_size=(2, 2), strides=2, padding='same')
        self.d1 = Dropout(0.2)  # dropout層

        self.c3 = Conv2D(filters=128, kernel_size=(3, 3), padding='same')
        self.b3 = BatchNormalization()  # BN層1
        self.a3 = Activation('relu')  # 激活層1
        self.c4 = Conv2D(filters=128, kernel_size=(3, 3), padding='same')
        self.b4 = BatchNormalization()  # BN層1
        self.a4 = Activation('relu')  # 激活層1
        self.p2 = MaxPool2D(pool_size=(2, 2), strides=2, padding='same')
        self.d2 = Dropout(0.2)  # dropout層

        self.c5 = Conv2D(filters=256, kernel_size=(3, 3), padding='same')
        self.b5 = BatchNormalization()  # BN層1
        self.a5 = Activation('relu')  # 激活層1
        self.c6 = Conv2D(filters=256, kernel_size=(3, 3), padding='same')
        self.b6 = BatchNormalization()  # BN層1
        self.a6 = Activation('relu')  # 激活層1
        self.c7 = Conv2D(filters=256, kernel_size=(3, 3), padding='same')
        self.b7 = BatchNormalization()
        self.a7 = Activation('relu')
        self.p3 = MaxPool2D(pool_size=(2, 2), strides=2, padding='same')
        self.d3 = Dropout(0.2)

        self.c8 = Conv2D(filters=512, kernel_size=(3, 3), padding='same')
        self.b8 = BatchNormalization()  # BN層1
        self.a8 = Activation('relu')  # 激活層1
        self.c9 = Conv2D(filters=512, kernel_size=(3, 3), padding='same')
        self.b9 = BatchNormalization()  # BN層1
        self.a9 = Activation('relu')  # 激活層1
        self.c10 = Conv2D(filters=512, kernel_size=(3, 3), padding='same')
        self.b10 = BatchNormalization()
        self.a10 = Activation('relu')
        self.p4 = MaxPool2D(pool_size=(2, 2), strides=2, padding='same')
        self.d4 = Dropout(0.2)

        self.c11 = Conv2D(filters=512, kernel_size=(3, 3), padding='same')
        self.b11 = BatchNormalization()  # BN層1
        self.a11 = Activation('relu')  # 激活層1
        self.c12 = Conv2D(filters=512, kernel_size=(3, 3), padding='same')
        self.b12 = BatchNormalization()  # BN層1
        self.a12 = Activation('relu')  # 激活層1
        self.c13 = Conv2D(filters=512, kernel_size=(3, 3), padding='same')
        self.b13 = BatchNormalization()
        self.a13 = Activation('relu')
        self.p5 = MaxPool2D(pool_size=(2, 2), strides=2, padding='same')
        self.d5 = Dropout(0.2)

        self.flatten = Flatten()
        self.f1 = Dense(512, activation='relu')
        self.d6 = Dropout(0.2)
        self.f2 = Dense(512, activation='relu')
        self.d7 = Dropout(0.2)
        self.f3 = Dense(10, activation='softmax')

一共18層

5.14InceptionNet

一層內(nèi)使用不同尺寸的卷積核,提升感知力(通過 padding 實(shí)現(xiàn)輸出特征面積一致); 使用 1 * 1 卷積核,改變輸出特征 channel 數(shù)(減少網(wǎng)絡(luò)參數(shù))
與之前的網(wǎng)絡(luò)不同,不再是簡單的縱向堆疊


基本單元

可以看到,InceptionNet 的基本單元中,卷積部分是比較統(tǒng)一的 C、B、A 典型結(jié)構(gòu),即卷積→BN→激活,激活均采用 Relu 激活函數(shù),同時(shí)包含最大池化操作。
在 Tensorflow 框架下利用 Keras 構(gòu)建 InceptionNet 模型時(shí),可以將 C、B、A 結(jié)構(gòu)封裝 在一起,定義成一個(gè)新的 ConvBNRelu 類,以減少代碼量,同時(shí)更便于閱讀。

class ConvBNRelu(Model):
    def __init__(self, ch, kernelsz=3, strides=1, padding='same'):
        super(ConvBNRelu, self).__init__()
        self.model = tf.keras.models.Sequential([
            Conv2D(ch, kernelsz, strides=strides, padding=padding),
            BatchNormalization(),
            Activation('relu')
        ])

    def call(self, x):
        x = self.model(x, training=False) #在training=False時(shí),BN通過整個(gè)訓(xùn)練集計(jì)算均值、方差去做批歸一化,training=True時(shí),通過當(dāng)前batch的均值、方差去做批歸一化。推理時(shí) training=False效果好
        return x

參數(shù) ch 代表特征圖的通道數(shù),也即卷積核個(gè)數(shù);kernelsz 代表卷積核尺寸;strides 代表 卷積步長;padding 代表是否進(jìn)行全零填充。
完成了這一步后,就可以開始構(gòu)建 InceptionNet 的基本單元了,同樣利用 class 定義的方式,定義一個(gè)新的 InceptionBlk 類


基本單元
class InceptionBlk(Model):
    def __init__(self, ch, strides=1):
        super(InceptionBlk, self).__init__()
        self.ch = ch
        self.strides = strides
        self.c1 = ConvBNRelu(ch, kernelsz=1, strides=strides)
        self.c2_1 = ConvBNRelu(ch, kernelsz=1, strides=strides)
        self.c2_2 = ConvBNRelu(ch, kernelsz=3, strides=1)
        self.c3_1 = ConvBNRelu(ch, kernelsz=1, strides=strides)
        self.c3_2 = ConvBNRelu(ch, kernelsz=5, strides=1)
        self.p4_1 = MaxPool2D(3, strides=1, padding='same')
        self.c4_2 = ConvBNRelu(ch, kernelsz=1, strides=strides)

    def call(self, x):
        x1 = self.c1(x)
        x2_1 = self.c2_1(x)
        x2_2 = self.c2_2(x2_1)
        x3_1 = self.c3_1(x)
        x3_2 = self.c3_2(x3_1)
        x4_1 = self.p4_1(x)
        x4_2 = self.c4_2(x4_1)
        # concat along axis=channel
        x = tf.concat([x1, x2_2, x3_2, x4_2], axis=3)
        return x

參數(shù) ch 仍代表通道數(shù),strides 代表卷積步長,與 ConvBNRelu 類中一致;tf.concat 函數(shù)將四個(gè)輸出連接在一起,x1、x2_2、x3_2、x4_2 分別代表四列輸出,結(jié)合結(jié)構(gòu)圖和代碼很容易看出二者的對(duì)應(yīng)關(guān)系。
InceptionNet 網(wǎng)絡(luò)的主體就是由其基本單元構(gòu)成的,其模型結(jié)構(gòu)如圖


主體
class Inception10(Model):
    def __init__(self, num_blocks, num_classes, init_ch=16, **kwargs):
        super(Inception10, self).__init__(**kwargs)
        self.in_channels = init_ch
        self.out_channels = init_ch
        self.num_blocks = num_blocks
        self.init_ch = init_ch
        self.c1 = ConvBNRelu(init_ch)
        self.blocks = tf.keras.models.Sequential()
        for block_id in range(num_blocks):
            for layer_id in range(2):
                if layer_id == 0:
                    block = InceptionBlk(self.out_channels, strides=2)
                else:
                    block = InceptionBlk(self.out_channels, strides=1)
                self.blocks.add(block)
            # enlarger out_channels per block
            self.out_channels *= 2
        self.p1 = GlobalAveragePooling2D()
        self.f1 = Dense(num_classes, activation='softmax')

    def call(self, x):
        x = self.c1(x)
        x = self.blocks(x)
        x = self.p1(x)
        y = self.f1(x)
        return y

參數(shù) num_layers 代表 InceptionNet 的 Block 數(shù),每個(gè) Block 由兩個(gè)基本單元構(gòu)成,每經(jīng) 過一個(gè) Block,特征圖尺寸變?yōu)?1/2,通道數(shù)變?yōu)?2 倍;num_classes 代表分類數(shù),對(duì)于 cifar10 數(shù)據(jù)集來說即為 10;init_ch 代表初始通道數(shù),也即 InceptionNet 基本單元的初始卷積核個(gè)數(shù)。
InceptionNet 網(wǎng)絡(luò)不再像 VGGNet 一樣有三層全連接層(全連接層的參數(shù)量占 VGGNet 總參數(shù)量的 90 %),而是采用“全局平均池化+全連接層”的方式,這減少了大量的參數(shù)。

5.15ResNet

層間殘差跳連,引入前方信息,減少梯度消失,使神經(jīng)網(wǎng)絡(luò)層數(shù)變身成為可能


ResNet塊
ResNet
class ResnetBlock(Model):

    def __init__(self, filters, strides=1, residual_path=False):
        super(ResnetBlock, self).__init__()
        self.filters = filters
        self.strides = strides
        self.residual_path = residual_path

        self.c1 = Conv2D(filters, (3, 3), strides=strides, padding='same', use_bias=False)
        self.b1 = BatchNormalization()
        self.a1 = Activation('relu')

        self.c2 = Conv2D(filters, (3, 3), strides=1, padding='same', use_bias=False)
        self.b2 = BatchNormalization()

        # residual_path為True時(shí),對(duì)輸入進(jìn)行下采樣,即用1x1的卷積核做卷積操作,保證x能和F(x)維度相同,順利相加
        if residual_path:
            self.down_c1 = Conv2D(filters, (1, 1), strides=strides, padding='same', use_bias=False)
            self.down_b1 = BatchNormalization()
        
        self.a2 = Activation('relu')

    def call(self, inputs):
        residual = inputs  # residual等于輸入值本身,即residual=x
        # 將輸入通過卷積、BN層、激活層,計(jì)算F(x)
        x = self.c1(inputs)
        x = self.b1(x)
        x = self.a1(x)

        x = self.c2(x)
        y = self.b2(x)

        if self.residual_path:
            residual = self.down_c1(inputs)
            residual = self.down_b1(residual)

        out = self.a2(y + residual)  # 最后輸出的是兩部分的和,即F(x)+x或F(x)+Wx,再過激活函數(shù)
        return out
主體
class ResNet18(Model):

    def __init__(self, block_list, initial_filters=64):  # block_list表示每個(gè)block有幾個(gè)卷積層
        super(ResNet18, self).__init__()
        self.num_blocks = len(block_list)  # 共有幾個(gè)block
        self.block_list = block_list
        self.out_filters = initial_filters
        self.c1 = Conv2D(self.out_filters, (3, 3), strides=1, padding='same', use_bias=False)
        self.b1 = BatchNormalization()
        self.a1 = Activation('relu')
        self.blocks = tf.keras.models.Sequential()
        # 構(gòu)建ResNet網(wǎng)絡(luò)結(jié)構(gòu)
        for block_id in range(len(block_list)):  # 第幾個(gè)resnet block
            for layer_id in range(block_list[block_id]):  # 第幾個(gè)卷積層

                if block_id != 0 and layer_id == 0:  # 對(duì)除第一個(gè)block以外的每個(gè)block的輸入進(jìn)行下采樣
                    block = ResnetBlock(self.out_filters, strides=2, residual_path=True)
                else:
                    block = ResnetBlock(self.out_filters, residual_path=False)
                self.blocks.add(block)  # 將構(gòu)建好的block加入resnet
            self.out_filters *= 2  # 下一個(gè)block的卷積核數(shù)是上一個(gè)block的2倍
        self.p1 = tf.keras.layers.GlobalAveragePooling2D()
        self.f1 = tf.keras.layers.Dense(10, activation='softmax', kernel_regularizer=tf.keras.regularizers.l2())

    def call(self, inputs):
        x = self.c1(inputs)
        x = self.b1(x)
        x = self.a1(x)
        x = self.blocks(x)
        x = self.p1(x)
        y = self.f1(x)
        return y


model = ResNet18([2, 2, 2, 2])
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容