Faza 01 · lecția 02

Vectori, matrice și operații

Scopul lecției: Orice rețea neuronală este doar o înmulțire matricială, însoțită de câțiva pași suplimentari.

Versiunea curentă AlexBred.com: primele 100 de lecții ale programului în limba română.

Curs
AI Engineering from Scratch
Fază
Fundamente matematice
Lectură
12 min.
Verificat
Cuprinsul lecției
  1. Obiective de învățare
  2. Problema
  3. Conceptul
  4. Vectori: liste ordonate de numere
  5. Matrice: tablouri de numere
  6. De ce contează dimensiunile
  7. Harta operațiilor
  8. Înmulțirea element cu element și înmulțirea matricelor
  9. Extinderea automată (broadcasting)
  10. Implementare
  11. Pasul 1: clasa Vector
  12. Pasul 2: clasa Matrix cu operațiile de bază
  13. Pasul 3: testați implementarea
  14. Pasul 4: legătura cu rețelele neuronale
  15. Utilizarea implementării
  16. Livrarea rezultatului
  17. Exerciții
  18. Termeni-cheie
  19. Lecturi suplimentare

Orice rețea neuronală este doar o înmulțire matricială, însoțită de câțiva pași suplimentari.

Tip: Construire Limbaje: Python, Julia Cerințe preliminare: Faza 1, lecția 01 (Intuiție pentru algebra liniară) Durată: ~60 de minute

Obiective de învățare

  • Construirea unei clase Matrix cu operații element cu element, înmulțirea matricelor, transpunere, determinant și inversă
  • Deosebirea înmulțirii element cu element de înmulțirea matricelor și explicarea situațiilor în care se aplică fiecare
  • Implementarea unui singur strat dens al unei rețele neuronale (relu(W @ x + b)) folosind numai clasa Matrix construită de la zero
  • Explicarea regulilor de broadcasting și a modului în care funcționează adăugarea biasului în frameworkurile pentru rețele neuronale

Problema

Doriți să construiți o rețea neuronală. Citiți codul și vedeți următoarea expresie:

output = activation(weights @ input + bias)

Operatorul @ efectuează înmulțirea matricelor. weights este o matrice, iar input este un vector. Dacă nu știți ce fac aceste operații, linia pare magică. Dacă le înțelegeți, ea reprezintă întreaga propagare înainte printr-un strat, exprimată în trei operații.

Fiecare imagine procesată de model este o matrice de valori ale pixelilor. Fiecare reprezentare vectorială (embedding) a unui cuvânt este un vector. Fiecare strat al oricărei rețele neuronale este o transformare matricială. Nu puteți construi sisteme de IA fără să stăpâniți operațiile cu matrice, la fel cum nu puteți scrie cod fără să înțelegeți variabilele.

Această lecție vă ajută să stăpâniți aceste operații de la zero.

Conceptul

Vectori: liste ordonate de numere

Un vector este o listă de numere caracterizată prin direcție și modul. În IA, vectorii reprezintă puncte de date, caracteristici sau parametri.

v = [3, 4]        -- a 2D vector
w = [1, 0, -2]    -- a 3D vector

Vectorul bidimensional [3, 4] indică punctul de coordonate (3, 4) într-un plan. Lungimea sa (modulul) este 5 (triunghiul cu laturile 3, 4 și 5).

Matrice: tablouri de numere

O matrice este un tablou bidimensional, alcătuit din linii și coloane. O matrice m x n are m linii și n coloane.

A = | 1  2  3 |     -- 2x3 matrix (2 rows, 3 columns)
    | 4  5  6 |

În rețelele neuronale, matricele de ponderi transformă vectorii de intrare în vectori de ieșire. Un strat cu 784 de intrări și 128 de ieșiri folosește o matrice de ponderi 128x784.

De ce contează dimensiunile

Înmulțirea matricelor respectă o regulă strictă: (m x n) @ (n x p) = (m x p). Dimensiunile interioare trebuie să coincidă.

(128 x 784) @ (784 x 1) = (128 x 1)
  ponderi       intrare     ieșire

Dimensiuni interioare: 784 = 784  -- valid

Acesta este motivul pentru care PyTorch raportează o eroare de incompatibilitate a dimensiunilor.

Harta operațiilor

Operație Ce face Utilizare în rețele neuronale
Adunare Combină elementele aflate pe poziții corespunzătoare Adăugarea biasului la ieșire
Înmulțire cu un scalar Scalează fiecare element Rata de învățare * gradienți
Înmulțirea matricelor Transformă vectori Propagarea înainte prin strat
Transpunere Interschimbă liniile și coloanele Retropropagare
Determinant Rezumă matricea printr-un singur număr Verificarea inversabilității
Inversă Anulează o transformare Rezolvarea sistemelor liniare
Matrice unitate Nu modifică vectorul Inițializare, conexiuni reziduale

