目录
[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学习笔记以及最新顶会文章解读
卖报的大地主
749

被折叠的 条评论
为什么被折叠?



