کاربرد جبر خطی در یادگیری عمیق

۱۴۲۶ بازدید
آخرین به‌روزرسانی: ۱۹ تیر ۱۴۰۲
زمان مطالعه: ۷ دقیقه
کاربرد جبر خطی در یادگیری عمیق

برای یادگیری بسیاری از مباحث علمی نیاز به آموختن دانش‌های پایه‌ای مرتبط با آن‌ها است. هوش مصنوعی و یادگیری ماشین از جمله علومی هستند که این روزها علاقمندان زیادی را در بخش‌های صنعت و دانشگاه به خود جذب کرده‌اند. همچون دیگر حوزه‌های دانش، یادگیری این دو مبحث نیز نیازمند آموختن پیش‌نیازهایی است که حتی می‌توان گفت جزئی جدا نشدنی از آن‌ها هستند. شاید اولین زمینه‌ای که یادگیری آن به عنوان پیش‌نیاز و یا هم‌نیاز مبحث هوش مصنوعی به ذهن افراد برسد، زبان‌های برنامه‌نویسی باشد.

اگرچه زبان‌های برنامه‌نویسی (چه محاسباتی و چه اسکریپت‌نویسی) بخش مهمی از مباحث هوش مصنوعی را تشکیل می‌دهند و برای پیاده‌سازی الگوریتم‌ها و مفاهیم این علم مورد استفاده قرار می‌گیرند، اما یادگیری «ریاضیات و جبرخطی» از این‌رو که مبنای ساخت این دانش و دیگر علوم مرتبط با آن بوده بسیار حائز اهمیت و اجتناب‌ناپذیر است. در پس کلیه الگوریتم‌های هوش مصنوعی و مفاهیم این حوزه، ریاضیات و جبر خطی نهفته است. بدون داشتن تسلط کافی بر مفاهیم جبر خطی، فرد قطعا در یادگیری هوش مصنوعی دچار اشکال خواهد شد.

از همین‌رو در بحث یادگیری عمیق، به عنوان یکی از زیرشاخه‌های هوش مصنوعی مرتبط با بحث شبکه‌های عصبی مصنوعی، نیاز به آموختن برخی مفاهیم جبر خطی به ویژه مباحث مرتبط با بردارها، ماتریس‌ها و عملیات روی آن‌ها است. جبرخطی، شکل پیوسته ریاضیات گسسته است که اغلب دانشمندان علوم رایانه تجربه کار با آن را دارند. درک عمیق این علم برای آموختن و کار با الگوریتم‌های یادگیری ماشین الزامی است.

چرا ریاضیات؟

جبر خطی، احتمالات و حساب دیفرانسیل و انتگرال، زبان‌هایی هستند که یادگیری ماشین با استفاده از آن‌ها رابطه‌سازی (فرموله) شده است. یادگیری این مباحث منجر به ایجاد درک عمیقی از ساز و کار الگوریتم‌های موجود و ساخت الگوریتم‌های جدید می‌شود.

در پس الگوریتم‌های یادگیری عمیق نیز مفاهیم جبر خطی نهفته است و بنابراین پیش از یادگیری و پیاده‌سازی آن‌ها با زبان‌های برنامه‌نویسی، باید این مفاهیم را آموخت. اسکالرها، بردارها، ماتریس‌ها و تانسورها، ساختارهای پایه‌ای موجود در یادگیری عمیق را تشکیل می‌دهند. در ادامه هر یک از این مفاهیم توضیح داده می‌شوند.

کمیت

در لغت‌نامه دهخدا «کمیت» را چندی و در مقابل کیفیت یا چونی تعریف کرده‌اند. کمیت در واقع مقدار چیزی است که سنجیده، پیموده یا شمرده شود. به عبارت دیگر، مقدار، اندازه و تعداد چیزی را کمیت آن گویند. کمیت و مقدار دو لفظ مترادف هستند. کمیت‌ها به دو نوع نرده‌ای (اسکالر یا عددی) و برداری تقسیم می‌شوند.

