Sesión 9: Tensores

Teoría

1. Definición de tensor, orden (rank), forma (shape)

Un tensor es una generalización de los conceptos de escalar, vector y matriz a un número arbitrario de dimensiones. Es una estructura de datos multidimensional que contiene elementos (generalmente números reales) organizados en una cuadrícula regular.

Objeto Orden (rank) Forma (shape) Ejemplo
Escalar 0 () 5
Vector 1 (n,) [1, 2, 3]
Matriz 2 (m, n) [[1,2], [3,4]]
Tensor 3D 3 (d1, d2, d3) Imagen en escala de grises: (alto, ancho, 1)
Tensor 4D 4 (d1, d2, d3, d4) Lote de imágenes color: (batch, alto, ancho, canales)

Notación: Usaremos negrita para tensores de orden ≥ 1. Por ejemplo, $\mathcal{T}$ para un tensor de orden 3.

Ejemplo 1: Un tensor de orden 3 con forma (2, 3, 4) tiene 2 × 3 × 4 = 24 elementos. Podemos visualizarlo como 2 matrices de 3×4 apiladas.

Ejemplo 2: En Python con NumPy o PyTorch:

import numpy as np
escalar = np.array(5)
vector = np.array([1, 2, 3])
matriz = np.array([[1,2], [3,4]])
tensor_3d = np.zeros((2,3,4))  # tensor de ceros de forma (2,3,4)
print(tensor_3d.shape)  # (2, 3, 4)

2. Representación de datos multidimensionales

Los tensores son la forma natural de representar datos en IA, ya que la mayoría de los datos tienen estructura multidimensional.

Imágenes (alto, ancho, canales)

Ejemplo: Una imagen RGB de 224×224 píxeles se representa como un tensor de forma (224, 224, 3). Un lote de 32 imágenes: (32, 224, 224, 3).

Secuencias (batch, tiempo, features)

Ejemplo: Un lote de 10 frases, cada una con hasta 20 palabras, usando embeddings de 300 dimensiones: tensor de forma (10, 20, 300). Las frases más cortas se rellenan (padding) con ceros.

Lotes (batch)

La dimensión de lote (batch) agrupa múltiples muestras independientes para procesamiento paralelo. Es común en todos los modelos de deep learning.

3. Operaciones tensoriales

Suma elemento a elemento

Dos tensores de la misma forma se suman componente a componente.

Ejemplo: $\mathcal{A} = \begin{pmatrix} 1 & 2 \ 3 & 4 \end{pmatrix}$, $\mathcal{B} = \begin{pmatrix} 5 & 6 \ 7 & 8 \end{pmatrix}$
$\mathcal{A} + \mathcal{B} = \begin{pmatrix} 6 & 8 \ 10 & 12 \end{pmatrix}$

Multiplicación elemento a elemento (Hadamard)

También llamada producto de Hadamard: $(\mathcal{A} \odot \mathcal{B}){ijk} = a{ijk} b_{ijk}$.

Ejemplo: $\begin{pmatrix} 1 & 2 \ 3 & 4 \end{pmatrix} \odot \begin{pmatrix} 5 & 6 \ 7 & 8 \end{pmatrix} = \begin{pmatrix} 5 & 12 \ 21 & 32 \end{pmatrix}$

Producto tensorial (contracciones)

Generaliza el producto de matrices a tensores de orden superior. La operación más común es el producto punto (suma sobre un eje común). Por ejemplo, el producto matriz-vector: $\mathbf{c} = A \mathbf{v}$ equivale a $c_i = \sum_j A_{ij} v_j$.

Para tensores de orden 3, podemos tener contracciones como $\mathcal{C}{ik} = \sum_j \mathcal{A}{ijk} \mathcal{B}_{jl}$ (sumando sobre j).

4. Broadcasting

El broadcasting es una técnica que permite realizar operaciones entre tensores de diferentes formas, “expandiendo” automáticamente las dimensiones más pequeñas para que coincidan con las más grandes. Las reglas:

  1. Las dimensiones se comparan desde la última hacia la primera.
  2. Dos dimensiones son compatibles si son iguales o una de ellas es 1 (o no existe).
  3. Si una dimensión falta en un tensor, se trata como 1.

Ejemplos paso a paso:

Ejemplo numérico:
A = [[1, 2, 3], [4, 5, 6]] (2×3)
B = [10, 20, 30] (3,)
Broadcasting: B se expande a [[10,20,30], [10,20,30]]
A + B = [[11,22,33], [14,25,36]]

5. Reorganización de dimensiones

Reshape

Cambia la forma de un tensor sin alterar el orden de los datos ni el número total de elementos.

Ejemplo: Un tensor de forma (2,3,4) con 24 elementos puede reconfigurarse a (6,4), (4,3,2), (2,12), etc., siempre que el producto de las dimensiones sea 24.

import torch
x = torch.randn(2, 3, 4)
y = x.reshape(6, 4)   # ahora y tiene 6 filas y 4 columnas
Transpose (trasponer)

Intercambia dos dimensiones específicas. Para matrices, es la operación habitual.

