分类模型--VGG16

前言

VGG参加2014年的ImageNet图像分类与定位挑战赛,取得了分类第二,定位第一的优秀成绩;

结构

VGG根据卷积核大小和卷积层数目不同,可以分为A,A-,LRN,B,C,D,E 6种,以D,E两种较为常用,分别称为VGG16和VGG19

下图给出VGG的六种结构配置:

上图中,每一列对应一种结构配置,比如:图中的绿色部分即指明了VGG16所采用的结构;

针对其进行具体分析,可以看到VGG16共包含:

  • 13个卷积层(Convolutional  Layer),分别用conv3-xxx表示
  • 3个全连接层(Fully connected Layer),分别用FC-XXXX表示
  • 5个池化层(Pool Layer),用maxpool表示

其中,因为卷积层和全连接层都具有权重系数,因此也被称为权重层,可以得到13+3=16。[池化层不涉及权重]

特点

VGG16的突出特点为简单,体现在:

  1. 卷积层使用相同的卷积核参数。卷积层均表示为conv3-xxx,其中conv3表示卷积层采用的卷积核kernel size为3,3*3是很小的卷积核尺寸,结合其他参数(步幅stride=1,填充方式padding=same),就能保持每一个卷积层(张量)与前一层(张量)保持相同的宽和高,XXX代表卷积层的通道数。
  2. 池化层采用相同的池化核参数。池化层的参数均为2*2,步幅stride=2,max的池化方式,这样就能够使得每一个池化层(张量)的宽和高是前一层(张量)的????。
  3. 模型是由若干卷积层和池化层堆叠(stack)的方式构成,比较容易构成较深的网络结构;

       

 块的结构

第一张图片的右侧,VGG16的卷积层和池化层可以分成不同的Block,从上到下依次编号为Block1~block5;每一个块内包含若干卷积层和一个池化层;并且同一Block内,卷积层的通道(channel)数是相同的,例如:

  • block2中包含2个卷积层,每个卷积层用convv3-128表示;即卷积核为3*3*3,通道数为128;

下图为按照块划分的VGG16结构图,可以结合第二张图进行理解:

 VGG的输入图像是244*244*3的图像张量,随着层数的增加,后一块内的张量相比于前一个块内的张量:

  • 通道数翻倍,从64--128--256--512(保持)
  • 高和宽变减半,由224--112--56--28--14--7

权重参数

VGG的结构虽然简单,但是所含的权重数目很大,达到惊人的139,357,544个参数,这些参数包括卷积核权重,全连接层权重。

  • 例如:第一层卷积,输入图的通道数是3,网络必须学习大小为3*3,通道数为3的卷积核,这样的卷积核有64个,因此总共有(3*3*3)*64=1728个参数
  • 计算全连接层的权重参数数目的方法为:前一层节点数*本层的节点数;因此全连接层的参数分别为:
    7*7*512*4096;4096*4096;4096*1000;

FeiFei Li在CS231的课件中给出了整个网络的全部参数的计算过程(不考虑偏置),如下图所示:

 图中蓝色是计算权重参数数量的部分;红色是计算所需存储容量的部分;

VGG16具有如此之大的参数数目,可以预期他具备很高的拟合能力;但同时具备了缺点:训练时间过长,需要的存储容量大;

实践

通过不断地卷积、池化来提取特征,通过全连接层以及softmax函数可以实现分类;根据结构来写代码,M代表最大池化,base里面定义了每一层通道数;

