当前位置:   article > 正文

Pytorch之经典神经网络CNN(六)——NiN(Fashion-MNIST)(全局平均池化GAP)(1*1卷积)(mlpconv)(k-fold validation)

mlpconv

2014年新加坡国立大学(颜水成)提出的

在GoogLeNet之前,该设计后来为GoogLeNet(Inception)和 ResNet 等网络模型所借鉴

NiN——Network In Network 网络中的网络

      前面的的LeNet、 AlexNet和VGG在设计上的共同之处是:先以由卷积层构成的模块充分抽取空间特征,再以由全连接层构成的模块来输出分类结果。其中, AlexNet和VGG对LeNet的改进主要在于如何对这两个模块加宽(增加通道数)和加深。

      而 NiN 提出了另外⼀一个思路,即串联多个由卷积层和“全连接”层构成的小网络来构建一个深层网络。采用了少量的参数就松松击败了Alexnet网络,Alexnet网络参数大小是230M,采用这篇paper的算法才29M,减小了将近10倍。NiN提出的网络结构,是对传统CNN网络的一种改进

一个mlpconv 就是一个NiN block


      我们知道,卷积层的输入和输出通常是四维数组(b,c,w,h), 而全连接层的输入和输出则通常是二维数组(样本,特征)。

      如果想在全连接层后再接上卷积层,则需要将全连接层的输出变换为四维。1*1的卷积层。它可以看成全连接层,其中空间维度(高和宽)上的每个元素相当于样本,通道相当于特征。因此, NiN使用1*1卷积层来替代全连接层,从而使空间信息能够自然传递到后面的层中去.

      这里的用1*1卷积代替全连接层只是在一个点的不同channel之间的全连接。并不是把整个feature打平了的那种全连接,那种看这里 https://blog.csdn.net/hxxjxw/article/details/123450949

      NIN网络也是首次提出1×1卷积核的paper,后续对1*1卷积核的应用都是自它而始

      NiN块是NiN中的基础块。它由一个卷积层加两个充当全连接层的卷积层串联⽽而成。其中第一个卷积层的超参数可以自行设置,⽽而第二和第三个卷积层的超参数一般是固定的

NiN的贡献总结主要就2点:

  • mlpconv的提出(我们用1x1卷积实现),整合多个feature map上的特征.进一步增强非线性.
  • 全局平均池化替代全连接层


MLP卷积层(mlpconv层)

      NiN paper最大的创新点就是提出了mlpconv层,即用MLP代替GLM广义线性模型

      一般卷积操作可以看成特征的提取操作,而一般卷积一层只相当于一个线性操作,所以其只能提取出线性特征。所以作者就想能否在卷积层后也加入一个MLP使得每层卷积操作能够提取非线性特征。而为了减少参数量,又用1*1的卷积层模拟了MLP

      注意一个NiN block是1个卷积后加两个1*1的卷积,但是每个1*1卷积后面都跟着一个ReLu, 所以可以引入更多的非线性。

全局平均池化

      Global Average Pooling主要为了解决全连接层参数过多的问题,早期对于分类问题,最后一个卷积层的 Feature Map 通常与全连接层连接,最后通过 softmax 逻辑回归分类。全连接层带来的问题就是参数空间过大,容易过拟合。早期 Alex 采用了Dropout 的方法,来减轻过拟合,提高网络的泛化能力,但依旧无法解决参数过多问题。

      global average pooling的概念非常简单,分类任务有多少个类别,就控制最终产生多少个feature map

      我们之前学的卷积神经网络后跟的普通池化都是局部池化。比方说一个10x10的输入,用2x2的窗口去做池化,然后这个窗口不断地滑动,从而对不同的2x2区域可以做求平均(平均池化),取最大值(最大值池化)等.这个就可以理解为'局部'的池化,2x2是10x10的一部分嘛.相应地,所谓全局池化,自然就是用一个和输入大小一样的窗口做池化,即对全部的输入做池化操作.

      例如,本例中经过4个NiN block后的x size是[128, 10, 5, 5],那么选取的avg pooling的kernel_size就是5