کمیت نرده‌ای

کمیت نرده‌ای یا اسکالر، با یک عدد نمایش داده می‌شود. برای مثال، فاصله دو نقطه (پنج متر) یک کمیت نرده‌ای است. کمیت‌های نرده‌ای برخلاف کمیت‌های برداری فاقد جهت هستند. در واقع کمیت نرده‌ای، یک تانسور مرتبه صفر است. از دیگر کمیت‌های نرده‌ای می‌توان به جرم، انرژی، دما و چگالی اشاره کرد. عبارت x ∈ ℝ حاکی از آن است که x یک کمیت اسکالر متعلق به مجموعه اعداد حقیقی ℝ است.

مجموعه داده‌های گوناگونی در بحث یادگیری عمیق مورد استفاده قرار می‌گیرند. ℕ یک مجموعه داده از اعداد صحیح مثبت (...،۱،۲،۳) و ℤ مجموعه‌ای از اعداد صحیح مثبت، منفی و صفر است. ℚ مجموعه‌ای از اعداد گویا است که به صورت کسر دو عدد صحیح نشان داده می‌شود. از جمله اسکالرهای موجود در پایتون می‌توان به نوع صحیح (int)، شناور (float)، بایت (byte) و یونی‌کد (Unicode) اشاره کرد. در کتابخانه NumPy پایتون، ۲۴ نوع داده پایه برای تعریف انواع اسکالرها موجود است.

تعریف اسکالرها و انجام عملیات روی آن‌ها در پایتون

در قطعه کد زیر، برخی از عملیات ریاضی روی کمیت‌های اسکالر در پایتون انجام شده است.

1# In-Built Scalars
2a = 5
3b = 7.5
4print(type(a))
5print(type(b))
6print(a + b)
7print(a - b)
8print(a * b)
9print(a / b)

با استفاده از قطعه کد زیر می‌توان اسکالر بودن یا نبودن یک متغیر را مورد بررسی قرار داد.

1import numpy as np
2
3# Is Scalar Function
4def isscalar(num):
5    if isinstance(num, generic):
6        return True
7    else:
8        return False
9
10print(np.isscalar(3.1))
11print(np.isscalar([3.1]))
12print(np.isscalar(False))

کمیت برداری

یک کمیت که توسط چند اسکالر (برای مثال اندازه و جهت) تعریف شود را بردار گویند. بردارها در واقع آرایه‌های مرتب شده‌ای از اعداد هستند. یک بردار، نمونه‌ای از تانسور مرتبه اول است. بردارها خود بخشی از چیزی هستند که با عنوان فضای برداری شناخته می‌شود.

یک فضای برداری را می‌توان به عنوان مجموعه‌ای از همه بردارهای محتمل دارای طول مشخص (یا بُعد) در نظر گرفت. یک فضای برداری سه‌بُعدی مقدار‌گذاری شده با اعداد حقیقی، به صورت ℝ^3 نشان داده می‌شود و اغلب به عنوان نمایش جهان واقعی از فضای سه‌بعدی به‌صورت ریاضی به‌کار می‌رود.