Înmulțirea element cu element și înmulțirea matricelor

Această diferență le creează permanent probleme începătorilor.

La înmulțirea element cu element se înmulțesc valorile aflate pe poziții corespunzătoare. Cele două matrice trebuie să aibă aceleași dimensiuni.

| 1  2 |   | 5  6 |   | 5  12 |
| 3  4 | * | 7  8 | = | 21 32 |

La înmulțirea matricelor se calculează produsele scalare dintre linii și coloane. Dimensiunile interioare trebuie să coincidă.

| 1  2 |   | 5  6 |   | 1*5+2*7  1*6+2*8 |   | 19  22 |
| 3  4 | @ | 7  8 | = | 3*5+4*7  3*6+4*8 | = | 43  50 |

Sunt operații diferite, cu rezultate și reguli diferite.

Extinderea automată (broadcasting)

Când adăugați un vector de bias la o matrice de ieșiri, dimensiunile nu coincid. Broadcastingul extinde tabloul mai mic pentru a se potrivi cu cel mai mare.

| 1  2  3 |   +   [10, 20, 30]
| 4  5  6 |

Broadcasting-ul extinde vectorul pe linii:

| 1  2  3 |   | 10  20  30 |   | 11  22  33 |
| 4  5  6 | + | 10  20  30 | = | 14  25  36 |

Orice framework modern face automat acest lucru. Înțelegerea mecanismului previne confuzia atunci când dimensiunile par greșite, dar codul rulează.

vector-projection

Implementare

Pasul 1: clasa Vector

class Vector:
    def __init__(self, data):
        self.data = list(data)
        self.size = len(self.data)

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

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

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

    def __mul__(self, scalar):
        return Vector([x * scalar for x in self.data])

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

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

Pasul 2: clasa Matrix cu operațiile de bază

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

    def __repr__(self):
        rows_str = "\n  ".join(str(row) for row in self.data)
        return f"Matrix({self.shape}):\n  {rows_str}"

    def __add__(self, other):
        return Matrix([
            [self.data[i][j] + other.data[i][j] for j in range(self.cols)]
            for i in range(self.rows)
        ])

    def __sub__(self, other):
        return Matrix([
            [self.data[i][j] - other.data[i][j] for j in range(self.cols)]
            for i in range(self.rows)
        ])

    def scalar_multiply(self, scalar):
        return Matrix([
            [self.data[i][j] * scalar for j in range(self.cols)]
            for i in range(self.rows)
        ])

    def element_wise_multiply(self, other):
        return Matrix([
            [self.data[i][j] * other.data[i][j] for j in range(self.cols)]
            for i in range(self.rows)
        ])

    def matmul(self, other):
        return Matrix([
            [
                sum(self.data[i][k] * other.data[k][j] for k in range(self.cols))
                for j in range(other.cols)
            ]
            for i in range(self.rows)
        ])

    def transpose(self):
        return Matrix([
            [self.data[j][i] for j in range(self.rows)]
            for i in range(self.cols)
        ])

    def determinant(self):
        if self.shape == (1, 1):
            return self.data[0][0]
        if self.shape == (2, 2):
            return self.data[0][0] * self.data[1][1] - self.data[0][1] * self.data[1][0]
        det = 0
        for j in range(self.cols):
            minor = Matrix([
                [self.data[i][k] for k in range(self.cols) if k != j]
                for i in range(1, self.rows)
            ])
            det += ((-1) ** j) * self.data[0][j] * minor.determinant()
        return det

    def inverse_2x2(self):
        det = self.determinant()
        if det == 0:
            raise ValueError("Matrix is singular, no inverse exists")
        return Matrix([
            [self.data[1][1] / det, -self.data[0][1] / det],
            [-self.data[1][0] / det, self.data[0][0] / det]
        ])

    @staticmethod
    def identity(n):
        return Matrix([
            [1 if i == j else 0 for j in range(n)]
            for i in range(n)
        ])

Pasul 3: testați implementarea

A = Matrix([[1, 2], [3, 4]])
B = Matrix([[5, 6], [7, 8]])

print("A + B =", (A + B).data)
print("A @ B =", A.matmul(B).data)
print("A^T =", A.transpose().data)
print("det(A) =", A.determinant())
print("A^-1 =", A.inverse_2x2().data)

I = Matrix.identity(2)
print("A @ A^-1 =", A.matmul(A.inverse_2x2()).data)

Pasul 4: legătura cu rețelele neuronale

import random

inputs = Matrix([[0.5], [0.8], [0.2]])
weights = Matrix([
    [random.uniform(-1, 1) for _ in range(3)]
    for _ in range(2)
])
bias = Matrix([[0.1], [0.1]])