def vgg(cfg, i, batch_norm=False):
    layers = [] 
    in_channels = i
    for v in cfg:
        if v == 'M':
            layers += [nn.MaxPool2d(kernel_size=2, stride=2)]
        else:
            conv2d = nn.Conv2d(in_channels, v, kernel_size=3, padding=1)
            if batch_norm:
                layers += [conv2d, nn.BatchNorm2d(v),nn.ReLU(inplace=True)]
            else:
                layers += [conv2d, nn.ReLU(inplace=True)]
            in_channels = v
    pool5 = nn.MaxPool2d(kernel_size=3, stride=1, padding=1)
    conv6 = nn.Linear(512,4096)
    conv7 = nn.Linear(4096,4096)
    conv8 = nn.Linear(4096,1000)
    layers += [pool5, conv6,
               nn.ReLU(inplace=True), conv7, nn.ReLU(inplace=True), conv8, nn.ReLU(inplace=True)]
    return layers
base =  [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 'M', 512, 512, 512,   'M',
        512, 512, 512]

基于CIFAR-10数据集实现分类

import torch
import torch.nn as nn
from torch import optim
from torch.autograd import Variable
from torch.utils.data import DataLoader
from torchvision import transforms
from torchvision import datasets
from tqdm import tqdm

'''定义超参数'''
batch_size = 256        # 批的大小
learning_rate = 1e-3    # 学习率
num_epoches = 100       # 遍历训练集的次数


'''
transform = transforms.Compose([
    transforms.RandomSizedCrop(224),
    transforms.RandomHorizontalFlip(),
    transforms.ToTensor(),
    transforms.Normalize(mean = [ 0.485, 0.456, 0.406 ],
                         std  = [ 0.229, 0.224, 0.225 ]),
    ])
'''

'''下载训练集 CIFAR-10 10分类训练集'''
train_dataset = datasets.CIFAR10('./data', train=True, transform=transforms.ToTensor(), download=True)
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)
test_dataset = datasets.CIFAR10('./data', train=False, transform=transforms.ToTensor(), download=True)
test_loader = DataLoader(test_dataset, batch_size=32, shuffle=False)

'''定义网络模型'''
class VGG16(nn.Module):
    def __init__(self, num_classes=10):
        super(VGG16, self).__init__()
        self.features = nn.Sequential(
            #1
            nn.Conv2d(3,64,kernel_size=3,padding=1),
            nn.BatchNorm2d(64),
'''在训练过程中,数据分布会发生变化,对下一层网络的学习带来困难。Batch Normalization将数据拉
回到均值为0,方差为1的正态分布上(归一化),一方面使得数据分布一致,另一方面避免梯度消失、梯度爆
炸。'''
            nn.ReLU(True),
            #2
            nn.Conv2d(64,64,kernel_size=3,padding=1),
            nn.BatchNorm2d(64),
            nn.ReLU(True),
            nn.MaxPool2d(kernel_size=2,stride=2),
            #3
            nn.Conv2d(64,128,kernel_size=3,padding=1),
            nn.BatchNorm2d(128),
            nn.ReLU(True),
            #4
            nn.Conv2d(128,128,kernel_size=3,padding=1),
            nn.BatchNorm2d(128),
            nn.ReLU(True),
            nn.MaxPool2d(kernel_size=2,stride=2),
            #5
            nn.Conv2d(128,256,kernel_size=3,padding=1),
            nn.BatchNorm2d(256),
            nn.ReLU(True),
            #6
            nn.Conv2d(256,256,kernel_size=3,padding=1),
            nn.BatchNorm2d(256),
            nn.ReLU(True),
            #7
            nn.Conv2d(256,256,kernel_size=3,padding=1),
            nn.BatchNorm2d(256),
            nn.ReLU(True),
            nn.MaxPool2d(kernel_size=2,stride=2),
            #8
            nn.Conv2d(256,512,kernel_size=3,padding=1),
            nn.BatchNorm2d(512),
            nn.ReLU(True),
            #9
            nn.Conv2d(512,512,kernel_size=3,padding=1),
            nn.BatchNorm2d(512),
            nn.ReLU(True),
            #10
            nn.Conv2d(512,512,kernel_size=3,padding=1),
            nn.BatchNorm2d(512),
            nn.ReLU(True),
            nn.MaxPool2d(kernel_size=2,stride=2),
            #11
            nn.Conv2d(512,512,kernel_size=3,padding=1),
            nn.BatchNorm2d(512),
            nn.ReLU(True),
            #12
            nn.Conv2d(512,512,kernel_size=3,padding=1),
            nn.BatchNorm2d(512),
            nn.ReLU(True),
            #13
            nn.Conv2d(512,512,kernel_size=3,padding=1),
            nn.BatchNorm2d(512),
            nn.ReLU(True),
            nn.MaxPool2d(kernel_size=2,stride=2),
            nn.AvgPool2d(kernel_size=1,stride=1),
            )
        self.classifier = nn.Sequential(
            #14
            nn.Linear(512,4096),
            nn.ReLU(True),
            nn.Dropout(),
            #15
            nn.Linear(4096, 4096),
            nn.ReLU(True),
            nn.Dropout(),
            #16
            nn.Linear(4096,num_classes),
            )
        #self.classifier = nn.Linear(512, 10)

    def forward(self, x):
        out = self.features(x)
        #        print(out.shape)
        out = out.view(out.size(0), -1)
        #        print(out.shape)
        out = self.classifier(out)
        #        print(out.shape)
        return out


'''创建model实例对象,并检测是否支持使用GPU'''
model = VGG16()
use_gpu = torch.cuda.is_available()  # 判断是否有GPU加速
if use_gpu:
    model = model.cuda()

'''定义loss和optimizer'''
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=learning_rate)

'''训练模型'''

for epoch in range(num_epoches):
    print('*' * 25, 'epoch {}'.format(epoch + 1), '*' * 25)  # .format为输出格式,formet括号里的即为左边花括号的输出
    running_loss = 0.0
    running_acc = 0.0
    for i, data in tqdm(enumerate(train_loader, 1)):

        img, label = data
        # cuda
        if use_gpu:
            img = img.cuda()
            label = label.cuda()
        img = Variable(img)
        label = Variable(label)
        # 向前传播
        out = model(img)
        loss = criterion(out, label)
        running_loss += loss.item() * label.size(0)
        _, pred = torch.max(out, 1)  # 预测最大值所在的位置标签
        num_correct = (pred == label).sum()
        accuracy = (pred == label).float().mean()
        running_acc += num_correct.item()
        # 向后传播
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
    print('Finish {} epoch, Loss: {:.6f}, Acc: {:.6f}'.format(
        epoch + 1, running_loss / (len(train_dataset)), running_acc / (len(train_dataset))))

    model.eval()  # 模型评估
    eval_loss = 0
    eval_acc = 0
    for data in test_loader:  # 测试模型
        img, label = data
        if use_gpu:
            img = Variable(img, volatile=True).cuda()
            label = Variable(label, volatile=True).cuda()
        else:
            img = Variable(img, volatile=True)
            label = Variable(label, volatile=True)
        out = model(img)
        loss = criterion(out, label)
        eval_loss += loss.item() * label.size(0)
        _, pred = torch.max(out, 1)
        num_correct = (pred == label).sum()
        eval_acc += num_correct.item()
    print('Test Loss: {:.6f}, Acc: {:.6f}'.format(eval_loss / (len(
        test_dataset)), eval_acc / (len(test_dataset))))
    print()

# 保存模型
torch.save(model.state_dict(), './cnn.pth')

结果展示

(由于本人电脑带不动,把Epoch改成20)

D:\VGG16\venv\Scripts\python.exe D:/VGG16/main.py
Files already downloaded and verified
Files already downloaded and verified
************************* epoch 1 *************************
782it [24:17,  1.86s/it]
Test Loss: 1.642333, Acc: 0.390700

************************* epoch 2 *************************
782it [18:56,  1.45s/it]
Finish 2 epoch, Loss: 1.541818, Acc: 0.429380
Test Loss: 1.504899, Acc: 0.452700

************************* epoch 3 *************************
782it [30:59,  2.38s/it]
Finish 3 epoch, Loss: 1.302713, Acc: 0.524280
Test Loss: 1.390377, Acc: 0.488000

