第2周学习:卷积神经网络基础

目录

Part1 视频学习 

一、绪论

卷积神经网络的应用

传统神经网络vs卷积神经网络

二、基本组成结构

卷积

池化 Polling:缩放

全连接:

三、卷积神经网络典型结构

AlexNet:2012年ImageNet图像分类竞赛中冠军

ZFNet:2013年ImageNet图像分类竞赛中冠军

VGG:一个更深的网络(原来只有8层),2014年第二名,先训练前11层,把参数固定住,再训练后面的

GoogleNet:2014年第一名

ResNet:残差学习网络,2015年ILSVRC竞赛冠军 ,深度有152层

Part2代码练习

MNIST 数据集分类

1. 加载数据 (MNIST)

2. 创建网络

3. 在小型全连接网络上训练(Fully-connected network)

4. 在卷积神经网络上训练

5. 打乱像素顺序再次在两个网络上训练与测试

CIFAR10 数据集分类

定义网络,损失函数和优化器: 

训练网络:

使用 VGG16 对 CIFAR10 分类

1. 定义 dataloader

2. VGG 网络定义

3. 网络训练

4. 测试验证准确率:

Part3 思考题:

1、dataloader 里面 shuffle 取不同值有什么区别?

2、transform 里,取了不同值,这个有什么区别?

3、epoch 和 batch 的区别?

4、1x1的卷积和 FC 有什么区别?主要起什么作用?

5、residual leanring 为什么能够提升准确率?

6、代码练习二里,网络和1989年 Lecun 提出的 LeNet 有什么区别?

7、代码练习二里,卷积以后feature map 尺寸会变小,如何应用 Residual Learning?

8、有什么方法可以进一步提升准确率?

已解决的一些小问题:


Part1 视频学习 

一、绪论

  1. 卷积神经网络的应用

    1. 分类
    2. 检索
    3. 人脸识别
    4. 表情识别
    5. 图像生成——基于对抗生产网络
    6. 图像风格转换
    7. 自动驾驶
  2. 传统神经网络vs卷积神经网络

    1. 深度学习三部曲
      1. 搭建神经网络结构(提特征)
      2. 找到合适的损失函数(交叉熵损失,均方误差MSE)评估表示与真实值之间的差异
      3. 找到合适的优化函数(BP,随机梯度下降SGD)更新参数
    2. 损失函数:用来衡量预测结果与真是结果之间的吻合度

               3.传统神经网络:层与层之间都是全连接的网络

                        参数太多:权重矩阵的参数太多—>过拟合

                4.卷积神经网络:由卷积层,激活层,池化层,全连接层构成

                                           局部关联,参数共享

二、基本组成结构

  1. 卷积

    1. 一维卷积:
      1. 用在信号处理中,用于计算信号的延迟累积
      2. 假设一个信号发生器在时刻t发出一个信号xt,其信息的衰减率为fx,即在k-1个时间步长后,信息衰减为原来的fk倍
      3. 在时刻t收到的信号yt为当前时刻产生的信息和以前时刻延迟信息的叠加
      4. 此处fx被称为滤波器或卷积核
      5. 设滤波器f的长度为m,它和一个信息序列x1x2x3x4……的卷积记为
    2. 卷积:是对两个实变函数的一种数学操作
    3. 二维卷积:卷积核(F*F*x)是一个矩阵,以一定步长移动
      1. 卷积核:一个卷积核有x个权重矩阵,x=原图像通道数
      2. 步长:卷积核每次移动几个格子
      3. 感受野:卷积核进行一次卷积时输入图像(N*N*x)所对应的区域
      4. 特征图:卷积后输出的结果
        1. 特征图大小(N-F)/步长+1
        2. 特征图大小(N+pedding*2-F)/步长+1
      5. Depth/channel:特征图的深度/个数(和卷积核的个数保持一致)
      6. pedding:如果原图像在卷积的过程中出现一部分不够一次卷积,则在图像四周都补0
      7. 参数量=(F*F+1)*channel
    4. 卷积可视化理解:浅层更关注整体信息,和输入图片比较像;深层更加抽象
  2. 池化 Polling:缩放

    1. 保留主要特征的同时减少参数和计算量,防止过拟合,提高模型泛化能力
    2. 它一般处于卷积层与卷积层之间,全连接层与全连接层之间
    3. 池化核,步长
    4. 类型:
      1. Max polling:最大值池化——分类任务中更倾向
      2. Average polling:平均池化
  3. 全连接:

    1. 两层之间的所有神经元都有权重链接
    2. 通常全连接层在卷积神经网络尾部
    3. 全连接层参数量通常最大

三、卷积神经网络典型结构

  1. AlexNet:2012年ImageNet图像分类竞赛中冠军

    1. ReLU函数
      1. 解决了梯度消失的问题
      2. 计算速度特别快,只需要判断输入是否大于0
      3. 收敛速度远快于sigmoid
    2. DropOut(随即失活):训练时随即关闭部分神经元,测试时整合所有神经元
    3. 数据增强:增大数据量
      1. 平移、翻转、对称:
        1. 随机crop,在256*256的图片中可以随机裁出来很多224*224的图片
        2. 水平翻转,相当于将样本倍增
      2. 改变RGB通道强度:对RGB加入高斯扰动

       

  2. ZFNet:2013年ImageNet图像分类竞赛中冠军

    1. 网络结构与AlexNet相同
    2. 将卷积层1中的感受野大小由11x11改为7x7,步长由4改为2(可以提取到更详细的信息)
    3. 卷积层3,4,5中的滤波器个数由384,384,256改为512,512,1024
  3. VGG:一个更深的网络(原来只有8层),2014年第二名,先训练前11层,把参数固定住,再训练后面的

    1. VGG16
    2. VGG19
  4. GoogleNet:2014年第一名

    1. 网络包含22个带参数的层(如果考虑pollig是27层),独立成块的层总共有约100个
    2. 参数量大概是Alexnet的1/12(因为它全连接层少,只有用来分类的)
    3. 没有FC层
    4. inception模块,在同一层用了大小不同的3个卷积核(1x1,3x3,5x5)增加特征多样性——特征图层数太多了,可以通过加入1*1的卷积核降维(inceptionV2)
    5. Inception V3:将5*5的卷积核裂变成两个3*3的卷积核,降低了参数量,增加了非线性激活函数,表征能力增强
    6. Inception V4
  5. ResNet:残差学习网络,2015年ILSVRC竞赛冠军 ,深度有152层

    1. 残差的思想:去掉相同的主体部分,从而突出微小的变化
    2. 是一个更灵活的网络,可以根据任务需求学习一个需要的网络深度

