2.7.2基于模块类的简单线性回归类

本文介绍了如何使用PyTorch构建一个简单的线性网络,并探讨了模型参数、状态切换、缓冲张量、子模块获取以及模型数据类型和位置的改变。通过`LinearModel`类展示了全连接网络的实现,强调了模型在训练和预测状态的区别,特别是对Dropout和BatchNorm层的影响。此外,还讨论了如何获取和操作模型的参数、子模块以及缓存张量。
摘要由CSDN通过智能技术生成

0.前言

简单线性网络其实就是全连接网络,比如一个输入形状为(5,),输出形状为(2,)的线性网络结构:
在这里插入图片描述

1.代码构建网络

使用pytorch实现这样一个线性网络,代码如下:

import torch
from torch import nn


class LinearModel(nn.Module):
    def __init__(self,input_features,out_features):
        super(LinearModel,self).__init__()
        self.weight=nn.Parameter(torch.randn(input_features,out_features))
        self.bias=nn.Parameter(torch.randn(out_features))
    def forward(self,x):
        # y=Wx+b
        return x.mm(self.weight)+self.bias
  • torch.randn:正态分布的随机数生成
  • nn.Parameter:将一个不可训练的类型Tensor转换成可以训练的类型parameter并将这个parameter绑定到这个module里面
  • x.mm:矩阵乘法

从结构图中看出,权重值w有5*2=10个,所以使用torch.randn(input_features,out_features)生成这样一个shape的权重tensor,通过Parameter将其转化为可优化的类型;偏置项可以看出有2个,同样的方法定义shape=(2,)的self.bias

2.网络的调用

# 实例化一个输入为(5,),输出为(2,)的线性网络
model=LinearModel(5,2)

# 同时生成4个输入
input=torc.randn(4,5)

out=model(input)
  • input:
    在这里插入图片描述
  • out
    在这里插入图片描述

3. 进阶

3.1 获取模型的参数

named:说出…的名称; 叫出…的名字; 准确陈述;
这个单词在本届使用挺多的,了解其含义有助于记忆API

  • model.named_parameters():该方法获取模型所有参数的名称和值
    在这里插入图片描述

  • model.parameters():该方法获取模型所有参数的
    在这里插入图片描述

3.2 转化模型的状态:train & eval

# 使用训练状态
model.train()

# 使用eval状态
model.eval()

# 实际上train()方法有默认的参数是(True)所以eval实际上也等价于
model.train(False)

在模型的使用过程中,比如dropout层和BN层有两种状态,即训练状态和预测状态。Pytorch模型在不同状态下的预测准确率会有差异。在训练时用训练状态,在预测时用预测状态,否则最后模型的预测准确率可能会降低,甚至会得到错误的结果。

BN层为什么会有影响?李宏毅中讲到,BN层是需要统计样本的均值和标准差这样的统计信息的,训练时我们batch_size大,样本多,所以此时的统计信息就是直接通过计算batch_size样本得来的;但是当我们测试时,一般只是输入1个样本,此时通过样本计算的统计信息没有任何意义,此时用的均值和方差是全量训练数据的均值和方差,这个可以通过移动平均法求得。。综上所述,BN层在不同状态时,统计信息的来源是不一样的,所以操作时必须设定模型的状态,否则BN层对模型的buff可能就变成了debuff。

Dropout层为什么会有影响?在训练时,使用dropout是为了减小网络过拟合的风险,为使用网络预测时,应该用整个训练好的模型,因此不需要dropout。所以train时,dropout层启用;test时,dropout停用。

3.3获取模型中缓存的张量:named_buffers()

模型中除了通过反向传播得到梯度来进行训练的参数外,还有一些参数不参与梯度传播,但是在训练中会得到更新,这种参数被称为缓存(Buffer)。比如BN层中的均值(Mean)和方差(Variance),这两值在训练时会根据样本计算得到,所以会更新,但不是通过梯度传播更新的。这样的值通过named_buffers()得到:

3.4 获取模型的子模块

一个模型可能是由多个子模型构建起来的,子模块又有子模块

  • named_children():该方法获取模型的子模块名称和子模块的生成器
  • children():该方法获取模型的子模块的生成器
  • named_modules():当模型的子模块还有子模块时,使用该函数来(递归地)得到相关信息

这里定义一个TestModel:

class TestModel(nn.Module):
    def __init__(self,input_channels):
        super(TestModel,self).__init__()
        self.block1=nn.Sequential(
                      nn.Conv2d(input_channels,8,3),
                      nn.ReLU(),
                      nn.BatchNorm2d(8))
        self.block2=nn.Sequential(
                      nn.Conv2d(8,16,3),
                      nn.ReLU())
    def forward(self,x):
        x=self.block1(x)
        x=self.block2(x)
        return x

# 实例化一个3通道输入的model
model=TestModel(3)

获取子模块的名称和生成器:
在这里插入图片描述
获取子模块的生成器:
在这里插入图片描述

3.5 使用apply方法递归地对子模块进行函数应用

如果需要对所有模块进行一个函数处理,可以使用apply方法,该方法通过传入一个函数(或者匿名函数lambda)来递归地应用该函数。传入的函数会自动以模块作为参数,在内部对模块进行修改

3.6 改变模块的数据类型和储存位置

  • 改变储存位置:
model.cuda()  # 将模型参数移动到GPU上

model.cpu()  # 将模型参数移动到CPU上
  • 改变数据类型
model.float()   # 将模型参数转为单精度浮点数
model.half()    # 将模型参数转为半精度浮点数
model.double()  # 将模型参数转为双精度浮点数
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我是一个对称矩阵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值