VGG网络 (Visual Geometry Group)

介绍

在 ImageNet 竞赛中 Localization Task(定位任务)第一名和 Classification Task(分类任务)第二名。

论文地址: https://arxiv.org/abs/1409.1556

图1 VGG卷积网络配置

作者使用了11层、13层、16层、19层,分别进行使用。其中对比了卷积核大小为 1 1 1 和卷积核大小为 3 3 3 的效果。(通常使用配置 D )图中使用了LRN

LRN(Local Response Normalization)

Local Response Normalization(LRN)技术主要是深度学习训练时的一种提高准确度的技术方法。其中caffe、tensorflow等里面是很常见的方法,其跟激活函数是有区别的,LRN一般是在激活、池化后进行的一种处理方法。LRN归一化技术首次在AlexNet模型中提出这个概念。通过实验确实证明它可以提高模型的泛化能力,但是提升的很少,以至于后面不再使用,甚至有人觉得它是一个“伪命题”,因而它饱受争议。

LRN局部归一化的灵感来源

在神经生物学中,有一个概念叫做侧抑制(lateral inhibitio),指的是被激活的神经元会抑制它周围的神经元,而 归一化(normalization) 的的目的不就是“抑制”吗,两者不谋而合,这就是局部归一化的动机,它就是借鉴“侧抑制”的思想来实现局部抑制,当我们使用RELU损失函数的时候,这种局部抑制显得很有效果。

归一化的好处

  • 统一量纲。样本数据的评价标准不一样,需要对其量纲化,统一评价标准。这算是应用层面的需求。为了后面数据处理的方便,归一化的确可以避免一些不必要的数值问题。保证输出数据中数值小的不被忽略。
  • 避免神经元饱和。当神经元的激活在接近0或者1时会饱和,在这些区域,梯度几乎为0,在反向传播过程中,局部梯度就会接近0,这会有效地“杀死”梯度。为了程序运行时加快收敛

LRN的提出

LRN 归一化技术首次在AlexNet 模型中提出这个概念。AlexNet 将 LeNet 的思想发扬光大,把 CNN 的基本原理应用到了很深很宽的网络中。AlexNet 主要使用到的新技术点如下:

  1. 成功使用 ReLU 作为 CNN 的激活函数,并验证其效果在较深的网络超过了 Sigmoid,成功解决了 Sigmoid 在网络较深时的梯度弥散问题。虽然 ReLU 激活函数在很久之前就被提出了,但是直到 AlexNet 的出现才将其发扬光大。

  2. 训练时使用 Dropout 随机忽略一部分神经元,以避免模型过拟合。Dropout 虽有单独的论文论述,但是 AlexNet 将其实用化,通过实践证实了它的效果。在 AlexNet 中主要是最后几个全连接层使用了 Dropout 。

  3. 在 CNN 中使用重叠的最大池化。此前 CNN 中普遍使用平均池化,AlexNet 全部使用最大池化,避免平均池化的模糊化效果。并且 AlexNet 中提出让步长比池化核的尺寸小,这样池化层的输出之间会有重叠和覆盖,提升了特征的丰富性。

  4. 提出了 LRN 层,对局部神经元的活动创建竞争机制,使得其中响应比较大的值变得相对更大,并抑制其他反馈较小的神经元,增强了模型的泛化能力。LRN 通过在相邻卷积核生成的 feature map 之间引入竞争,从而有些本来在 feature map 中显著的特征在之后更显著,而在相邻的其他 feature map 中被抑制,这样让不同卷积核产生的 feature map 之间的相关性变小

LRN的公式详解

Hinton 在 2012 年的 Alexnet 网络中给出其具体的计算公式如下:
b x , y i = a x , y i / ( k + α ∑ j = max ⁡ ( 0 , i − n / 2 ) min ⁡ ( N − 1 , i + n / 2 ) ( a x , y j ) 2 ) β {\Large b_{x,y}^{i} =a_{x,y}^{i}/(k+\alpha\sum_{j=\max (0,i-n/2)}^{\min (N-1,i+n/2) }(a_{x,y}^{j})^2 )^\beta} bx,yi​=ax,yi​/(k+αj=max(0,i−n/2)∑min(N−1,i+n/2)​(ax,yj​)2)β