Part2代码练习

MNIST 数据集分类

构建简单的CNN对 mnist 数据集进行分类

深度卷积神经网络中,有如下特性

  • 很多层: compositionality
  • 卷积: locality + stationarity of images
  • 池化: Invariance of object class to translations

1. 加载数据 (MNIST)

PyTorch里包含了 MNIST, CIFAR10 等常用数据集,调用 torchvision.datasets 即可把这些数据由远程下载到本地,下面给出MNIST的使用方法:

torchvision.datasets.MNIST(root, train=True, transform=None, target_transform=None, download=False)

  • root 为数据集下载到本地后的根目录,包括 training.pt 和 test.pt 文件
  • train,如果设置为True,从training.pt创建数据集,否则从test.pt创建。
  • download,如果设置为True, 从互联网下载数据并放到root文件夹下
  • transform, 一种函数或变换,输入PIL图片,返回变换之后的数据。
  • target_transform 一种函数或变换,输入目标,进行变换。

另外值得注意的是,DataLoader是一个比较重要的类,提供的常用操作有:batch_size(每个batch的大小), shuffle(是否进行随机打乱顺序的操作), num_workers(加载数据的时候使用几个子进程)

import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
import matplotlib.pyplot as plt
import numpy

# 一个函数,用来计算模型中有多少参数
def get_n_params(model):
    np=0
    for p in list(model.parameters()):
        np += p.nelement()
    return np

# 使用GPU训练,可以在菜单 "代码执行工具" -> "更改运行时类型" 里进行设置
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
datasets.MNIST(root='F:\pytorch', train=True, transform=None, target_transform=None, download=True)
input_size  = 28*28   # MNIST上的图像尺寸是 28x28
output_size = 10      # 类别为 0 到 9 的数字,因此为十类

train_loader = torch.utils.data.DataLoader(
    datasets.MNIST('./data', train=True, download=True,
        transform=transforms.Compose(
            [transforms.ToTensor(),
             transforms.Normalize((0.1307,), (0.3081,))])),
    batch_size=64, shuffle=True)

test_loader = torch.utils.data.DataLoader(
    datasets.MNIST('./data', train=False, transform=transforms.Compose([
             transforms.ToTensor(),
             transforms.Normalize((0.1307,), (0.3081,))])),
    batch_size=1000, shuffle=True)
plt.figure(figsize=(8, 5))
for i in range(20):
    plt.subplot(4, 5, i + 1)
    image, _ = train_loader.dataset.__getitem__(i)
    plt.imshow(image.squeeze().numpy(),'gray')
    plt.axis('off');

输出结果:

显示数据集中的部分图像:

plt.figure(figsize=(8, 5))
for i in range(20):
    plt.subplot(4, 5, i + 1)
    image, _ = train_loader.dataset.__getitem__(i)
    plt.imshow(image.squeeze().numpy(),'gray')
    plt.axis('off');
plt.show()  # 展示图象

输出结果:

 

2. 创建网络

定义网络时,需要继承nn.Module,并实现它的forward方法,把网络中具有可学习参数的层放在构造函数init中。

只要在nn.Module的子类中定义了forward函数,backward函数就会自动被实现(利用autograd)。

class FC2Layer(nn.Module):
    def __init__(self, input_size, n_hidden, output_size):
        # nn.Module子类的函数必须在构造函数中执行父类的构造函数
        # 下式等价于nn.Module.__init__(self)        
        super(FC2Layer, self).__init__()
        self.input_size = input_size
        # 这里直接用 Sequential 就定义了网络,注意要和下面 CNN 的代码区分开
        self.network = nn.Sequential(
            nn.Linear(input_size, n_hidden), 
            nn.ReLU(), 
            nn.Linear(n_hidden, n_hidden), 
            nn.ReLU(), 
            nn.Linear(n_hidden, output_size), 
            nn.LogSoftmax(dim=1)
        )
    def forward(self, x):
        # view一般出现在model类的forward函数中,用于改变输入或输出的形状
        # x.view(-1, self.input_size) 的意思是多维的数据展成二维
        # 代码指定二维数据的列数为 input_size=784,行数 -1 表示我们不想算,电脑会自己计算对应的数字
        # 在 DataLoader 部分,我们可以看到 batch_size 是64,所以得到 x 的行数是64
        # 大家可以加一行代码:print(x.cpu().numpy().shape)
        # 训练过程中,就会看到 (64, 784) 的输出,和我们的预期是一致的

        # forward 函数的作用是,指定网络的运行过程,这个全连接网络可能看不啥意义,
        # 下面的CNN网络可以看出 forward 的作用。
        x = x.view(-1, self.input_size)
        return self.network(x)
    


class CNN(nn.Module):
    def __init__(self, input_size, n_feature, output_size):
        # 执行父类的构造函数,所有的网络都要这么写
        super(CNN, self).__init__()
        # 下面是网络里典型结构的一些定义,一般就是卷积和全连接
        # 池化、ReLU一类的不用在这里定义
        self.n_feature = n_feature
        self.conv1 = nn.Conv2d(in_channels=1, out_channels=n_feature, kernel_size=5)
        self.conv2 = nn.Conv2d(n_feature, n_feature, kernel_size=5)
        self.fc1 = nn.Linear(n_feature*4*4, 50)
        self.fc2 = nn.Linear(50, 10)    
    
    # 下面的 forward 函数,定义了网络的结构,按照一定顺序,把上面构建的一些结构组织起来
    # 意思就是,conv1, conv2 等等的,可以多次重用
    def forward(self, x, verbose=False):
        x = self.conv1(x)
        x = F.relu(x)
        x = F.max_pool2d(x, kernel_size=2)
        x = self.conv2(x)
        x = F.relu(x)
        x = F.max_pool2d(x, kernel_size=2)
        x = x.view(-1, self.n_feature*4*4)
        x = self.fc1(x)
        x = F.relu(x)
        x = self.fc2(x)
        x = F.log_softmax(x, dim=1)
        return x

