我正在尝试优化使用 PyTorch 库实现的简单模型的参数。出于优化的目的,我想使用与模型类指定的参数不同的表示形式。我特别想将我的参数表示为单个向量(而不是像本例中那样的两个向量)。
我可以使用
model.parameters()
中的 Iterable
将 parameters_to_vector
(这是一个 torch.nn.utils.convert_parameters
)转换为所需的向量表示。但是,当我尝试将此向量标记为叶子(使用 detach
和 requires_grad_
),并使用它用 vector_to_parameters
填充模型对象的原始参数时,看起来计算图并未生成知道正在发生什么。
#!/usr/bin/python3
import torch
import torch.nn as nn
from torch.nn.utils.convert_parameters import *
class SimpleModel(nn.Module):
def __init__(self):
super(SimpleModel, self).__init__()
self.linear = nn.Linear(10, 1)
def forward(self, x):
return self.linear(x)
model = SimpleModel()
for name, param in model.named_parameters():
print(name, param.size())
# this prints:
## linear.weight torch.Size([1, 10])
## linear.bias torch.Size([1])
loss_function = nn.MSELoss()
vparams = parameters_to_vector(model.parameters()).detach().clone().requires_grad_(True)
# populate model.parameters() from vparams
vector_to_parameters(vparams, model.parameters())
input_data = torch.randn(1, 10)
output = model(input_data)
target = torch.randn(1, 1)
loss = loss_function(output, target)
# loss.backward() ## if we do this, then vparams.grad is None
## this one works, but we wanted to use vparams:
# vgrads = torch.autograd.grad(loss, model.linear.weight)[0]
## this gives an error:
vgrads = torch.autograd.grad(loss, vparams)[0]
## "One of the differentiated Tensors appears to not have been used in the graph."
我还尝试手动执行矢量切片,但这并不能解决错误。例如:
model.linear.weight.data.copy_(vparams[0:10].view_as(model.linear.weight.data))
或
model.linear.weight = nn.Parameter(vparams[0:10].view_as(model.linear.weight.data))
我对 PyTorch 有点陌生,但我读过 PyTorch 可以通过切片计算梯度,所以看来我正在尝试的应该是可能的。
我是否遗漏了 PyTorch 模型使用的
torch.nn.Parameter
类的某些内容?该类的成员是否需要成为计算图中的“叶子”?这是一个较小的示例,省略了 nn.Module
子类,只是尝试从“切片”创建 nn.Parameter
对象:
import torch
import torch.nn as nn
a = torch.tensor([1.0, 2.0, 3.0], requires_grad=True)
#b = nn.Parameter(a[1]) # "RuntimeError: One of the differentiated Tensors appears to not have been used in the graph."
#b = torch.Tensor(a[1]) # "IndexError: slice() cannot be applied to a 0-dim tensor."
b = a[1] # works
g = torch.autograd.grad(b, a)[0]
从错误消息来看,PyTorch 似乎无法通过
nn.Parameter
初始化进行区分。有办法解决这个问题吗?
提前致谢。
我认为您误解了
vector_to_parameters
的工作原理。看源码:
def vector_to_parameters(vec: torch.Tensor, parameters: Iterable[torch.Tensor]) -> None:
r"""Convert one vector to the parameters
Args:
vec (Tensor): a single vector represents the parameters of a model.
parameters (Iterable[Tensor]): an iterator of Tensors that are the
parameters of a model.
"""
# Ensure vec of type Tensor
if not isinstance(vec, torch.Tensor):
raise TypeError('expected torch.Tensor, but got: {}'
.format(torch.typename(vec)))
# Flag for the device where the parameter is located
param_device = None
# Pointer for slicing the vector for each parameter
pointer = 0
for param in parameters:
# Ensure the parameters are located in the same device
param_device = _check_param_device(param, param_device)
# The length of the parameter
num_param = param.numel()
# Slice the vector, reshape it, and replace the old data of the parameter
param.data = vec[pointer:pointer + num_param].view_as(param).data
# Increment the pointer
pointer += num_param
vector_to_parameters
将 vec
中的值分配给 parameters
中的参数。这会更新 data
中的 parameters
值,但 vec
和更新后的参数之间没有自动梯度链接。
当您进行前向传递时,您使用模型状态字典中的参数,并且梯度流回这些参数。
例如,当您运行以下代码时,您会得到在
model.linear.weight.grad
处填充的渐变,因为这是计算的叶参数。 vparams
不参与计算,因此没有返回到 vparams
的梯度链。
vector_to_parameters(vparams, model.parameters())
input_data = torch.randn(1, 10)
output = model(input_data)
target = torch.randn(1, 1)
loss = loss_function(output, target)
loss.backward()
print(model.linear.weight.grad)