برای تبیین مولفه‌های لازم جهت بیان یک بردار، i‌اُمین مولفه بردار به صورت [x[i نوشته می‌شود.

در یادگیری عمیق، معمولا از بردارها برای نمایش بردار ویژگی‌ها که با مولفه‌های اصلی خود میزان مرتبط بودن یک ویژگی مشخص را نمایش می‌دهند، مورد استفاده قرار می‌گیرند. بردار ویژگی می‌تواند در بر گیرنده اهمیت شدت یک مجموعه از پیکسل‌ها در یک فضای دو‌بُعدی، تصویر یا تاریخچه مقادیر قیمت‌ها در ابزارهای مالی باشد.

تعریف بردارها و اجرای برخی عملیات روی آن‌ها در پایتون

1import numpy as np
2
3# Declaring Vectors
4
5x = [1, 2, 3]
6y = [4, 5, 6]
7
8print(type(x))
9
10# This does'nt give the vector addition.
11print(x + y)
12
13# Vector addition using Numpy
14
15z = np.add(x, y)
16print(z)
17print(type(z))
18
19# Vector Cross Product
20mul = np.cross(x, y)
21print(mul)

ماتریس‌ها

ماتریس‌ها آرایه‌هایی مستطیل شکل از اعداد هستند و در واقع تانسورهای مرتبه دوم محسوب می‌شوند. اگر m و n را اعداد صحیح مثبت در نظر بگیریم، که در آن m, n ∈ ℕ باشند، در نتیجه ماتریس m×n شامل اعداد m*n با m سطر و n ستون است. یک ماتریس m×n کامل را می‌توان به صورت زیر نوشت:

خلاصه‌سازی کل مولفه‌های ماتریس با شیوه نمایشی که در ادامه نشان داده شده، می‌تواند مفید باشد:

در زبان برنامه‌نویسی پایتون، از کتابخانه Numpy استفاده می‌شود که امکان ساخت آرایه‌های n‌بُعدی که اساسا ماتریس‌ها هستند را فراهم می‌کند. با استفاده از متُد لیست و پاس دادن در لیست‌ها، می‌توان یک ماتریس را تعریف کرد.

تعریف ماتریس‌ها و انجام عملیات روی آن‌ها در پایتون

تعریف ماتریس‌ها: در قطعه کد زیر، در خط اول کتابخانه جبر خطی (محاسبات ماتریسی) numpy فراخوانی می‌شود (و آن را به عنوان np تعریف می‌کند تا فراخوانی آن در سراسر کد آسان‌تر باشد). سپس با مقادیر ۱، ۲ و ۲، ۳ یک ماتریس دوبُعدی ایجاد می‌کند.

np.matrix از یک شی آرایه مانند، یا رشته داده، ماتریکس می‌سازد. در نهایت، از محور افقی و در ادامه از ردیف‌ها (سطرها) میانگین گرفته است. سپس درخواست اعلام شکل ماتریس (ابعاد) داده شده است.

1>>> import numpy as np
2>>> x = np.matrix([[1,2],[2,3]])
3>>> x
4matrix([[1, 2],
5        [2, 3]])
6
7>>> a = x.mean(0)
8>>> a
9matrix([[1.5, 2.5]])
10>>> # Finding the mean with 1 with the matrix x.
11>>> z = x.mean(1)
12>>> z
13matrix([[ 1.5],
14        [ 2.5]])
15>>> z.shape
16(2, 1)
17>>> y = x - z
18matrix([[-0.5,  0.5],
19        [-0.5,  0.5]])
20>>> print(type(z))
21<class 'numpy.matrixlib.defmatrix.matrix'>

جمع ماتریس‌ها: ماتریس‌ها را می‌توان با کمیت‌های اسکالر، بردارها و دیگر ماتریس‌ها جمع کرد. هر یک از این عملیات تعریف مشخصی برای خود دارند. این روش‌ها معمولا در یادگیری ماشین و یادگیری عمیق استفاده می‌شوند و بنابراین آشنا شدن با آن‌ها بسیار ارزشمند است.

1# Matrix Addition
2
3import numpy as np
4
5x = np.matrix([[1, 2], [4, 3]])
6
7sum = x.sum()
8print(sum)
9# Output: 10

جمع ماتریس با ماتریس: برای جمع دو ماتریس A و B داریم: C = A + B، که در آن باید شکل (ابعاد) هر دو ماتریس A و B یکی باشد. ماتریس حاصل از جمع این دو، دارای شکلی مشابه ماتریس‌های A و B است. اگر شکل ماتریس‌ها یکی نباشد، پایتون در خروجی پیغام خطای «addition is not possible» را نمایش می‌دهد.

1# Matrix-Matrix Addition
2
3import numpy as np
4
5x = np.matrix([[1, 2], [4, 3]])
6y = np.matrix([[3, 4], [3, 10]])
7
8print(x.shape)
9# (2, 2)
10print(y.shape)
11# (2, 2)
12
13m_sum = np.add(x, y)
14print(m_sum)
15print(m_sum.shape)
16"""
17Output : 
18[[ 4  6]
19 [ 7 13]]
20(2, 2)
21"""

جمع ماتریس با کمیت اسکالر: در جمع ماتریس با کمیت‌های نرده‌ای، باید عدد را با تک به تک درایه‌های ماتریس جمع کنیم.

1# Matrix-Scalar Addition
2
3import numpy as np
4
5x = np.matrix([[1, 2], [4, 3]])
6s_sum = x + 1
7print(s_sum)
8"""
9Output:
10[[2 3]
11 [5 4]]
12"""

ضرب ماتریس در کمیت اسکالر: برای ضرب ماتریس و کمیت اسکالر، باید عدد موجود را تک به تک در درایه‌های ماتریس ضرب کنیم.

1# Matrix Scalar Multiplication
2
3import numpy as np
4
5x = np.matrix([[1, 2], [4, 3]])
6s_mul = x * 3
7print(s_mul)
8"""
9[[ 3  6]
10 [12  9]]
11"""

ضرب ماتریس‌ها: همان‌طور که در تصویر زیر نشان داده شده، حاصل‌ضرب ماتریس A با ابعاد m x n و B با ابعاد n x p برابر است با ماتریس C با ابعاد m x p.

1# Matrix Multiplication
2
3import numpy as np
4
5a = [[1, 0], [0, 1]]
6b = [1, 2]
7np.matmul(a, b)
8# Output: array([1, 2])
9
10complex_mul = np.matmul([2j, 3j], [2j, 3j])
11print(complex_mul)
12# Output: (-13+0j)

ترانهاده ماتریس: ترانهاده یک ماتریس مانند A، ماتریس دیگری است که با نماد AT نشان داده می‌شود. برای ایجاد ترانهاده یک ماتریس باید سطرهای آن را به شکل ستون و ستون‌های آن را به شکل سطر نوشت. به عبارت دیگر، یک ماتریس m×n تبدیل به ماتریس n×m می‌شود.

A=[aij]mxn

AT=[aji]n×m

1# Matrix Transpose
2
3import numpy as np
4
5a = np.array([[1, 2], [3, 4]])
6print(a)
7"""
8[[1 2]
9 [3 4]]
10"""
11a.transpose()
12print(a)
13"""
14array([[1, 3],
15       [2, 4]])
16"""

تانسورها

تانسور یک عنصر هندسی است که در ریاضیات جهت توسعه مفاهیم کمیت‌های اسکالر، بردارها و ماتریس‌ها مطرح می‌شود. تانسور در واقع آرایه‌ای از اعداد است که در یک جدول قرار گرفته‌اند. تانسور در ساده‌ترین حالت یک عضو دارد که در واقع همان کمیت اسکالر است.

در حالت پیشرفته‌تر، تانسور مرتبه اول در واقع آرایه‌ای از اعداد است. تانسورهای دارای دو بعد یا بیشتر، با نام ماتریس شناخته شده‌اند. در یادگیری ماشین گاهی نیاز است از تانسورهایی با مرتبه بالاتر از دو استفاده شود. به جای استفاده از ماتریس‌های تو در تو، می‌توان از کتابخانه Tensorflow یا PyTorch برای اعلان تانسورها استفاده کرد.

1import torch
2
3a = torch.Tensor([26])
4
5print(type(a))
6# <class 'torch.FloatTensor'>
7
8print(a.shape)
9# torch.Size([1])
10
11# Creates a Random Torch Variable of size 5x3.
12t = torch.Tensor(5, 3)
13print(t)
14"""
15 0.0000e+00  0.0000e+00  0.0000e+00
16 0.0000e+00  7.0065e-45  1.1614e-41
17 0.0000e+00  2.2369e+08  0.0000e+00
18 0.0000e+00  0.0000e+00  0.0000e+00
19        nan         nan -1.4469e+35
20[torch.FloatTensor of size 5x3]
21"""
22print(t.shape)
23# torch.Size([5, 3])

برخی اعمال حسابی انجام شده روی تانسورها در زبان پایتون در ادامه آورده شده‌اند.

1import torch
2
3# Creating Tensors
4
5p = torch.Tensor(4,4)
6q = torch.Tensor(4,4)
7ones = torch.ones(4,4)
8
9print(p, q, ones)
10"""
11Output:
12 0.0000e+00  0.0000e+00  0.0000e+00  0.0000e+00
13 1.6009e-19  4.4721e+21  6.2625e+22  4.7428e+30
14 3.1921e-09  8.0221e+17  5.1019e-08  8.1121e+17
15 8.1631e-07  8.2022e+17  1.1703e-19  1.5637e-01
16[torch.FloatTensor of size 4x4]
17 
18 0.0000e+00  0.0000e+00  0.0000e+00  0.0000e+00
19 1.8217e-44  1.1614e-41  0.0000e+00  2.2369e+08
20 0.0000e+00  0.0000e+00  2.0376e-40  2.0376e-40
21        nan         nan -5.3105e+37         nan
22[torch.FloatTensor of size 4x4]
23 
24 1  1  1  1
25 1  1  1  1
26 1  1  1  1
27 1  1  1  1
28[torch.FloatTensor of size 4x4]
29"""
30
31print("Addition:{}".format(p + q))
32print("Subtraction:{}".format(p - ones))
33print("Multiplication:{}".format(p * ones))
34print("Division:{}".format(q / ones))
35
36"""
37Addition:
38 0.0000e+00  0.0000e+00  0.0000e+00  0.0000e+00
39 1.6009e-19  4.4721e+21  6.2625e+22  4.7428e+30
40 3.1921e-09  8.0221e+17  5.1019e-08  8.1121e+17
41        nan         nan -5.3105e+37         nan
42[torch.FloatTensor of size 4x4]
43Subtraction:
44-1.0000e+00 -1.0000e+00 -1.0000e+00 -1.0000e+00
45-1.0000e+00  4.4721e+21  6.2625e+22  4.7428e+30
46-1.0000e+00  8.0221e+17 -1.0000e+00  8.1121e+17
47-1.0000e+00  8.2022e+17 -1.0000e+00 -8.4363e-01
48[torch.FloatTensor of size 4x4]
49Multiplication:
50 0.0000e+00  0.0000e+00  0.0000e+00  0.0000e+00
51 1.6009e-19  4.4721e+21  6.2625e+22  4.7428e+30
52 3.1921e-09  8.0221e+17  5.1019e-08  8.1121e+17
53 8.1631e-07  8.2022e+17  1.1703e-19  1.5637e-01
54[torch.FloatTensor of size 4x4]
55Division:
56 0.0000e+00  0.0000e+00  0.0000e+00  0.0000e+00
57 1.8217e-44  1.1614e-41  0.0000e+00  2.2369e+08
58 0.0000e+00  0.0000e+00  2.0376e-40  2.0376e-40
59        nan         nan -5.3105e+37         nan
60[torch.FloatTensor of size 4x4]
61"""

اگر نوشته بالا برای شما مفید بوده است، مطالب زیر نیز به شما پیشنهاد می‌شود:

^^

بر اساس رای ۱۶ نفر
آیا این مطلب برای شما مفید بود؟
اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.
منابع:
TowardsDataScience
نظر شما چیست؟

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *