PyTorch - सन्निहित ()


95

मैं जीथब (लिंक) पर एक LSTM भाषा मॉडल के इस उदाहरण से गुजर रहा था । यह सामान्य रूप से मेरे लिए बहुत स्पष्ट है। लेकिन मैं अभी भी समझने में संघर्ष कर रहा हूं कि कॉलिंग क्या contiguous()करता है, जो कोड में कई बार होता है।

उदाहरण के लिए, लाइन इनपुट के 74/75 लाइन में और LSTM के लक्ष्य अनुक्रम बनाए जाते हैं। डेटा (इसमें संग्रहीत ids) 2-आयामी है जहां पहला आयाम बैच आकार है।

for i in range(0, ids.size(1) - seq_length, seq_length):
    # Get batch inputs and targets
    inputs = Variable(ids[:, i:i+seq_length])
    targets = Variable(ids[:, (i+1):(i+1)+seq_length].contiguous())

तो एक साधारण उदाहरण के रूप में, बैच का आकार 1 और का उपयोग करते समय seq_length10 inputsऔर targetsइस प्रकार है:

inputs Variable containing:
0     1     2     3     4     5     6     7     8     9
[torch.LongTensor of size 1x10]

targets Variable containing:
1     2     3     4     5     6     7     8     9    10
[torch.LongTensor of size 1x10]

तो सामान्य तौर पर मेरा सवाल यह है contiguous()कि मुझे क्या करना चाहिए और इसकी आवश्यकता क्यों है?

इसके अलावा, मुझे समझ में नहीं आता कि लक्ष्य अनुक्रम के लिए विधि क्यों कहा जाता है, लेकिन इनपुट अनुक्रम नहीं है क्योंकि दोनों चर एक ही डेटा के शामिल हैं।

कैसे targetsबेकाबू हो सकता है और inputsअभी भी सन्निहित हो सकता है?

संपादित करें: मैंने कॉलिंग छोड़ने की कोशिश की contiguous(), लेकिन इससे नुकसान की गणना करते समय त्रुटि संदेश जाता है।

RuntimeError: invalid argument 1: input is not contiguous at .../src/torch/lib/TH/generic/THTensor.c:231

तो जाहिर contiguous()है इस उदाहरण में कॉल करना आवश्यक है।

(इस पठनीय को बनाए रखने के लिए मैंने यहां पूरा कोड पोस्ट करने से परहेज किया, यह ऊपर GitHub लिंक का उपयोग करके पाया जा सकता है।)

अग्रिम में धन्यवाद!


एक अधिक वर्णनात्मक शीर्षक उपयोगी होगा। मेरा सुझाव है कि आप शीर्षक में सुधार करें या कम से कम एक tldr; to the point summaryबिंदु सारांश के साथ लिखें ।
चार्ली पार्कर

क्रॉस-पोस्टेड: quora.com/unanswered/…
चार्ली पार्कर

मंच से एक उत्तर: चर्चा करें। pytorch.org/t/contigious-vs-non-contigious-tensor/30107/…
चार्ली पार्कर

जवाबों:


194

PyTorch में Tensor पर कुछ ऐसे ऑपरेशन हैं जो वास्तव में टेंसर की सामग्री को नहीं बदलते हैं, लेकिन केवल इंडिकेटर्स को टेंसर में बाइट स्थान पर कैसे परिवर्तित करें। इन कार्यों में शामिल हैं:

narrow(), view(), expand()औरtranspose()

उदाहरण के लिए: जब आप कॉल करते हैं transpose(), PyTorch नए लेआउट के साथ नया टेंसर उत्पन्न नहीं करता है, यह सिर्फ Tensor ऑब्जेक्ट में मेटा जानकारी को संशोधित करता है इसलिए नए आकार के लिए ऑफसेट और स्ट्राइड होते हैं। ट्रांसपोज़्ड टेंसर और ओरिजिनल टेंसर वास्तव में मेमोरी साझा कर रहे हैं!

x = torch.randn(3,2)
y = torch.transpose(x, 0, 1)
x[0, 0] = 42
print(y[0,0])
# prints 42

यह वह जगह है जहाँ सन्निहित की अवधारणा आती है। ऊपर xसन्निहित है, लेकिन yऐसा नहीं है क्योंकि इसकी मेमोरी लेआउट खरोंच से बने समान आकार के दसियों से अलग है। ध्यान दें कि "सन्निहित" शब्द थोड़ा भ्रामक है क्योंकि इसकी नहीं है कि टेंसर की सामग्री मेमोरी के डिस्कनेक्ट किए गए ब्लॉकों के आसपास फैली हुई है। यहां बाइट्स को अभी भी मेमोरी के एक ब्लॉक में आवंटित किया गया है लेकिन तत्वों का क्रम अलग है!

जब आप कॉल करते हैं contiguous(), तो यह वास्तव में टेंसर की एक प्रति बनाता है, इसलिए तत्वों का क्रम वैसा ही होगा जैसे कि स्क्रैच से बनाया गया एक ही आकार का टेंसर।

आम तौर पर आपको इस बारे में चिंता करने की जरूरत नहीं है। यदि PyTorch को सन्निहित दशांश की उम्मीद है, लेकिन यदि ऐसा नहीं है, तो आप प्राप्त करेंगे RuntimeError: input is not contiguousऔर फिर आप बस एक कॉल जोड़ेंगे contiguous()


मैं बस फिर से इस पार आ गया। आपकी व्याख्या बहुत अच्छी है! मुझे बस आश्चर्य है: यदि मेमोरी में ब्लॉक व्यापक रूप से फैले हुए नहीं हैं, तो मेमोरी लेआउट के साथ समस्या क्या है जो "खरोंच से बने समान आकार के दसियों से अलग है" ? कुछ कार्यों के लिए केवल आवश्यकता क्यों है?
एमबीटी

4
मैं निश्चित रूप से इसका उत्तर नहीं दे सकता, लेकिन मेरा अनुमान है कि कुछ PyTorch कोड C ++ में कार्यान्वित किए गए कार्यों के उच्च निष्पादन वेक्टराइज़्ड कार्यान्वयन का उपयोग करता है और यह कोड Tensor की मेटा जानकारी में निर्दिष्ट मनमाने ढंग से ऑफ़सेट / स्ट्राइड्स का उपयोग नहीं कर सकता है। हालांकि यह सिर्फ एक अनुमान है।
शीतल शाह

1
कैली केवल contiguous()अपने आप से कॉल क्यों नहीं कर सकती ?
information_interchange

बहुत संभव है, क्योंकि आप इसे एक सन्निहित तरीके से नहीं चाहते हैं, और जो आप करते हैं उस पर नियंत्रण रखना हमेशा अच्छा होता है।
shivam13juna

2
एक अन्य लोकप्रिय टेंसर ऑपरेशन है permute, जो गैर-"सन्निहित" टेंसर को भी लौटा सकता है।
ओलेग

32

[Pytorch प्रलेखन] से [1]:

contiguous () → टेन्सर

Returns a contiguous tensor containing the same data as self 

टेन्सर। यदि स्व टेंसर सन्निहित है, तो यह फ़ंक्शन स्व टेंसर को लौटाता है।

जहाँ contiguousयहाँ का अर्थ केवल स्मृति में सन्निहित नहीं है, बल्कि स्मृति में भी उसी क्रम में है जैसे सूचकांकों का क्रम: उदाहरण के लिए एक ट्रांसपोज़ेशन करने से मेमोरी में डेटा नहीं बदलता है, यह केवल सूचकांकों से मेमोरी पॉइंटर्स में मैप बदलता है, यदि आप तब contiguous()इसे लागू करें, यह डेटा को स्मृति में बदल देगा ताकि सूचकांकों से स्मृति स्थान तक का नक्शा विहित हो। [१]: http://pytorch.org/docs/master/tensors.html


1
आपके उत्तर के लिए धन्यवाद! क्या आप मुझे बता सकते हैं कि मुझे सन्निहित होने के लिए डेटा की आवश्यकता क्यों / कब है? क्या यह सिर्फ प्रदर्शन है, या कोई और कारण है? क्या PyTorch को कुछ ऑपरेशन के लिए सन्निहित डेटा की आवश्यकता होती है? लक्ष्य को सन्निहित होने की आवश्यकता क्यों है और इनपुट नहीं हैं?
MBT

यह सिर्फ प्रदर्शन के लिए है। मुझे नहीं पता कि क्यों कोड लक्ष्य के लिए करता है, लेकिन इनपुट के लिए नहीं।
patapouf_ai

2
तो जाहिर तौर पर पाइरॉच को स्मृति में मौजूद होने के नुकसान में लक्ष्य की आवश्यकता होती है, लेकिन न्यूरलनेट के इनपुट को इस आवश्यकता को पूरा करने की आवश्यकता नहीं है।
patapouf_ai

2
बहुत बहुत धन्यवाद! मुझे लगता है कि यह मेरे लिए समझ में आता है, मैंने देखा है कि आगे के फ़ंक्शन में सन्निहित () आउटपुट डेटा (जो निश्चित रूप से पूर्व में इनपुट था) पर भी लागू होता है, इसलिए नुकसान की गणना करते समय आउटपुट और लक्ष्य दोनों सन्निहित हैं। आपका बहुत बहुत धन्यवाद!
एमबीटी

1
@patapouf_ai नहीं। आपकी यह व्याख्या गलत है। जैसा कि सही उत्तर में बताया गया है, यह स्मृति के सन्निहित ब्लॉकों के बारे में बिल्कुल नहीं है।
आकिस्तफे 7

14

टेंसोर।कंटिगस () टेंसर की एक प्रति बनाएगा, और कॉपी में तत्व को सन्निहित तरीके से मेमोरी में संग्रहीत किया जाएगा। सन्निहित () फ़ंक्शन की आवश्यकता आमतौर पर तब होती है जब हम पहली बार एक तानवाला () और फिर इसे फिर से देखते हैं। सबसे पहले, आइए एक सन्निहित दशांश बनाएँ:

aaa = torch.Tensor( [[1,2,3],[4,5,6]] )
print(aaa.stride())
print(aaa.is_contiguous())
#(3,1)
#True

स्ट्राइड () रिटर्न (3,1) का मतलब है कि: प्रत्येक चरण (पंक्ति द्वारा पंक्ति) द्वारा पहले आयाम के साथ चलते समय, हमें मेमोरी में 3 चरणों को स्थानांतरित करने की आवश्यकता होती है। दूसरे आयाम (स्तंभ द्वारा स्तंभ) के साथ आगे बढ़ने पर, हमें मेमोरी में 1 चरण स्थानांतरित करने की आवश्यकता है। यह इंगित करता है कि दशांश में तत्व संचित रूप से संग्रहीत होते हैं।

अब हम टेंसर पर आने वाले कार्यों को लागू करने की कोशिश करते हैं:

bbb = aaa.transpose(0,1)
print(bbb.stride())
print(bbb.is_contiguous())

#(1, 3)
#False


ccc = aaa.narrow(1,1,2)   ## equivalent to matrix slicing aaa[:,1:3]
print(ccc.stride())
print(ccc.is_contiguous())

#(3, 1)
#False


ddd = aaa.repeat(2,1)   # The first dimension repeat once, the second dimension repeat twice
print(ddd.stride())
print(ddd.is_contiguous())

#(3, 1)
#True


## expand is different from repeat.
## if a tensor has a shape [d1,d2,1], it can only be expanded using "expand(d1,d2,d3)", which
## means the singleton dimension is repeated d3 times
eee = aaa.unsqueeze(2).expand(2,3,3)
print(eee.stride())
print(eee.is_contiguous())

#(3, 1, 0)
#False


fff = aaa.unsqueeze(2).repeat(1,1,8).view(2,-1,2)
print(fff.stride())
print(fff.is_contiguous())

#(24, 2, 1)
#True

ठीक है, हम पा सकते हैं कि संक्रमण (), संकीर्ण () और टेन्सर स्लाइसिंग, और विस्तार () जेनरेट किए गए टेंसर को सन्निहित नहीं बना देगा। दिलचस्प है, दोहराना () और दृश्य () यह असम्बद्ध नहीं करता है। तो अब सवाल यह है कि अगर मैं एक असंगत टेंसर का उपयोग करूँ तो क्या होगा?

इसका उत्तर यह है कि दृश्य () फ़ंक्शन एक असंगत टेंसर पर लागू नहीं किया जा सकता है। यह शायद इसलिए है क्योंकि दृश्य () के लिए आवश्यक है कि टेंसर को संचित रूप से संग्रहीत किया जाए ताकि यह स्मृति में तेजी से फेरबदल कर सके। उदाहरण के लिए:

bbb.view(-1,3)

हमें त्रुटि मिलेगी:

---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
<ipython-input-63-eec5319b0ac5> in <module>()
----> 1 bbb.view(-1,3)

RuntimeError: invalid argument 2: view size is not compatible with input tensor's size and stride (at least one dimension spans across two contiguous subspaces). Call .contiguous() before .view(). at /pytorch/aten/src/TH/generic/THTensor.cpp:203

इसे हल करने के लिए, एक असंगत टेंसर में केवल सन्निहित () जोड़ें, सन्निहित प्रतिलिपि बनाने के लिए और फिर दृश्य () लागू करें

bbb.contiguous().view(-1,3)
#tensor([[1., 4., 2.],
        [5., 3., 6.]])

10

जैसा कि पिछले उत्तर में सन्निहित () सन्निकट मेमोरी चंक्स आवंटित करता है , यह तब मददगार होगा जब हम c या c ++ बैकएंड कोड के लिए टेंसर पास कर रहे हों, जहाँ टेंसर्स पॉइंटर्स के रूप में पास किए जाते हैं


3

स्वीकार किए गए उत्तर बहुत शानदार थे, और मैंने transpose()फ़ंक्शन प्रभाव को डुबाने की कोशिश की । मैंने ऐसे दो फ़ंक्शंस बनाए जो samestorage()और जाँच सकते हैं contiguous

def samestorage(x,y):
    if x.storage().data_ptr()==y.storage().data_ptr():
        print("same storage")
    else:
        print("different storage")