************************* epoch 4 *************************
782it [30:20,  2.33s/it]
Finish 4 epoch, Loss: 1.139787, Acc: 0.587440
Test Loss: 1.444044, Acc: 0.489400

************************* epoch 5 *************************
782it [18:41,  1.43s/it]
Finish 5 epoch, Loss: 1.006991, Acc: 0.640920
0it [00:00, ?it/s]Test Loss: 1.122627, Acc: 0.600000

************************* epoch 6 *************************
782it [18:36,  1.43s/it]
Finish 6 epoch, Loss: 0.889473, Acc: 0.684000
0it [00:00, ?it/s]Test Loss: 1.399290, Acc: 0.534500

************************* epoch 7 *************************
782it [18:44,  1.44s/it]
Finish 7 epoch, Loss: 0.785274, Acc: 0.721920
0it [00:00, ?it/s]Test Loss: 1.046661, Acc: 0.633700

************************* epoch 8 *************************
782it [18:51,  1.45s/it]
Finish 8 epoch, Loss: 0.688535, Acc: 0.756460
0it [00:00, ?it/s]Test Loss: 1.060111, Acc: 0.652400

************************* epoch 9 *************************
782it [18:40,  1.43s/it]
Finish 9 epoch, Loss: 0.594265, Acc: 0.791100
0it [00:00, ?it/s]Test Loss: 1.010530, Acc: 0.655400

************************* epoch 10 *************************
782it [18:40,  1.43s/it]
Finish 10 epoch, Loss: 0.504314, Acc: 0.822940
0it [00:00, ?it/s]Test Loss: 1.903680, Acc: 0.509000

************************* epoch 11 *************************
782it [18:43,  1.44s/it]
Finish 11 epoch, Loss: 0.428687, Acc: 0.848480
0it [00:00, ?it/s]Test Loss: 1.145717, Acc: 0.652600

************************* epoch 12 *************************
782it [18:48,  1.44s/it]
Finish 12 epoch, Loss: 0.349519, Acc: 0.876460
Test Loss: 1.300202, Acc: 0.638100

************************* epoch 13 *************************
782it [18:41,  1.43s/it]
Finish 13 epoch, Loss: 0.272966, Acc: 0.906200
Test Loss: 1.025958, Acc: 0.706200

************************* epoch 14 *************************
782it [18:40,  1.43s/it]
Finish 14 epoch, Loss: 0.223521, Acc: 0.923620
Test Loss: 2.686784, Acc: 0.500500

************************* epoch 15 *************************
782it [18:34,  1.43s/it]
Finish 15 epoch, Loss: 0.179417, Acc: 0.940540
Test Loss: 1.121853, Acc: 0.712000

************************* epoch 16 *************************
782it [12:18:49, 56.69s/it]
Finish 16 epoch, Loss: 0.132646, Acc: 0.954180
0it [00:00, ?it/s]Test Loss: 1.269879, Acc: 0.702600

************************* epoch 17 *************************
782it [20:21,  1.56s/it]
Finish 17 epoch, Loss: 0.115610, Acc: 0.960840
Test Loss: 1.200604, Acc: 0.722800

************************* epoch 18 *************************
782it [20:40,  1.59s/it]
Finish 18 epoch, Loss: 0.099715, Acc: 0.964880
Test Loss: 1.644888, Acc: 0.651000

************************* epoch 19 *************************
782it [20:54,  1.60s/it]
Finish 19 epoch, Loss: 0.086601, Acc: 0.970900
0it [00:00, ?it/s]Test Loss: 1.844185, Acc: 0.656200

************************* epoch 20 *************************
782it [19:55,  1.53s/it]
Finish 20 epoch, Loss: 0.072556, Acc: 0.978360
Test Loss: 1.343983, Acc: 0.719900


Process finished with exit code 0

后验证,100个Epoch,结果为 

Finish 100 epoch, Loss: 0.000004, Acc: 1.000000
Test Loss: 2.109977, Acc: 0.766200

170500096it [5:54:05, 8025.12it/s] 

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: "opencv-boostdesc-vgg.rar"是一个文件的名称,其中包含了使用OpenCV和BoostDesc算法实现的VGG特征描述符的相关内容。OpenCV是一个开源的计算机视觉库,提供了许多用于图像和视频处理的功能和工具。BoostDesc是OpenCV中的一个特征描述算法,用于描述图像中的关键点。 VGG特征描述符是基于深度学习的特征提取方法之一,具有良好的特征表达能力和稳定性。它通过将图像输入预训练好的VGG网络中,提取最后一层全连接层的输出作为特征向量。这样得到的特征向量能够较好地描述图像中的物体和纹理信息。 "opencv-boostdesc-vgg.rar"文件中可能包含了使用BoostDesc算法结合VGG特征描述符的源代码、训练好的VGG网络模型、以及其他相关的资源文件。通过使用这些资源,我们可以使用OpenCV库中提供的接口来提取图像中的关键点,并将其转化为对应的VGG特征向量。 通过使用这个文件,我们可以进行图像特征提取和匹配等计算机视觉任务。对于图像识别、目标跟踪、图像检索等应用,使用VGG特征描述符可以提取到更具有表达力的特征,提高算法的准确性和鲁棒性。 总之,"opencv-boostdesc-vgg.rar"文件提供了一种使用OpenCV库和BoostDesc算法实现VGG特征描述符的方式,可以用于图像处理与计算机视觉的相关任务。 ### 回答2: opencv-boostdesc-vgg.rar是一个压缩文件,其中包含了使用OpenCV库实现的一种特征描述算法,具体是基于BoostDesc和VGGNet的方法。 特征描述算法是计算机视觉领域中非常重要的一个研究方向,它的目标是从图像中提取出具有代表性和区分度的特征点描述子。这些描述子能够用于在不同图像间进行匹配、检索、物体识别等任务。 BoostDesc是一种基于AdaBoost算法的特征描述方法,它能够在提取特征时考虑到图像的梯度方向信息,从而提高了特征描述子的区分度。 VGGNet是一种深度卷积神经网络,它由牛津大学的Visual Geometry Group开发,被用于大规模图像识别任务。将VGGNet应用于特征描述中,可以利用其强大的图像特征提取能力,生成更具有代表性的特征描述子。 所以,opencv-boostdesc-vgg.rar文件中可能包含了实现利用BoostDesc和VGGNet算法进行图像特征描述的相关源代码、模型文件或测试数据等内容。这些资源可以帮助开发者更方便地使用OpenCV库实现基于该算法的图像特征描述任务。 ### 回答3: opencv-boostdesc-vgg是OpenCV库中的一个特征描述符模块,使用了VGG Net的Boosted实现。该模块用于计算图像中关键点的特征描述符,可用于图像识别、目标检测和图像匹配等任务。 Boosted特征描述符是一种计算图像关键点描述符的方法,它结合了多尺度特征和多核学习器的优势。通过使用Boosting算法和多个权重核函数,能够有效提取图像中的局部特征并进行描述。 VGG Net是一种深度卷积神经网络模型,由牛津大学视觉几何组(Visual Geometry Group)提出。它以配置较深的卷积层和全连接层,能够在图像分类和物体识别任务中取得良好的性能。 通过结合Boosted特征描述符和VGG Net模型,opencv-boostdesc-vgg能够在计算特征描述符时获得更准确和具有较高判别能力的结果。该模块提供了多种参数和选项,可以根据应用需求进行灵活的配置和调整。 总之,opencv-boostdesc-vgg是OpenCV中的一个特征描述符模块,使用VGG Net的Boosted实现。它可以用于计算图像关键点的描述符,适用于多种计算机视觉任务。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值