MarthaIA commited on
Commit
21ab82c
·
verified ·
1 Parent(s): cfa7f41

Upload 2 files

Browse files
Files changed (2) hide show
  1. app.py +402 -0
  2. requirements.txt +7 -0
app.py ADDED
@@ -0,0 +1,402 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import pandas as pd
3
+ import spacy
4
+ from textblob import TextBlob
5
+ from transformers import pipeline
6
+ import torch
7
+ from io import BytesIO
8
+ import numpy as np
9
+
10
+ # Cargar modelos
11
+ print("⏳ Cargando modelos...")
12
+
13
+ # 1. Cargar modelo de spaCy para español
14
+ try:
15
+ nlp = spacy.load("es_core_news_sm")
16
+ print("✅ Modelo spaCy cargado correctamente")
17
+ except OSError:
18
+ print("❌ Modelo spaCy no encontrado. Usando análisis básico...")
19
+ nlp = None
20
+
21
+ # 2. Cargar múltiples modelos de sentimiento
22
+ models = {}
23
+
24
+ # Modelo multilingüe principal
25
+ try:
26
+ models['multilingual'] = pipeline(
27
+ "text-classification",
28
+ model="tabularisai/multilingual-sentiment-analysis"
29
+ )
30
+ print("✅ Modelo multilingüe cargado")
31
+ except Exception as e:
32
+ print(f"❌ Error con modelo multilingüe: {e}")
33
+ models['multilingual'] = None
34
+
35
+ # Modelo BERT para sentimiento detallado
36
+ try:
37
+ models['bert'] = pipeline(
38
+ "sentiment-analysis",
39
+ model="nlptown/bert-base-multilingual-uncased-sentiment",
40
+ tokenizer="nlptown/bert-base-multilingual-uncased-sentiment"
41
+ )
42
+ print("✅ Modelo BERT cargado")
43
+ except Exception as e:
44
+ print(f"❌ Error con modelo BERT: {e}")
45
+ models['bert'] = None
46
+
47
+ # Diccionario léxico mejorado para español
48
+ palabras_positivas = {
49
+ 'bueno', 'excelente', 'fantástico', 'maravilloso', 'perfecto', 'genial',
50
+ 'increíble', 'amo', 'encanta', 'feliz', 'contento', 'satisfecho', 'agradable',
51
+ 'recomiendo', 'magnífico', 'extraordinario', 'asombroso', 'increíble', 'estupendo',
52
+ 'fabril', 'perfectamente', 'óptimo', 'superior', 'mejor', 'inmejorable', 'ideal'
53
+ }
54
+
55
+ palabras_negativas = {
56
+ 'malo', 'terrible', 'horrible', 'pésimo', 'odio', 'decepcionado', 'fatal',
57
+ 'triste', 'enojado', 'frustrado', 'pobre', 'deficiente', 'desastroso',
58
+ 'insatisfecho', 'decepcionante', 'horroroso', 'desastroso', 'pésimo', 'malísimo',
59
+ 'inútil', 'defectuoso', 'deplorable', 'lamentable', 'desagradable', 'terrible'
60
+ }
61
+
62
+ def analizar_sentimiento_multimodelo(texto):
63
+ """Combina múltiples métodos para análisis más preciso"""
64
+ resultados = {}
65
+
66
+ # Método 1: Modelo multilingüe principal
67
+ if models['multilingual']:
68
+ try:
69
+ resultado = models['multilingual'](texto)[0]
70
+ resultados['multilingual'] = {
71
+ 'label': resultado['label'],
72
+ 'score': resultado['score'],
73
+ 'normalized_score': resultado['score'] if resultado['label'] == 'POSITIVE' else -resultado['score']
74
+ }
75
+ except Exception as e:
76
+ resultados['multilingual'] = {'error': str(e)}
77
+
78
+ # Método 2: Modelo BERT con estrellas
79
+ if models['bert']:
80
+ try:
81
+ resultado = models['bert'](texto)[0]
82
+ # Convertir estrellas a puntuación -1 a 1
83
+ star_mapping = {
84
+ '1 star': -1.0, '2 stars': -0.5, '3 stars': 0.0,
85
+ '4 stars': 0.5, '5 stars': 1.0
86
+ }
87
+ normalized_score = star_mapping.get(resultado['label'], 0.0)
88
+ resultados['bert'] = {
89
+ 'label': resultado['label'],
90
+ 'score': resultado['score'],
91
+ 'normalized_score': normalized_score
92
+ }
93
+ except Exception as e:
94
+ resultados['bert'] = {'error': str(e)}
95
+
96
+ # Método 3: Análisis léxico mejorado
97
+ if nlp:
98
+ try:
99
+ doc = nlp(texto.lower())
100
+ palabras = [token.lemma_ for token in doc if token.is_alpha and len(token.text) > 2]
101
+
102
+ positivas = sum(1 for palabra in palabras if palabra in palabras_positivas)
103
+ negativas = sum(1 for palabra in palabras if palabra in palabras_negativas)
104
+
105
+ total_relevantes = len(palabras)
106
+ if total_relevantes > 0:
107
+ polaridad = (positivas - negativas) / total_relevantes
108
+ # Normalizar a -1 a 1
109
+ polaridad = max(-1.0, min(1.0, polaridad * 5))
110
+ else:
111
+ polaridad = 0.0
112
+
113
+ resultados['lexico'] = {
114
+ 'positivas': positivas,
115
+ 'negativas': negativas,
116
+ 'total_palabras': total_relevantes,
117
+ 'normalized_score': polaridad
118
+ }
119
+ except Exception as e:
120
+ resultados['lexico'] = {'error': str(e)}
121
+
122
+ # Método 4: TextBlob (para inglés y español básico)
123
+ try:
124
+ blob = TextBlob(texto)
125
+ polaridad = blob.sentiment.polarity
126
+ resultados['textblob'] = {
127
+ 'polarity': polaridad,
128
+ 'subjectivity': blob.sentiment.subjectivity,
129
+ 'normalized_score': polaridad
130
+ }
131
+ except Exception as e:
132
+ resultados['textblob'] = {'error': str(e)}
133
+
134
+ return resultados
135
+
136
+ def determinar_sentimiento_final(resultados):
137
+ """Combina resultados de todos los métodos"""
138
+ scores = []
139
+ pesos = {'multilingual': 0.4, 'bert': 0.3, 'lexico': 0.2, 'textblob': 0.1}
140
+
141
+ for metodo, peso in pesos.items():
142
+ if metodo in resultados and 'normalized_score' in resultados[metodo]:
143
+ scores.append(resultados[metodo]['normalized_score'] * peso)
144
+
145
+ if scores:
146
+ score_final = sum(scores)
147
+
148
+ if score_final > 0.2:
149
+ return "😊 POSITIVO", score_final, "green"
150
+ elif score_final < -0.2:
151
+ return "😠 NEGATIVO", score_final, "red"
152
+ else:
153
+ return "😐 NEUTRO", score_final, "gray"
154
+ else:
155
+ return "❓ INDETERMINADO", 0.0, "orange"
156
+
157
+ def analizar_lingüistica(texto):
158
+ """Análisis lingüístico con spaCy"""
159
+ if not nlp:
160
+ return {"error": "Modelo spaCy no disponible"}
161
+
162
+ doc = nlp(texto)
163
+
164
+ analisis = {
165
+ 'estadisticas': {
166
+ 'total_tokens': len(doc),
167
+ 'total_palabras': len([token for token in doc if token.is_alpha]),
168
+ 'total_oraciones': len(list(doc.sents)),
169
+ 'total_entidades': len(doc.ents)
170
+ },
171
+ 'tokens': [],
172
+ 'entidades': [],
173
+ 'oraciones': [sent.text for sent in doc.sents]
174
+ }
175
+
176
+ # Análisis de tokens
177
+ for token in doc[:20]: # Mostrar solo primeros 20 tokens
178
+ analisis['tokens'].append({
179
+ 'texto': token.text,
180
+ 'lemma': token.lemma_,
181
+ 'POS': token.pos_,
182
+ 'explicacion': spacy.explain(token.pos_) or ''
183
+ })
184
+
185
+ # Entidades nombradas
186
+ for ent in doc.ents[:10]: # Mostrar solo primeras 10 entidades
187
+ analisis['entidades'].append({
188
+ 'texto': ent.text,
189
+ 'tipo': ent.label_,
190
+ 'explicacion': spacy.explain(ent.label_) or ''
191
+ })
192
+
193
+ return analisis
194
+
195
+ def analizar_texto_completo(texto):
196
+ """Función principal que combina todo el análisis"""
197
+ if not texto.strip():
198
+ return "❌ Por favor ingresa un texto para analizar", "", ""
199
+
200
+ # Análisis de sentimiento
201
+ resultados = analizar_sentimiento_multimodelo(texto)
202
+ sentimiento_final, score_final, color = determinar_sentimiento_final(resultados)
203
+
204
+ # Análisis lingüístico
205
+ analisis_ling = analizar_lingüistica(texto)
206
+
207
+ # Crear resumen de resultados
208
+ resumen_html = f"""
209
+ <div style='background-color: {color}20; padding: 20px; border-radius: 10px; border-left: 5px solid {color};'>
210
+ <h2 style='margin: 0; color: {color};'>{sentimiento_final}</h2>
211
+ <p style='margin: 5px 0;'><strong>Puntuación final:</strong> {score_final:.3f}</p>
212
+ <p style='margin: 5px 0;'><strong>Longitud del texto:</strong> {len(texto)} caracteres</p>
213
+ </div>
214
+ """
215
+
216
+ # Detalles por método
217
+ detalles_html = "<h3>📊 Resultados por Método:</h3>"
218
+ for metodo, resultado in resultados.items():
219
+ detalles_html += f"<div style='margin-bottom: 15px;'>"
220
+ detalles_html += f"<strong>{metodo.upper()}:</strong><br>"
221
+
222
+ if 'error' in resultado:
223
+ detalles_html += f"<span style='color: red;'>Error: {resultado['error']}</span>"
224
+ else:
225
+ if 'label' in resultado:
226
+ detalles_html += f"Etiqueta: {resultado['label']}<br>"
227
+ if 'score' in resultado:
228
+ detalles_html += f"Confianza: {resultado['score']:.3f}<br>"
229
+ if 'normalized_score' in resultado:
230
+ detalles_html += f"Puntuación: {resultado['normalized_score']:.3f}<br>"
231
+ if 'positivas' in resultado:
232
+ detalles_html += f"Palabras positivas: {resultado['positivas']}<br>"
233
+ detalles_html += f"Palabras negativas: {resultado['negativas']}<br>"
234
+
235
+ detalles_html += "</div>"
236
+
237
+ # Análisis lingüístico
238
+ ling_html = "<h3>📝 Análisis Lingüístico:</h3>"
239
+ if 'error' in analisis_ling:
240
+ ling_html += f"<p style='color: red;'>{analisis_ling['error']}</p>"
241
+ else:
242
+ stats = analisis_ling['estadisticas']
243
+ ling_html += f"""
244
+ <p><strong>Estadísticas:</strong></p>
245
+ <ul>
246
+ <li>Total de tokens: {stats['total_tokens']}</li>
247
+ <li>Palabras: {stats['total_palabras']}</li>
248
+ <li>Oraciones: {stats['total_oraciones']}</li>
249
+ <li>Entidades: {stats['total_entidades']}</li>
250
+ </ul>
251
+ """
252
+
253
+ if analisis_ling['entidades']:
254
+ ling_html += "<p><strong>Entidades detectadas:</strong></p>"
255
+ for ent in analisis_ling['entidades']:
256
+ ling_html += f"• {ent['texto']} ({ent['tipo']})<br>"
257
+
258
+ return resumen_html, detalles_html, ling_html
259
+
260
+ def analizar_archivo_excel(archivo):
261
+ """Analizar archivo Excel con textos"""
262
+ try:
263
+ df = pd.read_excel(archivo)
264
+
265
+ # Buscar columnas con texto
266
+ columnas_texto = []
267
+ for col in df.columns:
268
+ if df[col].dtype == 'object':
269
+ # Verificar si contiene texto
270
+ muestras = df[col].dropna()[:5]
271
+ if any(isinstance(x, str) and len(str(x).strip()) > 10 for x in muestras):
272
+ columnas_texto.append(col)
273
+
274
+ if not columnas_texto:
275
+ return pd.DataFrame({"Resultado": ["❌ No se encontraron columnas con texto suficiente para analizar"]})
276
+
277
+ resultados = []
278
+ for col in columnas_texto[:2]: # Analizar máximo 2 columnas
279
+ for idx, texto in enumerate(df[col].dropna()[:50]): # Máximo 50 filas por columna
280
+ if isinstance(texto, str) and len(texto.strip()) > 5:
281
+ resultado_sentimiento = analizar_sentimiento_multimodelo(texto)
282
+ sentimiento, score, _ = determinar_sentimiento_final(resultado_sentimiento)
283
+
284
+ resultados.append({
285
+ 'Columna': col,
286
+ 'Fila': idx + 1,
287
+ 'Texto': texto[:100] + '...' if len(texto) > 100 else texto,
288
+ 'Sentimiento': sentimiento,
289
+ 'Puntuación': f"{score:.3f}",
290
+ 'Longitud': len(texto)
291
+ })
292
+
293
+ if not resultados:
294
+ return pd.DataFrame({"Resultado": ["❌ No se pudieron analizar los textos del archivo"]})
295
+
296
+ df_resultados = pd.DataFrame(resultados)
297
+ return df_resultados
298
+
299
+ except Exception as e:
300
+ return pd.DataFrame({"Error": [f"❌ Error al procesar el archivo: {str(e)}"]})
301
+
302
+ # Interfaz Gradio simplificada
303
+ with gr.Blocks(theme="soft", title="Análisis Completo de Texto") as demo:
304
+ gr.Markdown("""
305
+ # 🎯 Análisis Completo de Texto - Multimodelo
306
+
307
+ **Combina análisis lingüístico + múltiples métodos de sentimiento para máxima precisión**
308
+ """)
309
+
310
+ with gr.Tab("📝 Análisis de Texto Individual"):
311
+ with gr.Row():
312
+ with gr.Column():
313
+ texto_input = gr.Textbox(
314
+ label="Ingresa tu texto",
315
+ placeholder="Escribe aquí tu texto en español, inglés, francés o portugués...",
316
+ lines=5
317
+ )
318
+ analizar_btn = gr.Button("🔍 Analizar Texto", variant="primary")
319
+
320
+ gr.Markdown("### Ejemplos rápidos:")
321
+ ejemplos = gr.Examples(
322
+ examples=[
323
+ ["Me encanta este producto! Es excelente y superó mis expectativas completamente."],
324
+ ["No estoy para nada satisfecho, la calidad es pésima y el servicio terrible."],
325
+ ["El producto cumple su función básica, pero podría mejorar en algunos aspectos."],
326
+ ["I love this amazing product! It works perfectly and the quality is outstanding."],
327
+ ["C'est un produit fantastique, je le recommande vivement à tous mes amis."],
328
+ ["Este serviço é muito bom, atendimento excelente e qualidade superior."]
329
+ ],
330
+ inputs=texto_input
331
+ )
332
+
333
+ with gr.Column():
334
+ resultado_resumen = gr.HTML(label="🎯 Resultado Principal")
335
+ resultado_detalles = gr.HTML(label="📊 Detalles por Método")
336
+ resultado_linguistica = gr.HTML(label="📝 Análisis Lingüístico")
337
+
338
+ with gr.Tab("📊 Análisis de Archivos Excel"):
339
+ with gr.Row():
340
+ with gr.Column():
341
+ archivo_input = gr.File(
342
+ label="Sube tu archivo Excel",
343
+ file_types=[".xlsx", ".xls"]
344
+ )
345
+ analizar_excel_btn = gr.Button("📈 Analizar Archivo", variant="primary")
346
+
347
+ gr.Markdown("""
348
+ **Formato esperado:**
349
+ - Archivo Excel (.xlsx o .xls)
350
+ - Columnas con texto (reseñas, comentarios, etc.)
351
+ - Mínimo 5-10 palabras por texto para mejor análisis
352
+ """)
353
+
354
+ with gr.Column():
355
+ resultado_excel = gr.Dataframe(label="Resultados del Análisis")
356
+
357
+ with gr.Tab("ℹ️ Información del Sistema"):
358
+ gr.Markdown("""
359
+ ## 🔧 Métodos de Análisis Utilizados
360
+
361
+ ### 🤖 Modelos de Machine Learning
362
+ - **Multilingual Sentiment**: Modelo especializado en múltiples idiomas
363
+ - **BERT Multilingual**: Modelo transformer con clasificación por estrellas (1-5)
364
+
365
+ ### 📚 Análisis Léxico
366
+ - Diccionario de palabras positivas/negativas en español
367
+ - Lematización y análisis morfológico
368
+
369
+ ### 📊 TextBlob
370
+ - Análisis tradicional de sentimientos
371
+ - Compatible con múltiples idiomas
372
+
373
+ ### 📝 Análisis Lingüístico (spaCy)
374
+ - Tokenización y POS tagging
375
+ - Reconocimiento de entidades
376
+ - Análisis de dependencias
377
+
378
+ ## 🎯 Combinación Inteligente
379
+ Los resultados se combinan usando pesos ponderados para mayor precisión
380
+ """)
381
+
382
+ # Conectar eventos
383
+ analizar_btn.click(
384
+ fn=analizar_texto_completo,
385
+ inputs=texto_input,
386
+ outputs=[resultado_resumen, resultado_detalles, resultado_linguistica]
387
+ )
388
+
389
+ texto_input.submit(
390
+ fn=analizar_texto_completo,
391
+ inputs=texto_input,
392
+ outputs=[resultado_resumen, resultado_detalles, resultado_linguistica]
393
+ )
394
+
395
+ analizar_excel_btn.click(
396
+ fn=analizar_archivo_excel,
397
+ inputs=archivo_input,
398
+ outputs=resultado_excel
399
+ )
400
+
401
+ if __name__ == "__main__":
402
+ demo.launch()
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ gradio>=4.0.0
2
+ transformers>=4.20.0
3
+ torch>=1.9.0
4
+ spacy>=3.0.0
5
+ textblob>=0.17.1
6
+ pandas>=1.3.0
7
+ numpy>=1.21.0