第二次作业:卷积神经网络 part 1

视频学习

深度学习的数学基础

  1. 自编码器变种

    • 正则自编码器:使提取的特征表达符合某种性质——惩罚大权重
    • 稀疏自编码器:提取稀疏特征表达——限制神经元平均激活度在一个很小的值
    • 去噪自编码器:提取鲁棒特征表达
    • 变分自编码器:数据生成、缺失数据填补、图像超高分辨率——基于隐层特征表达空间Z,通过解码曾,生成样本
  2. 数学基础

    • 概率论(统计)——基础、信息论

    • 线性代数

      • 矩阵线性变换

        • Ax=λx (x:特征向量、λ:特征值)
        • 矩阵相乘对原始向量同时世家方向和尺度变化,而对于特征向量只有尺度变化,没有方向变化,变化系数就是特征值
        • 定义

          • 度量矩阵行列之间的相关性,线性无关——满秩
          • 表示数据需要的最小的基的数量
            • 数据分布模式容易被捕捉——基少,秩小
            • 数据冗余度越大——基少,秩小
            • 矩阵表达的表达结构化信息相关性大——秩小
        • 机器学习:数据降维

          • 奇异值,较大奇异值包含了矩阵的主要信息
        • 机器学习:低秩近似

          • 矩阵低秩近似
          • 低秩近似的意义:保留决定数据分布的最主要的模式/方向(丢弃噪声或其他不关键信息)
          • 图像去噪
    • 最优化、微积分

    • 梯度下降是神经网络共同的基础

  3. 机器学习三要素

    • 模型——对要学习问题映射的假设(问题建模,确定假设空间)

    • 策略——从假设空间中学习/选择最优模型的准则(确定目标函数)

      • 训练误差—>泛化误差(衡量两者差异:计算学习理论)

        第二次作业:卷积神经网络 part 1_第1张图片

      • ”最合适“的模型:机器学习从有限的观测数据中学习出规律,并将总结的规律推广应用到未观测样本上——追求泛化性能

      • 机器学习目的是获得小的泛化误差

      • PAC给出实际训练学习器的目标:从合理数量的训练数据中通过合理计算量学习到可靠的知识

        • 合理数量的训练数据:数据集大小
        • 合理计算量:学习训练的时间
        • 可靠的知识:概率的置信度,近似的误差上界
      • 策略设计定理:

        • 无免费午餐定理:在所有问题上的平均性能是,任意两个模型相同
        • 奥卡姆剃刀原理:如无必要,勿增实体,即简单有效原理,选择同等程度符合一个问题观测结果模型中使用假设最少的->最简单的模型
      • 欠拟合VS过拟合

        • 欠拟合:训练集的一般性质尚未被学习器学好(训练误差大)
          • 解决方案:提高模型复杂度
            • 决策树:拓展分支
            • 神经网络:增加训练轮数
        • 过拟合:学习器把训练集特点当作样本的一般特点(训练误差小,测试误差大)
          • 解决方案:降低模型复杂度
            • 优化目标加正则项
            • 决策树:剪枝
            • 神经网路:early stop、dropout
          • 数据增广(扩大训练集)
            • 计算机视觉:图像旋转、缩放、剪切
            • 自然语言处理:同义词替换
            • 语音识别:添加随机噪声
    • 算法——根据目标函数求解最优模型的具体计算方法(求解模型参数)

    • 概率/函数形式的统一

第二次作业:卷积神经网络 part 1_第2张图片

第二次作业:卷积神经网络 part 1_第3张图片

  1. 损失函数

    • BP神经网络和损失函数

第二次作业:卷积神经网络 part 1_第4张图片

  • 平方损失

第二次作业:卷积神经网络 part 1_第5张图片

  • 交叉熵

第二次作业:卷积神经网络 part 1_第6张图片

  1. 频率学派VS贝叶斯学派

    • 频率学派
      • 关注可独立重复的随机实验中单个事件发生的频率
      • 可能性:事件发生频率的极限值->重复实验次数趋近无穷大,事件发生的频率会收敛到真实的概率
      • 假设概率是客观存在且固定的
      • 模型参数是唯一的,需要从有限的观测数据中估计参数值
    • 贝叶斯学派
      • 关注随机事件的”可信程度“,如天气预报下雨概率
      • 可能性=假设+数据:数据的作用是对初始假设做出修正,使观测折对概率的主观认识(先验)更接近客观实际(观测)
      • 模型参数本身使随机变量,需要估计参数的整个概率分布
  2. 概率学派VS机器学习方法

