Phase 01 - Lesson 03

Transformações Matriciais

This lesson includes a graded coding exercise that runs in your browser, unlocked with lifetime access.

Uma matriz é uma máquina que remodela o espaço. Aprenda o que ela faz com cada ponto e você entenderá a transformação inteira.

Tipo: Construir Linguagens: Python, Julia Pré-requisitos: Fase 1, Lições 01-02 (Intuição de Álgebra Linear, Operações de Vetores e Matrizes) Tempo: ~75 minutos

Objetivos de Aprendizagem

  • Construir matrizes de rotação, escala, cisalhamento e reflexão e aplicá-las a pontos 2D e 3D
  • Compor múltiplas transformações por multiplicação de matrizes e verificar que a ordem importa
  • Calcular autovalores e autovetores de matrizes 2x2 a partir da equação característica
  • Explicar por que os autovalores determinam as direções do PCA, a estabilidade de RNNs e o comportamento do clustering espectral

O Problema

Você lê sobre PCA e vê "encontre os autovetores da matriz de covariância". Você lê sobre estabilidade de modelos e vê "verifique se todos os autovalores têm magnitude menor que 1". Você lê sobre data augmentation e vê "aplique uma rotação aleatória". Nada disso faz sentido até você entender o que as matrizes fazem com o espaço geometricamente.

Matrizes não são apenas grades de números. São máquinas espaciais. Uma matriz de rotação gira pontos. Uma matriz de escala os estica. Uma matriz de cisalhamento os inclina. Toda transformação que uma rede neural aplica aos dados é uma dessas operações ou uma composição delas. Esta lição torna essas operações concretas.

O Conceito

Transformações como matrizes

Toda transformação linear em 2D pode ser escrita como uma matriz 2x2. A matriz te diz exatamente onde os vetores da base [1, 0] e [0, 1] vão parar. Todo o resto decorre disso.

graph LR
    subgraph Before["Standard Basis"]
        e1["e1 = [1, 0] (along x)"]
        e2["e2 = [0, 1] (along y)"]
    end
    subgraph Transform["Matrix M"]
        M["M = columns are new basis vectors"]
    end
    subgraph After["After Transformation M"]
        e1p["e1' = new x-basis"]
        e2p["e2' = new y-basis"]
    end
    e1 --> M --> e1p
    e2 --> M --> e2p

Rotação

Uma rotação 2D pelo ângulo theta mantém distâncias e ângulos intactos. Ela move cada ponto ao longo de um arco circular.

graph LR
    subgraph Before["Before Rotation"]
        A["A(2, 1)"]
        B["B(0, 2)"]
    end
    subgraph Rot["Rotate 45 degrees"]
        R["R(θ) = [[cos θ, -sin θ], [sin θ, cos θ]]"]
    end
    subgraph After["After Rotation"]
        Ap["A'(0.71, 2.12)"]
        Bp["B'(-1.41, 1.41)"]
    end
    A --> R --> Ap
    B --> R --> Bp

Em 3D, você rotaciona em torno de um eixo. Cada eixo tem sua própria matriz de rotação:

Rz(theta) = | cos  -sin  0 |     Rotate around z-axis
            | sin   cos  0 |     (x-y plane spins, z stays)
            |  0     0   1 |

Rx(theta) = | 1   0     0    |   Rotate around x-axis
            | 0  cos  -sin   |   (y-z plane spins, x stays)
            | 0  sin   cos   |

Ry(theta) = |  cos  0  sin |     Rotate around y-axis
            |   0   1   0  |     (x-z plane spins, y stays)
            | -sin  0  cos |

Escala

A escala estica ou comprime ao longo de cada eixo de forma independente.

graph LR
    subgraph Before["Before Scaling"]
        A["A(2, 1)"]
        B["B(0, 2)"]
    end
    subgraph Scale["Scale sx=2, sy=0.5"]
        S["S = [[2, 0], [0, 0.5]]"]
    end
    subgraph After["After Scaling"]
        Ap["A'(4, 0.5)"]
        Bp["B'(0, 1)"]
    end
    A --> S --> Ap
    B --> S --> Bp

Cisalhamento

O cisalhamento inclina um eixo enquanto mantém o outro fixo. Ele transforma retângulos em paralelogramos.

graph LR
    subgraph Before["Before Shear"]
        A["A(1, 0)"]
        B["B(0, 1)"]
    end
    subgraph Shear["Shear in x, k=1"]
        Sh["Shx = [[1, k], [0, 1]]"]
    end
    subgraph After["After Shear"]
        Ap["A(1, 0) unchanged"]
        Bp["B'(1, 1) shifted"]
    end
    A --> Sh --> Ap
    B --> Sh --> Bp

Matrizes de cisalhamento:

  • Shx = [[1, k], [0, 1]] desloca x por k * y
  • Shy = [[1, 0], [k, 1]] desloca y por k * x

Reflexão

A reflexão espelha pontos através de um eixo ou linha.

graph LR
    subgraph Before["Before Reflection"]
        A["A(2, 1)"]
    end
    subgraph Reflect["Reflect across y-axis"]
        R["[[-1, 0], [0, 1]]"]
    end
    subgraph After["After Reflection"]
        Ap["A'(-2, 1)"]
    end
    A --> R --> Ap

Matrizes de reflexão:

  • Refletir através do eixo y: [[-1, 0], [0, 1]]
  • Refletir através do eixo x: [[1, 0], [0, -1]]

Composição: encadeando transformações

Aplicar a transformação A e depois B é o mesmo que multiplicar suas matrizes: result = B @ A @ point. A ordem importa. Rotacionar e depois escalar dá resultados diferentes de escalar e depois rotacionar.

graph LR
    subgraph Path1["Rotate 90 then Scale (2, 0.5)"]
        P1["(1, 0)"] -->|"Rotate 90"| P2["(0, 1)"] -->|"Scale"| P3["(0, 0.5)"]
    end

Composta: S @ R = [[0, -2], [0.5, 0]]

graph LR
    subgraph Path2["Scale (2, 0.5) then Rotate 90"]
        Q1["(1, 0)"] -->|"Scale"| Q2["(2, 0)"] -->|"Rotate 90"| Q3["(0, 2)"]
    end

Composta: R @ S = [[0, -0.5], [2, 0]]

Resultados diferentes. A multiplicação de matrizes não é comutativa.

Autovalores e autovetores

A maioria dos vetores muda de direção quando uma matriz os atinge. Os autovetores são especiais: a matriz apenas os escala, nunca os rotaciona. O fator de escala é o autovalor.

A @ v = lambda * v

v is the eigenvector (direction that survives)
lambda is the eigenvalue (how much it stretches)

Example: A = | 2  1 |
             | 1  2 |

Eigenvector [1, 1] with eigenvalue 3:
  A @ [1,1] = [3, 3] = 3 * [1, 1]     (same direction, scaled by 3)

Eigenvector [1, -1] with eigenvalue 1:
  A @ [1,-1] = [1, -1] = 1 * [1, -1]  (same direction, unchanged)

A matriz estica o espaço em 3x ao longo de [1, 1] e mantém [1, -1] inalterado. Qualquer outra direção é uma mistura dessas duas.

Autodecomposição

Se uma matriz tem n autovetores linearmente independentes, ela pode ser decomposta:

A = V @ D @ V^(-1)

V = matrix whose columns are eigenvectors
D = diagonal matrix of eigenvalues
V^(-1) = inverse of V

This says: rotate into eigenvector coordinates, scale along each axis, rotate back.

Por que os autovalores importam

PCA. Os autovetores da matriz de covariância são as componentes principais. Os autovalores te dizem quanta variância cada componente captura. Ordene por autovalor, mantenha os k maiores e você tem redução de dimensionalidade.

Estabilidade. Em redes recorrentes e sistemas dinâmicos, autovalores com magnitude > 1 fazem as saídas explodirem. Magnitude < 1 faz com que elas desapareçam. Esse é o problema do gradiente que desaparece/explode declarado em uma frase.

Métodos espectrais. Redes neurais de grafos usam autovalores da matriz de adjacência. O clustering espectral usa autovalores do Laplaciano. Os autovetores revelam a estrutura do grafo.

Determinante como fator de escala de volume

O determinante de uma matriz de transformação te diz o quanto ela escala área (2D) ou volume (3D).

det = 1:   area preserved (rotation)
det = 2:   area doubled
det = 0:   space crushed to lower dimension (singular)
det = -1:  area preserved but orientation flipped (reflection)

| det(Rotation) | = 1        (always)
| det(Scale sx, sy) | = sx * sy
| det(Shear) | = 1           (area preserved)
| det(Reflection) | = -1     (orientation flipped)

Construa

Passo 1: Matrizes de transformação do zero (Python)

import math

def rotation_2d(theta):
    c, s = math.cos(theta), math.sin(theta)
    return [[c, -s], [s, c]]

def scaling_2d(sx, sy):
    return [[sx, 0], [0, sy]]

def shearing_2d(kx, ky):
    return [[1, kx], [ky, 1]]

def reflection_x():
    return [[1, 0], [0, -1]]

def reflection_y():
    return [[-1, 0], [0, 1]]

def mat_vec_mul(matrix, vector):
    return [
        sum(matrix[i][j] * vector[j] for j in range(len(vector)))
        for i in range(len(matrix))
    ]

