【深度学习笔记】task 05卷积网络


依依旧是跟着曹健老师的课程学习,这一节到了大名鼎鼎的卷积神经网络,给出了各种经典卷积神经网络模型代码,非常值得学习,不过对于几个经典卷积神经网络只是模型结构的介绍,对其理论原理没有进行深入

5. 卷积神经网络

在这里插入图片描述

5.1 卷积计算

在这里插入图片描述
实际项目的图片多是高分辨率的彩色图片,待优化参数过多,容易导致模型过拟合,为了减少待训练参数,我们在实际应用中会对原始图片进行特征提取,再把提取到的特征送给全连接网络
在这里插入图片描述
卷积计算是一种有效的特征提取方法
在这里插入图片描述
如果输入特征是单通道灰度图可以使用深度为1的单通道卷积核;如果输入特征是三通道彩色图,可以使用333的卷积核或者553的卷积核,总之卷积核的深度一定要和输入特征的深度(通道数)一致

在这里插入图片描述
卷积就是利用立体卷积核实现了参数的空间共享
在这里插入图片描述
卷积计算过程

  • 单通道图
    在这里插入图片描述

如下面一个551的单通道图像,选用了3*3的卷积核,滑动步长是1,每滑动一步输入特征图与卷卷积核中的9个元素重合,它们对应元素相乘再加上偏置项b
在这里插入图片描述
红绿蓝三层分别与卷积核的三层数据重合,它们对应元素相乘求和再加上偏置项b(红绿蓝三层的都计算上)到的输出中的一个特征值

当输入遍历完得到一个输出特征图,完成一个卷积计算过程,当有n个卷积核就会有n个输出特征图
在这里插入图片描述


5.2 感受野

第二个图的一个小方块是由原图33的大方块映射到的,所以其感受野为3,第三张图的一张小方块是由原图55的映射到的,所以它的感受野为5
在这里插入图片描述
这个感受野也是5在这里插入图片描述
第一个图经过两个33的卷积核作用
第二个图直接经过一个5
5的卷积核的作用,但是最终效果时一样的

但是计算量上不一样,3*3卷积核的计算量更小
在这里插入图片描述

5.3 全零填充padding

有时候我们希望卷积计算保持输入特征图的尺寸不变,可以使用全零填充,在输入特征图周围填充0
如下面这个551的输入特征图经过全零填充后再通过331的卷积核进行步长为1的卷积计算,输出特征图仍是551
在这里插入图片描述
卷积输出特征图维度的计算公式,即计算输出特征图的变长
在这里插入图片描述
在TF中使用全零填充使用参数padding='SAME'(全零填充)或padding='VALID'(不全零填充)表示
在这里插入图片描述

5.4 TF描述卷积计算层

