تنسورهای خاص در پایتورچ
بهنام خدا، سلام… در سومین جلسه آموزش پایتورچ میخواهیم به معرفی انواع تنسورهای خاص در پایتورچ بپردازیم. منظور از تنسورهای خاص ، همان تنسورهای صفر ( zeros )، واحد ( ones )، همانی ( eye ) و سایر موارد مشابه است. از ستون سمت چپ، میتوانید به تمامی جلسات منتشر شده پایتورچ دسترسی داشته باشید. با هوسم همراه باشید…
مقدمهای بر تنسورهای خاص در پایتورچ
در جلسه قبلی با نحوه تعریف تنسور در پایتورچ از صفربعدی تا چندبعدی همراه با مثال و تمرین فراوان آشنا شدید. تمامی اعداد موجود در تنسورها را بهصورت دستی وارد میکردیم. حال میخواهیم در این جلسه با روشهای دیگر ساخت تنسور بدون نیاز به وارد کردن دستی اعداد آشنا شویم. در ریاضیات با این نوع تنسورها با نامهای ماتریس واحد ( ones )، ماتریس صفر ( zeros )، ماتریس تصادفی یا رندوم ( random )، ماتریس همانی ( eye ) و غیره آشنا شدهاید. بجز مبحث اعداد تصادفی، سایر موارد را در این جلسه پوشش خواهیم داد. مبحث تصادفی آنقدر مهم است که میطلبد جلسهای جداگانه برایش بنویسیم.
دستور zeros در پایتورچ
در جلسه قبل آموختیم که برای ساخت یک تنسور از دستور ()torch.tensor میتوان استفاده کرد. مشابه با همین دستور، برای ساخت یک تنسور صفر کافی است از دستور ()torch.zeros استفاده شود. بله، دستور zeros دراینجا بسیار مشابه numpy و متلب است. اتفاقا، هدف پایتورچ هم همین بوده که شما درگیر یادگیری دستورات با نامهای جدید نشوید. اما چگونه یک تنسور صفر بسازیم؟ تنسور صفر ورودیهای مختلفی دارد. اما سادهترین حالت تعریف یک تنسور صفر این است که سایز تنسور را مشخص کنیم. مثلا بگوییم یک تنسور صفر با ابعاد 3×5×8 میخواهیم.
با دستور ()torch.zeros میتوانیم تنسور از یک تا چندبعدی بسازیم. مثلا با دستور زیر یک تنسور یک بعدی ساختهایم:
>>> zeros1d = torch.zeros(1) >>> print(zeros1d) tensor([0.])
خب، در ورودی دستور zeros گفتهایم که تنها شامل یک عدد باشد. اما دقت کنید، خروجی دستور بالا یک اسکالر یا تنسور صفربعدی نیست! یک براکت [] دارد، پس تنسور یک بعدی است. نتیجه بالا را پایین مقایسه کنید:
>>> zeros0d = torch.tensor(0.) >>> print(zeros0d) tensor(0.)
پس نتیجه اینکه، با استفاده از دستور zeros نمیتوان یک اسکالر صفر ساخت. اسکالر یا تنسور صفربعدی با مقدار صفر را با دستور ()torch.tensor باید بسازیم. اگر یک بردار با طول بزرگتر میخواهید، کافی است بنویسید:
>>> zeros1d = torch.zeros(5) >>> print(zeros1d) tensor([0., 0., 0., 0., 0.])
برای ساخت یک تنسور دوبعدی (همان ماتریس) کافی است بنویسیم:
>>> zeros2d = torch.zeros(2, 3) >>> print(zeros2d) tensor([[0., 0., 0.], [0., 0., 0.]])
خب یک تنسور صفر 3×2 ساختیم. برای ساخت تنسورهای صفر با ابعاد بالاتر هم بهصورت بالا عمل میکنیم. مثلا در کد زیر یک تنسور به ابعاد 5×6×8×3×2 ساختهایم!
>>> zeros5d = torch.zeros(2, 3, 8, 6, 5) >>> print(zeros5d.shape)
خروجی آنقدر زیاد بود که دیگر چاپ نکردیم و به نمایش سایز آن بسنده کردیم. این ماتریس 1440 درایه یا المان دارد!
نکته برای نمایش تعداد درایه یا المان تنسور کافی است از ()numel. استفاده کنید. این هم یکی دیگر از خواص تنسور مانند size یا shape . از ()numel به صورت زیر استفاده کنید:
>>> print(zeros5d.numel()) 1440
>>> zeros3d = torch.zeros(3, 5, 18)
>>> print(zeros3d.shape)
torch.Size([3, 5, 18])
>>> print(zeros3d.numel())
270
آیا دستور ()torch.zeros تنها یک ورودی تعیین سایز دارد؟ خیر، ورودیهای مختلفی دارد که بعضی از آنها را نمیتوانیم در این جلسه معرفی کنیم. اما، بجز سایز بقیه ورودیها دلخواه هستند. ما در اینجا نمیخواهیم همه ورودیهایش را به شما معرفی کنیم و تنها شما را با چند ورودی مهم آشنا خواهیم کرد. ورودی دیگر تنسور صفر در پایتورچ ، دیتاتایپ ( datatype ) است. مثلا ببینید، در کد زیر یک تنسور با دیتاتایپ float 64 بیتی تعریف کردهایم.
>>> zeros2d = torch.zeros(2, 2, dtype=torch.float64) >>> print(zeros2d) tensor([[0., 0.], [0., 0.]], dtype=torch.float64)
به چند نکته دقت کنید؛ نکته اول، برای اینکه ورودیها منظمتر و دستهبندیشده باشد، بهتر است سایز را در یک tuple یا همان پرانتز قرار دهید. ببینید:
>>> zeros3d = torch.zeros((2, 2, 80), torch.float64)
نکته دوم، برای وارد کردن دیتاتایپ حتما باید بنویسید =dtype وگرنه با خطا مواجه خواهید شد. کد زیر را امتحان کنید:
>>> zeros2d = torch.zeros(2, 2, torch.float64) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: zeros() takes 1 positional argument but 2 were given
نکته سوم، وقتی یک دیتاتایپ متفاوت از حالت پیشفرض ( torch.float32 و torch.int64 ) معرفی کنید، همراه با نمایش تنسور، دیتاتایپ آن نیز نمایش داده میشود. در بالا چون دیتاتایپ پیشفرض نگذاشتیم، دیتاتایپ در خروجی ظاهر شده است.
بسیار خب، همین اندازه برای تنسور صفر در پایتورچ کافی است. در جلسههای بعدی برمیگردیم و دو ورودی بسیار مهم دیگر را به شما معرفی خواهیم کرد. فعلا زود است! تمام توضیحاتی که برای تنسور صفر ارائه شد، برای دستورهای زیر هم صادق است. بنابراین، دیگر اینهمه توضیح نمیدهیم و خیلی خلاصه فقط دستورات را معرفی میکنیم.
دستور ones در پایتورچ
یکی دیگر از اعضای خانواده تنسورهای خاص در پایتورچ ، تنسور واحد یا ones است. این تنسور یک تنسور تماما با مقدار 1 است که با استفاده از دستور ones در پایتورچ ساخته میشود. ورودی و خروجی آن شبیه به تنسور صفر است. بیایید چند نمونه مثال ببینیم:
>>> ones1d = torch.ones(4) >>> print(ones1d) tensor([1., 1., 1., 1.]) >>> ones2d = torch.ones(4, 8) >>> print(ones2d) tensor([[1., 1., 1., 1., 1., 1., 1., 1.], [1., 1., 1., 1., 1., 1., 1., 1.], [1., 1., 1., 1., 1., 1., 1., 1.], [1., 1., 1., 1., 1., 1., 1., 1.]]) >>> ones3d = torch.ones(1, 2, 1, dtype=torch.int16) >>> print(ones3d) tensor([[[1], [1]]], dtype=torch.int16)
>>> ones2d = torch.ones((6, 3), dtype=torch.int16)
>>> print(ones2d.shape)
torch.Size([6, 3])
>>> print(ones2d.numel())
18
دستور full در پایتورچ
در دو دستور zeros و ones در پایتورچ توانستیم تنسورهایی با مقادیر تمام صفر و یک بسازیم. اما اگر بخواهیم یک تنسور تماما 5 داشته باشیم، باید چه کنیم؟ خب یک جواب ساده این است که با همان دستورات zeros یا ones و بهرهگیری از ضرب و جمع میسازیم. بله، درست است. اما پایتورچ برای اینکار دستور full را آماده کرده که به شما این امکان را میدهد تا بهراحتی تنسور مدنظرتان را با سایز و مقدار دلخواه ایجاد کنید. این دستور علاوه بر سایز تنسور ، مقدار تنسور را هم از شما میخواهد. کد زیر را ببینید:
>>> full2d = torch.full((3,4), 5) >>> print(full2d) tensor([[5., 5., 5., 5.], [5., 5., 5., 5.], [5., 5., 5., 5.]])
یک تنسور دوبعدی 4×3 که با عدد 5 پر شده است.
>>> full3d = torch.full((3, 5, 18), -0.25)
>>> print(full3d.shape)
torch.Size([3, 5, 18])
>>> print(zeros3d.numel())
270
نحوه تعریف دیتاتایپ در این دستور مشابه با دستورهای بالا است. تمرین زیر را حل کنید…
>>> full1d = torch.full((5,), 10, dtype=torch.int8)
>>> print(full1d)
tensor([10, 10, 10, 10, 10], dtype=torch.int8)
دستور eye در پایتورچ
چهارمین تنسور از خانواده تنسورهای خاص در پایتورچ ، تنسور همانی eye است. همانی یک تنسور با مقادیر صفر و یک است که روی درایههای اصلی آن مقادیر 1 است و سایر درایهها صفر هستند. با دستور eye در پایتورچ میتوانیم تنسور همانی داشته باشیم. این دستور کمی با دستورات قبلی متفاوت است. دراینجا فقط میتوانیم تنسور دوبعدی داشته باشیم. همچنین، پیشفرض این است که تنسور مربعی است (سطر و ستون برابر است). پس این دستور فقط از ما تعداد سطرها را میخواهد. مثلا در کد زیر یک تنسور دوبعدی با ابعاد 5×5 ساختهایم. تنها یک عدد وارد کردهایم که آن هم تعداد سطرهاست.
>>> eye_tensor = torch.eye(5) >>> print(eye_tensor) tensor([[1., 0., 0., 0., 0.], [0., 1., 0., 0., 0.], [0., 0., 1., 0., 0.], [0., 0., 0., 1., 0.], [0., 0., 0., 0., 1.]])
اما میتوانیم تنسور غیرمربعی هم داشته باشیم. ورودی دوم که دلخواه هست، تعداد ستونها را از شما میگیرد. مثلا در زیر یک تنسور 4×5 ساختهایم. در واقع یک تنسور 5×5 داریم که ستون آخرش را حذف کردهایم. ببینید:
>>> eye_tensor = torch.eye(5, 4) >>> print(eye_tensor) tensor([[1., 0., 0., 0.], [0., 1., 0., 0.], [0., 0., 1., 0.], [0., 0., 0., 1.], [0., 0., 0., 0.]])
>>> eye_tensor = torch.eye(3, 5)
>>> print(eye_tensor)
tensor([[1., 0., 0., 0.],
[0., 1., 0., 0.],
[0., 0., 1., 0.]])
از دو بعد بیشتر هم نداریم! اما دیتاتایپ در دستور eye مشابه با دستورات قبلی است. پس این را بهعنوان تمرین به شما میسپاریم…
>>> eye_tensor = torch.eye((2, 2), dtype=torch.int32)
>>> print(eye_tensor)
tensor([[1, 0],
[0, 1]])
دستور arange در پایتورچ
دستور arange در پایتورچ، برای شما یک تنسور یک بعدی میسازد. دستور arange یک مجموعه عدد با گام یا فاصله ثابت (step) در بازه (start, end] ایجاد میکند. درواقع، این دستور یک مجموعه عدد براساس تصاعد حسابی برای شما ایجاد میکند. اما به بازه (start, end] دقت کنید، از بازه بسته start اعداد شروع میشود و تا بازه باز end ادامه دارد. یعنی تنسور یک بعدی خروجی، شامل عدد start هست، اما شامل عدد end نیست. خب سه پارامتر بهنامهای step ، start و end را معرفی کردیم. این سه پارامتر را باید در ورودی دستور arange در پایتورچ وارد کنیم. ورودی اول start، ورودی دوم end و ورودی سوم step هست. در مثال زیر، یک تنسور یک بعدی در بازه (0,10] با گام 1 ساختهایم.
>>> arange_tensor = torch.arange(0, 10, 1) >>> print(arange_tensor) tensor([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
اما کد زیر هم همان جواب بالا را دارد!
>>> arange_tensor = torch.arange(10) >>> print(arange_tensor) tensor([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
در کد بالا، تنها پارامتر end را وارد کردهایم و خبری از start و step نیست. این دو ورودی مقادیر پیشفرض دارند. پیشفرض مقدار start و step بهترتیب برابر با 0 و 1 درنظر گرفته شده است.
در کد زیر، یک تنسور یک بعدی بین بازه (1.5,2.5] با گام 0.1 ساختهایم.
>>> arange_tensor = torch.arange(1.5, 2.5, 0.1)
>>> print(arange_tensor)
tensor([1.5000, 1.6000, 1.7000, 1.8000, 1.9000, 2.0000, 2.1000, 2.2000, 2.3000, 2.4000])
>>> arange_tensor = torch.arange(-0.5, 1.5, 0.33)
>>> print(arange_tensor)
tensor([-0.5000, -0.1700, 0.1600, 0.4900, 0.8200, 1.1500, 1.4800])
>>> print(arange_tensor.numel())
7
>>> arange_tensor = torch.arange(-0.5, 1.5, 0.5, dtype=torch.float64)
>>> print(arange_tensor)
tensor([-0.5000, 0.0000, 0.5000, 1.0000], dtype=torch.float64)
دستور linspace در پایتورچ
دستور linspace در پایتورچ سازوکاری مشابه دستور arange دارد. این دستور هم یک تنسور یک بعدی از اعداد در یک بازه مشخص ایجاد میکند. پس بازه (start, end] در اینجا هم وجود دارد. اما تفاوت این دو دستور بسیار ساده است. در دستور arange با step مشخص میکردیم که فاصله بین اعداد در یک تنسور چقدر باشد. اما در دستور linspace با استفاده از متغیر steps مشخص میکنیم که ما میخواهیم چند عدد در بازه (start, end] داشته باشیم. فاصله بین اعداد ثابت و خطی است. این نکته از نام دستور هم مشخص است، linspace مخفف linear space است. مثلا، ما میخواهیم 10 عدد در بازه (0,1] داشته باشیم. دیگر اینجا تعیین نمیکنیم فاصله بین اعداد چند باشد. بلکه میگوییم در این بازه کلا مثلا 10 عدد میخواهیم. با کد زیر 100 عدد در بازه (0,1] ساختهایم:
>>> linspace_tensor = torch.linspace(0, 1, 100) >>> print(linspace_tensor) tensor([0.0000, 0.0101, 0.0202, 0.0303, 0.0404, 0.0505, 0.0606, 0.0707, 0.0808, 0.0909, 0.1010, 0.1111, 0.1212, 0.1313, 0.1414, 0.1515, 0.1616, 0.1717, 0.1818, 0.1919, 0.2020, 0.2121, 0.2222, 0.2323, 0.2424, 0.2525, 0.2626, 0.2727, 0.2828, 0.2929, 0.3030, 0.3131, 0.3232, 0.3333, 0.3434, 0.3535, 0.3636, 0.3737, 0.3838, 0.3939, 0.4040, 0.4141, 0.4242, 0.4343, 0.4444, 0.4545, 0.4646, 0.4747, 0.4848, 0.4949, 0.5051, 0.5152, 0.5253, 0.5354, 0.5455, 0.5556, 0.5657, 0.5758, 0.5859, 0.5960, 0.6061, 0.6162, 0.6263, 0.6364, 0.6465, 0.6566, 0.6667, 0.6768, 0.6869, 0.6970, 0.7071, 0.7172, 0.7273, 0.7374, 0.7475, 0.7576, 0.7677, 0.7778, 0.7879, 0.7980, 0.8081, 0.8182, 0.8283, 0.8384, 0.8485, 0.8586, 0.8687, 0.8788, 0.8889, 0.8990, 0.9091, 0.9192, 0.9293, 0.9394, 0.9495, 0.9596, 0.9697, 0.9798, 0.9899, 1.0000])
از مجموعه سه ورودی بالا، تنها steps هست که مقدار پیشفرض برابر با 100 دارد. بنابراین، کد بالا را به شکل زیر هم میتوانیم بنویسیم:
>>> linspace_tensor = torch.linspace(0, 1)
>>> linspace_tensor = torch.linspace(50, 125, 75)
>>> linspace_tensor = torch.linspace(50, 125, 75, dtype=torch.int16)
دستور linspace فواصل بین اعداد را خطی درنظر میگرفت. اگر میخواهید فاصله بین اعداد لگاریتمی باشد، میتوانید از دستور logspace در پایتورچ استفاده کنید. ورودیهای این دستور مشابه با دستور linspace است. به همین خاطر کار با این دستور را بهعنوان تمرین به خودتان میسپاریم! برای اطلاعات بیشتر به راهنمای پایتورچ مراجعه کنید (کلیک کنید).
>>> logspace_tensor = torch.logspace(0, 1, 10)
>>> print(logspace_tensor)
tensor([ 1.0000, 1.2915, 1.6681, 2.1544, 2.7826, 3.5938, 4.6416, 5.9948, 7.7426, 10.0000])
>>> linspace_tensor = torch.linspace(0, 1, 10)
>>> print(linspace_tensor)
tensor([0.0000, 0.1111, 0.2222, 0.3333, 0.4444, 0.5556, 0.6667, 0.7778, 0.8889, 1.0000])
>>> logspace_tensor = torch.logspace(0, 1, 10, base=2)
>>> print(logspace_tensor)
tensor([1.0000, 1.0801, 1.1665, 1.2599, 1.3608, 1.4697, 1.5874, 1.7145, 1.8517, 2.0000])
دستور empty در پایتورچ
تاکنون تنسورهایی ساختهایم که شامل مجموعه اعداد با آرایش خاصی بودند. تنسوری با مقادیر تماما صفر، یک، تصاعدی و غیره… حال میخواهیم تنسوری بسازیم که مقدار مشخصی در آن وجود نداشته باشد. ورودیهای دستور empty در پایتورچ بسیار شبیه دستور zeros هست. کافی است که سایز تنسور را تعیین کنید. از یک بعدی تا چند بعدی را میتواند برای شما بسازد. درایهها مقدار مشخصی ندارند و شما نمیتوانید به آنها مقدار دهید. گاهی اعداد خیلی خیلی بزرگ و گاهی هم خیلی خیلی کوچک… به همین خاطر نامش تنسور خالی است! در کدهای زیر یک نمونه تنسور به ابعاد 2×2 ساختهایم:
>>> empty_tensor = torch.empty(2,2) >>> print(empty_tensor) tensor([[0.0000e+00, 0.0000e+00], [1.3036e+22, 4.5914e-41]])
این تنسور هم ورودی دیتاتایپ دارد. در کد زیر یک نمونه تنسور با دیتاتایپ int 8 بیتی ساختهایم:
>>> empty_tensor = torch.empty((2,2), dtype=torch.int8) >>> print(empty_tensor) tensor([[0, 0], [0, 0]], dtype=torch.int8)
>>> empty_tensor = torch.empty((5,5), dtype=torch.float16)
>>> print(empty_tensor)
tensor([[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.]], dtype=torch.float16)
جلسه سوم را هم به پایان رساندیم. تا اینجای دوره، این سنگینتر جلسه بوده که البته با کمی تمرین میتوانید مسلط شوید. بنابراین، خوب تمرین کنید و مثالهای متنوع حل کنید. راستی، یک کوئیز برای شما تا پایان جلسه سوم در نظر گرفتهایم. این کوئیز را در کانال تلگرامی پایتورچ (pytorch_howsam@) قرار دادهایم. میتوانید خود را محک بزنید!
دیدگاهتان را بنویسید