最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

PyTorch中的拷貝與就地操作詳解

 更新時間:2020年12月09日 09:44:56   作者:Chris_34  
這篇文章主要給大家介紹了關(guān)于PyTorch中拷貝與就地操作的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

前言

PyTroch中我們經(jīng)常使用到Numpy進(jìn)行數(shù)據(jù)的處理,然后再轉(zhuǎn)為Tensor,但是關(guān)系到數(shù)據(jù)的更改時我們要注意方法是否是共享地址,這關(guān)系到整個網(wǎng)絡(luò)的更新。本篇就In-palce操作,拷貝操作中的注意點(diǎn)進(jìn)行總結(jié)。

In-place操作

pytorch中原地操作的后綴為_,如.add_()或.scatter_(),就地操作是直接更改給定Tensor的內(nèi)容而不進(jìn)行復(fù)制的操作,即不會為變量分配新的內(nèi)存。Python操作類似+=或*=也是就地操作。(我加了我自己~)

為什么in-place操作可以在處理高維數(shù)據(jù)時可以幫助減少內(nèi)存使用呢,下面使用一個例子進(jìn)行說明,定義以下簡單函數(shù)來測量PyTorch的異位ReLU(out-of-place)和就地ReLU(in-place)分配的內(nèi)存:

import torch # import main library
import torch.nn as nn # import modules like nn.ReLU()
import torch.nn.functional as F # import torch functions like F.relu() and F.relu_()

def get_memory_allocated(device, inplace = False):
 '''
 Function measures allocated memory before and after the ReLU function call.
 INPUT:
 - device: gpu device to run the operation
 - inplace: True - to run ReLU in-place, False - for normal ReLU call
 '''
 
 # Create a large tensor
 t = torch.randn(10000, 10000, device=device)
 
 # Measure allocated memory
 torch.cuda.synchronize()
 start_max_memory = torch.cuda.max_memory_allocated() / 1024**2
 start_memory = torch.cuda.memory_allocated() / 1024**2
 
 # Call in-place or normal ReLU
 if inplace:
 F.relu_(t)
 else:
 output = F.relu(t)
 
 # Measure allocated memory after the call
 torch.cuda.synchronize()
 end_max_memory = torch.cuda.max_memory_allocated() / 1024**2
 end_memory = torch.cuda.memory_allocated() / 1024**2
 
 # Return amount of memory allocated for ReLU call
 return end_memory - start_memory, end_max_memory - start_max_memory
 # setup the device
device = torch.device('cuda:0' if torch.cuda.is_available() else "cpu")
#開始測試
# Call the function to measure the allocated memory for the out-of-place ReLU
memory_allocated, max_memory_allocated = get_memory_allocated(device, inplace = False)
print('Allocated memory: {}'.format(memory_allocated))
print('Allocated max memory: {}'.format(max_memory_allocated))
'''
Allocated memory: 382.0
Allocated max memory: 382.0
'''
#Then call the in-place ReLU as follows:
memory_allocated_inplace, max_memory_allocated_inplace = get_memory_allocated(device, inplace = True)
print('Allocated memory: {}'.format(memory_allocated_inplace))
print('Allocated max memory: {}'.format(max_memory_allocated_inplace))
'''
Allocated memory: 0.0
Allocated max memory: 0.0
'''

看起來,使用就地操作可以幫助我們節(jié)省一些GPU內(nèi)存。但是,在使用就地操作時應(yīng)該格外謹(jǐn)慎。

就地操作的主要缺點(diǎn)主要原因有2點(diǎn),官方文檔

1.可能會覆蓋計算梯度所需的值,這意味著破壞了模型的訓(xùn)練過程。

2.每個就地操作實(shí)際上都需要實(shí)現(xiàn)來重寫計算圖。異地操作Out-of-place分配新對象并保留對舊圖的引用,而就地操作則需要更改表示此操作的函數(shù)的所有輸入的創(chuàng)建者。

