পাইটর্চ - সংলগ্ন ()


93

আমি গিথুব (লিঙ্ক) এ একটি LSTM ভাষার মডেলের এই উদাহরণটি দিয়ে যাচ্ছিলাম । এটি সাধারণভাবে যা করে তা আমার কাছে বেশ পরিষ্কার। তবে আমি কলিং কী করে contiguous()তা বোঝার জন্য এখনও লড়াই করছি , যা কোডে বেশ কয়েকবার আসে।

উদাহরণস্বরূপ, এলএসটিএম এর কোড ইনপুট এবং টার্গেট সিকোয়েন্সগুলির লাইন /৪/75৫ তৈরি করা হয়েছে। ডেটা (সঞ্চিত 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()এই উদাহরণে কল করা প্রয়োজনীয়।

(এই পঠনযোগ্যটি ধরে রাখার জন্য আমি এখানে পূর্ণ কোড পোস্ট করা এড়িয়ে গিয়েছি, এটি উপরের গিটহাব লিঙ্কটি ব্যবহার করে পাওয়া যাবে))

আগাম ধন্যবাদ!


আরও বর্ণনামূলক শিরোনাম দরকারী হবে। আমি আপনাকে শিরোনাম উন্নত করার বা কমপক্ষে tldr; to the point summaryবিন্দুর সংক্ষিপ্তসারকে সংক্ষিপ্ত করে একটি লেখার পরামর্শ দিচ্ছি ।
চার্লি পার্কার


ফোরামের একটি উত্তর: আলোচনা. pytorch.org/t/contigious-vs-non-contigious-tensor/30107/…
চার্লি পার্কার

উত্তর:


193

পাইটর্চে টেনসরের কয়েকটি অপারেশন রয়েছে যা প্রকৃতপক্ষে টেনসরের সামগ্রী পরিবর্তন করে না, তবে কেবলমাত্র কীভাবে সূচকগুলিকে টেনসারে বাইট স্থানে রূপান্তর করা যায়। এই অপারেশনগুলির মধ্যে রয়েছে:

narrow(), view(), expand()এবংtranspose()

উদাহরণস্বরূপ: আপনি যখন কল করবেন তখন transpose()পাইটর্চ নতুন লেআউট সহ নতুন টেন্সর তৈরি করে না, এটি কেবল টেনসর অবজেক্টে মেটা তথ্য সংশোধন করে তাই অফসেট এবং স্ট্রাইড নতুন আকারের জন্য। ট্রান্সপোজড টেনসর এবং আসল টেনসর সত্যিই মেমরি ভাগ করে নিচ্ছে!

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

এখান থেকেই সংঘবদ্ধ ধারণাটি আসে xove উপরের মত সংলগ্ন তবে yএর স্মৃতি বিন্যাসটি স্ক্র্যাচ থেকে তৈরি একই আকারের সেন্সরের চেয়ে পৃথক। নোট করুন যে "সংঘবদ্ধ" শব্দটি কিছুটা বিভ্রান্তিকর কারণ এটি নয় যে টেমসরের সামগ্রীটি মেমরির সংযোগ বিচ্ছিন্ন ব্লকের চারদিকে ছড়িয়ে পড়ে। এখানে বাইটগুলি এখনও মেমরির একটি ব্লকে বরাদ্দ করা হয় তবে উপাদানগুলির ক্রমটি আলাদা!

আপনি যখন কল করবেন তখন contiguous()এটি প্রকৃতপক্ষে টেনসারের একটি অনুলিপি তৈরি করে যাতে উপাদানগুলির ক্রম একই রকম হয় যেন স্ক্র্যাচ থেকে একই আকারের টেনসর তৈরি হয়েছিল।

সাধারণত আপনার এই সম্পর্কে চিন্তা করার দরকার নেই। পাইটর্চ যদি সুসংগত টেনসরের আশা করে তবে এটি যদি না হয় তবে আপনি পাবেন RuntimeError: input is not contiguousএবং তারপরে আপনি কেবল একটি কল যুক্ত করুন contiguous()


আমি আবার এই জুড়ে এসেছি। আপনার ব্যাখ্যা খুব ভাল! আমি কেবল অবাক: মেমরির ব্লকগুলি যদি ব্যাপকভাবে ছড়িয়ে না যায় তবে "স্ক্র্যাচ থেকে তৈরি একই আকারের টেনসারের চেয়ে আলাদা" মেমরির লেআউটটিতে কী সমস্যা ? কেন কিছু ক্রিয়াকলাপের জন্য কেবল সংগত প্রয়োজন?
এমবিটি

4
আমি অবশ্যই এর উত্তর দিতে পারি না তবে আমার অনুমান যে পাইটর্চ কোডের কয়েকটি সি ++ এ প্রয়োগ করা অপারেশনগুলির উচ্চ কার্যকারিতা ভেক্টরাইজড প্রয়োগকরণ ব্যবহার করে এবং এই কোডটি টেনসরের মেটা তথ্যে উল্লিখিত নির্বিচারে অফসেট / স্ট্রাইড ব্যবহার করতে পারে না। যদিও এটি একটি অনুমান মাত্র।
শীতল শাহ

4
কলি কেবল contiguous()নিজেই ফোন করতে পারল না কেন ?
information_interchange

সম্ভবত সম্ভবত, কারণ আপনি এটি একটি সান্নিধ্যপূর্ণ উপায়ে চান না এবং আপনি যা করেন তার উপর নিয়ন্ত্রণ রাখা সর্বদা সুন্দর।
shivam13juna

4
আরেকটি জনপ্রিয় টেনসর অপারেশন হ'ল এটি permute"অবিচ্ছিন্ন" টেন্সরও ফিরে আসতে পারে।
ওলেগ

32

[পাইটর্চ ডকুমেন্টেশন] [1] থেকে:

সংলগ্ন () ens টেনসর

Returns a contiguous tensor containing the same data as self 

টেনসর যদি স্ব-টেন্সরটি সামঞ্জস্যপূর্ণ হয় তবে এই ফাংশনটি স্ব-টেন্সরটি দেয়।

কোথায় contiguousএখানে মানে মেমরি না শুধুমাত্র সংলগ্ন, কিন্তু মেমরি একই আদেশ হিসাবে সূচকের অর্ডার: উদাহরণস্বরূপ একটি পক্ষান্তরণ মেমরি তথ্য পরিবর্তন করে না ফলে জন্য, এটা শুধু মেমরি পয়েন্টার থেকে সূচকের থেকে মানচিত্র পরিবর্তন, যদি আপনি তারপর contiguous()এটি প্রয়োগ করুন এটি মেমরিতে ডেটা পরিবর্তন করবে যাতে সূচকগুলি থেকে মেমরির অবস্থানের মানচিত্রটি ক্যানোনিকাল। [1]: http://pytorch.org/docs/master/tensors.html


4
আপনার উত্তর করার জন্য আপনাকে ধন্যবাদ! আপনি আমাকে বলতে পারেন কেন / যখন আমার কাছে ডেটা সংমিত হওয়ার প্রয়োজন হয়? এটি কি কেবল পারফরম্যান্স, না অন্য কোনও কারণ? পাইটর্চের কিছু ক্রিয়াকলাপের জন্য কি সংগত ডেটা প্রয়োজন? লক্ষ্যগুলি সংগত এবং ইনপুটগুলি কেন হওয়া দরকার?
এমবিটি

এটি কেবল পারফরম্যান্সের জন্য। আমি জানি না কেন কোডগুলি লক্ষ্যগুলির জন্য এটি করে তবে ইনপুটগুলির জন্য নয়।
patapouf_ai

4
সুতরাং আপাতদৃষ্টিতে পাইটর্কের জন্য ক্ষতির লক্ষ্যগুলি প্রয়োজন মেমরিতে সংশ্লেষপূর্ণ হওয়া দরকার, তবে নিউরালনেটের ইনপুটগুলি এই প্রয়োজনীয়তাটি পূরণ করার প্রয়োজন হয় না।
patapouf_ai

4
দুর্দান্ত ধন্যবাদ! আমি মনে করি এটি আমার কাছে বোধগম্য হয়েছে, আমি লক্ষ্য করেছি যে সামঞ্জস্যপূর্ণ () এছাড়াও ফরোয়ার্ড ফাংশনে আউটপুট ডেটার (যা অবশ্যই পূর্বে ইনপুট ছিল) প্রয়োগ করা হয়, সুতরাং ক্ষতির গণনা করার সময় উভয় আউটপুট এবং লক্ষ্যগুলি সংগত হয়। অনেক ধন্যবাদ!
এমবিটি

4
@ পাটপাউফ_ইই না। আপনার ব্যাখ্যাটি ভুল। সঠিক উত্তরে নির্দেশিত হিসাবে, এটি মোটেও মেমরির সংকীর্ণ ব্লকগুলি সম্পর্কে নয়।
আকাইস্টেফ 7

14

tensor.contiguous () সেন্সরটির একটি অনুলিপি তৈরি করবে এবং অনুলিপিটিতে থাকা উপাদানটি স্মৃতিতে একটি সামঞ্জস্যভাবে সংরক্ষণ করা হবে। যখন আমরা প্রথমে একটি টেনসর () একটি ট্রান্সপোর্ট করি এবং তারপরে এটি পুনরায় আকার দিন (দেখুন) তখন সংযোগযুক্ত () ফাংশনটি সাধারণত প্রয়োজন হয়। প্রথমে আসুন একটি স্বচ্ছ টেন্সর তৈরি করুন:

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

স্ট্রাইড () রিটার্ন (3,1) এর অর্থ হ'ল: যখন প্রতিটি ধাপ (সারি সারি) প্রথম মাত্রা বরাবর সরানো হয় তখন আমাদের স্মৃতিতে 3 টি পদক্ষেপ সরিয়ে নেওয়া দরকার। দ্বিতীয় মাত্রা (কলাম অনুসারে কলাম) নিয়ে যাওয়ার সময়, আমাদের মেমরির 1 ধাপ এগিয়ে যেতে হবে move এটি ইঙ্গিত দেয় যে টেনসরের উপাদানগুলি স্বচ্ছভাবে সংরক্ষণ করা হয়।

এখন আমরা টেনসরে কম ফাংশন প্রয়োগ করার চেষ্টা করব:

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

পূর্ববর্তী উত্তরের মতো (জটিল) মেমরি খণ্ডগুলি বরাদ্দ করে , এটি কার্যকর হবে যখন আমরা সি বা সি ++ ব্যাকএন্ড কোড যেখানে টেনারগুলি পয়েন্টার হিসাবে পাস হয় সেখানে সেন্সর পাস করি


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

আমি যা বুঝতে পারি তার থেকে আরও সংক্ষিপ্ত উত্তর:

সংক্ষিপ্ত শব্দটি এটি ব্যবহার করতে ব্যবহৃত শব্দটি ব্যবহৃত হয় যা কোনও টেন্সরের মেমরি লেআউটটি তার বিজ্ঞাপনীকৃত মেটা-ডেটা বা আকারের তথ্যের সাথে একত্রিত হয় না।

আমার মতে সংক্ষিপ্ত শব্দটি একটি বিভ্রান্তিকর / বিভ্রান্তিমূলক শব্দ, যেহেতু সাধারণ প্রসঙ্গে এটির অর্থ যখন সংযোগ বিচ্ছিন্ন ব্লকগুলিতে স্মৃতিচারণ না হয় (অর্থাত্ এটি "সংলগ্ন / সংযুক্ত / ধারাবাহিক")।

কিছু ক্রিয়াকলাপের জন্য কোনও কারণে এই সামঞ্জস্যপূর্ণ সম্পত্তি প্রয়োজন হতে পারে (জিপিইউ ইত্যাদির মধ্যে সম্ভবত দক্ষতা)।

দ্রষ্টব্য এটি .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


উত্স / সংস্থান:

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.