tensorflow--經(jīng)典網(wǎng)絡(luò):LeNet、AlexNet、VggNet、InceptionNet、ResNet

1、LeNet

LeNet卷積神經(jīng)網(wǎng)絡(luò)是LeCun于1998年提出,是卷積神經(jīng)網(wǎng)絡(luò)的開篇之作。通過共享卷積核減少了網(wǎng)絡(luò)的參數(shù)。

LeNet.png

在統(tǒng)計(jì)卷積神經(jīng)網(wǎng)絡(luò)層數(shù)時(shí),一般只統(tǒng)計(jì)卷積計(jì)算層和全連接計(jì)算層,其余操作可以認(rèn)為是卷積計(jì)算層的附屬。LeNet一共有5層網(wǎng)絡(luò):C1卷積層,C3卷積層,C5、F6、Output三層全連接層。

  • C1卷積:
    • C:6個(gè)5*5的卷積核,步長(zhǎng)為1,不使用全零填充
    • B:不使用批標(biāo)準(zhǔn)化(LeNet提出時(shí)還沒有BN操作)
    • A: sigmoid激活函數(shù)
    • P: 最大值池化,2*2池化核,步長(zhǎng)為2,不使用全零填充
    • D: 沒有舍棄
  • C3卷積:
    • C:16個(gè)5*5的卷積核,步長(zhǎng)為1,不使用全零填充
    • B:不使用批標(biāo)準(zhǔn)化(LeNet提出時(shí)還沒有BN操作)
    • A: sigmoid激活函數(shù)
    • P: 最大值池化,2*2池化核,步長(zhǎng)為2,不使用全零填充
    • D: 沒有舍棄
  • C5全連接:120個(gè)神經(jīng)元,sigmoid激活函數(shù)
  • F6全連接:84個(gè)神經(jīng)元,sigmoid激活函數(shù)
  • Output:10個(gè)神經(jīng)元,softmax激活函數(shù)
    運(yùn)行代碼如下(幾乎model不一樣):
import tensorflow as tf
from tensorflow.keras import Model
from tensorflow.keras.layers import Conv2D, BatchNormalization, Activation, MaxPool2D, Dropout, Flatten, Dense
import matplotlib.pyplot as plt

import os


# 加載數(shù)據(jù)集
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

class LeNet(Model):
    def __init__(self):
        super(LeNet, 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

        
# model = BaseLine()
model = LeNet()

# 配置訓(xùn)練方法
model.compile(
    optimizer="adam",
    loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False),
    metrics=["sparse_categorical_accuracy"]
)

# 斷點(diǎn)續(xù)訓(xùn),讀取模型
# checkpoint_save_path = "cifar10/BaseLine.ckpt"
checkpoint_save_path = "cifar10/LeNet.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
)

# 訓(xùn)練模型
history = model.fit(x_train, y_train, batch_size=32, epochs=5, validation_data=(x_test, y_test),
                    validation_freq=1, callbacks=[cp_callback])

# 打印網(wǎng)絡(luò)結(jié)構(gòu)和參數(shù)
model.summary()

# 寫入?yún)?shù)
with open("cifar10_lenet_weights.txt", "w") as f:
    for v in model.trainable_variables:
        f.write(str(v.name) + "\n")
        f.write(str(v.shape) + "\n")
        f.write(str(v.numpy()) + "\n")


# 顯示訓(xùn)練和預(yù)測(cè)的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="train acc")
plt.plot(val_acc, label="validation acc")
plt.title("train & validation acc")
plt.legend()
plt.subplot(1, 2, 2)
plt.plot(loss, label="train loss")
plt.plot(val_loss, label="validation loss")
plt.title("train & validation loss")
plt.legend()
plt.show()

繪圖結(jié)果如下:


lenetplot.png

2、AlexNet

AlexNet網(wǎng)絡(luò)誕生于2012年,當(dāng)年ImageNet競(jìng)賽的冠軍,top5錯(cuò)誤率為16.4%。AlexNet使用relu激活函數(shù),提升了訓(xùn)練速度;使用dropout,緩解了過擬合。

AlexNet.png

