# 3DCNN 网络结构

学习内容基于:https://www.bilibili.com/video/BV1kc411Q7Tj

# 图像的本质

图像是由 RGB 三通道组成的,一般数值范围是 0 到 255.

因此像素矩阵为:H * W * 3,其中 H 为高,W 是宽。

# 从图像到视频

视频本质上是由连续的图片(视频帧),快速播放构成的。

图片的像素矩阵是 H * W * 3

视频就是 D * H * W * 3,其中 D 是深度,是视频帧的叠加。

不同的库对与这 4 个维度顺序表述不同,但这四个维度诚然如此。

# 2D 检测方法对比

1.png 1.png 1.png

# C3D 网络流程

这个(1,1,1),三个数字是有点手法的,第一个数是代表了深度,第二个、第三个代表了高和宽。

我们可以看到,一般来说这个卷积核大小一般是(3,3,3),填充是(1,1,1),经过这样的卷积过后,特征图大小不变。

这里是 up 的解释,已经是非常的直观易懂。

# 动手搭建一个 C3D

import torch
from torch import nn
from torchsummary import summary
class C3D(nn.Module):
    def __init__(self, num_classes, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)
        self.block1 = nn.Sequential(
            nn.Conv3d(3, 64, kernel_size=(3, 3, 3), padding=(1, 1, 1)),
            nn.ReLU(),
            nn.MaxPool3d(kernel_size=(1, 2, 2), stride=(1, 2, 2))
        )
        self.block2 = nn.Sequential(
            nn.Conv3d(64, 128, kernel_size=(3, 3, 3), padding=(1, 1, 1)),
            nn.ReLU(),
            nn.MaxPool3d(kernel_size=(2, 2, 2), stride=(2, 2, 2))
        )
        self.block3 = nn.Sequential(
            nn.Conv3d(128, 256, kernel_size=(3, 3, 3), padding=(1, 1, 1)),
            nn.ReLU(),
            nn.Conv3d(256, 256, kernel_size=(3, 3, 3), padding=(1, 1, 1)),
            nn.ReLU(),
            nn.MaxPool3d(kernel_size=(2, 2, 2), stride=(2, 2, 2))
        )
        self.block4 = nn.Sequential(
            nn.Conv3d(256, 512, kernel_size=(3, 3, 3), padding=(1, 1, 1)),
            nn.ReLU(),
            nn.Conv3d(512, 512, kernel_size=(3, 3, 3), padding=(1, 1, 1)),
            nn.ReLU(),
            nn.MaxPool3d(kernel_size=(2, 2, 2), stride=(2, 2, 2))
        )
        self.block5 = nn.Sequential(
            nn.Conv3d(512, 512, kernel_size=(3, 3, 3), padding=(1, 1, 1)),
            nn.ReLU(),
            nn.Conv3d(512, 512, kernel_size=(3, 3, 3), padding=(1, 1, 1)),
            nn.ReLU(),
            nn.MaxPool3d(kernel_size=(2, 2, 2), stride=(2, 2, 2), padding=(0, 1, 1))
        )
        self.fc = nn.Sequential(
            nn.Linear(8192, 4096),
            nn.ReLU(),
            nn.Dropout(p=0.5),
            nn.Linear(4096, 4096),
            nn.ReLU(),
            nn.Dropout(p=0.5),
            nn.Linear(4096, num_classes)
        )
        self._init_weights()
    def forward(self, x):
        x = self.block1(x)
        x = self.block2(x)
        x = self.block3(x)
        x = self.block4(x)
        x = self.block5(x)
        x = x.flatten(start_dim=1)
        x = self.fc(x)
        return x
    def _init_weights(self):
        for m in self.modules():
            if isinstance(m, nn.Conv3d):
                nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
                if m.bias is not None:
                    nn.init.constant_(m.bias, 0)
            elif isinstance(m, nn.Linear):
                nn.init.normal_(m.weight, 0, 0.01)
if __name__ == '__main__':
    model = C3D(num_classes=101)
    print(summary(model, (3, 16, 112, 112), device='cpu'))

# 预训练权重

比如,有一个猫狗分类的模型,权重和偏置已经训练好了,但是现在有一个新任务,就是狼和老虎进行分类,我们就可以用猫狗分类的权重来继续训练狼和老虎分类,这样的话会加速我们的训练速度,非常方便。

@staticmethod
    def __load__pretrained_model():
        p_dict = torch.load('./ucf101-caffe.pth')
        s_dict = model.state_dict()
        corresp_name = {
            'features.0.weight': 'block1.0.weight',
            'features.0.bias': 'block1.0.bias',
            'features.3.weight': 'block2.0.weight',
            'features.3.bias': 'block2.0.bias',
            'features.6.weight': 'block3.0.weight',
            'features.6.bias': 'block3.0.bias',
            'features.8.weight': 'block3.2.weight',
            'features.8.bias': 'block3.2.bias',
            'features.11.weight': 'block4.0.weight',
            'features.11.bias': 'block4.0.bias',
            'features.13.weight': 'block4.2.weight',
            'features.13.bias': 'block4.2.bias',
            'features.16.weight': 'block5.0.weight',
            'features.16.bias': 'block5.0.bias',
            'features.18.weight': 'block5.2.weight',
            'features.18.bias': 'block5.2.bias',
            'classifier.0.weight': 'fc.0.weight',
            'classifier.0.bias': 'fc.0.bias',
            'classifier.3.weight': 'fc.3.weight',
            'classifier.3.bias': 'fc.3.bias'
        }
        for name in p_dict:
            if name not in corresp_name:
                continue
            s_dict[corresp_name[name]] = p_dict[name]

像这样,我们就可以做参数移植了

更新于

请我喝[茶]~( ̄▽ ̄)~*

KarryLiu 微信支付

微信支付

KarryLiu 支付宝

支付宝