定义训练和测试函数

# 训练函数
def train(model):
    model.train()
    # 主里从train_loader里,64个样本一个batch为单位提取样本进行训练
    for batch_idx, (data, target) in enumerate(train_loader):
        # 把数据送到GPU中
        data, target = data.to(device), target.to(device)

        optimizer.zero_grad()
        output = model(data)
        loss = F.nll_loss(output, target)
        loss.backward()
        optimizer.step()
        if batch_idx % 100 == 0:
            print('Train: [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
                batch_idx * len(data), len(train_loader.dataset),
                100. * batch_idx / len(train_loader), loss.item()))


def test(model):
    model.eval()
    test_loss = 0
    correct = 0
    for data, target in test_loader:
        # 把数据送到GPU中
        data, target = data.to(device), target.to(device)
        # 把数据送入模型,得到预测结果
        output = model(data)
        # 计算本次batch的损失,并加到 test_loss 中
        test_loss += F.nll_loss(output, target, reduction='sum').item()
        # get the index of the max log-probability,最后一层输出10个数,
        # 值最大的那个即对应着分类结果,然后把分类结果保存在 pred 里
        pred = output.data.max(1, keepdim=True)[1]
        # 将 pred 与 target 相比,得到正确预测结果的数量,并加到 correct 中
        # 这里需要注意一下 view_as ,意思是把 target 变成维度和 pred 一样的意思                                                
        correct += pred.eq(target.data.view_as(pred)).cpu().sum().item()

    test_loss /= len(test_loader.dataset)
    accuracy = 100. * correct / len(test_loader.dataset)
    print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
        test_loss, correct, len(test_loader.dataset),
        accuracy))

3. 在小型全连接网络上训练(Fully-connected network)

n_hidden = 8 # number of hidden units

model_fnn = FC2Layer(input_size, n_hidden, output_size)
model_fnn.to(device)
optimizer = optim.SGD(model_fnn.parameters(), lr=0.01, momentum=0.5)
print('Number of parameters: {}'.format(get_n_params(model_fnn)))

train(model_fnn)
test(model_fnn)

结果输出:

 

4. 在卷积神经网络上训练

需要注意的是,上在定义的CNN和全连接网络,拥有相同数量的模型参数

# Training settings 
n_features = 6 # number of feature maps

model_cnn = CNN(input_size, n_features, output_size)
model_cnn.to(device)
optimizer = optim.SGD(model_cnn.parameters(), lr=0.01, momentum=0.5)
print('Number of parameters: {}'.format(get_n_params(model_cnn)))

train(model_cnn)
test(model_cnn)

结果输出: 

通过上面的测试结果,可以发现,含有相同参数的 CNN 效果要明显优于 简单的全连接网络,是因为 CNN 能够更好的挖掘图像中的信息,主要通过两个手段:

  • 卷积:Locality and stationarity in images
  • 池化:Builds in some translation invariance

5. 打乱像素顺序再次在两个网络上训练与测试

考虑到CNN在卷积与池化上的优良特性,如果我们把图像中的像素打乱顺序,这样 卷积 和 池化 就难以发挥作用了,为了验证这个想法,我们把图像中的像素打乱顺序再试试。

首先下面代码展示随机打乱像素顺序后,图像的形态:

# 这里解释一下 torch.randperm 函数,给定参数n,返回一个从0到n-1的随机整数排列
perm = torch.randperm(784)
plt.figure(figsize=(8, 4))
for i in range(10):
    image, _ = train_loader.dataset.__getitem__(i)
    # permute pixels
    image_perm = image.view(-1, 28*28).clone()
    image_perm = image_perm[:, perm]
    image_perm = image_perm.view(-1, 1, 28, 28)
    plt.subplot(4, 5, i + 1)
    plt.imshow(image.squeeze().numpy(), 'gray')
    plt.axis('off')
    plt.subplot(4, 5, i + 11)
    plt.imshow(image_perm.squeeze().numpy(), 'gray')
    plt.axis('off')

重新定义训练与测试函数,我们写了两个函数 train_perm 和 test_perm,分别对应着加入像素打乱顺序的训练函数与测试函数。

与之前的训练与测试函数基本上完全相同,只是对 data 加入了打乱顺序操作。

# 对每个 batch 里的数据,打乱像素顺序的函数
def perm_pixel(data, perm):
    # 转化为二维矩阵
    data_new = data.view(-1, 28*28)
    # 打乱像素顺序
    data_new = data_new[:, perm]
    # 恢复为原来4维的 tensor
    data_new = data_new.view(-1, 1, 28, 28)
    return data_new

# 训练函数
def train_perm(model, perm):
    model.train()
    for batch_idx, (data, target) in enumerate(train_loader):
        data, target = data.to(device), target.to(device)
        # 像素打乱顺序
        data = perm_pixel(data, perm)

        optimizer.zero_grad()
        output = model(data)
        loss = F.nll_loss(output, target)
        loss.backward()
        optimizer.step()
        if batch_idx % 100 == 0:
            print('Train: [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
                batch_idx * len(data), len(train_loader.dataset),
                100. * batch_idx / len(train_loader), loss.item()))