其中, a a a 表示卷积层(包括卷积操作和池化操作)后的输出结果。

这个输出结果 b x , y i b_{x,y}^{i} bx,yi​ 的结构是一个四维数组[batch,height,width,channel]。简单解释一下, batch 就是批次数(每一个 batch 输入一张图片), height 就是图片高度, width 就是图片宽度, channel 就是通道数,可以理解成一批图片中的某一个图片经过卷积操作后输出的神经元个数(或是理解成处理后的图片深度)。

a x , y i a_{x,y}^{i} ax,yi​ 表示在这个输出结构中的一个位置[a,b,c,d],可以理解成在某一张图中的某一个通道下的某个高度和某个宽度位置的点,即第 a 张图的第 d 个通道下的高度为 b 宽度为 c 的点。论文公式中的 N N N 表示通道数 [channel]

a a a, n / 2 n/2 n/2, k k k, α α α, β β β 分别表示函数中的input, depth_radius, bias, alpha, beta,其中 n / 2 , k , α , β n/2, k, α, β n/2,k,α,β都是自定义的,depth_radius 是用于定义邻域长度(像素的邻域也可以看作是根据像素之间的接近程度)。

特别注意一下, ∑ ∑ ∑ 叠加的方向是沿着[channel]方向的,即每个点值的平方和是沿着 a a a 中的第 3 维[channel]方向的,也就是一个点同方向的前面 n / 2 n/2 n/2 个通道(最小为第 0 0 0 个通道)和后 n / 2 n/2 n/2 个通道(最大为第 d − 1 d-1 d−1 个通道)的点的平方和(共 n + 1 n+1 n+1 个点)。而函数的英文注解中也说明了把 input 当成是 d d d 个 3 3 3 维的矩阵,说白了就是把input的通道数当作 3 3 3 维矩阵的个数,叠加的方向也是在通道方向。

i i i 表示第 i i i 个核在位置 ( x , y ) (x,y) (x,y) 运用激活函数ReLU后的输出, n n n 是同一位置上临近的kernel map 的数目, N N N 是 kernel 的总数。参数 K , n , a l p h a , b e l t a K, n, alpha, belta K,n,alpha,belta 都是超参数,一般设置k=2,n=5,aloha=1*e-4,beta=0.75

后期争议

在2015年 Very Deep Convolutional Networks for Large-Scale Image Recognition 提到LRN基本没什么用。
因而在后面的 GoogleNet ,以及之后的一些 CNN 架构模型, LRN 已经不再使用,因为出现了更加有说服能力的块归一化,也称之为 批量归一化 ,即 BN

网络亮点

网络中的亮点:通过堆叠多个 3 × 3 3\times3 3×3 的卷积核来替代大尺度卷积核减少训练参数)。

论文中提到,可以通过堆叠两个 3 × 3 3\times3 3×3 的卷积核替代 5 × 5 5\times5 5×5 的卷积核,堆叠三个 3 × 3 3\times3 3×3 的卷积核替代 7 × 7 7\times7 7×7 的卷积核。它们拥有相同的感受野

感受野

在卷积神经网络中,决定某一层输出结果中一个元素所对应的输入层的区域大小,被称作感受野(Receptive Field)。通俗的解释是,输出 feature map 上的一个单元对应输入层上的区域大小。
感受野计算公式:
F ( i ) = ( F ( i + 1 ) − 1 ) × S t r i d e + K s i z e F(i)=(F(i+1)- 1)\times Stride + Ksize F(i)=(F(i+1)−1)×Stride+Ksize
其中, F ( i ) F(i) F(i) 为第 i i i 层感受野, S t r i d e Stride Stride 为第 i i i 层的步距, K s i z e Ksize Ksize 为卷积核或池化核的尺寸。

