Pytorch学习笔记(三)Pytorch的常用操作记录

文章目录

    • 查看Pytorch网络的各层输出(feature map)、权重(weight)、偏置(bias)
      • weight and bias
      • feature map
    • 计算模型参数数量
    • 自定义Operation(Function)
      • 示例1:利用`class torch.autograd.Function`自定义`ReLU`激活函数
      • 示例2:利用`class torch.autograd.Function`自定义`OHEMHingeLoss`损失函数

查看Pytorch网络的各层输出(feature map)、权重(weight)、偏置(bias)

在使用Pytorch的时候,我总是在想我怎么看每一层的输出、权重、偏置,下面记录我自己的方法,应该是方式多样,但是我现在的水平没办法评价好坏,如果有更好的办法欢迎留言告知!!!

weight and bias

# Method 1 查看Parameters的方式多样化,直接访问即可
model = alexnet(pretrained=True).to(device)
conv1_weight = model.features[0].weight
# Method 2 
# 这种方式还适合你想自己参考一个预训练模型写一个网络,各层的参数不变,但网络结构上表述有所不同
# 这样你就可以把param迭代出来,赋给你的网络对应层,避免直接load不能匹配的问题!
for layer,param in model.state_dict().items(): # param is weight or bias(Tensor) 
	print layer,param

feature map

由于pytorch是动态网络,不存储计算数据,查看各层输出的特征图并不是很方便!分下面两种情况讨论:

  1. 你想查看的层是独立的,那么你在forward时用变量接收并返回即可!!!
class Net(nn.Module):
    def __init__(self):
        self.conv1 = nn.Conv2d(1, 1, 3)
        self.conv2 = nn.Conv2d(1, 1, 3)
        self.conv3 = nn.Conv2d(1, 1, 3)

    def forward(self, x):
        out1 = F.relu(self.conv1(x))
        out2 = F.relu(self.conv2(out1))
        out3 = F.relu(self.conv3(out2))
        return out1, out2, out3

  1. 你的想看的层在nn.Sequential()顺序容器中,这个麻烦些,主要有以下几种思路:
# Method 1 巧用nn.Module.children()
# 在模型实例化之后,利用nn.Module.children()删除你查看的那层的后面层
import torch
import torch.nn as nn
from torchvision import models

model = models.alexnet(pretrained=True)

# remove last fully-connected layer
new_classifier = nn.Sequential(*list(model.classifier.children())[:-1])
model.classifier = new_classifier
# Third convolutional layer
new_features = nn.Sequential(*list(model.features.children())[:5])
model.features = new_features
# Method 2 巧用hook,推荐使用这种方式,不用改变原有模型
# torch.nn.Module.register_forward_hook(hook)
# hook(module, input, output) -> None

model = models.alexnet(pretrained=True)
# 定义
def hook (module,input,output):
    print output.size()
# 注册
handle = model.features[0].register_forward_hook(hook)
# 删除句柄
handle.remove()

# torch.nn.Module.register_backward_hook(hook)
# hook(module, grad_input, grad_output) -> Tensor or None
model = alexnet(pretrained=True).to(device)
outputs = []
def hook (module,input,output):
    outputs.append(output)
    print len(outputs)

handle = model.features[0].register_backward_hook(hook)

注:还可以通过定义一个提取特征的类,甚至是重构成各层独立相同模型将问题转化成第一种,最后附上一个参考链接: How to extract features of an image from a trained model - PyTorch Forums

计算模型参数数量

之前发现Keras有个API为model.count_params(),能够返回模型参数的数量。
查了一下Pytorch没有这种API,刚好看见论坛有类似帖子How do I check the number of parameters of a model? - PyTorch Forums,不过于苛求精确度应该没问题。

def count_parameters(model):
    return sum(p.numel() for p in model.parameters() if p.requires_grad)

自定义Operation(Function)

class torch.autograd.Function能为微分操作定义公式并记录操作历史,在Tensor上执行的每个操作都会创建一个新的函数对象,它执行计算,并记录它发生的事件。历史记录以函数的DAG形式保留,边表示数据依赖关系(输入< - 输出)。 然后,当backward被调用时,通过调用每个Function对象的backward()方法并将返回的梯度传递给下一个Function,以拓扑顺序处理图。
一般来说,用户与函数交互的唯一方法是通过创建子类并定义新的操作。这是拓展torch.autograd的推荐方法。
创建子类的注意事项

  1. 子类必须重写forward()backward()方法,且为静态方法,定义时需加@staticmethod装饰器。
  2. forward()必须接受一个contextctx作为第一个参数,context可用于存储可在反向传播期间检索的张量。后面可接任意个数的参数(张量或者其他类型)。
  3. backward()必须接受一个contextctx作为第一个参数,context可用于检索前向传播期间保存的张量。 其参数是forward()给定输出的梯度,数量与forward()返回值个数一致。其返回值是forward()对应输入的梯度,数量与forward()的输入个数一致。
  4. 使用class_name.apply(arg)的方式即可调用该操作
    详见: Automatic differentiation package - torch.autograd

示例1:利用class torch.autograd.Function自定义ReLU激活函数

详见官方手册:PyTorch: Defining New autograd Functions

示例2:利用class torch.autograd.Function自定义OHEMHingeLoss损失函数

# from the https://github.com/yjxiong/action-detection
class OHEMHingeLoss(torch.autograd.Function):
    """
    This class is the core implementation for the completeness loss in paper.
    It compute class-wise hinge loss and performs online hard negative mining (OHEM).
    """

    @staticmethod
    def forward(ctx, pred, labels, is_positive, ohem_ratio, group_size):
        n_sample = pred.size()[0]
        assert n_sample == len(labels), "mismatch between sample size and label size"
        losses = torch.zeros(n_sample)
        slopes = torch.zeros(n_sample)
        for i in range(n_sample):
            losses[i] = max(0, 1 - is_positive * pred[i, labels[i] - 1])
            slopes[i] = -is_positive if losses[i] != 0 else 0

        losses = losses.view(-1, group_size).contiguous()
        sorted_losses, indices = torch.sort(losses, dim=1, descending=True)
        keep_num = int(group_size * ohem_ratio)
        loss = torch.zeros(1).cuda()
        for i in range(losses.size(0)):
            loss += sorted_losses[i, :keep_num].sum()
        ctx.loss_ind = indices[:, :keep_num]
        ctx.labels = labels
        ctx.slopes = slopes
        ctx.shape = pred.size()
        ctx.group_size = group_size
        ctx.num_group = losses.size(0)
        return loss

    @staticmethod
    def backward(ctx, grad_output):
        labels = ctx.labels
        slopes = ctx.slopes

        grad_in = torch.zeros(ctx.shape)
        for group in range(ctx.num_group):
            for idx in ctx.loss_ind[group]:
                loc = idx + group * ctx.group_size
                grad_in[loc, labels[loc] - 1] = slopes[loc] * grad_output.data[0]
        return torch.autograd.Variable(grad_in.cuda()), None, None, None, None

你可能感兴趣的:(PyTorch)