Skip to main content

Taller Docker

👥 Integrantes:

  • 👤 Daniel Sarmiento
  • 👤 Redactor: Jorge Garcia

📑 Material de apoyo:

Uso de Docker para Modelos de Inteligencia Artificial

Docker ha transformado la manera en que desarrollamos, compartimos y desplegamos aplicaciones. Gracias a su arquitectura basada en contenedores, es posible empaquetar entornos completos de forma reproducible, segura y portable.
En el campo de la inteligencia artificial (IA), esto resulta especialmente útil: podemos aislar modelos, dependencias y configuraciones, evitando conflictos y garantizando resultados consistentes entre equipos y entornos.

En este artículo exploraremos cómo usar Docker para proyectos de IA, desde su instalación hasta el despliegue de modelos en contenedores ligeros con soporte para entrenamiento, prueba y monitoreo con TensorBoard.


1. Introducción a Docker

Docker es una plataforma de contenedorización que permite ejecutar aplicaciones dentro de entornos aislados denominados containers.
Cada contenedor incluye el código, las dependencias y configuraciones necesarias, asegurando que el comportamiento de la aplicación sea idéntico sin importar el sistema donde se ejecute.

Aunque a menudo se compara con una máquina virtual (VM), Docker no virtualiza hardware. En cambio, utiliza el kernel del sistema operativo anfitrión junto con tecnologías como namespaces y cgroups para aislar procesos, lo que lo hace mucho más liviano y rápido.

Comparativa de Container vs Virtual Machine
Comparativa de container vs virtual machine

En la ilustración se observa que las VMs requieren un sistema operativo completo dentro del host, mientras que los contenedores comparten el mismo kernel. Esto permite que un contenedor arranque en segundos y consuma una fracción de los recursos.


2. Instalación de Docker

La instalación de Docker varía según la distribución o sistema operativo.
A continuación, se muestran los métodos más comunes:

Linux

Arch Linux / Manjaro

sudo pacman -S docker
sudo systemctl enable --now docker

Ubuntu / Debian

sudo apt update
sudo apt install docker.io -y
sudo systemctl enable --now docker

Fedora

sudo dnf install docker -y
sudo systemctl enable --now docker

Una vez instalado, puedes verificar su funcionamiento con:

docker run hello-world

Windows y macOS

Docker ofrece una aplicación oficial llamada Docker Desktop, que integra todas las herramientas necesarias. Puede descargarse desde el sitio oficial: 👉 https://www.docker.com/products/docker-desktop


3. Comandos básicos de Docker

ComandoDescripción
docker ps -aMuestra todos los contenedores (incluidos los detenidos)
docker pull <imagen>Descarga una imagen desde un registro (por ejemplo, Docker Hub)
docker run <imagen>Ejecuta una imagen en un nuevo contenedor
docker imagesLista las imágenes disponibles en el sistema
docker rm <id>Elimina un contenedor
docker rmi <id>Elimina una imagen
docker exec -it <nombre> bashAbre una sesión interactiva dentro de un contenedor

4. Estructura básica de un Dockerfile

Para construir una imagen personalizada, se utiliza un archivo llamado Dockerfile. Este define los pasos necesarios para preparar el entorno de ejecución.

Ejemplo simple:

FROM docker.io/postgres:latest  # Imagen base
RUN apt update && apt upgrade -y
ENV POSTGRES_USER=user \
    POSTGRES_PASSWORD=password \
    POSTGRES_DB=exampledb

Cada instrucción genera una capa (layer), lo que permite que las reconstrucciones sean más rápidas y eficientes.


5. Contenerizando un Modelo de IA con PyTorch

Imaginemos que queremos crear un contenedor para entrenar un modelo convolucional sencillo en el dataset CIFAR-10. El modelo en PyTorch podría ser el siguiente:

import torch.nn as nn
import torch.nn.functional as F
import torch

