美文网首页
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