viarias commited on
Commit
20820c3
·
verified ·
1 Parent(s): 25fb814

Upload 6 files

Browse files
docs/DEPLOYMENT.md ADDED
@@ -0,0 +1,313 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Clasificación de Uso de Suelo de Bogotá - Guía de Despliegue
2
+
3
+ Esta guía proporciona instrucciones completas para desplegar la aplicación de Clasificación de Uso de Suelo de Bogotá en varias plataformas, con enfoque en el despliegue en Hugging Face Spaces usando aceleración GPU.
4
+
5
+ ## Tabla de Contenidos
6
+ - [Resumen](#resumen)
7
+ - [Despliegue en Hugging Face Spaces](#despliegue-en-hugging-face-spaces)
8
+ - [Desarrollo Local](#desarrollo-local)
9
+ - [Despliegue con Docker](#despliegue-con-docker)
10
+ - [Variables de Entorno](#variables-de-entorno)
11
+ - [Solución de Problemas](#solución-de-problemas)
12
+
13
+ ## Resumen
14
+
15
+ La aplicación de Clasificación de Uso de Suelo de Bogotá es un servicio web basado en FastAPI que clasifica el uso del suelo en imágenes satelitales de Bogotá utilizando modelos de aprendizaje profundo. La aplicación está diseñada para ejecutarse eficientemente en entornos acelerados por GPU.
16
+
17
+ ## Despliegue en Hugging Face Spaces
18
+
19
+ ### Prerrequisitos
20
+ - Cuenta de Hugging Face con acceso a Spaces
21
+ - Acceso a hardware GPU (recomendado: L40S o similar)
22
+ - Conocimiento básico de Docker y contenedorización
23
+
24
+ ### Paso 1: Crear un Nuevo Space
25
+
26
+ 1. Ve a [Hugging Face Spaces](https://huggingface.co/spaces)
27
+ 2. Haz clic en "Create new Space"
28
+ 3. Configura tu space:
29
+ - **Nombre del Space**: `clasificador-uso-suelo-bogota` (o tu nombre preferido)
30
+ - **Licencia**: MIT
31
+ - **SDK**: Docker
32
+ - **Hardware**: **GPU L40S** (48GB VRAM, alto rendimiento para aprendizaje profundo)
33
+ - **Visibilidad**: Público o Privado (según necesidad)
34
+
35
+ ### Paso 2: Configurar Ajustes del Space
36
+
37
+ #### Configuración de Hardware
38
+ Selecciona la **GPU L40S** para rendimiento óptimo:
39
+ - **GPU**: NVIDIA L40S
40
+ - **VRAM**: 48GB
41
+ - **Núcleos CUDA**: 18,176
42
+ - **Ancho de Banda de Memoria**: 864 GB/s
43
+ - **Ideal para**: Modelos transformer grandes y tareas de visión por computadora
44
+
45
+ #### Archivo de Configuración del Space
46
+ Asegúrate de que tu `README.md` (en la raíz del space) contenga:
47
+
48
+ ```yaml
49
+ ---
50
+ title: Clasificador Uso de Suelo Bogotá
51
+ emoji: 🏙️
52
+ colorFrom: blue
53
+ colorTo: green
54
+ sdk: docker
55
+ pinned: false
56
+ license: mit
57
+ hardware: l40s
58
+ ---
59
+ ```
60
+
61
+ ### Paso 3: Subir Archivos de la Aplicación
62
+
63
+ Sube los siguientes archivos a tu Hugging Face Space:
64
+
65
+ ```
66
+ ├── app.py # Aplicación principal FastAPI
67
+ ├── classifier.py # Lógica de clasificación
68
+ ├── inference.py # Pipeline de inferencia
69
+ ├── model.py # Definiciones del modelo
70
+ ├── types_io.py # Definiciones de tipos
71
+ ├── requirements.txt # Dependencias de Python
72
+ ├── Dockerfile # Configuración del contenedor
73
+ └── README.md # Configuración del space
74
+ ```
75
+
76
+ ### Paso 4: Configuración del Dockerfile
77
+
78
+ La aplicación usa Docker para contenedorización. El Dockerfile debe estar optimizado para uso de GPU:
79
+
80
+ ```dockerfile
81
+ FROM python:3.10-slim
82
+
83
+ # Instalar dependencias del sistema
84
+ RUN apt-get update && apt-get install -y \
85
+ build-essential \
86
+ && rm -rf /var/lib/apt/lists/*
87
+
88
+ # Establecer directorio de trabajo
89
+ WORKDIR /app
90
+
91
+ # Copiar requisitos e instalar dependencias
92
+ COPY requirements.txt .
93
+ RUN pip install --no-cache-dir -r requirements.txt
94
+
95
+ # Copiar código de la aplicación
96
+ COPY . .
97
+
98
+ # Exponer puerto
99
+ EXPOSE 7860
100
+
101
+ # Ejecutar la aplicación
102
+ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
103
+ ```
104
+
105
+ ### Paso 5: Configuración del Entorno
106
+
107
+ #### Optimización GPU
108
+ La GPU L40S proporciona excelente rendimiento para esta aplicación:
109
+ - **CUDA**: Detectado automáticamente y utilizado por PyTorch
110
+ - **Memoria**: 48GB VRAM permite procesamiento en lotes grandes
111
+ - **Capacidad de Cómputo**: 8.9 (arquitectura Ampere)
112
+
113
+ #### Dependencias
114
+ Dependencias clave optimizadas para aceleración GPU:
115
+ ```
116
+ torch>=2.7.0 # PyTorch con soporte CUDA
117
+ torchvision>=0.22.0 # Utilidades de visión por computadora
118
+ transformers>=4.52.3 # Transformers de Hugging Face
119
+ accelerate>=1.7.0 # Utilidades de entrenamiento distribuido
120
+ ```
121
+
122
+ ### Paso 6: Desplegar y Monitorear
123
+
124
+ 1. **Subir al Space**: Sube todos los archivos a tu Hugging Face Space
125
+ 2. **Proceso de Construcción**: El space construirá automáticamente el contenedor Docker
126
+ 3. **Asignación de GPU**: La GPU L40S será asignada durante el inicio
127
+ 4. **Monitorear Logs**: Revisa los logs del space para cualquier problema de despliegue
128
+
129
+ #### Tiempo de Inicio Esperado
130
+ - **Construcción del Contenedor**: 5-10 minutos
131
+ - **Carga del Modelo**: 2-3 minutos en L40S
132
+ - **API Lista**: Total ~7-13 minutos
133
+
134
+ ### Paso 7: Uso de la API
135
+
136
+ Una vez desplegada, tu aplicación estará disponible en:
137
+ ```
138
+ https://huggingface.co/spaces/[USUARIO]/[NOMBRE_SPACE]
139
+ ```
140
+
141
+ #### Endpoints de la API
142
+ - `POST /classify`: Endpoint principal de clasificación
143
+ - `GET /health`: Endpoint de verificación de salud
144
+ - `GET /docs`: Documentación interactiva de la API
145
+
146
+ #### Ejemplo de Uso
147
+ ```python
148
+ import requests
149
+ import base64
150
+
151
+ # Codificar imagen a base64
152
+ with open("imagen_satelital.jpg", "rb") as f:
153
+ imagen_b64 = base64.b64encode(f.read()).decode()
154
+
155
+ # Hacer petición a la API
156
+ response = requests.post(
157
+ "https://huggingface.co/spaces/[USUARIO]/[NOMBRE_SPACE]/classify",
158
+ json={
159
+ "images": [
160
+ {
161
+ "data": imagen_b64,
162
+ "format": "base64"
163
+ }
164
+ ]
165
+ }
166
+ )
167
+
168
+ resultados = response.json()
169
+ print(f"Clasificación: {resultados}")
170
+ ```
171
+
172
+ ### Beneficios de Rendimiento GPU
173
+
174
+ Usar la GPU L40S proporciona ventajas significativas:
175
+
176
+ #### Métricas de Rendimiento
177
+ - **Velocidad de Inferencia**: ~40-50seg por imagen (vs 40-50min en CPU)
178
+ - **Procesamiento en Lotes**: Puede procesar múltiples imágenes simultáneamente
179
+ - **Memoria**: 48GB permite modelos grandes y tamaños de lote grandes
180
+ - **Rendimiento**: ~60-80 imágenes/hora dependiendo de la complejidad del modelo.
181
+
182
+ #### Optimización de Costos
183
+ - **Procesamiento Eficiente**: Inferencia más rápida reduce tiempo de cómputo
184
+ - **Operaciones en Lotes**: Procesa múltiples imágenes en paralelo
185
+ - **Auto-escalado**: Hugging Face gestiona la asignación de recursos
186
+
187
+ ## Desarrollo Local
188
+
189
+ ### Configuración
190
+ ```bash
191
+ # Clonar repositorio
192
+ git clone <url-repositorio>
193
+ cd bogota_land_use_final
194
+
195
+ # Instalar dependencias
196
+ pip install -r requirements.txt
197
+
198
+ # Ejecutar aplicación
199
+ uvicorn app:app --reload --host 0.0.0.0 --port 8000
200
+ ```
201
+
202
+ ### Requisitos GPU (Local)
203
+ - GPU NVIDIA con soporte CUDA
204
+ - CUDA 11.8 o posterior
205
+ - cuDNN 8.7 o posterior
206
+ - Al menos 8GB VRAM (16GB+ recomendado)
207
+
208
+ ## Despliegue con Docker
209
+
210
+ ### Construir y Ejecutar
211
+ ```bash
212
+ # Construir imagen
213
+ docker build -t bogota-land-use .
214
+
215
+ # Ejecutar contenedor (soporte GPU)
216
+ docker run --gpus all -p 7860:7860 bogota-land-use
217
+
218
+ # Ejecutar contenedor (solo CPU)
219
+ docker run -p 7860:7860 bogota-land-use
220
+ ```
221
+
222
+ ### Docker Compose
223
+ ```yaml
224
+ version: '3.8'
225
+ services:
226
+ app:
227
+ build: .
228
+ ports:
229
+ - "7860:7860"
230
+ deploy:
231
+ resources:
232
+ reservations:
233
+ devices:
234
+ - driver: nvidia
235
+ count: 1
236
+ capabilities: [gpu]
237
+ ```
238
+
239
+ ## Variables de Entorno
240
+
241
+ Configura estas variables de entorno para rendimiento óptimo:
242
+
243
+ ```bash
244
+ # Configuración GPU
245
+ CUDA_VISIBLE_DEVICES=0
246
+ TORCH_CUDA_ARCH_LIST="8.9" # Arquitectura L40S
247
+
248
+ # Configuraciones de la Aplicación
249
+ MODEL_CACHE_DIR="/tmp/model_cache"
250
+ MAX_BATCH_SIZE=16
251
+ INFERENCE_TIMEOUT=30
252
+
253
+ # Ajuste de Rendimiento
254
+ OMP_NUM_THREADS=4
255
+ TORCH_NUM_THREADS=4
256
+ ```
257
+
258
+ ## Solución de Problemas
259
+
260
+ ### Problemas Comunes
261
+
262
+ #### GPU No Detectada
263
+ ```bash
264
+ # Verificar disponibilidad de GPU
265
+ python -c "import torch; print(torch.cuda.is_available())"
266
+ python -c "import torch; print(torch.cuda.device_count())"
267
+ ```
268
+
269
+ #### Problemas de Memoria
270
+ - Reducir tamaño de lote en inferencia
271
+ - Habilitar gradient checkpointing
272
+ - Usar entrenamiento de precisión mixta
273
+
274
+ #### Inferencia Lenta
275
+ - Verificar utilización de GPU
276
+ - Revisar cuantización del modelo
277
+ - Optimizar preprocesamiento de imágenes
278
+
279
+ ### Monitoreo de Rendimiento
280
+
281
+ #### Monitoreo de GPU
282
+ ```bash
283
+ # Monitorear uso de GPU
284
+ nvidia-smi -l 1
285
+
286
+ # Verificar versión de CUDA
287
+ nvcc --version
288
+ ```
289
+
290
+ #### Logs de la Aplicación
291
+ Revisar logs del Hugging Face Space para:
292
+ - Tiempo de carga del modelo
293
+ - Rendimiento de inferencia
294
+ - Uso de memoria
295
+ - Mensajes de error
296
+
297
+ ### Soporte
298
+
299
+ Para problemas con:
300
+ - **Hugging Face Spaces**: Consulta [documentación de Hugging Face](https://huggingface.co/docs/hub/spaces)
301
+ - **Configuración GPU**: Verifica instalación y compatibilidad de CUDA
302
+ - **Lógica de la Aplicación**: Revisa logs de la aplicación y mensajes de error
303
+
304
+ ## Recursos Adicionales
305
+
306
+ - [Documentación de Hugging Face Spaces](https://huggingface.co/docs/hub/spaces)
307
+ - [Especificaciones GPU L40S](https://www.nvidia.com/en-us/data-center/l40s/)
308
+ - [Documentación GPU PyTorch](https://pytorch.org/docs/stable/notes/cuda.html)
309
+ - [Documentación FastAPI](https://fastapi.tiangolo.com/)
310
+
311
+ ---
312
+
313
+ *Última actualización: 13 de julio de 2025*
docs/WORKFLOW.md ADDED
@@ -0,0 +1,455 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Clasificación de Uso de Suelo de Bogotá - Flujo de Código
2
+
3
+ Este documento explica la arquitectura técnica y el flujo de código de la aplicación de Clasificación de Uso de Suelo de Bogotá, proporcionando una visión detallada de cómo interactúan los diferentes componentes del sistema.
4
+
5
+ ## Tabla de Contenidos
6
+ - [Resumen de Arquitectura](#resumen-de-arquitectura)
7
+ - [Diagrama de Flujo del Sistema](#diagrama-de-flujo-del-sistema)
8
+ - [Componentes Principales](#componentes-principales)
9
+ - [Flujo de Datos Detallado](#flujo-de-datos-detallado)
10
+ - [Modelo de Datos](#modelo-de-datos)
11
+ - [Manejo de Errores](#manejo-de-errores)
12
+ - [Optimizaciones de Rendimiento](#optimizaciones-de-rendimiento)
13
+ - [Patrones de Diseño](#patrones-de-diseño)
14
+
15
+ ## Resumen de Arquitectura
16
+
17
+ La aplicación sigue una arquitectura de microservicios basada en FastAPI con separación clara de responsabilidades:
18
+
19
+ - **Capa de API**: Manejo de peticiones HTTP y validación de entrada
20
+ - **Capa de Procesamiento**: Preprocesamiento de imágenes y orquestación
21
+ - **Capa de Inferencia**: Clasificación usando modelos de aprendizaje profundo
22
+ - **Capa de Modelo**: Gestión del modelo de transformers y procesador
23
+
24
+ ## Diagrama de Flujo del Sistema
25
+
26
+ ```mermaid
27
+ graph TD
28
+ A[Cliente HTTP] -->|POST /classify| B[FastAPI App]
29
+ B --> C{Validación de Request}
30
+ C -->|Error| D[HTTP 400 Error]
31
+ C -->|Válido| E[decode_base64_image]
32
+
33
+ E --> F[save_images_to_disk]
34
+ F --> G[Inference.classify_building]
35
+ G --> H[Classifier.get_response]
36
+
37
+ H --> I[get_input_tensor]
38
+ I --> J[resize_image]
39
+ J --> K[prepare_messages]
40
+
41
+ K --> L[Model.load_model]
42
+ L --> M[Model.load_processor]
43
+
44
+ M --> N[generate_model_response]
45
+ N --> O[processor.apply_chat_template]
46
+ O --> P[model.generate]
47
+ P --> Q[processor.batch_decode]
48
+
49
+ Q --> R[Respuesta JSON]
50
+ R --> S[Cliente HTTP]
51
+
52
+ style B fill:#e1f5fe
53
+ style H fill:#f3e5f5
54
+ style L fill:#e8f5e8
55
+ style N fill:#fff3e0
56
+ ```
57
+
58
+ ### Flujo Alternativo de Errores
59
+
60
+ ```mermaid
61
+ graph TD
62
+ A[Error en cualquier etapa] --> B{Tipo de Error}
63
+ B -->|Validación| C[HTTP 400 - Bad Request]
64
+ B -->|Procesamiento| D[HTTP 500 - Internal Error]
65
+ B -->|Modelo| E[HTTP 500 - Model Error]
66
+
67
+ C --> F[Log de Error]
68
+ D --> F
69
+ E --> F
70
+ F --> G[Respuesta de Error al Cliente]
71
+ ```
72
+
73
+ ## Componentes Principales
74
+
75
+ ### 1. app.py - Capa de API (Entrada del Sistema)
76
+
77
+ **Responsabilidades:**
78
+ - Manejo de peticiones HTTP
79
+ - Validación de entrada
80
+ - Decodificación de imágenes base64
81
+ - Guardado temporal de imágenes
82
+ - Orquestación del flujo principal
83
+
84
+ **Funciones clave:**
85
+ ```python
86
+ def decode_base64_image(base64_str: str) -> Optional[Image.Image]
87
+ def save_images_to_disk(images: List[Image.Image], output_dir: str) -> List[str]
88
+ async def classify(request: ClassificationRequest) -> dict
89
+ ```
90
+
91
+ **Flujo de datos:**
92
+ 1. Recibe request con imágenes en base64
93
+ 2. Valida formato usando Pydantic
94
+ 3. Decodifica cada imagen base64 a PIL.Image
95
+ 4. Guarda imágenes temporalmente en disco
96
+ 5. Delega clasificación a la capa de inferencia
97
+ 6. Retorna respuesta JSON estructurada
98
+
99
+ ### 2. inference.py - Capa de Procesamiento
100
+
101
+ **Responsabilidades:**
102
+ - Orquestación entre API y clasificador
103
+ - Preprocesamiento de imágenes
104
+ - Conversión de formatos de imagen
105
+ - Logging de operaciones
106
+
107
+ **Funciones clave:**
108
+ ```python
109
+ def _prepare_images(self, images: List[Image.Image]) -> List[Image.Image]
110
+ def classify_building(self, images: List[Image.Image], saved_image_paths: List[str]) -> dict
111
+ ```
112
+
113
+ **Flujo de datos:**
114
+ 1. Recibe lista de objetos PIL.Image
115
+ 2. Convierte todas las imágenes a formato RGB
116
+ 3. Valida integridad de cada imagen
117
+ 4. Delega clasificación al componente Classifier
118
+ 5. Retorna respuesta estructurada
119
+
120
+ ### 3. classifier.py - Lógica de Clasificación
121
+
122
+ **Responsabilidades:**
123
+ - Gestión del pipeline de clasificación
124
+ - Redimensionamiento de imágenes
125
+ - Preparación de mensajes para el modelo
126
+ - Generación de respuestas del modelo
127
+ - Aplicación de plantillas de chat
128
+
129
+ **Funciones clave:**
130
+ ```python
131
+ def get_response(self, images: List[Image.Image], saved_image_paths: List[str]) -> dict
132
+ def get_input_tensor(self, images: List[Image.Image]) -> List[Image.Image]
133
+ def generate_model_response(self, images: List[Image.Image], messages: List[dict]) -> str
134
+ def resize_image(image: Image.Image, max_size: int = 224) -> Image.Image
135
+ def prepare_messages(saved_image_paths: List[str]) -> List[dict]
136
+ ```
137
+
138
+ **Prompt del sistema:**
139
+ - Define 20 categorías de uso de suelo específicas para Bogotá
140
+ - Requiere mínimo 3 categorías por clasificación
141
+ - Incluye scores de confianza (0-1)
142
+ - Formato de salida JSON estructurado
143
+
144
+ ### 4. model.py - Gestión del Modelo
145
+
146
+ **Responsabilidades:**
147
+ - Carga única del modelo y procesador (Singleton)
148
+ - Configuración de parámetros del modelo
149
+ - Gestión de memoria y recursos GPU
150
+ - Abstracción del modelo Kimi-VL-A3B-Thinking
151
+
152
+ **Funciones clave:**
153
+ ```python
154
+ @classmethod
155
+ def load(cls) -> Tuple[AutoModelForCausalLM, AutoProcessor]
156
+ @classmethod
157
+ def load_model(cls) -> AutoModelForCausalLM
158
+ @classmethod
159
+ def load_processor(cls) -> AutoProcessor
160
+ ```
161
+
162
+ **Configuración del modelo:**
163
+ ```python
164
+ MODEL_PATH = "moonshotai/Kimi-VL-A3B-Thinking-2506"
165
+ model_kwargs = {
166
+ "device_map": "auto",
167
+ "torch_dtype": "auto",
168
+ "trust_remote_code": True
169
+ }
170
+ ```
171
+
172
+ ### 5. types_io.py - Definiciones de Tipos
173
+
174
+ **Responsabilidades:**
175
+ - Validación de datos de entrada y salida
176
+ - Definición de esquemas JSON
177
+ - Enumeración de categorías de clasificación
178
+ - Restricciones de formato y rangos
179
+
180
+ **Modelos principales:**
181
+ ```python
182
+ class ClassificationRequest(BaseModel) # Entrada de la API
183
+ class ImageData(BaseModel) # Salida de la clasificación
184
+ class ImageTag(BaseModel) # Tags individuales
185
+ class TagType(Enum) # Categorías de uso de suelo
186
+ ```
187
+
188
+ ## Flujo de Datos Detallado
189
+
190
+ ### Fase 1: Recepción y Validación (app.py)
191
+ ```
192
+ Cliente → POST /classify → FastAPI → Pydantic Validation
193
+
194
+ ClassificationRequest {
195
+ images: List[str] # base64 strings
196
+ }
197
+ ```
198
+
199
+ ### Fase 2: Decodificación y Almacenamiento (app.py)
200
+ ```
201
+ List[str] → decode_base64_image() → List[PIL.Image]
202
+
203
+ save_images_to_disk() → List[str] # file paths
204
+ ```
205
+
206
+ ### Fase 3: Preprocesamiento (inference.py)
207
+ ```
208
+ List[PIL.Image] → _prepare_images() → List[PIL.Image] # RGB converted
209
+
210
+ Validación de integridad
211
+ ```
212
+
213
+ ### Fase 4: Clasificación (classifier.py)
214
+ ```
215
+ List[PIL.Image] → get_input_tensor() → resize_image() → List[PIL.Image] # 224x224
216
+
217
+ List[str] # paths → prepare_messages() → List[dict] # chat format
218
+
219
+ generate_model_response() → modelo transformer → str # JSON response
220
+ ```
221
+
222
+ ### Fase 5: Carga del Modelo (model.py)
223
+ ```
224
+ Kimi-VL-A3B-Thinking-2506 → AutoModelForCausalLM.from_pretrained()
225
+
226
+ device_map="auto" # GPU allocation
227
+
228
+ torch_dtype="auto" # Mixed precision
229
+ ```
230
+
231
+ ### Fase 6: Generación de Respuesta
232
+ ```
233
+ Imágenes + Prompt → processor.apply_chat_template() → model.generate()
234
+
235
+ processor.batch_decode() → JSON string → ImageData
236
+ ```
237
+
238
+ ## Modelo de Datos
239
+
240
+ ### Estructura de Entrada
241
+ ```json
242
+ {
243
+ "images": [
244
+ "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD...",
245
+ "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB..."
246
+ ]
247
+ }
248
+ ```
249
+
250
+ ### Estructura de Salida
251
+ ```json
252
+ {
253
+ "output": {
254
+ "classification": [
255
+ {
256
+ "category": "Residenciales",
257
+ "confidence": 0.92
258
+ },
259
+ {
260
+ "category": "Comerciales1",
261
+ "confidence": 0.65
262
+ },
263
+ {
264
+ "category": "Moles",
265
+ "confidence": 0.33
266
+ }
267
+ ],
268
+ "think": "Esta edificación presenta características principalmente residenciales..."
269
+ }
270
+ }
271
+ ```
272
+
273
+ ### Categorías de Clasificación
274
+
275
+ | Categoría | Descripción | Ejemplo |
276
+ |-----------|-------------|---------|
277
+ | **Residenciales** | Edificios para vivienda | Casas, edificios PH, condominios |
278
+ | **Comerciales1-5** | Diferentes tipos comerciales | Tiendas, oficinas, hoteles, talleres |
279
+ | **Centros_Comerciales** | Complejos comerciales | Centros comerciales, plazas |
280
+ | **Bodegas** | Almacenamiento | Bodegas industriales y comerciales |
281
+ | **Parqueaderos** | Estacionamientos | Edificios de parqueo |
282
+ | **Dotacionales1-5** | Servicios públicos | Escuelas, hospitales, iglesias |
283
+ | **Especiales** | Usos especiales | Áreas militares, cementerios |
284
+ | **Moles** | Grandes edificios | >4 pisos o >10,000 m² |
285
+ | **Rurales** | Construcciones rurales | Galpones, silos, establos |
286
+ | **Mixto1-3** | Usos combinados | Residencial+Comercial |
287
+
288
+ ## Manejo de Errores
289
+
290
+ ### Tipos de Errores y Estrategias
291
+
292
+ #### 1. Errores de Validación (HTTP 400)
293
+ ```python
294
+ # En app.py
295
+ try:
296
+ images = []
297
+ for img_str in request.images:
298
+ img = decode_base64_image(img_str)
299
+ if img is None:
300
+ raise ValueError("Invalid base64 image")
301
+ images.append(img)
302
+ except ValueError as ve:
303
+ raise HTTPException(status_code=400, detail=str(ve))
304
+ ```
305
+
306
+ #### 2. Errores de Procesamiento (HTTP 500)
307
+ ```python
308
+ # En classifier.py
309
+ try:
310
+ img = self.resize_image(img)
311
+ processed_images.append(img)
312
+ except Exception as e:
313
+ logger.error(f"Error processing image at index {idx}: {str(e)}")
314
+ raise
315
+ ```
316
+
317
+ #### 3. Errores del Modelo (HTTP 500)
318
+ ```python
319
+ # En model.py
320
+ try:
321
+ cls._model = cls.model_class.from_pretrained(
322
+ cls.MODEL_PATH, **cls.model_kwargs
323
+ )
324
+ except Exception as e:
325
+ logger.error(f"Failed to load model: {str(e)}")
326
+ raise
327
+ ```
328
+
329
+ ### Sistema de Logging
330
+ ```python
331
+ logging.basicConfig(
332
+ level=logging.INFO,
333
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
334
+ )
335
+ ```
336
+
337
+ **Niveles de logging utilizados:**
338
+ - `INFO`: Operaciones principales y flujo normal
339
+ - `DEBUG`: Información detallada para debugging
340
+ - `ERROR`: Errores que requieren atención
341
+ - `WARNING`: Situaciones anómalas pero no críticas
342
+
343
+ ## Optimizaciones de Rendimiento
344
+
345
+ ### 1. Gestión de Memoria
346
+ - **Singleton para modelo**: Carga única en `model.py`
347
+ - **Auto device mapping**: `device_map="auto"` para distribución GPU óptima
348
+ - **Torch dtype automático**: `torch_dtype="auto"` para precisión mixta
349
+
350
+ ### 2. Procesamiento de Imágenes
351
+ - **Redimensionamiento inteligente**: Mantiene aspect ratio, reduce a 224x224
352
+ - **Conversión RGB**: Garantiza compatibilidad del formato
353
+ - **Guardado temporal**: Optimiza memoria usando rutas de archivo
354
+
355
+ ### 3. Batch Processing
356
+ ```python
357
+ # En classifier.py
358
+ inputs = self.processor(
359
+ images=images,
360
+ text=text,
361
+ return_tensors="pt",
362
+ padding=True, # Padding para batch processing
363
+ truncation=True # Truncación para consistencia
364
+ ).to(self.model.device)
365
+ ```
366
+
367
+ ### 4. Configuración GPU
368
+ ```python
369
+ # Parámetros optimizados para L40S
370
+ model_kwargs = {
371
+ "device_map": "auto", # Distribución automática en GPU
372
+ "torch_dtype": "auto", # Precision mixta FP16/FP32
373
+ "trust_remote_code": True
374
+ }
375
+ ```
376
+
377
+ ## Patrones de Diseño
378
+
379
+ ### 1. Singleton Pattern (model.py)
380
+ ```python
381
+ class Model:
382
+ _model = None
383
+ _processor = None
384
+
385
+ @classmethod
386
+ def load(cls):
387
+ if cls._model is None:
388
+ # Cargar solo una vez
389
+ cls._model = cls.model_class.from_pretrained(...)
390
+ ```
391
+
392
+ ### 2. Factory Pattern (types_io.py)
393
+ ```python
394
+ class ImageData(BaseModel):
395
+ """Factory para crear respuestas validadas"""
396
+ classification: List[ImageTag]
397
+ think: str
398
+ ```
399
+
400
+ ### 3. Strategy Pattern (classifier.py)
401
+ ```python
402
+ def resize_image(image: Image.Image, max_size: int = 224):
403
+ """Estrategia configurable de redimensionamiento"""
404
+ scale = min(max_size / width, max_size / height)
405
+ # Aplicar estrategia de redimensionamiento
406
+ ```
407
+
408
+ ### 4. Dependency Injection (app.py → inference.py → classifier.py)
409
+ ```python
410
+ # app.py
411
+ inference = Inference()
412
+
413
+ # inference.py
414
+ def __init__(self):
415
+ self.classifier = classifier # Inyección de dependencia
416
+
417
+ # classifier.py
418
+ def __init__(self):
419
+ self.model = Model.load_model() # Inyección lazy
420
+ self.processor = Model.load_processor()
421
+ ```
422
+
423
+ ### 5. Pipeline Pattern
424
+ ```
425
+ Cliente → API → Inference → Classifier → Model → Respuesta
426
+ ```
427
+
428
+ Cada etapa transforma los datos y los pasa a la siguiente, permitiendo:
429
+ - **Separación de responsabilidades**
430
+ - **Facilidad de testing**
431
+ - **Escalabilidad independiente**
432
+ - **Mantenimiento modular**
433
+
434
+ ## Consideraciones de Escalabilidad
435
+
436
+ ### 1. Arquitectura Horizontal
437
+ - Cada componente puede escalarse independientemente
438
+ - API stateless permite múltiples instancias
439
+ - Modelo singleton optimiza uso de memoria
440
+
441
+ ### 2. Gestión de Recursos
442
+ - Auto device mapping para múltiples GPUs
443
+ - Batch processing para eficiencia
444
+ - Cleanup automático de archivos temporales
445
+
446
+ ### 3. Monitoring y Observabilidad
447
+ - Logging estructurado en cada capa
448
+ - Métricas de tiempo de respuesta
449
+ - Seguimiento de uso de memoria GPU
450
+
451
+ ---
452
+
453
+ *Documento técnico actualizado: 13 de julio de 2025*
454
+
455
+ *Para más información sobre el despliegue, consulta [DEPLOYMENT.md](DEPLOYMENT.md)*
test/images/AAA0119DNBSPD01.jpg ADDED
test/images/AAA0119DNBSPD02.jpg ADDED
test/images/AAA0119DNBSPD03.jpg ADDED
test/test_api_local.py ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import requests
2
+ import base64
3
+ from pathlib import Path
4
+ from PIL import Image
5
+ import io
6
+
7
+ def resize_image(image: Image.Image, max_size: int = 224) -> Image.Image:
8
+ """
9
+ Resize an image while maintaining aspect ratio.
10
+
11
+ Args:
12
+ image: PIL Image object to resize
13
+ max_size: Maximum dimension (width or height) of the output image
14
+
15
+ Returns:
16
+ PIL Image: Resized image with maintained aspect ratio
17
+ """
18
+ # Get current dimensions
19
+ width, height = image.size
20
+
21
+ # Calculate scaling factor to fit within max_size
22
+ scale = min(max_size / width, max_size / height)
23
+
24
+ # Only resize if image is larger than max_size
25
+ if scale < 1:
26
+ new_width = int(width * scale)
27
+ new_height = int(height * scale)
28
+ image = image.resize(
29
+ (new_width, new_height),
30
+ Image.LANCZOS
31
+ )
32
+
33
+ return image
34
+
35
+
36
+ # Define your desired size
37
+ TARGET_SIZE = 16
38
+
39
+ # Define the image paths
40
+ image_paths = [
41
+ "images/AAA0119DNBSPD01.jpg",
42
+ "images/AAA0119DNBSPD02.jpg"
43
+ ]
44
+
45
+ # Read and encode images
46
+ images = []
47
+ for path in image_paths:
48
+ # Open the image
49
+ img = Image.open(path)
50
+
51
+ # Resize the image (using LANCZOS for high-quality downsampling)
52
+ img = resize_image(img, max_size=TARGET_SIZE)
53
+
54
+ # Convert to bytes
55
+ buffered = io.BytesIO()
56
+ img.save(buffered, format="JPEG") # You can change format to PNG if needed
57
+
58
+ # Encode to base64
59
+ base64_image = base64.b64encode(buffered.getvalue()).decode('utf-8')
60
+ images.append(base64_image)
61
+
62
+ # Make the request
63
+ print(images[0])
64
+ url = "http://localhost:8000/classify"
65
+ payload = {"images": [images[0]]}
66
+ headers = {"Content-Type": "application/json"}
67
+
68
+ response = requests.post(url, json=payload, headers=headers)
69
+ print(f"Status Code: {response.status_code}")
70
+ print("Response Text:")
71
+ print(response.text)