فاز 1 · درس 1

شهود جبر خطی برای هوش مصنوعی: از پیاده‌سازی دستی تا NumPy و PyTorch

نوع
مفهومی
زمان
حدود 60 دقیقه
زبان‌ها
Python, Julia
پیش‌نیاز
فاز 0
شبکه‌ای از خطوط مختصات سه‌بعدی با پیکان‌های برداری رنگی و سایه‌ی تصویرشده‌ی آن‌ها روی یک صفحه‌ی دوبعدی
فهرست مطالب
  1. اهداف یادگیری
  2. صورت مسئله
  3. مفاهیم اصلی
  4. بردارها همان نقطه‌ها (و جهت‌ها) هستند
  5. ماتریس‌ها همان تبدیلات هستند
  6. ضرب داخلی میزان شباهت را می‌سنجد
  7. استقلال خطی
  8. پایه و رتبه
  9. تصویر کردن
  10. فرایند گرام-اشمیت
  11. پیاده‌سازی
  12. مرحله‌ی 1: پیاده‌سازی بردارها از صفر (Python)
  13. مرحله‌ی 2: پیاده‌سازی ماتریس‌ها از صفر (Python)
  14. مرحله‌ی 3: چرا این مفاهیم در هوش مصنوعی اهمیت دارند
  15. مرحله‌ی 4: پیاده‌سازی در Julia
  16. مرحله‌ی 5: استقلال خطی و تصویر کردن از صفر (Python)
  17. استفاده از ابزارهای واقعی
  18. محاسبه‌ی رتبه، تصویر کردن و تجزیه‌ی QR با NumPy
  19. کتابخانه‌ی PyTorch — تنسورها همان بردارها با قابلیت تفکیک خودکار هستند
  20. خروجی نهایی
  21. ارتباط با مفاهیم کاربردی
  22. تمرین‌ها
  23. راهنمایی‌ها
  24. اصطلاحات کلیدی
  25. خلاصه‌ی مباحث

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

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

برای تثبیت کامل مفاهیم، این عملیات را یک‌بار از صفر با پایتون خالص پیاده‌سازی می‌کنید و سپس پیاده‌سازی استاندارد آن‌ها را با NumPy و PyTorch می‌بینید تا تفاوت کد خام با ابزارهای بهینه‌ی صنعتی را دقیق بشناسید.

هر مدل هوش مصنوعی در اصل همان محاسبات ماتریسی است؛ فقط کلاه شیکی به سر گذاشته است.

اهداف یادگیری

  • پیاده‌سازی عملیات برداری و ماتریسی (جمع، ضرب داخلی و ضرب ماتریسی) از صفر در Python
  • توضیح هندسی مفهوم ضرب داخلی، تصویر کردن و فرایند گرام-اشمیت (Gram-Schmidt process)
  • تعیین استقلال خطی، رتبه و پایه‌ی مجموعه‌ای از بردارها با استفاده از کاهش سطری (row reduction)
  • ارتباط دادن مفاهیم جبر خطی به کاربردهای آن‌ها در هوش مصنوعی: امبدینگ‌ها، امتیازهای اتنشن و LoRA

صورت مسئله

کافی است یک مقاله‌ی یادگیری ماشین (ML) را باز کنید؛ در همان صفحه‌ی اول با بردارها، ماتریس‌ها، ضرب‌های داخلی و تبدیلات روبه‌رو می‌شوید. اگر شهود جبر خطی را نداشته باشید، این‌ها مشتی نماد نامفهوم‌اند؛ اما با داشتن این شهود، دقیقاً می‌بینید که در شبکه عصبی چه می‌گذرد: جابه‌جا کردن نقطه‌ها در فضا.

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

مفاهیم اصلی

بردارها همان نقطه‌ها (و جهت‌ها) هستند

بردار صرفاً فهرستی از اعداد است؛ اما این اعداد معنا دارند: مختصات در فضا هستند.

بردار دوبعدی [3, 2]:

x y نقطه
3 2 بردار از مبدأ (0,0) به سمت نقطه‌ی (3, 2) روی صفحه اشاره می‌کند

این بردار اندازه‌ی 32+22=13\sqrt{3^2 + 2^2} = \sqrt{13} دارد و جهتش رو به بالا و راست است.

در هوش مصنوعی، بردارها همه‌چیز را نمایش می‌دهند:

  • یک کلمه ← برداری با 768 عدد («معنای» آن در فضای امبدینگ)
  • یک تصویر ← برداری از میلیون‌ها مقدار پیکسل
  • یک کاربر ← برداری از ترجیحات

ماتریس‌ها همان تبدیلات هستند

ماتریس یک بردار را به برداری دیگر تبدیل می‌کند؛ می‌تواند آن را بچرخاند، مقیاسش را تغییر دهد، بکشد یا تصویر کند.

بعدضرب ماتریسیقبلنقطه Aنقطه BM (تبدیل)نقطه 'Aنقطه 'B

در دنیای هوش مصنوعی، ماتریس‌ها خودِ مدل هستند:

  • وزن‌های شبکه عصبی ← ماتریس‌هایی که ورودی را به خروجی تبدیل می‌کنند
  • امتیازهای اتنشن ← ماتریس‌هایی که مشخص می‌کنند تمرکز مدل باید روی چه بخش‌هایی باشد
  • امبدینگ‌ها ← ماتریس‌هایی که کلمات را به بردارها نگاشت می‌کنند

ضرب داخلی میزان شباهت را می‌سنجد

ضرب داخلی (dot product) دو بردار نشان می‌دهد که آن‌ها چقدر به هم شبیه‌اند.

a⋅b=a1×b1+a2×b2+⋯+an×bnSame direction:a⋅b>0(similar)Perpendicular:a⋅b=0(unrelated)Opposite direction:a⋅b<0(dissimilar)\begin{aligned} a \cdot b &= a_1 \times b_1 + a_2 \times b_2 + \cdots + a_n \times b_n \\[4pt] \text{Same direction:} \quad a \cdot b &> 0 \quad \text{(similar)} \\ \text{Perpendicular:} \quad a \cdot b &= 0 \quad \text{(unrelated)} \\ \text{Opposite direction:} \quad a \cdot b &< 0 \quad \text{(dissimilar)} \end{aligned}

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

استقلال خطی

بردارها زمانی استقلال خطی (linear independence) دارند که هیچ برداری از آن مجموعه را نتوان به‌صورت ترکیبی از بقیه نوشت. اگر بردارهای v1,v2,v3v_1, v_2, v_3 مستقل باشند، یک فضای سه‌بعدی را اسپن (تولید) می‌کنند؛ اما اگر یکی از آن‌ها ترکیبی از بقیه باشد، تنها یک صفحه را پوشش می‌دهند.

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

یک مثال ملموس:

v1 = [1, 0, 0]
v2 = [0, 1, 0]
v3 = [2, 1, 0]   # v3 = 2*v1 + v2

بردارهای v1v_1 و v2v_2 مستقل هستند؛ چون هیچ‌کدام مضرب اسکالر یا ترکیبی از دیگری نیست. اما چون v3=2v1+v2v_3 = 2 v_1 + v_2 برقرار است، مجموعه‌ی {v1,v2,v3}\lbrace v_1, v_2, v_3 \rbrace یک مجموعه‌ی وابسته محسوب می‌شود. هر سه بردار در صفحه‌ی xy قرار دارند؛ هرطور هم آن‌ها را ترکیب کنید، هرگز به [0, 0, 1] نمی‌رسید. سه بردار دارید، ولی در واقع فقط دو درجه‌ی آزادی در اختیارتان است.

در یک مجموعه‌داده: اگر feature_3 = 2*feature_1 + feature_2 باشد، اضافه کردنِ feature_3 عملاً هیچ اطلاعات جدیدی به مدل نمی‌دهد. بدتر از آن، باعث منفرد (singular) شدن معادلات نرمال می‌شود؛ یعنی دیگر جواب یکتایی برای وزن‌ها وجود نخواهد داشت.

پایه و رتبه

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

پایه‌ی استاندارد برای فضای سه‌بعدی {[1,0,0], [0,1,0], [0,0,1]} است؛ هرچند هر سه بردار مستقل در فضای سه‌بعدی می‌توانند یک پایه‌ی معتبر تشکیل دهند. انتخاب پایه، در واقع همان انتخاب دستگاه مختصات است.