Ejemplo: $A$ de forma (3,4) → $A^T$ de forma (4,3).

Permute

Reordena todas las dimensiones según un nuevo orden.

Ejemplo: Un tensor de forma (batch, canales, alto, ancho) en PyTorch puede reordenarse a (batch, alto, ancho, canales) con permute(0, 2, 3, 1).

Squeeze y Unsqueeze

Ejemplo:
x = torch.randn(1, 3, 1, 4)
x.squeeze() → forma (3, 4) (elimina dimensiones con tamaño 1)
x.unsqueeze(0) → forma (1, 1, 3, 1, 4) (añade una dimensión al inicio)

6. Producto punto generalizado (einsum)

La notación einsum (de Einstein summation) permite expresar operaciones tensoriales complejas de manera concisa. Se basa en la convención de suma de Einstein: índices repetidos en una expresión se suman.

Sintaxis: einsum(índices, tensor1, tensor2, ...)

Ejemplos:

Ejemplo numérico con NumPy:

import numpy as np
A = np.array([[1,2], [3,4]])
B = np.array([[5,6], [7,8]])
C = np.einsum('ij,jk->ik', A, B)  # producto matricial
print(C)  # [[19,22], [43,50]]

7. Tensores en frameworks: PyTorch, TensorFlow

Los frameworks modernos de deep learning están construidos alrededor de tensores y operaciones diferenciables.

Ejemplo en PyTorch:

import torch
x = torch.randn(32, 3, 224, 224)  # lote de 32 imágenes RGB
w = torch.randn(64, 3, 3, 3)      # 64 kernels de 3×3 con 3 canales de entrada
y = torch.nn.functional.conv2d(x, w)  # convolución

8. Aplicaciones en arquitecturas de deep learning

CNNs (tensores 4D: batch, canales, alto, ancho)

En PyTorch, la convención es (batch, canales, alto, ancho). En TensorFlow, a veces es (batch, alto, ancho, canales). La operación de convolución aplica kernels (filtros) que son tensores 4D: (canales_salida, canales_entrada, alto_kernel, ancho_kernel).

RNNs (tensores 3D: batch, tiempo, features)

Una RNN procesa secuencias. La entrada típica es (batch, seq_len, input_size). La salida puede ser (batch, seq_len, hidden_size) o solo el último estado.

Transformers (tensores 3D: batch, secuencia, embedding)

En transformers, la entrada es un tensor de forma (batch, seq_len, d_model). Las matrices de consultas (Q), claves (K) y valores (V) se obtienen multiplicando por matrices de pesos, y la atención se calcula con productos punto entre Q y K.


Aplicaciones prácticas

Machine Learning (ML)

1. Reducción de dimensionalidad con SVD o PCA

Aunque SVD y PCA se aplican a matrices, cuando tenemos datos multidimensionales a menudo los convertimos en matrices. Por ejemplo, un dataset de imágenes puede aplanarse: cada imagen de (alto, ancho, canales) se convierte en un vector, formando una matriz de (n_muestras, alto×ancho×canales). Luego se aplica SVD para reducir dimensionalidad.

Ejemplo con imágenes: 1000 imágenes de 64×64×3 → matriz de 1000 × (64×64×3) = 1000 × 12288. SVD produce componentes principales que son vectores de longitud 12288, que pueden reconfigurarse a forma (64,64,3) para visualizar los “eigen-images”.

2. One-hot encoding

La codificación one-hot convierte variables categóricas en vectores binarios. Por ejemplo, si tenemos 10 categorías, cada categoría se representa como un vector de 10 elementos con un 1 en la posición correspondiente. Esto produce una matriz o tensor.

Ejemplo: Un lote de 5 palabras de un vocabulario de 1000 palabras produce un tensor one-hot de forma (5, 1000). En procesamiento de lenguaje, esto se usa antes de las capas de embedding.

Deep Learning (DL)

1. CNN: Representación de imágenes y operación de convolución

En una CNN, las imágenes se representan como tensores 4D: (batch, canales, altura, ancho). Los kernels (filtros) son también tensores 4D: (filtros, canales_entrada, altura_kernel, ancho_kernel). La convolución 2D realiza una suma ponderada local:

\[\text{output}[b, f, i, j] = \sum_{c=0}^{C-1} \sum_{u=0}^{H-1} \sum_{v=0}^{W-1} \text{input}[b, c, i+u, j+v] \cdot \text{weight}[f, c, u, v] + \text{bias}[f]\]

Esta operación es una contracción tensorial (suma sobre canales de entrada y dimensiones espaciales del kernel).

Ejemplo: Entrada: (32, 3, 224, 224) (lote de 32 imágenes RGB 224×224)
Convolución con 64 kernels de 3×3: peso (64, 3, 3, 3)
Salida: (32, 64, 222, 222) (sin padding) o (32, 64, 224, 224) con padding.

2. Embeddings: Conversión de palabras en vectores densos

Una capa de embedding es esencialmente una matriz de lookup: para cada índice de palabra, devuelve un vector denso. La entrada es un tensor de índices (por ejemplo, (batch, seq_len)) y la salida es un tensor (batch, seq_len, embedding_dim). Esto se implementa como una multiplicación de la matriz de embedding por los one-hot vectors (eficientemente mediante indexing).