在Autograd中支持就地操作很困難,并且在大多數(shù)情況下不鼓勵使用。Autograd積極的緩沖區(qū)釋放和重用使其非常高效,就地操作實(shí)際上降低內(nèi)存使用量的情況很少。除非在沉重的內(nèi)存壓力下運(yùn)行,否則可能永遠(yuǎn)不需要使用它們。

總結(jié):Autograd很香了,就地操作要慎用。

拷貝方法

淺拷貝方法: 共享 data 的內(nèi)存地址,數(shù)據(jù)會同步變化

* a.numpy() # Tensor—>Numpy array

* view() #改變tensor的形狀,但共享數(shù)據(jù)內(nèi)存,不要直接使用id進(jìn)行判斷

* y = x[:] # 索引

* torch.from_numpy() # Numpy array—>Tensor

* torch.detach() # 新的tensor會脫離計算圖,不會牽扯梯度計算。

* model:forward()

還有很多選擇函數(shù)也是數(shù)據(jù)共享內(nèi)存,如index_select() masked_select() gather()。

以及后文提到的就地操作in-place。

深拷貝方法:

* torch.clone() # 新的tensor會保留在計算圖中,參與梯度計算

下面進(jìn)行驗證,首先驗證淺拷貝:

import torch as t
import numpy as np
a = np.ones(4)
b = t.from_numpy(a) # Numpy->Tensor
print(a)
print(b)
'''輸出:
[1. 1. 1. 1.]
tensor([1., 1., 1., 1.], dtype=torch.float64)
'''
b.add_(1)# add_會修改b自身
print(a)
print(b)
'''輸出:
[2. 2. 2. 2.]
tensor([2., 2., 2., 2.], dtype=torch.float64)
b進(jìn)行add操作后, a,b同步發(fā)生了變化
'''

Tensor和numpy對象共享內(nèi)存(淺拷貝操作),所以他們之間的轉(zhuǎn)換很快,且會同步變化。

造torch中y = x + y這樣的運(yùn)算是會新開內(nèi)存的,然后將y指向新內(nèi)存。為了進(jìn)行驗證,我們可以使用Python自帶的id函數(shù):如果兩個實(shí)例的ID一致,那么它們所對應(yīng)的內(nèi)存地址相同;但需要注意是在torch中還有些特殊,數(shù)據(jù)共享時直接打印tensor的id仍然會出現(xiàn)不同。

x = torch.tensor([1, 2])
y = torch.tensor([3, 4])
id_0 = id(y)
y = y + x
print(id(y) == id_0) 
# False 

這時使用索引操作不會開辟新的內(nèi)存,而想指定結(jié)果到原來的y的內(nèi)存,我們可以使用索引來進(jìn)行替換操作。比如把x + y的結(jié)果通過[:]寫進(jìn)y對應(yīng)的內(nèi)存中。

x = torch.tensor([1, 2])
y = torch.tensor([3, 4])
id_0 = id(y)
y[:] = y + x
print(id(y) == id_0) 
# True

另外,以下兩種方式也可以索引到相同的內(nèi)存:

  • torch.add(x, y, out=y)
  • y += x, y.add_(x)
x = torch.tensor([1, 2])
y = torch.tensor([3, 4])
id_0 = id(y)
torch.add(x, y, out=y) 
# y += x, y.add_(x)
print(id(y) == id_0) 
# True

clone() 與 detach() 對比

Torch 為了提高速度,向量或是矩陣的賦值是指向同一內(nèi)存的,這不同于 Matlab。如果需要保存舊的tensor即需要開辟新的存儲地址而不是引用,可以用 clone() 進(jìn)行深拷貝,

首先我們來打印出來clone()操作后的數(shù)據(jù)類型定義變化:

(1). 簡單打印類型

import torch

a = torch.tensor(1.0, requires_grad=True)
b = a.clone()
c = a.detach()
a.data *= 3
b += 1

print(a) # tensor(3., requires_grad=True)
print(b)
print(c)

'''
輸出結(jié)果:
tensor(3., requires_grad=True)
tensor(2., grad_fn=<AddBackward0>)
tensor(3.)  # detach()后的值隨著a的變化出現(xiàn)變化
'''

