#copy #pytorch #tensor
#python #pytorch #Копировать #тензор
Вопрос:
Кажется, существует несколько способов создать копию тензора в PyTorch, в том числе
y = tensor.new_tensor(x) #a
y = x.clone().detach() #b
y = torch.empty_like(x).copy_(x) #c
y = torch.tensor(x) #d
b
явно предпочтительнее, чем a
и d
в соответствии с предупреждением пользователя, которое я получаю, если выполняю либо a
, либо d
. Почему это предпочтительнее? Производительность? Я бы сказал, что это менее читабельно.
Какие-либо причины за / против использования c
?
Комментарии:
1. одним из преимуществ
b
является то, что он делает явным тот факт, чтоy
больше не является частью вычислительного графика, т. Е. не требует градиента.c
отличается от всех трех тем, чтоy
по-прежнему требует grad.2. Как насчет
torch.empty_like(x).copy_(x).detach()
— это то же самое, чтоa/b/d
? Я признаю, что это не самый разумный способ сделать это, я просто пытаюсь понять, как работает autograd. Меня смущают документы дляclone()
, в которых говорится «В отличие от copy_(), эта функция записана в графе вычислений», что заставило меня подумать, чтоcopy_()
не потребует grad.3. В документах есть довольно четкое примечание:
When data is a tensor x, new_tensor() reads out ‘the data’ from whatever it is passed, and constructs a leaf variable. Therefore tensor.new_tensor(x) is equivalent to x.clone().detach() and tensor.new_tensor(x, requires_grad=True) is equivalent to x.clone().detach().requires_grad_(True). The equivalents using clone() and detach() are recommended.
4. Pytorch ‘1.1.0’ рекомендует #b сейчас и показывает предупреждение в #d
5. @ManojAcharya возможно, рассмотрите возможность добавления вашего комментария в качестве ответа здесь.
Ответ №1:
TL; DR
Используйте .clone().detach()
(или предпочтительно .detach().clone()
)
Если вы сначала отсоединяете тензор, а затем клонируете его, путь вычисления не копируется, наоборот, он копируется, а затем отбрасывается. Таким образом,
.detach().clone()
это немного более эффективно.— pytorch forums
поскольку он немного быстр и понятен в том, что он делает.
Используя perflot
, я построил график времени различных методов для копирования тензора pytorch.
y = tensor.new_tensor(x) # method a
y = x.clone().detach() # method b
y = torch.empty_like(x).copy_(x) # method c
y = torch.tensor(x) # method d
y = x.detach().clone() # method e
Ось x — это размерность созданного тензора, ось y показывает время. График представлен в линейном масштабе. Как вы можете ясно видеть, tensor()
или new_tensor()
занимает больше времени по сравнению с другими тремя методами.
Примечание: При нескольких запусках я заметил, что из b, c, e любой метод может иметь наименьшее время. То же самое верно для a и d. Но методы b, c, e последовательно имеют меньшее время, чем a и d.
import torch
import perfplot
perfplot.show(
setup=lambda n: torch.randn(n),
kernels=[
lambda a: a.new_tensor(a),
lambda a: a.clone().detach(),
lambda a: torch.empty_like(a).copy_(a),
lambda a: torch.tensor(a),
lambda a: a.detach().clone(),
],
labels=["new_tensor()", "clone().detach()", "empty_like().copy()", "tensor()", "detach().clone()"],
n_range=[2 ** k for k in range(15)],
xlabel="len(a)",
logx=False,
logy=False,
title='Timing comparison for copying a pytorch tensor',
)
Комментарии:
1. Глупый вопрос, но зачем нам нужен clone()? В противном случае оба тензора указывают на одни и те же необработанные данные?
2. Ах да, видите discuss.pytorch.org/t/clone-and-detach-in-v0-4-0/16861 /…
Ответ №2:
Согласно документации Pytorch #a и # b эквивалентны. В нем также говорится, что
Рекомендуются эквиваленты с использованием clone() и detach().
Итак, если вы хотите скопировать тензор и отделиться от графика вычислений, вы должны использовать
y = x.clone().detach()
Поскольку это самый чистый и читаемый способ. Во всех других версиях присутствует некоторая скрытая логика, и также не на 100% ясно, что происходит с графом вычислений и распространением градиента.
Что касается # c: это кажется немного сложным для того, что на самом деле делается, и также может привести к некоторым накладным расходам, но я не уверен в этом.
Редактировать: Поскольку в комментариях был задан вопрос, почему бы просто не использовать .clone()
.
В отличие от copy_(), эта функция записывается в графе вычислений. Градиенты, распространяющиеся на клонированный тензор, будут распространяться на исходный тензор.
Таким образом, .clone()
возвращая копию данных, он сохраняет график вычислений и записывает в нем операцию клонирования. Как упоминалось, это приведет к тому, что градиент, распространяющийся на клонированный тензор, также распространится на исходный тензор. Такое поведение может приводить к ошибкам и не является очевидным. Из-за этих возможных побочных эффектов тензор следует клонировать только через .clone()
, если такое поведение явно требуется. Чтобы избежать этих побочных эффектов, .detach()
добавлен для отключения графа вычислений от клонированного тензора.
Поскольку в общем случае для операции копирования требуется чистая копия, которая не может привести к непредвиденным побочным эффектам, предпочтительным способом копирования тензоров является .clone().detach()
.
Комментарии:
1. Почему
detach()
требуется?2. Из документации «В отличие от copy_(), эта функция записана в графе вычислений. Градиенты, распространяющиеся на клонированный тензор, будут распространяться на исходный тензор.». Итак, чтобы действительно скопировать тензор, вы хотите его отсоединить, иначе вы можете получить некоторые нежелательные обновления градиента, вы не знаете, откуда они берутся.
3. как насчет
.clone()
самого по себе?4. Я добавил текст, объясняющий, почему не клонировать само по себе. Надеюсь, это отвечает на вопрос.
Ответ №3:
Один из примеров проверки, скопирован ли тензор:
import torch
def samestorage(x,y):
if x.storage().data_ptr()==y.storage().data_ptr():
print("same storage")
else:
print("different storage")
a = torch.ones((1,2), requires_grad=True)
print(a)
b = a
c = a.data
d = a.detach()
e = a.data.clone()
f = a.clone()
g = a.detach().clone()
i = torch.empty_like(a).copy_(a)
j = torch.tensor(a) # UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).
print("a:",end='');samestorage(a,a)
print("b:",end='');samestorage(a,b)
print("c:",end='');samestorage(a,c)
print("d:",end='');samestorage(a,d)
print("e:",end='');samestorage(a,e)
print("f:",end='');samestorage(a,f)
print("g:",end='');samestorage(a,g)
print("i:",end='');samestorage(a,i)
Выход:
tensor([[1., 1.]], requires_grad=True)
a:same storage
b:same storage
c:same storage
d:same storage
e:different storage
f:different storage
g:different storage
i:different storage
j:different storage
Тензор копируется, если отображается другое хранилище.
PyTorch имеет почти 100 различных конструкторов, поэтому вы можете добавить еще много способов.
Если бы мне нужно было скопировать тензор, я бы просто использовал copy()
, это копирует также информацию, связанную с рекламой, поэтому, если мне нужно было бы удалить информацию, связанную с рекламой, я бы использовал:
y = x.clone().detach()
Ответ №4:
Pytorch ‘1.1.0’ рекомендует #b сейчас и показывает предупреждение для #d
Комментарии:
1. как насчет
.clone()
самого по себе?2. сам по себе clone также сохранит переменную, привязанную к исходному графику