আমরা পাইটর্কের ক্রমগুলি কেন "প্যাক" করব?


93

আমি আরএনএন-র জন্য ভেরিয়েবল-লেংথ সিক্যুয়েন্স ইনপুটগুলির জন্য প্যাকিং কীভাবে ব্যবহার করব তার প্রতিলিপি দেওয়ার চেষ্টা করছিলাম তবে আমার ধারণা আমি প্রথমে কেন অনুক্রমটি "প্যাক" করতে হবে তা আমার বুঝতে হবে।

আমি বুঝতে পারি যে তাদের কেন আমাদের "প্যাড" করা দরকার তবে কেন "প্যাকিং" (এর মাধ্যমে pack_padded_sequence) প্রয়োজনীয়?

উচ্চ স্তরের যে কোনও ব্যাখ্যা প্রশংসিত হবে!


: pytorch মধ্যে বোঁচকা সম্পর্কে সকল প্রশ্নের discuss.pytorch.org/t/...
চার্লি পার্কার

উত্তর:


88

আমি এই সমস্যাটিতেও হোঁচট খেয়েছি এবং নীচে আমি বুঝতে পেরেছি।

আরএনএন (এলএসটিএম বা জিআরইউ বা ভ্যানিলা-আরএনএন) প্রশিক্ষণ দেওয়ার সময়, পরিবর্তনশীল দৈর্ঘ্যের ক্রমগুলি ব্যাচ করা কঠিন difficult উদাহরণস্বরূপ: যদি একটি 8 টি ব্যাচের সিকোয়েন্সগুলির দৈর্ঘ্য [4,6,8,5,4,3,7,8] হয় তবে আপনি সমস্ত সিকোয়েন্স প্যাড করবেন এবং এর ফলে 8 টি দৈর্ঘ্যের অনুক্রম হবে 8 আপনি comp৪ টি গণনা (8x8) করে শেষ হবে তবে আপনাকে কেবল 45 টি গণনা করতে হবে। তদুপরি, আপনি যদি দ্বিদলীয়-আরএনএন ব্যবহার করার মতো অভিনব কিছু করতে চান, তবে কেবল প্যাডিংয়ের মাধ্যমে ব্যাচের গণনাগুলি করা আরও কঠিন এবং আপনি প্রয়োজনের চেয়ে আরও বেশি কম্পিউটিং করতে পারেন।

পরিবর্তে, পাইটর্চ আমাদের সিকোয়েন্সটি প্যাক করতে দেয়, অভ্যন্তরীণভাবে প্যাক করা সিকোয়েন্সটি দুটি তালিকার একটি অংশ। একটিতে অনুক্রমের উপাদান রয়েছে elements উপাদানগুলি সময় ধাপে আন্তঃবিভক্ত হয় (নীচের উদাহরণ দেখুন) এবং অন্যান্য প্রতিটি ধাপে প্রতিটি ক্রমের আকার ধারণ করে ব্যাচের আকার। এটি প্রতিটি ধাপে ব্যাচের আকার কী তা আরএনএনকে জানানোর পাশাপাশি প্রকৃত অনুক্রমগুলি পুনরুদ্ধারে সহায়ক। এটি @ আয়ারিন দ্বারা চিহ্নিত করা হয়েছে। এটি আরএনএন-এ দেওয়া যেতে পারে এবং এটি অভ্যন্তরীণভাবে গণনাগুলি অনুকূল করে তুলবে।

আমি কিছু পর্যায়ে অস্পষ্ট হতে পারে, সুতরাং আমাকে জানান এবং আমি আরও ব্যাখ্যা যুক্ত করতে পারেন।

এখানে একটি কোড উদাহরণ:

 a = [torch.tensor([1,2,3]), torch.tensor([3,4])]
 b = torch.nn.utils.rnn.pad_sequence(a, batch_first=True)
 >>>>
 tensor([[ 1,  2,  3],
    [ 3,  4,  0]])
 torch.nn.utils.rnn.pack_padded_sequence(b, batch_first=True, lengths=[3,2])
 >>>>PackedSequence(data=tensor([ 1,  3,  2,  4,  3]), batch_sizes=tensor([ 2,  2,  1]))

