之前在构建神经网络时,我们一般是采用这种方式,就像这样:
class Network1(nn.Module):
def __init__(self):
super(Network1,self).__init__()
self.conv1 = nn.Conv2d(in_channels=3, out_channels=6, kernel_size=5)
self.conv2 = nn.Conv2d(in_channels=6, out_channels=12, kernel_size=5)
self.fc1 = nn.Linear(in_features= 12*20*20, out_features=120)
self.out = nn.Linear(in_features=120, out_features=10)
def forward(self, t):
t=self.conv1(t)
t=self.conv2(t)
t=t.flatten(start_dim=1)
t=self.fc1(t)
t=self.out(t)
return t
在__init__()模块中,一系列的层被定义,比如卷积层,全连接层...在forward()方法中,我们对这些层进行操作,使得数据在网络中进行传播。
随着层的增加,以及池化操作,激活函数操作的增加,代码似乎变得很复杂。
在PyTorch中,提供了一种序列容器,叫做nn.Sequential(),它可以按照网络模块被添加的顺序依次执行。
于是上面的代码可以以这样的方式重写:
class Network2(nn.Module):
def __init__(self):
super(Network2,self).__init__()
self.layer1=nn.Sequential(nn.Conv2d(in_channels=3, out_channels=6, kernel_size=5),
nn.Conv2d(in_channels=6, out_channels=12, kernel_size=5),
nn.Flatten(),
nn.Linear(in_features= 12*20*20, out_features=120),
nn.Linear(in_features=120, out_features=10)
)
def forward(self,t):
t=self.layer1(t)
return t
让我们将一张图片输入到这两个网络中,看看会发生什么?
path="E:\\3-10\\input1.jpg"
img=Image.open(path)
img=img.resize((28,28))#改变图片尺寸
img=np.array(img)#转换为ndarray
img=torch.tensor(img,dtype=torch.float32)#转换为张量
img=img.permute(2,0,1)#改变维度顺序
img=img.unsqueeze(0)#增加批次维度
img.size()
torch.manual_seed(10)#随机数种子
net1=Network1()
torch.manual_seed(10)
net2=Network2()
net1(img),net2(img)
(tensor([[ 8.6586, 5.6796, -10.6183, -14.5155, -5.1435, -1.2218, -35.0356,
9.9759, -15.0035, -31.1104]], grad_fn=
), tensor([[ 8.6586, 5.6796, -10.6183, -14.5155, -5.1435, -1.2218, -35.0356,
9.9759, -15.0035, -31.1104]], grad_fn=
))
注:由于每次初始化网络时,权重是随机的,所以要设置一个随机数种子,使得两个网络以一种固定的权重初始化,以确保两个网络在接受数据输入前完全相同。
结果很显然,两种网络输出了同样的结果!
第一种构建网络的方法让层和操作隔离开来,使得我们在构建网络时不同部分时专注于一点即可,而第二种方法则是直接按照顺序直接构建了网络,看起来似乎更简洁。
当然,以什么方式构建网络完全取决于习惯,这并不会对结果造成任何影响!