项目场景:
提示:自己搭建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)