grad_fn=<CloneBackward>,表示clone后的返回值是個中間變量,因此支持梯度的回溯。clone操作在一定程度上可以視為是一個identity-mapping函數(shù)。

detach()操作后的tensor與原始tensor共享數(shù)據(jù)內(nèi)存,當(dāng)原始tensor在計算圖中數(shù)值發(fā)生反向傳播等更新之后,detach()的tensor值也發(fā)生了改變。

注意: 在pytorch中我們不要直接使用id是否相等來判斷tensor是否共享內(nèi)存,這只是充分條件,因為也許底層共享數(shù)據(jù)內(nèi)存,但是仍然是新的tensor,比如detach(),如果我們直接打印id會出現(xiàn)以下情況。

import torch as t
a = t.tensor([1.0,2.0], requires_grad=True)
b = a.detach()
#c[:] = a.detach()
print(id(a))
print(id(b))
#140568935450520
140570337203616

顯然直接打印出來的id不等,我們可以通過簡單的賦值后觀察數(shù)據(jù)變化進(jìn)行判斷。

(2). clone()的梯度回傳

detach()函數(shù)可以返回一個完全相同的tensor,與舊的tensor共享內(nèi)存,脫離計算圖,不會牽扯梯度計算。

而clone充當(dāng)中間變量,會將梯度傳給源張量進(jìn)行疊加,但是本身不保存其grad,即值為None

import torch
a = torch.tensor(1.0, requires_grad=True)
a_ = a.clone()
y = a**2
z = a ** 2+a_ * 3
y.backward()
print(a.grad) # 2
z.backward()
print(a_.grad)   # None. 中間variable,無grad
print(a.grad) 
'''
輸出:
tensor(2.) 
None
tensor(7.) # 2*2+3=7
'''

使用torch.clone()獲得的新tensor和原來的數(shù)據(jù)不再共享內(nèi)存,但仍保留在計算圖中,clone操作在不共享數(shù)據(jù)內(nèi)存的同時支持梯度梯度傳遞與疊加,所以常用在神經(jīng)網(wǎng)絡(luò)中某個單元需要重復(fù)使用的場景下。

通常如果原tensor的requires_grad=True,則:

  • clone()操作后的tensor requires_grad=True
  • detach()操作后的tensor requires_grad=False。
import torch
torch.manual_seed(0)

x= torch.tensor([1., 2.], requires_grad=True)
clone_x = x.clone() 
detach_x = x.detach()
clone_detach_x = x.clone().detach() 

f = torch.nn.Linear(2, 1)
y = f(x)
y.backward()

print(x.grad)
print(clone_x.requires_grad)
print(clone_x.grad)
print(detach_x.requires_grad)
print(clone_detach_x.requires_grad)
'''
輸出結(jié)果如下:
tensor([-0.0053, 0.3793])
True
None
False
False
'''

另一個比較特殊的是當(dāng)源張量的 require_grad=False,clone后的張量 require_grad=True,此時不存在張量回傳現(xiàn)象,可以得到clone后的張量求導(dǎo)。

如下:

import torch
a = torch.tensor(1.0)
a_ = a.clone()
a_.requires_grad_() #require_grad=True
y = a_ ** 2
y.backward()
print(a.grad) # None
print(a_.grad) 
'''
輸出:
None
tensor(2.)
'''

總結(jié):

torch.detach() —新的tensor會脫離計算圖,不會牽扯梯度計算

torch.clone() — 新的tensor充當(dāng)中間變量,會保留在計算圖中,參與梯度計算(回傳疊加),但是一般不會保留自身梯度。

原地操作(in-place, such as resize_ / resize_as_ / set_ / transpose_) 在上面兩者中執(zhí)行都會引發(fā)錯誤或者警告。

引用官方文檔的話:如果你使用了in-place operation而沒有報錯的話,那么你可以確定你的梯度計算是正確的。另外盡量避免in-place的使用。

