pytroch中ctx和self的区别

welcome to my blog

阅读某个pytorch模型源代码时碰见的ctx参数, 查阅了资料大概总结一下
  1. ctx是context的缩写, 翻译成"上下文; 环境"
  2. ctx专门用在静态方法中
  3. self指的是实例对象; 而ctx用在静态方法中, 调用的时候不需要实例化对象, 直接通过类名就可以调用, 所以self在静态方法中没有意义
  4. 自定义的forward()方法和backward()方法的第一个参数必须是ctx; ctx可以保存forward()中的变量,以便在backward()中继续使用, 下一条是具体的示例
  5. ctx.save_for_backward(a, b)能够保存forward()静态方法中的张量, 从而可以在backward()静态方法中调用, 具体地, 下面地代码通过a, b = ctx.saved_tensors重新得到a和b
  6. ctx.needs_input_grad是一个元组, 元素是True或者False, 表示forward()中对应的输入是否需要求导, 比如ctx.needs_input_grad[0]指的是下面forwad()代码中indices是否需要求导
class SpecialSpmmFunction(torch.autograd.Function):
    """
    Special function for only sparse region backpropataion layer.
    """
    # 自定义前向传播过程
    @staticmethod
    def forward(ctx, indices, values, shape, b):
        assert indices.requires_grad == False
        a = torch.sparse_coo_tensor(indices, values, shape)
        ctx.save_for_backward(a, b)
        ctx.N = shape[0]
        return torch.matmul(a, b)
    # 自定义反向传播过程
    @staticmethod
    def backward(ctx, grad_output):
        a, b = ctx.saved_tensors
        grad_values = grad_b = None
        if ctx.needs_input_grad[1]:
            grad_a_dense = grad_output.matmul(b.t())
            edge_idx = a._indices()[0, :] * ctx.N + a._indices()[1, :]
            grad_values = grad_a_dense.view(-1)[edge_idx]

        if ctx.needs_input_grad[3]:
            grad_b = a.t().matmul(grad_output)
        return None, grad_values, None, grad_b
ctx还能调用很多方法, pytorch1.3.1源码中竟然说"no doc", 没有相关的文档…
class _FunctionBase(object):
    # no doc
    @classmethod
    def apply(cls, *args, **kwargs): # real signature unknown
        pass

    def register_hook(self, *args, **kwargs): # real signature unknown
        pass

    def _do_backward(self, *args, **kwargs): # real signature unknown
        pass

    def _do_forward(self, *args, **kwargs): # real signature unknown
        pass

    def _register_hook_dict(self, *args, **kwargs): # real signature unknown
        pass

    def __init__(self, *args, **kwargs): # real signature unknown
        pass

    @staticmethod # known case of __new__
    def __new__(*args, **kwargs): # real signature unknown
        """ Create and return a new object.  See help(type) for accurate signature. """
        pass

    dirty_tensors = property(lambda self: object(), lambda self, v: None, lambda self: None)  # default

    metadata = property(lambda self: object(), lambda self, v: None, lambda self: None)  # default

    needs_input_grad = property(lambda self: object(), lambda self, v: None, lambda self: None)  # default

    next_functions = property(lambda self: object(), lambda self, v: None, lambda self: None)  # default

    non_differentiable = property(lambda self: object(), lambda self, v: None, lambda self: None)  # default

    requires_grad = property(lambda self: object(), lambda self, v: None, lambda self: None)  # default

    saved_tensors = property(lambda self: object(), lambda self, v: None, lambda self: None)  # default

    saved_variables = property(lambda self: object(), lambda self, v: None, lambda self: None)  # default

    to_save = property(lambda self: object(), lambda self, v: None, lambda self: None)  # default
