欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

PyTorch中view()與?reshape()的區(qū)別詳析

 更新時間:2022年01月28日 09:36:09   作者:你卷我不卷  
這篇文章主要給大家介紹了關(guān)于PyTorch中view()?與?reshape()?區(qū)別的相關(guān)資料,文中通過實例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下

前言

總之,兩者都是用來重塑tensor的shape的。view只適合對滿足連續(xù)性條件(contiguous)的tensor進行操作,而reshape同時還可以對不滿足連續(xù)性條件的tensor進行操作,具有更好的魯棒性。view能干的reshape都能干,如果view不能干就可以用reshape來處理。別看目錄挺多,但內(nèi)容很細(xì)呀~其實原理并不難啦~我們開始吧~

一、PyTorch中tensor的存儲方式

想要深入理解view與reshape的區(qū)別,首先要理解一些有關(guān)PyTorch張量存儲的底層原理,比如tensor的頭信息區(qū)(Tensor)和存儲區(qū) (Storage)以及tensor的步長Stride。不用慌,這部分的原理其實很簡單的(^-^)!

1、PyTorch張量存儲的底層原理

tensor數(shù)據(jù)采用頭信息區(qū)(Tensor)和存儲區(qū) (Storage)分開存儲的形式,如圖1所示。變量名以及其存儲的數(shù)據(jù)是分為兩個區(qū)域分別存儲的。比如,我們定義并初始化一個tensor,tensor名為A,A的形狀size、步長stride、數(shù)據(jù)的索引等信息都存儲在頭信息區(qū),而A所存儲的真實數(shù)據(jù)則存儲在存儲區(qū)。另外,如果我們對A進行截取、轉(zhuǎn)置或修改等操作后賦值給B,則B的數(shù)據(jù)共享A的存儲區(qū),存儲區(qū)的數(shù)據(jù)數(shù)量沒變,變化的只是B的頭信息區(qū)對數(shù)據(jù)的索引方式。如果聽說過淺拷貝和深拷貝的話,很容易明白這種方式其實就是淺拷貝。

圖1 Torch中Tensor的存儲結(jié)構(gòu)

舉個例子:

import torch
a = torch.arange(5)  # 初始化張量 a 為 [0, 1, 2, 3, 4]
b = a[2:]            # 截取張量a的部分值并賦值給b,b其實只是改變了a對數(shù)據(jù)的索引方式
print('a:', a)
print('b:', b)
print('ptr of storage of a:', a.storage().data_ptr())  # 打印a的存儲區(qū)地址
print('ptr of storage of b:', b.storage().data_ptr())  # 打印b的存儲區(qū)地址,可以發(fā)現(xiàn)兩者是共用存儲區(qū)
 
print('==================================================================')
 
b[1] = 0    # 修改b中索引為1,即a中索引為3的數(shù)據(jù)為0
print('a:', a)
print('b:', b)
print('ptr of storage of a:', a.storage().data_ptr())  # 打印a的存儲區(qū)地址,可以發(fā)現(xiàn)a的相應(yīng)位置的值也跟著改變,說明兩者是共用存儲區(qū)
print('ptr of storage of b:', b.storage().data_ptr())  # 打印b的存儲區(qū)地址
 
 
'''   運行結(jié)果   '''
a: tensor([0, 1, 2, 3, 4])
b: tensor([2, 3, 4])
ptr of storage of a: 2862826251264
ptr of storage of b: 2862826251264
==================================================================
a: tensor([0, 1, 2, 0, 4])
b: tensor([2, 0, 4])
ptr of storage of a: 2862826251264
ptr of storage of b: 2862826251264

2、PyTorch張量的步長(stride)屬性

torch的tensor也是有步長屬性的,說起stride屬性是不是很耳熟?是的,卷積神經(jīng)網(wǎng)絡(luò)中卷積核對特征圖的卷積操作也是有stride屬性的,但這兩個stride可完全不是一個意思哦。tensor的步長可以理解為從索引中的一個維度跨到下一個維度中間的跨度。為方便理解,就直接用圖1說明了,您細(xì)細(xì)品(^-^):

圖2 對張量的stride屬性的理解

舉個例子:

import torch
a = torch.arange(6).reshape(2, 3)  # 初始化張量 a
b = torch.arange(6).view(3, 2)     # 初始化張量 b
print('a:', a)
print('stride of a:', a.stride())  # 打印a的stride
print('b:', b)
print('stride of b:', b.stride())  # 打印b的stride
 
'''   運行結(jié)果   '''
a: tensor([[0, 1, 2],
           [3, 4, 5]])
stride of a: (3, 1)
 
b: tensor([[0, 1],
           [2, 3],
           [4, 5]])
stride of b: (2, 1)

二、對“視圖(view)”字眼的理解