4
আপনি কি ব্যাখ্যা করতে পারবেন যে প্রদত্ত উদাহরণের আউটপুটটি প্যাকড সেকেন্স (ডেটা = টেনসর ([1, 3, 2, 4, 3]), ব্যাচ_সাইজ = টেনসর ([2, 2, 1]))?
ascetic652

4
ডেটা অংশটি সমস্ত অক্ষরগুলি সময় অক্ষের সাথে একত্রিত হয়। ব্যাচ_সাইজ আসলে প্রতিটি সময় পদক্ষেপে ব্যাচের আকারের অ্যারে।
উমং গুপ্ত

4
ব্যাচ_ সাইজ = [2, 2, 1] যথাক্রমে [1, 3] [2, 4] এবং [3] গ্রুপিং উপস্থাপন করে।
চৈতন্য শিবাদে

@ চৈতন্যশিবাদে ব্যাচের আকার [২,২,১] কেন? এটি [1,2,2] হতে পারে না? এর পিছনে যুক্তি কী?
বেনামে প্রোগ্রামার

4
কারণ পদক্ষেপ টিতে, আপনি কেবল ধাপে টি তে ভেক্টরগুলি প্রক্রিয়া করতে পারেন, আপনি যদি ভেক্টরকে [১,২,২] হিসাবে অর্ডার করে থাকেন তবে আপনি সম্ভবত প্রতিটি ইনপুট একটি ব্যাচ হিসাবে রেখে দিচ্ছেন, তবে এটি সমান্তরাল হতে পারে না এবং তাই ব্যাচযোগ্য নয়
উমং গুপ্ত

51

এখানে কিছু ভিজ্যুয়াল ব্যাখ্যা 1 রয়েছে যা এর কার্যকারিতার জন্য আরও ভাল স্বীকৃতি বিকাশে সহায়তা করতে পারেpack_padded_sequence()

ধরে নেওয়া যাক আমাদের 6মোট ক্রমগুলি (চলক দৈর্ঘ্যের) রয়েছে। আপনি এই সংখ্যাটিকে হাইপারপ্যারামিটার 6হিসাবে বিবেচনা করতে পারেন batch_size

এখন, আমরা এই ক্রমগুলি কয়েকটি পুনরাবৃত্ত নিউরাল নেটওয়ার্ক আর্কিটেকচার (গুলি) এ পাস করতে চাই। এটি করার জন্য, 0আমাদের ব্যাচের সমস্ত সিকোয়েন্সগুলি (সাধারণত s এর সাথে) আমাদের ব্যাচে সর্বাধিক সিকোয়েন্স দৈর্ঘ্যের ( max(sequence_lengths)) এর প্যাড করতে হবে , যা নীচের চিত্রটিতে 9

প্যাডড-সিক্স

সুতরাং, ডেটা তৈরির কাজটি এখনই শেষ করা উচিত, তাই না? আসলেই নয় .. কারণ এখনও একটি চাপ সমস্যা রয়েছে, মূলত যখন প্রয়োজনীয় প্রয়োজনীয় সংখ্যার তুলনা করা হয় তখন আমাদের কতটা গণনা করতে হবে।

বোঝার স্বার্থে, ধরে নেওয়া যাক আমরা padded_batch_of_sequencesআকারের (6, 9)ওজন ম্যাট্রিক্সের Wসাহায্যে আকারের উপরের অংশটি মেট্রিক্স করব (9, 3)

সুতরাং, আমাদের 6x9 = 54গুণিতকরণ এবং 6x8 = 48সংযোজন                     ( nrows x (n-1)_cols) অপারেশন করতে হবে, কেবলমাত্র বেশিরভাগ গণ্য ফলাফলগুলি ফেলে দিতে হবে কারণ সেগুলি হবে 0(যেখানে আমাদের প্যাড রয়েছে)। এই ক্ষেত্রে প্রকৃত প্রয়োজনীয় গণনা নিম্নরূপ:

 9-mult  8-add 
 8-mult  7-add 
 6-mult  5-add 
 4-mult  3-add 
 3-mult  2-add 
 2-mult  1-add
---------------
32-mult  26-add
   
------------------------------  
#savings: 22-mult & 22-add ops  
          (32-54)  (26-48) 

এটি খুব সাধারণ ( খেলনা ) উদাহরণের জন্য এমনকি অনেক বেশি সঞ্চয় । আপনি এখন কল্পনা করতে পারেন যে pack_padded_sequence()কয়েক মিলিয়ন এন্ট্রি সহ বড় টেনার ব্যবহার করে এবং সারা বিশ্বে মিলিয়ন + সিস্টেমগুলি বারবার ব্যবহার করে কত পরিমান (শেষ পর্যন্ত ব্যয়, শক্তি, সময়, কার্বন নিঃসরণ ইত্যাদি) সংরক্ষণ করা যায়।

pack_padded_sequence()ব্যবহৃত রঙ-কোডিংয়ের সাহায্যে এর কার্যকারিতা নীচের চিত্র থেকে বোঝা যাবে:

প্যাক প্যাডড-সেকস

ব্যবহারের ফলস্বরূপ pack_padded_sequence(), আমরা উপরের উদাহরণের জন্য sequences(ii) সমতল (অক্ষ -1 বরাবর, উপরের চিত্রে) , (ii) সংশ্লিষ্ট ব্যাচের মাপযুক্ত টেনারগুলির একটি টুপল পাব tensor([6,6,5,4,3,3,2,2,1])

ডেটা টেনসর (অর্থাত্ চ্যাপ্টা সিকোয়েন্সগুলি) লোকসানের গণনার জন্য ক্রসএন্ট্রপির মতো উদ্দেশ্যমূলক ফাংশনে পৌঁছে দেওয়া যেতে পারে।


@ Sgrvinod- তে 1 টি চিত্র ক্রেডিট


4
দুর্দান্ত চিত্র!
ডেভিড ওয়াটারওয়ার্থ

4
সম্পাদনা: আমার মনে হয় স্ট্যাকওভারফ্লো.com / a / 55805785 / 6167850 (নীচে) আমার প্রশ্নের উত্তর দেয় যা আমি যেভাবেই এখানে ছেড়ে দেব: this এর মূলত গ্রেডিয়েন্টগুলি প্যাডযুক্ত ইনপুটগুলিতে প্রচার করা হয়নি? আমার ক্ষতির ফাংশনটি কেবল আরএনএন-এর চূড়ান্ত লুকানো অবস্থার / আউটপুটটিতে গণনা করা হলে কী হবে? দক্ষতা লাভ কি তখন ফেলে দেওয়া উচিত? অথবা প্যাডিং শুরু হওয়ার আগেই কী পদক্ষেপটি থেকে ক্ষতি গণনা করা হবে, যা এই উদাহরণে প্রতিটি ব্যাচের উপাদানগুলির জন্য পৃথক? ~
এনএলএমএল

25

উপরের উত্তরগুলি কেন খুব ভালভাবে প্রশ্নটিকে সম্বোধন করেছিল । এর ব্যবহারটি আরও ভাল করে বোঝার জন্য আমি একটি উদাহরণ যুক্ত করতে চাই pack_padded_sequence

একটি উদাহরণ নেওয়া যাক

দ্রষ্টব্য: pack_padded_sequenceব্যাচে (ক্রম দৈর্ঘ্যের অবতরণ ক্রমে) সাজানো ক্রমগুলি দরকার। নীচের উদাহরণে, সিকোয়েন্স ব্যাচটি ইতিমধ্যে কম বিশৃঙ্খলার জন্য বাছাই করা হয়েছিল। পরিদর্শন এই সারকথা লিংক পূর্ণ বাস্তবায়নের।

প্রথমত, আমরা নীচের মতো বিভিন্ন সিকোয়েন্স দৈর্ঘ্যের 2 টি সিকোয়েন্সের একটি ব্যাচ তৈরি করি। ব্যাচে সম্পূর্ণরূপে আমাদের কাছে 7 টি উপাদান রয়েছে।

  • প্রতিটি অনুক্রমের আকার 2 এম্বেডিং থাকে।
  • প্রথম অনুক্রমের দৈর্ঘ্য রয়েছে: 5
  • দ্বিতীয় ক্রমের দৈর্ঘ্য রয়েছে: 2
import torch 

seq_batch = [torch.tensor([[1, 1],
                           [2, 2],
                           [3, 3],
                           [4, 4],
                           [5, 5]]),
             torch.tensor([[10, 10],
                           [20, 20]])]

seq_lens = [5, 2]

আমরা seq_batchসমান দৈর্ঘ্য 5 (ব্যাচের সর্বোচ্চ দৈর্ঘ্য) সহ সিকোয়েন্সগুলির ব্যাচ পেতে প্যাড করি । এখন, নতুন ব্যাচে সম্পূর্ণরূপে 10 টি উপাদান রয়েছে।

# pad the seq_batch
padded_seq_batch = torch.nn.utils.rnn.pad_sequence(seq_batch, batch_first=True)
"""
>>>padded_seq_batch
tensor([[[ 1,  1],
         [ 2,  2],
         [ 3,  3],
         [ 4,  4],
         [ 5,  5]],

        [[10, 10],
         [20, 20],
         [ 0,  0],
         [ 0,  0],
         [ 0,  0]]])
"""

তারপরে, আমরা প্যাক padded_seq_batch। এটি দুটি টেনারগুলির একটি দ্বিগুণ ফিরিয়ে দেয়:

  • প্রথমটি হচ্ছে সিকোয়েন্স ব্যাচের সমস্ত উপাদান সহ ডেটা।
  • দ্বিতীয়টি হ'ল batch_sizesযা পদক্ষেপগুলি দ্বারা একে অপরের সাথে সম্পর্কিত উপাদানগুলি বলবে।
# pack the padded_seq_batch
packed_seq_batch = torch.nn.utils.rnn.pack_padded_sequence(padded_seq_batch, lengths=seq_lens, batch_first=True)
"""
>>> packed_seq_batch
PackedSequence(
   data=tensor([[ 1,  1],
                [10, 10],
                [ 2,  2],
                [20, 20],
                [ 3,  3],
                [ 4,  4],
                [ 5,  5]]), 
   batch_sizes=tensor([2, 2, 1, 1, 1]))
"""

এখন, আমরা packed_seq_batchপাইটরঞ্চের পুনরাবৃত্ত মডিউলগুলিতে যেমন টিপলটি পাস করি, যেমন আরএনএন, এলএসটিএম। এটি কেবল 5 + 2=7পুনরাবৃত্ত মডিউলটিতে গণনা প্রয়োজন ।

lstm = nn.LSTM(input_size=2, hidden_size=3, batch_first=True)
output, (hn, cn) = lstm(packed_seq_batch.float()) # pass float tensor instead long tensor.
"""
>>> output # PackedSequence
PackedSequence(data=tensor(
        [[-3.6256e-02,  1.5403e-01,  1.6556e-02],
         [-6.3486e-05,  4.0227e-03,  1.2513e-01],
         [-5.3134e-02,  1.6058e-01,  2.0192e-01],
         [-4.3123e-05,  2.3017e-05,  1.4112e-01],
         [-5.9372e-02,  1.0934e-01,  4.1991e-01],
         [-6.0768e-02,  7.0689e-02,  5.9374e-01],
         [-6.0125e-02,  4.6476e-02,  7.1243e-01]], grad_fn=<CatBackward>), batch_sizes=tensor([2, 2, 1, 1, 1]))

>>>hn
tensor([[[-6.0125e-02,  4.6476e-02,  7.1243e-01],
         [-4.3123e-05,  2.3017e-05,  1.4112e-01]]], grad_fn=<StackBackward>),
>>>cn
tensor([[[-1.8826e-01,  5.8109e-02,  1.2209e+00],
         [-2.2475e-04,  2.3041e-05,  1.4254e-01]]], grad_fn=<StackBackward>)))
"""

আমাদের outputআউটপুটের প্যাডেড ব্যাচে ফিরে যেতে হবে:

