系列文章目录
文章目录
- 系列文章目录
- 前言
- 一、VGG类的搭建
- 1.源码
- 2.初始化类
- 2.1 初始化函数
- 2.2 前向传播函数 forward(self,x)
- 二、卷积补充
- 卷积
前言
对于标准的 VGG net 输入图像的尺寸是 24 x 24,进行 32 维的下采样之后得到一个 7 x 7 的特征图,然后用 FC 层完成分类。在这里我们要对数据进行增强,然后 resize 到 28 * 28 的尺寸。如果直接使用 VGG net 没有办法直接进行 32 x 32 下采样的。,下面我们来搭建这个串联的网络结构。
一、VGG类的搭建
1.源码
import torch
import torch.nn.functional as F # 使用这个库进行softmax
import torch.nn as nn # 使用这个库进行卷积# 定义类
class VggBase(nn.Module):def __init__(self):# 第一步初始化函数super(VggBase, self).__init__()# 定义一些列算子,每次经过一个pooling层卷积加倍# 第一个卷积采用序列, 输入的数据 3 x 28 x 28 ,crop 之后self.conv1 = nn.Sequential(nn.Conv2d(3, 64, kernel_size=3,padding=1),nn.BatchNorm2d(64), # 输出的通道nn.ReLU())self.max_pooling1 = nn.MaxPool2d(kernel_size=2, stride=2) # 下采样减半# 第一个卷积采用序列之后, 输出的数据 14*14*64self.conv2_1 = nn.Sequential(nn.Conv2d(64, 128, kernel_size=3,padding=1),nn.BatchNorm2d(128), # 输出的通道nn.ReLU())# 目前卷积14*14*128self.conv2_2 = nn.Sequential(nn.Conv2d(128, 128, kernel_size=3,padding=1),nn.BatchNorm2d(128), # 输出的通道nn.ReLU())self.max_pooling2= nn.MaxPool2d(kernel_size=2, stride=2)# 输出 7*7*128self.conv3_1 = nn.Sequential(nn.Conv2d(128, 256, kernel_size=3,padding=1),nn.BatchNorm2d(256), # 输出的通道nn.ReLU())# 目前卷积7*7*256self.conv3_2 = nn.Sequential(nn.Conv2d(256, 256, kernel_size=3,padding=1),nn.BatchNorm2d(256), # 输出的通道nn.ReLU())# 由于图片7*7奇数,需要padding = 1,防止损失self.max_pooling3= nn.MaxPool2d(kernel_size=2, stride=2,padding=1)# 输出4*4*256self.conv4_1 = nn.Sequential(nn.Conv2d(256, 512, kernel_size=3,padding=1),nn.BatchNorm2d(512), # 输出的通道nn.ReLU())# 目前卷积4*4*512self.conv4_2 = nn.Sequential(nn.Conv2d(512, 512, kernel_size=3,padding=1),nn.BatchNorm2d(512), # 输出的通道nn.ReLU())self.max_pooling4= nn.MaxPool2d(kernel_size=2, stride=2)#全连接层,目前的数据 batch * 4 * 4 * 512 我们要变成--> batch * (4 * 512)self.fc1 = nn.Linear(4*512,10) # 第一个输入参数的个数是 maxpooling之后特征图的大小 512*2*2# 输出 2*2*512def forward(self,x):# st1取出batch_size,就是x的第0维batch_size = x.size(0)#st2 定义输出,取第一个卷积out = self.conv1(x)out = self.max_pooling1(out)out = self.conv2_1(out)out = self.conv2_2(out)out = self.max_pooling2(out)out = self.conv3_1(out)out = self.conv3_2(out)out = self.max_pooling3(out)out = self.conv4_1(out)out = self.conv4_2(out)out = self.max_pooling4(out)# 全连接层,开始变形out = out.view(batch_size,-1) # -1 会根据具体的维度进行计算 -1 就是把 batch * 2 *2 * 512变成 batch*(4*512) 也就是batch_size * nout = self.fc1(out) # 实际上VGG有三个fc层,输出结果 batch_size * 10out = F.log_softmax(out, dim=1) #return outdef VGGNet():return VGGNet()
网络的搭建不难,一个初始化类和一个前向传播函数,我觉得重点是要去理解一下形状是怎么变化的。如何控制padding和stride来变化形状,不知道的话,下面讲解将会很难听懂,去补基础吧,哈哈哈哈,点击这里,1-9集。
2.初始化类
2.1 初始化函数
重点在卷积层,定义了四个卷积层和4个池化层,卷积一次,采用序列定义卷积层。
self.conv1 = nn.Sequential(nn.Conv2d(3,64,kernel_size=3,padding=1),nn.BatchNorm2d(64), nn.ReLU())
-
nn.Conv2d(3,64,kernel_size=3,padding=1)
- 3代表输入通道,此处RGB通道等于3
- 64是输出通道,也就是卷积核的数量
- kernel_size = 3,卷积核的大小 3*3 的方阵
- padding = 1,在图片四周补一圈0,保证卷积不改变图片尺寸,具体还得看情况。
- 卷积的步长stride默认等于1,一般不调整。
-
nn.BatchNorm2d(64) 归一化函数,64是一定要与卷积之后的输出通道相同。
-
nn.ReLU() 激活函数,负数变0,正数不变。
池化层:降维,改变图片大小,不改变通道数目
self.max_pooling1 = nn.MaxPool2d(kernel_size=2, stride=2)
- kernel_size=2 卷积核大小 2*2
- stride = 2, 向右移动两个步长,卷积核多大就移动几个步长。看教程一般都是2
后面无非就是重复多建立几个层,卷积和池化呗,注意,VGGNet池化之后,图片大小变少,下一次卷积时输出通道翻倍(卷积核翻倍)
2.2 前向传播函数 forward(self,x)
前向传播就是调用前向传播函数,然后这个函数调用初始化中的操作算子,卷积池化,在卷积再池化 … 全链接层,分类或回归。顺序与初始化中的操作算子一样,只不过全连接层要处理一下。
张量tensor(batch, C, H,W)翻译(一组图片,特征数目,图片竖高,图片横宽),第一步统计一组图片的数量,就是第 0 维。为什么分批次?6w张图片或者80w张,内存不够,我们分组传入图片训练。
全链接层处理:
out = self.fc1(out)
需要参数是(batch,n)目前是(batch,512,2,2)- 张量处理:
- 继续卷积池化:(batch,512,2,2)变成(batch,1024,1,1)由于(1,1)=1个元素,即(batch,1024)
out = out.view(batch_size,-1)
自动转化,最好是卷积吧。
out = self.fc1(out)
数据形成类别,形状 batch *10,每张图片在10个类别中都有数据,out = F.log_softmax(out, dim=1)
统计形成类别的概率,选择最大相似度的打印。
二、卷积补充
卷积
大家看了基础视频,我也发表一下我的看法。卷积就是特征提取,但是特征又是什么?我们熟悉一下一张图片的输入和张量。