1×1卷积核的作用

       那么1×1卷积核有什么作用呢,如果当前层和下一层都只有一个通道那么1×1卷积核确实没什么作用,但是如果它们分别为m层和n层的话,1×1卷积核可以起到一个跨通道聚合的作用所以进一步可以起到降维(或者升维)的作用,起到减少参数的目的。 
      比如当前层为 x×x×m即图像大小为x×x,特征层数为m,然后如果将其通过1×1的卷积核,特征层数为n,那么只要n<m。这样就能起到降维的目的,减少之后步骤的运算量。如果使用1x1的卷积核,这个操作实现的就是多个feature map的线性组合,可以实现feature map在通道个数上的变化。 而因为卷积操作本身就可以做到各个通道的重新聚合的作用,所以1×1的卷积核也能达到这个效果。

        卷积核的每一个layer都是要在input的所有通道卷积求和,然后作为结果的一层layer

Fashion-MNIST数据集

       Fashion-MNIST 是一个替代原始的MNIST手写数字数据集的另一个图像数据集。 号称计算机视觉领域的Hello, World。它是由Zalando(一家德国的时尚科技公司)旗下的研究部门提供。其涵盖了来自10种类别的共7万个不同商品的正面图片(口红、包包、鞋子、裤子、衬衫、T恤、衬衣、靴子)。

       Fashion-MNIST的大小、格式和训练集/测试集划分与原始的MNIST完全一致。60000/10000的训练测试数据划分,28x28的单通道灰度图片。你可以直接用它来测试你的机器学习和深度学习算法性能,且不需要改动任何的代码

NiN实现代码

新增验证集的使用

  1. import torch
  2. from torch import nn, optim
  3. import torchvision
  4. from datetime import datetime
  5. device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
  6. #NiN块
  7. def nin_block(in_channels, out_channels, kernel_size, stride, padding):
  8. blk = nn.Sequential(
  9. nn.Conv2d(in_channels, out_channels, kernel_size, stride, padding),
  10. nn.ReLU(),
  11. #1*1卷积层
  12. nn.Conv2d(out_channels, out_channels, kernel_size=1),
  13. nn.ReLU(),
  14. #1*1卷积层
  15. nn.Conv2d(out_channels, out_channels, kernel_size=1),
  16. nn.ReLU()
  17. )
  18. return blk
  19. net = nn.Sequential(
  20. #输入x是[128, 1, 224, 224]
  21. #第一个卷积块
  22. nin_block(1, 96, kernel_size=11, stride=4, padding=0),
  23. #x是[128, 96, 54, 54]
  24. nn.MaxPool2d(kernel_size=3, stride=2),
  25. #x是[128, 96, 26, 26]
  26. #第二个卷积块
  27. nin_block(96, 256, kernel_size=5, stride=1, padding=2),
  28. #x是[128, 256, 26, 26]
  29. nn.MaxPool2d(kernel_size=3, stride=2),
  30. #x是[128, 256, 12, 12]
  31. #第三个卷积块
  32. nin_block(256, 384, kernel_size=3, stride=1, padding=1),
  33. #x是[128,384,12,12]
  34. nn.MaxPool2d(kernel_size=3, stride=2),
  35. nn.Dropout(0.5),
  36. #x是[128, 384, 5, 5]
  37. #第四个卷积块
  38. # 标签类别数是10
  39. nin_block(384, 10, kernel_size=3, stride=1, padding=1),
  40. #x是[128, 10, 5, 5]
  41. #全局平均池化层
  42. #全局平均池化层可通过将窗口形状设置成输入的高和宽实现
  43. nn.AvgPool2d(kernel_size=5),
  44. #x是[128, 10, 1, 1]
  45. # 将四维的输出转成二维的输出,其形状为(批量大小, 10)
  46. nn.Flatten(start_dim=1, end_dim=3)
  47. #x是[128, 10]
  48. )
  49. def get_acc(output, label):
  50. total = output.shape[0]
  51. #output是概率,每行概率最高的就是预测值
  52. _, pred_label = output.max(1)
  53. num_correct = (pred_label == label).sum().item()
  54. return num_correct / total
  55. batch_size = 128
  56. transform = torchvision.transforms.Compose([
  57. torchvision.transforms.Resize(size=224),
  58. torchvision.transforms.ToTensor()
  59. ])
  60. train_set = torchvision.datasets.FashionMNIST(
  61. root='dataset/',
  62. train=True,
  63. download=True,
  64. transform=transform
  65. )
  66. #hand-out留出法划分
  67. train_set, val_set = torch.utils.data.random_split(train_set, [50000,10000])
  68. test_set = torchvision.datasets.FashionMNIST(
  69. root='dataset/',
  70. train=False,
  71. download=True,
  72. transform=transform
  73. )
  74. train_loader = torch.utils.data.DataLoader(
  75. dataset=train_set,
  76. batch_size=batch_size,
  77. shuffle=True
  78. )
  79. val_loader = torch.utils.data.DataLoader(
  80. dataset=val_set,
  81. batch_size=batch_size,
  82. shuffle=True
  83. )
  84. test_loader = torch.utils.data.DataLoader(
  85. dataset=test_set,
  86. batch_size=batch_size,
  87. shuffle=False
  88. )
  89. lr = 2e-3
  90. optimizer = optim.Adam(net.parameters(), lr=lr)
  91. critetion = nn.CrossEntropyLoss()
  92. net = net.to(device)
  93. prev_time = datetime.now()
  94. valid_data = val_loader
  95. for epoch in range(3):
  96. train_loss = 0
  97. train_acc = 0
  98. net.train()
  99. for inputs,labels in train_loader:
  100. inputs = inputs.to(device)
  101. labels = labels.to(device)
  102. #forward
  103. outputs = net(inputs)
  104. loss = critetion(outputs,labels)
  105. #backward
  106. optimizer.zero_grad()
  107. loss.backward()
  108. optimizer.step()
  109. train_loss += loss.item()
  110. train_acc += get_acc(outputs, labels)
  111. #最后还要求平均的
  112. #显示时间
  113. cur_time = datetime.now()
  114. h,remainder = divmod((cur_time - prev_time).seconds, 3600)
  115. m,s = divmod(remainder,60)
  116. # time_str = 'Time %02d:%02d:%02d'%(h,m,s)
  117. time_str = 'Time %02d:%02d:%02d(from %02d/%02d/%02d %02d:%02d:%02d to %02d/%02d/%02d %02d:%02d:%02d)' % (h, m, s,prev_time.year,prev_time.month,prev_time.day,prev_time.hour,prev_time.minute,prev_time.second,cur_time.year,cur_time.month,cur_time.day,cur_time.hour,cur_time.minute,cur_time.second)
  118. #validation
  119. with torch.no_grad():
  120. net.eval()
  121. valid_loss = 0
  122. valid_acc = 0
  123. for inputs,labels in valid_data:
  124. inputs = inputs.to(device)
  125. labels = labels.to(device)
  126. outputs = net(inputs)
  127. loss = critetion(outputs,labels)
  128. valid_loss += loss.item()
  129. valid_acc += get_acc(outputs,labels)
  130. print("Epoch %d. Train Loss: %f, Train Acc: %f, Valid Loss: %f, Valid Acc: %f,"
  131. %(epoch, train_loss/len(train_loader), train_acc / len(train_loader), valid_loss / len(valid_data),
  132. valid_acc / len(valid_data))
  133. + time_str)
  134. torch.save(net.state_dict(),'params.pkl')
  135. #测试
  136. with torch.no_grad():
  137. net.eval()
  138. correct = 0
  139. total = 0
  140. for (images, labels) in test_loader:
  141. images, labels = images.to(device), labels.to(device)
  142. output = net(images)
  143. _, predicted = torch.max(output.data, 1)
  144. total += labels.size(0)
  145. correct += (predicted == labels).sum().item()
  146. print("The accuracy of total {} images: {}%".format(total, 100 * correct / total))

在神经网络中一般少见到k-fold validation,因为在机器学习中用k-fold validation是为了充分利用数据集,而在比如CNN中数据量足够大的话可以不做交叉验证的

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/92536
推荐阅读
相关标签
  

闽ICP备14008679号