padded_output, output_lens = torch.nn.utils.rnn.pad_packed_sequence(output, batch_first=True, total_length=5)
"""
>>> padded_output
tensor([[[-3.6256e-02,  1.5403e-01,  1.6556e-02],
         [-5.3134e-02,  1.6058e-01,  2.0192e-01],
         [-5.9372e-02,  1.0934e-01,  4.1991e-01],
         [-6.0768e-02,  7.0689e-02,  5.9374e-01],
         [-6.0125e-02,  4.6476e-02,  7.1243e-01]],

        [[-6.3486e-05,  4.0227e-03,  1.2513e-01],
         [-4.3123e-05,  2.3017e-05,  1.4112e-01],
         [ 0.0000e+00,  0.0000e+00,  0.0000e+00],
         [ 0.0000e+00,  0.0000e+00,  0.0000e+00],
         [ 0.0000e+00,  0.0000e+00,  0.0000e+00]]],
       grad_fn=<TransposeBackward0>)

>>> output_lens
tensor([5, 2])
"""

এই প্রচেষ্টাটির সাথে স্ট্যান্ডার্ড উপায়ে তুলনা করুন

  1. স্ট্যান্ডার্ড উপায়ে, আমাদের কেবলমাত্র মডিউলটি পাস করতে padded_seq_batchহবে lstm। তবে এর জন্য 10 টি গণনা প্রয়োজন। এটি প্যাডিং উপাদানগুলিতে আরও কয়েকটি গণনা জড়িত যা গণনাগতভাবে অক্ষম হবে।

  2. দ্রষ্টব্য যে এটি ভুল প্রতিনিধিত্বের দিকে পরিচালিত করে না , তবে সঠিক উপস্থাপনাগুলি নিষ্কাশন করতে আরও অনেক যুক্তির প্রয়োজন।

    • কেবলমাত্র সামনের দিকের এলএসটিএম (বা কোনও পুনরাবৃত্ত মডিউল) এর জন্য, যদি আমরা একটি সিক্যুয়েন্সের জন্য উপস্থাপনা হিসাবে শেষ ধাপের লুকানো ভেক্টরটি বের করতে চাই, আমাদের টি (ম) ধাপ থেকে লুকানো ভেক্টরগুলি বেছে নিতে হবে, যেখানে টি ইনপুট দৈর্ঘ্য হয়। শেষ উপস্থাপনা বাছাই ভুল হবে। নোট করুন যে ব্যাচের বিভিন্ন ইনপুটগুলির জন্য টি আলাদা হবে।
    • দ্বি-নির্দেশমূলক এলএসটিএম (বা যে কোনও পুনরাবৃত্ত মডিউল) এর জন্য এটি আরও বেশি জটিল is কারণ একজনকে দুটি আরএনএন মডিউল বজায় রাখতে হবে, একটি যা ইনপুটটির শুরুতে প্যাডিংয়ের সাথে কাজ করে এবং একটি ইনপুটটির শেষে প্যাডিং সহ, এবং উপরে বর্ণিত হিসাবে অবশেষে লুকানো ভেক্টরগুলি নিষ্কাশন করা এবং সংযুক্ত করা।

আসুন পার্থক্যটি দেখুন:

# The standard approach: using padding batch for recurrent modules
output, (hn, cn) = lstm(padded_seq_batch.float())
"""
>>> output
 tensor([[[-3.6256e-02, 1.5403e-01, 1.6556e-02],
          [-5.3134e-02, 1.6058e-01, 2.0192e-01],
          [-5.9372e-02, 1.0934e-01, 4.1991e-01],
          [-6.0768e-02, 7.0689e-02, 5.9374e-01],
          [-6.0125e-02, 4.6476e-02, 7.1243e-01]],

         [[-6.3486e-05, 4.0227e-03, 1.2513e-01],
          [-4.3123e-05, 2.3017e-05, 1.4112e-01],
          [-4.1217e-02, 1.0726e-01, -1.2697e-01],
          [-7.7770e-02, 1.5477e-01, -2.2911e-01],
          [-9.9957e-02, 1.7440e-01, -2.7972e-01]]],
        grad_fn= < TransposeBackward0 >)

>>> hn
tensor([[[-0.0601, 0.0465, 0.7124],
         [-0.1000, 0.1744, -0.2797]]], grad_fn= < StackBackward >),

>>> cn
tensor([[[-0.1883, 0.0581, 1.2209],
         [-0.2531, 0.3600, -0.4141]]], grad_fn= < StackBackward >))
"""