AlexNet使用了八層網(wǎng)絡(luò)結(jié)構(gòu):

  • 第一層卷積:
    1. C: 96個(gè)3*3卷積核,步長(zhǎng)為1,不使用全零填充
    2. B: 局部相應(yīng)標(biāo)準(zhǔn)化(LRN,現(xiàn)在使用較少,改用BN)
    3. A: relu激活函數(shù)
    4. P: 最大值池化,3*3池化核,步長(zhǎng)2
    5. D:不舍棄
  • 第二層卷積:
    1. C: 256個(gè)3*3卷積核,步長(zhǎng)為1,不使用全零填充
    2. B: 局部相應(yīng)標(biāo)準(zhǔn)化(LRN,現(xiàn)在使用較少,改用BN)
    3. A: relu激活函數(shù)
    4. P: 最大值池化,3*3池化核,步長(zhǎng)2
    5. D:不舍棄
  • 第三層卷積:
    1. C: 384個(gè)3*3卷積核,步長(zhǎng)為1,使用全零填充
    2. B: 不標(biāo)準(zhǔn)化
    3. A: relu激活函數(shù)
    4. P: 不池化
    5. D:不舍棄
  • 第四層卷積:
    1. C: 384個(gè)3*3卷積核,步長(zhǎng)為1,使用全零填充
    2. B: 不標(biāo)準(zhǔn)化
    3. A: relu激活函數(shù)
    4. P: 不池化
    5. D:不舍棄
  • 第五層卷積:
    1. C: 256個(gè)3*3卷積核,步長(zhǎng)為1,使用全零填充
    2. B: 不標(biāo)準(zhǔn)化
    3. A: relu激活函數(shù)
    4. P: 最大值池化,池化核3*3,步長(zhǎng)2
    5. D:不舍棄
  • 第六層全連接:2048個(gè)神經(jīng)元,relu激活函數(shù),0.5的舍棄
  • 第七層全連接:2048個(gè)神經(jīng)元,relu激活函數(shù),0.5的舍棄
  • 第八層全連接:10個(gè)神經(jīng)元,softmax激活函數(shù)
    相關(guān)代碼如下(只貼變更部分):
