第J7周:对于ResNeXt-50算法的思考

>- **🍨 本文为[🔗365天深度学习训练营](https://mp.weixin.qq.com/s/0dvHCaOoFnW8SCp3JpzKxg "🔗365天深度学习训练营")中的学习记录博客**

>- **🍖 原作者:[K同学啊](https://mtyjkh.blog.csdn.net/ "K同学啊")**

一、代码复现

参考上节课程 第J6周:ResNeXt-50实战解析(pytorch版) 第J6周:ResNeXt-50实战解析(TensorFlow版)的内容,复现ResNeXt-50模型的代码如下:

1、引入命名空间

复制代码
import numpy as np
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.utils import to_categorical
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Input,Dense,Dropout,Conv2D,MaxPooling2D,Flatten,GlobalAvgPool2D
from tensorflow.keras.layers import concatenate,BatchNormalization,Activation,Add,ZeroPadding2D,Lambda
from tensorflow.keras.layers import ReLU
from tensorflow.keras.optimizers import Adam
import matplotlib.pyplot as plt
from tensorflow.keras.callbacks import LearningRateScheduler
from tensorflow.keras.models import Model

2、建立分组卷积模块

复制代码
#定义分组卷积
# ----------------------- #
#   groups代表多少组
#   g_channels代表每组的特征图数量
# ----------------------- #
def grouped_convolution_block(init_x,strides,groups,g_channels):
    group_list=[]
    #分组进行卷积
    for c in range(groups):
        #分组取出数据
        x=Lambda(lambda x: x[:,:,:,c*g_channels:(c+1)*g_channels])(init_x)
        #分组进行卷积
        x=Conv2D(filters=g_channels,kernel_size=(3,3),strides=strides,padding='same',use_bias=False)(x)
        #存入list
        group_list.append(x)
    #合并list中的数据
    group_merage=concatenate(group_list,axis=3)
    x=BatchNormalization(epsilon=1.001e-5)(group_merage)
    x=ReLU()(x)
    return x

3、建立残差单元模块

复制代码
#定义残差单元
def block(x,filters,strides=1,groups=32,conv_shortcut=True):
    
    if conv_shortcut:
        shortcut=Conv2D(filters*2,kernel_size=(1,1),strides=strides,padding='same',use_bias=False)(x)
        # eosilon为BN公式中防止分母为零的值
        shortcut=BatchNormalization(epsilon=1.001e-5)(shortcut)
    else:
        # identity_shortcut
        shortcut=x
    
    # 三层卷积层
    x=Conv2D(filters=filters,kernel_size=(1,1),strides=1,padding='same',use_bias=False)(x)
    x=BatchNormalization(epsilon=1.001e-5)(x)
    x=ReLU()(x)
    # 计算每组的通道数
    g_channels=int(filters/groups)
    # 进行分组卷积
    x=grouped_convolution_block(x,strides,groups,g_channels)
    
    x=Conv2D(filters=filters*2,kernel_size=(1,1),strides=1,padding='same',use_bias=False)(x)
    x=BatchNormalization(epsilon=1.001e-5)(x)
    x=Add()([x,shortcut])
    x=ReLU()(x)
    return x

4、堆叠残差单元

每个stack的第一个block的输入和输出的shape是不一致的,所以残差连接都需要使用1*1卷积升维后才能进行Add操作。

而其他block的输入和输出的shape是一致的,所以可以直接执行Add操作。

复制代码
# 堆叠残差单元
def stack(x,filters,blocks,strides,groups=32):
    # 每个stack的第一个block的残差连接都需要使用1*1卷积升堆
    x=block(x,filters,strides=strides,groups=groups)
    for i in range(blocks):
        x=block(x,filters,groups=groups,conv_shortcut=False)
    return x

5、搭建ResNeXt-50 网络模型

复制代码
# 定义ResNext50(32*4d)网络
def ResNext50(input_shape,num_classes):
    inputs=Input(shape=input_shape)
    # 填充3圈0,[224,224,3]->[230,230,3]
    x=ZeroPadding2D((3,3))(inputs)
    x=Conv2D(filters=64,kernel_size=(7,7),strides=2,padding='valid')(x)
    x=BatchNormalization(epsilon=1.001e-5)(x)
    x=ReLU()(x)
    # 填充1圈0
    x=ZeroPadding2D((1,1))(x)
    x=MaxPooling2D(pool_size=(3,3),strides=2,padding='valid')(x)
    # 堆叠残差结构
    x=stack(x,filters=128,blocks=2,strides=1)
    x=stack(x,filters=256,blocks=3,strides=2)
    x=stack(x,filters=512,blocks=5,strides=2)
    x=stack(x,filters=1024,blocks=2,strides=2)
    # 根据特征图大小进行全局平均池化
    x=GlobalAvgPool2D()(x)
    x=Dense(num_classes,activation='softmax')(x)
    # 定义模型
    model=Model(inputs=inputs,outputs=x)
    return model

上面不使用ZeroPadding2D也是可以的,令第一个卷积和池化的padding='same'即可。

6、查看模型摘要

二、问题引入

1、问题

如果conv_shortcut=False,那么执行"x=Add()..."语句时,通道数不一致的,为什么不会报错?

2、思考

首先,联想到Python中numpy数组的广播机制,即允许矩阵在不同维度下进行加减。这点在《deep learning》这本书上有明确显示。如下图所示:

或参考相关文章:

Python 中矩阵或者数组相减的法则

numpy之矩阵相减


其次,查阅TensorFlow和pytorch的教程文档,发现两者都存在广播机制。所以,在ResNet网络中,如果定义的残差单元块中 conv_shortcut=False,那么在执行 x = Add()(x, shortcut) 语句时,即使通道数不一致也不会报错的原因如下:

在这种情况下,虽然shortcut的维度不同于x的维度,但是,由于Add()方法具有广播机制,TensorFlow会隐式地把一个张量的维度调整到与另一个张量相匹配的维度以实现维度兼容(这种操作指的是较小张量的形状会被扩展以匹配较大张量的形状)。

所以,在上述的操作中,即使由于通道数不同也不会发生报错的现象。

相关推荐
爱睡懒觉的焦糖玛奇朵1 分钟前
【从视频到数据集:焦糖玛奇朵的魔法工具Dataset Cleaner】
人工智能·python·学习·算法·yolo·音视频
xjxijd3 分钟前
行为感知算法赋能运维,提前预判硬件故障与异常访问
运维·算法
江屿风5 分钟前
C++图论基础拓扑排序经典OJ题流食般投喂
开发语言·c++·笔记·算法·图论
C+-C资深大佬8 分钟前
C++ 数字与字符串互转
java·c++·算法
满怀冰雪14 分钟前
第12篇-二分答案法-当答案不好求时如何反向搜索
java·算法
KaMeidebaby16 分钟前
卡梅德生物技术快报|兔单克隆抗体应用实战:禽源病原 IFA 检测全流程拆解
前端·人工智能·物联网·算法·百度
CC数学建模19 分钟前
2026年第十六届APMCM 亚太地区大学生数学建模竞赛(中文赛项)赛题A题:自来水厂水质预测与评估完整思路、代码、模型、文章,全网首发高质量分享!
python·算法·数学建模
折哥的程序人生 · 物流技术专研8 小时前
Java面试85题图解版 · 特别篇:2026后端高频面试题复盘(算法底层逻辑+高并发架构设计全解析,附Java实战代码)
java·网络·数据库·算法·面试
想吃火锅100510 小时前
【leetcode】14.最长公共前缀js
算法·leetcode·职场和发展
云絮.11 小时前
数据库操作
数据库·mysql·算法·oracle