PyTorch में "व्यू" विधि कैसे काम करती है?


207

मैं view()निम्नलिखित कोड स्निपेट में विधि के बारे में उलझन में हूं ।

class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(3, 6, 5)
        self.pool  = nn.MaxPool2d(2,2)
        self.conv2 = nn.Conv2d(6, 16, 5)
        self.fc1   = nn.Linear(16*5*5, 120)
        self.fc2   = nn.Linear(120, 84)
        self.fc3   = nn.Linear(84, 10)

    def forward(self, x):
        x = self.pool(F.relu(self.conv1(x)))
        x = self.pool(F.relu(self.conv2(x)))
        x = x.view(-1, 16*5*5)
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x

net = Net()

मेरा भ्रम निम्न पंक्ति के बारे में है।

x = x.view(-1, 16*5*5)

tensor.view()कार्य क्या करता है? मैंने कई स्थानों पर इसका उपयोग देखा है, लेकिन मैं यह नहीं समझ सकता कि यह अपने मापदंडों की व्याख्या कैसे करता है।

यदि मैं view()फ़ंक्शन के मापदंडों के रूप में नकारात्मक मान देता हूं तो क्या होगा ? उदाहरण के लिए, मुझे कॉल करने पर क्या होता है tensor_variable.view(1, 1, -1),?

क्या कोई view()फ़ंक्शन के मुख्य सिद्धांत को कुछ उदाहरणों के साथ समझा सकता है ?

जवाबों:


285

देखने का कार्य टेंसर को फिर से आकार देने के लिए है।

कहते हैं कि आपके पास एक टेंसर है

import torch
a = torch.range(1, 16)

aएक टेंसर है जिसमें 1 से 16 तक (शामिल) से 16 तत्व हैं। यदि आप इस टेंसर को 4 x 4फिर से टेंसर बनाने के लिए फिर से खोलना चाहते हैं तो आप उपयोग कर सकते हैं

a = a.view(4, 4)

अब aएक हो जाएगा 4 x 4टेन्सर। ध्यान दें कि फेरबदल के बाद तत्वों की कुल संख्या समान रहने की आवश्यकता है। टेंसर aको एक टेंपरेचर में बदलना 3 x 5उचित नहीं होगा।

पैरामीटर -1 का अर्थ क्या है?

यदि ऐसी कोई स्थिति है जो आपको नहीं पता है कि आप कितनी पंक्तियाँ चाहते हैं, लेकिन स्तंभों की संख्या के बारे में सुनिश्चित हैं, तो आप इसे -1 के साथ निर्दिष्ट कर सकते हैं। ( ध्यान दें कि आप इसे अधिक आयामों के साथ दसियों तक बढ़ा सकते हैं। केवल एक अक्ष मूल्य -1 हो सकता है )। यह लाइब्रेरी को बताने का एक तरीका है: "मुझे एक टेंसर दें जिसमें ये कई कॉलम हैं और आप उचित संख्या में पंक्तियों की गणना करते हैं जो ऐसा करने के लिए आवश्यक हैं"।

यह तंत्रिका नेटवर्क कोड में देखा जा सकता है जो आपने ऊपर दिया है। x = self.pool(F.relu(self.conv2(x)))फॉरवर्ड फंक्शन में लाइन के बाद , आपके पास 16 डेप्थ फीचर मैप होगा। आपको इसे पूरी तरह से जुड़ी हुई परत को देने के लिए समतल करना होगा। तो आप पाईटोर को बताएंगे कि आपके पास विशिष्ट संख्या में कॉलम के लिए प्राप्त टेनर को फिर से खोलना है और इसे अपने लिए पंक्तियों की संख्या तय करने के लिए कहें।

Numpy और pytorch के बीच एक समानता ड्राइंग, viewnumpy के लिए समान है आकृति बदलें कार्य करते हैं।


93
"दृश्य numpy के आकार के समान है" - उन्होंने इसे reshapePyTorch में कॉल क्यों नहीं किया ?!
1

54
@ एमएक्सबी रिशेप के विपरीत, नया टेंसर "व्यू" द्वारा लौटाया गया है, जो मूल टेनर के साथ अंतर्निहित डेटा साझा करता है, इसलिए यह वास्तव में एक नया ब्रांड बनाने के बजाय पुराने टेंसर में एक दृश्य है।
चिहुकि

37
@blckbird "रिशेप हमेशा मेमोरी को कॉपी करता है। देखें कभी भी मेमोरी को कॉपी न करें।" github.com/torch/cutorch/issues/98
devinbost

3
@devinbost मशाल रिशेप हमेशा मेमोरी को कॉपी करती है। NumPy reshape नहीं करता है।
तवियन बार्न्स

32

आइए कुछ उदाहरणों को सरल से और अधिक कठिन करते हैं।

  1. viewविधि के रूप में एक ही डेटा के साथ एक टेन्सर रिटर्न selfटेन्सर (जिसका अर्थ है कि लौटे टेन्सर तत्वों की एक ही नंबर है), लेकिन एक अलग आकार के साथ। उदाहरण के लिए:

    a = torch.arange(1, 17)  # a's shape is (16,)
    
    a.view(4, 4) # output below
      1   2   3   4
      5   6   7   8
      9  10  11  12
     13  14  15  16
    [torch.FloatTensor of size 4x4]
    
    a.view(2, 2, 4) # output below
    (0 ,.,.) = 
    1   2   3   4
    5   6   7   8
    
    (1 ,.,.) = 
     9  10  11  12
    13  14  15  16
    [torch.FloatTensor of size 2x2x4]
  2. यह मानते हुए कि -1मापदंडों में से एक नहीं है, जब आप उन्हें एक साथ गुणा करते हैं, तो परिणाम टेंसर में तत्वों की संख्या के बराबर होना चाहिए। यदि आप करते हैं: a.view(3, 3)यह एक RuntimeErrorकारण आकार बढ़ाएगा (3 x 3) 16 तत्वों के साथ इनपुट के लिए अमान्य है। दूसरे शब्दों में: 3 x 3 16 नहीं बल्कि 9 के बराबर है।

  3. आप उन -1मापदंडों में से एक के रूप में उपयोग कर सकते हैं जिन्हें आप फ़ंक्शन में पास करते हैं, लेकिन केवल एक बार। यह सब होता है कि वह विधि आपके लिए उस आयाम को भरने के लिए गणित करेगी। उदाहरण के लिए a.view(2, -1, 4)के बराबर है a.view(2, 2, 4)। [१६ / (२ x ४) = २]

  4. ध्यान दें कि लौटा हुआ टेंसर समान डेटा साझा करता है । यदि आप "दृश्य" में बदलाव करते हैं तो आप मूल टेंसर के डेटा को बदल रहे हैं:

    b = a.view(4, 4)
    b[0, 2] = 2
    a[2] == 3.0
    False
  5. अब, अधिक जटिल उपयोग के मामले के लिए। दस्तावेज़ीकरण कहता है कि प्रत्येक नए दृश्य आयाम को या तो मूल आयाम का एक उप-क्षेत्र होना चाहिए, या केवल d, d + 1, ..., d + k , जो निम्नलिखित सन्निकटन-जैसी स्थिति को संतुष्ट करता है, जो कि सभी के लिए I = 0,। .., k - 1, stride [i] = stride [i + 1] x आकार [i + 1] । अन्यथा, contiguous()दसियों को देखे जाने से पहले बुलाया जाना चाहिए। उदाहरण के लिए:

    a = torch.rand(5, 4, 3, 2) # size (5, 4, 3, 2)
    a_t = a.permute(0, 2, 3, 1) # size (5, 3, 2, 4)
    
    # The commented line below will raise a RuntimeError, because one dimension
    # spans across two contiguous subspaces
    # a_t.view(-1, 4)
    
    # instead do:
    a_t.contiguous().view(-1, 4)
    
    # To see why the first one does not work and the second does,
    # compare a.stride() and a_t.stride()
    a.stride() # (24, 6, 2, 1)
    a_t.stride() # (24, 2, 1, 6)

    ध्यान दें कि a_t, 24 के बाद से [0]! = Stride [1] x आकार [1]