tf.keras.layers.Conv2D(
filters=卷积核个数,
kernel_size=卷积核尺寸,正方形写核长整数,或(核高h,核宽w)
strides=滑动步长,#横纵向相同写步长整数,或(纵向步长h,横向步长w),默认1
padding='same'or'valid',#使用全零填充是'same',不使用时'valid'(默认)
activation='relu' or 'sigmoid' or 'tanh' or 'softmax'#如有卷积后还有批标准化操作,不在这里进行激活,不写激活函数
input_shape=(,,通道数)#输入特征维度,可以忽略

黄框括起来的是几种写法,推荐第三种传递参数哦的方式,代码可读性更强
在这里插入图片描述

5.5 批标准化(Batch Normalization,BN)

神经网络对0附近的数据更敏感,但是随着网络层数的增加,特征数据会出现便宜0均值的情况,标准化可以使数据符合以0为均值

  • 标准化:使数据符合0均值,1为标准差的分布,把偏移的数据重新拉回0附近
  • 批标准化:对一小批数据(batch),做标准化处理
    批标准化(BN)常用在卷积操作和激活操作之间,可以通过下面的式子计算批标准化后的输出特征
    在这里插入图片描述
    其中在这里插入图片描述
    通过批标准化操作,数据重新分布到了激活函数的线性区,使得输入数据的微小变化,更明显的体现到激活函数的输出,提升了激活函数对数据数据的区分力
    在这里插入图片描述
    但是这种简单的数据标准化,使得数据完全满足标准正态分布,集中在激活函数的线性区域,使得激活函数丧失了非线性特性,因此在BN操作中为每个卷积核引入了两个可训练参数
    反向传播时候引入的两个参数和其他参数一样会被训练优化,优化特征数据分布的宽窄和偏移量
    在这里插入图片描述
    BN层位于卷积层之后,激活层之前
    在这里插入图片描述
  • TF 描述批标准化
tf.keras.layers.BatchNormalization()

例如

在这里插入图片描述

5.6 池化Pooling

池化操作用于减少神经网络中特征数据量,池化的主要方法有

  • 最大池化:可用于提取图片纹理
  • 均值池化:可保留背景特征
    在这里插入图片描述
    TF描述池化
  • 最大池化tf.keras.layers.MaxPool2D
  • 均值池化tf.keras.layers.AveragePooling2d
    在这里插入图片描述

5.7 舍弃Dropout

为了缓解神经网络过拟合,在神经网络训练时候,将隐藏层一部分神经元按照一定的概率从神经网络中暂时舍弃,神经网络使用时,被舍弃的神经元恢复链接

在这里插入图片描述

  • TF描述池化
    tf.keras.layer.Dropout(舍弃的概率)
    在这里插入图片描述

5.8 卷积神经网络

卷积神经网络:借助卷积核提取特征后,送入全连接网络
卷积神经网络的主要模块
在这里插入图片描述
卷积是什么?即编写卷积神经网络的八股套路
卷积就是特征提取器,就是CBAPD
在这里插入图片描述

5.9 CIFAR10数据集

共有5万张训练集和1万张测试集的10类数据,对应标签0-9
在这里插入图片描述

  • 导入cifar10数据集
import tensorflow as tf
from matplotlib import pyplot as plt
import numpy as np

np.set_printoptions(threshold=np.inf)

cifar10 = tf.keras.datasets.cifar10
(x_train, y_train), (x_test, y_test) = cifar10.load_data()

# 可视化训练集输入特征的第一个元素
plt.imshow(x_train[0])  # 绘制图片
plt.show()

# 打印出训练集输入特征的第一个元素
print("x_train[0]:\n", x_train[0])
# 打印出训练集标签的第一个元素
print("y_train[0]:\n", y_train[0])

# 打印出整个训练集输入特征形状
print("x_train.shape:\n", x_train.shape)
# 打印出整个训练集标签的形状
print("y_train.shape:\n", y_train.shape)
# 打印出整个测试集输入特征的形状
print("x_test.shape:\n", x_test.shape)
# 打印出整个测试集标签的形状
print("y_test.shape:\n", y_test.shape)

得到的x_train[0]是32行32列的RGB三通道数据,y_train[0]是数值6代表青蛙

5.10 卷积神经网络搭建示例

通过卷积神经网络来训练cifar10数据集,搭建一个一层卷积,两层全连接的网络
其中卷积层包括

  • 5*5的卷积核,步长为6
  • 2*2的池化核,步长为2
    在这里插入图片描述
    搭建神经网络诀窍CBAPD
    在这里插入图片描述
    带入一下得到
    在这里插入图片描述
    由于网络相对复杂,此处采用class类搭建网络结构
    在这里插入图片描述
    使用六步法可以写出相应代码,最重要的就是第三部分
##1 导入相关包
import tensorflow as tf
import os
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

np.set_printoptions(threshold=np.inf)

## 2.划分训练集和测试集
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

## 3.定义神经网络模型
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层

        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()

## 4.设置神经网络参数
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_best_only=True,
                                              save_weights_only=True)

##5.训练模型
history = model.fit(x_train, y_train, batch_size=32, epochs=5, validation_data=(x_test, y_test), validation_freq=1,
                    callbacks=[cp_callback])

