LexiMusUSAL commited on
Commit
c032fa2
·
verified ·
1 Parent(s): 148f60a

Subida inicial LexiMus-BETO-per-v1 [2/25]

Browse files
Files changed (1) hide show
  1. cargar_modelo.py +177 -0
cargar_modelo.py ADDED
@@ -0,0 +1,177 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ cargar_modelo.py
5
+ ────────────────
6
+ Ejemplo de uso completo de LexiMus-BETO-per-v1.
7
+
8
+ Carga el modelo NER transformer + Entity Ruler desde CSV y aplica
9
+ la inferencia sobre textos de prensa musical española histórica.
10
+
11
+ Requisitos:
12
+ pip install spacy>=3.8,<3.9 spacy-transformers
13
+
14
+ Uso:
15
+ python cargar_modelo.py
16
+ python cargar_modelo.py --texto "Anoche actuó la Orquesta Nacional"
17
+ python cargar_modelo.py --archivo mis_textos.txt
18
+ """
19
+
20
+ import csv
21
+ import argparse
22
+ from pathlib import Path
23
+
24
+ import spacy
25
+
26
+ # ── Configuración ──────────────────────────────────────────────────────────────
27
+
28
+ ETIQUETAS = {"COMPOSITOR", "INTERPRETE", "CANTANTE", "AGRUPACION"}
29
+
30
+ COLORES_TERMINAL = {
31
+ "COMPOSITOR": "\033[94m", # azul
32
+ "INTERPRETE": "\033[93m", # amarillo
33
+ "CANTANTE": "\033[92m", # verde
34
+ "AGRUPACION": "\033[91m", # rojo
35
+ "RESET": "\033[0m",
36
+ }
37
+
38
+ EJEMPLOS = [
39
+ "El maestro Arbós dirigió anoche la Orquesta Sinfónica de Madrid interpretando la Sinfonía en re menor de Beethoven.",
40
+ "La soprano Conchita Supervía cosechó grandes ovaciones en el Teatro Real junto al tenor Miguel Fleta.",
41
+ "El Cuarteto Francés, formado por los profesores Francés, Outumuro, Del Campo y Fontova, ofreció una sesión de música de cámara.",
42
+ "Wagner ocupa un lugar central en el programa de la temporada, con la colaboración del Orfeón Pamplonés.",
43
+ "Falla presentó su nuevo ballet ante un público entregado; la coreografía corrió a cargo de la compañía de Diaghilev.",
44
+ ]
45
+
46
+ # ── Funciones ──────────────────────────────────────────────────────────────────
47
+
48
+ def cargar_patrones_csv(ruta_csv: Path) -> list:
49
+ """Lee el CSV del Entity Ruler y devuelve lista de patrones spaCy."""
50
+ patrones = []
51
+ vistos = set()
52
+ with open(ruta_csv, encoding="utf-8") as f:
53
+ reader = csv.DictReader(f)
54
+ for fila in reader:
55
+ etq = fila["etiqueta"]
56
+ if etq not in ETIQUETAS:
57
+ continue
58
+ textos_patron = {fila["texto"].strip()}
59
+ if fila.get("variante_limpia", "").strip():
60
+ textos_patron.add(fila["variante_limpia"].strip())
61
+ notas = fila.get("notas", "")
62
+ if "Apodo:" in notas:
63
+ apodo = notas.split("Apodo:")[-1].split("|")[0].strip()
64
+ if apodo:
65
+ textos_patron.add(apodo)
66
+ for t in textos_patron:
67
+ if not t or len(t) < 2:
68
+ continue
69
+ clave = (t.lower(), etq)
70
+ if clave in vistos:
71
+ continue
72
+ vistos.add(clave)
73
+ patrones.append({"label": etq, "pattern": t})
74
+ return patrones
75
+
76
+
77
+ def cargar_leximus(ruta_modelo: str | Path, ruta_csv: str | Path) -> spacy.language.Language:
78
+ """
79
+ Carga el modelo LexiMus con Entity Ruler integrado.
80
+
81
+ Args:
82
+ ruta_modelo: Ruta al directorio del modelo spaCy (model-best/).
83
+ ruta_csv: Ruta al archivo entidades_ner_leximus.csv.
84
+
85
+ Returns:
86
+ nlp: Pipeline spaCy listo para inferencia.
87
+ """
88
+ ruta_modelo = Path(ruta_modelo)
89
+ ruta_csv = Path(ruta_csv)
90
+
91
+ if not ruta_modelo.exists():
92
+ raise FileNotFoundError(f"Modelo no encontrado: {ruta_modelo}")
93
+ if not ruta_csv.exists():
94
+ raise FileNotFoundError(f"CSV de patrones no encontrado: {ruta_csv}")
95
+
96
+ print(f"Cargando modelo desde {ruta_modelo}...")
97
+ nlp = spacy.load(ruta_modelo)
98
+
99
+ # Eliminar Entity Ruler previo (puede tener patrones desactualizados)
100
+ if "entity_ruler" in nlp.component_names:
101
+ nlp.remove_pipe("entity_ruler")
102
+
103
+ patrones = cargar_patrones_csv(ruta_csv)
104
+ ruler = nlp.add_pipe("entity_ruler", last=True, config={"overwrite_ents": False})
105
+ ruler.add_patterns(patrones)
106
+ print(f"Sistema listo: NER transformer + Entity Ruler ({len(ruler)} patrones)")
107
+ return nlp
108
+
109
+
110
+ def analizar_texto(nlp: spacy.language.Language, texto: str, color: bool = True) -> list:
111
+ """
112
+ Analiza un texto y devuelve las entidades musicales detectadas.
113
+
114
+ Args:
115
+ nlp: Pipeline cargado con cargar_leximus().
116
+ texto: Texto a analizar.
117
+ color: Si True, imprime con colores ANSI en terminal.
118
+
119
+ Returns:
120
+ Lista de dicts con keys: texto, etiqueta, inicio, fin.
121
+ """
122
+ doc = nlp(texto)
123
+ entidades = [
124
+ {"texto": e.text, "etiqueta": e.label_, "inicio": e.start_char, "fin": e.end_char}
125
+ for e in doc.ents if e.label_ in ETIQUETAS
126
+ ]
127
+
128
+ if color:
129
+ print(f"\nTexto: {texto[:100]}{'...' if len(texto) > 100 else ''}")
130
+ if entidades:
131
+ for e in entidades:
132
+ c = COLORES_TERMINAL.get(e["etiqueta"], "")
133
+ r = COLORES_TERMINAL["RESET"]
134
+ print(f" {c}[{e['etiqueta']}]{r} {e['texto']}")
135
+ else:
136
+ print(" (sin entidades detectadas)")
137
+
138
+ return entidades
139
+
140
+
141
+ def analizar_archivo(nlp: spacy.language.Language, ruta: str) -> None:
142
+ """Analiza cada línea no vacía de un archivo de texto."""
143
+ ruta = Path(ruta)
144
+ lineas = [l.strip() for l in ruta.read_text(encoding="utf-8").splitlines() if l.strip()]
145
+ print(f"\nAnalizando {len(lineas)} textos de {ruta.name}...")
146
+ total_ents = 0
147
+ for linea in lineas:
148
+ ents = analizar_texto(nlp, linea)
149
+ total_ents += len(ents)
150
+ print(f"\nTotal: {total_ents} entidades detectadas en {len(lineas)} textos.")
151
+
152
+
153
+ # ── Main ───────────────────────────────────────────────────────────────────────
154
+
155
+ def main():
156
+ parser = argparse.ArgumentParser(description="LexiMus NER - inferencia sobre texto musical español")
157
+ parser.add_argument("--modelo", default=".", help="Ruta al directorio del modelo (por defecto: directorio actual)")
158
+ parser.add_argument("--csv", default="entidades_ner_leximus.csv", help="Ruta al CSV del Entity Ruler")
159
+ parser.add_argument("--texto", help="Texto a analizar directamente")
160
+ parser.add_argument("--archivo", help="Archivo .txt con un texto por línea")
161
+ args = parser.parse_args()
162
+
163
+ nlp = cargar_leximus(args.modelo, args.csv)
164
+
165
+ if args.texto:
166
+ analizar_texto(nlp, args.texto)
167
+ elif args.archivo:
168
+ analizar_archivo(nlp, args.archivo)
169
+ else:
170
+ # Ejecutar ejemplos por defecto
171
+ print("\n── Ejemplos de inferencia ─────────────────────────────────────────")
172
+ for ejemplo in EJEMPLOS:
173
+ analizar_texto(nlp, ejemplo)
174
+
175
+
176
+ if __name__ == "__main__":
177
+ main()