美文网首页
Pytorch构建神经网络的一些简单操作

Pytorch构建神经网络的一些简单操作

作者: 小黄不头秃 | 来源:发表于2022-09-01 00:27 被阅读0次

    (一)pytorch层与块的灵活性

    通过继承nn.Module这个类,可以对神经网络的构造进行自定义的设计。
    这里面主要要写两个函数:构造函数和forward函数

    下面直接开始代码吧!

    import torch
    from torch import nn
    from torch.nn import functional as F
    
    # 这里sequential是一个容器,用来装一些层,定义了一个特殊的module
    # 在神经网络里面,任何一个层应该都是nn.module的子类
    net  = nn.Sequential(nn.Linear(20,256),nn.ReLU(),nn.Linear(256,10))
    
    X = torch.rand((2,20))
    
    print(net(X).shape)
    
    # 我们来看一下如何来封装一个一样的多层感知机
    
    class MLP(nn.Module):
        def __init__(self):
            # 这里必须调用super 的构造函数,初始化一些内部参数
            super().__init__()
            self.hidden = nn.Linear(20,256)
            self.out = nn.Linear(256,10)
    
        # 重写forward方法
        def forward(self,X):
            return self.out(F.relu(self.hidden(X)))
    
    net1 = MLP()
    print(net1(X))
    
    # 现在来重新实现一下sequential
    
    from turtle import forward
    
    
    class my_sequential(nn.Module):
        def __init__(self,*args) -> None:
            super().__init__()
            for block in args:
                # 这是python里面的一个特殊的容器
                self._modules[block] = block
    
        def forward(self,X):
            for block in self._modules.values():
                X = block(X)
            return X
    
    net2  = my_sequential(nn.Linear(20,256),nn.ReLU(),nn.Linear(256,10))
    net2(X)                             
    
    
    # 以上我们定义的类似乎都是pytorch已经实现好的,那么我们为什么还要做这些工作呢?
    # 原因是我们可以做一些更为复杂或者巧妙地神经网络结构的设计
    
    class FixedHiddenMLP(nn.Module):
        def __init__(self) -> None:
            super().__init__()
            # 这个weight不参与反向传播,及不参与梯度下降
            self.rand_weight = torch.rand((20,20),requires_grad=False)
            self.linear = nn.Linear(20,20)
    
        def forward(self,X):
            # 不用疑惑为什么这么做,因为只是告诉你,在这里你可以为所欲为
            X = self.linear(X)
            X = F.relu(torch.mm(X, self.rand_weight)+1)
            X = self.linear(X)
            while X.abs().sum()>1:
                X /= 2
            return X.sum()
    
    net3 = FixedHiddenMLP()
    net3(X)
    
    # 下面的代码也没有实际意义,展示一下pytorch的灵活
    # 各种的混合搭配
    
    class NestMLP(nn.Module):
        def __init__(self) -> None:
            super().__init__()
            self.net = nn.Sequential(nn.Linear(20,256),nn.ReLU(),nn.Linear(256,10))
            self.linear = nn.Linear(10,10)
    
        def forward(self,X):
            return self.linear(self.net(X))
    
    net4 = NestMLP()
    net4(X)
    
    net5 = nn.Sequential(NestMLP(),nn.Linear(10,20),FixedHiddenMLP())
    net5(X)
    

    (二)参数管理

    怎样定义和访问参数,首先我们关注单隐藏层的多层感知机里面的参数。

    net = nn.Sequential(nn.Linear(4,8),nn.ReLU(),nn.Linear(8,1))
    
    X = torch.rand((4,4))
    
    net(X)
    
    # 可以通过下标的方式来访问网络中的层,因为nn.Sequential就是一个网络数组
    # state_dict()获取状态字典
    print(net[2].state_dict())
    print("-----------------------------------------------------------------------")
    
    # 直接获取bias和weights
    print(type(net[2].bias))
    print(net[2].bias)
    print(net[2].bias.data)
    
    print("-----------------------------------------------------------------------")
    print(type(net[2].weight))
    print(net[2].weight)
    print(net[2].weight.data)
    
    # 由于现在还没有做方向传播,所以梯度 = None
    net[2].weight.grad == None
    
    # 一次性的访问所有层的参数
    for name, param in net.named_parameters():
        print(f"name = {name}, parameters.shape = {param.shape}")
    
    print("-----------------------------------------------------------------------")
    print(*[(name, param.shape) for name, param in net[0].named_parameters()])
    
    # 还可以通过名字访问数据
    net.state_dict()['2.bias'].data
    
    # 从嵌套块收集参数
    def block1():
        return nn.Sequential(nn.Linear(4,8),nn.ReLU(),nn.Linear(8,4))
    
    def block2():
        net = nn.Sequential()
        for i in range(4):
            net.add_module(name=f"block_{i}",module=block1())
        return net
    
    rgnet = nn.Sequential(nn.Linear(4,4),block2())
    rgnet(X)
    
    # 打印网络结构
    print(rgnet)
    
    # 初始化参数,内置初始化,初始化为正态分布
    def init_normal(m):
        if type(m) == nn.Linear:
            # normal_做的事情就是直接修改参数,没有返回值
            nn.init.normal_(m.weight,mean=0,std=0.01)
            nn.init.zeros_(m.bias)
    
    # apply 遍历每个层,进行一些操作
    rgnet.apply(init_normal)
    print(rgnet[0].weight.data,rgnet[0].bias.data)
    print("-----------------------------------------------------------------------")
    
    # 初始化参数,内置初始化,初始化为一个常数
    def init_constant(m):
        if type(m) == nn.Linear:
            # normal_做的事情就是直接修改参数,没有返回值
            nn.init.constant_(m.weight,1)
            nn.init.zeros_(m.bias)
    
    # apply 遍历每个层,进行一些操作
    rgnet.apply(init_constant)
    print(rgnet[0].weight.data,rgnet[0].bias.data)
    
    def xavier(m):
        if type(m) == nn.Linear:
            nn.init.xavier_uniform_(m.weight)
    
    def init_42(m):
        if type(m) == nn.Linear:
            nn.init.constant_(m.weight,42)
    
    net[0].apply(xavier)
    net[2].apply(init_42)
    print(net[0].weight.data)
    print(net[2].weight.data)
    
    # 最暴力的方法
    net[0].weight.data[:] +=1
    net[0].weight.data[0,0] = 42
    print(net[0].weight.data)
    
    # 怎么共享参数,有可能前后有两个层的参数是一样的
    # 实际上就是一个变量用了两次
    shared = nn.Linear(8,8)
    
    net = nn.Sequential(nn.Linear(4,8),nn.ReLU(),shared,nn.ReLU(),shared,nn.ReLU(),nn.Linear(8,4))
    net(X)
    
    print(net[2].weight.data[0] == net[4].weight.data[0])
    

    (三)自定义层

    前面讲述了,怎么构造神经网络的结构,怎么访问参数和修改参数。
    那么神经网络的层是不是也是可以自定义的呢?
    下面将从带有参数和不带参数两个方面去考虑。

    # 不带参数的层
    class CenteredLayer(nn.Module):
        def __init__(self) -> None:
            super().__init__()
        
        def forward(self,X):
            return X-X.mean()
    
    layer = CenteredLayer()
    print(layer(torch.arange(12,dtype=torch.float32)))
    
    net = nn.Sequential(nn.Linear(4,8),CenteredLayer())
    net(X)
    
    class MyLinear(nn.Module):
        def __init__(self, in_uinits, unints) -> None:
            super().__init__()
            self.weight = nn.Parameter(torch.randn( in_uinits, unints))
            self.bias = nn.Parameter(torch.zeros(unints,))
    
        def forward(self,X):
            linear = torch.matmul(X,self.weight.data) + self.bias.data
            return F.relu(linear)
    
    dense = MyLinear(4,8)
    dense.weight
    dense(torch.arange(12,dtype=torch.float32).reshape((3,4)))
    

    (四)读写文件

    加载张量,保存张量。为了保存模型和参数。但是pytorch对模型结构的保存的支持并不是很好,所以我们主要是学习保存参数。

    import os 
    
    # create a directory
    if os.path.isdir("./network_param"):
        pass
    else:
        os.mkdir("./network_param")
    
    # save a tensor attribution
    x = torch.arange(12)
    torch.save(x,r'./network_param/x-file')
    
    x2 = torch.load(r'./network_param/x-file')
    print(x2)
    
    
    # 保存一个张量列表和字典
    y = torch.zeros(12)
    dic = {"x":x,"y":y}
    
    torch.save([x,y],r'./network_param/[x,y]-file')
    torch.save(dic,r'./network_param/dic-file')
    
    print(torch.load(r'./network_param/[x,y]-file'))
    print(torch.load(r'./network_param/dic-file'))
    
    # 加载和保存模型的参数
    class MLP(nn.Module):
        def __init__(self):
            # 这里必须调用super 的构造函数,初始化一些内部参数
            super().__init__()
            self.hidden = nn.Linear(20,256)
            self.out = nn.Linear(256,10)
    
        # 重写forward方法
        def forward(self,X):
            return self.out(F.relu(self.hidden(X)))
    
    net = MLP()
    y = net(torch.randn(2,20))
    
    # 保存参数
    torch.save(net.state_dict(),"./network_param/mlp_param")
    
    # 加载参数
    clone = MLP()
    clone.load_state_dict(torch.load("./network_param/mlp_param"))
    clone.eval()
    
    print(net.state_dict()["hidden.weight"] == clone.state_dict()["hidden.weight"])
    

    相关文章

      网友评论

          本文标题:Pytorch构建神经网络的一些简单操作

          本文链接:https://www.haomeiwen.com/subject/enkknrtx.html