class Net(nn.Module):
    def __init__(self, dropout_rate=0.3):
        super().__init__()
        self.conv1 = nn.Conv2d(3, 6, 5)
        self.pool = nn.MaxPool2d(2, 2)
        self.conv2 = nn.Conv2d(6, 16, 5)
        self.fc1 = nn.Linear(16 * 5 * 5, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)
        self.dropout = nn.Dropout(dropout_rate)
        self._initialize_weights()
    
    def _initialize_weights(self):
        for m in self.modules():
            if isinstance(m, (nn.Conv2d, nn.Linear)):
                nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
                if m.bias is not None:
                    nn.init.constant_(m.bias, 0)
    
    def forward(self, x):
        x = self.pool(F.relu(self.conv1(x)))
        x = self.pool(F.relu(self.conv2(x)))
        x = torch.flatten(x, 1)
        x = F.relu(self.fc1(x))
        x = self.dropout(F.relu(self.fc2(x)))
        x = self.fc3(x)
        return x
flowchart TB
    A["Input 3x32x32"] --> B["Conv2d 3→6, kernel=5x5"]
    B --> C["ReLU"]
    C --> D["MaxPool 2x2 → 6x14x14"]
    D --> E["Conv2d 6→16, kernel=5x5"]
    E --> F["ReLU"]
    F --> G["MaxPool 2x2 → 16x5x5"]
    G --> H["Flatten → 400"]
    H --> I["Linear 400→120"]
    I --> J["ReLU"]
    J --> K["Dropout p=0.3"]
    K --> L["Linear 120→84"]
    L --> M["ReLU"]
    M --> N["Dropout p=0.3"]
    N --> O["Linear 84→10"]
    O --> P["Output: 10 clases"]
    style A fill:#E3F2FD,stroke:#2196F3,stroke-width:2px
    style B fill:#FFF3E0,stroke:#FB8C00,stroke-width:2px
    style C fill:#E3F2FD,stroke:#1E88E5,stroke-width:2px
    style D fill:#FFF8E1,stroke:#FDD835,stroke-width:2px
    style E fill:#FFF3E0,stroke:#FB8C00,stroke-width:2px
    style F fill:#E3F2FD,stroke:#1E88E5,stroke-width:2px
    style G fill:#FFF8E1,stroke:#FDD835,stroke-width:2px
    style H fill:#E8F5E9,stroke:#43A047,stroke-width:2px
    style I fill:#E8F5E9,stroke:#43A047,stroke-width:2px
    style J fill:#E3F2FD,stroke:#1E88E5,stroke-width:2px
    style K fill:#F3E5F5,stroke:#8E24AA,stroke-width:2px
    style L fill:#E8F5E9,stroke:#43A047,stroke-width:2px
    style M fill:#E3F2FD,stroke:#1E88E5,stroke-width:2px
    style N fill:#F3E5F5,stroke:#8E24AA,stroke-width:2px
    style O fill:#E8F5E9,stroke:#43A047,stroke-width:2px
    style P fill:#FFEBEE,stroke:#E53935,stroke-width:2px
flowchart TB
    A["Input 3x32x32"] --> B["Conv2d 3→6, kernel=5x5"]
    B --> C["ReLU"]
    C --> D["MaxPool 2x2 → 6x14x14"]
    D --> E["Conv2d 6→16, kernel=5x5"]
    E --> F["ReLU"]
    F --> G["MaxPool 2x2 → 16x5x5"]
    G --> H["Flatten → 400"]
    H --> I["Linear 400→120"]
    I --> J["ReLU"]
    J --> K["Dropout p=0.3"]
    K --> L["Linear 120→84"]
    L --> M["ReLU"]
    M --> N["Dropout p=0.3"]
    N --> O["Linear 84→10"]
    O --> P["Output: 10 clases"]
    style A fill:#E3F2FD,stroke:#2196F3,stroke-width:2px
    style B fill:#FFF3E0,stroke:#FB8C00,stroke-width:2px
    style C fill:#E3F2FD,stroke:#1E88E5,stroke-width:2px
    style D fill:#FFF8E1,stroke:#FDD835,stroke-width:2px
    style E fill:#FFF3E0,stroke:#FB8C00,stroke-width:2px
    style F fill:#E3F2FD,stroke:#1E88E5,stroke-width:2px
    style G fill:#FFF8E1,stroke:#FDD835,stroke-width:2px
    style H fill:#E8F5E9,stroke:#43A047,stroke-width:2px
    style I fill:#E8F5E9,stroke:#43A047,stroke-width:2px
    style J fill:#E3F2FD,stroke:#1E88E5,stroke-width:2px
    style K fill:#F3E5F5,stroke:#8E24AA,stroke-width:2px
    style L fill:#E8F5E9,stroke:#43A047,stroke-width:2px
    style M fill:#E3F2FD,stroke:#1E88E5,stroke-width:2px
    style N fill:#F3E5F5,stroke:#8E24AA,stroke-width:2px
    style O fill:#E8F5E9,stroke:#43A047,stroke-width:2px
    style P fill:#FFEBEE,stroke:#E53935,stroke-width:2px