def relu_matrix(m):
    return Matrix([[max(0, val) for val in row] for row in m.data])

pre_activation = weights.matmul(inputs) + bias
output = relu_matrix(pre_activation)

print(f"Input shape: {inputs.shape}")
print(f"Weight shape: {weights.shape}")
print(f"Output shape: {output.shape}")
print(f"Output: {output.data}")

Acesta este un singur strat dens: output = relu(W @ x + b). Fiecare strat dens din orice rețea neuronală efectuează exact aceste operații.

Utilizarea implementării

NumPy efectuează toate operațiile de mai sus în mai puține linii și de câteva ordine de mărime mai rapid.

import numpy as np

A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])

print("A + B =\n", A + B)
print("A * B (element-wise) =\n", A * B)
print("A @ B (matrix multiply) =\n", A @ B)
print("A^T =\n", A.T)
print("det(A) =", np.linalg.det(A))
print("A^-1 =\n", np.linalg.inv(A))
print("I =\n", np.eye(2))

inputs = np.random.randn(3, 1)
weights = np.random.randn(2, 3)
bias = np.array([[0.1], [0.1]])
output = np.maximum(0, weights @ inputs + bias)

print(f"\nNeural network layer: {weights.shape} @ {inputs.shape} = {output.shape}")
print(f"Output:\n{output}")

Operatorul @ din Python apelează __matmul__. NumPy îl implementează prin rutine BLAS optimizate, scrise în C și Fortran. Aceeași matematică, dar de 100 de ori mai rapidă.

Broadcasting în NumPy:

matrix = np.array([[1, 2, 3], [4, 5, 6]])
bias = np.array([10, 20, 30])
print(matrix + bias)

NumPy extinde automat vectorul unidimensional de bias pe ambele linii. Astfel funcționează adăugarea biasului în orice framework pentru rețele neuronale.

Livrarea rezultatului

În urma acestei lecții obțineți un prompt pentru predarea operațiilor cu matrice prin intuiție geometrică. Consultați outputs/prompt-matrix-operations.md.

Clasa Matrix construită aici constituie baza mini-frameworkului pentru rețele neuronale pe care îl vom realiza în faza 3, lecția 10.

Exerciții

  1. Verificați inversa. Înmulțiți A @ A.inverse_2x2() și confirmați că obțineți matricea unitate. Încercați operația cu trei matrice 2x2 diferite. Ce se întâmplă când determinantul este zero?

  2. Implementați inversa unei matrice 3x3. Extindeți clasa Matrix astfel încât să calculeze inversele matricelor 3x3 prin metoda matricei adjuncte. Verificați rezultatul prin comparație cu np.linalg.inv din NumPy.

  3. Construiți o rețea cu două straturi. Folosind numai clasa Matrix creată de dvs. (fără NumPy), construiți o rețea neuronală cu două straturi: intrare (3) -> ascuns (4) -> ieșire (2). Inițializați ponderi aleatorii, efectuați o propagare înainte și verificați dacă toate dimensiunile sunt corecte.

Termeni-cheie

Termen Cum îi spune lumea Ce înseamnă de fapt
Vector „O săgeată” O listă ordonată de numere. În IA: un punct într-un spațiu cu multe dimensiuni.
Matrice „Un tabel de numere” O transformare liniară. Transformă vectorii dintr-un spațiu în altul.
Înmulțirea matricelor „Doar înmulțiți numerele” Produse scalare între fiecare linie a primei matrice și fiecare coloană a celei de-a doua. Ordinea contează.
Transpunere „O răsturnați” Interschimbarea liniilor cu coloanele. Transformă o matrice m x n într-o matrice n x m. Este esențială în retropropagare.
Determinant „Un număr oarecare obținut din matrice” Măsoară factorul cu care matricea scalează aria (în 2D) sau volumul (în 3D). Valoarea zero înseamnă că transformarea reduce o dimensiune la zero.
Inversă „Anulați matricea” Matricea care inversează transformarea. Există numai atunci când determinantul este nenul.
Matrice unitate „Matricea banală” Echivalentul matricial al înmulțirii cu 1. Este folosită în conexiunile reziduale (ResNet).
Broadcasting „Corectarea magică a dimensiunilor” Extinderea unui tablou mai mic pentru a corespunde unuia mai mare, prin repetare de-a lungul dimensiunilor care lipsesc.
Element cu element „Înmulțirea obișnuită” Înmulțirea valorilor aflate pe poziții corespunzătoare. Ambele tablouri trebuie să aibă aceleași dimensiuni sau să permită broadcasting.

Lecturi suplimentare


Sursă: Vectors, Matrices & Operations — original

Navigare: înapoi: 01.01 — Intuiție pentru algebra liniară · Faza 1 — Fundamente matematice · Catalog complet · în continuare: 01.03 — Transformări matriciale.