رتبه‌ی (rank) یک ماتریس = تعداد ستون‌های مستقل خطی = تعداد سطرهای مستقل خطی. اگر rank<min⁡(rows,cols)\text{rank} < \min(\text{rows}, \text{cols}) باشد، ماتریس با کمبود رتبه (rank-deficient) مواجه است. یعنی:

  • دستگاه معادلات بی‌نهایت جواب دارد (یا اصلاً جواب ندارد)؛
  • در جریان تبدیل ماتریسی، بخشی از اطلاعات از بین می‌رود؛
  • ماتریس وارون‌پذیر نیست.
وضعیت رتبه معنای آن برای یادگیری ماشین
رتبه‌ی کامل (rank=min⁡(m,n))(\text{rank} = \min(m, n)) بیشینه‌ی مقدار ممکن اگر تعداد سطرها دست‌کم برابر با ستون‌ها باشد (رتبه‌ی ستونی کامل)، جواب کمترین مربعات یکتاست. اگر تعداد سطرها کمتر از ستون‌ها باشد، بی‌نهایت جواب برازش دقیق دارند.
کمبود رتبه (rank<min⁡(m,n))(\text{rank} < \min(m, n)) کمتر از بیشینه ویژگی‌ها حشو و تکراری‌اند. بی‌نهایت جواب برای وزن‌ها وجود دارد. نیاز به منظم‌سازی (regularization) است.
رتبه‌ی 1 1 همه‌ی ستون‌ها مضربی از یک بردار واحدند. همه‌ی داده‌ها روی یک خط مستقیم قرار می‌گیرند.
نزدیک به کمبود رتبه (مقادیر تکین کوچک) از نظر عددی پایین ماتریس بدحالت (ill-conditioned) است. نویز ناچیز در ورودی تغییرات بزرگی در خروجی ایجاد می‌کند. باید از برش SVD یا رگرسیون ریج استفاده شود.

تصویر کردن

تصویر کردنِ (projection) بردار a روی بردار b، مؤلفه‌ای از a را به دست می‌دهد که در امتداد b قرار دارد:

projb(a)=a⋅bb⋅b b\mathop{\mathrm{proj}}_b(a) = \frac{a \cdot b}{b \cdot b} \, b

باقی‌مانده‌ی (a−projb(a))(a - \mathop{\mathrm{proj}}_b(a)) بر bb عمود است. این تجزیه‌ی متعامد (orthogonal decomposition)، شالوده‌ی برازش کمترین مربعات (least-squares fitting) به شمار می‌رود.

تصویر کردن در سراسر یادگیری ماشین دیده می‌شود:

  • رگرسیون خطی فاصله‌ی مشاهدات تا فضای ستونی را کمینه می‌کند؛ در اصل پاسخ نهایی خود یک تصویر هندسی است؛
  • تحلیل مؤلفه‌های اصلی (PCA) داده‌ها را روی راستاهایی با بیشترین واریانس تصویر می‌کند؛
  • اتنشن در ترنسفورمرها تصویر کوئری‌ها روی کلیدها را محاسبه می‌کند.
تصویر a روی bb (جهت)a (اصلی)proj_b(a)باقی‌مانده (عمود)مبدأbaتصویر

مثال: a=[3,4]a = [3, 4] و b=[1,0]b = [1, 0]

projb(a)=(3⋅1+4⋅0)/(1⋅1+0⋅0)⋅[1,0]=3⋅[1,0]=[3,0]\mathop{\mathrm{proj}}_b(a) = (3 \cdot 1 + 4 \cdot 0) / (1 \cdot 1 + 0 \cdot 0) \cdot [1, 0] = 3 \cdot [1, 0] = [3, 0]

در این تصویر کردن، مؤلفه‌ی y حذف می‌شود. این ساده‌ترین شکل کاهش ابعاد (dimensionality reduction) است: راستاهایی را که برایتان مهم نیستند، کنار می‌گذارید.

فرایند گرام-اشمیت

تبدیل هر مجموعه‌ای از بردارهای مستقل به یک پایه‌ی متعامد یکه (orthonormal basis). متعامد یکه یعنی طول هر بردار برابر با 1 است و همه‌ی جفت‌ها بر هم عمودند.

مراحل الگوریتم:

  1. بردار اول را بردارید و نرمالایز کنید؛
  2. بردار دوم را بردارید، تصویر آن روی بردار اول را کم کنید و نتیجه را نرمالایز کنید؛
  3. بردار سوم را بردارید، تصویرهای آن روی همه‌ی بردارهای قبلی را کم کنید و نتیجه را نرمالایز کنید؛
  4. همین مراحل را برای بقیه‌ی بردارها تکرار کنید.
Input:  v1, v2, v3, ... (linearly independent)

u1 = v1 / |v1|

w2 = v2 - (v2 dot u1) * u1
u2 = w2 / |w2|

w3 = v3 - (v3 dot u1) * u1 - (v3 dot u2) * u2
u3 = w3 / |w3|

Output: u1, u2, u3, ... (orthonormal basis)

تجزیه‌ی QR در لایه‌های زیرین دقیقاً به همین شکل کار می‌کند: Q پایه‌ی متعامد یکه است و R ضرایب تصویر کردن را ثبت می‌کند. از تجزیه‌ی QR در موارد زیر استفاده می‌شود:

  • حل دستگاه‌های خطی (با پایداری عددی بالاتر نسبت به حذف گاوسی)؛
  • محاسبه‌ی مقادیر ویژه (الگوریتم QR)؛
  • رگرسیون کمترین مربعات (روش استاندارد در محاسبات عددی).
جهت‌های ویژهماتریس را بکشید
EIGEN-DIRECTIONSFor a symmetric matrix the eigenvectors are the dashed axes, and the matrix simply stretches anything along them by the eigenvalue (orange). A generic grey vector, off those axes, both stretches and rotates into the blue image. Drag its angle: only on an eigen-axis does the output stay parallel to the input.
برای یک ماتریس متقارن، بردارهای ویژه همان محورهای خط‌چین هستند و ماتریس صرفاً هر برداری را در راستای آن‌ها با مقادیر ویژه (نارنجی) می‌کِشد. یک بردار خاکستری معمولی خارج از این محورها، هم کشیده شده و هم دوران می‌یابد تا تصویر آبی‌رنگ شکل گیرد. زاویه آن را بکشید: تنها روی محور ویژه است که خروجی موازی با ورودی باقی می‌ماند.

پیاده‌سازی

مرحله‌ی 1: پیاده‌سازی بردارها از صفر (Python)

class Vector:
    def __init__(self, components):
        self.components = list(components)
        self.dim = len(self.components)

    def __add__(self, other):
        return Vector([a + b for a, b in zip(self.components, other.components)])

    def __sub__(self, other):
        return Vector([a - b for a, b in zip(self.components, other.components)])

    def dot(self, other):
        return sum(a * b for a, b in zip(self.components, other.components))

    def magnitude(self):
        return sum(x**2 for x in self.components) ** 0.5

    def normalize(self):
        mag = self.magnitude()
        return Vector([x / mag for x in self.components])

    def cosine_similarity(self, other):
        return self.dot(other) / (self.magnitude() * other.magnitude())

    def __repr__(self):
        return f"Vector({self.components})"


a = Vector([1, 2, 3])
b = Vector([4, 5, 6])

print(f"a + b = {a + b}")
print(f"a · b = {a.dot(b)}")
print(f"|a| = {a.magnitude():.4f}")
print(f"cosine similarity = {a.cosine_similarity(b):.4f}")

مرحله‌ی 2: پیاده‌سازی ماتریس‌ها از صفر (Python)

class Matrix:
    def __init__(self, rows):
        self.rows = [list(row) for row in rows]
        self.shape = (len(self.rows), len(self.rows[0]))

    def __matmul__(self, other):
        if isinstance(other, Vector):
            return Vector([
                sum(self.rows[i][j] * other.components[j] for j in range(self.shape[1]))
                for i in range(self.shape[0])
            ])
        rows = []
        for i in range(self.shape[0]):
            row = []
            for j in range(other.shape[1]):
                row.append(sum(
                    self.rows[i][k] * other.rows[k][j]
                    for k in range(self.shape[1])
                ))
            rows.append(row)
        return Matrix(rows)

    def transpose(self):
        return Matrix([
            [self.rows[j][i] for j in range(self.shape[0])]
            for i in range(self.shape[1])
        ])

    def __repr__(self):
        return f"Matrix({self.rows})"