def contiguous(y):
    if True==y.is_contiguous():
        print("contiguous")
    else:
        print("non contiguous")

मैंने जाँच की और यह परिणाम तालिका के रूप में मिला:

कार्यों

आप नीचे दिए गए चेकर कोड की समीक्षा कर सकते हैं, लेकिन आइए एक उदाहरण देते हैं जब टेंसर गैर सन्निहित है । हम view()उस टेंसर पर सरल कॉल नहीं कर सकते , हमें इसकी आवश्यकता होगी reshape()या हम कॉल भी कर सकते हैं .contiguous().view()

x = torch.randn(3,2)
y = x.transpose(0, 1)
y.view(6) # 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.
  
x = torch.randn(3,2)
y = x.transpose(0, 1)
y.reshape(6)

x = torch.randn(3,2)
y = x.transpose(0, 1)
y.contiguous().view(6)

आगे ध्यान दें कि ऐसी विधियाँ हैं जो अंत में सन्निहित और गैर सन्निहित टॉन्सर्स बनाती हैं। ऐसे तरीके हैं जो एक ही भंडारण पर काम कर सकते हैं , और कुछ तरीके flip()जो एक नया स्टोरेज बनाएंगे (वापसी के पहले टॉनर पढ़ें)।

चेकर कोड:

import torch
x = torch.randn(3,2)
y = x.transpose(0, 1) # flips two axes
print("\ntranspose")
print(x)
print(y)
contiguous(y)
samestorage(x,y)

print("\nnarrow")
x = torch.randn(3,2)
y = x.narrow(0, 1, 2) #dim, start, len  
print(x)
print(y)
contiguous(y)
samestorage(x,y)

print("\npermute")
x = torch.randn(3,2)
y = x.permute(1, 0) # sets the axis order
print(x)
print(y)
contiguous(y)
samestorage(x,y)

print("\nview")
x = torch.randn(3,2)
y=x.view(2,3)
print(x)
print(y)
contiguous(y)
samestorage(x,y)

print("\nreshape")
x = torch.randn(3,2)
y = x.reshape(6,1)
print(x)
print(y)
contiguous(y)
samestorage(x,y)

print("\nflip")
x = torch.randn(3,2)
y = x.flip(0)
print(x)
print(y)
contiguous(y)
samestorage(x,y)

print("\nexpand")
x = torch.randn(3,2)
y = x.expand(2,-1,-1)
print(x)
print(y)
contiguous(y)
samestorage(x,y) 

0

मैं इसे और अधिक संक्षेप में समझने वाले उत्तर से:

Contiguous वह शब्द है जिसका उपयोग यह दर्शाने के लिए किया जाता है कि किसी टेंसर का मेमोरी लेआउट उसके विज्ञापित मेटा-डेटा या आकार की जानकारी के साथ संरेखित नहीं होता है।

मेरी राय में शब्द सन्निहित एक भ्रामक / भ्रामक शब्द है क्योंकि सामान्य संदर्भों में इसका मतलब है कि जब डिस्कनेक्ट किए गए ब्लॉकों में मेमोरी चारों ओर फैली नहीं होती है (अर्थात इसके "सन्निहित / जुड़े / निरंतर")।

कुछ ऑपरेशनों को किसी कारणवश (जीपीयू आदि में सबसे अधिक संभावना दक्षता) के लिए इस सन्निहित संपत्ति की आवश्यकता हो सकती है।

ध्यान दें कि .viewएक और ऑपरेशन है जो इस समस्या का कारण हो सकता है। निम्नलिखित कोड को देखें, जिसे मैंने केवल सन्निहित कहा है (ठेठ पारगमन के मुद्दे के बजाय यह यहां कारण है कि एक उदाहरण है जब आरएनएन अपने इनपुट से खुश नहीं है)

        # normal lstm([loss, grad_prep, train_err]) = lstm(xn)
        n_learner_params = xn_lstm.size(1)
        (lstmh, lstmc) = hs[0] # previous hx from first (standard) lstm i.e. lstm_hx = (lstmh, lstmc) = hs[0]
        if lstmh.size(1) != xn_lstm.size(1): # only true when prev lstm_hx is equal to decoder/controllers hx
            # make sure that h, c from decoder/controller has the right size to go into the meta-optimizer
            expand_size = torch.Size([1,n_learner_params,self.lstm.hidden_size])
            lstmh, lstmc = lstmh.squeeze(0).expand(expand_size).contiguous(), lstmc.squeeze(0).expand(expand_size).contiguous()
        lstm_out, (lstmh, lstmc) = self.lstm(input=xn_lstm, hx=(lstmh, lstmc))

त्रुटि जो मुझे मिलती थी:

RuntimeError: rnn: hx is not contiguous


/ संसाधन सूत्रों का कहना है:

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