【Pytorch 学习笔记】一文快速了解nn.Module中的常见语句

[Pytorch] 深度学习模型中常见函数, 回调函数、hook

1.1 关于self:

self 代表类的实例,而非类。类的方法与普通的函数只有一个特别的区别——它们必须有一个额外的第一个参数名称, 按照惯例它的名称是 self。但是self并非关键字,用别的名称也可以。

class Test:
    def prt(self):
        print(self)
        print(self.__class__)

t = Test()
t.prt()

输出:

<__main__.Test instance at 0x100771878>
__main__.Test

从执行结果可以很明显的看出,self 代表的是类的实例,代表当前对象的地址,而 self.class 则指向类。

1.2 关于类变量和实例变量:

类变量定义在类中,但是在函数体之外.

class Dog:
    species = "Canis familiaris"  # 类变量
    def __init__(self, name):#__init__ 表示构造函数,实例化class时自动调用
        self.species = name 

dogs =Dog('cat')
print(dogs.species) # 'cat',实例变量可以和类变量重名,但是这样的话,实例化的对象无法调用类变量了
print(Dog.species) #"Canis familiaris"

1.3 关于深度学习模型中常见的模块(Module)写法

class DecoderBlock(nn.Module): #继承父类Module
    def __init__(self, key_size, query_size, value_size, num_hiddens,
                 norm_shape, ffn_num_input, ffn_num_hiddens, num_heads,
                 dropout, i, **kwargs):
        # **kwargs 表示将接收的所有关键字参数(key-value形式的参数)传递给父类的构造函数。
        super(DecoderBlock, self).__init__(**kwargs) #初始化父类中的构造函数,以此来保证子类DecoderBlock能够正常继承父类中的一些功能和属性

下面是一个简化版的例子,也讲述了**kwargs的使用

class Parent:
    def __init__(self, **kwargs):
        print("Parent initialized")
        self.parent_param = kwargs.get("parent_param", 0)#参数传入后便转为了字典,可以使用字典的key查询用法,key:"parent_param"若不存在,则返回0

class Child(Parent):
    def __init__(self, **kwargs):
        super(Child, self).__init__(**kwargs)  # 调用父类构造函数
        print("Child initialized")
child = Child(parent_param=100) ##parent_param=100作为参数(**kwargs)传入到父类中的构造函数中

1.4 关于Pytorch中的回调函数: def __call__(self, *input, **kwargs):

回调函数,一个非常特别的 Python 魔法方法。

当你给类的对象加上括号调用时(像调用函数一样),Python 会自动调用 __call__ 方法

举例:

import torch

# 定义一个 L1 类
class L1:
    def __init__(self):
        self.calc = torch.nn.L1Loss()
	# 回调函数中调用并返回了calc这一函数/方法
    def __call__(self, x, y):
        return self.calc(x, y)

# 创建对象
l1 = L1()

# 两个张量
pred = torch.tensor([2.5, 0.0, 2.1, 7.8])   # 预测值
target = torch.tensor([3.0, -0.5, 2.0, 7.5]) # 真实值

# 在调用class(L1)下的实例对象(l1)时,回调函数会被自动调用,并执行其函数体
loss = l1(pred, target)

print(loss)  # 输出: tensor(0.3250)

而常规的基于pytorch的DL模型中,都需要撰写forward函数体,在调用_,_=model(x,y)这一实例对象时, 会被自动执行forward中的函数体。

举一个沐神课程中MLP的例子:

# 定义 MLP 模型
class MLP(nn.Module):
    def __init__(self, input_dim, output_dim):
        super(MLP, self).__init__()
        self.fc1 = nn.Linear(input_dim, 128)
        self.relu = nn.ReLU()
        self.fc2 = nn.Linear(128, output_dim)

    def forward(self, x):
        x = self.fc1(x)
        x = self.relu(x)
        x = self.fc2(x)
        return x

forward其实是写在了nn.Module (父类)下的回调函数:

def __call__(self, *input, **kwargs):
    # 一些前处理(比如检查 hook)
    return self.forward(*input, **kwargs)

1.5 关于Pytorch中常见的接收参数格式:def __call__(self, *input, **kwargs):

前面1.3中详细举例了 **kwargs的用法

*input 代表 任意个位置参数,会被打包成一个 tuple

**kwargs 代表 任意个关键字参数,会被打包成一个 dict

举个例子:

def demo(*args, **kwargs):
    print("args:", args)
    print("kwargs:", kwargs)

demo(1, 2, 3, a=10, b=20)

输出:

args: (1, 2, 3)
kwargs: {'a': 10, 'b': 20}

1.6 Hook (钩子)

常见的 hook 有两种:

(1) Forward hook

forward 执行时调用。

  • 你可以拿到输入、输出。
  • 常用于调试,查看中间层的输出
import torch
import torch.nn as nn

layer = nn.Linear(2, 2)

# 定义 hook,forward_hook非关键词
def forward_hook(module, input, output):
    print("Inside hook:")
    print("input:", input)
    print("output:", output)

# 注册 hook
handle = layer.register_forward_hook(forward_hook)

x = torch.tensor([[1.0, 2.0]])

# layer这一对象中包含了`forward`回调函数:layer = nn.Linear(2, 2)
y = layer(x)

# 运行时会先算 forward,再自动调用 hook

#------------------------------------
# 结果
Inside hook:
input: (tensor([[1., 2.]]),)
output: tensor([[...]])

(2) Backward hook

反向传播时调用。

  • 你可以拿到梯度输入、梯度输出。
  • 常用于调试梯度是否消失或爆炸。
def backward_hook(module, grad_input, grad_output):
    print("Backward hook:")
    print("grad_input:", grad_input)
    print("grad_output:", grad_output)

layer.register_backward_hook(backward_hook)

x = torch.tensor([[1.0, 2.0]], requires_grad=True)
y = layer(x).sum()
y.backward()  # 会触发 backward hook

推荐一个博主,分享基础的pytorch学习笔记以及最新顶会文章解读
卖报的大地主

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值