# 测试函数
def test_perm(model, perm):
    model.eval()
    test_loss = 0
    correct = 0
    for data, target in test_loader:
        data, target = data.to(device), target.to(device)

        # 像素打乱顺序
        data = perm_pixel(data, perm)

        output = model(data)
        test_loss += F.nll_loss(output, target, reduction='sum').item()
        pred = output.data.max(1, keepdim=True)[1]                                            
        correct += pred.eq(target.data.view_as(pred)).cpu().sum().item()

    test_loss /= len(test_loader.dataset)
    accuracy = 100. * correct / len(test_loader.dataset)
    print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
        test_loss, correct, len(test_loader.dataset),
        accuracy))

 在全连接网络上训练与测试:

perm = torch.randperm(784)
n_hidden = 8 # number of hidden units

model_fnn = FC2Layer(input_size, n_hidden, output_size)
model_fnn.to(device)
optimizer = optim.SGD(model_fnn.parameters(), lr=0.01, momentum=0.5)
print('Number of parameters: {}'.format(get_n_params(model_fnn)))

train_perm(model_fnn, perm)
test_perm(model_fnn, perm)

 在卷积神经网络上训练与测试:

perm = torch.randperm(784)
n_features = 6 # number of feature maps

model_cnn = CNN(input_size, n_features, output_size)
model_cnn.to(device)
optimizer = optim.SGD(model_cnn.parameters(), lr=0.01, momentum=0.5)
print('Number of parameters: {}'.format(get_n_params(model_cnn)))

train_perm(model_cnn, perm)
test_perm(model_cnn, perm)

从打乱像素顺序的实验结果来看,全连接网络的性能基本上没有发生变化,但是 卷积神经网络的性能明显下降。

这是因为对于卷积神经网络,会利用像素的局部关系,但是打乱顺序以后,这些像素间的关系将无法得到利用。

CIFAR10 数据集分类

对于视觉数据,PyTorch 创建了一个叫做 totchvision 的包,该包含有支持加载类似Imagenet,CIFAR10,MNIST 等公共数据集的数据加载模块 torchvision.datasets 和支持加载图像数据数据转换模块 torch.utils.data.DataLoader。

下面将使用CIFAR10数据集,它包含十个类别:‘airplane’, ‘automobile’, ‘bird’, ‘cat’, ‘deer’, ‘dog’, ‘frog’, ‘horse’, ‘ship’, ‘truck’。CIFAR-10 中的图像尺寸为3x32x32,也就是RGB的3层颜色通道,每层通道内的尺寸为32*32。

首先,加载并归一化 CIFAR10 使用 torchvision 。torchvision 数据集的输出是范围在[0,1]之间的 PILImage,我们将他们转换成归一化范围为[-1,1]之间的张量 Tensors。

使用 CNN 对 CIFAR10 数据集进行分类

import torch
import torchvision
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
import numpy as np
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim

# 使用GPU训练,可以在菜单 "代码执行工具" -> "更改运行时类型" 里进行设置
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

transform = transforms.Compose(
    [transforms.ToTensor(),
     transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

# 注意下面代码中:训练的 shuffle 是 True,测试的 shuffle 是 false
# 训练时可以打乱顺序增加多样性,测试是没有必要
trainset = torchvision.datasets.CIFAR10(root='./data', train=True,
                                        download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=64,
                                          shuffle=True, num_workers=2)

testset = torchvision.datasets.CIFAR10(root='./data', train=False,
                                       download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=8,
                                         shuffle=False, num_workers=2)

classes = ('plane', 'car', 'bird', 'cat',
           'deer', 'dog', 'frog', 'horse', 'ship', 'truck')

下面展示 CIFAR10 里面的一些图片:

def imshow(img):
    plt.figure(figsize=(8,8))
    img = img / 2 + 0.5     # 转换到 [0,1] 之间
    npimg = img.numpy()
    plt.imshow(np.transpose(npimg, (1, 2, 0)))
    plt.show()

# 得到一组图像
images, labels = iter(trainloader).next()
# 展示图像
imshow(torchvision.utils.make_grid(images))
# 展示第一行图像的标签
for j in range(8):
    print(classes[labels[j]])

 结果输出:

 

定义网络,损失函数和优化器: 

 

class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(3, 6, 5)
        self.pool = nn.MaxPool2d(2, 2)
        self.conv2 = nn.Conv2d(6, 16, 5)
        self.fc1 = nn.Linear(16 * 5 * 5, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)

    def forward(self, x):
        x = self.pool(F.relu(self.conv1(x)))
        x = self.pool(F.relu(self.conv2(x)))
        x = x.view(-1, 16 * 5 * 5)
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x

# 网络放到GPU上
net = Net().to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(net.parameters(), lr=0.001)

训练网络:

for epoch in range(10):  # 重复多轮训练
    for i, (inputs, labels) in enumerate(trainloader):
        inputs = inputs.to(device)
        labels = labels.to(device)
        # 优化器梯度归零
        optimizer.zero_grad()
        # 正向传播 + 反向传播 + 优化 
        outputs = net(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()
        # 输出统计信息
        if i % 100 == 0:   
            print('Epoch: %d Minibatch: %5d loss: %.3f' %(epoch + 1, i + 1, loss.item()))

print('Finished Training')

 

 

 现在我们从测试集中取出8张图片:

# 得到一组图像
images, labels = iter(testloader).next()
# 展示图像
imshow(torchvision.utils.make_grid(images))
# 展示图像的标签
for j in range(8):
    print(classes[labels[j]])

 

我们把图片输入模型,看看CNN把这些图片识别成什么:

outputs = net(images.to(device))
_, predicted = torch.max(outputs, 1)

# 展示预测的结果
for j in range(8):
    print(classes[predicted[j]])

  

 可以看到,有几个都识别错了~~~ 让我们看看网络在整个数据集上的表现:

correct = 0
total = 0

for data in testloader:
    images, labels = data
    images, labels = images.to(device), labels.to(device)
    outputs = net(images)
    _, predicted = torch.max(outputs.data, 1)
    total += labels.size(0)
    correct += (predicted == labels).sum().item()

print('Accuracy of the network on the 10000 test images: %d %%' % (
    100 * correct / total))

 

准确率还可以,通过改进网络结构,性能还可以进一步提升。在 Kaggle 的LeaderBoard上,准确率高的达到95%以上。

使用 VGG16 对 CIFAR10 分类

VGG是由Simonyan 和Zisserman在文献《Very Deep Convolutional Networks for Large Scale Image Recognition》中提出卷积神经网络模型,其名称来源于作者所在的牛津大学视觉几何组(Visual Geometry Group)的缩写。

该模型参加2014年的 ImageNet图像分类与定位挑战赛,取得了优异成绩:在分类任务上排名第二,在定位任务上排名第一。

VGG16的网络结构如下图所示:

 

16层网络的结节信息如下:

  • 01:Convolution using 64 filters
  • 02: Convolution using 64 filters + Max pooling
  • 03: Convolution using 128 filters
  • 04: Convolution using 128 filters + Max pooling
  • 05: Convolution using 256 filters
  • 06: Convolution using 256 filters
  • 07: Convolution using 256 filters + Max pooling
  • 08: Convolution using 512 filters
  • 09: Convolution using 512 filters
  • 10: Convolution using 512 filters + Max pooling
  • 11: Convolution using 512 filters
  • 12: Convolution using 512 filters
  • 13: Convolution using 512 filters + Max pooling
  • 14: Fully connected with 4096 nodes
  • 15: Fully connected with 4096 nodes
  • 16: Softmax

1. 定义 dataloader

 

import torch
import torchvision
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
import numpy as np
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim

# 使用GPU训练,可以在菜单 "代码执行工具" -> "更改运行时类型" 里进行设置
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

transform_train = transforms.Compose([
    transforms.RandomCrop(32, padding=4),
    transforms.RandomHorizontalFlip(),
    transforms.ToTensor(),
    transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010))])

transform_test = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010))])