Ejemplo: Vocabulario de 10,000 palabras, embedding de 300 dimensiones. Una frase de 20 palabras produce un tensor de entrada de índices (1, 20). La capa de embedding produce un tensor (1, 20, 300).

3. RNN: Modelado de secuencias

En una RNN, la entrada es un tensor 3D: (batch, seq_len, input_size). El estado oculto es un tensor 2D: (batch, hidden_size) (o 3D si se devuelven todos los estados). La operación recurrente es:

\[h_t = \tanh(W_{ih} x_t + b_{ih} + W_{hh} h_{t-1} + b_{hh})\]

Esto se aplica a cada paso de tiempo, procesando el tensor a lo largo de la dimensión de secuencia.

Ejemplo: Series temporales de 100 pasos con 5 características, lote de 64: entrada (64, 100, 5). Una RNN con hidden_size=128 produce salida (64, 100, 128) si se devuelven todas las secuencias, o (64, 128) si solo el último estado.

4. Transformers: Atención entre tensores Q, K, V

En un transformer, la atención escalada por producto punto se calcula como:

\[\text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right) V\]

Donde Q, K, V son tensores de forma (batch, seq_len, d_k). El producto $QK^T$ tiene forma (batch, seq_len, seq_len) y luego se multiplica por V para obtener (batch, seq_len, d_v). Todo esto son operaciones tensoriales.

Ejemplo: Entrada de 32 frases, cada una de hasta 50 palabras, embedding de 512 dimensiones: Q, K, V son (32, 50, 512). La atención produce (32, 50, 512).

5. Vision Transformers (ViT): Parcheo de imágenes

En ViT, una imagen se divide en parches (patches) de tamaño fijo. Cada parche se aplana y se proyecta linealmente a un embedding. Esto convierte una imagen de (H, W, C) en una secuencia de parches de forma (num_patches, patch_embed_dim). Luego se añade posición y se procesa con un transformer.

Ejemplo: Imagen 224×224 RGB, parches de 16×16: número de parches = (224/16)² = 196. Cada parche se aplana a 16×16×3 = 768. Tras proyección lineal, se obtiene un tensor (196, embed_dim). Con lote de 32 imágenes: (32, 196, embed_dim).

6. Series Temporales con RNNs/LSTMs

Los datos de series temporales multivariadas se representan como tensores 3D: (batch, timesteps, features). Por ejemplo, datos de sensores con 10 mediciones cada 5 minutos durante 24 horas (288 pasos), lote de 100: (100, 288, 10).

7. Transfer Learning: Congelación de “rebanadas” de tensores

En transfer learning, se cargan pesos pre-entrenados (tensores) y se “congelan” ciertas capas, es decir, se evita que se actualicen durante el entrenamiento. Esto se hace configurando requires_grad=False en PyTorch. Las “rebanadas” (slicing) de tensores pueden seleccionarse para fine-tuning parcial.

Ejemplo: Cargar ResNet pre-entrenada y congelar todas las capas excepto la última (clasificador). Los pesos de las capas convolucionales son tensores 4D que permanecen fijos; solo se entrena el tensor de la última capa lineal.

8. Broadcasting en operaciones

Broadcasting es esencial para eficiencia. Por ejemplo, al normalizar un lote de imágenes, se resta la media por canal (un tensor de forma (3,)) a cada imagen (H,W,3). Broadcasting expande automáticamente la media a (H,W,3).

Ejemplo: Normalización por lotes (BatchNorm): durante entrenamiento, se mantiene una media móvil por canal (tensor (C,)). Al aplicarla a un tensor (N,C,H,W), broadcasting permite restar la media a todos los píxeles de cada canal sin bucles explícitos.

9. Reorganización de dimensiones (reshape, transpose, permute)

Estas operaciones son cruciales para conectar capas. Por ejemplo, antes de una capa lineal en una CNN, se suele aplanar el tensor: de (batch, C, H, W) a (batch, CHW) con reshape. En transformers, a veces se permutan dimensiones para compatibilidad entre diferentes frameworks.

Ejemplo: En una CNN para clasificación, tras las capas convolucionales, tenemos (batch, 512, 7, 7). Se aplica reshape(batch, 512*7*7) para conectar con una capa lineal de salida.

10. Autoencoders y modelos generativos

Los autoencoders comprimen datos a una representación latente (codificación) y luego reconstruyen. Los tensores fluyen a través del codificador (que reduce dimensiones) y el decodificador (que las expande). En modelos generativos como VAE o GANs, los tensores se manipujan continuamente con operaciones de reshaping, convoluciones transpuestas, etc.

Ejemplo: Un autoencoder convolucional para imágenes: entrada (batch, 3, 64, 64) → codificador produce (batch, 128, 8, 8) → aplanado a (batch, 12888) → capa densa a (batch, latent_dim) → decodificador: capa densa a (batch, 12888) → reshape a (batch, 128, 8, 8) → convoluciones transpuestas hasta (batch, 3, 64, 64).