使用PyTorch简单实现卷积神经网络模型

  这里我们会用 Python 实现三个简单的卷积神经网络模型:LeNet 、AlexNet 、VGGNet,首先我们需要了解三大基础数据集:MNIST 数据集、Cifar 数据集和 ImageNet 数据集

三大基础数据集

MNIST 数据集

  MNIST数据集是用作手写体识别的数据集。MNIST 数据集包含 60000 张训练图片,10000 张测试图片。其中每一张图片都是 0~9 中的一个数字。图片尺寸为 28×28。由于数据集中数据相对比较简单,人工标注错误率仅为 0.2%。

Cifar 数据集

  Cifar 数据集是一个图像分类数据集。分为 Cifar-10 和 Cifar-100 两个数据集。Cifar 数据集中的图片为 32×32 的彩色图片,这些图片是由 Alex Krizhenevsky 教授、Vinod Nair 博士和 Geoffrey Hilton 教授整理的。Cifar-10 数据集收集了来自 10 个不同种类的 60000 张图片,这些种类有:飞机、汽车、鸟、猫、鹿、狗、青蛙、马、船和卡车。在 Cifar-10 数据集上,人工标注的正确率为94%。

ImageNet数据集

  ImageNet 数据集是一个大型图像数据集,由斯坦福大学的李飞飞教授带头整理而成。在 ImageNet 中,近 1500 万张图片关联到 WordNet 中 20000 个名次同义词集上。ImageNet 每年举行计算机视觉相关的竞赛, ImageNet 的数据集涵盖计算机视觉的各个研究方向,其用做图像分类的数据集是 ILSVRC2012 图像分类数据集。

  ILSVRC2012 数据集的数据和 Cifar-10 数据集一致,识别图像中主要物体,其包含了来自 1000 个种类的 120 万张图片,每张图片只属于一个种类,大小从几千字节到几百万字节不等。卷积神经网络也正是在此数据集上一战成名。

 

三种经典的卷积神经网络模型

  三种经典的卷积神经网络模型:LeNetAlexNetVGGNet。这三种卷积神经网络的结构不算特别复杂,下面使用 PyTorch 进行实现

LeNet模型

  LeNet 具体指的是 LeNet-5。LeNet-5 模型是 Yann LeCun 教授于 1998 年在论文 Gradient-based learning applied to document recognition 中提出的,它是第一个成功应用于数字识别问题的卷积神经网络。在 MNIST 数据集上,LeNet-5 模型可以达到大约 99.2% 的正确率。LeNet-5 模型总共有7层,包括有2个卷积层,2个池化层,2个全连接层和一个输出层,下图展示了 LeNet-5 模型的架构。

 使用 PyTorch 实现

class LeNet(nn.Module):
    def __init__(self):
        super(LeNet, self).__init__()
        self.conv1 = nn.Conv2d(3, 6, 5)
        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, 10)
    def forward(self, x):
        out = F.relu(self.conv1(x))
        out = F.max_pool2d(out, 2)
        out = F.relu(self.conv2(out))
        out = F.max_pool2d(out, 2)
        out = out.view(out.size(0), -1)
        out = F.relu(self.fc1(out))
        out = F.relu(self.fc2(out))
        out = self.fc3(out)
        return out
PyTorch 实现 LeNet 模型

 

AlexNet模型

  Alex Krizhevsky 提出了卷积神经网络模型 AlexNet。AlexNet 在卷积神经网络上成功地应用了 Relu,Dropout 和 LRN 等技巧。在 ImageNet 竞赛上,AlexNet 以领先第二名 10% 的准确率而夺得冠军。成功地展示了深度学习的威力。它的网络结构如下:

  由于当时GPU计算能力不强,AlexNet使用了两个GPU并行计算,现在可以用一个GPU替换。以单个GPU的AlexNet模型为例,包括有:5个卷积层,3个池化层,3个全连接层。其中卷积层和全连接层包括有relu层,在全连接层中还有dropout层。具体参数的配置可以看下图

 使用 PyTorch 实现