图片的tensor张量=(batch, C, H,W)翻译(一组图片,特征数目,图片竖高,图片横宽) 一组图片的数量是不会改变的,改变的只有特征数目,图片的大小(最后两个维度控制),卷积的过程理解成特征提取变多,图片变小,越是细小,特征细节越多嘛。开始可以理解成只有红绿蓝这三种特征。

用一个卷积核33去和 R G B 这三张图片的33区域做内积(对应位置相乘)再相加,三张图片得到三个数,三个数再次求和得到一个元素,这就是卷积一个视野所得的新元素。然后滑动去卷积下一个元素,图片像素不够,为了防止遗漏细节还需要拓展叫做padding. padding = 1 ,在四周加一圈 0。比如这张 55 的图片,33的卷积核,图片大小只能是卷积核的倍数才能完美契合,否则需要拓展,拓展后可以比倍数多一点
我们进行padding = 1,四周加一圈0,变成77,多了一点无妨。卷积后的图片像素依旧是 55,如下图所示。我们卷积一般不改变图片大小,仅仅提升通道数目,特征数目。池化的时候才改变图片大小。怎么加padding呢?有一个技巧,选择卷积核中心,外扩,33 扩一圈,55 扩两圈 实际上也只有 33,发现不成倍数就 扩建一圈padding = 1, 5 * 5 的卷积核大了,完全可以使用两个33的卷积核代替,而且计算还小。