def mat_mul(a, b):
    rows_a, cols_b = len(a), len(b[0])
    cols_a = len(a[0])
    return [
        [sum(a[i][k] * b[k][j] for k in range(cols_a)) for j in range(cols_b)]
        for i in range(rows_a)
    ]

point = [1.0, 0.0]
angle = math.pi / 4

rotated = mat_vec_mul(rotation_2d(angle), point)
print(f"Rotate (1,0) by 45 deg: ({rotated[0]:.4f}, {rotated[1]:.4f})")

scaled = mat_vec_mul(scaling_2d(2, 3), [1.0, 1.0])
print(f"Scale (1,1) by (2,3): ({scaled[0]:.1f}, {scaled[1]:.1f})")

sheared = mat_vec_mul(shearing_2d(1, 0), [1.0, 1.0])
print(f"Shear (1,1) kx=1: ({sheared[0]:.1f}, {sheared[1]:.1f})")

reflected = mat_vec_mul(reflection_y(), [2.0, 1.0])
print(f"Reflect (2,1) across y: ({reflected[0]:.1f}, {reflected[1]:.1f})")

Passo 2: Composição de transformações

R = rotation_2d(math.pi / 2)
S = scaling_2d(2, 0.5)

rotate_then_scale = mat_mul(S, R)
scale_then_rotate = mat_mul(R, S)

point = [1.0, 0.0]
result1 = mat_vec_mul(rotate_then_scale, point)
result2 = mat_vec_mul(scale_then_rotate, point)

print(f"Rotate 90 then scale: ({result1[0]:.2f}, {result1[1]:.2f})")
print(f"Scale then rotate 90: ({result2[0]:.2f}, {result2[1]:.2f})")
print(f"Same? {result1 == result2}")

Passo 3: Autovalores do zero (2x2)

Para uma matriz 2x2 [[a, b], [c, d]], os autovalores resolvem a equação característica: lambda^2 - (a+d)*lambda + (ad - bc) = 0.

def eigenvalues_2x2(matrix):
    a, b = matrix[0]
    c, d = matrix[1]
    trace = a + d
    det = a * d - b * c
    discriminant = trace ** 2 - 4 * det
    if discriminant < 0:
        real = trace / 2
        imag = (-discriminant) ** 0.5 / 2
        return (complex(real, imag), complex(real, -imag))
    sqrt_disc = discriminant ** 0.5
    return ((trace + sqrt_disc) / 2, (trace - sqrt_disc) / 2)

def eigenvector_2x2(matrix, eigenvalue):
    a, b = matrix[0]
    c, d = matrix[1]
    if abs(b) > 1e-10:
        v = [b, eigenvalue - a]
    elif abs(c) > 1e-10:
        v = [eigenvalue - d, c]
    else:
        if abs(a - eigenvalue) < 1e-10:
            v = [1, 0]
        else:
            v = [0, 1]
    mag = (v[0] ** 2 + v[1] ** 2) ** 0.5
    return [v[0] / mag, v[1] / mag]

A = [[2, 1], [1, 2]]
vals = eigenvalues_2x2(A)
print(f"Matrix: {A}")
print(f"Eigenvalues: {vals[0]:.4f}, {vals[1]:.4f}")

for val in vals:
    vec = eigenvector_2x2(A, val)
    result = mat_vec_mul(A, vec)
    scaled = [val * vec[0], val * vec[1]]
    print(f"  lambda={val:.1f}, v={[round(x,4) for x in vec]}")
    print(f"    A@v = {[round(x,4) for x in result]}")
    print(f"    l*v = {[round(x,4) for x in scaled]}")

Passo 4: Determinante como fator de escala de volume

def det_2x2(matrix):
    return matrix[0][0] * matrix[1][1] - matrix[0][1] * matrix[1][0]

print(f"det(rotation 45) = {det_2x2(rotation_2d(math.pi/4)):.4f}")
print(f"det(scale 2,3)   = {det_2x2(scaling_2d(2, 3)):.1f}")
print(f"det(shear kx=1)  = {det_2x2(shearing_2d(1, 0)):.1f}")
print(f"det(reflect y)   = {det_2x2(reflection_y()):.1f}")

singular = [[1, 2], [2, 4]]
print(f"det(singular)     = {det_2x2(singular):.1f}")
print("Singular: columns are proportional, space collapses to a line.")

Use

O NumPy lida com tudo isso com rotinas otimizadas.

import numpy as np

theta = np.pi / 4
R = np.array([[np.cos(theta), -np.sin(theta)],
              [np.sin(theta),  np.cos(theta)]])

point = np.array([1.0, 0.0])
print(f"Rotate (1,0) by 45 deg: {R @ point}")

S = np.diag([2.0, 3.0])
composed = S @ R
print(f"Scale(2,3) after Rotate(45): {composed @ point}")

A = np.array([[2, 1], [1, 2]], dtype=float)
eigenvalues, eigenvectors = np.linalg.eig(A)
print(f"\nEigenvalues: {eigenvalues}")
print(f"Eigenvectors (columns):\n{eigenvectors}")

for i in range(len(eigenvalues)):
    v = eigenvectors[:, i]
    lam = eigenvalues[i]
    print(f"  A @ v{i} = {A @ v}, lambda * v{i} = {lam * v}")

print(f"\ndet(R) = {np.linalg.det(R):.4f}")
print(f"det(S) = {np.linalg.det(S):.1f}")

B = np.array([[3, 1], [0, 2]], dtype=float)
vals, vecs = np.linalg.eig(B)
D = np.diag(vals)
V = vecs
reconstructed = V @ D @ np.linalg.inv(V)
print(f"\nEigendecomposition A = V @ D @ V^-1:")
print(f"Original:\n{B}")
print(f"Reconstructed:\n{reconstructed}")

Rotações 3D com NumPy

def rotation_3d_z(theta):
    c, s = np.cos(theta), np.sin(theta)
    return np.array([[c, -s, 0], [s, c, 0], [0, 0, 1]])

def rotation_3d_x(theta):
    c, s = np.cos(theta), np.sin(theta)
    return np.array([[1, 0, 0], [0, c, -s], [0, s, c]])

point_3d = np.array([1.0, 0.0, 0.0])
rotated_z = rotation_3d_z(np.pi / 2) @ point_3d
rotated_x = rotation_3d_x(np.pi / 2) @ point_3d

print(f"\n3D point: {point_3d}")
print(f"Rotate 90 around z: {np.round(rotated_z, 4)}")
print(f"Rotate 90 around x: {np.round(rotated_x, 4)}")

Entregue

Esta lição constrói a base geométrica para o PCA (Fase 2) e a análise de pesos de redes neurais. O código de autovalor/autovetor construído aqui é o mesmo algoritmo que alimenta a redução de dimensionalidade, o clustering espectral e a análise de estabilidade em sistemas de ML em produção.

Exercícios

  1. Aplique rotação, escala e cisalhamento a um quadrado unitário (cantos em [0,0], [1,0], [1,1], [0,1]). Imprima os cantos transformados de cada um. Verifique que a rotação preserva as distâncias entre os cantos.

  2. Encontre os autovalores da matriz [[4, 2], [1, 3]] à mão usando a equação característica. Depois verifique com sua função feita do zero e com o NumPy.

  3. Crie uma composição de três transformações (rotacionar 30 graus, escalar por [1.5, 0.8], cisalhar com kx=0.3) e aplique-a a 8 pontos dispostos em um círculo. Imprima as coordenadas antes e depois. Calcule o determinante da matriz composta e verifique que ele é igual ao produto dos determinantes individuais.

Termos-chave

Termo O que as pessoas dizem O que realmente significa
Matriz de rotação "Gira coisas" Uma matriz ortogonal que move pontos ao longo de arcos circulares preservando distâncias e ângulos. O determinante é sempre 1.
Matriz de escala "Aumenta as coisas" Uma matriz diagonal que estica ou comprime de forma independente ao longo de cada eixo. O determinante é o produto dos fatores de escala.
Matriz de cisalhamento "Inclina as coisas" Uma matriz que desloca uma coordenada proporcionalmente a outra, transformando retângulos em paralelogramos. O determinante é 1.
Reflexão "Espelha as coisas" Uma matriz que inverte o espaço através de um eixo ou plano. O determinante é -1.
Composição "Fazer duas coisas" Multiplicar matrizes de transformação para encadear operações. A ordem importa: B @ A significa aplicar A primeiro, depois B.
Autovetor "Direção especial" Uma direção que a matriz apenas escala, nunca rotaciona. A impressão digital da transformação.
Autovalor "Quanto ela estica" O fator escalar pelo qual a matriz escala seu autovetor. Pode ser negativo (inverte) ou complexo (rotação).
Autodecomposição "Decompor a matriz" Escrever uma matriz como V @ D @ V^(-1), separando-a em suas direções e magnitudes fundamentais de escala.
Determinante "Um único número de uma matriz" O fator pelo qual a transformação escala área (2D) ou volume (3D). Zero significa que a transformação é irreversível.
Equação característica "De onde vem os autovalores" det(A - lambda * I) = 0. O polinômio cujas raízes são os autovalores.

Leitura Complementar

0 lifetime access. Curriculum based on AI Engineering from Scratch by Rohit Ghumare (MIT, used under attribution).