例如:
F e a t u r e m a p : F = 1 Feature map:F =1 Featuremap:F=1
C o n v 3 × 3 ( 3 ) : F = ( 1 − 1 ) × 1 + 3 = 3 Conv3\times3(3):F= (1-1)\times1+3=3 Conv3×3(3):F=(1−1)×1+3=3
C o n v 3 × 3 ( 2 ) : F = ( 3 − 1 ) × 1 + 3 = 5 Conv3\times3(2):F = (3-1)\times1+3= 5 Conv3×3(2):F=(3−1)×1+3=5
C o n v 3 × 3 ( 1 ) : F = ( 5 − 1 ) × 1 + 3 = 7 Conv3\times3(1):F=(5- 1)\times1+ 3=7 Conv3×3(1):F=(5−1)×1+3=7

论文中提到,可以通过堆叠两个 3 × 3 3\times3 3×3 的卷积核替代 5 × 5 5\times5 5×5 的卷积核,堆叠三个 3 × 3 3\times3 3×3 的卷积核替代 7 × 7 7\times7 7×7 的卷积核。
使用 7 × 7 7\times7 7×7 卷积核所需参数,与堆叠三个 3 × 3 3\times3 3×3 卷积核所需参数计算如下:(假设输入输出channelC
7 × 7 × C × C = 49 C 2 7\times7\times C \times C= 49C^{2} 7×7×C×C=49C2
3 × 3 × C × C + 3 × 3 × C × C + 3 × 3 × C × C = 27 C 2 3×3×C×C+3×3×C×C+3×3×C×C=27C^{2} 3×3×C×C+3×3×C×C+3×3×C×C=27C2

模型结构

图2 VGG卷积网络示意图

我们选择配置 D D D 并进行展示如图 2 2 2 ,其中,convstride 为 1 1 1 , padding 为 1 1 1; maxpoolsize 为 2 2 2 ,stride 为 2 2 2 。

Pytorch代码详解

model代码

总代码如下:

import torch.nn as nn
import torch# official pretrain weights
model_urls = {'vgg11': 'https://download.pytorch.org/models/vgg11-bbd30ac9.pth','vgg13': 'https://download.pytorch.org/models/vgg13-c768596a.pth','vgg16': 'https://download.pytorch.org/models/vgg16-397923af.pth','vgg19': 'https://download.pytorch.org/models/vgg19-dcbb9e9d.pth'
}class VGG(nn.Module): # nn.Module 神经网络的基类def __init__(self, features, num_classes=1000, init_weights=False):# 特征网络,分类个数,是否初始化权重super(VGG, self).__init__()self.features = featuresself.classifier = nn.Sequential(nn.Dropout(p=0.5),nn.Linear(512*7*7, 4096), nn.ReLU(True),nn.Dropout(p=0.5), # Dropout 层一般加在全连接层防止过拟合提升模型泛化能力nn.Linear(4096, 4096),nn.ReLU(True),nn.Linear(4096, num_classes))if init_weights:self._initialize_weights()def forward(self, x):# N x 3 x 224 x 224x = self.features(x)# N x 512 x 7 x 7x = torch.flatten(x, start_dim=1)# N x 512*7*7x = self.classifier(x)return xdef _initialize_weights(self):for m in self.modules():if isinstance(m, nn.Conv2d):# nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')nn.init.xavier_uniform_(m.weight)# xavier初始化方法if m.bias is not None:nn.init.constant_(m.bias, 0)# 如果采用偏置,默认初始化为 0 elif isinstance(m, nn.Linear):nn.init.xavier_uniform_(m.weight)# nn.init.normal_(m.weight, 0, 0.01)nn.init.constant_(m.bias, 0)def make_features(cfg: list): # 传入 cfg 字典,选中框架的列表layers = [] # 定义空的列表in_channels = 3 # 输入为 RGB 图像,所以通道数是 3 for v in cfg:if v == "M": # 是否为 MaxPooling 层layers += [nn.MaxPool2d(kernel_size=2, stride=2)] # 定义 MaxPooling 层else:conv2d = nn.Conv2d(in_channels, v, kernel_size=3, padding=1) # 第一次输入为 3 ,之后为 v (卷积核数量)= in_channels, 卷积核统一为 3*3 layers += [conv2d, nn.ReLU(True)]# 每一层后都是使用的 ReLU 激活函数in_channels = vreturn nn.Sequential(*layers) # 非关键字参数传入cfgs = {# 数字表示 3 * 3 的卷积核的个数,'M' 表示 maxpooling 层'vgg11': [64, 'M', 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'],# vgg11, A'vgg13': [64, 64, 'M', 128, 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'],# vgg13, B'vgg16': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 'M', 512, 512, 512, 'M', 512, 512, 512, 'M'],# vgg16, D'vgg19': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 256, 'M', 512, 512, 512, 512, 'M', 512, 512, 512, 512, 'M'],# vgg19, E
}def vgg(model_name="vgg16", **kwargs):# 默认为 vgg16assert model_name in cfgs, "Warning: model number {} not in cfgs dict!".format(model_name)# assert(in),被称为“断言”。意思是:运行到该处时,表达式“in”必须被满足,否则就出错。通常程序会停止运行,产生一个断言意外。cfg = cfgs[model_name]model = VGG(make_features(cfg), **kwargs)# **kwargs 允许你将不定长度的键值对, 作为参数传递给一个函数。 如果你想要在一个函数里处理带名字的参数, 你应该使用**kwargsreturn model