到此這篇關(guān)于PyTorch中拷貝與就地操作的文章就介紹到這了,更多相關(guān)PyTorch拷貝與就地操作內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Python通過socketserver處理多個鏈接

    Python通過socketserver處理多個鏈接

    這篇文章主要介紹了Python通過socketserver處理多個鏈接,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-03-03
  • python跨文件使用全局變量的實(shí)現(xiàn)

    python跨文件使用全局變量的實(shí)現(xiàn)

    這篇文章主要介紹了python跨文件使用全局變量的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-11-11
  • pycharm中創(chuàng)建sql文件及模板的過程

    pycharm中創(chuàng)建sql文件及模板的過程

    很多小伙伴剛開始使用pycharm時發(fā)現(xiàn)以前的老員工在使用pycharm創(chuàng)建sql文件時會自帶文件頭模板,例如時間、作者、版本、郵件等信息,這是怎么做到的呢,一起來看一下吧
    2022-07-07
  • Python快速實(shí)現(xiàn)一鍵摳圖功能的全過程

    Python快速實(shí)現(xiàn)一鍵摳圖功能的全過程

    你有沒想過,Python也能成為這樣的一種工具:在只有一張圖片,需要細(xì)致地?fù)赋鋈宋锏那闆r下,能幫你減少摳圖步驟,這篇文章主要給大家介紹了關(guān)于Python快速實(shí)現(xiàn)一鍵摳圖功能的相關(guān)資料,需要的朋友可以參考下
    2021-06-06
  • 解決Python3中的中文字符編碼的問題

    解決Python3中的中文字符編碼的問題

    Unicode是一32位編碼格式,不適合用來傳輸和存儲,所以必須轉(zhuǎn)換成utf-8,gbk等等。這篇文章主要介紹了Python3中的解決中文字符編碼的問題,需要的朋友可以參考下
    2018-07-07
  • Python之np.where()如何替換缺失值

    Python之np.where()如何替換缺失值

    這篇文章主要介紹了Python中的np.where()如何替換缺失值問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-02-02
  • python中第三方庫pyecharts的使用詳解

    python中第三方庫pyecharts的使用詳解

    這篇文章主要介紹了python中第三方庫pyecharts的使用, pyecharts的作用是用來做數(shù)據(jù)圖表,本文給大家介紹了作圖的步驟及實(shí)例代碼,需要的朋友可以參考下
    2022-08-08
  • django緩存配置的幾種方法詳解

    django緩存配置的幾種方法詳解

    緩存對各位學(xué)習(xí)或者使用django的朋友們來說應(yīng)該都不陌生,下面這篇文章主要給大家介紹了關(guān)于django緩存配置的幾種方法,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2018-07-07
  • django 數(shù)據(jù)庫 get_or_create函數(shù)返回值是tuple的問題

    django 數(shù)據(jù)庫 get_or_create函數(shù)返回值是tuple的問題

    這篇文章主要介紹了django 數(shù)據(jù)庫 get_or_create函數(shù)返回值是tuple的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-05-05
  • Python?的counter()函數(shù)解析與示例詳解

    Python?的counter()函數(shù)解析與示例詳解

    在?Python?中,?collections?模塊提供了?Counter?類,用于計算可迭代對象中元素的數(shù)量,?Counter?是一個字典的子類,它以元素作為鍵,以元素出現(xiàn)的次數(shù)作為值進(jìn)行計數(shù),本文給大家介紹Python?的counter()函數(shù),感興趣的朋友一起看看吧
    2023-08-08

最新評論

岳池县| 涟源市| 芦山县| 瑞金市| 吴忠市| 射阳县| 永嘉县| 河东区| 凌云县| 乌恰县| 原阳县| 墨脱县| 凤阳县| 从化市| 聂荣县| 定襄县| 古田县| 大理市| 台东县| 陵川县| 娄底市| 浦东新区| 阜康市| 共和县| 巴里| 桂东县| 清丰县| 巴林左旗| 尼玛县| 孟州市| 游戏| 全州县| 会泽县| 竹山县| 武功县| 临澧县| 辽中县| 韶关市| 稷山县| 新郑市| 托克逊县|