rotation_90 = Matrix([[0, -1], [1, 0]])
point = Vector([3, 1])

rotated = rotation_90 @ point
print(f"Original: {point}")
print(f"Rotated 90°: {rotated}")

مرحله‌ی 3: چرا این مفاهیم در هوش مصنوعی اهمیت دارند

import random

random.seed(42)
weights = Matrix([[random.gauss(0, 0.1) for _ in range(3)] for _ in range(2)])
input_vector = Vector([1.0, 0.5, -0.3])

output = weights @ input_vector
print(f"Input (3D): {input_vector}")
print(f"Output (2D): {output}")
print("This is what a neural network layer does -- matrix multiplication.")

مرحله‌ی 4: پیاده‌سازی در Julia

a = [1.0, 2.0, 3.0]
b = [4.0, 5.0, 6.0]

println("a + b = ", a + b)
println("a · b = ", a ⋅ b)       # Julia supports unicode operators
println("|a| = ", √(a ⋅ a))
println("cosine = ", (a ⋅ b) / (√(a ⋅ a) * √(b ⋅ b)))

# ضرب ماتریس در بردار
W = [0.1 -0.2 0.3; 0.4 0.5 -0.1]
x = [1.0, 0.5, -0.3]
println("Wx = ", W * x)
println("This is a neural network layer.")

مرحله‌ی 5: استقلال خطی و تصویر کردن از صفر (Python)

def is_linearly_independent(vectors):
    n = len(vectors)
    dim = len(vectors[0].components)
    mat = Matrix([v.components[:] for v in vectors])
    rows = [row[:] for row in mat.rows]
    rank = 0
    for col in range(dim):
        pivot = None
        for row in range(rank, len(rows)):
            if abs(rows[row][col]) > 1e-10:
                pivot = row
                break
        if pivot is None:
            continue
        rows[rank], rows[pivot] = rows[pivot], rows[rank]
        scale = rows[rank][col]
        rows[rank] = [x / scale for x in rows[rank]]
        for row in range(len(rows)):
            if row != rank and abs(rows[row][col]) > 1e-10:
                factor = rows[row][col]
                rows[row] = [rows[row][j] - factor * rows[rank][j] for j in range(dim)]
        rank += 1
    return rank == n


def project(a, b):
    scalar = a.dot(b) / b.dot(b)
    return Vector([scalar * x for x in b.components])


def gram_schmidt(vectors):
    orthonormal = []
    for v in vectors:
        w = v
        for u in orthonormal:
            proj = project(w, u)
            w = w - proj
        if w.magnitude() < 1e-10:
            continue
        orthonormal.append(w.normalize())
    return orthonormal


v1 = Vector([1, 0, 0])
v2 = Vector([1, 1, 0])
v3 = Vector([1, 1, 1])
basis = gram_schmidt([v1, v2, v3])
for i, u in enumerate(basis):
    print(f"u{i+1} = {u}")
    print(f"  |u{i+1}| = {u.magnitude():.6f}")

print(f"u1 · u2 = {basis[0].dot(basis[1]):.6f}")
print(f"u1 · u3 = {basis[0].dot(basis[2]):.6f}")
print(f"u2 · u3 = {basis[1].dot(basis[2]):.6f}")

استفاده از ابزارهای واقعی

حالا همان پیاده‌سازی را با NumPy انجام می‌دهیم؛ یعنی همان چیزی که در پروژه‌های واقعی به کار خواهید برد:

import numpy as np

a = np.array([1, 2, 3], dtype=float)
b = np.array([4, 5, 6], dtype=float)

print(f"a + b = {a + b}")
print(f"a · b = {np.dot(a, b)}")
print(f"|a| = {np.linalg.norm(a):.4f}")
print(f"cosine = {np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b)):.4f}")

W = np.random.randn(2, 3) * 0.1
x = np.array([1.0, 0.5, -0.3])
print(f"Wx = {W @ x}")

محاسبه‌ی رتبه، تصویر کردن و تجزیه‌ی QR با NumPy

import numpy as np

A = np.array([[1, 2], [2, 4]])
print(f"Rank: {np.linalg.matrix_rank(A)}")

a = np.array([3, 4])
b = np.array([1, 0])
proj = (np.dot(a, b) / np.dot(b, b)) * b
print(f"Projection of {a} onto {b}: {proj}")