class AlexNet(nn.Module):
    def __init__(self, num_classes):
        super(AlexNet, self).__init__()
        self.features = nn.Sequential(
            nn.Conv2d(3, 96, kernel_size=11, stride=4, padding=2),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, stride=2),
            nn.Conv2d(64, 256, kernel_size=5, padding=2),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, stride=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=3, stride=2),
        )
        self.classifier = nn.Sequential(
            nn.Dropout(),
            nn.Linear(256 * 6 * 6, 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 * 6 * 6)
        x = self.classifier(x)
        return x
PyTorch 实现 AlexNet 模型

 

VGGNet模型

  VGGNet是牛津大学计算机视觉组和Google DeepMind公司的研究人员一起研发的一种卷积神经网络。通过堆叠3×3 的小型卷积核和2×2的最大池化层,VGGNet成功地构筑了最深达19层的卷积神经网络。由于VGGNet的拓展性强,迁移到其他图片数据上的泛化性比较好,可用作迁移学习。

下图为 VGG16 的整体架构图

  从左至右,一张彩色图片输入到网络,白色框是卷积层,红色是池化,蓝色是全连接层,棕色框是预测层。预测层的作用是将全连接层输出的信息转化为相应的类别概率,而起到分类作用。

可以看到 VGG16 是13个卷积层+3个全连接层叠加而成。

使用 PyTorch 实现

cfg = {
    'VGG11': [64, 'M', 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'],
    'VGG13': [64, 64, 'M', 128, 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'],
    'VGG16': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 'M', 512, 512, 512, 'M', 512, 512, 512, 'M'],
    'VGG19': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 256, 'M', 512, 512, 512, 512, 'M', 512, 512, 512, 512, 'M'],
}
class VGG(nn.Module):
    def __init__(self, vgg_name):
        super(VGG, self).__init__()
        self.features = self._make_layers(cfg[vgg_name])
        self.classifier = nn.Linear(512, 10)
    def forward(self, x):
        out = self.features(x)
        out = out.view(out.size(0), -1)
        out = self.classifier(out)
        return out
    def _make_layers(self, cfg):
        layers = []
        in_channels = 3
        for x in cfg:
            if x == 'M':
                layers += [nn.MaxPool2d(kernel_size=2, stride=2)]
            else:
                layers += [nn.Conv2d(in_channels, x, kernel_size=3, padding=1),
                           nn.BatchNorm2d(x),
                           nn.ReLU(inplace=True)]
                in_channels = x
        layers += [nn.AvgPool2d(kernel_size=1, stride=1)]
        return nn.Sequential(*layers)
PyTorch 实现 VGGNet 模型

 

  VGG16 是基于大量真实图像的 ImageNet 图像库预训练的网络

  VGG16 对应的供 keras 使用的模型人家已经帮我们训练好,我们将学习好的 VGG16 的权重迁移(transfer)到自己的卷积神经网络上作为网络的初始权重,这样我们自己的网络不用从头开始从大量的数据里面训练,从而提高训练速度。这里的迁移就是平时所说的迁移学习。

from keras.models import Sequential
from keras.layers.core import Flatten, Dense, Dropout
from keras.layers.convolutional import Convolution2D, MaxPooling2D, ZeroPadding2D
from keras.optimizers import SGD
import cv2, numpy as np


def VGG_16(weights_path=None):
    model = Sequential()
    model.add(ZeroPadding2D((1, 1), input_shape=(3, 244, 244))) # 卷积输入层指定输入图像大小(网络开始输入(3,224,224)的图像数据,即一张宽224,高244的彩色RGB图片)
    model.add(Convolution2D(64, 3, 3, activation='relu')) # 64个3*3的卷积核,生成64*244*244的图像
    model.add(ZeroPadding2D(1, 1)) # 补0,(1, 1)表示横向和纵向都补0,保证卷积后图像大小不变,可以用padding='valid'参数代替
    model.add(Convolution2D(64, 3, 3, activation='relu')) # 再次卷积操作,生成64*244*244的图像,激活函数是relu
    model.add(MaxPooling2D((2, 2), strides=(2, 2))) # pooling操作,相当于变成64*112*112,小矩阵是(2,2),步长(2,2),指的是横向每次移动2格,纵向每次移动2格。

    # 再往下,同理,只不过是卷积核个数依次变成128,256,512,而每次按照这样池化之后,矩阵都要缩小一半。
    model.add(ZeroPadding2D((1, 1)))
    model.add(Convolution2D(128, 3, 3, activation='relu'))
    model.add(ZeroPadding2D((1, 1)))
    model.add(Convolution2D(128, 3, 3, activation='relu'))
    model.add(MaxPooling2D((2, 2), strides=(2, 2))) # 128*56*56

    model.add(ZeroPadding2D((1, 1)))
    model.add(Convolution2D(256, 3, 3, activation='relu'))
    model.add(ZeroPadding2D((1, 1)))
    model.add(Convolution2D(256, 3, 3, activation='relu'))
    model.add(ZeroPadding2D((1, 1)))
    model.add(Convolution2D(256, 3, 3, activation='relu'))
    model.add(MaxPooling2D((2, 2), strides=(2, 2)))  # 256*28*28

    model.add(ZeroPadding2D((1, 1)))
    model.add(Convolution2D(512, 3, 3, activation='relu'))
    model.add(ZeroPadding2D((1, 1)))
    model.add(Convolution2D(512, 3, 3, activation='relu'))
    model.add(ZeroPadding2D((1, 1)))
    model.add(Convolution2D(512, 3, 3, activation='relu'))
    model.add(MaxPooling2D((2, 2), strides=(2, 2)))  # 512*14*14

    model.add(ZeroPadding2D((1, 1)))
    model.add(Convolution2D(512, 3, 3, activation='relu'))
    model.add(ZeroPadding2D((1, 1)))
    model.add(Convolution2D(512, 3, 3, activation='relu'))
    model.add(ZeroPadding2D((1, 1)))
    model.add(Convolution2D(512, 3, 3, activation='relu'))
    model.add(MaxPooling2D((2, 2), strides=(2, 2)))  # 128*7*7
    # 13层卷积和池化之后,数据变成了512 * 7 * 7

    model.add(Flatten) # 压平上述向量,变成一维512*7*7=25088
    # 三个全连接层
    '''
    4096只是个经验值,其他数当然可以,试试效果,只要不要小于要预测的类别数,这里要预测的类别有1000种,
    所以最后预测的全连接有1000个神经元。如果你想用VGG16 给自己的数据作分类任务,这里就需要改成你预测的类别数。
    '''
    model.add(Dense(4096, activation='relu')) # 全连接层有4096个神经元,参数个数是4096*25088
    model.add(Dropout(0.5)) # 0.5的概率抛弃一些连接
    model.add(Dense(4096, activation='relu'))  # 全连接层有4096个神经元,参数个数是4096*25088
    model.add(Dropout(0.5))  # 0.5的概率抛弃一些连接
    model.add(Dense(1000, activation='softmax'))

    if weights_path:
        model.load_weights(weights_path)
    return model

# 如果要设计其他类型的CNN网络,就是将这些基本单元比如卷积层个数、全连接个数按自己需要搭配变换,
使用 Keras 实现 VGG16
# 模型需要事先下载
model = VGG_16('vgg16_weights.h5')

sgd = SGD(lr=0.1, decay=1e-6, momentum=0.9, nesterov=True)
model.compile(optimizer=sgd, loss='categorical_crossentropy')

# 加载图片
def load_image(img):
    im = cv2.resize(cv2.imread(img), (224, 224)).astype(np.float32)
    im[:,:,0] -= 103.939
    im[:,:,1] -= 116.779
    im[:,:,2] -= 123.68
    im = im.transpose((2, 0, 1))
    im = np.expand_dims(im, axis=0)
    return im

# 读取vgg16类别的文件
f = open('synset_words.txt', 'r')
lines = f.readline()
f.close()

def predict(url):
    im = load_image(url)
    pre = np.argmax(model.predict(im))
    print(lines[pre])
    
# 测试图片
predict('xx.jpg')
测试 VGG16 模型

 

           

 

 

参考:https://www.cnblogs.com/wmr95/articles/7814892.html          

 

 

 

 

 

                 

posted @ 2019-09-17 16:26  ''竹先森゜  阅读(3815)  评论(0编辑  收藏  举报