pytorch-torch.nn介绍

torch介绍

torch.nn

Parameters

Parameter继承自torch.tesor。其和tensor的区别如下:

  1. torch.tensorrequires_grad属性默认为False, 但是parametersrequires_grad属性默认为True
  2. Parameter()可以理解为类型转换函数。将一个不可训练类型的tensor转化为可以训练的parameter。当定义网络的时候,使用Parameter赋值给module的参数的时候,该参数会自动的被加入到module的参数列表中(会自动的出现在parameters()的迭代器中),如果使用torch.tesor来定义网络参数的时候(requires_grad=True可以将tensor变成可训练的),则不会自动加入
  3. parameter有两个参数:nn.Parameter(data=None, requires_grad=True)
    • data 输入tensor
    • requires_grad = True : 默认为True
  4. 参数访问一般是通过如下,torch.Mdule定义的两个方法和一个属性
    1. .parameters() : 返回一个包含模型所有参数的迭代器
    2. .named_parameters() :返回包含参数名称和参数的迭代器
    3. ._parameters : torch.Module属性,包含模型所有参数
import torch.nn.functional as F
import os
import numpy as np
import math
import random 
from torch.utils import data 

class LR1(nn.Module):
    def __init__(self, dim):
        super(LR1, self).__init__()
        self.weight = torch.randn(dim, requires_grad=True)
        self.bias = torch.randn(1, requires_grad=True)

    def forward(self, X):
        out = torch.matmul(X,self.weight) + self.bias
        return 1 / (torch.exp(out)+1)

class LR2(nn.Module):
    def __init__(self, dim):
        super(LR2, self).__init__()
        self.weight = torch.nn.Parameter(torch.randn(dim))
        self.bias = torch.nn.Parameter(torch.randn(1))

    def forward(self, X):
        out = torch.matmul(X,self.weight) + self.bias
        return 1 / (torch.exp(out)+1)

net1 = LR1(3)
net2 = LR2(3)
print(net1._parameters)  # OrderedDict()
rint(net2._parameters)   # 输出weight和bias

# 读取模型参数
for param in net1.parameters():  # 输出None
    print('net1: param: ',param)  
for param in net2.parameters(): 
    print('net2:param:', param)
# 输出
net2:param: Parameter containing:
tensor([-0.1286,  1.6369,  1.8213], requires_grad=True)
net2:param: Parameter containing:
tensor([-0.8231], requires_grad=True)

# 输出参数名字和具体参数
for name,param in net2.named_parameters():
    print('name={}'.format(name), param)
# 输出
name=weight Parameter containing:
tensor([-0.1286,  1.6369,  1.8213], requires_grad=True)
name=bias Parameter containing:
tensor([-0.8231], requires_grad=True)

Containers(容器)

nn.Module

任何一个layer、block、netword都是nn.Module的子类。该基类很多重要的方法和属性,分别如下所示。

1. modules相关方法和属性

属性

  • self._modules: 返回值结构:Dict[str, Optional['Module']]
    • 字典类型,作用:用来保存定义网络的children模块。所谓children模块,是直接基于nn.Modules定义的模块,只针对一阶派生,即儿子,不会对儿子再进行深度遍历

第一类方法

  • children(): 返回当前网络中,所有children模块的迭代器。只返回“儿子”一层
  • modules():返回所有模块的迭代器。将整个模型的所有构成(包含layer,自定义层,block)等由浅入深的依次遍历出来。
    • 作用:某一些行为需要获取模型的各个最小layer,比如初始化,模型加载参数等
  • named_modules(): 也是对网络中定义的模块进行深度优先遍历,返回所有模块。其和modules基本等价,不同的地方在于,其返回的是一个元祖,第一个是名称,第二个是对象。
  • get_submodule(target):返回特定的模块,注意target是子模块的名字,如果没有命名,那么就从‘0’开始

4个函数源码如下:

def children(self) -> Iterator['Module']:  # 
       """Return an iterator over immediate children modules."""
        for name, module in self.named_children():
            yield module