Q, R = np.linalg.qr(np.random.randn(3, 3))
print(f"Q is orthogonal: {np.allclose(Q @ Q.T, np.eye(3))}")
print(f"R is upper triangular: {np.allclose(R, np.triu(R))}")

کتابخانه‌ی PyTorch — تنسورها همان بردارها با قابلیت تفکیک خودکار هستند

import torch

x = torch.randn(3, requires_grad=True)
y = torch.tensor([1.0, 0.0, 0.0])

similarity = torch.dot(x, y)
similarity.backward()

print(f"x = {x.data}")
print(f"y = {y.data}")
print(f"dot product = {similarity.item():.4f}")
print(f"d(dot)/dx = {x.grad}")

گرادیان ضرب داخلی نسبت به x، دقیقاً همان y است. PyTorch این مقدار را خودکار محاسبه کرد. تک‌تک عملیات درون یک شبکه عصبی از همین مبانی ساخته شده‌اند: ضرب‌های ماتریسی، ضرب‌های داخلی، تصویر کردن‌ها، و سیستم مشتق‌گیری خودکار (autodiff) که گرادیان‌ها را در تمام این مسیر دنبال می‌کند.

چیزی را از صفر ساختید که NumPy با یک خط کد انجام می‌دهد. حالا دیگر می‌دانید زیر پوست کدها چه می‌گذرد.

خروجی نهایی

خروجی این درس:

  • outputs/prompt-linear-algebra-tutor.md -- پرامپتی برای دستیارهای هوش مصنوعی جهت آموزش جبر خطی با شهود هندسی

ارتباط با مفاهیم کاربردی

تمام مباحث این درس به بخش‌های معینی از هوش مصنوعی مدرن گره خورده‌اند:

مفهوم محل کاربرد
ضرب داخلی امتیازهای اتنشن در ترنسفورمرها، شباهت کسینوسی در RAG
ضرب ماتریسی تمام لایه‌های شبکه عصبی، تمام تبدیلات خطی
استقلال خطی انتخاب ویژگی، جلوگیری از هم‌خطی چندگانه
رتبه تعیین حل‌پذیر بودن دستگاه، LoRA (تطبیق کم‌رتبه)
تصویر کردن رگرسیون خطی (تصویر روی فضای ستونی)، PCA
گرام-اشمیت / QR حل‌کننده‌های عددی، محاسبه‌ی مقادیر ویژه
پایه‌ی متعامد یکه محاسبات عددی پایدار، تبدیلات سفیدکننده (whitening)

تکنیک LoRA به‌طور ویژه شایسته‌ی توجه است؛ روشی که مدل‌های زبانی بزرگ را با تجزیه‌ی به‌روزرسانی وزن‌ها به ماتریس‌های کم‌رتبه فاین‌تیون می‌کند. LoRA به‌جای به‌روزرسانی یک ماتریس وزن 4096×4096 (با 16 میلیون پارامتر)، دو ماتریس با ابعاد 4096×16 و 16×4096 (با 131 هزار پارامتر) را به‌روزرسانی می‌کند. قید رتبه‌ی 16 یعنی LoRA فرض می‌کند به‌روزرسانی وزن در زیرفضایی 16‌بُعدی از کل فضای 4096‌بُعدی رخ می‌دهد. این جلوه‌ی واقعی قدرت جبر خطی است.

تمرین‌ها

  1. متد Vector.angle_between(other) را طوری پیاده‌سازی کنید که زاویه‌ی بین دو بردار را برحسب درجه برگرداند.
  2. یک ماتریس مقیاس‌بندی دوبعدی بسازید که مختصات x را دو برابر و مختصات y را سه برابر کند؛ سپس آن را روی بردار [1, 1] اعمال کنید.
  3. با داشتن 5 بردار کلمه‌مانند تصادفی (با بُعد 50)، دو برداری را که بر اساس شباهت کسینوسی بیشترین شباهت را دارند پیدا کنید.
  4. بررسی کنید که آیا خروجی الگوریتم گرام-اشمیت واقعاً متعامد یکه است یا خیر: بررسی کنید که ضرب داخلی هر دو بردار دلخواه برابر با 0 و اندازه‌ی هر بردار برابر با 1 باشد.
  5. یک ماتریس 3×3 با رتبه‌ی 2 بسازید. درستی آن را با استفاده از متد rank() ارزیابی کنید. سپس توضیح دهید ستون‌های این ماتریس چه شیء هندسی را پوشش می‌دهند.
  6. بردار [1, 2, 3] را روی بردار [1, 1, 1] تصویر کنید. این نتیجه از نظر هندسی چه معنایی دارد؟