一、 cfgs 字典:

分别表示VGG11层,13层,16层,19层,分别对应A,B,D,E。

cfgs = {# 数字表示 3 * 3 的卷积核的个数,'M' 表示 maxpooling 层'vgg11': [64, 'M', 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'],# vgg11, A'vgg13': [64, 64, 'M', 128, 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'],# vgg13, B'vgg16': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 'M', 512, 512, 512, 'M', 512, 512, 512, 'M'],# vgg16, D'vgg19': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 256, 'M', 512, 512, 512, 512, 'M', 512, 512, 512, 512, 'M'],# vgg19, E
}

二、传入选择的网络:

def make_features(cfg: list): # 传入 cfg 字典,选中框架的列表layers = [] # 定义空的列表in_channels = 3 # 输入为 RGB 图像,所以通道数是 3 for v in cfg:if v == "M": # 是否为 MaxPooling 层layers += [nn.MaxPool2d(kernel_size=2, stride=2)] # 定义 MaxPooling 层else:conv2d = nn.Conv2d(in_channels, v, kernel_size=3, padding=1) # 第一次输入为 3 ,之后为 v (卷积核数量)= in_channels, 卷积核统一为 3*3 layers += [conv2d, nn.ReLU(True)]# 每一层后都是使用的 ReLU 激活函数in_channels = vreturn nn.Sequential(*layers) # 非关键字参数传入

三、Seuquential 函数的用法:

# Using Sequential to create a small model. When `model` is run,
# input will first be passed to `Conv2d(1,20,5)`. The output of
# `Conv2d(1,20,5)` will be used as the input to the first
# `ReLU`; the output of the first `ReLU` will become the input
# for `Conv2d(20,64,5)`. Finally, the output of
# `Conv2d(20,64,5)` will be used as input to the second `ReLU`
model = nn.Sequential(nn.Conv2d(1,20,5), # in_channels, out_channels, kernel_sizenn.ReLU(),nn.Conv2d(20,64,5),nn.ReLU())# Using Sequential with OrderedDict. This is functionally the
# same as the above code
model = nn.Sequential(OrderedDict([('conv1', nn.Conv2d(1,20,5)),('relu1', nn.ReLU()),('conv2', nn.Conv2d(20,64,5)),('relu2', nn.ReLU())]))

* )的作用在形参上,代表这个位置接收任意多个非关键字参数,转化成元组方式;作用在实参上,代表的是将输入迭代器拆成一个个元素。

四、Dropout层

Dropout 层一般加在全连接层防止过拟合提升模型泛化能力。而很少见到卷积层后接 Drop out (原因主要是 卷积参数少,不易过拟合)

五、 Xavier

基本思想是通过网络层时,使得输入和输出的方差相同,包括前向传播后向传播

如果初始值很小,那么随着层数的传递,方差就会趋于0,此时输入值 也变得越来越小,在sigmoid上就是在0附近,接近于线性,失去了非线性。
如果初始值很大,那么随着层数的传递,方差会迅速增加,此时输入值变得很大,而sigmoid在大输入值写倒数趋近于0,反向传播时会遇到梯度消失的问题。
其他的激活函数同样存在相同的问题。