可以使用PyTorch中的DANN(Domain-Adversarial Neural Networks)模型实现CNN领域自适应结合进行样本迁移。DANN模型在训练过程中,通过引入一个领域分类器来判别输入数据的领域信息,再通过反向传播来最小化领域分类器的误差原始任务分类器的误差,从而实现领域自适应。 以下是一个简单的示例代码: ```python import torch import torch.nn as nn import torch.optim as optim import torch.nn.functional as F class CNN(nn.Module): def __init__(self): super(CNN, self).__init__() self.conv1 = nn.Conv2d(3, 64, kernel_size=5, padding=2) self.bn1 = nn.BatchNorm2d(64) self.conv2 = nn.Conv2d(64, 128, kernel_size=5, padding=2) self.bn2 = nn.BatchNorm2d(128) self.fc1 = nn.Linear(128*8*8, 256) self.fc2 = nn.Linear(256, 10) def forward(self, x): x = F.relu(self.bn1(self.conv1(x))) x = F.max_pool2d(x, 2) x = F.relu(self.bn2(self.conv2(x))) x = F.max_pool2d(x, 2) x = x.view(-1, 128*8*8) x = F.relu(self.fc1(x)) x = self.fc2(x) return x class DANN(nn.Module): def __init__(self, cnn): super(DANN, self).__init__() self.cnn = cnn self.domain_classifier = nn.Sequential( nn.Linear(128*8*8, 1024), nn.ReLU(), nn.Dropout(), nn.Linear(1024, 1024), nn.ReLU(), nn.Dropout(), nn.Linear(1024, 1) ) def forward(self, x, alpha): features = self.cnn(x) reverse_features = ReverseLayerF.apply(features, alpha) domain_output = self.domain_classifier(reverse_features) class_output = F.softmax(features, dim=1) return domain_output, class_output class ReverseLayerF(torch.autograd.Function): @staticmethod def forward(ctx, x, alpha): ctx.alpha = alpha return x @staticmethod def backward(ctx, grad_output): output = grad_output.neg() * ctx.alpha return output, None # 定义数据加载器 train_loader = torch.utils.data.DataLoader( torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=torchvision.transforms.Compose([ torchvision.transforms.RandomHorizontalFlip(), torchvision.transforms.RandomCrop(32, 4), torchvision.transforms.ToTensor(), torchvision.transforms.Normalize( (0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010)) ])), batch_size=128, shuffle=True, num_workers=2) test_loader = torch.utils.data.DataLoader( torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=torchvision.transforms.Compose([ torchvision.transforms.ToTensor(), torchvision.transforms.Normalize( (0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010)) ])), batch_size=128, shuffle=False, num_workers=2) # 定义模型、优化器损失函数 cnn = CNN() model = DANN(cnn) optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9) criterion = nn.CrossEntropyLoss() # 开始训练 for epoch in range(100): for i, (inputs, labels) in enumerate(train_loader): # 梯度清零 optimizer.zero_grad() # 计算领域标签,0表示源域,1表示目标域 domain_labels = torch.zeros(inputs.size(0)) domain_labels[inputs.size(0)//2:] = 1 # 计算领域自适应参数alpha alpha = 0.1 * epoch / 100 # 前向传播 domain_outputs, class_outputs = model(inputs, alpha) # 计算损失函数 class_loss = criterion(class_outputs, labels) domain_loss = criterion(domain_outputs.squeeze(), domain_labels) loss = class_loss + domain_loss # 反向传播优化 loss.backward() optimizer.step() if i % 100 == 0: print('Epoch [%d/%d], Iter [%d/%d], Class Loss: %.4f, Domain Loss: %.4f' % (epoch+1, 100, i+1, len(train_loader), class_loss.item(), domain_loss.item())) # 在测试集上测试准确率 correct = 0 total = 0 with torch.no_grad(): for inputs, labels in test_loader: _, outputs = model(inputs, 0) _, predicted = torch.max(outputs.data, 1) total += labels.size(0) correct += (predicted == labels).sum().item() print('Test Accuracy of the model on the test images: %.4f %%' % (100 * correct / total)) ``` 在这个示例代码中,我们首先定义了一个CNN模型,然后用DANN模型将其包装起来,形成一个领域自适应模型。在训练过程中,我们使用了PyTorch中的反向传播函数`torch.autograd.Function`来实现领域自适应参数alpha的自动求导。最后,在训练结束后,在测试集上测试了模型的准确率。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值