由x = x.view(x.size()[0], -1)引发的对CNN模型的分析

本文详细解析了在构建LeNet模型时,x=x.view(x.size()[0],-1)这行代码的作用,该代码用于将特征张量扁平化,以便输入到全连接层。通过实例展示了从3通道32x32的图像经过卷积和池化操作后,如何转换为适合多层感知机学习的一维向量。最后,讨论了在分类器中使用Softmax的重要性,以获得概率形式的分类结果。
摘要由CSDN通过智能技术生成

项目场景:

提示:自己搭建CNN以及ALexNet时:


问题描述:

有一段代码看不懂:x = x.view(x.size()[0], -1)
初步分析:应该是让特征张量扁平化,一维化。

class LeNetSequential(nn.Module):
    def __init__(self, classes):
        super(LeNetSequential, self).__init__()
        self.features = nn.Sequential(
            nn.Conv2d(3, 6, 5),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2, stride=2),
            nn.Conv2d(6, 16, 5),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2, stride=2),)

        self.classifier = nn.Sequential(
            nn.Linear(16*5*5, 120),
            nn.ReLU(),
            nn.Linear(120, 84),
            nn.ReLU(),
            nn.Linear(84, classes),)

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

原因分析:

这句话一般出现在model类的forward函数中,具体位置一般都是在调用分类器之前。分类器是一个简单的nn.Linear()结构,输入输出都是维度为一的值,x = x.view(x.size(0), -1) 这句话的出现就是为了将前面多维度的tensor展平成一维。

这里CNN分两步构建:1.特征提取模块搭建。2.分类器搭建。

  • 在特征提取模块self.features,先对图像进行365的卷积,relu函数作为激活函数,这是因为RELU函数的性能是优于sigmoid的,尤其在梯度消失方面,特征能够得到明显的刻画
  • 在分类器部分,就是用了多层感知机做分类。

在二者之间,x = x.view(x.size()[0], -1) 是将特征的高维度张量,变成扁平的一维列向量,供多层感知机学习。

要真正理解x = x.view(x.size()[0], -1),还需看下这段代码:

  `nn.Conv2d(3, 6, 5),
              nn.ReLU(),
              nn.MaxPool2d(kernel_size=2, stride=2),
              nn.Conv2d(6, 16, 5),
              nn.ReLU(),
              nn.MaxPool2d(kernel_size=2, stride=2),)`

假设:

net = LeNetSequential(classes=2)  # classes=2表示数据分两类
fake_img = torch.randn((4, 3, 32, 32))
output = net(fake_img)
print(output)`

即输入3通道3232的4张服从高斯分布的图片,nn.Conv2d(3, 6, 5),就让图片从3通道变成了6通道,二维卷积的卷积核大小是55(默认 stride=2),这时图片变成了16(32-5+1)28.
这些图片信息经过RELU后, nn.MaxPool2d(kernel_size=2, stride=2), 对图片池化,这时图片6
(28/2)*14.

nn.Conv2d(6, 16, 5) 让图片从6通道变成了16通道的1010大小图片。再经过池化,变成55.
最终,图片大小 1655, 有4张。
所以,nn.Linear(16*5*5, 120), 直接就写出来了图片的大小 torch.Size([4, 16, 5, 5]);

然而x.view(x.size()[0], -1) 中x.view的功能类似reshape,把图片变成了(4,1655)的二维张量 torch.Size([4, 400])。注意:这里的4是由于我生成了四张图,实际上进入分类器的是[1,400]的特征值,对这400各特征值逐层分类从400到120到84到2.

至此,我们得到了4*2的矩阵

      tensor([[ 0.0587, -0.0557],
      [ 0.0904, -0.0376],
      [ 0.0747, -0.0607],
      [ 0.0689, -0.0449]], grad_fn=<AddmmBackward>)

但是,我们发现对于二分类的分类结果仍然是毫无意义的,我们在分类器的输出增加一个softmax,让分类的结果的和为1,这样分类输出值即表示当前item等于这一类别的概率,结果如下:

    tensor([[0.4694, 0.5306],
    [0.4777, 0.5223],
    [0.4784, 0.5216],
    [0.4801, 0.5199]], grad_fn=<SoftmaxBackward>)

解决方案:

附代码:

import torch
import torchvision
import torch.nn as nn
from collections import OrderedDict

class LeNetSequential(nn.Module):
    def __init__(self, classes):
        super(LeNetSequential, self).__init__()
        self.features = nn.Sequential(
            nn.Conv2d(3, 6, 5),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2, stride=2),
            nn.Conv2d(6, 16, 5),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2, stride=2),)

        self.classifier = nn.Sequential(
            nn.Linear(16*5*5, 120),
            nn.ReLU(),
            nn.Linear(120, 84),
            nn.ReLU(),
            nn.Linear(84, classes),
            nn.Softmax(dim=1)
        )

    def forward(self, x):
        x = self.features(x)
        x = x.view(x.size()[0], -1)
        xx = x.size()
        print(xx)
        x = self.classifier(x)
        return x
        
net = LeNetSequential(classes=2)  # classes=2表示数据分两类
fake_img = torch.randn((4, 3, 32, 32))
output = net(fake_img)
print(output)



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值