六、**kwargs的用法

**kwargs允许你将不定长度的键值对, 作为参数传递给一个函数。 如果你想要在一个函数里处理带名字的参数, 你应该使用 **kwargs

train代码

import os
import sys
import jsonimport torch
import torch.nn as nn
from torchvision import transforms, datasets
import torch.optim as optim
from tqdm import tqdmfrom model import vggdef main():device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") # 有的话使用GPUprint("using {} device.".format(device))data_transform = {"train": transforms.Compose([transforms.RandomResizedCrop(224),# 随机裁剪到 224*224transforms.RandomHorizontalFlip(),# 随机水平翻转transforms.ToTensor(),transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]),"val": transforms.Compose([transforms.Resize((224, 224)),transforms.ToTensor(),transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])}data_root = os.path.abspath(os.path.join(os.getcwd(), "../.."))  # get data root path# os.getcwd() 方法用于返回当前工作目录,os.path.abspath()返回绝对路径image_path = os.path.join(data_root, "data_set", "flower_data")  # flower data set pathassert os.path.exists(image_path), "{} path does not exist.".format(image_path)train_dataset = datasets.ImageFolder(root=os.path.join(image_path, "train"),transform=data_transform["train"])train_num = len(train_dataset)# {'daisy':0, 'dandelion':1, 'roses':2, 'sunflower':3, 'tulips':4}flower_list = train_dataset.class_to_idx # 通过这个获取分类名称对应索引。cla_dict = dict((val, key) for key, val in flower_list.items())# write dict into json filejson_str = json.dumps(cla_dict, indent=4) # 见二with open('class_indices.json', 'w') as json_file:json_file.write(json_str)batch_size = 32nw = min([os.cpu_count(), batch_size if batch_size > 1 else 0, 8])  # number of workersprint('Using {} dataloader workers every process'.format(nw))train_loader = torch.utils.data.DataLoader(train_dataset,batch_size=batch_size, shuffle=True,num_workers=nw)validate_dataset = datasets.ImageFolder(root=os.path.join(image_path, "val"),transform=data_transform["val"])val_num = len(validate_dataset)validate_loader = torch.utils.data.DataLoader(validate_dataset,batch_size=batch_size, shuffle=False,num_workers=nw)print("using {} images for training, {} images for validation.".format(train_num,val_num))# test_data_iter = iter(validate_loader)# test_image, test_label = test_data_iter.next()model_name = "vgg16"net = vgg(model_name=model_name, num_classes=5, init_weights=True)net.to(device)loss_function = nn.CrossEntropyLoss()optimizer = optim.Adam(net.parameters(), lr=0.0001)epochs = 30best_acc = 0.0save_path = './{}Net.pth'.format(model_name)train_steps = len(train_loader)for epoch in range(epochs):# trainnet.train()running_loss = 0.0train_bar = tqdm(train_loader, file=sys.stdout)for step, data in enumerate(train_bar):images, labels = dataoptimizer.zero_grad() # zero the parameter gradientsoutputs = net(images.to(device))loss = loss_function(outputs, labels.to(device))loss.backward() # 反向传播optimizer.step() # 进行参数更新# print statisticsrunning_loss += loss.item()train_bar.desc = "train epoch[{}/{}] loss:{:.3f}".format(epoch + 1,epochs,loss)# 为了能够看到训练的实施进度,打印所需的时间# validatenet.eval()acc = 0.0  # accumulate accurate number / epochwith torch.no_grad():val_bar = tqdm(validate_loader, file=sys.stdout)for val_data in val_bar:val_images, val_labels = val_dataoutputs = net(val_images.to(device))predict_y = torch.max(outputs, dim=1)[1]acc += torch.eq(predict_y, val_labels.to(device)).sum().item()val_accurate = acc / val_numprint('[epoch %d] train_loss: %.3f  val_accuracy: %.3f' %(epoch + 1, running_loss / train_steps, val_accurate))if val_accurate > best_acc:best_acc = val_accuratetorch.save(net.state_dict(), save_path)print('Finished Training')if __name__ == '__main__':main()