7

torch.Tensor.view()

सीधे शब्दों में कहें, torch.Tensor.view()जो प्रेरित है numpy.ndarray.reshape()या numpy.reshape(), टेंसर का एक नया दृष्टिकोण बनाता है , जब तक कि नया आकार मूल टेंसर के आकार के साथ संगत हो।

आइए इसे एक ठोस उदाहरण का उपयोग करके विस्तार से समझें।

In [43]: t = torch.arange(18) 

In [44]: t 
Out[44]: 
tensor([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17])

tआकार के इस दशांश के साथ (18,), नए विचार केवल निम्नलिखित आकृतियों के लिए बनाए जा सकते हैं:

(1, 18)या समतुल्य (1, -1)या या समकक्ष या या समकक्ष या या समकक्ष या या समकक्ष या या समकक्ष या(-1, 18)
(2, 9)(2, -1)(-1, 9)
(3, 6)(3, -1)(-1, 6)
(6, 3)(6, -1)(-1, 3)
(9, 2)(9, -1)(-1, 2)
(18, 1)(18, -1)(-1, 1)

जैसा कि हम पहले से ही उपरोक्त आकार के टुपल्स से देख सकते हैं, आकार टपल के तत्वों का गुणा (जैसे 2*9, 3*6आदि) हमेशा मूल टेंसर में तत्वों की कुल संख्या के बराबर होना चाहिए ( 18हमारे उदाहरण में)।

निरीक्षण करने के लिए एक और बात यह है कि हमने -1प्रत्येक आकार के टुपल्स में से एक में एक जगह का उपयोग किया । -1A का उपयोग करके , हम स्वयं संगणना करने में आलसी हो रहे हैं और कार्य के लिए PyTorch को उस आकार की गणना करने के लिए कार्य सौंपते हैं जब वह नया दृश्य बनाता है । ध्यान देने वाली एक महत्वपूर्ण बात यह है कि हम केवल एक ही -1आकार में टपल का उपयोग कर सकते हैं । शेष मूल्यों को हमारे द्वारा स्पष्ट रूप से आपूर्ति की जानी चाहिए। Else PyTorch फेंकने से शिकायत करेगा RuntimeError:

RuntimeError: केवल एक आयाम का अनुमान लगाया जा सकता है

इसलिए, उपरोक्त सभी आकृतियों के साथ, PyTorch हमेशा मूल टेंसर का एक नया दृश्य लौटाएगा t। मूल रूप से इसका मतलब है कि यह केवल अनुरोध किए गए नए विचारों में से प्रत्येक के लिए टेंसर की कठोर जानकारी को बदलता है।

नीचे कुछ उदाहरण दिए गए हैं जो बताते हैं कि प्रत्येक नए दृश्य के साथ टेंसर्स के तारों को कैसे बदला जाता है ।

# stride of our original tensor `t`
In [53]: t.stride() 
Out[53]: (1,)

अब, हम नए विचारों के लिए प्रगति देखेंगे :

# shape (1, 18)
In [54]: t1 = t.view(1, -1)
# stride tensor `t1` with shape (1, 18)
In [55]: t1.stride() 
Out[55]: (18, 1)

# shape (2, 9)
In [56]: t2 = t.view(2, -1)
# stride of tensor `t2` with shape (2, 9)
In [57]: t2.stride()       
Out[57]: (9, 1)

