5.13学习日志

Pytorch 神经网络基础

1.模型构造

1》层和块

块可以描述单个层,由多个层组成的组件或者模型本身

块由类表示,类的任何子类都必须定义一个将其输入转换为输出的前向传播函数。为了计算梯度,块必须具有反向传播函数

自定义块:

顺序块:

python 复制代码
import  torch
from torch import nn

#输入
X=torch.rand(2,20)

#顺序块
class MySequential(nn.Module):
    def __init__(self,*args):#*args是收集参数,相当于把若干个参数打包成一个来传入
       super().__init__()#调用父类的__init__函数
       for idx, block in enumerate(args):#遍历传入的神经网络层,并将它们以字符串索引作为键存储在 MySequential 实例的 _modules 属性中
            self._modules[str(idx)] = block
        #在模型参数初始化过程中,系统直到在_modules字典中查找需要初始化参数的子块

    def forward(self,X):
        for block in self._modules.values():
        #遍历 _modules 中的每个神经网络层
            X = block(X)#将当前层的输出作为下一层输入
        return X

net=MySequential(nn.Linear(20,256),nn.ReLU(),nn.Linear(256,10))
a=net(X)

print(a)

在前向传播函数中执行代码

当sequential函数过于简单没法满足需求,可以在init和forward函数里进行自定义运算,继承nn.Module比继承Sequential更灵活的定义参数,和进行前向运算

python 复制代码
import  torch
from torch import nn
from torch.nn import functional as F

X=torch.rand(2,20)

class FixedHiddenMLP(nn.Module):
    def __init__(self):
        super().__init__()
        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与权重相乘
        X = self.linear(X)
        while X.abs().sum()>1:#取绝对值之和
            X /= 2
        #使用 while 循环,如果输出张量的绝对值之和大于 1,则将输出张量除以 2,直到满足条件为止。这个操作可以视为一种归一化或缩放操作,以控制输出的数值范围
        return X.sum()

net = FixedHiddenMLP()
a = net(X)
print(a)

嵌套使用Module 的子类

嵌套体现在两个方面:

  1. NestMLP 类内部的 self.netself.linear 形成了一个嵌套的子模块结构。

chimera 模块通过组合 NestMLP、线性层和 FixedHiddenMLP,形成了一个嵌套的计算流程。

python 复制代码
import  torch
from torch import nn
from torch.nn import functional as F

X=torch.rand(2,20)

class FixedHiddenMLP(nn.Module):
    def __init__(self):
        super().__init__()
        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与权重相乘
        X = self.linear(X)
        while X.abs().sum()>1:#取绝对值之和
            X /= 2
        #使用 while 循环,如果输出张量的绝对值之和大于 1,则将输出张量除以 2,直到满足条件为止。这个操作可以视为一种归一化或缩放操作,以控制输出的数值范围
        return X.sum()

class NestMLP(nn.Module):
    def __init__(self):
        super().__init__()
        self.net = nn.Sequential(nn.Linear(20,64),nn.ReLU(),
                                 nn.Linear(64,32),nn.ReLU())
        self.linear = nn.Linear(32,16)

    def forward(self,X):
        return self.linear(self.net(X))

chimera = nn.Sequential(NestMLP(),nn.Linear(16,20),FixedHiddenMLP())
chimera(X)

2.参数管理

参数访问:
python 复制代码
import torch
from torch import nn
from torch.nn import functional as F

net = nn.Sequential(nn.Linear(4,8),nn.ReLU(),nn.Linear(8,1))
X = torch.rand(size=(2,4))
net(X)

print(net[2].state_dict())#检查第二个全连接层即Linear(8,1)的参数
#OrderedDict([('weight', tensor([[ 0.2360,  0.1646,  0.0861,  0.0126, -0.0548,  0.1074,  0.0612, -0.1232]])), ('bias', tensor([-0.1082]))])
访问目标参数
python 复制代码
print(type(net[2].bias))#访问第二个全连接层的偏移的类型
print(net[2].bias)#访问第二个全连接层的偏移
print(net[2].bias.data)#访问第二个全连接层的偏移的数值
'''<class 'torch.nn.parameter.Parameter'>
Parameter containing:
tensor([0.3516], requires_grad=True)
tensor([0.3516])'''

