手写ResNet50——pytorch

为了加深对resnet的理解,参考他人代码重写了一份pytorch版的resnet50。

注意resnet50中,只有131卷积,即Basic

1. ConvBlock

卷积层进入的模块,第一层要利用步长为2进行下采样。

# 每个stage第一个卷积模块,主要进行下采样
class ConvBlock(nn.Module):  
    def __init__(self, in_channel, f, filters, s):
        # resnet50 只有 131卷积
        # filters传入的是一个 [filter1, filter2, filter3] 列表
        super(ConvBlock,self).__init__()
        F1, F2, F3 = filters
        self.stage = nn.Sequential(
            nn.Conv2d(in_channel,F1,1,stride=s, padding=0, bias=False), # 1*1卷积
            nn.BatchNorm2d(F1),
            nn.ReLU(True),
            nn.Conv2d(F1,F2,f,stride=1, padding=True, bias=False), # 3*3 卷积
            nn.BatchNorm2d(F2),
            nn.ReLU(True),
            nn.Conv2d(F2,F3,1,stride=1, padding=0, bias=False), # 1*1 卷积
            nn.BatchNorm2d(F3),
        )
        # 短路部分,从输入in_channel直接卷积到输出F3,恒等映射
        self.shortcut_1 = nn.Conv2d(in_channel, F3, 1, stride=s, padding=0, bias=False)
        self.batch_1 = nn.BatchNorm2d(F3)
        self.relu_1 = nn.ReLU(True)
        
    def forward(self, X):
        X_shortcut = self.shortcut_1(X)
        X_shortcut = self.batch_1(X_shortcut)
        X = self.stage(X)
        # 残差加和
        X = X + X_shortcut
        X = self.relu_1(X)
        return X    

2. stage内部的卷积模块

# stage内部的卷积模块
class IndentityBlock(nn.Module):
    def __init__(self, in_channel, f, filters):
        super(IndentityBlock,self).__init__()
        F1, F2, F3 = filters
        self.stage = nn.Sequential(
            nn.Conv2d(in_channel,F1,1,stride=1, padding=0, bias=False),
            nn.BatchNorm2d(F1),
            nn.ReLU(True),
            nn.Conv2d(F1,F2,f,stride=1, padding=True, bias=False),
            nn.BatchNorm2d(F2),
            nn.ReLU(True),
            nn.Conv2d(F2,F3,1,stride=1, padding=0, bias=False),
            nn.BatchNorm2d(F3),
        )
        self.relu_1 = nn.ReLU(True)
        
    def forward(self, X):
        X_shortcut = X
        X = self.stage(X)
        X = X + X_shortcut
        X = self.relu_1(X)
        return X

3. ResNet核心

resnet50:包含了50个conv2d操作,共分5个stage。stage又称作残差块

stage1: 1层卷积,1层池化
stage2: 三个131卷积(1*1, 3*3, 1*1), 共9层卷积
stage3: 四个131卷积,共12层卷积
stage4: 六个131卷积,共18层卷积
stage5: 三个131卷积,9层
fc: 全连接

