Deep Learning for Vision
VGG
The very deep convolutional networks of Simonyan and Zisserman (2015), plain stacks of 3×3 convolutions with 11 to 19 weight layers; VGG-16 and VGG-19 became standard backbones and feature extractors, at a cost of about 140 million parameters and 15 to 20 billion multiply-adds per image.
intermediate
VGG is a family of convolutional neural networks for image classification introduced by Karen Simonyan and Andrew Zisserman of Oxford’s Visual Geometry Group, first on arXiv in September 2014 and in final form at ICLR 2015. Its networks are plain feed-forward stacks of convolutions, max pooling and three fully connected layers, with 11 to 19 weight layers; the 16- and 19-layer versions, VGG-16 and VGG-19, are the ones still in use. This article covers VGG as an architecture: the six configurations, why small kernels were stacked, how the networks were trained and evaluated, what they cost, and the roles they took on afterwards. The publication itself, with the team’s ILSVRC 2014 entry and the paper’s revisions, is covered in the VGG paper article.
Problem Addressed
After AlexNet, the leading ImageNet entries of 2013 improved the network mainly at its input, with a smaller first-layer kernel and stride ( at stride 2 instead of at stride 4), and by applying networks densely over whole images and several scales. Simonyan and Zisserman set out to measure the effect of one variable, depth: they fixed the other design choices and added convolutional layers, which they state was feasible because every convolution is . The result was a controlled series of networks rather than a single model.
Architecture
Every configuration follows one template (Simonyan & Zisserman, 2015, §2.1). The input is a RGB crop from which the mean training-set RGB value has been subtracted. Convolutions are with stride 1 and padding 1, so they preserve the spatial size; five max-pooling layers with stride 2 halve it, from 224 to 7. The width starts at 64 channels and doubles after each pooling layer until it reaches 512. Three fully connected layers of 4,096, 4,096 and 1,000 units and a softmax end every network, and every hidden layer is followed by a ReLU. The configurations differ only in how many convolutions sit in each stage (Table 1 of the paper):
| Config. | Weight layers | Convolutions per stage | Notes | Parameters (Table 2) |
|---|---|---|---|---|
| A | 11 | 1, 1, 2, 2, 2 | 133M | |
| A-LRN | 11 | 1, 1, 2, 2, 2 | local response normalization after the first convolution | 133M |
| B | 13 | 2, 2, 2, 2, 2 | 133M | |
| C | 16 | 2, 2, 3, 3, 3 | third convolution of stages 3–5 is | 134M |
| D (VGG-16) | 16 | 2, 2, 3, 3, 3 | all | 138M |
| E (VGG-19) | 19 | 2, 2, 4, 4, 4 | all | 144M |
Depth barely changes the parameter count because most parameters are in the fully connected head, which is the same in every configuration; the multilayer perceptron article splits VGG-16’s parameters between its convolutions and its head. Only A-LRN uses local response normalization, the cross-channel normalization of AlexNet; the authors found that it did not improve A and left it out of the deeper networks.
Stacked 3×3 Convolutions
The paper’s main design argument is about kernel size. Two convolutions with no pooling between them have the receptive field of one convolution, and three have that of a one. The authors give two reasons to prefer the stack (§2.3). It has three ReLUs instead of one, which they say makes the decision function more discriminative. And it has fewer weights: with channels in and out, three layers have
weights, while one layer has , 81% more. Likewise, by our arithmetic, two layers have against for a layer. The authors read the stack as a regularization of the filters, which are forced to decompose into filters with nonlinearities in between. In our reading, without the nonlinearities the composition of convolutions is itself a convolution, so the stack would be a or layer with a constrained kernel (the example below checks this).
The camera-ready version adds a direct test (§4.1): replacing each pair of layers in configuration B by one layer, which keeps the receptive fields, gave a shallower network whose top-1 error on a centre crop was 7% higher.
1×1 Convolutions in Configuration C
Configuration C inserts a convolution as the third layer of stages 3, 4 and 5. With equal input and output widths it is a linear projection of each pixel’s channel vector onto a space of the same dimension, and the ReLU after it adds a nonlinearity without enlarging the receptive field; the authors note the link to Network in Network (Lin et al., 2014). C was more accurate than B but less accurate than D, which has convolutions in the same positions, and the authors conclude that extra nonlinearity helps but spatial context matters too (§4.1).
Stages and Head
Each pooling layer quarters the number of positions and, up to 512 channels, each new stage doubles the width, so the full-width convolutions of stages 1 to 4 cost the same number of multiply-adds; stage 5 keeps 512 channels at and costs a quarter as much per layer (our reasoning, confirmed by the per-stage counts below). The early layers are where activations are largest: each of the two convolutions of stage 1 outputs a tensor, 3.2 million values per image.
The head flattens the output of the last pooling layer into 25,088 values. At test time it is rewritten as convolutions (see Inference), which removes the dependence on the input size.
Parameters and Computation
The script below (run with PyTorch 2.14.1) checks the receptive-field argument and the paper’s parameter counts. It measures the response of stacks of one, two and three layers to a single bright pixel, composes two linear layers into one kernel, compares weight counts at several widths, builds all six configurations from Table 1 and counts their parameters and multiply-adds on a synthetic image (red stripes and a blue disk), prints VGG-16’s shapes stage by stage, and rewrites the head as convolutions.
import torch
import torch.nn as nn
import torch.nn.functional as F
torch.manual_seed(0)
torch.set_num_threads(4)
# 1. Receptive field of stacked 3x3 convolutions, measured as an impulse response.
# Positive random weights and no nonlinearity, so no output can cancel to zero.
impulse = torch.zeros(1, 1, 15, 15)
impulse[0, 0, 7, 7] = 1.0 # one bright pixel in a dark image
for n in (1, 2, 3):
stack = nn.Sequential(*[nn.Conv2d(1, 1, 3, padding=1, bias=False) for _ in range(n)])
for conv in stack:
nn.init.uniform_(conv.weight, 0.1, 1.0)
with torch.no_grad():
nz = stack(impulse)[0, 0] != 0
rows, cols = nz.any(1).nonzero(), nz.any(0).nonzero()
print(f"{n} x (3x3): nonzero response {int(rows.max() - rows.min() + 1)} x "
f"{int(cols.max() - cols.min() + 1)} pixels")
# Without a nonlinearity, two 3x3 layers are one 5x5 layer whose kernel is the
# composition of the two: a constrained 5x5 convolution (C = 8 channels here).
C = 8
c1, c2 = nn.Conv2d(C, C, 3, padding=1, bias=False), nn.Conv2d(C, C, 3, padding=1, bias=False)
delta = torch.zeros(C, C, 9, 9)
delta[torch.arange(C), torch.arange(C), 4, 4] = 1.0 # one impulse per input channel
x = torch.rand(1, C, 32, 32)
with torch.no_grad():
k5 = c2(c1(delta))[:, :, 2:7, 2:7].flip(2, 3).transpose(0, 1) # (C_out, C_in, 5, 5)
diff = (c2(c1(x)) - F.conv2d(x, k5, padding=2))[..., 1:-1, 1:-1].abs().max() # interior
print(f"two linear 3x3 layers vs their composed 5x5 kernel {tuple(k5.shape)}:\n"
f" max |diff| {diff:.1e} away from the border")
# 2. Weights of a stack versus one large kernel, C channels in and out (biases ignored).
for c in (64, 256, 512):
print(f"C = {c:3d}: 2 x 3x3 {2 * 9 * c * c:>9,} vs 5x5 {25 * c * c:>9,} | "
f"3 x 3x3 {3 * 9 * c * c:>9,} vs 7x7 {49 * c * c:>10,} ({49 / 27 - 1:.0%} more)")
# 3. The six configurations of Table 1: numbers are 3x3 conv widths, "1-" marks a
# 1x1 conv (configuration C), "LRN" local response normalization, "M" max pooling.
cfgs = {
"A": [64, "M", 128, "M", 256, 256, "M", 512, 512, "M", 512, 512, "M"],
"A-LRN": [64, "LRN", "M", 128, "M", 256, 256, "M", 512, 512, "M", 512, 512, "M"],
"B": [64, 64, "M", 128, 128, "M", 256, 256, "M", 512, 512, "M", 512, 512, "M"],
"C": [64, 64, "M", 128, 128, "M", 256, 256, "1-256", "M", 512, 512, "1-512", "M",
512, 512, "1-512", "M"],
"D": [64, 64, "M", 128, 128, "M", 256, 256, 256, "M", 512, 512, 512, "M",
512, 512, 512, "M"],
"E": [64, 64, "M", 128, 128, "M", 256, 256, 256, 256, "M", 512, 512, 512, 512, "M",
512, 512, 512, 512, "M"],
}
def vgg(cfg, classes=1000):
layers, c_in = [], 3
for v in cfg:
if v == "M":
layers.append(nn.MaxPool2d(2, stride=2))
elif v == "LRN": # AlexNet's parameters; PyTorch divides alpha by 5
layers.append(nn.LocalResponseNorm(5, alpha=5e-4, beta=0.75, k=2.0))
else:
k, c_out = (1, int(v[2:])) if isinstance(v, str) else (3, v)
layers += [nn.Conv2d(c_in, c_out, k, padding=k // 2), nn.ReLU()]
c_in = c_out
head = [nn.Flatten(), nn.Linear(512 * 7 * 7, 4096), nn.ReLU(), nn.Dropout(0.5),
nn.Linear(4096, 4096), nn.ReLU(), nn.Dropout(0.5), nn.Linear(4096, classes)]
return nn.Sequential(nn.Sequential(*layers), nn.Sequential(*head))
def macs(net, x):
"""Multiply-adds of conv and linear layers for input x (one per weight use)."""
conv = fc = 0
for m in list(net[0]) + list(net[1]):
x = m(x)
if isinstance(m, nn.Conv2d):
conv += m.weight.numel() * x.shape[-2] * x.shape[-1]
elif isinstance(m, nn.Linear):
fc += m.weight.numel()
return conv, fc
# Synthetic 224x224 RGB image: vertical stripes (period 16 px) in red, a blue disk.
yy, xx = torch.meshgrid(torch.arange(224.0), torch.arange(224.0), indexing="ij")
img = torch.zeros(1, 3, 224, 224)
img[0, 0] = 0.5 + 0.5 * torch.sin(2 * torch.pi * xx / 16)
img[0, 2] = (((xx - 140) ** 2 + (yy - 90) ** 2) < 50 ** 2).float()
table2 = {"A": 133, "A-LRN": 133, "B": 133, "C": 134, "D": 138, "E": 144}
print("\nconfig weight layers parameters Table 2 (M) multiply-adds (conv + FC)")
nets = {}
with torch.no_grad():
for name, cfg in cfgs.items():
net = nets[name] = vgg(cfg).eval()
n = sum(p.numel() for p in net.parameters())
depth = sum(isinstance(m, (nn.Conv2d, nn.Linear)) for m in net.modules())
conv, fc = macs(net, img)
print(f"{name:6s} {depth:13d} {n:11,} {table2[name]:5d}"
f" {conv / 1e9:.2f} + {fc / 1e9:.2f} = {(conv + fc) / 1e9:.2f} billion")
# Per-stage shapes of VGG-16 (configuration D) on the synthetic image.
print("\nVGG-16 (D) on a 224x224 image:")
net, h, stage, count, n_stage, m_stage = nets["D"], img, 1, 0, 0, 0
with torch.no_grad():
for m in net[0]:
h = m(h)
if isinstance(m, nn.Conv2d):
count += 1
n_stage += sum(p.numel() for p in m.parameters())
m_stage += m.weight.numel() * h.shape[-2] * h.shape[-1]
before_pool = tuple(h.shape)
if isinstance(m, nn.MaxPool2d):
print(f" stage {stage}: {count} conv {str(before_pool):18s} "
f"pool {str(tuple(h.shape)):17s}{n_stage:>10,} params {m_stage / 1e9:5.2f} GMACs")
stage, count, n_stage, m_stage = stage + 1, 0, 0, 0
# 4. Dense evaluation: the FC layers rewritten as convolutions (7x7, then 1x1, 1x1).
fc6, fc7, fc8 = net[1][1], net[1][4], net[1][7]
dense = nn.Sequential(nn.Conv2d(512, 4096, 7), nn.ReLU(), nn.Conv2d(4096, 4096, 1), nn.ReLU(),
nn.Conv2d(4096, 1000, 1))
with torch.no_grad():
dense[0].weight.copy_(fc6.weight.view(4096, 512, 7, 7)); dense[0].bias.copy_(fc6.bias)
dense[2].weight.copy_(fc7.weight.view(4096, 4096, 1, 1)); dense[2].bias.copy_(fc7.bias)
dense[4].weight.copy_(fc8.weight.view(1000, 4096, 1, 1)); dense[4].bias.copy_(fc8.bias)
a = net(img)
b = dense(net[0](img))
print(f"\nconvolutionalized net, 224x224: score map {tuple(b.shape)}, "
f"max |diff| vs FC net {(a - b.flatten(1)).abs().max():.1e}")
big = F.interpolate(img, size=(384, 384), mode="bilinear", align_corners=False)
s = dense(net[0](big))
print(f"convolutionalized net, 384x384: score map {tuple(s.shape)}, "
f"averaged to {tuple(s.mean(dim=(2, 3)).shape)}")
for q in (256, 384, 512): # output size: floor(Q / 32) - 7 + 1
print(f" test scale Q = {q}: class score map {q // 32 - 6} x {q // 32 - 6}")
1 x (3x3): nonzero response 3 x 3 pixels
2 x (3x3): nonzero response 5 x 5 pixels
3 x (3x3): nonzero response 7 x 7 pixels
two linear 3x3 layers vs their composed 5x5 kernel (8, 8, 5, 5):
max |diff| 4.2e-07 away from the border
C = 64: 2 x 3x3 73,728 vs 5x5 102,400 | 3 x 3x3 110,592 vs 7x7 200,704 (81% more)
C = 256: 2 x 3x3 1,179,648 vs 5x5 1,638,400 | 3 x 3x3 1,769,472 vs 7x7 3,211,264 (81% more)
C = 512: 2 x 3x3 4,718,592 vs 5x5 6,553,600 | 3 x 3x3 7,077,888 vs 7x7 12,845,056 (81% more)
config weight layers parameters Table 2 (M) multiply-adds (conv + FC)
A 11 132,863,336 133 7.49 + 0.12 = 7.61 billion
A-LRN 11 132,863,336 133 7.49 + 0.12 = 7.61 billion
B 13 133,047,848 133 11.18 + 0.12 = 11.31 billion
C 16 133,638,952 134 11.65 + 0.12 = 11.77 billion
D 16 138,357,544 138 15.35 + 0.12 = 15.47 billion
E 19 143,667,240 144 19.51 + 0.12 = 19.63 billion
VGG-16 (D) on a 224x224 image:
stage 1: 2 conv (1, 64, 224, 224) pool (1, 64, 112, 112) 38,720 params 1.94 GMACs
stage 2: 2 conv (1, 128, 112, 112) pool (1, 128, 56, 56) 221,440 params 2.77 GMACs
stage 3: 3 conv (1, 256, 56, 56) pool (1, 256, 28, 28) 1,475,328 params 4.62 GMACs
stage 4: 3 conv (1, 512, 28, 28) pool (1, 512, 14, 14) 5,899,776 params 4.62 GMACs
stage 5: 3 conv (1, 512, 14, 14) pool (1, 512, 7, 7) 7,079,424 params 1.39 GMACs
convolutionalized net, 224x224: score map (1, 1000, 1, 1), max |diff| vs FC net 0.0e+00
convolutionalized net, 384x384: score map (1, 1000, 6, 6), averaged to (1, 1000)
test scale Q = 256: class score map 2 x 2
test scale Q = 384: class score map 6 x 6
test scale Q = 512: class score map 10 x 10
The composed kernel differs from the two-layer stack only in the outermost ring of pixels, where the stack pads its intermediate map with zeros. All six parameter counts round to the paper’s Table 2. The cost runs the other way from the parameters: the fully connected layers take 0.12 billion of VGG-16’s 15.47 billion multiply-adds, under 1%. In stages 1 to 4, each layer with equal input and output width costs billion multiply-adds (for example ). The paper prints no operation count. He et al. (2016) give 15.3 and 19.6 billion FLOPs, which they define as multiply-adds, for VGG-16 and VGG-19; our totals of 15.47 and 19.63 billion match the _ops values in TorchVision’s metadata (see Implementations).
Training
The networks output logits and were trained on the ILSVRC-2012 training set (1.3 million images, 1,000 classes) to minimize the cross-entropy of the correct class , with an L2 penalty on the weights. The recipe follows AlexNet (§3.1): mini-batch stochastic gradient descent with batch size 256 and momentum 0.9, weight decay multiplier , dropout with ratio 0.5 on the first two fully connected layers, and a learning rate of divided by 10 whenever validation accuracy stopped improving, three times in all, stopping after 370,000 iterations (74 epochs).
Initialization. Configuration A was trained from random initialization. For the deeper networks, the first four convolutional layers and the three fully connected layers were copied from a trained A and the layers in between were initialized randomly; the copied layers kept the full learning rate. Random weights were drawn from a normal distribution with zero mean and, as printed, variance , and biases were set to zero. The authors explain the staged scheme by the instability of gradients in deep networks; only the camera-ready version (arXiv v6) adds that after submission they found the networks could be initialized without pre-training using the procedure of Glorot and Bengio (2010). He et al. (2015) used VGG’s configuration B to illustrate why a fixed initial scale stalls deep networks; their analysis assumes a standard deviation of 0.01, not the 0.1 that the printed variance implies. The weight initialization article covers that analysis and the rules that replaced staged training.
Scale jittering. Each training crop is taken at random from an image rescaled so that its shorter side equals the training scale , then flipped horizontally at random and given AlexNet’s random RGB colour shift. With the crop spans the whole shorter side; with it covers a small part of the image, such as one object or part of it. The authors trained single-scale models with and (the latter initialized from the former, with learning rate ), and multi-scale models in which is drawn for each image from , which they describe as scale-jittering augmentation. For speed, multi-scale models were obtained by fine-tuning all layers of the model.
With data parallelism over four NVIDIA Titan Black GPUs in a modified Caffe, one network took two to three weeks to train.
Inference
At test time the image is rescaled so that its shorter side is , the test scale, which need not equal . The authors then apply the network densely (§3.2): the first fully connected layer becomes a convolution and the last two become convolutions, the conversion that fully convolutional networks also use, and the converted network runs on the whole uncropped image. The output is a map of class scores whose size depends on the image, which is averaged over positions into one score vector; the softmax outputs of the image and its mirror image are averaged.
The alternative is multi-crop evaluation, which reruns the network on each crop. For comparison the authors used 50 crops per scale (a grid with flips) at three scales, 150 crops in all. They argue that the two are complementary because of the borders: a crop’s feature maps are padded with zeros, while in dense evaluation the same region sees real image context, which enlarges the effective receptive field. They judged that in practice the extra computation of many crops does not justify the small gain.
With multi-scale testing, the softmax outputs at several are averaged: for a model trained at fixed , and for a model trained with .
Performance
All numbers below are ImageNet ILSVRC-2012 top-1 and top-5 error (see accuracy), from Simonyan and Zisserman (2015), on the validation set unless marked as test, with dense evaluation unless stated.
| Config. | Train | Test | Evaluation | Top-1 (%) | Top-5 (%) | Table |
|---|---|---|---|---|---|---|
| A | 256 | 256 | single scale | 29.6 | 10.4 | 3 |
| A-LRN | 256 | 256 | single scale | 29.7 | 10.5 | 3 |
| B | 256 | 256 | single scale | 28.7 | 9.9 | 3 |
| C | 256 | 256 | single scale | 28.1 | 9.4 | 3 |
| D | 256 | 256 | single scale | 27.0 | 8.8 | 3 |
| D | [256, 512] | 384 | single scale | 25.6 | 8.1 | 3 |
| E | [256, 512] | 384 | single scale | 25.5 | 8.0 | 3 |
| D | [256, 512] | 256, 384, 512 | multi-scale | 24.8 | 7.5 | 4 |
| E | [256, 512] | 256, 384, 512 | multi-scale | 24.8 | 7.5 | 4 |
| D | [256, 512] | 256, 384, 512 | multi-crop and dense | 24.4 | 7.2 | 5 |
| E | [256, 512] | 256, 384, 512 | multi-crop and dense | 24.4 | 7.1 | 5 |
The authors read Table 3 as error decreasing with depth from A to E and saturating at 19 layers; in the table, E is slightly worse than D at fixed and slightly better with scale jittering. Scale jittering in training helped C, D and E, the configurations trained with it, even when they were tested at one scale, and averaging dense and multi-crop outputs beat either alone.
On the ILSVRC-2014 test set, the team’s challenge entry, an ensemble of seven networks, reached 7.3% top-5 error and second place in classification. After the challenge, an ensemble of only D and E reached 7.0% with dense evaluation and 6.8% with dense and multi-crop evaluation combined (Table 6), and a single network 7.0% with multi-crop and dense evaluation (Table 7). The winner, GoogLeNet, had 6.7% with seven networks. A localization system built on configuration D won the localization task with 25.3% test error (Appendix A).
Strengths
- Uniform and easy to modify. One kernel size, one pooling rule and a width that doubles per stage; the configurations differ only in how many layers each stage repeats.
- Transferable features. With weights frozen and a linear SVM on its 4,096-dimensional penultimate activations, D and E together reached 89.7% mAP on PASCAL VOC 2007 classification, against 82.4% for the best earlier representation pretrained on ILSVRC in the authors’ comparison (Appendix B, Table 11). The released weights made this use widespread (see transfer learning).
Limitations
- Cost. VGG-16 needs about 15.5 billion multiply-adds per image by our count (15.3 billion in He et al., 2016), and VGG-19 about 19.6 billion; the released weight files are listed as 528 MB and 548 MB (project page), which by our arithmetic matches bytes of 32-bit floats for VGG-16, 527.8 MiB.
- A heavy head. Almost 90% of the parameters sit in the fully connected layers; later networks replaced such heads with global average pooling (see pooling).
- Depth stopped paying at 19 layers. The original training relied on the staged initialization above, and VGG-16 and VGG-19 tied in multi-scale evaluation (Table 4), which He et al. (2015) cite among signs that added depth was giving diminishing returns; the vanishing and exploding gradients article covers the degradation that residual connections later removed.
- No normalization layers. TorchVision’s reference recipe trains the original VGG networks with an initial learning rate of 0.01 instead of its default 0.1, which it calls too high for networks without batch normalization.
Historical Importance
VGG made depth through repeated small kernels the default design (our summary). He et al. (2016) based the plain baselines of ResNet on “the philosophy of VGG nets”: mostly filters, the same number of filters for the same map size, and twice the filters when the map is halved, so that each layer’s cost stays constant.
Its convolutional layers became the standard backbone of 2015–2016 detection and segmentation networks:
- FCN (Long et al., 2015) compared convolutionalized AlexNet, VGG-16 and GoogLeNet and built on VGG-16, the most accurate of the three, which they found equivalent to VGG-19 for the task.
- Faster R-CNN (Ren et al., 2015) shared VGG-16’s 13 convolutional layers between its region proposal network and its detector.
- SSD (Liu et al., 2016), a one-stage detector, used VGG-16 as its base, converting fc6 and fc7 to convolutional layers with subsampled parameters, changing pool5 to with stride 1, and using dilated (à trous) convolution; the authors report that about 80% of the forward time is spent in the base network.
Its features also became a measure of image similarity. Gatys et al. (2016) built style transfer on a normalized version of VGG-19’s 16 convolutional and 5 pooling layers, with max pooling replaced by average pooling for synthesis, matching content at conv4_2 and style through Gram matrices at conv1_1, conv2_1, conv3_1, conv4_1 and conv5_1. Johnson et al. (2016) trained feed-forward networks against a fixed VGG-16 “loss network”, with feature reconstruction at relu3_3 and style at relu1_2, relu2_2, relu3_3 and relu4_3 for style transfer, and feature reconstruction at relu2_2 for super-resolution (the arXiv v1 preprint gives relu2_2 for the style-transfer feature loss as well); the loss function article defines this perceptual loss. Zhang et al. (2018) asked how well such losses match human judgments and compared five convolutional layers of VGG with AlexNet and SqueezeNet features, the study behind the LPIPS metric (see neural rendering); VGG-16 features also underlie some of the generative-model measures discussed with the Fréchet inception distance.
For classification, VGG was overtaken on cost. GoogLeNet, which won the 2014 classification task, was designed for a budget of 1.5 billion multiply-adds at inference (Szegedy et al., 2015; see Inception), and even ResNet-152, at 11.3 billion FLOPs, cost less than VGG-16 while being more accurate (He et al., 2016; see ResNet).
Variants
- Configurations A to E (2014–2015). The six networks above. D and E, released by the authors, are the ones called VGG-16 and VGG-19; A and B are sometimes called VGG-11 and VGG-13, as in TorchVision.
- VGG with batch normalization. Not part of the paper. TorchVision’s
vgg11_bn,vgg13_bn,vgg16_bnandvgg19_bninsert a batch normalization layer between each convolution and its ReLU. Its VGG-16 weights list 71.592% top-1 and 90.382% top-5 accuracy on ImageNet-1K without batch normalization and 73.360% and 91.516% with it, from one centre crop of an image resized to 256 (as of October 2026). These come from TorchVision’s own simplified training recipe, so they are not comparable with the paper’s dense multi-scale numbers.
Implementations
- Original models. The authors released configurations D and E for Caffe under a Creative Commons Attribution licence, with MatConvNet versions; the project page warns that these toolboxes may lack the dense multi-scale evaluation, so results can differ from the paper’s.
- TorchVision (
torchvision.models.vgg, version 0.29.0): builders for configurations A, B, D and E with and without batch normalization; there is no A-LRN or C. An adaptive average pooling to before the classifier lets the model accept other input sizes. Convolutions are initialized with He’s normal initialization in fan-out mode and linear layers from , and the weights were trained from scratch. The VGG-16 metadata gives 138,357,544 parameters and an_opsvalue of 15.47, matching the counts above. A second VGG-16 weight set,IMAGENET1K_FEATURES, ported from the ssd.pytorch project, has valid convolutional weights only and is meant for feature extraction, with the paper’s original input standardization.
Related
- ResNet
The family of residual convolutional networks introduced by He et al. in 2015, a 7×7 stem and four stages of basic or bottleneck residual blocks at strides 4 to 32, in depths from ResNet-18 to ResNet-152, with its main variants (v1.5, pre-activation, Wide ResNet, ResNeXt, ResNet-D) and the training recipes that changed its accuracy.
- Backbone Network
The pretrained feature-extracting network at the base of a detector, segmenter or other vision model, which turns an image into feature maps at several resolutions for task-specific necks and heads to read.
- Image Classification
Assigning one or more category labels to a whole image, usually by predicting a probability for each of a fixed set of classes.
- FCN
The fully convolutional network of Long, Shelhamer and Darrell (2015), a semantic segmentation network trained end to end from pixels to pixels; it recasts a classification CNN's fully connected layers as convolutions, upsamples coarse class scores in-network, and fuses scores from finer layers (FCN-32s, FCN-16s, FCN-8s).
- Faster R-CNN
A two-stage object detector in which a region proposal network, sharing the detector's convolutional features, replaces hand-designed region proposals, making proposal generation nearly free and the whole detector one network.
- Very Deep Convolutional Networks for Large-Scale Image Recognition
Simonyan and Zisserman's ICLR 2015 paper, first posted in September 2014, which tested plain convolutional networks of 11 to 19 layers built from 3×3 filters, won ILSVRC 2014 localization and placed second in classification, and released the VGG-16 and VGG-19 models.
References
- Simonyan, K. & Zisserman, A. (2015). Very Deep Convolutional Networks for Large-Scale Image Recognition. International Conference on Learning Representations (ICLR). arXiv:1409.1556.
- Simonyan, K. & Zisserman, A. Very Deep Convolutional Networks for Large-Scale Visual Recognition, project page (released models and licence), as of October 2026.
- Glorot, X. & Bengio, Y. (2010). Understanding the Difficulty of Training Deep Feedforward Neural Networks. Proceedings of the 13th International Conference on Artificial Intelligence and Statistics (AISTATS), PMLR 9, 249–256.
- He, K., Zhang, X., Ren, S. & Sun, J. (2015). Delving Deep into Rectifiers: Surpassing Human-Level Performance on ImageNet Classification. IEEE International Conference on Computer Vision (ICCV), 1026–1034.
- He, K., Zhang, X., Ren, S. & Sun, J. (2016). Deep Residual Learning for Image Recognition. IEEE Conference on Computer Vision and Pattern Recognition (CVPR), 770–778.
- Szegedy, C., Liu, W., Jia, Y., Sermanet, P., Reed, S., Anguelov, D., Erhan, D., Vanhoucke, V. & Rabinovich, A. (2015). Going Deeper with Convolutions. IEEE Conference on Computer Vision and Pattern Recognition (CVPR), 1–9.
- Long, J., Shelhamer, E. & Darrell, T. (2015). Fully Convolutional Networks for Semantic Segmentation. IEEE Conference on Computer Vision and Pattern Recognition (CVPR), 3431–3440.
- Ren, S., He, K., Girshick, R. & Sun, J. (2015). Faster R-CNN: Towards Real-Time Object Detection with Region Proposal Networks. Advances in Neural Information Processing Systems 28 (NIPS).
- Liu, W., Anguelov, D., Erhan, D., Szegedy, C., Reed, S., Fu, C.-Y. & Berg, A. C. (2016). SSD: Single Shot MultiBox Detector. European Conference on Computer Vision (ECCV), Lecture Notes in Computer Science, 21–37.
- Gatys, L. A., Ecker, A. S. & Bethge, M. (2016). Image Style Transfer Using Convolutional Neural Networks. IEEE Conference on Computer Vision and Pattern Recognition (CVPR), 2414–2423.
- Johnson, J., Alahi, A. & Fei-Fei, L. (2016). Perceptual Losses for Real-Time Style Transfer and Super-Resolution. European Conference on Computer Vision (ECCV), Lecture Notes in Computer Science, 694–711.
- Zhang, R., Isola, P., Efros, A. A., Shechtman, E. & Wang, O. (2018). The Unreasonable Effectiveness of Deep Features as a Perceptual Metric. IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR), 586–595.
- TorchVision source code (version 0.29.0): torchvision/models/vgg.py.
- TorchVision classification reference scripts (version 0.29.0): references/classification/README.md.