簡體   English   中英

使用 PyTorch 進行就地操作

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM