总结一下相关概念:
- torch.Tensor - 一个近似多维数组的数据结构
- autograd.Variable - 改变Tensor并且记录下来操作的历史记录。和Tensor拥有相同的API,以及backward()的一些API。同时包含着和张量相关的梯度
- nn.Module - 神经网络模块,便捷的数据封装,能够将运算移往GPU,还包括一些输入输出的东西
- nn.Parameter - 一种变量(Variable),当将任何值赋予Module时自动注册为一个参数
- autograd.Function - 实现了使用自动求导方法的前馈和后馈的定义。每个Variable的操作都会生成至少一个独立的Function节点,与生成了Variable的函数相连之后记录下操作历史
导入库:
# Author : Hellcat # Time : 2018/2/10 import torch as t import torch.nn as nn import torch.optim as optim import torch.nn.functional as F from torch.autograd import Variable
torch.nn:网络层
torch.nn.functional:激活函数、池化函数归于此模块
pytorch中的网络层是class,而tensorflow
print(t.nn.Conv2d)
<class 'torch.nn.modules.conv.Conv2d'>
print(tf.nn.conv2d)
<function conv2d at 0x000001A33CC44510>
网络主体:
net网络要使用class并继承父类才行,因而有一些自带的方法
net.parameters():返回全部的参数值,迭代器
net.named_parameters():返回参数名称和值,迭代器
net.参数名:就是参数变量,Variable,可以直接查看data和grad等等
class Net(nn.Module): def __init__(self): # nn.Module子类的函数必须在构造函数中执行父类的构造函数 # 等价于nn.Model.__init__(self) super(Net,self).__init__() # 输入1通道,输出6通道,卷积核5*5 self.conv1 = nn.Conv2d(1, 6, 5) # 定义卷积层:输入6张特征图,输出16张特征图,卷积核5x5 self.conv2 = nn.Conv2d(6,16,5) # 定义全连接层:线性连接(y = Wx + b),16*5*5个节点连接到120个节点上 self.fc1 = nn.Linear(16*5*5,120) # 定义全连接层:线性连接(y = Wx + b),120个节点连接到84个节点上 self.fc2 = nn.Linear(120,84) # 定义全连接层:线性连接(y = Wx + b),84个节点连接到10个节点上 self.fc3 = nn.Linear(84,10) # 定义向前传播函数,并自动生成向后传播函数(autograd) def forward(self,x): # 输入x->conv1->relu->2x2窗口的最大池化->更新到x x = F.max_pool2d(F.relu(self.conv1(x)),(2,2)) # 输入x->conv2->relu->2x2窗口的最大池化->更新到x x = F.max_pool2d(F.relu(self.conv2(x)),2) # view函数将张量x变形成一维向量形式,总特征数不变,为全连接层做准备 x = x.view(x.size()[0], -1) # 输入x->fc1->relu,更新到x x = F.relu(self.fc1(x)) # 输入x->fc2->relu,更新到x x = F.relu(self.fc2(x)) # 输入x->fc3,更新到x x = self.fc3(x) return x if __name__ == "__main__": net = Net()
展示网络参数:
# #########查看参数######### print(net) """ Net( (conv1): Conv2d (1, 6, kernel_size=(5, 5), stride=(1, 1)) (conv2): Conv2d (6, 16, kernel_size=(5, 5), stride=(1, 1)) (fc1): Linear(in_features=400, out_features=120) (fc2): Linear(in_features=120, out_features=84) (fc3): Linear(in_features=84, out_features=10) ) """ # 返回参数值:顺序和下面的named一致 params = list(net.parameters()) print(len(params)) """ 10 """ # net.named_parameters(): ((参数名称,参数属性),……) for name, parameters in net.named_parameters(): print(name, ":", parameters.size()) """ conv1.weight : torch.Size([6, 1, 5, 5]) conv1.bias : torch.Size([6]) conv2.weight : torch.Size([16, 6, 5, 5]) conv2.bias : torch.Size([16]) fc1.weight : torch.Size([120, 400]) fc1.bias : torch.Size([120]) fc2.weight : torch.Size([84, 120]) fc2.bias : torch.Size([84]) fc3.weight : torch.Size([10, 84]) fc3.bias : torch.Size([10]) """
模拟单次向前&向后传播:
# #########网络传播过程模拟######### # 输入如果没有batch数,则少一维度,Tensor,unsqueeze()可以为张量扩维 input_ = Variable(t.randn(1, 1, 32, 32)) out = net(input_) print(out.size()) """ torch.Size([1, 10]) """ # net.zero_grad() # 输出值为10个标量(一个向量),所以需要指定每个标量梯度的权重 # out.backward(t.ones(1,10))
注意: torch.nn 只接受小批量的数据
整个torch.nn包只接受那种小批量样本的数据,而非单个样本。 例如,nn.Conv2d能够结构一个四维的TensornSamples x nChannels x Height x Width。
如果你拿的是单个样本,使用input.unsqueeze(0)来加一个假维度就可以了。
维度是[batch,channel,height,width]。
Loss函数构建
# #########Loss设计######### target = Variable(t.arange(0,10)) # Loss需要先实例化,然后是callable的实例 loss_fn = nn.MSELoss() # 均方误差 loss = loss_fn(out, target) print(loss) net.zero_grad() print("反向传播之前:", net.conv1.bias.grad) loss.backward() print("反向传播之后:", net.conv1.bias.grad)
反向传播之前: None
反向传播之后: Variable containing:
-0.1330
-0.0888
-0.0101
-0.0186
0.0462
0.0317
[torch.FloatTensor of size 6]
优化器构建
# #########优化器设计######### print(net.parameters()) """ <generator object Module.parameters at 0x0000021B525BE888> """ # 初始化优化器 optimizer = optim.SGD(net.parameters(), lr=0.01) optimizer.zero_grad() # 效果等同net.zero_grad() output = net(input_) loss = loss_fn(output, target) loss.backward() print("反向传播之前:", net.conv1.bias.data) optimizer.step() print("反向传播之后:", net.conv1.bias.data)
反向传播之前:
-0.1702
0.1192
0.1349
0.1307
-0.0141
-0.0558
[torch.FloatTensor of size 6]
反向传播之后:
-0.1689
0.1201
0.1350
0.1309
-0.0146
-0.0561
[torch.FloatTensor of size 6]