উপরের ফলাফলগুলি দেখায় যে hn, cnদুটি উপায়ে পৃথক এবং দুটি উপায়ে outputপ্যাডিং উপাদানগুলির জন্য পৃথক মান নিয়ে যায়।


4
চমৎকার উত্তর! আপনি যদি প্যাডিং করেন কেবল একটি সংশোধন করে আপনার ইনপুট দৈর্ঘ্যের সমান সূচকটিতে শেষ এইচ টি ব্যবহার করা উচিত নয়। এছাড়াও, দ্বি নির্দেশমূলক আরএনএন করতে আপনি দুটি ভিন্ন আরএনএন ব্যবহার করতে চান --- একটি সঠিক প্যাড সহ সামনের প্যাডিং এবং অন্যটি প্যাডিং সহ সঠিক ফলাফল পেতে get প্যাডিং এবং শেষ আউটপুট বাছাই করা "ভুল"। সুতরাং আপনার যুক্তি যে এটি ভুল প্রতিনিধিত্বের দিকে পরিচালিত করে তা ভুল। প্যাডিংয়ের সমস্যাটি হ'ল এটি সঠিক তবে অদক্ষ (যদি প্যাকড সিকোয়েন্সগুলির বিকল্প থাকে) এবং এটি জটিল হতে পারে (উদাহরণস্বরূপ: দ্বি-দির আরএনএন)
উমং গুপ্ত

18

উমংয়ের উত্তরে যুক্ত করে আমি এটি গুরুত্বপূর্ণ মনে করেছি।

প্রদত্ত টুপল এর প্রথম আইটেমটি pack_padded_sequenceহ'ল ডেটা (টেনসর) - প্যাকড সিকোয়েন্সযুক্ত টেনসর। দ্বিতীয় আইটেমটি প্রতিটি ক্রম ধাপে ব্যাচের আকার সম্পর্কে তথ্য ধারণ করে পূর্ণসংখ্যার একটি সেন্সর।

এখানে গুরুত্বপূর্ণ যা তবে দ্বিতীয় আইটেমটি (ব্যাচের আকারগুলি) ব্যাচের প্রতিটি ক্রম ধাপে উপাদানগুলির সংখ্যা উপস্থাপন করে, পরিবর্তিত অনুক্রমের দৈর্ঘ্যটি পাস হয় না pack_padded_sequence

উদাহরণস্বরূপ, প্রদত্ত ডেটা abcএবং x : শ্রেণি: সহ PackedSequenceডেটা থাকবে ।axbcbatch_sizes=[2,1,1]


4
ধন্যবাদ, আমি পুরোপুরি ভুলে গেছি এবং আমার উত্তরটিতে এটি আপডেট করতে গিয়ে একটি ভুল করেছে। তবে, সিক্যুয়েন্সগুলি পুনরুদ্ধার করার জন্য প্রয়োজনীয় কিছু ডেটা হিসাবে আমি দ্বিতীয় সিক্যুয়েন্সটির দিকে চেয়েছিলাম এবং সে কারণেই আমার বিবরণটি
গণ্ডগোল করেছে

2

আমি নীচে প্যাক প্যাডেড সিকোয়েন্স ব্যবহার করেছি।

packed_embedded = nn.utils.rnn.pack_padded_sequence(seq, text_lengths)
packed_output, hidden = self.rnn(packed_embedded)

যেখানে পাঠ্য-দৈর্ঘ্য পৃথক ক্রমের দৈর্ঘ্য প্যাডিং এবং ক্রম একটি নির্দিষ্ট ব্যাচের মধ্যে দৈর্ঘ্যের ক্রমহ্রাসমান ক্রম অনুসারে বাছাই করা হয়।

আপনি এখানে একটি উদাহরণ চেক করতে পারেন ।

এবং আমরা প্যাকিং করি যাতে আরএনএন ক্রম প্রক্রিয়া করার সময় অযাচিত প্যাডড সূচক দেখতে না পায় যা সামগ্রিক কর্মক্ষমতাকে প্রভাবিত করে।

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