Spaces:
Sleeping
Sleeping
| """ | |
| Detector de Imágenes IA vs. Reales | |
| Pontificia Universidad Javeriana — Técnicas de Aprendizaje de Máquina | |
| Proyecto de Aplicación 2 — HuggingFace Spaces | |
| """ | |
| import os | |
| import numpy as np | |
| from PIL import Image | |
| import tensorflow as tf | |
| import gradio as gr | |
| # ── Configuración ────────────────────────────────────────────────────────── | |
| IMG_SIZE = 64 | |
| MODEL_PATH = "mejor_modelo_ai_vs_real.keras" | |
| # ── Cargar modelo ────────────────────────────────────────────────────────── | |
| try: | |
| modelo = tf.keras.models.load_model(MODEL_PATH) | |
| MODEL_OK = True | |
| except Exception as e: | |
| print(f"[ERROR] {e}") | |
| modelo = None | |
| MODEL_OK = False | |
| # ── Predicción ───────────────────────────────────────────────────────────── | |
| def predecir(imagen_np): | |
| if not MODEL_OK or modelo is None: | |
| return {"Error: modelo no disponible": 1.0}, "Modelo no cargado", "" | |
| if imagen_np is None: | |
| return {"Sin imagen": 1.0}, "Sube una imagen primero", "" | |
| img = Image.fromarray(imagen_np.astype("uint8")).convert("RGB") | |
| img = img.resize((IMG_SIZE, IMG_SIZE), Image.LANCZOS) | |
| arr = np.array(img, dtype=np.float32) / 255.0 | |
| arr = np.expand_dims(arr, axis=0) | |
| prob_real = float(modelo.predict(arr, verbose=0)[0][0]) | |
| prob_ai = 1.0 - prob_real | |
| confianza = max(prob_ai, prob_real) * 100 | |
| probs = { | |
| "IA-Generada": round(prob_ai, 4), | |
| "Real": round(prob_real, 4), | |
| } | |
| if prob_ai >= 0.80: | |
| veredicto = f"Imagen generada por IA — confianza {confianza:.1f}%" | |
| elif prob_ai >= 0.60: | |
| veredicto = f"Probablemente generada por IA — confianza {confianza:.1f}%" | |
| elif prob_ai >= 0.40: | |
| veredicto = f"Resultado incierto — zona de borde de decisión" | |
| elif prob_real >= 0.60: | |
| veredicto = f"Probablemente real — confianza {confianza:.1f}%" | |
| else: | |
| veredicto = f"Imagen real — confianza {confianza:.1f}%" | |
| detalle = ( | |
| f"P(IA-Generada) = {prob_ai*100:.2f}%\n" | |
| f"P(Real) = {prob_real*100:.2f}%\n" | |
| f"Confianza = {confianza:.1f}%\n" | |
| f"Resolución = {IMG_SIZE}×{IMG_SIZE} px" | |
| ) | |
| return probs, veredicto, detalle | |
| # ── CSS ──────────────────────────────────────────────────────────────────── | |
| css = """ | |
| @import url('https://fonts.googleapis.com/css2?family=DM+Serif+Display&family=DM+Mono:wght@300;400;500&family=DM+Sans:wght@300;400;500&display=swap'); | |
| * { box-sizing: border-box; } | |
| body, .gradio-container { | |
| font-family: 'DM Sans', sans-serif !important; | |
| background: #0a0a0f !important; | |
| } | |
| gradio-app { | |
| background: #0a0a0f !important; | |
| } | |
| .gradio-container { | |
| max-width: 1100px !important; | |
| margin: 0 auto !important; | |
| padding: 0 24px !important; | |
| } | |
| /* Header */ | |
| .site-header { | |
| border-bottom: 1px solid #1e1e2e; | |
| padding: 32px 0 28px; | |
| margin-bottom: 36px; | |
| } | |
| .site-header h1 { | |
| font-family: 'DM Serif Display', serif; | |
| font-size: 2.1rem; | |
| color: #f0f0f5; | |
| margin: 0 0 6px; | |
| letter-spacing: -0.02em; | |
| font-weight: 400; | |
| } | |
| .site-header p { | |
| font-family: 'DM Mono', monospace; | |
| font-size: 0.72rem; | |
| color: #4a4a6a; | |
| margin: 0; | |
| letter-spacing: 0.08em; | |
| text-transform: uppercase; | |
| } | |
| /* Tabs */ | |
| .tab-nav { | |
| border-bottom: 1px solid #1e1e2e !important; | |
| background: transparent !important; | |
| margin-bottom: 28px; | |
| } | |
| .tab-nav button { | |
| font-family: 'DM Mono', monospace !important; | |
| font-size: 0.72rem !important; | |
| letter-spacing: 0.06em !important; | |
| text-transform: uppercase !important; | |
| color: #4a4a6a !important; | |
| background: transparent !important; | |
| border: none !important; | |
| border-bottom: 2px solid transparent !important; | |
| padding: 10px 18px !important; | |
| margin-bottom: -1px !important; | |
| transition: color 0.2s, border-color 0.2s !important; | |
| } | |
| .tab-nav button.selected { | |
| color: #c8c8e8 !important; | |
| border-bottom-color: #6060c0 !important; | |
| } | |
| .tab-nav button:hover { | |
| color: #a0a0c0 !important; | |
| } | |
| /* Inputs & outputs */ | |
| .gr-input, .gr-output, input, textarea, .gr-box { | |
| background: #111120 !important; | |
| border: 1px solid #1e1e2e !important; | |
| border-radius: 8px !important; | |
| color: #d0d0e8 !important; | |
| font-family: 'DM Sans', sans-serif !important; | |
| } | |
| label span, .gr-label { | |
| font-family: 'DM Mono', monospace !important; | |
| font-size: 0.7rem !important; | |
| letter-spacing: 0.07em !important; | |
| text-transform: uppercase !important; | |
| color: #4a4a6a !important; | |
| } | |
| /* Button */ | |
| button.primary { | |
| background: #6060c0 !important; | |
| border: none !important; | |
| border-radius: 6px !important; | |
| font-family: 'DM Mono', monospace !important; | |
| font-size: 0.75rem !important; | |
| letter-spacing: 0.08em !important; | |
| text-transform: uppercase !important; | |
| color: #fff !important; | |
| padding: 10px 24px !important; | |
| transition: background 0.2s !important; | |
| cursor: pointer !important; | |
| } | |
| button.primary:hover { | |
| background: #7070d8 !important; | |
| } | |
| /* Section titles */ | |
| .section-title { | |
| font-family: 'DM Serif Display', serif; | |
| font-size: 1.15rem; | |
| color: #c8c8e8; | |
| margin: 0 0 16px; | |
| font-weight: 400; | |
| letter-spacing: -0.01em; | |
| } | |
| /* Cards */ | |
| .card { | |
| background: #111120; | |
| border: 1px solid #1e1e2e; | |
| border-radius: 10px; | |
| padding: 20px 24px; | |
| margin-bottom: 16px; | |
| } | |
| /* Tables */ | |
| .data-table { | |
| width: 100%; | |
| border-collapse: collapse; | |
| font-size: 0.83rem; | |
| color: #c0c0d8; | |
| } | |
| .data-table th { | |
| font-family: 'DM Mono', monospace; | |
| font-size: 0.68rem; | |
| letter-spacing: 0.07em; | |
| text-transform: uppercase; | |
| color: #4a4a6a; | |
| padding: 8px 12px; | |
| text-align: left; | |
| border-bottom: 1px solid #1e1e2e; | |
| font-weight: 400; | |
| } | |
| .data-table td { | |
| padding: 10px 12px; | |
| border-bottom: 1px solid #16162a; | |
| vertical-align: top; | |
| line-height: 1.5; | |
| } | |
| .data-table tr:last-child td { | |
| border-bottom: none; | |
| } | |
| .data-table tr:hover td { | |
| background: #16162a; | |
| } | |
| .winner-row td { | |
| background: rgba(96, 96, 192, 0.08) !important; | |
| } | |
| .badge { | |
| display: inline-block; | |
| font-family: 'DM Mono', monospace; | |
| font-size: 0.62rem; | |
| letter-spacing: 0.06em; | |
| text-transform: uppercase; | |
| padding: 2px 8px; | |
| border-radius: 4px; | |
| background: rgba(96, 96, 192, 0.2); | |
| color: #9090d8; | |
| border: 1px solid rgba(96, 96, 192, 0.3); | |
| margin-left: 8px; | |
| vertical-align: middle; | |
| } | |
| /* Variable grid */ | |
| .var-grid { | |
| display: grid; | |
| grid-template-columns: repeat(4, 1fr); | |
| gap: 8px; | |
| margin-top: 4px; | |
| } | |
| .var-card { | |
| background: #0d0d1a; | |
| border: 1px solid #1e1e2e; | |
| border-radius: 8px; | |
| padding: 14px 12px; | |
| text-align: center; | |
| transition: border-color 0.2s; | |
| } | |
| .var-card:hover { | |
| border-color: #3a3a6a; | |
| } | |
| .var-val { | |
| font-family: 'DM Mono', monospace; | |
| font-size: 1.25rem; | |
| font-weight: 500; | |
| color: #e0e0f5; | |
| letter-spacing: -0.01em; | |
| } | |
| .var-lbl { | |
| font-family: 'DM Mono', monospace; | |
| font-size: 0.62rem; | |
| letter-spacing: 0.07em; | |
| text-transform: uppercase; | |
| color: #3a3a5a; | |
| margin-top: 5px; | |
| } | |
| .var-card.accent-blue .var-val { color: #7878e0; } | |
| .var-card.accent-violet .var-val { color: #a060c8; } | |
| .var-card.accent-green .var-val { color: #40b080; } | |
| /* Category labels */ | |
| .cat-label { | |
| font-family: 'DM Mono', monospace; | |
| font-size: 0.65rem; | |
| letter-spacing: 0.08em; | |
| text-transform: uppercase; | |
| color: #3a3a5a; | |
| margin: 18px 0 8px; | |
| } | |
| /* Verdict output */ | |
| .verdict-box textarea { | |
| font-family: 'DM Serif Display', serif !important; | |
| font-size: 1.05rem !important; | |
| color: #d0d0f0 !important; | |
| font-style: italic !important; | |
| letter-spacing: 0.01em !important; | |
| } | |
| /* Info note */ | |
| .note { | |
| font-family: 'DM Mono', monospace; | |
| font-size: 0.7rem; | |
| color: #3a3a5a; | |
| letter-spacing: 0.04em; | |
| margin-top: 8px; | |
| } | |
| /* Divider */ | |
| hr { border: none; border-top: 1px solid #1e1e2e; margin: 28px 0; } | |
| footer { display: none !important; } | |
| """ | |
| # ── HTML blocks ──────────────────────────────────────────────────────────── | |
| header_html = """ | |
| <div class="site-header"> | |
| <h1>Detector de Imágenes IA vs. Reales</h1> | |
| <p>Pontificia Universidad Javeriana · Técnicas de Aprendizaje de Máquina · 2026</p> | |
| </div> | |
| """ | |
| tabla_modelos = """ | |
| <div class="card"> | |
| <p class="section-title">Arquitecturas Comparadas</p> | |
| <table class="data-table"> | |
| <thead> | |
| <tr> | |
| <th>Modelo</th> | |
| <th>Tipo</th> | |
| <th style="text-align:right">Parámetros</th> | |
| <th>Características clave</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| <tr> | |
| <td><strong>CNN Scratch</strong></td> | |
| <td>Capas Convolucionales + Densas</td> | |
| <td style="text-align:right; font-family:'DM Mono',monospace">~800 K</td> | |
| <td>3 bloques Conv → BN → MaxPool → Dropout, cabeza Dense(256)</td> | |
| </tr> | |
| <tr class="winner-row"> | |
| <td><strong>EfficientNetB0</strong><span class="badge">mejor modelo</span></td> | |
| <td>Transfer Learning — ImageNet</td> | |
| <td style="text-align:right; font-family:'DM Mono',monospace">~5.3 M</td> | |
| <td>Base congelada fase 1 → fine-tuning últimas 20 capas fase 2</td> | |
| </tr> | |
| <tr> | |
| <td><strong>ViT Small</strong></td> | |
| <td>Vision Transformer</td> | |
| <td style="text-align:right; font-family:'DM Mono',monospace">~2.5 M</td> | |
| <td>Patch 8×8, 64 patches, 4 bloques Transformer, EMBED_DIM 128</td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| """ | |
| tabla_metricas = """ | |
| <div class="card"> | |
| <p class="section-title">Resultados — Test Set</p> | |
| <p class="note">Actualizar con los valores reales tras ejecutar el notebook en Colab.</p> | |
| <table class="data-table" style="margin-top:12px"> | |
| <thead> | |
| <tr> | |
| <th>Modelo</th> | |
| <th style="text-align:center">Accuracy</th> | |
| <th style="text-align:center">Precision</th> | |
| <th style="text-align:center">Recall</th> | |
| <th style="text-align:center">F1-Score</th> | |
| <th style="text-align:center">AUC-ROC</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| <tr> | |
| <td><strong>CNN Scratch</strong></td> | |
| <td style="text-align:center; font-family:'DM Mono',monospace">—</td> | |
| <td style="text-align:center; font-family:'DM Mono',monospace">—</td> | |
| <td style="text-align:center; font-family:'DM Mono',monospace">—</td> | |
| <td style="text-align:center; font-family:'DM Mono',monospace">—</td> | |
| <td style="text-align:center; font-family:'DM Mono',monospace">—</td> | |
| </tr> | |
| <tr class="winner-row"> | |
| <td><strong>EfficientNetB0</strong><span class="badge">mejor</span></td> | |
| <td style="text-align:center; font-family:'DM Mono',monospace">—</td> | |
| <td style="text-align:center; font-family:'DM Mono',monospace">—</td> | |
| <td style="text-align:center; font-family:'DM Mono',monospace">—</td> | |
| <td style="text-align:center; font-family:'DM Mono',monospace">—</td> | |
| <td style="text-align:center; font-family:'DM Mono',monospace">—</td> | |
| </tr> | |
| <tr> | |
| <td><strong>ViT Small</strong></td> | |
| <td style="text-align:center; font-family:'DM Mono',monospace">—</td> | |
| <td style="text-align:center; font-family:'DM Mono',monospace">—</td> | |
| <td style="text-align:center; font-family:'DM Mono',monospace">—</td> | |
| <td style="text-align:center; font-family:'DM Mono',monospace">—</td> | |
| <td style="text-align:center; font-family:'DM Mono',monospace">—</td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| """ | |
| tabla_hiperparametros = """ | |
| <div class="card"> | |
| <p class="section-title">Hiperparámetros de Entrenamiento</p> | |
| <table class="data-table"> | |
| <thead> | |
| <tr> | |
| <th>Parámetro</th> | |
| <th>CNN Scratch</th> | |
| <th>EfficientNetB0</th> | |
| <th>ViT Small</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| <tr> | |
| <td>Optimizador</td> | |
| <td>Adam</td><td>Adam</td><td>Adam</td> | |
| </tr> | |
| <tr> | |
| <td>Learning Rate</td> | |
| <td><span style="font-family:'DM Mono',monospace">1e-3</span></td> | |
| <td><span style="font-family:'DM Mono',monospace">1e-4 → 1e-5</span></td> | |
| <td><span style="font-family:'DM Mono',monospace">1e-4</span></td> | |
| </tr> | |
| <tr> | |
| <td>Batch Size</td> | |
| <td><span style="font-family:'DM Mono',monospace">32</span></td> | |
| <td><span style="font-family:'DM Mono',monospace">32</span></td> | |
| <td><span style="font-family:'DM Mono',monospace">32</span></td> | |
| </tr> | |
| <tr> | |
| <td>Épocas máx.</td> | |
| <td><span style="font-family:'DM Mono',monospace">30</span></td> | |
| <td><span style="font-family:'DM Mono',monospace">10 + 10 (fases)</span></td> | |
| <td><span style="font-family:'DM Mono',monospace">30</span></td> | |
| </tr> | |
| <tr> | |
| <td>Loss</td> | |
| <td>Binary Cross-Entropy</td> | |
| <td>Binary Cross-Entropy</td> | |
| <td>Binary Cross-Entropy</td> | |
| </tr> | |
| <tr> | |
| <td>Dropout</td> | |
| <td><span style="font-family:'DM Mono',monospace">0.25 – 0.40</span></td> | |
| <td><span style="font-family:'DM Mono',monospace">0.30</span></td> | |
| <td><span style="font-family:'DM Mono',monospace">0.10 – 0.30</span></td> | |
| </tr> | |
| <tr> | |
| <td>EarlyStopping</td> | |
| <td colspan="3"><span style="font-family:'DM Mono',monospace">patience=5, monitor=val_loss, restore_best_weights=True</span></td> | |
| </tr> | |
| <tr> | |
| <td>ReduceLROnPlateau</td> | |
| <td colspan="3"><span style="font-family:'DM Mono',monospace">factor=0.5, patience=3, min_lr=1e-6</span></td> | |
| </tr> | |
| <tr> | |
| <td>Class weights</td> | |
| <td colspan="3">Balanceado via <span style="font-family:'DM Mono',monospace">sklearn.compute_class_weight</span></td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| """ | |
| tabla_dataset = """ | |
| <div class="card"> | |
| <p class="section-title">Dataset</p> | |
| <table class="data-table"> | |
| <tbody> | |
| <tr> | |
| <td style="width:38%; color:#4a4a6a; font-family:'DM Mono',monospace; font-size:0.72rem; letter-spacing:0.05em; text-transform:uppercase">Fuente</td> | |
| <td>Parveshiiii/AI-vs-Real — HuggingFace Datasets</td> | |
| </tr> | |
| <tr> | |
| <td style="color:#4a4a6a; font-family:'DM Mono',monospace; font-size:0.72rem; letter-spacing:0.05em; text-transform:uppercase">Total imágenes</td> | |
| <td><span style="font-family:'DM Mono',monospace">13 999</span></td> | |
| </tr> | |
| <tr> | |
| <td style="color:#4a4a6a; font-family:'DM Mono',monospace; font-size:0.72rem; letter-spacing:0.05em; text-transform:uppercase">Distribución</td> | |
| <td><span style="font-family:'DM Mono',monospace">~75% IA-Generada / ~25% Real</span> — desbalanceado</td> | |
| </tr> | |
| <tr> | |
| <td style="color:#4a4a6a; font-family:'DM Mono',monospace; font-size:0.72rem; letter-spacing:0.05em; text-transform:uppercase">División</td> | |
| <td><span style="font-family:'DM Mono',monospace">80% train+val / 20% test</span> — estratificado</td> | |
| </tr> | |
| <tr> | |
| <td style="color:#4a4a6a; font-family:'DM Mono',monospace; font-size:0.72rem; letter-spacing:0.05em; text-transform:uppercase">Resolución entrada</td> | |
| <td><span style="font-family:'DM Mono',monospace">64×64 px</span> — normalizado [0, 1]</td> | |
| </tr> | |
| <tr> | |
| <td style="color:#4a4a6a; font-family:'DM Mono',monospace; font-size:0.72rem; letter-spacing:0.05em; text-transform:uppercase">Data augmentation</td> | |
| <td>Flip horizontal, rotación ±10%, zoom ±10%</td> | |
| </tr> | |
| <tr> | |
| <td style="color:#4a4a6a; font-family:'DM Mono',monospace; font-size:0.72rem; letter-spacing:0.05em; text-transform:uppercase">Pipeline</td> | |
| <td><span style="font-family:'DM Mono',monospace">tf.data.Dataset</span> con prefetch y augmentation en GPU</td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| """ | |
| variables_html = """ | |
| <div class="card" style="margin-top:8px"> | |
| <p class="section-title">Variables del Sistema</p> | |
| <p class="cat-label">Generales</p> | |
| <div class="var-grid"> | |
| <div class="var-card"> | |
| <div class="var-val">64×64</div> | |
| <div class="var-lbl">IMG_SIZE</div> | |
| </div> | |
| <div class="var-card"> | |
| <div class="var-val">32</div> | |
| <div class="var-lbl">BATCH_SIZE</div> | |
| </div> | |
| <div class="var-card"> | |
| <div class="var-val">42</div> | |
| <div class="var-lbl">SEED</div> | |
| </div> | |
| <div class="var-card"> | |
| <div class="var-val">80 / 20</div> | |
| <div class="var-lbl">Train / Test</div> | |
| </div> | |
| <div class="var-card accent-blue"> | |
| <div class="var-val">1e-3</div> | |
| <div class="var-lbl">LR — CNN</div> | |
| </div> | |
| <div class="var-card accent-blue"> | |
| <div class="var-val">1e-4 → 1e-5</div> | |
| <div class="var-lbl">LR — EfficientNet</div> | |
| </div> | |
| <div class="var-card accent-blue"> | |
| <div class="var-val">1e-4</div> | |
| <div class="var-lbl">LR — ViT</div> | |
| </div> | |
| <div class="var-card"> | |
| <div class="var-val">0.50</div> | |
| <div class="var-lbl">Umbral decisión</div> | |
| </div> | |
| </div> | |
| <p class="cat-label">Vision Transformer</p> | |
| <div class="var-grid"> | |
| <div class="var-card accent-violet"> | |
| <div class="var-val">8×8</div> | |
| <div class="var-lbl">PATCH_SIZE</div> | |
| </div> | |
| <div class="var-card accent-violet"> | |
| <div class="var-val">64</div> | |
| <div class="var-lbl">NUM_PATCHES</div> | |
| </div> | |
| <div class="var-card accent-violet"> | |
| <div class="var-val">128</div> | |
| <div class="var-lbl">EMBED_DIM</div> | |
| </div> | |
| <div class="var-card accent-violet"> | |
| <div class="var-val">4</div> | |
| <div class="var-lbl">NUM_HEADS</div> | |
| </div> | |
| <div class="var-card accent-violet"> | |
| <div class="var-val">4</div> | |
| <div class="var-lbl">NUM_BLOCKS</div> | |
| </div> | |
| <div class="var-card accent-violet"> | |
| <div class="var-val">256</div> | |
| <div class="var-lbl">FF_DIM</div> | |
| </div> | |
| <div class="var-card accent-violet"> | |
| <div class="var-val">0.10</div> | |
| <div class="var-lbl">DROPOUT_RATE</div> | |
| </div> | |
| </div> | |
| <p class="cat-label">Callbacks</p> | |
| <div class="var-grid"> | |
| <div class="var-card accent-green"> | |
| <div class="var-val">5</div> | |
| <div class="var-lbl">EarlyStopping patience</div> | |
| </div> | |
| <div class="var-card accent-green"> | |
| <div class="var-val">0.5</div> | |
| <div class="var-lbl">ReduceLR factor</div> | |
| </div> | |
| <div class="var-card accent-green"> | |
| <div class="var-val">3</div> | |
| <div class="var-lbl">ReduceLR patience</div> | |
| </div> | |
| <div class="var-card accent-green"> | |
| <div class="var-val">1e-6</div> | |
| <div class="var-lbl">ReduceLR min_lr</div> | |
| </div> | |
| </div> | |
| </div> | |
| """ | |
| sobre_html = """ | |
| <div class="card"> | |
| <p class="section-title">Sobre el Proyecto</p> | |
| <table class="data-table"> | |
| <tbody> | |
| <tr> | |
| <td style="width:22%; color:#4a4a6a; font-family:'DM Mono',monospace; font-size:0.72rem; letter-spacing:0.05em; text-transform:uppercase">Problema</td> | |
| <td>Clasificación binaria supervisada: imagen IA-Generada vs. imagen Real</td> | |
| </tr> | |
| <tr> | |
| <td style="color:#4a4a6a; font-family:'DM Mono',monospace; font-size:0.72rem; letter-spacing:0.05em; text-transform:uppercase">Motivación</td> | |
| <td>Los generadores de imagen por IA producen imágenes indistinguibles para el ojo humano, generando riesgos de desinformación, deepfakes y fraude digital</td> | |
| </tr> | |
| <tr> | |
| <td style="color:#4a4a6a; font-family:'DM Mono',monospace; font-size:0.72rem; letter-spacing:0.05em; text-transform:uppercase">Autores</td> | |
| <td>Javier Felipe Aldana Jaramillo · Integrante 2 · Integrante 3 · Integrante 4</td> | |
| </tr> | |
| <tr> | |
| <td style="color:#4a4a6a; font-family:'DM Mono',monospace; font-size:0.72rem; letter-spacing:0.05em; text-transform:uppercase">Curso</td> | |
| <td>Técnicas de Aprendizaje de Máquina — PUJ Bogotá · 2026</td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| """ | |
| # ── Interfaz ─────────────────────────────────────────────────────────────── | |
| with gr.Blocks( | |
| title="Detector IA vs Real", | |
| css=css, | |
| ) as demo: | |
| gr.HTML(header_html) | |
| with gr.Tabs(): | |
| # Tab 1 — Predictor | |
| with gr.Tab("Predictor"): | |
| with gr.Row(equal_height=True): | |
| with gr.Column(scale=1): | |
| img_input = gr.Image( | |
| label="Imagen de entrada", | |
| type="numpy", | |
| height=300, | |
| ) | |
| btn = gr.Button("Analizar", variant="primary") | |
| with gr.Column(scale=1): | |
| out_label = gr.Label(label="Probabilidad por clase", num_top_classes=2) | |
| out_veredicto = gr.Textbox( | |
| label="Veredicto", | |
| interactive=False, | |
| lines=1, | |
| elem_classes=["verdict-box"], | |
| ) | |
| out_detalle = gr.Textbox( | |
| label="Detalle técnico", | |
| interactive=False, | |
| lines=4, | |
| ) | |
| gr.HTML(variables_html) | |
| btn.click(fn=predecir, inputs=[img_input], | |
| outputs=[out_label, out_veredicto, out_detalle]) | |
| img_input.change(fn=predecir, inputs=[img_input], | |
| outputs=[out_label, out_veredicto, out_detalle]) | |
| # Tab 2 — Modelos | |
| with gr.Tab("Modelos"): | |
| gr.HTML(tabla_modelos) | |
| gr.HTML(tabla_metricas) | |
| # Tab 3 — Hiperparámetros | |
| with gr.Tab("Hiperparámetros"): | |
| gr.HTML(tabla_hiperparametros) | |
| # Tab 4 — Dataset | |
| with gr.Tab("Dataset"): | |
| gr.HTML(tabla_dataset) | |
| # Tab 5 — Proyecto | |
| with gr.Tab("Proyecto"): | |
| gr.HTML(sobre_html) | |
| if __name__ == "__main__": | |
| demo.launch() | |