##6.打印结果
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()
#结果可视化
# 显示训练集和验证集的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.11 经典卷积网络

在这里插入图片描述

5.11.1 LeNet

LeNet由Yann LeCun提出是卷积网络的开篇之作,通过共享卷积核减少了网络的参数
在统计卷积网络层数时候,一般只统计卷积计算层和全连接层
LeNet一共有五层网络:卷积(CBAPD)-卷积(CBAPD)-全连接-全连接-全连接
LeNet时期主流激活函数为sigmoid,没有dropout,没有全零填充
在这里插入图片描述

对应代码为:
在这里插入图片描述

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.11.2 AlexNet

AlexNet诞生于2012年,是Hinton的代表作之一,也是当年ImageNet竞赛冠军

  • 使用relu激活函数提高了计算效率
  • 使用dropout缓解了过拟合
    AlexNet共有八层
    卷积层(最初始的用的LRN操作,但是现在已经被主流BN操作所替代)-卷积层-卷积层-卷积层-卷积层-全连接-全连接-全连接
    其中第三层和第四层完全一致
    在这里插入图片描述
    在这里插入图片描述
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')

    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
model=AlexNet8()

5.11.3VGGNet

VGGNet诞生于2014年,是当年ImageNet竞赛的亚军,Top5错误率减少到7.3%,
VGGNet使用小尺寸卷积核,在减少参数的同时,提高了识别准确率
VGGNet的网络结构规整,非常适合硬件加速
具体结构为两次CBA-CBAPD三次CBA-CBA-CBAPD三个全连接层,共计十六层

  • CAB
  • CBAPD
  • CBA
  • CBAPD
  • CBA
  • CBA
  • CBAPD
  • CBA
  • CBA
  • CBAPD
  • CBAPD
  • CBA
  • CBA
  • CBAPD
  • 全连接层
  • 全连接层
  • 全连接层
    在这里插入图片描述
    在这里插入图片描述
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')

    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


model = VGG16()

5.11.4 Inception Net

Inception Net也诞生于2014年,是当年ImageNet竞赛冠军,TOP5错误率为6.67%
Inception Net引入了Inception结构块

  • 在同一层网络内使用不同尺寸的卷积核,可以提取不同尺寸的特征,提升了模型感知力
  • 使用批标准化,缓解了梯度消失
    Inception Net的核心是它的基本单元Inception 结构块无论是GoogLeNet也就是Inception v1及其后续版本v2,v3,v4都是基于Inception结构块搭建的
    通过11卷积核作用到图中每个像素点,通过设定少于输入特征图深度的11卷积核个数减少了输出特诊图深度,起到了降维作用,减少了参数量和计算量

下面便是一个Inception结构块,Inception结构块包含四个分支

  • 经过1*1卷积核输出到卷积连接器
  • 经过11卷积核配合33卷积核输出到卷积连接器
  • 经过11卷积核配合55卷积核输出到卷积连接器
  • 经过33最大池化核配合11卷积核输出到卷积连接器
    送到卷积连接器的特征数据尺寸相同,卷积连接器会把收到的这四路特征按深度方向拼接形成Inception结构块的输出
    在这里插入图片描述
    Inception结构块的CBAPD描述为

在这里插入图片描述
由于Inception结构块都采用了CBA结构即先卷积在BN再relu激活函数所以可以将其定义为一个新的类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, kernelsz, strides=strides, padding=padding),
            BatchNormalization(),
            Activation('relu')
        ])

    def call(self, x):
        x = self.model(x, training=False) #在training=False时,BN通过整个训练集计算均值、方差去做批归一化,training=True时,通过当前batch的均值、方差去做批归一化。推理时 training=False效果好
        return x

整体过程如下
其中c1为第一分支,c2_1,c2_2为第二分支。。。。。
在这里插入图片描述

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


在这里插入图片描述


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