trainset = torchvision.datasets.CIFAR10(root='./data', train=True,  download=True, transform=transform_train)
testset  = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform_test)

trainloader = torch.utils.data.DataLoader(trainset, batch_size=128, shuffle=True, num_workers=2)
testloader = torch.utils.data.DataLoader(testset, batch_size=128, shuffle=False, num_workers=2)

classes = ('plane', 'car', 'bird', 'cat',
           'deer', 'dog', 'frog', 'horse', 'ship', 'truck')

2. VGG 网络定义

下面定义VGG网络,参数太多,随便改简单了些~~~

现在的结构基本上是:

64 conv, maxpooling,

128 conv, maxpooling,

256 conv, 256 conv, maxpooling,

512 conv, 512 conv, maxpooling,

512 conv, 512 conv, maxpooling,

softmax

下面是模型的实现代码:

class VGG(nn.Module):
    def __init__(self):
        super(VGG, self).__init__()
        self.cfg = [64, 'M', 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M']
        self.features = self._make_layers(cfg)
        self.classifier = nn.Linear(512, 10)

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

    def _make_layers(self, self.cfg):
        layers = []
        in_channels = 3
        for x in cfg:
            if x == 'M':
                layers += [nn.MaxPool2d(kernel_size=2, stride=2)]
            else:
                layers += [nn.Conv2d(in_channels, x, kernel_size=3, padding=1),
                           nn.BatchNorm2d(x),
                           nn.ReLU(inplace=True)]
                in_channels = x
        layers += [nn.AvgPool2d(kernel_size=1, stride=1)]
        return nn.Sequential(*layers)

初始化网络,根据实际需要,修改分类层。因为 tiny-imagenet 是对200类图像分类,这里把输出修改为200。

# 网络放到GPU上
net = VGG().to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(net.parameters(), lr=0.001)

3. 网络训练

训练的代码和以前是完全一样的:

for epoch in range(10):  # 重复多轮训练
    for i, (inputs, labels) in enumerate(trainloader):
        inputs = inputs.to(device)
        labels = labels.to(device)
        # 优化器梯度归零
        optimizer.zero_grad()
        # 正向传播 + 反向传播 + 优化 
        outputs = net(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()
        # 输出统计信息
        if i % 100 == 0:   
            print('Epoch: %d Minibatch: %5d loss: %.3f' %(epoch + 1, i + 1, loss.item()))

print('Finished Training')

4. 测试验证准确率:

测试的代码和之前也是完全一样的。

correct = 0
total = 0

for data in testloader:
    images, labels = data
    images, labels = images.to(device), labels.to(device)
    outputs = net(images)
    _, predicted = torch.max(outputs.data, 1)
    total += labels.size(0)
    correct += (predicted == labels).sum().item()

print('Accuracy of the network on the 10000 test images: %.2f %%' % (
    100 * correct / total))

 

 

可以看到,使用一个简化版的 VGG 网络,就能够显著地将准确率由 64%,提升到 83.85%

Part3 思考题:

1、dataloader 里面 shuffle 取不同值有什么区别?

DataLoader用于加载数据到模型中
在pytorch 中的数据加载到模型的操作顺序是这样的:

① 创建一个 Dataset 对象
② 创建一个 DataLoader 对象
③ 循环这个 DataLoader 对象,将img, label加载到模型中进行训练

shuffer=False表示不打乱数据的顺序,然后以batch为单位从头到尾按顺序取用数据。shuffer=Ture表示在每一次epoch中都打乱所有数据的顺序,然后以batch为单位从头到尾按顺序取用数据。这样的结果就是不同epoch中的数据都是乱序的。
 

2、transform 里,取了不同值,这个有什么区别?

torchvision.transforms是pytorch中的图像预处理包,包含了很多种对图像数据进行变换的函数,这些都是在我们进行图像数据读入步骤中必不可少的。

 torchvision.datasets 中的 数据集都有两个参数:

  • transform:可以对数据进行的变换;
  • target_transform:可以对标签进行的变换。

而 torchvision.transforms.functional 模块提供了一些常用的转换,这些转换都能够接受以下三种输入:

  • PIL Image:对于 RGB 图像,size 为 (W, H),将其转换为 NumPy array 后 size 为 (H, W, C);
  • Tensor Image:指具有 shape 为 (C, H, W) 的一个 tensor,C 为通道数,H、W 分别是图像的高和宽;
  • batch of Tensor Images:指具有 shape 为 (B, C, H, W) 的一个 tensor,B 为 batchsize,也就是一个批次中的图像数量。

这里需要注意的一个地方是:

Pytorch 中存储的 Tensor Image 的存储格式为 (C, H, W);而转换为 NumPy array 的 PIL Image 的 存储格式 为 (H, W, C);
相关函数具体使用可见 Pytorch(三):数据变换 Transforms_犬冢紬希的博客-CSDN博客_target_transform

3、epoch 和 batch 的区别?

Batch大小是一个超参数,用于定义在更新内部模型参数之前要处理的样本数。

Epoch数是一个超参数,它定义了学习算法在整个训练数据集中的工作次数。Epoch由一个或多个Batch组成。

Batch大小是在更新模型之前处理的多个样本。Epoch数是通过训练数据集的完整传递次数。批处理的大小必须大于或等于1且小于或等于训练数据集中的样本数。

工作实例

假设您有一个包含200个样本(数据行)的数据集,并且您选择的Batch大小为5和1,000个Epoch。

这意味着数据集将分为40个Batch,每个Batch有5个样本。每批五个样品后,模型权重将更新。

这也意味着一个epoch将涉及40个Batch或40个模型更新。

有1000个Epoch,模型将暴露或传递整个数据集1,000次。在整个培训过程中,总共有40,000Batch。

神经网络中Batch和Epoch之间的区别是什么?_喜欢打酱油的老鸟的博客-CSDN博客_batch

4、1x1的卷积和 FC 有什么区别?主要起什么作用?

卷积跟全连接都是一个点乘的操作,区别在于卷积是作用在一个局部的区域,而全连接是对于整个输入而言,那么只要把卷积作用的区域扩大为整个输入,那就变成全连接了。全连接的输入是特征图所有元素乘以权重再求和,但是这个权重向量是在设计网络的时候就需要固定的,所以全连接没办法适应输入尺寸的变化只能固定。但是1*1卷积的输出与输入尺寸是一样大的,输出尺寸可以随着输入尺寸的变化而变化,所以1*1卷积无需固定输出尺寸。

1x1的卷积核可以降低特征图的维度同时加入非线性,实现跨通道信息交互(channal 的变换)

全连接层可以对输入的数据进行线性变换,还可以完成最后的分类任务


5、residual leanring 为什么能够提升准确率?

残差网络在每次输出前都在结果上+输入,从而去掉相同的主体部分,从而突出微小的变化,同时它还是一个更灵活的网络,可以根据任务需求学习一个需要的网络深度


6、代码练习二里,网络和1989年 Lecun 提出的 LeNet 有什么区别?

1989年 Lecun 提出的 LeNet 是CNN的开山之作:

在Lenet-1中, 28x28 的输入图像 --> 4个24×24 feature maps卷积层(5×5 size) -->平均池化层(2×2大小) -->8个12×12 feature maps 卷积层(5×5 size)--> 平均池化层(2×2大小)--> 直接全连接后输出

引入卷积和下采样/池化层后,LeNet-1对测试数据的错误率为1.7%.值得注意的是,在作者发明LeNet时,他们使用平均池化层,输出2×2特征图的平均值。目前很多LeNet实现使用max pooling只输出2×2 feature map的最大值,这有助于加快训练速度。当选择最强的特征时,反向传播可以得到较大的梯度。
在LeNet系列中LeNet-5最受欢迎:

32x32的输入图像 => 6个28×28 feature maps 卷积层(5×5 size) => 平均池化层(2×2大小)=>16个10×10 feature maps 卷积层(5×5 size) => 平均池化层(2×2大小)=>全连接到120个神经元 => 全连接84个神经元 => 全连接到10个输出

有了更多的feature map和全连接层,测试数据的错误率为0.95%。

代码练习二中所构建的网络是LeNet-5的变形,比LeNet-5少了一个池化层,同时将LeNet-5中的平均池化优化为最大池化,将激活函数由softmax变换为relu
 


7、代码练习二里,卷积以后feature map 尺寸会变小,如何应用 Residual Learning?

通过线性变换将原图像缩小为和feature map大小相同的图像

当输入输出维度上升时有两种处理方式:第一种是仍使用恒等映射,多出来的通道使用零矩阵填充,这样做的好处是不会带来额外的参数;第二种是添加变换方程,通常来说会使用 1*1 卷积来完成升维。


8、有什么方法可以进一步提升准确率?

  • 改进网络结构
  • 选择合适的激活函数
  • 选择合适的损失函数
  • 使用更大的训练集:使用数据增强技术(data augmentation),主要是在训练数据上增加微小的扰动或者变化,一方面可以增加训练数据,从而提升模型的泛化能力,另一方面可以增加噪声数据,从而增强模型的鲁棒性。主要的数据增强方法有:翻转变换 flip、随机修剪(random crop)、色彩抖动(color jittering)、平移变换(shift)、尺度变换(scale)、对比度变换(contrast)、噪声扰动(noise)、旋转变换/反射变换 (rotation/reflection)等
  • 使用更深的网络
  • 增加训练轮次

已解决的一些小问题:

1、VGG网络定义时报错name ‘cfg’ is not defined,需要做以下更改:

改为:

2、VGG网络训练时报错RuntimeError: mat1 and mat2 shapes cannot be multiplied (128x512 and 2048x10),图片大小不同,需要做以下更改

改为:

 

 

海绵干儿
关注 关注
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用Numpy从头构建卷神经网络
深度学习与计算机视觉
02-07 549
使用该网络对手写数字进行分类。所获得的结果不是最先进的水平,但仍然令人满意。现在想更进一步,我们的目标是开发一个仅使用Numpy的卷神经网络(CNN)。这项任务背后的动机与创建全连接的网络的动机相同:尽管Python深度学习库是强大的工具,但它阻止从业者理解底层正在发生的事情。对于CNNs来说,这一点尤其正确,因为该过程不如经典深度网络执行的过程直观。解决这一问题的唯一办法是尝试自己实现这些网络...
Pytorch tensor.view_as的用法
qq_20014079的博客
07-15 3510
示例代码 import torch a=torch.tensor([1,2,3]) b=torch.tensor([2,2,4]) print(a.detach().view_as(b)) 一般是用来做比较的时候使用,确保两个比较的数据,维度是一致的。 ...
神经网络中view(),torch.flatten(),torch.nn.Flatten()详解
hjkdh的博客
02-23 1万+
神经网络view(),torch.flatten(),torch.nn.Flatten()1、view()2、torch.nn.Flatten()3、torch.flatten() 在神经网络中经常看到view(),torch.flatten(),torch.nn.Flatten()这几个方法。这几个方法一般用于改变tensor的形状。为日后方便使用下面就一一透彻的理解一下。 1、view() view()的原理很简单,其实就是把原先tensor中的数据进行排列,排成一行,然后根据所给的view()中的参数
神经网络可视化和理解
一遍看不懂,我就再看一遍
06-05 7950
神经网络可视化
只需一次向前推导,深度神经网络可视化方法来了!(ECCVW 2022)
CV_Autobot的博客
10-13 232
作者|Ziyang Li 编辑|汽车人点击下方卡片,关注“自动驾驶之心”公众号ADAS巨卷干货,即可获取点击进入→自动驾驶之心技术交流群导读本文首次提出可学习的类激活方法,通过设计适当损失来迫使注意机制学习有效CAM输出,并只需一次前向推理。论文链接:https://arxiv.org/abs/2209.11189写在前面的话类激活图(CAM)致力于解释卷神经网络的“黑盒”属性。本...
深度学习之CNN入门基础——1.卷及numpy实现
weixin_60368327的博客
06-02 684
本文转载自AIHIA盟友公众号:所向披靡的张大刀卷神经网络(ConvNets或CNNs)作为人工智能的入门神经网络,已被广泛用于图像识别和分类等领域。除了为机器人和自动驾驶汽车提供视觉之外,ConvNets 在识别人脸、物体和交通标志方面也应用广泛。其中卷操作作为cnn的灵魂,其出现加快了人工智能的发展。卷一词一开始出现在数学中,为了表示一个函数对另外一个函数所有微量的响应的总叠加效应;其本质上是一种过滤器,在一维信号上完成对信号的过滤,在二维图像上完成为图像的一种过滤,过滤掉其不重要部分,提取主要特
深度学习d5:卷神经网络基础;leNet;卷神经网络进阶
01-07
神经网络基础神经网络:包括卷层、池化层 二维卷层: 最常用,用于处理图像数据,将输入和卷核做互相关运算,并加上一个标量偏置来得到输出。 其模型参数=卷核+标量偏置。 训练模型的时候,通常...
Datawhale 组队学习打卡营 任务13:卷神经网络基础
01-20
神经网络基础 本章介绍卷神经网络基础概念,主要是卷层和池化层,并解释填充、步幅、输入通道和输出通道的含义。 目录 二维卷层 二维互相关运算 二维卷层 互相关运算和卷运算 特征图和感受野 填充和...
Task05:卷神经网络基础;leNet;卷神经网络进阶
01-06
5 卷神经网络(CNN) ​ 卷神经网络是一种用来处理局部和整体相关性的计算网络结构,被应用在图像识别、自然语言处理甚至是语音识别领域,因为图像数据具有显著的局部与整体关系,其在图像识别领域的应用获得了...
Task05:卷神经网络基础+LeNet
01-06
神经网络 感受野 LeNet架构 卷层 互相关运算与卷运算 卷层得名于卷运算,但卷层中用到的并非卷运算而是互相关运算。我们将核数组上下翻转、左右翻转,再与输入数组做互相关运算,这一过程就是...
Numpy实现卷神经网络(CNN)的示例
12-16
import numpy as np import sys def conv_(img, conv_filter): filter_size = conv_filter.shape[1] result = np.zeros((img.shape)) # 循环遍历图像以应用卷运算 for r in np.uint16(np.arange(filter_size/2.0, img.shape[0]-filter_size/2.0+1)): for c in np.uint16(np.arange(filter_size/2.0, img.shape[1]-filter_s
Task05:卷神经网络基础;leNet;卷神经网络进阶 学习笔记
01-07
神经网络基础 本节我们介绍卷神经网络基础概念,主要是卷层和池化层,并解释填充、步幅、输入通道和输出通道的含义。 二维卷层 本节介绍的是最常见的二维卷层,常用于处理图像数据。 二维互相关运算 ...
详解卷神经网络(CNN)
热门推荐
qq_25762497的博客
04-04 25万+
神经网络(Convolutional Neural Network, CNN)是一种前馈神经网络,它的人工神经元可以响应一部分覆盖范围内的围单元,对于大型图像处理有出色表现。
问题记录7:Pytorch训练网络模型过程中Loss为负值的问题及其解决方案
weixin_42214565的博客
10-08 2万+
1. 问题描述 在复现论文的过程中,遇到了训练模型Loss一直为负的情况。 程序主要通过深度学习实现一个分类任务。编程与debug过程全部在windows10系统,Pycharm2018v1.4的IDE下完成,主要框架为pytorch 1.2.0。复现过程中采用了交叉熵损失函数计算Loss。训练过程中输出信息如下: 输出部分的代码段: for batch_idx, (data...
PyTorch使用技巧1:F.dropout加self.training、F.log_softmax
源代码杀手的博客
07-07 1411
dropout方法是将输入Tensor的元素按伯努利分布随机置0,具体原理此处不赘,以后待补。总之就是训练的时候要用dropout,验证/测试的时候要关dropout。以下介绍Module的training属性,F(torch.nn.functional).dropout 和 nn(torch.nn).Dropout 中相应操作的实现方式,以及Module的training属性受train()和eval()方法影响而改变的机制。方法来自论文:https://www.jmlr.org/papers/volum
深度学习—卷神经网络(CNN)全笔记,附代码
weixin_58176527的博客
07-03 2万+
K近邻;神经网络基础,线性函数,损失函数,前向传播; 卷神经网络,卷网络与传统网络的区别,卷层,卷层涉及参数,池化层,最大池化,特征图的变化;经典网络Alexnet,经典网络—Vgg经典网络—Resnet,感受野; 项目实战—基于CNN构建识别模型一 项目实战—基于CNN构建识别模型二 图像识别实战常用模块解读...
4. Pytorch入门教程——创建一个基类来构建一个基本的神经网络
u010409517的博客
05-26 1049
现在我们已经准备好了Dataloaders,之后要定义神经网络并训练它。为了定义一个神经网络,最好的方法是定义类来分离和抽象所有类型网络的通用功能,如训练循环,验证,评估,预测,设置不同的超参数等。 我们还需要定义实现特定类型网络的类,例如专门用于迁移学习的类,或为全连接操作的类等等。记住这一点,我们将创建三个主要类: 从Pytorch的核心类nn.Module派生的神经网络的基类,它是Pytorch中任何神经网络基础; 派生自我们创建的基类的一个类,实现迁移学习的功能; 派生自我们创建的基类的一个类,
pytorch--知识蒸馏--小小实践
qq_35275007的博客
12-03 2594
import math import torch import torch.nn as nn import torch.nn.functional as F import torch.utils.data from torchvision import datasets, transforms from torchvision import models import matplotlib.pyplot as plt torch.manual_seed(0) # torch.cuda.manual_se.
不用框架,python实现卷神经网络
小黑的专栏
09-05 2万+
不用框架,python简单实现卷神经网络
图像压缩:卷神经网络与 JPEG
最新发布
10-09
神经网络和JPEG都能用于图像压缩,它们的原理略有不同。JPEG使用了离散余弦变换和量化来减少图像数据量,而卷神经网络则通过编码器和解码器之间的卷层来减少图像数据量。具体来说,编码器将图像压缩为较小的...

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
6
原创
4
点赞
6
收藏
2
粉丝
关注
私信
写文章

热门文章

  • 第2周学习:卷积神经网络基础 1001
  • 第3周学习:ResNet+ResNeXt 600
  • 第1周学习:深度学习和pytorch基础 422
  • 第5周学习:ShuffleNet & EfficientNet & 迁移学习 248
  • 第6周学习:Vision Transformer & Swin Transformer 243

您愿意向朋友推荐“博客详情页”吗?

  • 强烈不推荐
  • 不推荐
  • 一般般
  • 推荐
  • 强烈推荐
提交

最新文章

  • 第6周学习:Vision Transformer & Swin Transformer
  • 第5周学习:ShuffleNet & EfficientNet & 迁移学习
  • 第4周学习:MobileNetV1, V2, V3
2022年6篇

目录

目录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值

深圳SEO优化公司绥化企业网站建设多少钱东营网站改版报价和田关键词排名包年推广长春建设网站河源模板推广漯河模板制作价格张家口网站优化按天计费山南SEO按天扣费哪家好周口seo排名公司常州百度关键词包年推广哪家好绵阳网站优化多少钱柳州网站改版公司永州外贸网站设计价格惠州如何制作网站公司周口如何制作网站公司聊城网络广告推广多少钱聊城企业网站改版推荐沧州网站排名优化推荐西宁阿里店铺运营哪家好宝鸡设计网站报价娄底SEO按效果付费哪家好天津百度爱采购价格丽水关键词排名价格宝鸡阿里店铺托管价格合肥网站优化按天扣费多少钱西宁网站seo优化价格和县网站优化软件报价西乡网站排名优化多少钱西宁关键词排名多少钱百色模板推广公司歼20紧急升空逼退外机英媒称团队夜以继日筹划王妃复出草木蔓发 春山在望成都发生巨响 当地回应60岁老人炒菠菜未焯水致肾病恶化男子涉嫌走私被判11年却一天牢没坐劳斯莱斯右转逼停直行车网传落水者说“没让你救”系谣言广东通报13岁男孩性侵女童不予立案贵州小伙回应在美国卖三蹦子火了淀粉肠小王子日销售额涨超10倍有个姐真把千机伞做出来了近3万元金手镯仅含足金十克呼北高速交通事故已致14人死亡杨洋拄拐现身医院国产伟哥去年销售近13亿男子给前妻转账 现任妻子起诉要回新基金只募集到26元还是员工自购男孩疑遭霸凌 家长讨说法被踢出群充个话费竟沦为间接洗钱工具新的一天从800个哈欠开始单亲妈妈陷入热恋 14岁儿子报警#春分立蛋大挑战#中国投资客涌入日本东京买房两大学生合买彩票中奖一人不认账新加坡主帅:唯一目标击败中国队月嫂回应掌掴婴儿是在赶虫子19岁小伙救下5人后溺亡 多方发声清明节放假3天调休1天张家界的山上“长”满了韩国人?开封王婆为何火了主播靠辱骂母亲走红被批捕封号代拍被何赛飞拿着魔杖追着打阿根廷将发行1万与2万面值的纸币库克现身上海为江西彩礼“减负”的“试婚人”因自嘲式简历走红的教授更新简介殡仪馆花卉高于市场价3倍还重复用网友称在豆瓣酱里吃出老鼠头315晚会后胖东来又人满为患了网友建议重庆地铁不准乘客携带菜筐特朗普谈“凯特王妃P图照”罗斯否认插足凯特王妃婚姻青海通报栏杆断裂小学生跌落住进ICU恒大被罚41.75亿到底怎么缴湖南一县政协主席疑涉刑案被控制茶百道就改标签日期致歉王树国3次鞠躬告别西交大师生张立群任西安交通大学校长杨倩无缘巴黎奥运

深圳SEO优化公司 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化