Para contenerizarlo, creamos un Dockerfile como este:

# Imagen base ligera con Python
FROM python:3.12-slim

# Directorio de trabajo
WORKDIR /app

# Copiar el código fuente
COPY . /app

# Instalar dependencias (CPU-only)
RUN pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu
RUN pip install matplotlib scikit-learn albumentations tqdm tensorboard

# Instalar dependencias adicionales (si existen)
RUN if [ -f requirements.txt ]; then pip install -r requirements.txt; fi

# Variables de entorno
ENV PYTHONUNBUFFERED=1
ENV PYTHONDONTWRITEBYTECODE=1

# Comando por defecto
CMD ["python", "train.py"]

💡 Si cuentas con GPU y drivers CUDA, puedes usar una imagen base con soporte GPU, como:

FROM pytorch/pytorch:2.2.0-cuda12.1-cudnn8-runtime

6. Orquestación con Docker Compose

En proyectos de IA es habitual tener múltiples tareas: entrenamiento, evaluación y monitoreo. Con Docker Compose podemos definir todos los servicios en un solo archivo docker-compose.yml:

version: '3.9'
services:
  train:
    build:
      context: .
      dockerfile: Dockerfile
    image: pytorch-uv-app:latest
    container_name: pytorch-train
    command: python train.py
    volumes:
      - ./data:/app/data
      - ./model:/app/model
      - ./runs:/app/runs
      - ./checkpoints:/app/checkpoints
    restart: "no"

  test:
    image: pytorch-uv-app:latest
    container_name: pytorch-test
    command: python test.py
    volumes:
      - ./data:/app/data
      - ./model:/app/model
      - ./runs:/app/runs
      - ./checkpoints:/app/checkpoints
    restart: "no"

  tensorboard:
    image: python:3.12-slim
    container_name: pytorch-tensorboard
    command: >
      sh -c "pip install --quiet tensorboard>=2.20.0 &&
             tensorboard --logdir=/app/runs --host=0.0.0.0 --port=6006"
    ports:
      - "6006:6006"
    volumes:
      - ./runs:/app/runs
    restart: unless-stopped
    profiles:
      - monitoring

Ejecución

Ejecutar el entrenamiento:

docker compose up train

Ejecutar las pruebas:

docker compose up test

Iniciar TensorBoard para monitoreo:

docker compose --profile monitoring up tensorboard

Luego, accede a http://localhost:6006 para visualizar las métricas.


7. Conclusiones

El uso de Docker en proyectos de IA ofrece ventajas notables:

  • Reproducibilidad: garantiza que los experimentos sean consistentes entre máquinas.
  • Portabilidad: los modelos pueden ejecutarse en cualquier entorno con Docker.
  • Aislamiento: elimina conflictos de dependencias entre proyectos.
  • Escalabilidad: simplifica el despliegue en servidores, clusters o la nube.
  • Integración continua: facilita pipelines de entrenamiento y validación automatizados.

En definitiva, Docker se ha convertido en una herramienta esencial en el ciclo de vida de los modelos de inteligencia artificial, desde su desarrollo hasta su puesta en producción.


📘 Recursos adicionales: