Pytorch 层的介绍以及网络的搭建

前一篇介绍了pytorch的基本变量、库等知识,这篇着重介绍pytorch的层的功能。

层的定义都在torch.nn

  • 全连接层:torch.nn.Linear()
  • ReLU激活层:torch.nn.ReLU()
  • Drop层:torch.nn.Dropout(p=0.5)
  • 卷积层:torch.nn.Conv2d(1,64,kernel_size = 3,strid = 1,padding =1)
  • 池化层:torch.nn.MaxPool2d(strid = 2,kernel_size = 2)
  • 归一层:torch.nn.BatchNorm1d(in_channels * A.size(1))

网络的搭建

5.重写Model 类

在Model类中可以实现网络结构以及前向、候选传播函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Model(torch.nn.Module):
"""docstring for Model"torch.nn.Module"""
def __init__(self):
super(Model,self).__init__()

##可以定意网络结构:
self.conv1 = torch.nn.Sequential(
torch.nn.Conv2d(1,64,kernel_size = 3,strid = 1,padding =1),
torch.nn.ReLU(),
torch.nn.Conv2d(64,128,kernel_size = 3,strid = 1,padding =1),
torch.nn.ReLU(),
torch.nn.MaxPool2d(strid = 2,kernel_size = 2)
)

Conv2d(输入通道数,输出通道数...).

##不一定需要定义完,可以分开定意不同模块:
self.dense = torch.nn.Sequential(
torch.nn.Linear(14*14*128,1024),
torch.nn.ReLU(),
torch.nn.Dropout(p=0.5),
torch.nn.Linear(1024,10)
)

##定义forward() 函数:
def forward(self,x):
x = conv1(x)
x = view(-1,14*14*1024) ##torch中对Tensor变量的flat操作。
x = dense(x)
return x

Model

Model 类来自package: torch.nn.Module
其中会有定义的网络Sequential参数,比如定义了 conv1 在其下有_modules参数:可以在着拉看到每一层的data。在该层变量中,可以看到其中包含的参数,其中比较重要的是:

  • weight
  • bias
  • dense

已有模型的加载loadModel

model.load_state_dict(torch.load("checkPoints/LLModel_0708_21:54:25.pth"))

model的加载是使用.load_state_dict()

torch.load("checkPoints/LLModel_0708_21:54:25.pth") 这个得到的是一个OrderDict,包含了每一层的参数(有参数的参数层,像Pool层是没有的)

1
2
3
4
5
6
7
8
model_path = "/home/joey/Documents/models"
# model = models.resnet50(pretrained=True)
# # model_modules = model._modules
# features = model.features
model_name = "vgg16"
load_model_path = os.path.join(model_path,model_name,"Pytorch",model_name+".pth")
model = models.vgg16(pretrained= False)
model.load_state_dict(torch.load(load_model_path))

Pytorch中的模型层的搭建:

  • 使用nn.Sequential()来搭建
    一个时序容器。Modules 会以他们传入的顺序被添加到容器中。当然,也可以传入一个OrderedDict。
    nn.Sequential()的输入参数可以是多个torch.nn.对象。
    为了更容易的理解如何使用Sequential, 下面给出了一个例子:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    def __init__(self):
    super(Models, self).__init__()
    self.Conv = torch.nn.Sequential(
    torch.nn.Conv2d(3, 32, kernel_size=3, stride=1, padding=1),
    torch.nn.ReLU(),
    torch.nn.Conv2d(32, 32, kernel_size=3, stride=1, padding=1),
    torch.nn.ReLU(),
    torch.nn.MaxPool2d(kernel_size=2, stride=2),
    torch.nn.Conv2d(32, 64, kernel_size=3, stride=1, padding=1),
    torch.nn.ReLU(),
    torch.nn.Conv2d(64, 64, kernel_size=3, stride=1, padding=1),
    torch.nn.ReLU(),
    torch.nn.MaxPool2d(kernel_size=2, stride=2)
    )
    self.Classes = torch.nn.Sequential(
    torch.nn.Linear(9 * 9 * 64, 1024),
    torch.nn.ReLU(), torch.nn.Dropout(p=0.5),
    torch.nn.Linear(1024, 1024),
    torch.nn.ReLU(),
    torch.nn.Dropout(p=0.5),
    torch.nn.Linear(1024, 45)
    )

    # Example of using Sequential

    model = nn.Sequential(
    nn.Conv2d(1,20,5),
    nn.ReLU(),
    nn.Conv2d(20,64,5),
    nn.ReLU()
    )
    # Example of using Sequential with OrderedDict
    model = nn.Sequential(OrderedDict([
    ('conv1', nn.Conv2d(1,20,5)),
    ('relu1', nn.ReLU()),
    ('conv2', nn.Conv2d(20,64,5)),
    ('relu2', nn.ReLU())
    ]))

OrderedDict可以存储层的名字,输出是这样的

1
2
3
4
5
6
(ta): Sequential(
(conv1): Conv2d(1, 20, kernel_size=(5, 5), stride=(1, 1))
(relu1): ReLU()
(conv2): Conv2d(20, 64, kernel_size=(5, 5), stride=(1, 1))
(relu2): ReLU()
)

而直接使用Sequential输出是这样的:

1
2
3
4
5
6
7
8
9
(Classes): Sequential(
(0): Linear(in_features=5184, out_features=1024, bias=True)
(1): ReLU()
(2): Dropout(p=0.5)
(3): Linear(in_features=1024, out_features=1024, bias=True)
(4): ReLU()
(5): Dropout(p=0.5)
(6): Linear(in_features=1024, out_features=45, bias=True)
)

默认从0开始的标号作为其层的名字

  • 使用nn.ModuleList()来搭建
    nn.ModuleList()的输入参数是一个元组,包含了所有的Module
    st-gcn中正是使用ModuleList来wrap st_gcn这个自定义层的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    self.st_gcn_networks = nn.ModuleList((
    st_gcn(in_channels, 64, kernel_size, 1, residual=False, **kwargs0),
    st_gcn(64, 64, kernel_size, 1, **kwargs),
    st_gcn(64, 64, kernel_size, 1, **kwargs),
    st_gcn(64, 64, kernel_size, 1, **kwargs),
    st_gcn(64, 128, kernel_size, 2, **kwargs),
    st_gcn(128, 128, kernel_size, 1, **kwargs),
    st_gcn(128, 128, kernel_size, 1, **kwargs),
    st_gcn(128, 256, kernel_size, 2, **kwargs),
    st_gcn(256, 256, kernel_size, 1, **kwargs),
    st_gcn(256, 256, kernel_size, 1, **kwargs),
    ))
  • 使用类属性注册搭建:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    for id in range(self.hiden_Num):
    inSize = 1 if id ==0 else 10
    fc = nn.Linear(inSize,10)
    self.feature.add_module('fc_%i' % id, fc)
    # setattr(self.feature, 'fc_%i' % id, fc) # 注意! pytorch 一定要你将层信息变成 class 的属性! 我在这里花了2天时间发现了这个 bug
    self._set_init(fc) # 参数初始化
    self.fcs.append(fc)

    if use_Bn:
    bn = nn.BatchNorm1d(10,momentum=0.5)
    setattr(self.feature,'bn_%i'%id,bn)
    self.bns.append(bn)

    re = nn.ReLU()
    self.feature.add_module('relu_%i' % id, re)

    self.predict = nn.Linear(10, 1) # output layer
    self._set_init(self.predict) # 参数初始化

setattr(self.feature, ‘fc_%i’ % id, fc) 也可以在序列中添加层,以达到类似于Sequential(OrderDict[])的效果。

1
2
3
4
5
6
self.feature = nn.Sequential() # 新建一个序列对象。

inSize = 1 if id ==0 else 10
fc = nn.Linear(inSize,10)
setattr(self.feature, 'fc_%i' % id, fc) #向序列中添加层
re = nn.ReLU()

self.feature.addmodule(‘fc%i’ % id, fc)
也可以调用Sequentialadd_module()函数添加层

损失函数

cost = torch.nn.CrossEntropyLoss() softmax损失
定义cost对象,之后计算只用传入数据即可
需要注意的是loss = cost(y_pred,var_y) 输入时有顺序的先写预测,再写label

  1. cost = torch.nn.MSELoss() 欧式损失

优化算法

optimizer = torch.optim.Adam(model.parameters())定义opt对象,可采用自定义优化算法,需要将model的参数全部传入,以算法进行计算。
需要注意的是:
optimizer.zero_grad(),每次更新w前,需要将上一次迭代的梯度清理。

关于data_loader_traindata的说明:

data_loader_train 是整个数据集

data_loader_train 一次生成batch_size个数据shape = (64,1,28,28)所有第二个for是整个batch_size的矩阵计算

Model的forward()

自定义Model继承了Module后需要重写父类的 forward()函数即计算整个计算图。y_pred = model(var_x)
需要注意的是:model(var_x)的输入参数必须是Variable类型,需要转换。

启动GPU模式

  • model.cuda()
  • variable(x).cuda()

gpu-cpu数据转化

model = Model()
model.cuda() 开启GPU训练模式


如果要使用GPU训练,则所所有数据必须转换成cuda的形式


cuda_var(x).cpu()

1
2
3
4
5
6
7
x,y = next(iter(dataloader["test"]))
with torch.no_grad():
x, y = Variable(x).cuda(), Variable(y).cuda()
pre = self.model.forward(x)
plt.plot(x.cpu().numpy(),pre.cpu().numpy(),'x')
plt.plot(x.cpu().detach().numpy(),y.cpu().detach().numpy(),'o')
plt.show()

从PyTorch中取出数据进行numpy操作:

如果对象是Variable类型,取出其数据需要注意以下几点:

  • 如果是有grad_requires = True的,当前phase又不需要梯度,那么需要with torch.no_grad()来节约显存资源
  • 如果不使用with torch.no_grad,也可以使用x.cpu().detach().numpy()来获得
  • 如果在gpu上的数据,需要使用data_x.cpu放在cpu上
  • 数据得到后Tensor 2 numpy只需要执行 tesnor.numpy即可