mirror of
https://github.com/MartinGia/lora-analyzer.git
synced 2026-03-28 17:43:01 +01:00
392 lines
14 KiB
Python
392 lines
14 KiB
Python
"""
|
|
LoRA Analyzer Web App
|
|
Interfaz web con Gradio para analizar archivos LoRA
|
|
Versión mejorada con interfaz en español
|
|
"""
|
|
|
|
import gradio as gr
|
|
import json
|
|
from pathlib import Path
|
|
import tempfile
|
|
import numpy as np
|
|
from lora_analyzer import LoRAAnalyzer, format_analysis_report
|
|
|
|
|
|
def convert_numpy_types(obj):
|
|
"""Convierte tipos numpy a tipos nativos de Python para serialización JSON"""
|
|
if isinstance(obj, dict):
|
|
return {key: convert_numpy_types(value) for key, value in obj.items()}
|
|
elif isinstance(obj, list):
|
|
return [convert_numpy_types(item) for item in obj]
|
|
elif isinstance(obj, np.integer):
|
|
return int(obj)
|
|
elif isinstance(obj, np.floating):
|
|
return float(obj)
|
|
elif isinstance(obj, np.ndarray):
|
|
return obj.tolist()
|
|
elif isinstance(obj, set):
|
|
return list(obj)
|
|
else:
|
|
return obj
|
|
|
|
|
|
def analyze_lora_file(file):
|
|
"""Analiza un archivo LoRA subido por el usuario"""
|
|
if file is None:
|
|
return "❌ Por favor sube un archivo LoRA primero", "{}", "⚠️ Sin archivo para analizar"
|
|
|
|
try:
|
|
# Analizar el archivo
|
|
analyzer = LoRAAnalyzer(file.name)
|
|
analysis = analyzer.analyze()
|
|
|
|
# Convertir tipos numpy a tipos nativos de Python
|
|
analysis = convert_numpy_types(analysis)
|
|
|
|
# Formato de reporte legible
|
|
report = format_analysis_report(analysis)
|
|
|
|
# JSON formateado
|
|
json_output = json.dumps(analysis, indent=2, ensure_ascii=False)
|
|
|
|
# Información clave para la UI
|
|
summary = generate_summary(analysis)
|
|
|
|
return report, json_output, summary
|
|
|
|
except Exception as e:
|
|
error_msg = f"❌ Error al analizar el archivo: {str(e)}\n\n💡 Verifica que sea un archivo LoRA válido (.safetensors, .pt, .ckpt)"
|
|
return error_msg, "{}", error_msg
|
|
|
|
|
|
def generate_summary(analysis):
|
|
"""Genera un resumen visual de la información clave"""
|
|
summary_parts = []
|
|
|
|
# Información del archivo
|
|
if "file_info" in analysis:
|
|
info = analysis["file_info"]
|
|
summary_parts.append(f"📁 **{info.get('nombre', 'N/A')}**")
|
|
summary_parts.append(f"💾 Tamaño: {info.get('tamaño_mb', 0)} MB")
|
|
|
|
# Arquitectura
|
|
if "architecture" in analysis:
|
|
arch = analysis["architecture"]
|
|
summary_parts.append(f"🏗️ Capas: {arch.get('total_layers', 0)}")
|
|
|
|
if "rank_info" in arch and arch["rank_info"]:
|
|
rank = arch["rank_info"].get("most_common_rank", "N/A")
|
|
summary_parts.append(f"📊 Rank: {rank}")
|
|
|
|
# Metadatos clave
|
|
if "metadata" in analysis:
|
|
meta = analysis["metadata"]
|
|
|
|
if "ss_base_model" in meta:
|
|
model = str(meta["ss_base_model"])
|
|
if len(model) > 50:
|
|
model = model[:47] + "..."
|
|
summary_parts.append(f"🤖 Modelo base: {model}")
|
|
|
|
if "ss_num_train_images" in meta:
|
|
summary_parts.append(f"🖼️ Imágenes entrenamiento: {meta['ss_num_train_images']}")
|
|
|
|
if "ss_learning_rate" in meta:
|
|
summary_parts.append(f"📈 Learning rate: {meta['ss_learning_rate']}")
|
|
|
|
# Recomendaciones
|
|
if "recommendations" in analysis and analysis["recommendations"]:
|
|
summary_parts.append("\n**💡 Recomendaciones principales:**")
|
|
for i, rec in enumerate(analysis["recommendations"][:3], 1):
|
|
summary_parts.append(f"{i}. {rec}")
|
|
|
|
return "\n\n".join(summary_parts) if summary_parts else "No hay información disponible"
|
|
|
|
|
|
def compare_loras(files):
|
|
"""Compara múltiples archivos LoRA"""
|
|
if not files or len(files) < 2:
|
|
return "❌ Por favor sube al menos 2 archivos LoRA para comparar"
|
|
|
|
try:
|
|
results = []
|
|
for file in files:
|
|
analyzer = LoRAAnalyzer(file.name)
|
|
analysis = analyzer.analyze()
|
|
# Convertir tipos numpy
|
|
analysis = convert_numpy_types(analysis)
|
|
results.append(analysis)
|
|
|
|
# Crear tabla comparativa
|
|
comparison = create_comparison_table(results)
|
|
return comparison
|
|
|
|
except Exception as e:
|
|
return f"❌ Error al comparar archivos: {str(e)}"
|
|
|
|
|
|
def create_comparison_table(results):
|
|
"""Crea una tabla comparativa de múltiples LoRAs"""
|
|
if not results:
|
|
return "No hay resultados para comparar"
|
|
|
|
table = "# 📊 Comparación de LoRAs\n\n"
|
|
|
|
# Nombres de archivos
|
|
table += "| Característica | " + " | ".join(r["file_info"]["nombre"] for r in results) + " |\n"
|
|
table += "|" + "---|" * (len(results) + 1) + "\n"
|
|
|
|
# Filas comparativas
|
|
rows = [
|
|
("Tamaño (MB)", lambda r: f"{r['file_info'].get('tamaño_mb', 'N/A')}"),
|
|
("Formato", lambda r: r['file_info'].get('extension', 'N/A')),
|
|
("Total capas", lambda r: r.get('architecture', {}).get('total_layers', 'N/A')),
|
|
("Rank", lambda r: r.get('architecture', {}).get('rank_info', {}).get('most_common_rank', 'N/A')),
|
|
("Modelo base", lambda r: str(r.get('metadata', {}).get('ss_base_model', 'N/A'))[:30]),
|
|
("Imágenes entreno", lambda r: r.get('metadata', {}).get('ss_num_train_images', 'N/A')),
|
|
("Learning rate", lambda r: r.get('metadata', {}).get('ss_learning_rate', 'N/A')),
|
|
("Epochs", lambda r: r.get('metadata', {}).get('ss_num_epochs', 'N/A')),
|
|
]
|
|
|
|
for label, getter in rows:
|
|
row = f"| {label} | "
|
|
row += " | ".join(str(getter(r)) for r in results)
|
|
row += " |\n"
|
|
table += row
|
|
|
|
return table
|
|
|
|
|
|
# Crear la interfaz Gradio
|
|
with gr.Blocks(title="LoRA Analyzer", theme=gr.themes.Soft()) as app:
|
|
gr.Markdown("""
|
|
# 🔍 LoRA Analyzer - Analizador de Archivos LoRA
|
|
### Descubre la arquitectura, metadatos y configuración de entrenamiento de tus LoRAs
|
|
|
|
**📁 Formatos soportados:** `.safetensors` (recomendado), `.pt`, `.pth`, `.ckpt`
|
|
|
|
**💡 Tip rápido:** Arrastra tu archivo LoRA en la zona de carga y haz clic en "🔍 Analizar"
|
|
""")
|
|
|
|
with gr.Tabs():
|
|
# Tab 1: Análisis individual
|
|
with gr.Tab("📄 Analizar LoRA"):
|
|
gr.Markdown("### Sube un archivo LoRA para análisis completo")
|
|
|
|
with gr.Row():
|
|
with gr.Column(scale=1):
|
|
file_input = gr.File(
|
|
label="Arrastra tu archivo LoRA aquí",
|
|
file_types=[".safetensors", ".pt", ".pth", ".ckpt"]
|
|
)
|
|
analyze_btn = gr.Button("🔍 Analizar", variant="primary", size="lg")
|
|
|
|
with gr.Column(scale=1):
|
|
summary_output = gr.Markdown(label="Resumen")
|
|
|
|
with gr.Row():
|
|
with gr.Column():
|
|
report_output = gr.Textbox(
|
|
label="Reporte Completo",
|
|
lines=20,
|
|
max_lines=30
|
|
)
|
|
|
|
with gr.Column():
|
|
json_output = gr.Code(
|
|
label="Datos JSON",
|
|
language="json",
|
|
lines=20
|
|
)
|
|
|
|
analyze_btn.click(
|
|
fn=analyze_lora_file,
|
|
inputs=[file_input],
|
|
outputs=[report_output, json_output, summary_output]
|
|
)
|
|
|
|
# Tab 2: Comparación
|
|
with gr.Tab("📊 Comparar LoRAs"):
|
|
gr.Markdown("### Sube 2 o más archivos LoRA para comparar")
|
|
|
|
files_input = gr.File(
|
|
label="Arrastra múltiples archivos LoRA aquí",
|
|
file_count="multiple",
|
|
file_types=[".safetensors", ".pt", ".pth", ".ckpt"]
|
|
)
|
|
compare_btn = gr.Button("⚖️ Comparar", variant="primary", size="lg")
|
|
|
|
comparison_output = gr.Markdown(label="Comparación")
|
|
|
|
compare_btn.click(
|
|
fn=compare_loras,
|
|
inputs=[files_input],
|
|
outputs=[comparison_output]
|
|
)
|
|
|
|
# Tab 3: Información y ayuda
|
|
with gr.Tab("📖 Cómo Usar"):
|
|
gr.Markdown("""
|
|
## 🚀 Guía Rápida de Uso
|
|
|
|
### Paso 1: Consigue un archivo LoRA
|
|
- Descarga LoRAs de [CivitAI](https://civitai.com) o [Hugging Face](https://huggingface.co)
|
|
- O usa tus propios LoRAs entrenados
|
|
- Formatos aceptados: `.safetensors`, `.pt`, `.pth`, `.ckpt`
|
|
|
|
### Paso 2: Analiza un LoRA
|
|
1. Ve a la pestaña **"📄 Analizar LoRA"**
|
|
2. Arrastra tu archivo a la zona de carga (o haz clic para seleccionar)
|
|
3. Haz clic en el botón **"🔍 Analizar"**
|
|
4. ¡Listo! Verás el análisis completo
|
|
|
|
### Paso 3: Lee los resultados
|
|
- **Panel izquierdo (Resumen)**: Información clave visual
|
|
- **Panel central (Reporte)**: Análisis completo en texto
|
|
- **Panel derecho (JSON)**: Datos técnicos en formato JSON
|
|
|
|
### Paso 4: Compara LoRAs (opcional)
|
|
1. Ve a la pestaña **"📊 Comparar LoRAs"**
|
|
2. Sube 2 o más archivos LoRA
|
|
3. Haz clic en **"⚖️ Comparar"**
|
|
4. Verás una tabla comparativa
|
|
|
|
---
|
|
|
|
## 📊 ¿Qué información obtienes?
|
|
|
|
### ✅ Arquitectura del LoRA
|
|
- **Rank**: Dimensión de las matrices (8, 16, 32, 64, 128...)
|
|
- **Alpha**: Factor de escalado
|
|
- **Capas**: Número total de capas modificadas
|
|
- **Parámetros**: Total de parámetros entrenables
|
|
|
|
### ✅ Metadatos de Entrenamiento
|
|
- **Modelo base**: SD 1.5, SDXL, etc.
|
|
- **Learning rate**: Tasa de aprendizaje
|
|
- **Epochs**: Número de épocas
|
|
- **Batch size**: Tamaño del lote
|
|
- **Resolución**: Resolución de entrenamiento
|
|
- **Dataset**: Número de imágenes usadas
|
|
|
|
### ✅ Recomendaciones
|
|
- Sugerencias para optimizar tus LoRAs
|
|
- Comparación con mejores prácticas
|
|
- Ideas para mejorar resultados
|
|
|
|
---
|
|
|
|
## 💡 Casos de Uso
|
|
|
|
### 🔍 Ingeniería Inversa
|
|
Analiza LoRAs públicos exitosos para aprender:
|
|
- ¿Qué rank utilizaron?
|
|
- ¿Cuántas imágenes necesitaron?
|
|
- ¿Qué learning rate funcionó?
|
|
|
|
### 🎯 Optimización
|
|
Compara diferentes versiones de tu LoRA:
|
|
- Encuentra la configuración óptima
|
|
- Identifica qué cambios mejoraron resultados
|
|
- Ahorra tiempo en experimentación
|
|
|
|
### 🐛 Debugging
|
|
Detecta problemas en tus LoRAs:
|
|
- Verifica que se entrenó correctamente
|
|
- Confirma los parámetros usados
|
|
- Identifica configuraciones incorrectas
|
|
|
|
### 📚 Investigación
|
|
Estudia diferentes enfoques:
|
|
- Compara técnicas de entrenamiento
|
|
- Analiza múltiples LoRAs
|
|
- Documenta mejores prácticas
|
|
|
|
---
|
|
|
|
## ⚠️ Limitaciones Importantes
|
|
|
|
### ❌ NO puedes recuperar:
|
|
- **Imágenes originales del dataset** (técnicamente imposible)
|
|
- **Prompts exactos usados** (a menos que estén en metadatos)
|
|
- **Detalles del preprocesamiento** (cómo se limpiaron las imágenes)
|
|
|
|
### ⚠️ Dependencias:
|
|
- La cantidad de información depende de cómo fue guardado el LoRA
|
|
- LoRAs de Kohya suelen tener más metadatos
|
|
- Algunos LoRAs antiguos pueden tener información limitada
|
|
|
|
---
|
|
|
|
## 🛠️ Otras Herramientas Disponibles
|
|
|
|
Además de esta Web App, también puedes usar:
|
|
|
|
### 📟 CLI (Línea de Comandos)
|
|
```bash
|
|
python lora_cli.py mi_lora.safetensors
|
|
python lora_cli.py lora1.safetensors lora2.safetensors --compare
|
|
```
|
|
Ideal para: análisis rápido, automatización, scripts
|
|
|
|
### 🔌 API REST
|
|
```bash
|
|
python lora_api.py
|
|
# Visita http://localhost:8000/docs
|
|
```
|
|
Ideal para: integración con otras apps, procesamiento masivo
|
|
|
|
---
|
|
|
|
## 📞 Necesitas Ayuda?
|
|
|
|
1. **Verifica la instalación**: `python test_installation.py`
|
|
2. **Lee el README.md**: Documentación completa
|
|
3. **Revisa examples.py**: Ejemplos de código
|
|
4. **Archivos de prueba**: Descarga LoRAs de ejemplo de CivitAI
|
|
|
|
---
|
|
|
|
## 🎓 Tips y Mejores Prácticas
|
|
|
|
### Para Analizar:
|
|
- Usa archivos `.safetensors` cuando sea posible (más seguros)
|
|
- Verifica que el archivo no esté corrupto
|
|
- Ten paciencia con archivos grandes (>500MB)
|
|
|
|
### Para Comparar:
|
|
- Compara LoRAs del mismo tipo (mismo modelo base)
|
|
- Enfócate en diferencias de rank y dataset
|
|
- Usa la comparación para A/B testing
|
|
|
|
### Para Aprender:
|
|
- Analiza varios LoRAs populares de tu estilo favorito
|
|
- Documenta qué configuraciones funcionan mejor
|
|
- Experimenta con los parámetros que descubras
|
|
|
|
---
|
|
|
|
**📚 Desarrollado para la comunidad de ML y creadores con IA**
|
|
|
|
*Versión Web App - LoRA Analyzer v1.0*
|
|
""")
|
|
|
|
|
|
gr.Markdown("""
|
|
---
|
|
💡 **Tip**: Para obtener mejores resultados, asegúrate de que tus LoRAs incluyan metadatos de entrenamiento.
|
|
""")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
print("🚀 Iniciando LoRA Analyzer Web App...")
|
|
print("📱 Abre tu navegador en: http://localhost:7860")
|
|
print("🛑 Presiona Ctrl+C para detener el servidor")
|
|
|
|
app.launch(
|
|
server_name="0.0.0.0",
|
|
server_port=7860,
|
|
share=False,
|
|
show_error=True
|
|
)
|