class AlexNet(Model):
    def __init__(self):
        super(AlexNet, 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")

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

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

        x = self.c3(x)

        x = self.c4(x)

        x = self.c5(x)
        x = self.p3(x)

        x = self.flatten(x)
        x = self.f1(x)
        x = self.d1(x)

        x = self.f2(x)
        x = self.d2(x)

        y = self.f3(x)
        return y

ps:AlexNet速度明顯慢了很多。LeNet大概喝口水就跑完了,這個(gè)AlexNet跑了有50min左右(畢竟神經(jīng)元個(gè)數(shù)明顯增多了嘛)·····最后結(jié)果有九百多萬個(gè)參數(shù)····
pps: 我把GPU相關(guān)軟件裝好了———原來8G的CPU跑50分鐘,現(xiàn)在2G的GPU跑了大概5分鐘-----55555----GPU萬歲

3、VGGNet

VGGNet是2014年ImageNet競(jìng)賽的亞軍,top5錯(cuò)誤率減小到了7.3%。VGGNet使用小尺寸卷積核,在減少參數(shù)的同時(shí),提高了識(shí)別準(zhǔn)確率。VGGNet的網(wǎng)絡(luò)結(jié)構(gòu)規(guī)整,非常適合硬件加速。
VGGNet有16層網(wǎng)絡(luò)結(jié)構(gòu):

VGGNet.png

相關(guān)代碼如下(沒信心跑這個(gè)模型):

class VGGNet(Model):
    def __init__(self):
        super(VGGNet, self).__init__()
        self.c1 = Conv2D(filters=64, kernel_size=(3, 3), padding='same')
        self.b1 = BatchNormalization()
        self.a1 = Activation('relu')

        self.c2 = Conv2D(filters=64, kernel_size=(3, 3), padding='same', )
        self.b2 = BatchNormalization()
        self.a2 = Activation('relu')
        self.p1 = MaxPool2D(pool_size=(2, 2), strides=2, padding='same')
        self.d1 = Dropout(0.2)

        self.c3 = Conv2D(filters=128, kernel_size=(3, 3), padding='same')
        self.b3 = BatchNormalization()
        self.a3 = Activation('relu')

        self.c4 = Conv2D(filters=128, kernel_size=(3, 3), padding='same')
        self.b4 = BatchNormalization()
        self.a4 = Activation('relu')
        self.p2 = MaxPool2D(pool_size=(2, 2), strides=2, padding='same')
        self.d2 = Dropout(0.2)

        self.c5 = Conv2D(filters=256, kernel_size=(3, 3), padding='same')
        self.b5 = BatchNormalization()
        self.a5 = Activation('relu')

        self.c6 = Conv2D(filters=256, kernel_size=(3, 3), padding='same')
        self.b6 = BatchNormalization()
        self.a6 = Activation('relu')

        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()
        self.a8 = Activation('relu')

        self.c9 = Conv2D(filters=512, kernel_size=(3, 3), padding='same')
        self.b9 = BatchNormalization()
        self.a9 = Activation('relu')

        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()
        self.a11 = Activation('relu')

        self.c12 = Conv2D(filters=512, kernel_size=(3, 3), padding='same')
        self.b12 = BatchNormalization()
        self.a12 = Activation('relu')
        
        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')

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

        x = self.c3(x)
        x = self.b3(x)
        x = self.a3(x)
        x = self.c4(x)
        x = self.b4(x)
        x = self.a4(x)
        x = self.p2(x)
        x = self.d2(x)

        x = self.c5(x)
        x = self.b5(x)
        x = self.a5(x)
        x = self.c6(x)
        x = self.b6(x)
        x = self.a6(x)
        x = self.c7(x)
        x = self.b7(x)
        x = self.a7(x)
        x = self.p3(x)
        x = self.d3(x)

        x = self.c8(x)
        x = self.b8(x)
        x = self.a8(x)
        x = self.c9(x)
        x = self.b9(x)
        x = self.a9(x)
        x = self.c10(x)
        x = self.b10(x)
        x = self.a10(x)
        x = self.p4(x)
        x = self.d4(x)

        x = self.c11(x)
        x = self.b11(x)
        x = self.a11(x)
        x = self.c12(x)
        x = self.b12(x)
        x = self.a12(x)
        x = self.c13(x)
        x = self.b13(x)
        x = self.a13(x)
        x = self.p5(x)
        x = self.d5(x)

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

4、InceptionNet

InceptionNet誕生于2014年,是當(dāng)年ImageNet競(jìng)賽的冠軍,top5錯(cuò)誤率為6.67%。InceptionNet引入了Inception結(jié)構(gòu)塊,在同一網(wǎng)絡(luò)內(nèi)使用了不同尺寸的卷積核,提升了模型的感知力;使用了批標(biāo)準(zhǔn)化,緩解了梯度消失。
InceptionNet的核心是它的基本單元Inception結(jié)構(gòu)塊,無論是GoogleNet,也就是Inception v1,還是InceptionNet的后續(xù)版本,比如v2、v3、v4版本,都是基于Inception結(jié)構(gòu)塊搭建的網(wǎng)絡(luò)。Inception結(jié)構(gòu)塊在同一層網(wǎng)絡(luò)中使用了多個(gè)尺寸的卷積核,可以提取不同尺寸的特征。通過11的卷積核,作用到輸入特征圖的每個(gè)像素點(diǎn);通過設(shè)定少于輸入特征圖深度的11卷積核個(gè)數(shù),減少了輸出特征圖深度,起到了降維的作用,減少了參數(shù)量和計(jì)算量。

InceptionNet.png

Inception結(jié)構(gòu)塊包含四個(gè)分支:

  • 經(jīng)過1 * 1卷積核輸出到卷積連接器(最左列)
  • 經(jīng)過1 * 1卷積核配合3*3卷積核輸出到卷積連接器(第二列)
  • 經(jīng)過1 * 1卷積核配合5*5卷積核輸出到卷積連接器(第三列)
  • 經(jīng)過3 * 3最大池化核配合1*1卷積核輸出到卷積連接器(第四列)

送到卷積連接器的特征尺寸相同,卷積連接器會(huì)把收到的這四路特征數(shù)據(jù)按深度方向拼接,形成Inception結(jié)構(gòu)塊的輸出。
代碼如下:

# 由于Inception結(jié)構(gòu)塊中的卷積均采用了CBA結(jié)構(gòu),先卷積,再BN,再采用relu激活函數(shù),
# 所以為了代碼復(fù)用,定義一個(gè)新的類ConvBNrelu
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, kernel_size=kernelsz, strides=strides, padding=padding),
            BatchNormalization(),
            Activation("relu")
        ])

    def call(self, x):
        x = self.model(x)
        return x


class InceptionBlk(Model):
    def __init__(self, ch, strides=1):
        super(InceptionBlk, self).__init__()
        self.ch = ch
        self.strides = strides
        # 第一個(gè)分支
        self.c1 = ConvBNRelu(ch, kernelsz=1, strides=strides)

        # 第二個(gè)分支
        self.c2_1 = ConvBNRelu(ch, kernelsz=1, strides=strides)
        self.c2_2 = ConvBNRelu(ch, kernelsz=3, strides=1)

        # 第三個(gè)分支
        self.c3_1 = ConvBNRelu(ch, kernelsz=1, strides=strides)
        self.c3_2 = ConvBNRelu(ch, kernelsz=5, strides=1)

        # 第四個(gè)分支
        self.p4_1 = MaxPool2D(3, strides=1, padding="same")
        self.c4_2 = ConvBNRelu(ch, kernelsz=1, strides=strides)

    def call(self, x):
        """分別經(jīng)歷四個(gè)分支的傳播"""
        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)

        # 將四個(gè)分支的輸出堆疊在一起,并指定堆疊的維度是沿深度方向
        x = tf.concat([x1, x2_2, x3_2, x4_2], axis=3)
        return x

有了Inception結(jié)構(gòu)塊后,就可以搭建出一個(gè)精簡(jiǎn)版本的InceptionNet了。


Inception10.png

代碼如下:


