Tensor's Grad

何为梯度 grad

记录loss对参数的偏导,用于更新参数,模型训练的时候,默认为参数创建,可以使用tensor.requires_grad查询张量是否带梯度

1
2
3
4
5
6
7
8
9
10
11
12
import torch
tensor_demo = torch.tensor([[1,2,3],[4,5,6]])
# 返回False说明不带grad
tensor_demo.requires_grad

# 创建一个带有grad的张量
# 注意,int不能带有grad,故使用float32
tensor_demo = torch.tensor(
[[1, 2, 3], [4, 5, 6]], dtype=torch.float32, requires_grad=True
)
# 返回True说明这是一个带grad的张量
tensor_demo.requires_grad

grad有什么用

可以利用torch的自动求导特性,得到loss关于次参数的导数,并利用此导数进行参数更新

具体而言,模型的一个epoch有这么五步

如果模型的设计是,参数是每个step更新,那每个step都有这么几步

  1. 向前传播,forward,即使用模型,传入feature,得到模型输出pred

    1
    pred = model(feature)
  2. 计算loss,这里的loss_func为自己定义的函数,比如常见的L1 loss

    1
    loss = loss_func(pred, target)
  3. 向后传播,backward,即对模型各个参数计算对应的grad

    1
    loss.backward()
  4. 更新模型参数,根据自己定义的优化器optimizer策略,进行参数更新

    1
    optimizer.step() 
  5. 将模型所有参数的grad重置为0

    1
    optimizer.zero_grad()

清空单个张量的grad

当张量被初始化时,是没有梯度,是不用清空的,此时gradNone

1
2
3
4
import torch
tensor_demo = torch.tensor([[1,2,3],[4,5,6]],dtype=torch.float32).requires_grad_(True)
# 啥也不返回说明grad是None
tensor_demo.grad

一旦基于此tensor的计算的值进行了backward,也就是依据计算图计算了各个参数的grad,此tensor附带的grad就不是None

1
2
3
4
5
6
7
8
import torch
tensor_demo = torch.tensor([[1,2,3],[4,5,6]],dtype=torch.float32).requires_grad_(True)
# 基于tesor_demo计算得到res
res =(tensor_demo**2).sigmoid().sum()
# 计算grad
res.backward()
# 打印tensor的grad
tensor_demo.grad

梯度在每次backward是不会被覆盖的,而是累计,所以需要手动清空,这里的清空是指将梯度设置为0;需要注意的是如果gradNone,是不能被清空的

1
2
3
4
5
if tensor_demo.grad is not None:
print("grad is not None, will set grad to zeros")
tensor_demo.grad.zero_()
# 如果grad不是None,将被重置为0
tensor_demo.grad

grad 转为 feature importance

feature添加grad,进行backward后,基于grad计算feature importance

  1. 读取模型参数,使用模型预测

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    import torch

    model = DailyNet.load_from_checkpoint(
    Path("epoch04.ckpt"),
    )
    # 如果模型中有dropout等层,需要进行处理,否则后续操作有随机性
    model._gru_block._gru.dropout = 0.0

    # 可以将参数的梯度去掉, 并不影响featuer的grad计算
    # for param in model.parameters():
    # param.detach_()

    # NOTE 需要给feature添加grad
    input_feature = input_feature.requires_grad_(True)
    preds = model(input_feature).cpu()
  2. 计算loss,并backward

    loss_func为自定义的损失函数,target为真实值

    1
    2
    3
    4
    5
    6
    7
    8
    9
    if input_feature.requires_grad:
    if input_feature.grad is not None:
    input_feature.grad.zero_()
    else:
    raise KeyError("input_feature requires grad")

    loss = loss_func(preds, target)

    loss.backward()
  3. 根据grad计算feature importance

    1
    2
    grad = input_feature.grad
    grad.abs().sum(axis=(0, 1)) / grad.abs().sum()