print(net[2].weight.grad == None)#访问参数梯度,由于还没有开始反向传播,因此参数的梯度处于初始状态
#True
一次性访问所有参数
python 复制代码
print(*[(name,param.shape)for name, param in net[0].named_parameters()])
#('weight', torch.Size([8, 4])) ('bias', torch.Size([8]))
print(*[(name,param.shape)for name, param in net.named_parameters()])
#('0.weight', torch.Size([8, 4])) ('0.bias', torch.Size([8])) ('2.weight', torch.Size([1, 8])) ('2.bias', torch.Size([1]))
从嵌套块收集参数
python 复制代码
import torch
from torch import nn
from torch.nn import functional as F

X=torch.rand(2,4)

def block1():
    return nn.Sequential(nn.Linear(4,8),nn.ReLU(),
                         nn.Linear(8,4),nn.ReLU())

def block2():
    net = nn.Sequential()#创建一个空的 nn.Sequential 容器,用于按顺序组合多个 block1 模块
    for i in range(4):
        #嵌套
        net.add_module(f'block{i}',block1())#将四个block1()组合起来,用add_module的好处是可以传一个字符串的名字
    return(net)

rgnet = nn.Sequential(block2(),nn.Linear(4,1))
output = rgnet(X)
print(output)
print(rgnet)

显示嵌套结构

python 复制代码
Sequential(
  (0): Sequential(
    (block0): Sequential(
      (0): Linear(in_features=4, out_features=8, bias=True)
      (1): ReLU()
      (2): Linear(in_features=8, out_features=4, bias=True)
      (3): ReLU()
    )
    (block1): Sequential(
      (0): Linear(in_features=4, out_features=8, bias=True)
      (1): ReLU()
      (2): Linear(in_features=8, out_features=4, bias=True)
      (3): ReLU()
    )
    (block2): Sequential(
      (0): Linear(in_features=4, out_features=8, bias=True)
      (1): ReLU()
      (2): Linear(in_features=8, out_features=4, bias=True)
      (3): ReLU()
    )
    (block3): Sequential(
      (0): Linear(in_features=4, out_features=8, bias=True)
      (1): ReLU()
      (2): Linear(in_features=8, out_features=4, bias=True)
      (3): ReLU()
    )
  )
  (1): Linear(in_features=4, out_features=1, bias=True)
)

按照以上结构,可以分层访问特定参数

python 复制代码
print(rgnet[0][1][0].bias.data)#表示第二个 block1 模块中的第一个子模块,即第一个线性层的偏置参数
#tensor([-0.1861, -0.0683,  0.3388, -0.4251, -0.3339,  0.2232, -0.1247,  0.0476])
参数初始化:
内置初始化
python 复制代码
def init_normal(m):
    if type(m) == nn.Linear:#检查传入的模块 m 的类型是否为全连接层
        nn.init.normal_(m.weight,mean=0,std=0.01)#权重参数初始化为均值为0,标准差为0.01的正态分布
        #或者初始化为1
        #nn.init.constant_(m.weight,1)
        nn.init.zeros_(m.bias)#偏置参数初始化为0

net.apply(init_normal)#递归地遍历 net 的每个子模块,对于每个子模块,调用 init_normal 函数。

同样可以对不同的神经网络层使用不同的初始化方法:

net[0].apply()

net[2].apply()

自定义初始化
python 复制代码
def my_init(m):
    if type(m) == nn.Linear:
        print("Init",
              *[(name,param.shape) for name,param in m.named_parameters()][0])
        nn.init.uniform_(m.weight,-10,10)#将该层的权重参数初始化为均匀分布,范围为 [-10, 10]
        m.weight.data *= m.weight.data.abs()>=5#将权重参数中绝对值小于 5 的元素设置为 0