راهنمایی‌ها

تمرین 1. کار را با cosine_similarity شروع کنید؛ مقدار را در بازه‌ی -1 تا 1 نگه دارید، سپس آرک‌کسینوس آن را بگیرید و رادیان را به درجه تبدیل کنید. خروجی را بررسی کنید تا مطمئن شوید Vector([1, 0]) و Vector([0, 1]) زاویه‌ی 90 و بردار با خودش زاویه‌ی 0 می‌دهد.

تمرین 2. دو فاکتور مقیاس را روی قطر اصلی یک ماتریس 2×2 بگذارید و دو درایه‌ی دیگر را 0 کنید. خروجی برای [1, 1] باید [2, 3] باشد.

تمرین 3. شباهت کسینوسی را برای تمام جفت‌های ممکن از میان 5 بردار (10 جفت) محاسبه کنید و بیشترین مقدار را نگه دارید. برای اطمینان از درستی کار، حلقه‌ی خود را ابتدا با یک بردار در برابر خودش بررسی کنید که باید حاصل 1 بدهد.

تمرین 4. تابع gram_schmidt را روی سه بردار مستقل اعمال کنید؛ سپس متد dot را برای هر جفت و magnitude را برای هر بردار حساب کنید. برای مقایسه از تلورانس خطایی مثل 1e-9 کمک بگیرید؛ چون محاسبات ممیز شناور صفر مطلق تولید نمی‌کنند.

تمرین 5. سطر یا ستون سوم را حاصل‌جمع دو سطر یا ستون اول بگذارید تا یکی از راستاها تکراری شود. متد rank() باید مقدار 2 را برگرداند؛ در این حالت ستون‌ها صفحه‌ای را پوشش می‌دهند که از مبدأ می‌گذرد.

تمرین 6. ضریب مقیاس a⋅bb⋅b\frac{a \cdot b}{b \cdot b} را محاسبه کنید و bb را در آن ضرب کنید؛ یا این‌که متد project_onto را فرابخوانید. بررسی کنید که آیا ضرب داخلی باقی‌مانده‌ی aa (منهای تصویر) با بردار [1, 1, 1] برابر با 0 می‌شود یا خیر.

اصطلاحات کلیدی

اصطلاح در گفتار عامیانه معنای دقیق و فنی
بردار (Vector) «یک فلش» فهرستی از اعداد که نشان‌دهنده‌ی یک نقطه یا جهت در فضایی n‌بُعدی است
ماتریس (Matrix) «جدولی از اعداد» تبدیلی که بردارها را از فضایی به فضای دیگر نگاشت می‌کند
ضرب داخلی (Dot product) «ضرب و جمع کردن» معیاری برای سنجش هم‌راستایی دو بردار؛ شالوده‌ی جست‌وجوی شباهت
امبدینگ (Embedding) «جادوی هوش مصنوعی» برداری که نشان‌دهنده‌ی معنای یک موجودیت (کلمه، تصویر، کاربر) است
استقلال خطی (Linear independence) «هم‌پوشانی نداشتن» وضعیتی که در آن هیچ برداری از مجموعه را نتوان به‌صورت ترکیبی از بقیه نوشت
رتبه (Rank) «تعداد ابعاد» تعداد ستون‌ها (یا سطرهای) مستقل خطی در یک ماتریس
تصویر کردن (Projection) «سایه» مؤلفه‌ای از یک بردار در امتداد جهت برداری دیگر
پایه (Basis) «محورهای مختصات» کمینه‌مجموعه‌ای از بردارهای مستقل که کل فضا را پوشش می‌دهند
متعامد یکه (Orthonormal) «بردارهای یکه و عمود بر هم» بردارهایی که دوبه‌دو بر هم عمودند و طول هر کدام دقیقاً 1 است

خلاصه‌ی مباحث