def named_children(self) -> Iterator[Tuple[str, 'Module']]: 
        memo = set()
        for name, module in self._modules.items():  # 从self._modules字典中获取layer和相对应的名字
            if module is not None and module not in memo:
                memo.add(module)
                yield name, module
def modules(self) -> Iterator['Module']:
         for _, module in self.named_modules(): # 调用的是named_modules()
            yield module
def named_modules(self, memo: Optional[Set['Module']] = None, prefix: str = '', remove_duplicate: bool = True):
        if memo is None:
            memo = set()
        if self not in memo:
            if remove_duplicate:
                memo.add(self)
            yield prefix, self
            for name, module in self._modules.items():
                if module is None:
                    continue
                submodule_prefix = prefix + ('.' if prefix else '') + name
                yield from module.named_modules(memo, submodule_prefix, remove_duplicate)   # 递归调用了 

案例1: self._modules

net = nn.Sequential(nn.Linear(2,2),nn.Linear(2,1))
print(net._modules)   # 1
print(net._modules['0']) # 2

# 1号输出
OrderedDict({'0': Linear(in_features=2, out_features=2, bias=True), '1': Linear(in_features=2, out_features=1, bias=True)})
# 2号输出
Linear(in_features=2, out_features=2, bias=True)

案例2. modulesnamed_modules的区别

print(net.get_submodule('1'))
# 输出:Linear(in_features=2, out_features=1, bias=True)

for idx,module in enumerate(net.modules()):
    print(idx,'-->',module)
# 输出
0 --> Sequential(
  (0): Linear(in_features=2, out_features=2, bias=True)
  (1): Linear(in_features=2, out_features=1, bias=True)
)
1 --> Linear(in_features=2, out_features=2, bias=True)
2 --> Linear(in_features=2, out_features=1, bias=True)

for idx,module in enumerate(net.children()):
    print(idx,'-->',module)
# 输出
Linear(in_features=2, out_features=2, bias=True)
0 --> Linear(in_features=2, out_features=2, bias=True)
1 --> Linear(in_features=2, out_features=1, bias=True)

第二类方法

  • add_module(name, module): 增加模块。由如下源码可以看出,和 register_module完全等价。
def add_module(self, name: str, module: Optional['Module']) -> None:
    ..... # 其他源码没啥用,删除
    self._modules[name] = module
def register_module(self, name: str, module: Optional['Module']) -> None:
        r"""Alias for :func:`add_module`."""
        self.add_module(name, module)

2. parameters相关方法和属性

属性

  • ._parameters 功能:输出模型的所有注册的参数,没有注册的是无法输出的
    • 其是一个OrderDict

方法

  • .parameters() 功能: 输出模型注册的所有参数
  • .get_parameter(target: str)功能:根据参数的名字,调用具体的参数
    • exprint(net2.get_parameter('bias'))
  • .named_parameters() 功能:输出参数名字和具体参数
  • .register_parameter(name, param) 功能:将参数添加到模型的参数属性中去(加入到._parameters)
    • 注意:参数param 必须是Parameter类型
def register_parameter(self, name: str, param: Optional[Parameter]) -> None:
            self._parameters[name] = param # 核心就下面一句话

案例:

class LR1(nn.Module):
    def __init__(self, dim):
        super(LR1, self).__init__()
        self.weight = torch.randn(dim, requires_grad=True)
        self.bias = torch.randn(1, requires_grad=True)

    def forward(self, X):
        out = torch.matmul(X,self.weight) + self.bias
        return 1 / (torch.exp(out)+1)

class LR2(nn.Module):
    def __init__(self, dim):
        super(LR2, self).__init__()
        self.weight = torch.nn.Parameter(torch.randn(dim))
        self.bias = torch.nn.Parameter(torch.randn(1))

    def forward(self, X):
        out = torch.matmul(X,self.weight) + self.bias
        return 1 / (torch.exp(out)+1)

net1 = LR1(3)
net2 = LR2(3)
print(net1._parameters)  # OrderedDict()
print(net2._parameters)   # 输出weight和bias


for param in net2.parameters(): 
    print('net2:param:', param)

print(net2.get_parameter('bias'))
# 输出
Parameter containing:
tensor([-1.2585], requires_grad=True)

print(type(net2._parameters))  #输出:<class 'collections.OrderedDict'>

案例:注册参数

class LR1(nn.Module):
    def __init__(self, dim):
        super(LR1, self).__init__()
        self.weight = torch.randn(dim, requires_grad=True)
        self.bias = torch.randn(1, requires_grad=True)
        for i,param in enumerate([self.weight, self.bias]):
            # self._parameters['{}'.format(i)] = param
            self.register_parameter(name='{}'.format(i), param=nn.Parameter(param)) 

    def forward(self, X):
        out = torch.matmul(X,self.weight) + self.bias
        return 1 / (torch.exp(out)+1)

print(net1._parameters)  
# 输出
OrderedDict({'0': Parameter containing:
tensor([ 0.5769, -0.5915,  1.6764], requires_grad=True), '1': Parameter containing:
tensor([1.1855], requires_grad=True)})

3. buffer模型缓存相关方法和属性

模型中的参数分成两类:

  • 一种是反向传播,需要被更新的,称之为parameter
  • 一种是反向传播,不需要被更新的,称之为buffer

如上两类变量,都会保存在模型的state_dict() 的返回值中。(其返回值是OrderDict

buffers模型缓存主要是将一些不训练的张量保存进state_dict中,如果普通张量没有使用set_buffers()接口进行注册,那么我们保存加载就不会有这个变量。

属性

  • .buffers() : 输出模型所有注册的buffer
    • 结果也是OrderDict类型

方法

  • get_buffer(target):获取特定的buffer对象,target是bffer的名字
  • named_buffers()::输出buffer的名字和具体数值
  • register_buffer(name, tensor, persistent=True) # 将一些buffer进行注册
class LR1(nn.Module):
    def __init__(self, dim):
        super(LR1, self).__init__()
        self.weight = nn.Parameter(torch.randn(dim, requires_grad=True))
        self.bias = torch.randn(1, requires_grad=True) # 其不会更新
        self.bias2 = torch.randn(1, requires_grad=True)
        self.register_buffer('b', self.bias)


    def forward(self, X):
        out = torch.matmul(X,self.weight) + self.bias
        return 1 / (torch.exp(out)+1)


net1 = LR1(3)
print(net1._parameters)  # OrderedDict({'weight': Parameter containing: tensor([-0.9199, -0.5809, -1.4698], requires_grad=True)})
print(net1._buffers)     # 输出 OrderedDict({'b': tensor([-1.7809], requires_grad=True)}
print(net1.state_dict()) # 输出不带self.bias2 因为其既没有加入到self._parameters中,又没有加入到self._buffer中
# OrderedDict({'weight': tensor([-0.9199, -0.5809, -1.4698]), 'b': tensor([0.2081])})

4. apply

函数体定义在基类torch.Module中,递归的调用slef.children()将函数f应用于网络的子模块。其和pandas中的apply作用类似。

功能:递归的将函数f应用到网络的各个子模块。常用于参数初始化

def init_weight(m):
    if type(m) == nn.Linear:
        m.weight.data.fill_(1.0)
        m.bias.data.fill_(0.0)
        
model = nn.Sequential(nn.Linear(2,2), nn.Linear(2,1))
model.apply(init_weight)
for param in model.parameters():
    print(param)
# 输出
Parameter containing:
tensor([[1., 1.],
        [1., 1.]], requires_grad=True)
Parameter containing:
tensor([0., 0.], requires_grad=True)
Parameter containing:
tensor([[1., 1.]], requires_grad=True)
Parameter containing:

eval()

forward

load_stat_dict(state_dict)

parameters()

返回一个包含模型所有参数的迭代器

register_buffer(name, tensor)

给模型添加一个persistent buffer

使用场景:在一些场景中,我们需要保存一些模型参数,但是这些参数不是模型的优化

属性变量:training

apply函数: 将所有子模块传给函数f进行处理。

https://www.cnblogs.com/dan-baishucaizi/p/16082456.html#21-module

https://www.cnblogs.com/ArdenWang/p/16104956.html

posted @ 2024-09-14 07:45  金字塔下的蜗牛  阅读(438)  评论(0)    收藏  举报