第二次作业:卷积神经网络 part 1_第7张图片

  1. Beyond深度学习

    • 因果推断

      • 统计机器学习:寻找相关性

        X—>Y

      • 相关性不可靠:Yule-Simpson悖论

        相关关系可能由于CONTEXT等混杂因素而改变

      • 联结主义 vs 贝叶斯:相关性 vs 因果性

    • 群体智能

      • 验证码:
      • ESP:图像标注
      • Peekaboom:图像分割

    卷积神经网络

  2. 卷积神经网络

    • 全连接网络处理图像问题:权重矩阵参数太多导致过拟合

      卷积神经网络解决方式:局部关联,参数共享

    • 一个经典的卷积神经网络由卷积层、池化层、全连接层交叉堆叠而成

    • 搭建神经网络结构

    • 找到一个合适的损失函数

第二次作业:卷积神经网络 part 1_第8张图片

  • 找到一个合适的优化函数
  1. 卷积

    • 一维卷积

第二次作业:卷积神经网络 part 1_第9张图片

  • 二维卷积

    卷积是对两个实变函数的一种数学操作

    在图像处理中,图像是以二维矩阵输入到神经网络的,因此需要二维卷积

    y=WX+b(W:卷积核参数,X:卷积区域,b:偏置项)

  • 基本概念

    • 输入,input

    • 卷积核/滤波器,kernel/filter

    • 权重,weights

    • 感受野,receptive field:卷积核进行卷积时对应的区域

    • 特征图,activation map:一次卷积后的输出结果,大小:(N-F)/stride+1,

      有padding时:(N+padding*2-F)/stride+1

    • padding:零填充

    • 深度,depth/channel

    • 输出:output

  1. 池化-Pooling Layer

    • Pooling:
      • 保留了主要特征的同时减少参数和计算量, 防止过拟合,提高模型泛化能力
      • 一般处于卷积层与卷积层之间,全连接层与全连接层之间
    • 类型:
      • Max pooling:最大值池化——分类
      • Average pooling:平均池化
    • 概念
      • filer,区域,一般2*2,3x3
      • 步长stride,一般2
  2. 全连接—Fully Connected Layer

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

    • 模型结构

第二次作业:卷积神经网络 part 1_第10张图片

+ 成功原因:

  + 大数据训练:百万级ImageNet图像数据

  + 非线性激活函数:ReLU

第二次作业:卷积神经网络 part 1_第11张图片

  + 防止过拟合:Dropout,Data augmentation

    - DropOut(随机失活):训练时随即关闭部分神经元,测试时整合所有神经元

    - Data augmentation(数据增强):

      + 平移、翻转、对称
        + 随机crop。
        + 水平翻转,相当将样本倍增
      + 改变RGB通道强度:对RGB空间做一个高斯扰动。

      

  + 其他:双Gpu实现

+ 分层解析

  + 第一次卷积:卷积-ReLU-池化        224x224x3—55x55x96—27x27x96—27x27x96
  + 第二次卷积:卷积-ReLU-池化        27x27x96—27x27x256—13x13x256—13x13x256
  + 第三次卷积:卷积-ReLU                  13x13x256—13x13x384—13x13x384
  + 第四次卷积:卷积-ReLU                  13x13x384—13x13x384—13x13x384
  + 第五次卷积:卷积-ReLU-池化         13x13x384—13x13x256—13x13x256—6x6x256
  + 第六次卷积:全连接-ReLU-DropOut      6x6x256—4096—4096—4096
  + 第七次卷积:全连接-ReLU-DropOut      4096—4096—4096—4096
  + 第八次卷积:全连接-SoftMax                  4096—1000

+ 参数数量

第二次作业:卷积神经网络 part 1_第12张图片

  1. ZFNet

    • 网络结构与AlexNet相同
    • 将卷积层1中的感受野大小由11x11改为7x7,步长由4改为2
    • 卷积层3,4,5中的滤波器个数由384,384,256改为512,512,1024
  2. VGG—更深的网络

    • 将AlexNet8layers—>16—>19(VGG)
      第二次作业:卷积神经网络 part 1_第13张图片
  3. GoogleNet

    • 网络总体结构:
      • 网络包含22个带参数的层(考虑pooling层就是27层),独立成快的层总共约有100个
      • 参数量大概是AlexNet的1/12
      • 没有FC层
    • Naive Inception
      • 初衷:多卷积核增加特征多样性
      • 计算复杂度过高
    • Inception V2
      • 解决思路:插入1x1卷积核进行降维
    • Inception V3
      • 进一步对V2的参数两进行降低,用小的卷积核替代打的卷积核
      • 降低参数量
      • 增加非线性激活函数:使网络产生更多独立特征,表征能力更强,训练更快
    • 结构
      • Stem部分(stem network):卷积-池化-卷积-卷积-池化
      • 多个Inception结构堆叠
      • 输出:没有额外的全连接层(除了最后的类别输出层)
      • 辅助分类器:解决由于模型深度过深导致的梯度消失问题
  4. ResNet

    • 残差学习网络(deep residual leaning network)
      • 残差的思想:去掉相同的主体部分,从而突出微小的变化,防止梯度消失
      • 可以被用来训练非常深的网络
    • 深度有152层
    • 除了输出层没有其他全连接层
  5. 二维卷积和三维卷积区别

    二维卷积可以处理任何尺寸图像,三维卷积无视图像输入的通道数

    pytorch代码讲解ResNet

  6. ResNet解决网络层数加深导致网络退化

  7. 残差学习

    CNN可以拟合任何一个函数

    网络退化——H(x)难以训练,F(x)=H(x)-x可以实现,称为残差。

    数学公式:

第二次作业:卷积神经网络 part 1_第14张图片

网络实现:

第二次作业:卷积神经网络 part 1_第15张图片

  1. ResNet网络结构:

第二次作业:卷积神经网络 part 1_第16张图片

第二次作业:卷积神经网络 part 1_第17张图片

  1. 全局平均池化——替代全连接层——参数少,更少出现过拟合

  2. 50层上下的ResNet结构最大的区别

    50层以上的block有三层:BottleNeck(入口1x1降维,出口1x1升回),50层以下两层,

第二次作业:卷积神经网络 part 1_第18张图片

  1. ResNeXt:分组卷积

第二次作业:卷积神经网络 part 1_第19张图片

  1. SENet——注意力机制

第二次作业:卷积神经网络 part 1_第20张图片

  1. ResNeXt-Attention

第二次作业:卷积神经网络 part 1_第21张图片

  1. ResNeXt WSL

    弱监督训练

  2. 分组卷积最早出现在AlexNet

代码练习

  1. MNIST数据集分类

    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")
    
    #加载数据MNIST
    
    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)
    

第二次作业:卷积神经网络 part 1_第22张图片

#显示数据集中部分图像
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');

第二次作业:卷积神经网络 part 1_第23张图片

  • 创建网络
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))
  • 在小型全连接网络上训练
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)

第二次作业:卷积神经网络 part 1_第24张图片

  • 在卷积神经网络上训练
# 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)

第二次作业:卷积神经网络 part 1_第25张图片

  • 打乱像素顺序再次在两个网络上训练与测试
# 这里解释一下 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')

第二次作业:卷积神经网络 part 1_第26张图片

#全连接网络
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)

第二次作业:卷积神经网络 part 1_第27张图片

#卷积神经网络
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)

第二次作业:卷积神经网络 part 1_第28张图片

  1. 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')
    
    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]])
    

第二次作业:卷积神经网络 part 1_第29张图片

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')

网络训练:

第二次作业:卷积神经网络 part 1_第30张图片

# 得到一组图像
images, labels = iter(testloader).next()
# 展示图像
imshow(torchvision.utils.make_grid(images))
# 展示图像的标签
for j in range(8):
    print(classes[labels[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))

第二次作业:卷积神经网络 part 1_第31张图片

测试验证准确率结果:

Accuracy of the network on the 10000 test images: 64 %
  1. 使用VGG16对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_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')
    
    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(self.cfg)   #此处提示cfg未定义,添加self
            self.classifier = nn.Linear(512, 128)   #由(2048, 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, 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)
        
    # 网络放到GPU上
    net = VGG().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)   #报错,前后匹配,修改VGG
            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')
    

    网路训练:

第二次作业:卷积神经网络 part 1_第32张图片

测试验证准确率:

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))
Accuracy of the network on the 10000 test images: 83.67 %
  1. 使用VGG模型迁移学习进行猫狗大战

    import numpy as np
    import matplotlib.pyplot as plt
    import os
    import torch
    import torch.nn as nn
    import torchvision
    from torchvision import models,transforms,datasets
    import time
    import json
    import rarfile
    # 判断是否存在GPU设备
    device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
    print('Using gpu: %s ' % torch.cuda.is_available())
    
    Using gpu: True
    
    # 下载AI研习社的数据集并解压
    ! wget https://static.leiphone.com/cat_dog.rar
    path = "./cat_dog.rar"
    path2 = "./"
    rf = rarfile.RarFile(path)
    #待解压文件
    rf.extractall(path2) 
    #解压指定文件路径
    

    修改数据集路径格式,如下所示:

第二次作业:卷积神经网络 part 1_第33张图片

#datasets 是 torchvision 中的一个包,可以用做加载图像数据。它可以以多线程(multi-thread)的形式从硬盘中读取数据,使用 mini-batch 的形式,在网络训练中向 GPU 输送。在使用CNN处理图像时,需要进行预处理。图片将被整理成 $224x 224 x3 的大小,同时还将进行归一化处理
normalize = transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])

vgg_format = transforms.Compose([
                transforms.CenterCrop(224),
                transforms.ToTensor(),
                normalize,
            ])

data_dir = './cat_dog'

dsets = {x: datasets.ImageFolder(os.path.join(data_dir, x), vgg_format)
         for x in ['train', 'val']}
dset_sizes = {x: len(dsets[x]) for x in ['train', 'val']}
dset_classes = dsets['train'].classes

tsets = {y: datasets.ImageFolder(os.path.join(data_dir, y), vgg_format)
        for y in ['test']}
tset_sizes = {y: len(tsets[y]) for y in ['test']}

loader_train = torch.utils.data.DataLoader(dsets['train'], batch_size=64, shuffle=True, num_workers=6)
loader_valid = torch.utils.data.DataLoader(dsets['val'], batch_size=5, shuffle=False, num_workers=6)

loader_test=torch.utils.data.DataLoader(tsets['test'],batch_size=5,shuffle=False,num_workers=6)

'''
val 数据一共有2000张图,每个batch是5张,因此,下面进行遍历一共会输出到 400
同时,把第一个 batch 保存到 inputs_try, labels_try,分别查看
'''
count = 1
for data in loader_valid:
    print(count, end='\n')
    if count == 1:
        inputs_try,labels_try = data
    count +=1

# 显示图片的小程序
def imshow(inp, title=None):
#   Imshow for Tensor.
    inp = inp.numpy().transpose((1, 2, 0))
    mean = np.array([0.485, 0.456, 0.406])
    std = np.array([0.229, 0.224, 0.225])
    inp = np.clip(std * inp + mean, 0,1)
    plt.imshow(inp)
    if title is not None:
        plt.title(title)
    plt.pause(0.001)  # pause a bit so that plots are updated
    
# 显示 labels_try 的5张图片,即valid里第一个batch的5张图片
out = torchvision.utils.make_grid(inputs_try)
imshow(out, title=[dset_classes[x] for x in labels_try])

第二次作业:卷积神经网络 part 1_第34张图片

!wget https://s3.amazonaws.com/deep-learning-models/image-models/imagenet_class_index.json

第二次作业:卷积神经网络 part 1_第35张图片

model_vgg = models.vgg16(pretrained=True)

with open('./imagenet_class_index.json') as f:
    class_dict = json.load(f)
dic_imagenet = [class_dict[str(i)][1] for i in range(len(class_dict))]

inputs_try , labels_try = inputs_try.to(device), labels_try.to(device)
model_vgg = model_vgg.to(device)

outputs_try = model_vgg(inputs_try)

print(outputs_try)
print(outputs_try.shape)

'''
可以看到结果为5行,1000列的数据,每一列代表对每一种目标识别的结果。
但是我也可以观察到,结果非常奇葩,有负数,有正数,
为了将VGG网络输出的结果转化为对每一类的预测概率,我们把结果输入到 Softmax 函数
'''
m_softm = nn.Softmax(dim=1)
probs = m_softm(outputs_try)
vals_try,pred_try = torch.max(probs,dim=1)

print( 'prob sum: ', torch.sum(probs,1))
print( 'vals_try: ', vals_try)
print( 'pred_try: ', pred_try)

print([dic_imagenet[i] for i in pred_try.data])
imshow(torchvision.utils.make_grid(inputs_try.data.cpu()), 
       title=[dset_classes[x] for x in labels_try.data.cpu()])

第二次作业:卷积神经网络 part 1_第36张图片

'''
第一步:创建损失函数和优化器

损失函数 NLLLoss() 的 输入 是一个对数概率向量和一个目标标签. 
它不会为我们计算对数概率,适合最后一层是log_softmax()的网络. 
'''
criterion = nn.NLLLoss()

# 学习率
lr = 0.001

# 随机梯度下降
optimizer_vgg = torch.optim.SGD(model_vgg_new.classifier[6].parameters(),lr = lr)

'''
第二步:训练模型
'''

def train_model(model,dataloader,size,epochs=1,optimizer=None):
    model.train()
    
    for epoch in range(epochs):
        running_loss = 0.0
        running_corrects = 0
        count = 0
        for inputs,classes in dataloader:
            inputs = inputs.to(device)
            classes = classes.to(device)
            outputs = model(inputs)
            loss = criterion(outputs,classes)           
            optimizer = optimizer
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
            _,preds = torch.max(outputs.data,1)
            # statistics
            running_loss += loss.data.item()
            running_corrects += torch.sum(preds == classes.data)
            count += len(inputs)
            print('Training: No. ', count, ' process ... total: ', size)
        epoch_loss = running_loss / size
        epoch_acc = running_corrects.data.item() / size
        print('Loss: {:.4f} Acc: {:.4f}'.format(
                     epoch_loss, epoch_acc))
                
# 模型训练
train_model(model_vgg_new,loader_train,size=dset_sizes['train'], epochs=1, 
            optimizer=optimizer_vgg)

第二次作业:卷积神经网络 part 1_第37张图片

'''
第三步:测试模型
'''
def test_model(model,dataloader,size):
    model.eval()
    predictions = np.zeros(size)
    all_classes = np.zeros(size)
    all_proba = np.zeros((size,2))
    i = 0
    running_loss = 0.0
    running_corrects = 0
    for inputs,classes in dataloader:
        inputs = inputs.to(device)
        classes = classes.to(device)
        outputs = model(inputs)
        loss = criterion(outputs,classes)           
        _,preds = torch.max(outputs.data,1)
        # statistics
        running_loss += loss.data.item()
        running_corrects += torch.sum(preds == classes.data)
        predictions[i:i+len(classes)] = preds.to('cpu').numpy()
        all_classes[i:i+len(classes)] = classes.to('cpu').numpy()
        all_proba[i:i+len(classes),:] = outputs.data.to('cpu').numpy()
        i += len(classes)
        print('Testing: No. ', i, ' process ... total: ', size)        
    epoch_loss = running_loss / size
    epoch_acc = running_corrects.data.item() / size
    print('Loss: {:.4f} Acc: {:.4f}'.format(
                     epoch_loss, epoch_acc))
    return predictions, all_proba, all_classes
  
predictions, all_proba, all_classes test_model(model_vgg_new,loader_valid,size=dset_sizes['val'])

第二次作业:卷积神经网络 part 1_第38张图片

训练test数据集,将结果写入results.csv

import re

result = []
# 测试数据集,获到识别结果
for item,lable in loader_test:
  item = item.to(device)
  outputs =  model_vgg_new(item)
  _,pre = torch.max(outputs.data,1)
  result += pre

result_end =list()

count1 = 0
for item in result:
  string = tsets['test'].imgs[count1][0]
  num = re.sub("\D", "", string)
  result_end.append((num,item.tolist()))
  count1 += 1

result_sort = sorted(result_end,key=lambda x:int(x[0]))

# 写入csv文件
import csv
 
f = open('results.csv','w')
writer = csv.writer(f)
for i in result_sort:
    writer.writerow(i)
f.close()

提交AI研习社,查看结果评测

你可能感兴趣的:(第二次作业:卷积神经网络 part 1)