فرمول مفهوم
a⋅b=∑iaibia \cdot b = \sum_i a_i b_i ضرب داخلی؛ هم‌راستایی را اندازه می‌گیرد و مبنای امتیازهای اتنشن و شباهت کسینوسی است
cos⁡θ=a⋅b∣a∣∣b∣\cos\theta = \frac{a \cdot b}{\lvert a\rvert \lvert b\rvert} زاویه‌ی بین بردارها؛ 1 یعنی هم‌جهت و 0 یعنی متعامد
∣a∣=a⋅a\lvert a\rvert = \sqrt{a \cdot a} اندازه‌ی بردار (طول)
y=Ax,yi=∑jAijxjy = Ax, y_i = \sum_j A_{ij} x_j ضرب ماتریس در بردار؛ هر لایه‌ی شبکه عصبی این عملیات را روی تمام ورودی‌ها اعمال می‌کند
projba=a⋅bb⋅bb\text{proj}_b a = \frac{a \cdot b}{b \cdot b} b تصویر کردن aa روی bb؛ مؤلفه‌ای از aa در راستای bb
b⊥=a−projbab^\perp = a - \text{proj}_b a مؤلفه‌ای از aa که بر bb عمود است؛ کاربردی در فرایند گرام-اشمیت
rank(A)≤min⁡(m,n)\text{rank}(A) \leq \min(m,n) رتبه، تعداد ستون‌های مستقل؛ تعیین می‌کند که آیا Ax=bAx = b جواب دارد یا خیر
ΔW=BA\Delta W = BA به‌طوری که BB با ابعاد 4096×16 و AA با ابعاد 16×4096 است به‌روزرسانی کم‌رتبه؛ LoRA به‌جای به‌روزرسانی 4096×4096 (با 16 میلیون پارامتر)، از دو ماتریس با رتبه‌ی 16 (با 131 هزار پارامتر) استفاده می‌کند
پایه‌ی متعامد یکه: ∣vi∣=1,vi⋅vj=0\lvert v_i\rvert = 1, v_i \cdot v_j = 0 تبدیلات پایدار و برگشت‌پذیر، مانند تبدیل سفیدکننده و تجزیه‌ی QR

خودآزمایی

به سؤال‌ها جواب بدهید تا ببینید چقدر از درس در ذهنتان مانده است.

  1. پیش از درسضرب داخلی دو بردار چه چیزی را می‌سنجد؟
  2. پیش از درسدر هوش مصنوعی، منظور از «امبدینگ» چیست؟
  3. پس از درسسه بردار v1=[1,0,0]، v2=[0,1,0] و v3=[2,1,0] داده شده‌اند. آیا آن‌ها از نظر خطی مستقل هستند؟
  4. پس از درسرتبه (Rank) یک ماتریس در زمینه یادگیری ماشین بیانگر چیست؟
  5. پس از درستکنیک LoRA (Low-Rank Adaptation) چگونه از جبر خطی برای فاین‌تیون کارآمد مدل‌های زبانی بزرگ استفاده می‌کند؟

سؤالات متداول

چرا ضرب داخلی در هوش مصنوعی و مدل‌های زبانی این‌قدر مهم است؟

ضرب داخلی میزان هم‌راستایی و شباهت دو بردار را اندازه می‌گیرد؛ به همین دلیل هسته‌ی اصلی محاسبه‌ی امتیازهای اتنشن در ترنسفورمرها و جست‌وجوی شباهت کسینوسی در پایگاه‌های داده برداری (RAG) است.

وابستگی خطی ستون‌ها چه مشکلی در مدل‌های یادگیری ماشین ایجاد می‌کند؟

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

روش LoRA چگونه از مفهوم رتبه‌ی ماتریس در جبر خطی استفاده می‌کند؟

تکنیک LoRA به‌جای آپدیت یک ماتریس وزن کامل (مثلاً 4096×4096 با 16 میلیون پارامتر)، آن را به دو ماتریس کم‌رتبه (4096×16 و 16×4096 با مجموع 131 هزار پارامتر) تجزیه می‌کند و با فرض تغییرات در یک زیرفضای 16‌بُعدی، مصرف حافظه و هزینه را به‌شدت کاهش می‌دهد.

ما رو توی گوگل بیشتر ببین

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