視圖是數(shù)據(jù)的一個別稱或引用,通過該別稱或引用亦便可訪問、操作原有數(shù)據(jù),但原有數(shù)據(jù)不會產(chǎn)生拷貝。如果我們對視圖進行修改,它會影響到原始數(shù)據(jù),物理內(nèi)存在同一位置,這樣避免了重新創(chuàng)建張量的高內(nèi)存開銷。由上面介紹的PyTorch的張量存儲方式可以理解為:對張量的大部分操作就是視圖操作!

與之對應(yīng)的概念就是副本。副本是一個數(shù)據(jù)的完整的拷貝,如果我們對副本進行修改,它不會影響到原始數(shù)據(jù),物理內(nèi)存不在同一位置。

有關(guān)視圖與副本,在NumPy中也有著重要的應(yīng)用??蓞⒖?a target="_blank">這里。

三、view() 和reshape() 的比較

1、對 torch.Tensor.view() 的理解

定義:

view(*shape) → Tensor

作用:類似于reshape,將tensor轉(zhuǎn)換為指定的shape,原始的data不改變。返回的tensor與原始的tensor共享存儲區(qū)。返回的tensor的size和stride必須與原始的tensor兼容。每個新的tensor的維度必須是原始維度的子空間,或滿足以下連續(xù)條件:

式1 張量連續(xù)性條件

否則需要先使用contiguous()方法將原始tensor轉(zhuǎn)換為滿足連續(xù)條件的tensor,然后就可以使用view方法進行shape變換了?;蛘咧苯邮褂胷eshape方法進行維度變換,但這種方法變換后的tensor就不是與原始tensor共享內(nèi)存了,而是被重新開辟了一個空間。

如何理解tensor是否滿足連續(xù)條件吶?下面通過一系列例子來慢慢理解下:

首先,我們初始化一個張量 a ,并查看其stride、storage等屬性:

import torch
a = torch.arange(9).reshape(3, 3)  # 初始化張量a
print('struct of a:\n', a)
print('size   of a:', a.size())    # 查看a的shape
print('stride of a:', a.stride())  # 查看a的stride
 
'''   運行結(jié)果   '''
struct of a:
tensor([[0, 1, 2],
        [3, 4, 5],
        [6, 7, 8]])
size   of a: torch.Size([3, 3])
stride of a: (3, 1)   # 注:滿足連續(xù)性條件

把上面的結(jié)果帶入式1,可以發(fā)現(xiàn)滿足tensor連續(xù)性條件。

我們再看進一步處理——對a進行轉(zhuǎn)置后的結(jié)果:

import torch
a = torch.arange(9).reshape(3, 3)     # 初始化張量a
b = a.permute(1, 0)  # 對a進行轉(zhuǎn)置
print('struct of b:\n', b)
print('size   of b:', b.size())    # 查看b的shape
print('stride of b:', b.stride())  # 查看b的stride
 
'''   運行結(jié)果   '''
struct of b:
tensor([[0, 3, 6],
        [1, 4, 7],
        [2, 5, 8]])
size   of b: torch.Size([3, 3])
stride of b: (1, 3)   # 注:此時不滿足連續(xù)性條件

 將a轉(zhuǎn)置后再看最后的輸出結(jié)果,帶入到式1中,是不是發(fā)現(xiàn)等式不成立了?所以此時就不滿足tensor連續(xù)的條件了。這是為什么那?我們接著往下看:

首先,輸出a和b的存儲區(qū)來看一下有沒有什么不同:

import torch
a = torch.arange(9).reshape(3, 3)             # 初始化張量a
print('ptr of storage of a: ', a.storage().data_ptr())  # 查看a的storage區(qū)的地址
print('storage of a: \n', a.storage())        # 查看a的storage區(qū)的數(shù)據(jù)存放形式
b = a.permute(1, 0)                           # 轉(zhuǎn)置
print('ptr of storage of b: ', b.storage().data_ptr())  # 查看b的storage區(qū)的地址
print('storage of b: \n', b.storage())        # 查看b的storage區(qū)的數(shù)據(jù)存放形式
 
'''   運行結(jié)果   '''
ptr of storage of a:  2767173747136
storage of a: 
  0
 1
 2
 3
 4
 5
 6
 7
 8
[torch.LongStorage of size 9]
ptr of storage of b:  2767173747136
storage of b:
  0
 1
 2
 3
 4
 5
 6
 7
 8
[torch.LongStorage of size 9]

 由結(jié)果可以看出,張量a、b仍然共用存儲區(qū),并且存儲區(qū)數(shù)據(jù)存放的順序沒有變化,這也充分說明了b與a共用存儲區(qū),b只是改變了數(shù)據(jù)的索引方式。那么為什么b就不符合連續(xù)性條件了吶(T-T)?其實原因很簡單,我們結(jié)合圖3來解釋下:

圖3 對張量連續(xù)性條件的理解

轉(zhuǎn)置后的tensor只是對storage區(qū)數(shù)據(jù)索引方式的重映射,但原始的存放方式并沒有變化.因此,這時再看tensor b的stride,從b第一行的元素1到第二行的元素2,顯然在索引方式上已經(jīng)不是原來+1了,而是變成了新的+3了,你在仔細(xì)琢磨琢磨是不是這樣的(^-^)。所以這時候就不能用view來對b進行shape的改變了,不然就報錯咯,不信你看下面;

import torch
a = torch.arange(9).reshape(3, 3)             # 初始化張量a
print(a.view(9))
print('============================================')
b = a.permute(1, 0)  # 轉(zhuǎn)置
print(b.view(9))
 
'''   運行結(jié)果   '''
tensor([0, 1, 2, 3, 4, 5, 6, 7, 8])
============================================
Traceback (most recent call last):
  File "此處打碼", line 23, in <module>
    print(b.view(9))
RuntimeError: view size is not compatible with input tensor's size and stride (at least one dimension spans across two contiguous subspaces). Use .reshape(...) instead.

但是嘛,上有政策下有對策,這種情況下,直接用view不行,那我就先用contiguous()方法將原始tensor轉(zhuǎn)換為滿足連續(xù)條件的tensor,在使用view進行shape變換,值得注意的是,這樣的原理是contiguous()方法開辟了一個新的存儲區(qū)給b,并改變了b原始存儲區(qū)數(shù)據(jù)的存放順序!同樣的例子:

import torch
a = torch.arange(9).reshape(3, 3)      # 初始化張量a
print('storage of a:\n', a.storage())  # 查看a的stride
print('+++++++++++++++++++++++++++++++++++++++++++++++++')
b = a.permute(1, 0).contiguous()       # 轉(zhuǎn)置,并轉(zhuǎn)換為符合連續(xù)性條件的tensor
print('size    of b:', b.size())       # 查看b的shape
print('stride  of b:', b.stride())     # 查看b的stride
print('viewd      b:\n', b.view(9))    # 對b進行view操作,并打印結(jié)果
print('+++++++++++++++++++++++++++++++++++++++++++++++++')
print('storage of a:\n', a.storage())  # 查看a的存儲空間
print('storage of b:\n', b.storage())  # 查看b的存儲空間
print('+++++++++++++++++++++++++++++++++++++++++++++++++')
print('ptr of a:\n', a.storage().data_ptr())  # 查看a的存儲空間地址
print('ptr of b:\n', b.storage().data_ptr())  # 查看b的存儲空間地址
 
'''   運行結(jié)果   '''
storage of a:
  0
 1
 2
 3
 4
 5
 6
 7
 8
[torch.LongStorage of size 9]
+++++++++++++++++++++++++++++++++++++++++++++++++
size    of b: torch.Size([3, 3])
stride  of b: (3, 1)
viewd      b:
 tensor([0, 3, 6, 1, 4, 7, 2, 5, 8])
+++++++++++++++++++++++++++++++++++++++++++++++++
storage of a:
 0
 1
 2
 3
 4
 5
 6
 7
 8
[torch.LongStorage of size 9]
storage of b:
 0
 3
 6
 1
 4
 7
 2
 5
 8
[torch.LongStorage of size 9]
+++++++++++++++++++++++++++++++++++++++++++++++++
ptr of a:
 1842671472000
ptr of b:
 1842671472128

由上述結(jié)果可以看出,張量a與b已經(jīng)是兩個存在于不同存儲區(qū)的張量了。也印證了contiguous()方法開辟了一個新的存儲區(qū)給b,并改變了b原始存儲區(qū)數(shù)據(jù)的存放順序。對應(yīng)文章開頭提到的淺拷貝,這種開辟一個新的內(nèi)存區(qū)的方式其實就是深拷貝。

2、對 torch.reshape() 的理解

定義:

torch.reshape(input, shape) → Tensor

作用:與view方法類似,將輸入tensor轉(zhuǎn)換為新的shape格式。

但是reshape方法更強大,可以認(rèn)為a.reshape = a.view() + a.contiguous().view()。

即:在滿足tensor連續(xù)性條件時,a.reshape返回的結(jié)果與a.view()相同,否則返回的結(jié)果與a.contiguous().view()相同。

不信你就看人家官方的解釋嘛,您在細(xì)細(xì)品:

關(guān)于兩者區(qū)別,還可以參考這個鏈接:

What's the difference between reshape and view in pytorch? - Stack Overflow

2021.03.30更新:最近又發(fā)現(xiàn)了pytorch官網(wǎng)對view及reshape原理的闡述,說的很清晰明了,大家可以參考下:

Tensor Views — PyTorch 1.9.0 documentation

放一張該網(wǎng)站上的截圖:

四、總結(jié)

torch的view()與reshape()方法都可以用來重塑tensor的shape,區(qū)別就是使用的條件不一樣。view()方法只適用于滿足連續(xù)性條件的tensor,并且該操作不會開辟新的內(nèi)存空間,只是產(chǎn)生了對原存儲空間的一個新別稱和引用,返回值是視圖。而reshape()方法的返回值既可以是視圖,也可以是副本,當(dāng)滿足連續(xù)性條件時返回view,否則返回副本[ 此時等價于先調(diào)用contiguous()方法在使用view() ]。因此當(dāng)不確能否使用view時,可以使用reshape。如果只是想簡單地重塑一個tensor的shape,那么就是用reshape,但是如果需要考慮內(nèi)存的開銷而且要確保重塑后的tensor與之前的tensor共享存儲空間,那就使用view()。

2020.10.23

以上是我個人看了官網(wǎng)的的解釋并實驗得到的結(jié)論,所以有沒有dalao知道為啥沒把view廢除那?是不是還有我不知道的地方

2020.11.14

為什么沒把view廢除那?最近偶然看到了些資料,又想起了這個問題,覺得有以下原因:

1、在PyTorch不同版本的更新過程中,view先于reshape方法出現(xiàn),后來出現(xiàn)了魯棒性更好的reshape方法,但view方法并沒因此廢除。其實不止PyTorch,其他一些框架或語言比如OpenCV也有類似的操作。

2、view的存在可以顯示地表示對這個tensor的操作只能是視圖操作而非拷貝操作。這對于代碼的可讀性以及后續(xù)可能的bug的查找比較友好。

總之,我們沒必要糾結(jié)為啥a能干的b也能干,b還能做a不能干的,a存在還有啥意義的問題。就相當(dāng)于馬云能日賺1個億而我不能,那我存在的意義是啥。。。存在不就是意義嗎?存在即合理,最重要的是我們使用不同的方法可以不同程度上提升效率,何樂而不為?

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

相關(guān)文章

  • python 的生產(chǎn)者和消費者模式

    python 的生產(chǎn)者和消費者模式

    這篇文章主要介紹了python 的生產(chǎn)者和python 的消費者模式的具體相關(guān)資料,需要的朋友可以參考下面文章內(nèi)容
    2021-09-09
  • 一些Python中的二維數(shù)組的操作方法

    一些Python中的二維數(shù)組的操作方法

    這篇文章主要介紹了一些Python中的二維數(shù)組的操作方法,是Python學(xué)習(xí)當(dāng)中的基礎(chǔ)知識,需要的朋友可以參考下
    2015-05-05
  • 淺談tensorflow與pytorch的相互轉(zhuǎn)換

    淺談tensorflow與pytorch的相互轉(zhuǎn)換

    本文主要介紹了簡單介紹一下tensorflow與pytorch的相互轉(zhuǎn)換,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-06-06
  • 使用python畫出邏輯斯蒂映射(logistic map)中的分叉圖案例

    使用python畫出邏輯斯蒂映射(logistic map)中的分叉圖案例

    這篇文章主要介紹了使用python畫出邏輯斯蒂映射(logistic map)中的分叉圖案例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • python過濾中英文標(biāo)點符號的實例代碼

    python過濾中英文標(biāo)點符號的實例代碼

    今天小編就為大家分享一篇python過濾中英文標(biāo)點符號的實例代碼,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-07-07
  • pyqt5的QComboBox 使用模板的具體方法

    pyqt5的QComboBox 使用模板的具體方法

    這篇文章主要介紹了pyqt5的QComboBox 使用模板的具體方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-09-09
  • jupyter notebook更換皮膚主題的實現(xiàn)

    jupyter notebook更換皮膚主題的實現(xiàn)

    這篇文章主要介紹了jupyter notebook更換皮膚主題的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01
  • Python實現(xiàn)基于socket的udp傳輸與接收功能詳解

    Python實現(xiàn)基于socket的udp傳輸與接收功能詳解

    這篇文章主要介紹了Python實現(xiàn)基于socket的udp傳輸與接收功能,結(jié)合實例形式詳細(xì)分析了Python使用socket進行udp文件傳輸與接收相關(guān)操作技巧及注意事項,需要的朋友可以參考下
    2019-11-11
  • python實現(xiàn)dbscan算法

    python實現(xiàn)dbscan算法

    DBSCAN 算法是一種基于密度的空間聚類算法,本文主要介紹了python實現(xiàn)dbscan算法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-05-05
  • 使用Python實現(xiàn)windows下的抓包與解析

    使用Python實現(xiàn)windows下的抓包與解析

    這篇文章主要介紹了使用Python實現(xiàn)windows下的抓包與解析,非常不錯,具有參考借鑒價值,需要的朋友可以參考下
    2018-01-01

最新評論