经典分类网络(传送门):目录索引、LeNet、AlexNet、VGG、ResNet、Inception、DenseNet、SeNet。
LeNet诞生于1998年,网络结构比较完整,包括卷积层、pooling层、全连接层。被认为是CNN的鼻祖,LeNet结构如下:
输入:32*32大小的原图;
1)6个5*5的核卷积,得到28*28*6的特征图,pooling:2*2的核,步长为2,结果为14*14*6的特征图;
2)16个5*5*6的核卷积,得到10*10*16的图,pooling:2*2的核,步长为2,结果为5*5*16的特征图;
3)120个5*5*16的核卷积,得到120*1的向量,进入全连接层;
4)由120*84*10的BP网络构成全连接层,输出层是10个节点,对应0~9十个类别的数字。
损失函数:
网络性能指标:
1)损失函数越小越好,小心过拟合;
2)训练样本的正确率;
3)测试样本的正确率。
特点:
卷积->池化->卷积->池化->全连接->分类。
卷积层深度:从小到大,从大到小。(6->16->Dense->全连接)
卷积层和池化层后输出大小方法:
W:图像宽,H:图像高,D:图像深度(通道数),F:卷积核宽高,N:卷积核(过滤器)个数,S:步长,P:用零填充个数。
卷积后输出图像大小:
Width=[(W-F+2P)/S]+1
Height=[(H-F+2P)/S]+1
卷积后输出图像深度:N=D
输出图像大小: (Width,Height,N)
weight个数: F*F*D*N
bias个数: N
Same Pading计算方式:
pad = (kernel_size - 1) // 2
总结:卷积输出大小=[(输入大小-卷积核(过滤器)大小+2*P)/步长]+1,向下取整
通用的卷积时padding 的选择
如卷积核宽高为3时 padding 选择1
如卷积核宽高为5时 padding 选择2
如卷积核宽高为7时 padding 选择3
池化后输出图像大小:
W=[(W-F)/S]+1
H=[(H-F)/S]+1
池化后输出图像深度: N=D
总结:池化输出大小=[(输入大小-卷积核(过滤器)大小)/步长]+1,向下取整
代码实现:
import torch as t
import torch.nn as nn
__all__ = ["LeNet"]
class LeNet5(nn.Module):
def __init__(self, num_classes=-1): # num_classes=10
super(LeNet5, self).__init__()
self.num_classes = num_classes
self.features = nn.Sequential(
nn.Conv2d(1, 6, kernel_size=5, stride=1), nn.Tanh(), # [(32-5+2*0)/1]+1=28
nn.AvgPool2d(kernel_size=2), # 28/2=14
nn.Conv2d(6, 16, kernel_size=5, stride=1), nn.Tanh(), # [(14-5+2*0)/1]+1=10
nn.AvgPool2d(kernel_size=2), # 10/2=5
nn.Conv2d(16, 120, kernel_size=5, stride=1), nn.Tanh()) # [(5-5+2*0)/1]+1=1
if self.num_classes > 0:
self.classifier = nn.Sequential(
nn.Linear(120, 84),
nn.Tanh(),
nn.Linear(84, num_classes),
)
def forward(self, x): # (1,32,32)
x = self.features(x) # (120,)
if self.num_classes > 0:
x = self.classifier(x.squeeze()) # (84,)
return x
if __name__ == '__main__':
x = t.randn(1, 1, 32, 32) # 0~1正态分布
model = LeNet()
y = model(x)
print(y, y.shape)
pass
完整代码地址:https://github.com/alexzhang19/pytorch-classify/blob/master/models/lenet.py
下一篇:AlexNet论文翻译,传送门:分类网络目录索引。