值得注意的是,由于预训练的模型是在 ImageNet 中训练的。
在迁移学习中,我们需要减去 ImageNet 图像的均值分量[123.68,116.78,103.94]
我们这里从头训练的,所以不需要减去,具体见之后的 ResNet 网络。

一、ImageFolder

ImageFolder(root, transform = None, target_transform = None, loader = default_loader)

root : 在指定的 root 路径下面寻找图片。
transform:对 PIL Image 进行转换操作,transform 输入是 loader 读取图片返回的对象。
target_transform :对 label 进行变换。
loader:指定加载图片的函数,默认操作是读取 PIL image 对象。

二、json.dumps()

json库的一些用法:

方法 作用
json.dumps() 将 python 对象编码成 json 字符串
json.loads() 将 json 字符串解码成 python 对象
json.dump() 将 python 中的对象转化成 json 储存到文件中
json.load() 将文件中的 json 的格式转化成 python 对象提取出来

三、model.eval() 和 model.train()

a) model.eval():不启用 BatchNormalization 和 Dropout 。此时 pytorch 会自动把 BN 和 DropOut 固定住,不会取平均,而是用训练好的值。不然的话,一旦 test 的 batch_size 过小,很容易就会因 BN 层导致模型 performance 损失较大。

b) model.train() :启用 BatchNormalization 和 Dropout 。 在模型测试阶段使用 model.train() 让 model 变成训练模式,此时 dropout 和 batch normalization 的操作在训练中起到防止网络过拟合的问题。

四、Tqdm

Tqdm 是一个快速,可扩展的 Python 进度条,可以在 Python 长循环中添加一个进度提示信息,用户只需要封装任意的迭代器 tqdm(iterator) 。

Predict代码

import os
import jsonimport torch
from PIL import Image
from torchvision import transforms
import matplotlib.pyplot as pltfrom model import vggdef main():device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")data_transform = transforms.Compose([transforms.Resize((224, 224)),transforms.ToTensor(),transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])# load imageimg_path = "../tulip.jpg" # 图片路径assert os.path.exists(img_path), "file: '{}' dose not exist.".format(img_path)img = Image.open(img_path)plt.imshow(img) # 展示图片# [N, C, H, W]img = data_transform(img)# expand batch dimensionimg = torch.unsqueeze(img, dim=0) # unsqueeze对数据维度进行扩充,加维度# read class_indictjson_path = './class_indices.json'assert os.path.exists(json_path), "file: '{}' dose not exist.".format(json_path)with open(json_path, "r") as f:class_indict = json.load(f)# create modelmodel = vgg(model_name="vgg16", num_classes=5).to(device)# load model weightsweights_path = "./vgg16Net.pth"assert os.path.exists(weights_path), "file: '{}' dose not exist.".format(weights_path)model.load_state_dict(torch.load(weights_path, map_location=device))model.eval()with torch.no_grad():# predict classoutput = torch.squeeze(model(img.to(device))).cpu() # squeeze 维度的压缩predict = torch.softmax(output, dim=0)predict_cla = torch.argmax(predict).numpy()print_res = "class: {}   prob: {:.3}".format(class_indict[str(predict_cla)],predict[predict_cla].numpy())plt.title(print_res)for i in range(len(predict)):print("class: {:10}   prob: {:.3}".format(class_indict[str(i)],predict[i].numpy()))plt.show()if __name__ == '__main__':main()

结果展示

图3 训练结果展示 图4 预测结果展示 (左)预测结果 (右)预测图片——向日葵

值得注意的是,虽然预测正确,但是在另外一次训练中,将此图片判断成了 DandelionFlower (蒲公英花),由于两种花都是黄色,且选取的向日葵为一大群,所以预测错误也是正常的,期待更好的网络能够解决这个问题。