# shape (3, 6)
In [59]: t3 = t.view(3, -1) 
# stride of tensor `t3` with shape (3, 6)
In [60]: t3.stride() 
Out[60]: (6, 1)

# shape (6, 3)
In [62]: t4 = t.view(6,-1)
# stride of tensor `t4` with shape (6, 3)
In [63]: t4.stride() 
Out[63]: (3, 1)

# shape (9, 2)
In [65]: t5 = t.view(9, -1) 
# stride of tensor `t5` with shape (9, 2)
In [66]: t5.stride()
Out[66]: (2, 1)

# shape (18, 1)
In [68]: t6 = t.view(18, -1)
# stride of tensor `t6` with shape (18, 1)
In [69]: t6.stride()
Out[69]: (1, 1)

तो यह view()फंक्शन का जादू है । यह नए विचारों में से प्रत्येक के लिए (मूल) स्पर्शक के स्ट्रैड को बदलता है , जब तक कि नए दृश्य का आकार मूल आकार के साथ संगत नहीं हो जाता।

एक और दिलचस्प बात यह है कि एक उल्लेखनीय प्रगति tuples से पालन हो सकता है कि 0 में तत्व का मान है वें स्थिति 1 में तत्व के मूल्य के बराबर है सेंट आकार टपल की स्थिति।

In [74]: t3.shape 
Out[74]: torch.Size([3, 6])
                        |
In [75]: t3.stride()    |
Out[75]: (6, 1)         |
          |_____________|

यह है क्योंकि:

In [76]: t3 
Out[76]: 
tensor([[ 0,  1,  2,  3,  4,  5],
        [ 6,  7,  8,  9, 10, 11],
        [12, 13, 14, 15, 16, 17]])

स्ट्राइड का (6, 1)कहना है कि 0 एल आयाम के साथ एक तत्व से अगले तत्व तक जाने के लिए , हमें कूदना होगा या 6 कदम उठाने होंगे। (यानी से जाने के लिए 0करने के लिए 6, एक 6 कदम उठाने के लिए है।) लेकिन एक तत्व से 1 में अगले तत्व पर जाने के लिए सेंट आयाम है, हम बस केवल एक कदम की जरूरत है (उदाहरण के लिए से जाने के लिए 2करने के लिए3 )।

इस प्रकार, गणना की जानकारी इस बात के दिल में है कि गणना करने के लिए मेमोरी से तत्वों को कैसे एक्सेस किया जाता है।


torch.reshape ()

यह फ़ंक्शन एक दृश्य लौटाएगा और बिल्कुल वैसा ही होगा torch.Tensor.view()जैसा कि नया आकार मूल टेंसर के आकार के साथ संगत है। अन्यथा, यह एक प्रति लौटाएगा।

हालाँकि, torch.reshape()चेतावनी के नोट :

संगत आवृत्तियों के साथ सन्निहित इनपुट और आदानों को कॉपी किए बिना फिर से आकार दिया जा सकता है, लेकिन किसी को नकल बनाम देखने के व्यवहार पर निर्भर नहीं होना चाहिए।


1

मुझे लगा कि x.view(-1, 16 * 5 * 5)यह बराबर है x.flatten(1), जहां पैरामीटर 1 इंगित करता है कि सपाट प्रक्रिया 1 आयाम से शुरू होती है ('नमूना' आयाम को समतल नहीं करता है) जैसा कि आप देख सकते हैं, बाद का उपयोग शब्दार्थ रूप से अधिक स्पष्ट और उपयोग करने में आसान है, इसलिए मैं पसंद करते हैं flatten()


1

पैरामीटर -1 का अर्थ क्या है?

आप -1मापदंडों या "कुछ भी" की गतिशील संख्या के रूप में पढ़ सकते हैं । उसके कारण केवल एक पैरामीटर हो सकता -1है view()

यदि आप पूछते हैं कि x.view(-1,1)यह [anything, 1]तत्वों की संख्या के आधार पर टेंसर आकार का उत्पादन करेगा x। उदाहरण के लिए:

import torch
x = torch.tensor([1, 2, 3, 4])
print(x,x.shape)
print("...")
print(x.view(-1,1), x.view(-1,1).shape)
print(x.view(1,-1), x.view(1,-1).shape)

उत्पादन होगा:

tensor([1, 2, 3, 4]) torch.Size([4])
...
tensor([[1],
        [2],
        [3],
        [4]]) torch.Size([4, 1])
tensor([[1, 2, 3, 4]]) torch.Size([1, 4])

1

weights.reshape(a, b) आकार के साथ वजन (ए, बी) के समान डेटा के साथ एक नया टेंसर लौटाएगा क्योंकि यह डेटा को मेमोरी के दूसरे हिस्से में कॉपी करता है।

weights.resize_(a, b)एक अलग आकार के साथ एक ही तन्यता देता है। हालांकि, यदि नया आकार मूल टेंसर की तुलना में कम तत्वों में परिणत होता है, तो कुछ तत्व टैंसर (लेकिन मेमोरी से नहीं) से हटा दिए जाएंगे। यदि नया आकार मूल टेंसर की तुलना में अधिक तत्वों में परिणत होता है, तो नए तत्वों को स्मृति में असमान किया जाएगा।

weights.view(a, b) आकार के साथ वज़न के समान डेटा के साथ एक नया टेंसर लौटाएगा (ए, बी)


0

मुझे वास्तव में @ जैडिल डी अरामा उदाहरण पसंद आया।

मैं एक छोटी जानकारी जोड़ना चाहता हूं कि कैसे .view (...) के लिए तत्वों का आदेश दिया जाता है।

  • आकार (ए, बी, सी) के साथ एक सेंसर के लिए , इसके तत्वों का क्रम एक नंबरिंग प्रणाली द्वारा निर्धारित किया जाता है: जहां पहले अंक में एक संख्या होती है, दूसरे अंक में बी संख्या होती है और तीसरे अंक में सी होता है संख्या होती है।
  • नए Tensor में तत्वों की मैपिंग .view (...) द्वारा लौटाए गए मूल Tensor के इस क्रम को बनाए रखती है ।

0

आइए निम्नलिखित उदाहरणों को देखने की कोशिश करें:

    a=torch.range(1,16)

print(a)

    tensor([ 1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9., 10., 11., 12., 13., 14.,
            15., 16.])

print(a.view(-1,2))

    tensor([[ 1.,  2.],
            [ 3.,  4.],
            [ 5.,  6.],
            [ 7.,  8.],
            [ 9., 10.],
            [11., 12.],
            [13., 14.],
            [15., 16.]])

print(a.view(2,-1,4))   #3d tensor

    tensor([[[ 1.,  2.,  3.,  4.],
             [ 5.,  6.,  7.,  8.]],

            [[ 9., 10., 11., 12.],
             [13., 14., 15., 16.]]])
print(a.view(2,-1,2))

    tensor([[[ 1.,  2.],
             [ 3.,  4.],
             [ 5.,  6.],
             [ 7.,  8.]],

            [[ 9., 10.],
             [11., 12.],
             [13., 14.],
             [15., 16.]]])

print(a.view(4,-1,2))

    tensor([[[ 1.,  2.],
             [ 3.,  4.]],

            [[ 5.,  6.],
             [ 7.,  8.]],

            [[ 9., 10.],
             [11., 12.]],

            [[13., 14.],
             [15., 16.]]])

-1 के रूप में तर्क मान x के मान की गणना करने का एक आसान तरीका है बशर्ते कि हम 3 डी के मामले में y, z या दूसरे तरीके के मानों को जानते हों और 2d के लिए फिर से कहे गए x के मान की गणना करने का एक आसान तरीका प्रदान किया जाए। y या इसके विपरीत के मूल्यों को जानें ।।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.