【MetaLearning】有关Pytorch的元学习库higher的基本用法

【MetaLearning】有关Pytorch的元学习库higher的基本用法

1. 基本介绍

higher.innerloop_ctxhigher库的上下文管理器,用于创建内部循环(inner loop)的上下文,内部循环通常用于元学习场景,其中在模型参数更新的内部循环中进行一些额外的操作。

这个上下文管理器主要有五个参数:(详细请参考官方库说明

higher.innerloop_ctx(model, opt, device=None, copy_initial_weights=True, override=None, track_higher_grads=True)
  • 第一个参数model是需要进行内部循环的模型,通常是你的元模型
  • 第二个参数opt是优化器,这是你用来更新模型参数的优化器
  • 第三个参数copy_initial_weights是一个布尔值,用于指定是否在每个内部循环之前复制初始权重,如果设置为True则表示在每个内部循环之前都会将模型的初始权重进行复制,以确保每个内部循环都从相同的初始权重开始。如果设置为False,则所有的内部循环共享相同的权重模型。
  • 第四个参数override是一个字典,例如override={'lr':lr_tensor, "momentum': momentum_tensor},用于指定在内部循环期间覆盖优化器的参数,比如在这里示例中,lr_tensormomentum_tensor是张量,用于指定内部循环期间覆盖的学习率和动量。
  • 第五个参数track_higher_grads是一个布尔值,用于跟踪更高阶的梯度,如果是True,则内部循环中计算的梯度将被跟踪以支持高阶的梯度计算,如果设置为False,则不会跟踪高阶梯度。

with语句块中,通过(fmodel, diffopt)获取内部循环的上下文。fmodel表示内部循环中的模型,diffopt表示内部循环中的优化器,在这个上下文中,你可以执行内部循环的计算和参数更新。

下面给出一个基本的使用示例,演示如何使用higher.innerloop_ctx,使用higher库需要习惯下列的转变

从通常使用pytorch的用法

model = MyModel()
opt = torch.optim.Adam(model.parameters())

for xs, ys in data:
    opt.zero_grad()
    logits = model(xs)
    loss = loss_function(logits, ys)
    loss.backward()
    opt.step()

转变到

model = MyModel()
opt = torch.optim.Adam(model.parameters())

with higher.innerloop_ctx(model, opt) as (fmodel, diffopt):
    for xs, ys in data:
        logits = fmodel(xs)  # modified `params` can also be passed as a kwarg
        loss = loss_function(logits, ys)  # no need to call loss.backwards()
        diffopt.step(loss)  # note that `step` must take `loss` as an argument!,这一步相当于使用了loss.backward()和opt.step()

    # At the end of your inner loop you can obtain these e.g. ...
    grad_of_grads = torch.autograd.grad(
        meta_loss_fn(fmodel.parameters()), fmodel.parameters(time=0))

训练模型和执行diffopt.step 来更新fmodel之间的区别在于,fmodel不会像原始部分中的opt.step()那样就地更新参数。 相反,每次调用 diffopt.step时都会以这样的方式创建新版本的参数,即fmodel将在下一步中使用新的参数,但所有以前的参数仍会保留。

运行的原理是什么呢?举个例子,fmodelfmodel.parameters(time=0)开始迭代(这里的time=0表示就是第0次迭代),当我们调用diffopt.stepN次之后,我们可以使用fmodel.parameters(time=i)来访问,其中i可以取到1N,并且我们仍然可以访问fmodel.parameters(time=0),这个结果和迭代之前是一样的,这是为什么呢?

因为fmodel的创建依赖于参数copy_initial_weights,如果copy_initial_weights=True,那么fmodel.parameters(time=0)是从原模型clone’d别且是detach’ed(即是从原模型克隆过来并且进行分离计算图了),如果copy_initial_weights=False,那么只是进行了clone’d并没有detach‘ed。

放一段原文在这里方便大家理解

I.e. fmodel starts with only fmodel.parameters(time=0) available, but after you called diffopt.step N times you can ask fmodel to give you fmodel.parameters(time=i) for any i up to N inclusive. Notice that fmodel.parameters(time=0) doesn’t change in this process at all, just every time fmodel is applied to some input it will use the latest version of parameters it currently has.

Now, what exactly is fmodel.parameters(time=0)? It is created here and depends on copy_initial_weights. If copy_initial_weights==True then fmodel.parameters(time=0) are clone’d and detach’ed parameters of model. Otherwise they are only clone’d, but not detach’ed!

That means that when we do meta-optimization step, the original model’s parameters will actually accumulate gradients if and only if copy_initial_weights==False. And in MAML we want to optimize model’s starting weights so we actually do need to get gradients from meta-optimization step.

2. Toy Example

import torch
import torch.nn as nn
import torch.optim as optim
import higher
import numpy as np

np.random.seed(1)
torch.manual_seed(3)
N = 100
actual_multiplier = 3.5
meta_lr = 0.00001
loops = 5 # how many iterations in the inner loop we want to do

x = torch.tensor(np.random.random((N,1)), dtype=torch.float64) # features for inner training loop
y = x * actual_multiplier # target for inner training loop
model = nn.Linear(1, 1, bias=False).double() # simplest possible model - multiple input x by weight w without bias
meta_opt = optim.SGD(model.parameters(), lr=meta_lr, momentum=0.)


def run_inner_loop_once(model, verbose, copy_initial_weights):
    lr_tensor = torch.tensor([0.3], requires_grad=True)
    momentum_tensor = torch.tensor([0.5], requires_grad=True)
    opt = optim.SGD(model.parameters(), lr=0.3, momentum=0.5)
    with higher.innerloop_ctx(model, opt, copy_initial_weights=copy_initial_weights, override={'lr': lr_tensor, 'momentum': momentum_tensor}) as (fmodel, diffopt):
        for j in range(loops):
            if verbose:
                print('Starting inner loop step j=={0}'.format(j))
                print('    Representation of fmodel.parameters(time={0}): {1}'.format(j, str(list(fmodel.parameters(time=j)))))
                print('    Notice that fmodel.parameters() is same as fmodel.parameters(time={0}): {1}'.format(j, (list(fmodel.parameters())[0] is list(fmodel.parameters(time=j))[0])))
            out = fmodel(x)
            if verbose:
                print('    Notice how `out` is `x` multiplied by the latest version of weight: {0:.4} * {1:.4} == {2:.4}'.format(x[0,0].item(), list(fmodel.parameters())[0].item(), out[0].item()))
            loss = ((out - y)**2).mean()
            diffopt.step(loss)

        if verbose:
            # after all inner training let's see all steps' parameter tensors
            print()
            print("Let's print all intermediate parameters versions after inner loop is done:")
            for j in range(loops+1):
                print('    For j=={0} parameter is: {1}'.format(j, str(list(fmodel.parameters(time=j)))))
            print()

        # let's imagine now that our meta-learning optimization is trying to check how far we got in the end from the actual_multiplier
        weight_learned_after_full_inner_loop = list(fmodel.parameters())[0]
        meta_loss = (weight_learned_after_full_inner_loop - actual_multiplier)**2
        print('  Final meta-loss: {0}'.format(meta_loss.item()))
        meta_loss.backward() # will only propagate gradient to original model parameter's `grad` if copy_initial_weight=False
        if verbose:
            print('  Gradient of final loss we got for lr and momentum: {0} and {1}'.format(lr_tensor.grad, momentum_tensor.grad))
            print('  If you change number of iterations "loops" to much larger number final loss will be stable and the values above will be smaller')
        return meta_loss.item()

print('=================== Run Inner Loop First Time (copy_initial_weights=True) =================\n')
meta_loss_val1 = run_inner_loop_once(model, verbose=True, copy_initial_weights=True)
print("\nLet's see if we got any gradient for initial model parameters: {0}\n".format(list(model.parameters())[0].grad))

print('=================== Run Inner Loop Second Time (copy_initial_weights=False) =================\n')
meta_loss_val2 = run_inner_loop_once(model, verbose=False, copy_initial_weights=False)
print("\nLet's see if we got any gradient for initial model parameters: {0}\n".format(list(model.parameters())[0].grad))

print('=================== Run Inner Loop Third Time (copy_initial_weights=False) =================\n')
final_meta_gradient = list(model.parameters())[0].grad.item()
# Now let's double-check `higher` library is actually doing what it promised to do, not just giving us
# a bunch of hand-wavy statements and difficult to read code.
# We will do a simple SGD step using meta_opt changing initial weight for the training and see how meta loss changed
meta_opt.step()
meta_opt.zero_grad()
meta_step = - meta_lr * final_meta_gradient # how much meta_opt actually shifted inital weight value
# before we run inner loop third time, we update the meta parameter firstly.
meta_loss_val3 = run_inner_loop_once(model, verbose=False, copy_initial_weights=False)

meta_loss_gradient_approximation = (meta_loss_val3 - meta_loss_val2) / meta_step

print()
print('Side-by-side meta_loss_gradient_approximation and gradient computed by `higher` lib: {0:.4} VS {1:.4}'.format(meta_loss_gradient_approximation, final_meta_gradient))

结果如下

=================== Run Inner Loop First Time (copy_initial_weights=True) =================

Starting inner loop step j==0
    Representation of fmodel.parameters(time=0): [tensor([[-0.9915]], dtype=torch.float64, requires_grad=True)]
    Notice that fmodel.parameters() is same as fmodel.parameters(time=0): True
    Notice how `out` is `x` multiplied by the latest version of weight: 0.417 * -0.9915 == -0.4135
Starting inner loop step j==1
    Representation of fmodel.parameters(time=1): [tensor([[-0.1217]], dtype=torch.float64, grad_fn=<AddBackward0>)]
    Notice that fmodel.parameters() is same as fmodel.parameters(time=1): True
    Notice how `out` is `x` multiplied by the latest version of weight: 0.417 * -0.1217 == -0.05075
Starting inner loop step j==2
    Representation of fmodel.parameters(time=2): [tensor([[1.0145]], dtype=torch.float64, grad_fn=<AddBackward0>)]
    Notice that fmodel.parameters() is same as fmodel.parameters(time=2): True
    Notice how `out` is `x` multiplied by the latest version of weight: 0.417 * 1.015 == 0.4231
Starting inner loop step j==3
    Representation of fmodel.parameters(time=3): [tensor([[2.0640]], dtype=torch.float64, grad_fn=<AddBackward0>)]
    Notice that fmodel.parameters() is same as fmodel.parameters(time=3): True
    Notice how `out` is `x` multiplied by the latest version of weight: 0.417 * 2.064 == 0.8607
Starting inner loop step j==4
    Representation of fmodel.parameters(time=4): [tensor([[2.8668]], dtype=torch.float64, grad_fn=<AddBackward0>)]
    Notice that fmodel.parameters() is same as fmodel.parameters(time=4): True
    Notice how `out` is `x` multiplied by the latest version of weight: 0.417 * 2.867 == 1.196

Let's print all intermediate parameters versions after inner loop is done:
    For j==0 parameter is: [tensor([[-0.9915]], dtype=torch.float64, requires_grad=True)]
    For j==1 parameter is: [tensor([[-0.1217]], dtype=torch.float64, grad_fn=<AddBackward0>)]
    For j==2 parameter is: [tensor([[1.0145]], dtype=torch.float64, grad_fn=<AddBackward0>)]
    For j==3 parameter is: [tensor([[2.0640]], dtype=torch.float64, grad_fn=<AddBackward0>)]
    For j==4 parameter is: [tensor([[2.8668]], dtype=torch.float64, grad_fn=<AddBackward0>)]
    For j==5 parameter is: [tensor([[3.3908]], dtype=torch.float64, grad_fn=<AddBackward0>)]

  Final meta-loss: 0.011927987982895929
  Gradient of final loss we got for lr and momentum: tensor([-1.6295]) and tensor([-0.9496])
  If you change number of iterations "loops" to much larger number final loss will be stable and the values above will be smaller

Let's see if we got any gradient for initial model parameters: None

=================== Run Inner Loop Second Time (copy_initial_weights=False) =================

  Final meta-loss: 0.011927987982895929

Let's see if we got any gradient for initial model parameters: tensor([[-0.0053]], dtype=torch.float64)

=================== Run Inner Loop Third Time (copy_initial_weights=False) =================

  Final meta-loss: 0.01192798770078706

Side-by-side meta_loss_gradient_approximation and gradient computed by `higher` lib: -0.005311 VS -0.005311

Reference

Parper: Generalized Inner Loop Meta-Learning
What does the copy_initial_weights documentation mean in the higher library for Pytorch?

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
### 回答1: 深度学习是一项非常热门的技术,在人工智能领域得到广泛应用。PyTorch是一种使用Python编程语言的开源深度学习框架,它非常适合研究和开发深度学习模型。为了帮助初学者更好地学习PyTorch深度学习技术,CSDN(全球最大中文IT社区)开设了“DeepLearning with PyTorch”系列课程。 这个系列课程以实践为主要教学方式,让学生在实际操作中掌握PyTorch深度学习的技能。在学习过程中,学生可以学到基础的模型结构设计,各种优化算法,如学习率调整、梯度下降等,并且可以在实战操作中学到如何使用PyTorch完成各种实际应用,例如图像分类和识别,自然语言处理等等。 这门课程的受众群体不仅仅是那些想要从事人工智能开发的工程师,它对于对深度学习感兴趣的学生和科研人员也是非常有用的。这是因为在这个课程中,教师基于实际使用场景和数据集介绍了PyTorch深度学习技术,从实践中总结出的方法和经验不仅可以快速提升工程开发效率,也可以加深对深度学习理论的理解。 总之,“DeepLearning with PyTorch”系列课程非常实用和有趣,可以为初学者提供全面而深入的深度学习知识,帮助他们掌握用PyTorch来开发深度学习模型的基础技能。 ### 回答2: Deep Learning是一种用于训练多层神经网络的机器学习方法,已被广泛应用于视觉、语音、自然语言处理等领域。而PyTorch是一种开源的深度学习框架,具有快速、灵活、易用等优点,因此受到了越来越多的关注和使用。 CSDN是一个致力于IT技术在线学习和分享的平台,在其中学习deeplearning with pytorch将能够获取丰富的知识和实践经验。首先,我们需要了解PyTorch基本概念和操作方法,如如何构建网络模型、定义损失函数和优化器、进行前向传播和反向传播等。然后,我们可以学习如何使用PyTorch进行数据预处理,如数据清洗、标准化、归一化等。此外,还可了解如何使用PyTorch进行分布式训练、混合精度训练等高级技术,以及如何在GPU上进行训练和推理等实践技巧。 总之,在CSDN上学习deeplearning with pytorch,能够让我们更好地掌握PyTorch的使用技巧,帮助我们更快、更好地完成深度学习的应用开发和研究工作。同时也可以通过活跃在CSDN平台上与其他开发者的交流来共同进步。 ### 回答3: PyTorch是一种针对深度学习任务的开源机器学习,它支持快速的原型设计和大量的实验,是当前科学界和工业界中最受欢迎的深度学习框架之一。CSDN推出的Deeplearning with Pytorch系列课程就是致力于教授学生如何使用PyTorch进行深度学习,以及在此基础上更深层次的研究探索。 此系列课程包含了从入门到进阶多个方面的内容,在基础课程中,学员将学会如何使用PyTorch进行深度学习的各个方面,包括但不限于神经网络、优化器、损失函数等,使其基本掌握PyTorch的使用方法。而在进阶课程中,以一些大型深度学习任务为基础,详细介绍了超参数优化、神经网络模型架构选择、分布式训练、自己写网络模型等更高级的知识,通过深度剖析一些开源的源码,为学员提供了很多实现深度学习任务的技巧和方法。 课程的开设不仅帮助了很多想更深入了解深度学习的爱好者,也有助于那些打算将深度学习应用在自己的科研工作中的研究者们更加快捷、有效地完成自己的研究任务。相信随着人工智能的不断发展,PyTorch这样的框架将会发挥越来越重要的作用,而帮助大家掌握这些工具的Deeplearning with Pytorch系列课程也必将得到更多的关注和支持。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木心

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值