VGG (Visual Geometry Group)相关推荐

  1. VGG(visual geometry group,超分辨率测试序列)

    VGG(visual geometry group,超分辨率测试序列)   这里有两个表格,其中第一个表格是描述的是VGGNet的诞生过程.为了解决初始化(权重初始化)等问题,VGG采用的是一种Pre ...

  2. Visual Geometry Group 资源(vgg)

    1.Phog 代码:http://www.robots.ox.ac.uk/~vgg/research/caltech/phog.html 其它链接:http://www.robots.ox.ac.uk ...

  3. CV算法复现(分类算法3/6):VGG(2014年 牛津大学)

    致谢:霹雳吧啦Wz:https://space.bilibili.com/18161609 目录 致谢:霹雳吧啦Wz:https://space.bilibili.com/18161609 1 本次要 ...

  4. 基于深度学习模型的花卉图像分类代码_实战 | 基于深度学习模型VGG的图像识别(附代码)...

    本文演示了如何使用百度公司的PaddlePaddle实现基于深度学习模型VGG的图像识别. 准备工作 VGG简介 牛津大学VGG(Visual Geometry Group)组在2014年ILSVRC ...

  5. VGG网络详解(实现猫猫和狗狗识别)

    VGG VGG在2014年由牛津大学著名研究组vGG (Visual Geometry Group)提出,斩获该年lmageNet竞赛中Localization Task (定位任务)第一名和 Cla ...

  6. VGG网络结构的搭建(pytorch以及百度飞桨)

    对应百度飞桨页面 VGG网络是在2014年由牛津大学著名研究组VGG (Visual Geometry Group) 提出. 下载花分类数据集 import requests import os im ...

  7. 深度学习(六)——CNN进化史

    https://antkillerfarm.github.io/ CNN进化史 计算机视觉 6大关键技术: 图像分类:根据图像的主要内容进行分类.数据集:MNIST, CIFAR, ImageNet ...

  8. 人工智能 - paddlepaddle飞桨 - 深度学习基础教程 - 图像分类

    图像分类 本教程源代码目录在book/image_classification,初次使用请您参考Book文档使用说明. 说明:¶ 1.硬件环境要求: 本文可支持在CPU.GPU下运行 2.Docker ...

  9. 单目深度估计方法:现状与前瞻

    今天为大家推荐的是<中国图象图形学报>2019年第12期论文<单目深度估计技术进展综述>,该文由中国图象图形学学会成像探测与感知专委会组织,北京理工大学刘越教授等学者撰写,对国 ...

最新文章

  1. Java 利用replaceAll 替换中括号
  2. 龙岗网络推广为SEO优化人员介绍如何合理处理垃圾外链?
  3. 南通工学院计算机系97顾月,南通大学电气工程学院
  4. 数据分层/ODS/DW/DM
  5. python中__init__和__new__方法的使用
  6. 第六届蓝桥杯决赛JavaC组真题——详细答案对照(完整版)
  7. 捕获异常VS抛出异常
  8. 站在K2角度审视流程--任务的独占与释放
  9. linux 解压rar密码,linux下rar包的压缩与解压方案
  10. 弱电施工流程及规范(二)
  11. Python--turtle绘图模块讲解
  12. AJPFX分享java排序之希尔排序
  13. Eurek自我保护机制
  14. [安全攻防进阶篇] 八.那些年的熊猫烧香及PE病毒行为机理分析
  15. python ctp接口_GitHub - keli/ctp-python: 穿透式监管版本CTP接口的Python封装
  16. MMDetection2.XX-Backbone之ResNet源码最全解析
  17. 诺基亚c1 02java软件_诺基亚c1-02详细刷机步骤
  18. 用计算机编程解决土方调配,LINGO在土方调配计算问题应用.doc
  19. QQ空间技术架构之深刻揭秘
  20. .net ImageProcessor组件转换图片格式

热门文章

  1. 计算机盘无法访问,电脑F盘没法访问,参数不正确?
  2. mapbox初步使用
  3. border-radius简介
  4. 浅谈几种网络攻击及攻防原理
  5. 使用 DDL 语句分别创建仓库表、供应商表、产品表和入库表,并对其进行操作
  6. three.js使用obj + mtl文件加载材质问题
  7. 计算机怎样打印机驱动程序,详细教您电脑怎么安装打印机驱动
  8. 计算机组成原理 | 第八章《计算机的外围设备》、第九章《输入/输出系统》 学习笔记
  9. uniapp 子组件 mounted 刷新
  10. 大河奔流的精神 ——俞敏洪