经典CNN模型介绍及Pytorch实现

2023-11-02


前言

本文主要介绍了一些经典的卷积神经网络模型,包括LeNet、AlexNet、VGGNet、GoogLeNet和ResNet,其中前三个模型属于单分支网络结构,后两个模型属于多分支网络结构。


一、LeNet

1. LeNet介绍

Yann Lecun在1988年提出的LeNet是早期具有代表性的一个卷积神经网络。LeNet-5网络模型已经使用了近些年卷积神经网络中能见到的几乎所有基础操作,包括卷积层、池化层和反向传播训练方法等。
当时Lecun使用该网络模型帮助银行识别文档中的手写数字,输入的图像仅有32像素×32像素大小而且是灰度图像。该模型结构受限于当时的计算能力,不能用来设计为更加复杂的模型,也不能处理更大的图像。

2. LeNet核心代码

LeNet-5网络结构核心代码如下:

import torch.nn as nn
import torch.nn.functional as F

#Lenet network
class Net(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(3, 6, 5) #in_channels, out_channels, kernel_size, stride=1 ...
        self.conv2 = nn.Conv2d(6, 16, 5)
        self.fc1 = nn.Linear(16 * 5 * 5, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, num_classes)

    def forward(self, x):
        x = F.max_pool2d(F.relu(self.conv1(x)), kernel_size=(2, 2))
        x = F.max_pool2d(F.relu(self.conv2(x)), kernel_size=(2, 2))

        x = x.view(x.size()[0], -1)
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x

3. LeNet在MNIST上测试

LeNet在MNIST上测试代码如下:

import torch
from torchvision import transforms
from torchvision import datasets
from torch.utils.data import DataLoader
import torch.nn.functional as F
import torch.optim as optim


class Net(torch.nn.Module):
    def __init__(self):
        super().__init__()
        # batch*1*28*28(每次会送入batch个样本,输入通道数1(黑白图像),图像分辨率是28x28)
        # 下面的卷积层Conv2d的第一个参数指输入通道数,第二个参数指输出通道数,第三个参数指卷积核的大小
        self.conv1 = torch.nn.Conv2d(1, 6, 5)  # 输入通道数1,输出通道数6,核的大小5
        self.conv2 = torch.nn.Conv2d(6, 16, 5)  # 输入通道数6,输出通道数16,核的大小5
        # 下面的全连接层Linear的第一个参数指输入通道数,第二个参数指输出通道数
        self.fc1 = torch.nn.Linear(16*4*4, 120)  # 输入通道数是16*4*4,输出通道数是120
        self.fc2 = torch.nn.Linear(120, 84)  # 输入通道数是120,输出通道数是84,即10分类
        self.fc3 = torch.nn.Linear(84, 10)  # 输入通道数是84,输出通道数是10,即10分类

    def forward(self, x):
        in_size = x.size(0)  # 在本例中in_size=512,也就是BATCH_SIZE的值。输入的x可以看成是512*1*28*28的张量。
        out = self.conv1(x)  # batch*1*28*28 -> batch*6*24*24(28x28的图像经过一次核为5x5的卷积,输出变为24x24)
        out = F.relu(out)  # batch*6*24*24(激活函数ReLU不改变形状))
        out = F.avg_pool2d(out, 2, 2)  # batch*6*24*24 -> batch*6*12*12(2*2的池化层会减半)
        out = self.conv2(out)  # batch*6*12*12 -> batch*16*8*8(再卷积一次,核的大小是5)
        out = F.relu(out)  # batch*20*10*10
        out = F.avg_pool2d(out, 2, 2)  # batch*16*8*8 -> batch*16*4*4(2*2的池化层会减半)
        out = out.view(in_size, -1)  # batch*16*4*4 -> batch*256(out的第二维是-1,说明是自动推算,本例中第二维是16*4*4)
        out = self.fc1(out)  # batch*256 -> batch*120
        out = F.relu(out)  # batch*120
        out = self.fc2(out)  # batch*120 -> batch*84
        out = F.relu(out)
        out = self.fc3(out)  # batch*84 -> batch*10
        out = F.log_softmax(out, dim=1)  # 计算log(softmax(x))
        return out


def train(model, device, train_loader, optimizer, epoch):
    model.train()
    for batch_idx, (data, target) in enumerate(train_loader):
        data, target = data.to(device), target.to(device)
        optimizer.zero_grad()
        output = model(data)
        loss = criterion(output, target)
        loss.backward()
        optimizer.step()

        if (batch_idx + 1) % 30 == 0:
            print('Train Epoch: {} [{}/{} ({:.4f}%)]\tLoss: {:.6f}'.format(
                epoch, batch_idx * len(data), len(train_loader.dataset),
                       100. * batch_idx / len(train_loader), loss.item()))


def test(model, device, test_loader):
    model.eval()
    correct = 0
    with torch.no_grad():
        for data, target in test_loader:
            data, target = data.to(device), target.to(device)
            output = model(data)
            pred = output.max(1, keepdim=True)[1]  # 找到概率最大的下标
            correct += pred.eq(target.view_as(pred)).sum().item()

    print('\nAccuracy: {}/{} ({:.4f}%)\n'.format(
        correct, len(test_loader.dataset),
        100. * correct / len(test_loader.dataset)))


if __name__ == '__main__':
    BATCH_SIZE = 512  # 大概需要2G的显存
    EPOCHS = 10  # 总共训练批次
    DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")

    transform = transforms.Compose([
        transforms.ToTensor(),
        transforms.Normalize((0.1307,), (0.3081,))
    ])

    train_dataset = datasets.MNIST(root='../dataset/mnist/',
                                   train=True,
                                   download=True,
                                   transform=transform)
    train_loader = DataLoader(train_dataset,
                              shuffle=True,
                              batch_size=BATCH_SIZE)

    test_datasets = datasets.MNIST(root='../dataset/mnist/',
                                   train=False,
                                   download=True,
                                   transform=transform)
    test_loader = DataLoader(test_datasets,
                             shuffle=False,
                             batch_size=BATCH_SIZE)

    model = Net().to(DEVICE)
    criterion = torch.nn.CrossEntropyLoss()
    optimizer = optim.Adam(model.parameters())

    for epoch in range(1, EPOCHS + 1):
        train(model, DEVICE, train_loader, optimizer, epoch)
        test(model, DEVICE, test_loader)

运行10次后模型精度达到98.58%。


二、AlexNet

1. AlexNet介绍

在LeNet之后,随着计算能力的提升,研究者不断地提升模型的表达能力。最明显的是卷积层数的增加,每一层的内部通道数目也在增加。其中的一个经典网络模型就是2012年ImageNet竞赛的冠军AlexNet。AlexNet凭借深度学习方法打败了以往所有传统的特征算法(包括SIFT),并且准确率大幅提高。
AlexNet成功的另一个原因是ImageNet数据集的出现。在大数据时代,深度模型的学习离不开大量数据的训练,而此前一直没有能够让深度模型充分训练的大规模数据。AlexNet以绝对优势取得2012年ImageNet竞赛冠军后,使得深度学习逐渐成为学术研究热点,卷积神经网络称为计算机视觉任务上的主要模型。

2. AlexNet核心代码

AlexNet-7网络结构核心代码如下:

import torch.nn as nn
import torch.nn.functional as F

class AlexNet(nn.Module):
    def __init__(self, num_classes=NUM_CLASSES):
        super(AlexNet, self).__init__()
        self.features = nn.Sequential(
            nn.Conv2d(3, 64, kernel_size=3, stride=2, padding=1),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=2),
            nn.Conv2d(64, 192, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=2),
            nn.Conv2d(192, 384, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.Conv2d(384, 256, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.Conv2d(256, 256, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=2),
        )
        self.classifier = nn.Sequential(
            nn.Dropout(),
            nn.Linear(256 * 2 * 2, 4096),
            nn.ReLU(inplace=True),
            nn.Dropout(),
            nn.Linear(4096, 4096),
            nn.ReLU(inplace=True),
            nn.Linear(4096, num_classes),
        )

    def forward(self, x):
        x = self.features(x)
        x = x.view(x.size(0), 256 * 2 * 2)
        x = self.classifier(x)
        return x

3. AlexNet在MNIST上测试

AlexNet在MNIST上测试代码如下:

import torch
from torchvision import transforms
from torchvision import datasets
from torch.utils.data import DataLoader
import torch.nn.functional as F
import torch.optim as optim


class Net(torch.nn.Module):
    def __init__(self):
        super().__init__()
        # batch*1*28*28(每次会送入batch个样本,输入通道数1(黑白图像),图像分辨率是28x28)
        # 下面的卷积层Conv2d的第一个参数指输入通道数,第二个参数指输出通道数,第三个参数指卷积核的大小
        self.conv1 = torch.nn.Conv2d(1, 10, kernel_size=5, stride=1, padding=2)
        self.conv2 = torch.nn.Conv2d(10, 20, kernel_size=3, stride=1, padding=1)
        self.conv3 = torch.nn.Conv2d(20, 40, kernel_size=3, stride=1, padding=1)
        self.conv4 = torch.nn.Conv2d(40, 40, kernel_size=3, stride=1, padding=1)
        self.conv5 = torch.nn.Conv2d(40, 20, kernel_size=3, stride=1, padding=1)
        # 下面的全连接层Linear的第一个参数指输入通道数,第二个参数指输出通道数
        self.fc1 = torch.nn.Linear(20*7*7, 400)
        self.fc2 = torch.nn.Linear(400, 100)
        self.fc3 = torch.nn.Linear(100, 10)
        self.droupout = torch.nn.Dropout(p=0.5)

    def forward(self, x):
        in_size = x.size(0)
        out = self.conv1(x)
        out = F.relu(out)
        out = F.max_pool2d(out, 2, 2)
        out = self.conv2(out)
        out = F.relu(out)
        out = F.max_pool2d(out, 2, 2)
        out = self.conv3(out)
        out = F.relu(out)
        out = self.conv4(out)
        out = F.relu(out)
        out = self.conv5(out)
        out = F.relu(out)
        out = out.view(in_size, -1)
        out = self.droupout(out)
        out = self.fc1(out)
        out = F.relu(out)
        out = self.droupout(out)
        out = self.fc2(out)
        out = F.relu(out)
        out = self.fc3(out)
        out = F.log_softmax(out, dim=1)
        return out


def train(model, device, train_loader, optimizer, epoch):
    model.train()
    for batch_idx, (data, target) in enumerate(train_loader):
        data, target = data.to(device), target.to(device)
        optimizer.zero_grad()
        output = model(data)
        loss = criterion(output, target)
        loss.backward()
        optimizer.step()

        if (batch_idx + 1) % 30 == 0:
            print('Train Epoch: {} [{}/{} ({:.4f}%)]\tLoss: {:.6f}'.format(
                epoch, batch_idx * len(data), len(train_loader.dataset),
                       100. * batch_idx / len(train_loader), loss.item()))


def test(model, device, test_loader):
    model.eval()
    correct = 0
    with torch.no_grad():
        for data, target in test_loader:
            data, target = data.to(device), target.to(device)
            output = model(data)
            pred = output.max(1, keepdim=True)[1]  # 找到概率最大的下标
            correct += pred.eq(target.view_as(pred)).sum().item()

    print('\nAccuracy: {}/{} ({:.4f}%)\n'.format(
        correct, len(test_loader.dataset),
        100. * correct / len(test_loader.dataset)))


if __name__ == '__main__':
    BATCH_SIZE = 512  # 大概需要2G的显存
    EPOCHS = 10  # 总共训练批次
    DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")

    transform = transforms.Compose([
        transforms.ToTensor(),
        transforms.Normalize((0.1307,), (0.3081,))
    ])

    train_dataset = datasets.MNIST(root='../dataset/mnist/',
                                   train=True,
                                   download=True,
                                   transform=transform)
    train_loader = DataLoader(train_dataset,
                              shuffle=True,
                              batch_size=BATCH_SIZE)

    test_datasets = datasets.MNIST(root='../dataset/mnist/',
                                   train=False,
                                   download=True,
                                   transform=transform)
    test_loader = DataLoader(test_datasets,
                             shuffle=False,
                             batch_size=BATCH_SIZE)

    model = Net().to(DEVICE)
    criterion = torch.nn.CrossEntropyLoss()
    optimizer = optim.Adam(model.parameters())

    for epoch in range(1, EPOCHS + 1):
        train(model, DEVICE, train_loader, optimizer, epoch)
        test(model, DEVICE, test_loader)

运行10次后模型精度达到99.08%。


三、VGGNet

1. VGGNet介绍

Simonyan和Zisserman在2014年的ImageNet竞赛中使用了19层的网络结构VGGNet,比AlexNet的7层网络结构增加两倍多。尽管VGGNet使用了如此多的参数,其依然没有夺得ImageNet竞赛的冠军。但是该网络结构的16层版本在学术界被广泛使用,并在其他如目标检测、目标语义分割等视觉任务上取得了显著的效果。

2. VGGNet核心代码

VGGNet网络结构核心代码如下:

import torch.nn as nn
import torch.nn.functional as F

def VGGNet():
	def VGGLayers(configs):
	    channels = 1  # 输入通道数
	    layers = []
	    # 根据配置,将多个VGGBlock串联在一起组成VGGNet
	    for (N, C) in configs:
	        # 每个block指定N个卷积层,每层通道数是C个
	        for _ in range(N):
	            conv = torch.nn.Conv2d(channels, C, kernel_size=3, padding=1)
	            layers += [conv, torch.nn.ReLU(inplace=True)]
	            channels = C
	        layers += [torch.nn.MaxPool2d(kernel_size=2, stride=2)]
	    return torch.nn.Sequential(*layers)
	
	net = nn.Sequential(
		VGGLayers(configs=[(2, 64), (2, 128), (3, 256), (3, 512), (3, 512)]),
		nn.Flatten(),
		nn.Linear(512*7*7, 4096),
		nn.ReLU(inplace=True),
		nn.Dropout(),
		nn.Linear(4096, 4096),
		nn.ReLU(inplace=True),
		nn.Dropout(),
		nn.Linear(4096, 1000)
		)
	return net

3. VGGNet在MNIST上测试

VGGNet在MNIST上测试代码如下:

import torch
from torchvision import transforms
from torchvision import datasets
from torch.utils.data import DataLoader
import torch.nn.functional as F
import torch.optim as optim


def VGGLayers(configs):
    channels = 1  # 输入通道数
    layers = []
    # 根据配置,将多个VGGBlock串联在一起组成VGGNet
    for (N, C) in configs:
        # 每个block指定N个卷积层,每层通道数是C个
        for _ in range(N):
            conv = torch.nn.Conv2d(channels, C, kernel_size=3, padding=1)
            layers += [conv, torch.nn.ReLU(inplace=True)]
            channels = C
        layers += [torch.nn.MaxPool2d(kernel_size=2, stride=2)]
    return torch.nn.Sequential(*layers)


class Net(torch.nn.Module):
    def __init__(self):
        super().__init__()
        # batch*1*28*28(每次会送入batch个样本,输入通道数1(黑白图像),图像分辨率是28x28)
        # 下面的卷积层Conv2d的第一个参数指输入通道数,第二个参数指输出通道数,第三个参数指卷积核的大小
        self.VGG = VGGLayers(configs=[(2, 6), (3, 16)])

        # 下面的全连接层Linear的第一个参数指输入通道数,第二个参数指输出通道数
        self.fc1 = torch.nn.Linear(16*7*7, 500)
        self.fc2 = torch.nn.Linear(500, 200)
        self.fc3 = torch.nn.Linear(200, 10)
        self.droupout = torch.nn.Dropout(p=0.5)

    def forward(self, x):
        in_size = x.size(0)  # 在本例中in_size=512,也就是BATCH_SIZE的值。输入的x可以看成是512*1*28*28的张量。
        out = self.VGG(x)
        out = out.view(in_size, -1)
        out = self.fc1(out)
        out = F.relu(out)
        out = self.droupout(out)
        out = self.fc2(out)
        out = F.relu(out)
        out = self.droupout(out)
        out = self.fc3(out)
        out = F.log_softmax(out, dim=1)  # 计算log(softmax(x))
        return out


def train(model, device, train_loader, optimizer, epoch):
    model.train()
    for batch_idx, (data, target) in enumerate(train_loader):
        data, target = data.to(device), target.to(device)
        optimizer.zero_grad()
        output = model(data)
        loss = criterion(output, target)
        loss.backward()
        optimizer.step()

        if (batch_idx + 1) % 30 == 0:
            print('Train Epoch: {} [{}/{} ({:.4f}%)]\tLoss: {:.6f}'.format(
                epoch, batch_idx * len(data), len(train_loader.dataset),
                       100. * batch_idx / len(train_loader), loss.item()))


def test(model, device, test_loader):
    model.eval()
    correct = 0
    with torch.no_grad():
        for data, target in test_loader:
            data, target = data.to(device), target.to(device)
            output = model(data)
            pred = output.max(1, keepdim=True)[1]  # 找到概率最大的下标
            correct += pred.eq(target.view_as(pred)).sum().item()

    print('\nAccuracy: {}/{} ({:.4f}%)\n'.format(
        correct, len(test_loader.dataset),
        100. * correct / len(test_loader.dataset)))


if __name__ == '__main__':
    BATCH_SIZE = 512  # 大概需要2G的显存
    EPOCHS = 10  # 总共训练批次
    DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")

    transform = transforms.Compose([
        transforms.ToTensor(),
        transforms.Normalize((0.1307,), (0.3081,))
    ])

    train_dataset = datasets.MNIST(root='../dataset/mnist/',
                                   train=True,
                                   download=True,
                                   transform=transform)
    train_loader = DataLoader(train_dataset,
                              shuffle=True,
                              batch_size=BATCH_SIZE)

    test_datasets = datasets.MNIST(root='../dataset/mnist/',
                                   train=False,
                                   download=True,
                                   transform=transform)
    test_loader = DataLoader(test_datasets,
                             shuffle=False,
                             batch_size=BATCH_SIZE)

    model = Net().to(DEVICE)
    criterion = torch.nn.CrossEntropyLoss()
    optimizer = optim.Adam(model.parameters())

    for epoch in range(1, EPOCHS + 1):
        train(model, DEVICE, train_loader, optimizer, epoch)
        test(model, DEVICE, test_loader)

运行10次后模型精度达到99.07%。


四、GoogLeNet

1. GoogLeNet介绍

GoogLeNet在2014年的ImageNet竞赛中夺冠并取得了6.7%的Top-5分类错误率。目前在ImageNet数据集上人类识别的Top-5错误率为5.1%,GoogLeNet的结果表明卷积神经网络模型在该数据集上已经逼近人类的识别能力。GoogLeNet网络结构如下图所示:
GoogLeNet网络结构示意图
GoogLeNet网络使用多个基本组成模块组成最终的卷积神经网络,该基本组成模块被GoogLeNet提出者称为Inception模块。GoogLeNet提出者之后又提出多个改进版本的网络结构,主要也是在Inception模块的基础上改进。原始的Inception模块如下图所示,包含几种不同大小的卷积层,即1×1卷积层、3×3卷积层和5×5卷积层,还包括一个3×3的最大池化层。这些卷积层和池化层得到的特征拼接在一起作为最终的输出,同时也是下一个模块的输入。
Inception模块示意图

2. GoogLeNet核心代码

GoogLeNet网络结构核心代码如下:

import torch
import torch.nn as nn
import torch.nn.functional as F

from torch.autograd import Variable


class Inception(nn.Module):
    """
    1、输入通过Inception模块的4个分支分别计算,得到的输出宽和高相同(因为使用了padding),而通道不同。
    2、将4个分支的通道进行简单的合并,即得到Inception模块的输出。
    3、每次卷积之后都使用批正则化`BatchNorm2d`,并使用relu函数进行激活。
    """

    def __init__(self, in_planes, n1x1, n3x3red, n3x3, n5x5red, n5x5, pool_planes):
        super(Inception, self).__init__()
        # 1x1 conv branch
        self.b1 = nn.Sequential(
            nn.Conv2d(in_planes, n1x1, kernel_size=1),
            nn.BatchNorm2d(n1x1),
            nn.ReLU(True),
        )

        # 1x1 conv -> 3x3 conv branch
        self.b2 = nn.Sequential(
            nn.Conv2d(in_planes, n3x3red, kernel_size=1),
            nn.BatchNorm2d(n3x3red),
            nn.ReLU(True),
            nn.Conv2d(n3x3red, n3x3, kernel_size=3, padding=1),
            nn.BatchNorm2d(n3x3),
            nn.ReLU(True),
        )

        # 1x1 conv -> 5x5 conv branch
        self.b3 = nn.Sequential(
            nn.Conv2d(in_planes, n5x5red, kernel_size=1),
            nn.BatchNorm2d(n5x5red),
            nn.ReLU(True),
            # 2个3x3卷积代替1个5x5卷积
            nn.Conv2d(n5x5red, n5x5, kernel_size=3, padding=1),
            nn.BatchNorm2d(n5x5),
            nn.ReLU(True),
            nn.Conv2d(n5x5, n5x5, kernel_size=3, padding=1),
            nn.BatchNorm2d(n5x5),
            nn.ReLU(True),
        )

        # 3x3 pool -> 1x1 conv branch
        self.b4 = nn.Sequential(
            nn.MaxPool2d(3, stride=1, padding=1),
            nn.Conv2d(in_planes, pool_planes, kernel_size=1),
            nn.BatchNorm2d(pool_planes),
            nn.ReLU(True),
        )

    def forward(self, x):
        y1 = self.b1(x)
        y2 = self.b2(x)
        y3 = self.b3(x)
        y4 = self.b4(x)
        return torch.cat([y1, y2, y3, y4], 1)


class GoogLeNet(nn.Module):
    def __init__(self):
        super(GoogLeNet, self).__init__()
        self.pre_layers = nn.Sequential(
            nn.Conv2d(3, 192, kernel_size=3, padding=1),
            nn.BatchNorm2d(192),
            nn.ReLU(True),
        )

        self.a3 = Inception(192, 64, 96, 128, 16, 32, 32)
        self.b3 = Inception(256, 128, 128, 192, 32, 96, 64)

        self.maxpool = nn.MaxPool2d(3, stride=2, padding=1)

        self.a4 = Inception(480, 192, 96, 208, 16, 48, 64)
        self.b4 = Inception(512, 160, 112, 224, 24, 64, 64)
        self.c4 = Inception(512, 128, 128, 256, 24, 64, 64)
        self.d4 = Inception(512, 112, 144, 288, 32, 64, 64)
        self.e4 = Inception(528, 256, 160, 320, 32, 128, 128)

        self.a5 = Inception(832, 256, 160, 320, 32, 128, 128)
        self.b5 = Inception(832, 384, 192, 384, 48, 128, 128)

        self.avgpool = nn.AvgPool2d(8, stride=1)
        self.linear = nn.Linear(1024, 10)

    def forward(self, x):
        out = self.pre_layers(x)
        out = self.a3(out)
        out = self.b3(out)
        out = self.maxpool(out)
        out = self.a4(out)
        out = self.b4(out)
        out = self.c4(out)
        out = self.d4(out)
        out = self.e4(out)
        out = self.maxpool(out)
        out = self.a5(out)
        out = self.b5(out)
        out = self.avgpool(out)
        out = out.view(out.size(0), -1)
        out = self.linear(out)
        return out

3. GoogLeNet在MNIST上测试

GoogLeNet在MNIST上测试代码如下:

import torch
from torchvision import transforms
from torchvision import datasets
from torch.utils.data import DataLoader
import torch.nn.functional as F
import torch.optim as optim


class Inception(torch.nn.Module):
    #  Inception模块
    # 输入ninput:输入通道数
    # 输入n1:第一个1×1卷积核分支的输出通道数
    # 输入n3pre和n3:第二个3×3卷积核分支的输出通道数
    # 输入n5pre和n5:第三个5×5卷积核分支的输出通道数
    # 输入npool:第四个池化层分支的输出通道数
    def __init__(self, ninput, n1, n3pre, n3, n5pre, n5, npool):
        super(Inception, self).__init__()
        self.b1 = torch.nn.Sequential(
            torch.nn.Conv2d(ninput, n1, kernel_size=1),
            torch.nn.ReLU(inplace=True)
        )
        self.b2 = torch.nn.Sequential(
            torch.nn.Conv2d(ninput, n3pre, kernel_size=1),
            torch.nn.ReLU(inplace=True),
            torch.nn.Conv2d(n3pre, n3, kernel_size=3, padding=1)
        )
        self.b3 = torch.nn.Sequential(
            torch.nn.Conv2d(ninput, n5pre, kernel_size=1),
            torch.nn.ReLU(inplace=True),
            torch.nn.Conv2d(n5pre, n5, kernel_size=5, padding=2),
            torch.nn.ReLU(inplace=True)
        )
        self.b4 = torch.nn.Sequential(
            torch.nn.MaxPool2d(kernel_size=3, stride=1, padding=1),
            torch.nn.ReLU(inplace=True)
        )

    def forward(self, x):
        outpus = [self.b1(x), self.b2(x), self.b3(x), self.b4(x)]
        return torch.cat(outpus, dim=1)


class Net(torch.nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = torch.nn.Conv2d(1, 10, kernel_size=5)
        self.conv2 = torch.nn.Conv2d(74, 20, kernel_size=5)

        self.incep1 = Inception(10, 16, 16, 24, 16, 24, 24)
        self.incep2 = Inception(20, 16, 16, 24, 16, 24, 24)

        self.mp = torch.nn.MaxPool2d(2)
        self.fc = torch.nn.Linear(1344, 10)

    def forward(self, x):
        in_size = x.size(0)
        out = F.relu(self.mp(self.conv1(x)))
        out = self.incep1(out)
        out = self.conv2(out)
        out = self.mp(out)
        out = F.relu(out)
        out = self.incep2(out)
        out = out.view(in_size, -1)
        out = self.fc(out)
        return out


def train(model, device, train_loader, optimizer, epoch):
    model.train()
    for batch_idx, (data, target) in enumerate(train_loader):
        data, target = data.to(device), target.to(device)
        optimizer.zero_grad()
        output = model(data)
        loss = criterion(output, target)
        loss.backward()
        optimizer.step()

        if (batch_idx + 1) % 30 == 0:
            print('Train Epoch: {} [{}/{} ({:.4f}%)]\tLoss: {:.6f}'.format(
                epoch, batch_idx * len(data), len(train_loader.dataset),
                       100. * batch_idx / len(train_loader), loss.item()))


def test(model, device, test_loader):
    model.eval()
    correct = 0
    with torch.no_grad():
        for data, target in test_loader:
            data, target = data.to(device), target.to(device)
            output = model(data)
            pred = output.max(1, keepdim=True)[1]  # 找到概率最大的下标
            correct += pred.eq(target.view_as(pred)).sum().item()

    print('\nAccuracy: {}/{} ({:.4f}%)\n'.format(
        correct, len(test_loader.dataset),
        100. * correct / len(test_loader.dataset)))


if __name__ == '__main__':
    BATCH_SIZE = 512  # 大概需要2G的显存
    EPOCHS = 10  # 总共训练批次
    DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")

    transform = transforms.Compose([
        transforms.ToTensor(),
        transforms.Normalize((0.1307,), (0.3081,))
    ])

    train_dataset = datasets.MNIST(root='../dataset/mnist/',
                                   train=True,
                                   download=True,
                                   transform=transform)
    train_loader = DataLoader(train_dataset,
                              shuffle=True,
                              batch_size=BATCH_SIZE)

    test_datasets = datasets.MNIST(root='../dataset/mnist/',
                                   train=False,
                                   download=True,
                                   transform=transform)
    test_loader = DataLoader(test_datasets,
                             shuffle=False,
                             batch_size=BATCH_SIZE)

    # for i, img_data in enumerate(train_loader, 1):
    #     images, labels = img_data
    #     print('batch{0}:images shape info-->{1} labels-->{2}'.format(i, images.shape, labels))

    model = Net().to(DEVICE)
    criterion = torch.nn.CrossEntropyLoss()
    optimizer = optim.Adam(model.parameters())

    for epoch in range(1, EPOCHS + 1):
        train(model, DEVICE, train_loader, optimizer, epoch)
        test(model, DEVICE, test_loader)

运行10次后模型精度达到99.26%。


五、ResNet

1. ResNet介绍

从LeNet到AlexNet再到VGGNet,在ImageNet竞赛上网络深度有逐渐加深的趋势。在2015年的ImageNet竞赛上就出现了152层的网络结构,即ResNet,这远远超出GoogLeNet的22层和VGGNet的16层。ImageNet数据集的识别精度上,ResNet网络的表现超出了人类的认识水平,获得了3.57%的Top-5分类错误率。为了解决由于网络变深导致梯度小时/爆炸的问题,ResNet引入了跳层连接,该结构能够将网络加深到任意深度。
ResNet主要采用跳层连接组成残差模块,包括一个跳层连接和若干卷积层等操作。其中卷积层等操作主要学习一个原始输入x的补充值,参考资料中称为残差值。这样就可将单分支网络结构中的每几层变为双分支结构,增加的跳层连接分支是简单的Identity映射,即f(x)=x操作。残差模块如下图所示:
残差模块示意图

2. ResNet核心代码

网络结构核心代码如下:

import torch
import torch.nn as nn

# 3x3 convolution
def conv3x3(in_channels, out_channels, stride=1):
    return nn.Conv2d(in_channels, out_channels, kernel_size=3,
                     stride=stride, padding=1, bias=False)


# Residual block
class ResidualBlock(nn.Module):
    def __init__(self, in_channels, out_channels, stride=1, downsample=None):
        super(ResidualBlock, self).__init__()
        self.conv1 = conv3x3(in_channels, out_channels, stride)
        self.bn1 = nn.BatchNorm2d(out_channels)
        self.relu = nn.ReLU(inplace=True)
        self.conv2 = conv3x3(out_channels, out_channels)
        self.bn2 = nn.BatchNorm2d(out_channels)
        self.downsample = downsample

    def forward(self, x):
        residual = x
        out = self.conv1(x)
        out = self.bn1(out)
        out = self.relu(out)
        out = self.conv2(out)
        out = self.bn2(out)
        if self.downsample:
            residual = self.downsample(x)
        out += residual
        out = self.relu(out)
        return out
# ResNet
class Net(nn.Module):
    def __init__(self, block, layers, num_classes=10):
        super(Net, self).__init__()
        self.in_channels = 16
        self.conv = conv3x3(3, 16)
        self.bn = nn.BatchNorm2d(16)
        self.relu = nn.ReLU(inplace=True)
        self.layer1 = self.make_layer(block, 16, layers[0])
        self.layer2 = self.make_layer(block, 32, layers[1], 2)
        self.layer3 = self.make_layer(block, 64, layers[2], 2)
        self.avg_pool = nn.AvgPool2d(8)
        self.fc = nn.Linear(64, num_classes)

    def make_layer(self, block, out_channels, blocks, stride=1):
        downsample = None
        if (stride != 1) or (self.in_channels != out_channels):
            downsample = nn.Sequential(
                conv3x3(self.in_channels, out_channels, stride=stride),
                nn.BatchNorm2d(out_channels))
        layers = []
        layers.append(block(self.in_channels, out_channels, stride, downsample))
        self.in_channels = out_channels
        for i in range(1, blocks):
            layers.append(block(out_channels, out_channels))
        return nn.Sequential(*layers)

    def forward(self, x):
        out = self.conv(x)
        out = self.bn(out)
        out = self.relu(out)
        out = self.layer1(out)
        out = self.layer2(out)
        out = self.layer3(out)
        out = self.avg_pool(out)
        out = out.view(out.size(0), -1)
        out = self.fc(out)
        return out

六、卷积神经网络的一些说明

1. 卷积层输出维度的计算公式

如果你的步幅为 1,而且把零填充设置为:
Z e r o P a d d i n g = ( k − 1 ) / 2 Zero Padding=(k-1)/2 ZeroPadding=(k1)/2
K 是过滤器尺寸,那么输入和输出就能保持一致的空间维度。
计算任意给定卷积层的输出的大小的公式是:
O = ( W − K + 2 P ) / S + 1 O=(W-K+2P)/S+1 O=(WK+2P)/S+1
其中 O 是输出尺寸,K 是过滤器尺寸,P 是填充,S 是步幅。

2. 激励层的实践经验

①不要用sigmoid!不要用sigmoid!不要用sigmoid!
② 首先试RELU,因为快,但要小心点。
③ 如果2失效,请用Leaky ReLU或者Maxout。
④ 某些情况下tanh倒是有不错的结果,但是很少。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

经典CNN模型介绍及Pytorch实现 的相关文章

随机推荐

  • 64位win7下安装MongoDB以zip包的形式 图文(超详细)

    首先从mongodb的官网上下载对应版本的zip包 如果你使用Windows 64 bit 2008 R2 或win7需要安装Hotfix补丁 读者可以去网上下载相应的版本 解压后会得到如下的一个目录 然后自己在某个录下下建好一个目录 我这
  • window.open (‘page.html‘)

    window open page html 用于控制弹出新的窗口
  • 2024王道408数据结构 P143 T8

    2024王道408数据结构 P143 T8 思考过程 首先题目的意思非常简单明了 就是让我们找二叉树中度为2的结点 也就是既有左子树又有右子树的结点 那我们只需要在代码里判断如果该结点有左子树就入队 同时如果该结点有右子树就计数器 1 并且
  • vi/vim基本使用命令

    转自 http www lupaworld com uid 296380 action viewspace itemid 118973 vi vim 基本使用方法 本文介绍了vi vim 的基本使用方法 但对于普通用户来说基本上够了 i v
  • QT+VS配置及调试

    QT下载 https download qt io archive qt QT Creator设置 打开 Qt Creator 进入编译器部分 工具 gt 选项 gt 构建和运行 gt 编译器 可以看到vs的内容 之后 进入 工具 gt 选
  • Android平台GB28181接入端如何对接UVC摄像头?

    我们在对接Android平台GB28181接入的时候 有公司提出这样的需求 除了采集执法记录仪摄像头自带的数据外 还想通过执法记录仪采集外接UVC摄像头 实际上 这块对我们来说有点炒冷饭了 不算新的诉求 大牛直播SDK 在2016年对接RT
  • 2023国庆节放假通知

    喜迎国庆 放假通知 公司相关各部门 国庆来临之际 根据国家有关规定 现将2023年国庆放假事项通知如下 1 9月29至10月6日放假调休 共8天 10月7日上班 10月8日上班 2 各部门接通知后 妥善安排好值班工作 并将各部门值班表于20
  • 500套优秀简历模板,送给您!

    点击上方 成猿之路 选择 置顶公众号 第一时间送达实用技术干货 最近收藏保存了一些简历模板 觉得不错 送给即将步入社会或需要简历模板的你 01单面简历 150款 02多页简历 95款 03表格简历 18款 04英文简历 27款 05艺术气质
  • pytorch: 数据增广(Data Augmentation)

    常用的数据增广方法 比例缩放 位置截取 翻转 旋转 亮度 对比度和色调的变化 读取原图 import torchvision transforms as transforms from PIL import Image img Image
  • Vue.js如何实现倒计时?颜小白实测可用!

    Vue JS如何实现倒计时功能 1 首先一般来说前端小伙伴们会收到后端同学传过来的一个结束时间 大部分需要倒计时得情况都是在详情页 比如商品 活动等一些场景 2 如果需要实现倒计时功能 首先我们需要知道如何计算剩余时间 首先我们会拿到后端传
  • jdbctemplate 执行多条sql_SpringBoot使用JdbcTemplate连接Mysql实现增删改查

    摘要 本文是springboot工程使用JdbcTemplate连接Mysql数据库 实现增删改查的实例 及在搭建过程中碰到的几个问题 前几篇介绍怎么搭建SpringBoot工程 接下来直接入正题 什么是JDBC JDBC Java Dat
  • HDFS RPC限流方案实践探索

    文章目录 前言 HDFS RPC限流方案 分级RPC queue的调参 分级RPC queue的insight 前言 在前面的一篇关于分布式集群下的限流方案文章里 笔者阐述了一种在HDFS集群里的RPC限流架构 其间也提到了很多关于分布式限
  • Ubuntu连接不上网络问题的解决方法

    这学期经常要用虚拟机做实验 但经常在某一次开机后网络连接不上 查过很多解决方法 每次奏效的方法又都不一样 这里记录一下 省的下次一个一个找了 第一次写博客 有点点小激动嘿嘿 以下方法都是在NAT模式下的连接 方法一 还原默认设置 将虚拟机关
  • 20个基本电路图讲解_记住这些规则,再看电路图就不会乱了!

    在我们进行电子DIY制作时 看图是难免的 但对于很多新手来说 刚开始似乎总有种很乱的感觉 走过来后我们才知道 当时只是没有了解这些规则 今天小编以电子电路图为主要示例进行总结一下 电路图走向 是指电路图中各部分电路 从最初的输入端到最终的输
  • 【数据结构】 二叉树面试题讲解->叁

    文章目录 引言 根据二叉树创建字符串 https leetcode cn problems construct string from binary tree submissions 题目描述 示例 示例一 示例二 思路解析 代码完整实现
  • Spring MVC的表单标签库详解

    表单标签库中包含了可以用在 JSP 页面中渲染 HTML 元素的标签 在 JSP 页面使用 Spring 表单标签库时 必须在 JSP 页面开头处声明 taglib 指令 指令代码如下 在表单标签库中有 form input passwor
  • Ueditor去掉图片之间的间隙

    问题 运营在后台配置商品信息的时候 复制京东上面的图片到ueditor富文本编辑器里面 两张图片中总是存在空白间隙 但查看html源码又很简单没有发现什么问题p标签之类的 而且硬着配置上去后 在uniapp打包的微信小程序里面查看商品详情一
  • cartographer 前端PoseExtrapolator及IMU预积分

    卡尔曼滤波器应用 用于单目标追踪的IMM模型 知乎 Cartographer前端的优化 基于IMU预积分的LIO实现 知乎 Cartographer PoseExtrapolator 位姿外推器 分析总结与优化思路 知乎 MCGA Make
  • 设计模式(五)适配器模式Adapter(结构型)

    设计模式 五 适配器模式Adapter 结构型 1 概述 接口的改变 是一个需要程序员们必须 虽然很不情愿 接受和处理的普遍问题 程序提供者们修改他们的代码 系统库被修正 各种程序语言以及相关库的发展和进化 例子1 iphone4 你即可以
  • 经典CNN模型介绍及Pytorch实现

    文章目录 前言 一 LeNet 1 LeNet介绍 2 LeNet核心代码 3 LeNet在MNIST上测试 二 AlexNet 1 AlexNet介绍 2 AlexNet核心代码 3 AlexNet在MNIST上测试 三 VGGNet 1