#num_classes用于指定是几分类的
model = Inception10(num_blocks=4, num_classes=10)

5.11 ResNet2015

ResNet诞生于2015年是当年ImageNet竞赛冠军,ResNet提出了层间残差跳连,引入了前方信息,缓解了梯度消失,使得神经网络层数增加成为可能

  • 可以发现网络层数加深提高识别准确率,但是单纯的堆叠网络层数会使得神经网络模型退化以至于后边的特征丢失了前边特征的原本模样
    在这里插入图片描述
    Resnet通过一根跳连线,使得前面层的特征直接跟后面层相连
    下图中的X跳过了两层,直接把信息给了我第三层
    在这里插入图片描述
    在这里插入图片描述
    resnet的两种结构,一种结构堆叠卷积前后维度相同,一种堆叠卷积前后维度不同,将这两种结构封装到下面橙色块中形成ResentBloack类
    在这里插入图片描述
    每调用一次ResNet类会生成一个黄色块
  • 如果堆叠卷积前后维度不同,residual_path=1,调用红色块中的代码使用1*1卷积操作,调整输入特征图inputs的尺寸或深度后将堆叠卷积输出特征y和if预计计算出的residual相加或激活输出
  • 如果堆叠卷积层前后维度相同,不执行红色块内的代码,直接将堆叠卷积层输出特征y和输入特征图inputs相加过激活输出
    在这里插入图片描述
    ResNet18用CBAPD表示的结构
    在这里插入图片描述
    结构:卷积层-八个ResNet块-全连接,每一个ResNet块有两层卷积,一共是是18层网络

循环此处是由参数次数决定的,这里列表赋值为2,2,2,2,所以最外层for循环执行4次,每次进来根据当前是第几个元素选择residual_path=True用虚线连接和residual_path=False用实线连接,调用ResnetBlock生产左边ResNet18结构中的一个橙色块,经过平均全局池化核全连接得到输出结果在这里插入图片描述

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时,对输入进行下采样,即用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层、激活层,计算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,再过激活函数
        return out


class ResNet18(Model):

    def __init__(self, block_list, initial_filters=64):  # block_list表示每个block有几个卷积层
        super(ResNet18, self).__init__()
        self.num_blocks = len(block_list)  # 共有几个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()
        # 构建ResNet网络结构
        for block_id in range(len(block_list)):  # 第几个resnet block
            for layer_id in range(block_list[block_id]):  # 第几个卷积层

                if block_id != 0 and layer_id == 0:  # 对除第一个block以外的每个block的输入进行下采样
                    block = ResnetBlock(self.out_filters, strides=2, residual_path=True)
                else:
                    block = ResnetBlock(self.out_filters, residual_path=False)
                self.blocks.add(block)  # 将构建好的block加入resnet
            self.out_filters *= 2  # 下一个block的卷积核数是上一个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])
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
根据引用,这篇笔记是关于卷积神经网络(CNN)的深度学习理论详解和项目实战的学习笔记。这篇笔记主要介绍了CNN在计算机视觉和图像识别中的应用。 根据引用,在CNN中,Co代表输出通道数(也称为卷积核数量),Ci代表输入通道数,Kh代表卷积核的高度,Kw代表卷积核的宽度。这些参数可以用来定义CNN的结构。 根据引用,你提到想要使用R语言实现一维CNN,并输入一些变量进行二分类。你可以按照想要的数据进行二分类,然后使用R语言编写代码来实现CNN,并添加损失函数的曲线。 希望这些信息对你有帮助!<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [深度学习卷积神经网络(CNN)全笔记,附代码](https://blog.csdn.net/weixin_58176527/article/details/125530000)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [深度学习——CNN卷积神经网络个人理解笔记汇总](https://blog.csdn.net/m0_59998867/article/details/123696930)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [语言实现一维CNN吗,想输入些变量实现二分类 就是有一些变量,想输入到cnn里算一下进行二分类,这个是想做...](https://download.csdn.net/download/Mrrunsen/88267034)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值