net.apply(my_init)#将 my_init 函数应用于网络 net 的每个子模块
net[0].weight[:2]#[:2] 表示对权重参数进行切片操作,选取前两行
可以直接设置参数
python 复制代码
net[0].weight.data[:]+=1
net[0].weight.data[0,0] = 42
net[0].weight.data[0]
参数绑定:

多个层间共享参数

python 复制代码
shared = nn.Linear(8,8)
net = nn.Sequential(nn.Linear(4,8),nn.ReLU(),
                    shared,nn.ReLU(),
                    shared,nn.ReLU(),
                    nn.Linear())
#第二个层和第四个层是相同的

3.自定义层

和自定义块相似,只需要继承层类并实现前向传播功能

不带参数的层:
python 复制代码
import torch
from torch import nn
from torch.nn import functional as F

X=torch.FloatTensor([1,2,3,4,5])
class CenteredLayer(nn.Module):
    def __init__(self):
        super().__init__()

    def forward(self,X):
        return X-X.mean()#输入减均值,将均值变为0

layer = CenteredLayer()
a=layer(X)
print(a)
#tensor([-2., -1.,  0.,  1.,  2.])
python 复制代码
#将自定义层作为组件合并到网络中
net = nn.Sequential(nn.Linear(8,128),CenteredLayer())
带参数的层:
python 复制代码
class MyLinear(nn.Module):
    def __init__(self,in_units,units):#in_units是输入,units是输出
        super().__init__()
        self.weight = nn.Parameter(torch.randn(in_units,units))
        # torch.randn 函数生成一个服从标准正态分布的随机张量,形状为(in_units, units)
        self.bias = nn.Parameter(torch.randn(units,))
        #生成一个服从标准正态分布的随机向量,形状为(units,)

    def forward(self,X):
        linear = torch.matmul(X,self.weight.data)+self.bias.data
        #使用 torch.matmul对输入 X 和权重参数 self.weight.data 进行矩阵乘法运算
        #.data 属性访问权重参数的原始数据
        return F.relu(linear)#将线性输出传递给 ReLU 激活函数 F.relu

dense = MyLinear(5,3)
print(dense.weight)
'''tensor([[ 0.1203,  1.2635, -0.7978],
        [-1.4768,  1.0113, -0.8263],
        [-0.1474,  0.9414, -1.6847],
        [-1.4617,  0.7734, -1.3046],
        [-0.7199, -0.7151,  0.8831]], requires_grad=True)'''

4.读写文件

加载和保存张量:

对于单个张量,直接调用load和save函数分别读写

x=torch.arange(4)

torch.save(x,'x-file')

torch.load('x-file')

可以存储一个张量列表,把它们读回内存

python 复制代码
x=torch.arange(4)
y= torch.zeros(4)
torch.save([x,y],'x-files')
x2,y2=torch.load('x-files')
print(x2,y2)

#tensor([0, 1, 2, 3]) tensor([0., 0., 0., 0.])

将x,y存入字典,写入读取

python 复制代码
x=torch.arange(4)
y= torch.zeros(4)
mydict={'x':x,'y':y}

torch.save(mydict,'mydict')
mydict2=torch.load('mydict')
print(mydict2)
#{'x': tensor([0, 1, 2, 3]), 'y': tensor([0., 0., 0., 0.])}
加载和保存模型参数:
python 复制代码
torch.save(net.state_dict(),'mil.params')
#为了恢复模型,先实例化原始模型,直接读取文件中存储的模型参数
clone=MLP()
clone.load_state_dict(torch.load('mlp.params'))
#开启评估模式clone.eval()
相关推荐
西岸行者6 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
悠哉悠哉愿意6 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码6 天前
嵌入式学习路线
学习
毛小茛6 天前
计算机系统概论——校验码
学习
babe小鑫6 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
winfreedoms6 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下6 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。6 天前
2026.2.25监控学习
学习
im_AMBER6 天前
Leetcode 127 删除有序数组中的重复项 | 删除有序数组中的重复项 II
数据结构·学习·算法·leetcode
CodeJourney_J6 天前
从“Hello World“ 开始 C++
c语言·c++·学习