اندیس گذاری در پایتورچ
در این جلسه از دوره آموزش پایتورچ رایگان، میخواهیم به آموزش اسلایسینگ و اندیس گذاری در پایتورچ بپردازیم. تسلط بر دو مفهوم اسلایسینگ و اندیس گذاری در پایتورچ، به شما قابلیت انجام کارهای پیچیده با تنسورها در پایتورچ را میدهد. با هوسم همراه باشید…
مقدمهای بر اسلایسینگ و اندیس گذاری در پایتورچ
اندیس گذاری و اسلایسنیگ دو تکنیک بسیار مهم برای انتخاب یک درایه یا بخشی از تنسور هستند. این تکنیکها چندان جدید نیستند و احتمالا بسیاری از شما قبلا در ریاضیات یا کدنویسی با آنها آشنا شدهاید. بههمین دلیل، نمیخواهیم وارد توضیحات پایهای در مورد اندیس گذاری و اسلایسنیگ شویم. اما چنانچه، اطلاعات زیادی در مورد این دو تکنیک ندارید، پیشنهاد میکنیم جلسه اسلایسینگ و اندیس گذاری در متلب را بخوانید. چون بسیار ساده و از پایه درمورد این آنها توضیح دادهایم.
اندیس گذاری در پایتورچ
اندیس گذاری اشاره به یک درایه یا المان در تنسور دارد. بهعنوان مثال، میخواهیم بدانیم در یک تنسور (مثلا تنسور دوبعدی)، مقدار درایه (5,3) چقدر است. ابعاد تنسور اهمیتی ندارد و میتوان مقدار درایههای یکبعدی تا بعدهای بالاتر را با اندیس گذاری مشاهده کرد. ابتدا، برای روشن شدن نحوه اندیس گذاری، بیایید مثال زیر را باهم بررسی کنیم:
>>> a = torch.randn(10) >>> print(a) tensor([-0.5951, -0.8796, 0.6917, 2.5978, -1.3183, -0.4151, 0.4762, 0.2704, -1.0277, 0.4039]) >>> print(a[5]) tensor(-0.4151)
مشاهده میکنید که برای اندیس گذاری در تنسور یکبعدی بالا، تنها از یک عدد در داخل براکت استفاده شده است. بسیار ساده… اما اگر ابعاد تنسور بالاتر رود چه اتفاقی میافتد؟ برای تنسورهای چندبعدی کافی است به نکات ساده زیر دقت کنید:
- همواره برای اسلایسینگ و اندیس گذاری در پایتورچ، از براکت استفاده میشود.
- تعداد اعداد درون یک براکت برای اندیس گذاری، معادل با تعداد بعد یک تنسور است. یعنی برای یک تنسور سهبعدی، باید 3 عدد در براکت قرار دهید. یعنی هربعد، باید بهصورت جداگانه آدرسدهی شود. اندیس گذاری یعنی آدرسدهی!
- اندیسها یا اعداد را با ویرگول از هم جدا کنید.
- اعداد از صفر شروع میشوند.
با همین قواعد بالا، میتوانید اندیس گذاری در هر تنسوری را انجام دهید. بیایید یک مثال دیگر را باهم مرور کنیم. در مثال زیر یک تنسور دوبعدی ساخته و اندیسهای (0,0)، (1,2) و (1,1) را چاپ کردهایم:
>>> a = torch.rand(2, 3) >>> print(a) tensor([[0.3998, 0.1289, 0.5550], [0.4936, 0.3883, 0.4064]]) >>> print(a[0, 0], a[1, 2], a[1, 1]) tensor(0.3998) tensor(0.4064) tensor(0.3883)
در مثال زیر، اندیس گذاری در تنسور سهبعدی را مشاهده میکنید:
a = torch.rand(4, 5, 3) print(a[0, 0, 0], a[2, 3, 1], a[3, 4, 2])
توجه به مثال تنسور سهبعدی بالا دقت کنید؛ وقتی اندیسها از صفر شروع میشوند، برای هربعد، مثلا بعد اول که 4 است، نهایتا تا 3 میتوانید اندیسدهی کنید. یا بعد دوم (5) که معادل تعداد سطرهاست را درنظر بگیرید. برای اندیس گذاری از اعداد 0 تا 4 میتوانید استفاده کنید.
>>> a = torch.randint(0, 100, (8, 2, 5, 4))
>>>
>>> print(a[0, 0, 0, 0], a[7, 1, 4, 3], a[2, 1, 4, 1], a[6, 0, 2, 2])
tensor(3) tensor(40) tensor(42) tensor(68)
نکته در مواردی پیش میآید که بخواهید به اندیس آخر یک بعد اشاره کنید. مثلا در یک تنسور دوبعدی 10×8 میخواهیم به مقدار اندیس (سطر 0 و ستون آخر) (0,9) اشاره کنیم. دراینحالت میتوانید از عدد 1- نیز استفاده کنید. با عدد 1- میتوانید آدرسدهی از آخر را انجام دهید. مثلا اندیس بالا را میتوان بهصورت (1-,0) نیز نوشت.
>>> print(a[-1, -1, -1, -1])
tensor(40)
بسیارخب، بههمین سادگی اندیس گذاری در پایتورچ به پایان رسید. حال باید سراغ اسلایسینگ در پایتورچ برویم. این تکنیک کمی پیچیدهتر است و عملگرهای متنوعی هم در آن استفاده میشوند. همچنین، در کارهای جبری، یادگیری ماشین و یادگیری عمیق بسیار پرکاربرد است.
اسلایسینگ در پایتورچ
اسلایسینگ در پایتورچ، یعنی بخشی از یک تنسور انتخاب شود. درواقع، یک تکه از کیک را ببریم و برداریم! این حالت، مقابل حالت اندیس گذاری است که در آنجا تنها یک نقطه، درایه یا المان را انتخاب میکردیم. اما دراینجا میخواهیم یک مجموعه درایه یا المان را انتخاب کنیم. برویم سراغ یک مثال ساده؛ در کد زیر، چهار المان از یک تنسور یکبعدی به طول 10 انتخاب شده است.
>>> a = torch.randn(10) >>> print(a) tensor([-0.5951, -0.8796, 0.6917, 2.5978, -1.3183, -0.4151, 0.4762, 0.2704, -1.0277, 0.4039]) >>> print(a[4:8]) tensor([ 0.3088, -0.5783, 1.1667, -0.1869])
مشاهده میکنید، بازهم از براکت استفاده شده است. دو عدد و یک علامت : هم استفاده شده است. علامت : برای نمایش یک بازه در اسلایسینگ بهکار میرود. هربازهای نیاز به عدد ابتدایی و انتهایی دارد. مثلا [a:b] اشاره به یک بازه دارد که از a شروع میشود و تا b ادامه دارد.
یادآوری در جلسات قبلی هم گفتیم که بازهها در پایتون بهاینصورت است که شامل a میشود اما شامل b خیر…
حال اگر بخواهیم اسلایسینگ را در تنسورهای چندبعدی ببینیم، کافی است بازه را برای هر بعد تنسور تعریف کنیم و هربعد را هم با ویرگول جدا کنیم. چند نمونه مثال ببینیم. در مثال زیر، یک تنسور دوبعدی بهابعاد 5×3 داریم که میخواهیم (سطر صفر تا دو، ستون دوم تا چهارم) آن را انتخاب کنیم:
>>> b = torch.randn(3,5) >>> print(b) tensor([[ 1.2051, 1.1934, -1.2770, 0.5241, -0.9711], [ 0.4834, -1.1279, -0.3279, 0.2349, 0.4098], [ 0.9134, 0.8365, 0.5349, 1.6984, 1.0256]]) >>> print(b[0:2, 2:4]) tensor([[-1.2770, 0.5241], [-0.3279, 0.2349]])
حال میخواهیم (سطر اول تا دوم، ستون اول) را انتخاب کنیم:
>>> print(b[1:2, 1]) tensor([-1.1279])
در مثال زیر، یک تنسور سهبعدی داریم که میخواهیم (صفحه اول تا دوم، سطر سوم تا پنجم، ستون دوم تا چهارم) را انتخاب کنیم.
>>> c = torch.randn(4, 6, 7) >>> print(c[1:2, 3:5, 2:4]) tensor([[[ 1.0819, -1.6137], [-0.9009, 0.2869]]])
حال در تنسور دوبعدی b میخواهیم (سطر دوم، ستون سوم تا آخر) را انتخاب کنیم. میخواهیم ستون آخر هم انتخاب شود. بسیار خب، اگر کد زیر را بنویسیم، ستون آخر انتخاب نمیشود:
>>> print(b[2, 3:4]) tensor([1.6984])
خب آخرین عدد ممکن برای ستونها 4 است ولی با گذاشتن این عدد ستون آخر انتخاب نشد. شاید بهتر باشد بجای عدد آخر، 1- بگذاریم! ببینید:
>>> print(b[2, 3:-1]) tensor([1.6984])
بازهم نشد… بیایید بجای 4 عدد 5 قرار دهیم و نتیجه را ببینیم:
>>> print(b[2, 3:5]) tensor([1.6984, 1.0256])
خب عدد 5 گذاشتیم و نهتنها خطا نداد، بلکه جواب مورد انتظار را هم گرفتیم. اما کار راحتتری هم میتوانیم انجام دهیم. دراینحالت که انتهای بازه ندارید، خب شما هم عددی وارد نکنید! یعنی کد زیر:
>>> print(b[2, 3:]) tensor([1.6984, 1.0256])
کد بالا یعنی، از ستون سوم تا آخر… حال بیایید برای یک تنسور یکبعدی از المان دوم تا آخر را انتخاب کنیم:
>>> a = torch.rand(5) >>> print(a) tensor([0.8211, 0.6227, 0.7089, 0.6312, 0.8277]) >>> print(a[2:]) tensor([0.7089, 0.6312, 0.8277])
حال، بیایید بازه را برعکس کنیم. از ابتدا تا المان چهارم… بسیار ساده است و بهصورت زیر حاصل میشود:
>>> print(a[0:4]) tensor([0.8211, 0.6227, 0.7089, 0.6312])
اما بازهم اگر از ابتدا شروع میشود، یعنی ابتدای بازه ندارید. پس میتوانید تنبلی کنید و عدد ابتدای بازه را ننویسید. یعنی به شکل زیر:
>>> print(a[:4]) tensor([0.8211, 0.6227, 0.7089, 0.6312])
>>> c = torch.randn(4, 5, 6)
>>> print(c[2:, 3:4, 1:3])
tensor([[[-1.2612, -0.5005]],
[[-0.2332, 0.9402]]])
میخواهیم در تنسور دوبعدی b، سطر دوم را انتخاب کنیم. دراینحالت، چه باید بنویسیم؟ اصلا برای ستون هیچ قید و شرطی نگذاشتهایم. یعنی بنویسیم:
>>> print(b[2, 0:5]) tensor([0.9134, 0.8365, 0.5349, 1.6984, 1.0256]) >>> print(b[2, 0:]) tensor([0.9134, 0.8365, 0.5349, 1.6984, 1.0256])
بله، کد بالا درست کار میکند. اما وقتی روی یک بعد (بعد ستون در بالا) هیچ قید و شرطی نداریم، بهتراست اصلا عددی هم برای ابتدا و انتهای بازه وارد نکنیم و بنویسیم:
>>> print(b[2, :]) tensor([0.9134, 0.8365, 0.5349, 1.6984, 1.0256])
بله، اگر با یک بعد در تنسور کاری ندارید، کافی است تنها : بگذارید.
حالا یک تنسور چهاربعدی را درنظر بگیرید. میخواهیم فقط ستون سوم را انتخاب کنیم و بقیه بعدها کامل حضور داشته باشند. بنابراین، باید بنویسیم:
>>> d = torch.randint(0, 1000, (10, 5, 8, 3)) >>> e = d[:, :, :, 2] >>> print(e.shape) torch.Size([10, 5, 8])
خب، ساده بود. اما میبینید که چندبار از : استفاده کردهایم. دراین حالت هم یک میانبر برای تنبلها داریم. از … استفاده کنید. ببینید:
>>> e = d[..., 2] >>> print(e.shape) torch.Size([10, 5, 8])
زمانیکه به چند بعد پشت سرهم کاری ندارید، کافی است از … استفاده کنید.
>>> f = d[..., 2, :]
>>> print(f.shape)
torch.Size([10, 5, 3])
تا اینجا آموختید که چگونه یک سطر، ستون، صفحه را انتخاب کنید. یک تنسور دو بعدی را درنظر بگیرید. میخواهیم ستونهای صفر، دو و چهار را انتخاب کنیم. یعنی چند ستون را میخواهیم. چه باید کنیم؟ میتوانیم کد زیر را بنویسید:
>>> a = torch.rand(4, 6) >>> print(a) tensor([[0.2891, 0.6018, 0.5932, 0.7537, 0.3142, 0.9050], [0.4362, 0.5825, 0.9217, 0.3075, 0.1240, 0.3947], [0.9747, 0.0743, 0.3372, 0.6876, 0.0248, 0.0022], [0.9254, 0.5736, 0.9657, 0.6508, 0.1614, 0.5609]]) >>> print(a[:, [0, 2, 4]]) tensor([[0.2891, 0.5932, 0.3142], [0.4362, 0.9217, 0.1240], [0.9747, 0.3372, 0.0248], [0.9254, 0.9657, 0.1614]])
بنابراین، اگر در یک بعد چند نمونه (مثلا چند ستون یا سطر دلخواه) را خواستیم، کافی است یک لیست با براکت [] بسازیم و درون آن اندیسهای مدنظرمان را قرار دهیم.
>>> c = torch.rand(5, 4, 3)
>>> print(c[3, 1, [0, -1]])
tensor([0.3888, 0.9681])
تا اینجا، با عملگرهای : ، … و لیست اندیسها به شکلهای مختلف آشنا شدید. درادامه، میخواهیم عملگر جدیدی را معرفی کنیم. با آخرین قسمت از جلسه اسلایسینگ و اندیس گذاری در پایتورچ همراه باشید…
در یکی از مثالهای بالا گفتیم که بیایید ستونهای صفر و دو و چهار را انتخاب کنیم. دقت کنید، اندیس ستونها دوتا دوتا افزایش یافته است. برای اینکار میتوانید از :: استفاده کنید. :: معادل با بازه همراه با گام بزرگتر از 1 است. عددی که در سمت راست مینویسید، نشاندهنده گام است. مثلا عبارت 2::0، یعنی از 0، با گام 2 برو جلو. حال بیایید، این تکنیک را روی تنسورمان پیاده کنیم:
>>> a = torch.rand(4, 6) >>> print(a[:, 0::2]) tensor([[0.3906, 0.5119, 0.1454], [0.8949, 0.8278, 0.4501], [0.8427, 0.0814, 0.7554], [0.4338, 0.7335, 0.3218]])
بسیار خب، جلسه پنجم آموزش پایتورچ رایگان هم به پایان رسید. در این جلسه، سعی کردیم تمامی عملگرها و تکنیکها را با مثال و تمرین آموزش دادیم. همچنان در بخش نامپای از پایتورچ هستیم. خوب تمرین کنید و با هوسم همراه باشید.
دیدگاهتان را بنویسید