当前位置: 首页 > article >正文

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

  • 🍨 本文为🔗365天深度学习训练营 中的学习记录博客
  • 🍖 原作者:K同学啊

一、问题

在ResNeXt网络中定义残差单元块中,如果conv_shortcut=False,那么在执行“x=Add()…”语句时,通道数不一致的,为什么不会报错?
在这里插入图片描述

二、conv_shortcut=True

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, MaxPool2D, Flatten, GlobalAvgPool2D, 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
# 定义分组卷积
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


# 定义残差单元
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)
        # epsilon为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
# 堆叠残差单元
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
# 定义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 = MaxPool2D(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
model=ResNext50(input_shape=(224,224,3),num_classes=1000)
model.summary()

在这里插入图片描述

三、conv_shortcut=False

我这边显示的报错

在这里插入图片描述
原因分析如下:
当 conv_shortcut=False 时,shortcut 分支直接传递了输入的张量,而主分支经过卷积后改变了通道数,这导致在 Add() 操作时,主分支和快捷分支的通道数不一致,从而引发了错误:
在这里插入图片描述
解决办法:

  1. 在 block 函数中添加通道对齐逻辑
    修改 shortcut 的定义,确保它的通道数与主分支一致:
def block(x, filters, strides=1, groups=32, conv_shortcut=True):
    if conv_shortcut:
        # 使用 1x1 卷积调整通道数
        shortcut = Conv2D(filters * 2, kernel_size=(1, 1), strides=strides, padding='same', use_bias=False)(x)
        shortcut = BatchNormalization(epsilon=1.001e-5)(shortcut)
    else:
        # 如果 shortcut 分支未调整,检查是否需要升维
        shortcut = x
        if x.shape[-1] != filters * 2:
            shortcut = Conv2D(filters * 2, kernel_size=(1, 1), strides=1, padding='same', use_bias=False)(shortcut)
            shortcut = BatchNormalization(epsilon=1.001e-5)(shortcut)

    # 主分支
    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)

    # Add 操作前确保形状匹配
    x = Add()([x, shortcut])
    x = ReLU()(x)
    return x

  1. 检查 x.shape[-1] 和 filters * 2 是否一致
    如果 shortcut 的通道数和主分支的通道数不一致,使用 1x1 卷积对其调整。

保持 BatchNormalization 一致性
调整后的 shortcut 张量也需要经过 Batch Normalization,以匹配主分支的归一化效果。

四、总结

在构建 ResNeXt 模型时,conv_shortcut=False 会导致残差连接的输入 (x) 和残差分支输出 (shortcut) 的通道数不一致,从而引发形状不匹配错误。在修改代码时,我们通过以下方式解决了问题:

确保形状一致性:当 conv_shortcut=False 时,将跳跃连接中的 shortcut 设置为输入 x,避免形状不匹配。
关键点检查:在堆叠多个残差块(stack 函数)时,确保仅在第一个块中执行通道数调整操作,后续块使用 identity shortcut。
最终,通过添加条件语句确保 conv_shortcut=False 时的正确行为,模型能够顺利运行并生成合理的架构。这种方法强调了在实现残差网络时,保持跳跃连接和残差分支之间形状一致性的必要性。


http://www.kler.cn/a/406469.html

相关文章:

  • 量子感知机
  • 大数据基于Spring Boot的化妆品推荐系统的设计与实现
  • 使用 Oracle.DataAccess.Client 驱动 和 OleDB 调用Oracle 函数的区别
  • Kubernetes的pod控制器
  • 要素市场与收入分配
  • 刷题-1122
  • Linux Docker 部署 Jenkins 详解教程
  • C#里怎么样判断文件是否存在?
  • Idea修改Commit Changes模式、idea使用git缺少部分Commit Changes
  • 基本的SELECT语句
  • 在 Ubuntu 系统上安装 npm 环境以及 nvm(Node Version Manager)
  • 车企如何实现安全图纸外发管理
  • 单片机学习笔记 5. 数码管静态显示
  • Diving into the STM32 HAL-----DAC笔记
  • 设计模式:6、装饰模式(包装器)
  • 如何修复WordPress卡在维护模式
  • 适配屏幕px、rem单位换算, 将 pxToRem 函数设置为一个全局工具如:在 utils.js 文件、SCSS/Mixin 定义
  • 外卖系统开发实战:从架构设计到代码实现
  • Docker 容器自动启动设置
  • XCode Build时遇到 .entitlements could not be opened 的问题
  • 在 IDEA 中关闭 Spark 的日志输出 已解决
  • JVM(五、垃圾回收器)
  • 初级数据结构——树
  • Element-UI中el-input输入手机号时,如何限制只能输入数字
  • django基于Django的二手电子设备交易平台设计与开发
  • 服务机器人三甲坎德拉:用智能化开启售后服务新篇章