conv2d = 1+9+12+18+9+1 = 50
class ResModel(nn.Module):
    '''
    resnet50:包含了50个conv2d操作,共分5个stage

    stage1: 1层卷积,1层池化
    stage2: 三个131卷积(1*1, 3*3, 1*1), 共9层卷积
    stage3: 四个131卷积,共12层卷积
    stage4: 六个131卷积,共18层卷积
    stage5: 三个131卷积,9层
    fc: 全连接

    conv2d = 1+9+12+18+9+1 = 50
    '''
    def __init__(self, n_class):
        '''
        ConvBlock相比IndentityBlock,多了使用stride下采样的环节,且Identity不进行bn

        所以每个stage都是通过ConvBlock进入,决定是否下采样
        '''
        super(ResModel,self).__init__()
        self.stage1 = nn.Sequential(
            nn.Conv2d(3,64,7,stride=2, padding=3, bias=False), # 3通道输入,64个7*7卷积,步长2
            nn.BatchNorm2d(64),
            nn.ReLU(True),
            nn.MaxPool2d(3,2,padding=1),
        ) 

        self.stage2 = nn.Sequential(
            ConvBlock(64, f=3, filters=[64, 64, 256], s=1),
            IndentityBlock(256, 3, [64, 64, 256]),
            IndentityBlock(256, 3, [64, 64, 256]),
        ) # 3个卷积

        self.stage3 = nn.Sequential(
            ConvBlock(256, f=3, filters=[128, 128, 512], s=2),
            IndentityBlock(512, 3, [128, 128, 512]),
            IndentityBlock(512, 3, [128, 128, 512]),
            IndentityBlock(512, 3, [128, 128, 512]),
        ) # 4个卷积块

        self.stage4 = nn.Sequential(
            ConvBlock(512, f=3, filters=[256, 256, 1024], s=2),
            IndentityBlock(1024, 3, [256, 256, 1024]),
            IndentityBlock(1024, 3, [256, 256, 1024]),
            IndentityBlock(1024, 3, [256, 256, 1024]),
            IndentityBlock(1024, 3, [256, 256, 1024]),
            IndentityBlock(1024, 3, [256, 256, 1024]),
        ) # 6个卷积块

        self.stage5 = nn.Sequential(
            ConvBlock(1024, f=3, filters=[512, 512, 2048], s=2),
            IndentityBlock(2048, 3, [512, 512, 2048]),
            IndentityBlock(2048, 3, [512, 512, 2048]),
        ) # 3个卷积块

        # 平均池化
        self.pool = nn.AvgPool2d(2,2,padding=1)
        self.fc = nn.Sequential(
            nn.Linear(32768,n_class)  # 输入参数等于 = 2048 * 4 * 4, 4是最后一层池化输出大小
        )
    
    def forward(self, X):
        out = self.stage1(X)
        out = self.stage2(out)
        out = self.stage3(out)
        out = self.stage4(out)
        out = self.stage5(out)
        out = self.pool(out)
        # 输出的尺寸是 [32, 2048, 4, 4]
        out = out.view(batches, -1)
        # 调整后 [32, 32768]
        out = self.fc(out)
        return out
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
以下是一个简单的Python代码示例,用于在FER2013数据集上训练人脸表情识别模型: ```python import numpy as np import pandas as pd import matplotlib.pyplot as plt import tensorflow as tf from tensorflow.keras.preprocessing.image import ImageDataGenerator # 加载数据 df = pd.read_csv('fer2013.csv') # 将数据拆分为训练、验证和测试集 train_data = df[df['Usage'] == 'Training'] val_data = df[df['Usage'] == 'PublicTest'] test_data = df[df['Usage'] == 'PrivateTest'] # 创建数据生成器 train_datagen = ImageDataGenerator(rescale=1./255, rotation_range=10, width_shift_range=0.1, height_shift_range=0.1, shear_range=0.1, zoom_range=0.1, horizontal_flip=True, fill_mode='nearest') val_datagen = ImageDataGenerator(rescale=1./255) test_datagen = ImageDataGenerator(rescale=1./255) # 定义批量大小和图像大小 batch_size = 32 img_size = (48, 48) # 创建数据流 train_generator = train_datagen.flow_from_dataframe(train_data, x_col='pixels', y_col='emotion', target_size=img_size, batch_size=batch_size, class_mode='categorical') val_generator = val_datagen.flow_from_dataframe(val_data, x_col='pixels', y_col='emotion', target_size=img_size, batch_size=batch_size, class_mode='categorical') test_generator = test_datagen.flow_from_dataframe(test_data, x_col='pixels', y_col='emotion', target_size=img_size, batch_size=batch_size, class_mode='categorical') # 创建模型 model = tf.keras.models.Sequential([ tf.keras.layers.Conv2D(32, (3, 3), activation='relu', input_shape=(48, 48, 1)), tf.keras.layers.MaxPooling2D((2, 2)), tf.keras.layers.Conv2D(64, (3, 3), activation='relu'), tf.keras.layers.MaxPooling2D((2, 2)), tf.keras.layers.Conv2D(128, (3, 3), activation='relu'), tf.keras.layers.MaxPooling2D((2, 2)), tf.keras.layers.Flatten(), tf.keras.layers.Dense(128, activation='relu'), tf.keras.layers.Dense(7, activation='softmax') ]) # 编译模型 model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy']) # 训练模型 history = model.fit(train_generator, steps_per_epoch=len(train_data) // batch_size, epochs=50, validation_data=val_generator, validation_steps=len(val_data) // batch_size) # 评估模型 test_loss, test_acc = model.evaluate(test_generator, verbose=2) print('\nTest accuracy:', test_acc) ``` 注意:这只是一个简单的示例,并不一定是最优的模型架构或超参数设置。对于FER2013数据集,还有很多其他的模型和技术可以尝试。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值