class Inception10(Model):
    def __init__(self, num_blocks, num_classes, init_ch=16, **kwargs):
        """
        :param num_blocks: block數(shù)量
        :param num_classes: n分類
        :param init_ch: 默認(rèn)輸出深度16
        :param 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)  # 其余參數(shù)已默認(rèn)

        # 4個(gè)Inception結(jié)構(gòu)塊順序相連,每?jī)蓚€(gè)結(jié)構(gòu)塊組成一個(gè)block。
        # 每個(gè)block中第一個(gè)結(jié)構(gòu)塊步長(zhǎng)為2,第二個(gè)步長(zhǎng)為1。這使得
        # 第一個(gè)結(jié)構(gòu)塊輸出特征圖尺寸減半。因此把輸出特征圖深度加深,盡可能
        # 保證特征抽取中信息的承載量一致
        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)
            # block_0通道數(shù)為16,經(jīng)過4個(gè)分支,輸出深度為4*16=64;由于*=2了,所以
            # block_1通道數(shù)為32,同樣經(jīng)過4個(gè)分支,輸出深度為4*32=128;
            self.out_channels *= 2
        self.p1 = tf.keras.layers.GlobalAveragePooling2D()  # 將128通道的數(shù)據(jù)送入平均池化
        self.f1 = Dense(num_classes, activation="softmax")  # 送入10分類的全連接

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


model = Inception10(num_blocks=2, num_classes=10)

5、ResNet

ResNet誕生于2015年,是當(dāng)年ImageNet競(jìng)賽的冠軍,Top5錯(cuò)誤率為3.57%。ResNet提出了層間殘差跳連,引入了前方信息,緩解梯度消失,使神經(jīng)網(wǎng)絡(luò)層數(shù)增加成為可能。

net_comparision.png

通過上圖可見,在探索卷積實(shí)現(xiàn)特征提取的道路上,通過加深網(wǎng)絡(luò)層數(shù),可以取得越來越好的效果。但是單純的堆積網(wǎng)絡(luò)模型的層數(shù)會(huì)使模型退化,以至于后面的特征丟失了前面特征的原本模樣。于是,ResNet的作者用了一根跳連線,將前面的特征直接接到了后面,使得輸出Fx包含了堆疊卷積的非線性輸出F(x)和跳過這兩層堆疊卷積直接連接過來的恒等映射x,讓它們對(duì)應(yīng)元素相加。這一操作,有效緩解了神經(jīng)網(wǎng)絡(luò)模型堆疊導(dǎo)致的退化,使得神經(jīng)網(wǎng)絡(luò)可以向著更深層級(jí)發(fā)展。
res塊.png

ResNet塊中有兩種情況:

  • 實(shí)線表示,兩層堆疊卷積沒有改變特征圖的維度
  • 虛線表示,兩層堆疊卷積改變了特征圖的維度,需要借助1*1的卷積來調(diào)整x的維度,使W(x)與F(x)的維度一致


    res塊2.png

    ResNet塊有兩種形式:

  • 一種在堆疊前后維度相同
  • 另一種在堆疊卷積前后維度不同


    ResNetBlock.png

封裝的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()

        if residual_path:  # 堆疊卷積層前后維度不同為True
            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

        x = self.c1(inputs)
        x = self.b1(x)
        x = self.a1(x)

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

        if self.residual_path:  # 堆疊卷積層前后維度不同為True
            residual = self.down_c1(inputs)
            residual = self.down_b1(residual)

        out = self.a2(y + residual)  # 堆疊卷積和跳連卷積相加
        return out

根據(jù)ResNet塊,可以搭建一個(gè)ResNet18的神經(jīng)網(wǎng)絡(luò)


ResNet18.png

代碼如下:


class ResNet18(Model):
    def __init__(self, block_list, inital_filters=64):
        """
        :param block_list: 每個(gè)block有幾個(gè)卷積層
        :param inital_filters:
        """
        super(ResNet18, self).__init__()
        self.num_blocks = len(block_list)  # 共有幾個(gè)block
        self.block_list = block_list
        self.out_filters = inital_filters

        self.c1 = Conv2D(self.out_filters, (3, 3), strides=1, padding="same",
                         use_bias=False, kernel_initializer="he_normal")
        self.b1 = BatchNormalization()
        self.a1 = Activation("relu")

        self.blocks = tf.keras.models.Sequential()

        # 構(gòu)建ResNet網(wǎng)絡(luò)結(jié)構(gòu): 每一個(gè)ResNet塊有兩層卷積,
        for block_id in range(len(block_list)):
            for layer_id in range(block_list[block_id]):
                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)
            self.out_filters *= 2  # 下一個(gè)block的卷積核數(shù)是上一個(gè)block的2倍
        self.p1 = tf.keras.layers.GlobalAveragePooling2D()
        self.f1 = Dense(10)

    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)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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