[英]In-place operations with PyTorch
我想知道如何處理 PyTorch 中的就地操作。 我記得在 autograd 中使用就地操作一直存在問題。
實際上,我很驚訝下面的這段代碼可以工作,盡管我還沒有測試過它,但我相信這段代碼會在0.3.1
版中引發錯誤。
基本上我想做的是將張量向量的某個位置設置為某個值,例如:
my_tensor[i] = 42
工作示例代碼:
# test parameter a
a = torch.rand((2), requires_grad=True)
print('a ', a)
b = torch.rand(2)
# calculation
c = a + b
# performing in-place operation
c[0] = 0
print('c ', c)
s = torch.sum(c)
print('s ', s)
# calling backward()
s.backward()
# optimizer step
optim = torch.optim.Adam(params=[a], lr=0.5)
optim.step()
# changed parameter a
print('changed a', a)
輸出:
a tensor([0.2441, 0.2589], requires_grad=True)
c tensor([0.0000, 1.1511], grad_fn=<CopySlices>)
s tensor(1.1511, grad_fn=<SumBackward0>)
changed a tensor([ 0.2441, -0.2411], requires_grad=True)
所以很明顯在0.4.1
版本中。 這工作得很好,沒有警告或錯誤。
參考文檔中的這篇文章: autograd-mechanics
在 autograd 中支持就地操作是一件困難的事情,我們不鼓勵在大多數情況下使用它們。 Autograd 積極的緩沖區釋放和重用使其非常高效,並且就地操作實際上很少顯着降低內存使用量。 除非您在內存壓力很大的情況下運行,否則您可能永遠不需要使用它們。
但即使它有效,在大多數情況下也不鼓勵使用就地操作。
所以我的問題是:
就地操作的使用對性能有多大影響?
在我想將張量的一個元素設置為某個值的情況下,如何使用就地操作?
提前致謝!
我不確定就地操作對性能的影響有多大,但我可以解決第二個查詢。 您可以使用掩碼代替就地操作。
a = torch.rand((2), requires_grad=True)
print('a ', a)
b = torch.rand(2)
# calculation
c = a + b
# performing in-place operation
mask = np.zeros(2)
mask[1] =1
mask = torch.tensor(mask)
c = c*mask
...
這可能不是對您問題的直接回答,而只是為了提供信息。
就地操作適用於計算圖中的非葉張量。
葉張量是作為計算圖“末端”的張量。 正式(來自is_leaf
屬性在這里),
對於 requires_grad 為 True 的張量,如果它們是由用戶創建的,它們將是葉張量。 這意味着它們不是操作的結果,因此 grad_fn 為 None。
沒有錯誤的例子:
a = torch.tensor([3.,2.,7.], requires_grad=True)
print(a) # tensor([3., 2., 7.], requires_grad=True)
b = a**2
print(b) # tensor([ 9., 4., 49.], grad_fn=<PowBackward0>)
b[1] = 0
print(b) # tensor([ 9., 0., 49.], grad_fn=<CopySlices>)
c = torch.sum(2*b)
print(c) # tensor(116., grad_fn=<SumBackward0>)
c.backward()
print(a.grad) # tensor([12., 0., 28.])
另一方面,就地操作不適用於葉張量。
導致錯誤的示例:
a = torch.tensor([3.,2.,7.], requires_grad=True)
print(a) # tensor([3., 2., 7.], requires_grad=True)
a[1] = 0
print(a) # tensor([3., 0., 7.], grad_fn=<CopySlices>)
b = a**2
print(b) # tensor([ 9., 0., 49.], grad_fn=<PowBackward0>)
c = torch.sum(2*b)
print(c) # tensor(116., grad_fn=<SumBackward0>)
c.backward() # Error occurs at this line.
# RuntimeError: leaf variable has been moved into the graph interior
我想b[1]=0
操作,在上面的第一個例子中,並不是真正的就地操作。 我想它會創建一個帶有“CopySlices”操作的新張量。 就地操作之前的“舊 b”可能會保留在內部(只是它的名稱被“新 b”覆蓋)。 我在這里找到了一個不錯的人物。
舊 b ---(CopySlices)----> 新 b
另一方面,張量a
是葉張量。 在 CopySlices 操作a[1]=0
,它變成了一個中間張量。 為了避免反向傳播時葉張量和中間張量之間的這種復雜混合,禁止對葉張量進行 CopySlices 操作與向后共存。
以上僅為個人觀點,請以官方文檔為准。
筆記:
盡管就地操作適用於中間張量,但在執行一些就地操作時,盡可能多地使用克隆和分離是安全的,以明確創建獨立於計算圖的新張量。
對於您的第二個查詢,當您執行c[i] = i
或類似操作時,通常會調用__setitem__
。 為了就地進行該操作,您可以嘗試調用__setitem__
函數(如果這是執行c[i] = i
操作的原因。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.