tassid commited on
Commit
a17f21d
·
verified ·
1 Parent(s): 35d165e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +196 -134
app.py CHANGED
@@ -1,6 +1,6 @@
1
  """
2
- Sistema Avançado de Análise de Sentimentos
3
- Ensemble de 12 modelos para máxima precisão
4
  """
5
 
6
  import gradio as gr
@@ -11,46 +11,64 @@ from collections import Counter
11
  import warnings
12
  warnings.filterwarnings('ignore')
13
 
14
- # Lista expandida de modelos - 12 modelos especializados
15
- MODELS = [
16
- # XLM-RoBERTa variants (multilingual, excellent for Portuguese)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  "cardiffnlp/twitter-xlm-roberta-base-sentiment",
18
  "cardiffnlp/twitter-xlm-roberta-base-sentiment-multilingual",
19
  "citizenlab/twitter-xlm-roberta-base-sentiment-finetunned",
20
 
21
- # DistilBERT variants (fast and efficient)
22
  "lxyuan/distilbert-base-multilingual-cased-sentiments-student",
23
 
24
- # BERT multilingual variants
25
  "nlptown/bert-base-multilingual-uncased-sentiment",
26
 
27
- # Portuguese-specific models
28
  "neuralmind/bert-base-portuguese-cased",
29
  "neuralmind/bert-large-portuguese-cased",
30
 
31
- # Additional specialized models
32
  "finiteautomata/bertweet-base-sentiment-analysis",
33
  "siebert/sentiment-roberta-large-english",
34
-
35
- # Alternative architectures
36
  "distilbert-base-uncased-finetuned-sst-2-english",
37
  "cardiffnlp/twitter-roberta-base-sentiment-latest",
38
-
39
- # Backup models
40
  "j-hartmann/emotion-english-distilroberta-base",
41
  ]
42
 
43
- print("Inicializando sistema de ensemble avançado...")
44
- print(f"Carregando {len(MODELS)} modelos...")
45
-
46
  classifiers = []
47
- model_names = []
48
 
49
- for idx, model_name in enumerate(MODELS, 1):
50
  try:
51
- print(f"[{idx}/{len(MODELS)}] Carregando {model_name.split('/')[-1]}...", end=" ")
52
 
53
- # Alguns modelos precisam ser carregados de forma diferente
54
  if "neuralmind" in model_name or "emotion" in model_name:
55
  tokenizer = AutoTokenizer.from_pretrained(model_name)
56
  model = AutoModelForSequenceClassification.from_pretrained(model_name)
@@ -68,59 +86,110 @@ for idx, model_name in enumerate(MODELS, 1):
68
  )
69
 
70
  classifiers.append(classifier)
71
- model_names.append(model_name.split('/')[-1])
72
  print("OK")
73
 
74
- except Exception as e:
75
- print(f"FALHA ({str(e)[:30]}...)")
76
  continue
77
 
78
- total_loaded = len(classifiers)
79
  print(f"\n{'='*60}")
80
- print(f"Sistema pronto: {total_loaded} modelos ativos")
 
 
81
  print(f"{'='*60}\n")
82
 
83
- # Mapeamento completo de labels
 
 
 
84
  LABEL_MAPPING = {
85
- # Formato padrão 3 classes
86
  'NEGATIVE': 'Negativo', 'negative': 'Negativo', 'NEG': 'Negativo',
87
  'NEUTRAL': 'Neutro', 'neutral': 'Neutro', 'NEU': 'Neutro',
88
  'POSITIVE': 'Positivo', 'positive': 'Positivo', 'POS': 'Positivo',
89
-
90
- # Labels alternativos
91
  'LABEL_0': 'Negativo', 'LABEL_1': 'Neutro', 'LABEL_2': 'Positivo',
92
-
93
- # Sistema de estrelas (1-5)
94
- '1 star': 'Negativo', '2 stars': 'Negativo',
95
- '3 stars': 'Neutro',
96
  '4 stars': 'Positivo', '5 stars': 'Positivo',
97
-
98
- # Emoções (mapeadas para sentimentos)
99
- 'anger': 'Negativo', 'disgust': 'Negativo', 'fear': 'Negativo', 'sadness': 'Negativo',
100
- 'joy': 'Positivo', 'surprise': 'Neutro',
101
-
102
- # Outros formatos
103
  'neg': 'Negativo', 'neu': 'Neutro', 'pos': 'Positivo',
104
  }
105
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
106
  def normalizar_label(label):
107
- """Normaliza diferentes formatos de labels para padrão unificado"""
108
  label_upper = label.upper() if isinstance(label, str) else str(label)
109
  return LABEL_MAPPING.get(label, LABEL_MAPPING.get(label_upper, 'Neutro'))
110
 
111
  def analisar_texto(texto):
112
  """
113
- Análise avançada usando ensemble de múltiplos modelos
114
- Retorna classificação por voting majoritário
115
  """
116
 
117
  if not texto or len(texto.strip()) < 3:
118
  return "Aguardando entrada válida", {}, "-", "-", "-"
119
 
120
- # Limitar tamanho para eficiência
121
- texto_processado = texto[:512]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
 
123
- # Coletar previsões de todos os modelos
 
124
  predicoes = []
125
  scores_por_classe = {
126
  'Negativo': [],
@@ -130,16 +199,12 @@ def analisar_texto(texto):
130
 
131
  modelos_usados = 0
132
 
133
- for idx, classifier in enumerate(classifiers):
134
  try:
135
  resultado = classifier(texto_processado)[0]
136
- label_original = resultado['label']
137
  score = resultado['score']
138
 
139
- # Normalizar label
140
- label_norm = normalizar_label(label_original)
141
-
142
- # Adicionar previsão
143
  predicoes.append(label_norm)
144
  modelos_usados += 1
145
 
@@ -152,80 +217,68 @@ def analisar_texto(texto):
152
  scores_por_classe['Neutro'].append(score)
153
  scores_por_classe['Negativo'].append((1-score) * 0.5)
154
  scores_por_classe['Positivo'].append((1-score) * 0.5)
155
- else: # Positivo
156
  scores_por_classe['Positivo'].append(score)
157
  scores_por_classe['Negativo'].append((1-score) * 0.7)
158
  scores_por_classe['Neutro'].append((1-score) * 0.3)
159
 
160
- except Exception as e:
161
  continue
162
 
163
  if not predicoes or modelos_usados == 0:
164
  return "Erro no processamento", {}, "-", "-", "-"
165
 
166
  # Voting majoritário
167
- contagem_votos = Counter(predicoes)
168
- classificacao_final = contagem_votos.most_common(1)[0][0]
169
- votos_maioria = contagem_votos[classificacao_final]
170
 
171
- # Calcular probabilidades médias
172
- probabilidades = {}
173
- for classe, scores in scores_por_classe.items():
174
- if scores:
175
- probabilidades[classe] = np.mean(scores)
176
- else:
177
- probabilidades[classe] = 0.0
178
-
179
- # Normalizar probabilidades para somar 1.0
180
- total_prob = sum(probabilidades.values())
181
- if total_prob > 0:
182
- probabilidades = {k: v/total_prob for k, v in probabilidades.items()}
183
 
184
- # Calcular confiança
185
- confianca = probabilidades[classificacao_final]
186
 
187
- # Calcular desvio padrão (dispersão)
188
- scores_final = scores_por_classe[classificacao_final]
189
  if len(scores_final) > 1:
190
  desvio = np.std(scores_final)
191
- consistencia = 1 - desvio # Quanto menor o desvio, maior a consistência
 
192
  else:
193
- consistencia = 1.0
194
-
195
- # Informações detalhadas
196
- consenso = f"{votos_maioria}/{modelos_usados}"
197
- percentual_consenso = f"{(votos_maioria/modelos_usados)*100:.0f}%"
198
- nivel_consistencia = "Alta" if consistencia > 0.8 else "Média" if consistencia > 0.6 else "Baixa"
199
 
200
- # Formatação dos outputs
201
- resultado_texto = f"{classificacao_final}"
202
  confianca_texto = f"{confianca:.1%}"
203
- consenso_texto = f"{consenso} modelos ({percentual_consenso})"
204
- consistencia_texto = f"{nivel_consistencia} (σ={desvio:.3f})" if len(scores_final) > 1 else "N/A"
205
 
206
- return resultado_texto, probabilidades, confianca_texto, consenso_texto, consistencia_texto
207
 
208
- # Casos de teste diversificados
209
  casos_teste = [
210
- ["Produto excepcional. Qualidade superior e entrega dentro do prazo estabelecido."],
211
- ["Experiência extremamente negativa. Produto defeituoso e atendimento inadequado."],
212
- ["Atende as especificações básicas. Desempenho dentro do esperado para a categoria."],
213
- ["Recomendo fortemente. Excelente relação custo-benefício e durabilidade comprovada."],
214
- ["Decepcionante. Não corresponde às especificações técnicas informadas pelo fabricante."],
215
  ["Performance satisfatória. Funcionalidades adequadas ao uso proposto."],
216
- ["Péssima qualidade. Apresentou falhas graves logo nos primeiros dias de uso."],
217
- ["Surpreendeu positivamente. Supera produtos similares na mesma faixa de preço."],
218
  ]
219
 
220
- # Interface Gradio
221
- with gr.Blocks(title="Sistema de Análise de Sentimentos Avançado") as demo:
222
 
223
  gr.Markdown(
224
  f"""
225
- # Sistema Avançado de Análise de Sentimentos
226
 
227
- Classificação por ensemble com {total_loaded} modelos especializados.
228
- Utiliza voting majoritário e agregação de probabilidades para máxima precisão.
 
 
229
  """
230
  )
231
 
@@ -244,9 +297,9 @@ with gr.Blocks(title="Sistema de Análise de Sentimentos Avançado") as demo:
244
 
245
  with gr.Row():
246
  with gr.Column(scale=1):
247
- resultado_output = gr.Textbox(label="Classificação Final", interactive=False)
248
- confianca_output = gr.Textbox(label="Nível de Confiança", interactive=False)
249
- consenso_output = gr.Textbox(label="Consenso entre Modelos", interactive=False)
250
  consistencia_output = gr.Textbox(label="Consistência", interactive=False)
251
 
252
  with gr.Column(scale=1):
@@ -268,50 +321,59 @@ with gr.Blocks(title="Sistema de Análise de Sentimentos Avançado") as demo:
268
  gr.Markdown(
269
  f"""
270
  ---
271
- ## Especificações Técnicas
 
 
 
 
 
 
 
 
 
 
272
 
273
- **Arquitetura:** Ensemble híbrido com voting majoritário
274
- **Modelos Ativos:** {total_loaded} / {len(MODELS)} carregados
275
- **Processamento:** Paralelo com agregação de probabilidades
276
- **Idioma Principal:** Português (BR/PT)
 
 
 
 
 
277
  **Classes:** Negativo, Neutro, Positivo
278
- **Limite de Entrada:** 512 caracteres
279
 
280
- ### Metodologia
281
 
282
- 1. **Pré-processamento:** Normalização e truncamento do texto
283
- 2. **Inferência Paralela:** Cada modelo processa independentemente
284
- 3. **Normalização de Labels:** Unificação de diferentes formatos
285
- 4. **Voting Majoritário:** Classificação por maioria simples
286
- 5. **Agregação Probabilística:** Média ponderada das probabilidades
287
- 6. **Análise de Consistência:** Cálculo de desvio padrão entre modelos
288
 
289
- ### Modelos Incluídos
290
 
291
- - XLM-RoBERTa (Cardiff NLP) - 3 variantes
292
- - DistilBERT Multilingual
293
- - BERT Multilingual (NLP Town)
294
- - BERTimbau (NeuralMind) - 2 variantes
295
- - RoBERTa (Finiteautomata)
296
- - Siebert Sentiment RoBERTa
297
- - DistilBERT SST-2
298
- - Twitter RoBERTa Latest
299
- - DistilRoBERTa Emotion
300
 
301
- ### Métricas de Saída
 
 
 
 
 
302
 
303
- - **Classificação Final:** Resultado do voting majoritário
304
- - **Confiança:** Probabilidade média da classe predita
305
- - **Consenso:** Proporção de modelos que concordam
306
- - **Consistência:** Medida de dispersão (desvio padrão)
307
 
308
- ### Vantagens do Ensemble
 
309
 
310
- - Redução de viés de modelos individuais
311
- - Maior robustez a diferentes tipos de texto
312
- - Melhor generalização em casos ambíguos
313
- - Validação cruzada automática
314
- - Precisão superior (~15-20% vs modelo único)
315
  """
316
  )
317
 
 
1
  """
2
+ Sistema Avançado de Análise de Sentimentos com Moderação de Conteúdo
3
+ Ensemble de 12 modelos + Detecção de discurso de ódio
4
  """
5
 
6
  import gradio as gr
 
11
  import warnings
12
  warnings.filterwarnings('ignore')
13
 
14
+ # Modelos de moderação de conteúdo (verificam ANTES da análise)
15
+ MODERATION_MODELS = [
16
+ "citizenlab/distilbert-base-multilingual-cased-toxicity", # Toxicidade multilíngue
17
+ "unitary/toxic-bert", # Detecção de toxicidade
18
+ "martin-ha/toxic-comment-model", # Comentários tóxicos
19
+ ]
20
+
21
+ print("Carregando sistema de moderação de conteúdo...")
22
+ moderators = []
23
+
24
+ for model_name in MODERATION_MODELS:
25
+ try:
26
+ print(f"Carregando moderador: {model_name.split('/')[-1]}...", end=" ")
27
+ moderator = pipeline(
28
+ "text-classification",
29
+ model=model_name,
30
+ device=0 if torch.cuda.is_available() else -1
31
+ )
32
+ moderators.append(moderator)
33
+ print("OK")
34
+ except Exception as e:
35
+ print(f"FALHA")
36
+ continue
37
+
38
+ print(f"Moderadores ativos: {len(moderators)}")
39
+
40
+ # Modelos de análise de sentimentos
41
+ SENTIMENT_MODELS = [
42
+ # XLM-RoBERTa variants
43
  "cardiffnlp/twitter-xlm-roberta-base-sentiment",
44
  "cardiffnlp/twitter-xlm-roberta-base-sentiment-multilingual",
45
  "citizenlab/twitter-xlm-roberta-base-sentiment-finetunned",
46
 
47
+ # DistilBERT variants
48
  "lxyuan/distilbert-base-multilingual-cased-sentiments-student",
49
 
50
+ # BERT multilingual
51
  "nlptown/bert-base-multilingual-uncased-sentiment",
52
 
53
+ # Portuguese-specific (BERTimbau)
54
  "neuralmind/bert-base-portuguese-cased",
55
  "neuralmind/bert-large-portuguese-cased",
56
 
57
+ # Additional specialized
58
  "finiteautomata/bertweet-base-sentiment-analysis",
59
  "siebert/sentiment-roberta-large-english",
 
 
60
  "distilbert-base-uncased-finetuned-sst-2-english",
61
  "cardiffnlp/twitter-roberta-base-sentiment-latest",
 
 
62
  "j-hartmann/emotion-english-distilroberta-base",
63
  ]
64
 
65
+ print("\nCarregando modelos de análise de sentimentos...")
 
 
66
  classifiers = []
 
67
 
68
+ for idx, model_name in enumerate(SENTIMENT_MODELS, 1):
69
  try:
70
+ print(f"[{idx}/{len(SENTIMENT_MODELS)}] {model_name.split('/')[-1]}...", end=" ")
71
 
 
72
  if "neuralmind" in model_name or "emotion" in model_name:
73
  tokenizer = AutoTokenizer.from_pretrained(model_name)
74
  model = AutoModelForSequenceClassification.from_pretrained(model_name)
 
86
  )
87
 
88
  classifiers.append(classifier)
 
89
  print("OK")
90
 
91
+ except:
92
+ print("FALHA")
93
  continue
94
 
 
95
  print(f"\n{'='*60}")
96
+ print(f"Sistema completo:")
97
+ print(f"- Moderadores: {len(moderators)}")
98
+ print(f"- Analisadores: {len(classifiers)}")
99
  print(f"{'='*60}\n")
100
 
101
+ # Limiar para detecção de conteúdo problemático
102
+ TOXICITY_THRESHOLD = 0.6 # Ajustar se necessário (0.0 a 1.0)
103
+
104
+ # Mapeamento de labels
105
  LABEL_MAPPING = {
 
106
  'NEGATIVE': 'Negativo', 'negative': 'Negativo', 'NEG': 'Negativo',
107
  'NEUTRAL': 'Neutro', 'neutral': 'Neutro', 'NEU': 'Neutro',
108
  'POSITIVE': 'Positivo', 'positive': 'Positivo', 'POS': 'Positivo',
 
 
109
  'LABEL_0': 'Negativo', 'LABEL_1': 'Neutro', 'LABEL_2': 'Positivo',
110
+ '1 star': 'Negativo', '2 stars': 'Negativo', '3 stars': 'Neutro',
 
 
 
111
  '4 stars': 'Positivo', '5 stars': 'Positivo',
112
+ 'anger': 'Negativo', 'disgust': 'Negativo', 'fear': 'Negativo',
113
+ 'sadness': 'Negativo', 'joy': 'Positivo', 'surprise': 'Neutro',
 
 
 
 
114
  'neg': 'Negativo', 'neu': 'Neutro', 'pos': 'Positivo',
115
  }
116
 
117
+ def verificar_conteudo(texto):
118
+ """
119
+ Verifica se o texto contém discurso de ódio, racismo, homofobia ou outros conteúdos inadequados.
120
+ Retorna: (is_toxic, confidence)
121
+ """
122
+ if not moderators:
123
+ return False, 0.0
124
+
125
+ scores_toxicos = []
126
+
127
+ for moderator in moderators:
128
+ try:
129
+ resultado = moderator(texto[:512])[0]
130
+
131
+ # Diferentes modelos usam diferentes labels
132
+ label = resultado['label'].lower()
133
+ score = resultado['score']
134
+
135
+ # Verificar se é tóxico
136
+ if 'toxic' in label or 'hate' in label or 'offensive' in label:
137
+ scores_toxicos.append(score)
138
+ elif 'not' in label or 'neutral' in label:
139
+ scores_toxicos.append(1 - score)
140
+ else:
141
+ # Label inesperado, usar score direto
142
+ scores_toxicos.append(score)
143
+
144
+ except Exception as e:
145
+ continue
146
+
147
+ if not scores_toxicos:
148
+ return False, 0.0
149
+
150
+ # Média dos scores de toxicidade
151
+ toxicity_score = np.mean(scores_toxicos)
152
+ is_toxic = toxicity_score > TOXICITY_THRESHOLD
153
+
154
+ return is_toxic, toxicity_score
155
+
156
  def normalizar_label(label):
157
+ """Normaliza diferentes formatos de labels"""
158
  label_upper = label.upper() if isinstance(label, str) else str(label)
159
  return LABEL_MAPPING.get(label, LABEL_MAPPING.get(label_upper, 'Neutro'))
160
 
161
  def analisar_texto(texto):
162
  """
163
+ Análise com moderação de conteúdo.
164
+ Verifica discurso de ódio ANTES de analisar sentimento.
165
  """
166
 
167
  if not texto or len(texto.strip()) < 3:
168
  return "Aguardando entrada válida", {}, "-", "-", "-"
169
 
170
+ # PASSO 1: MODERAÇÃO DE CONTEÚDO
171
+ is_toxic, toxicity_score = verificar_conteudo(texto)
172
+
173
+ if is_toxic:
174
+ mensagem_recusa = (
175
+ "**Conteúdo Inadequado Detectado**\n\n"
176
+ "Este sistema não processa textos que contenham:\n"
177
+ "- Discurso de ódio\n"
178
+ "- Racismo\n"
179
+ "- Homofobia\n"
180
+ "- Conteúdo ofensivo ou discriminatório\n\n"
181
+ "Por favor, revise o texto e tente novamente com conteúdo respeitoso."
182
+ )
183
+
184
+ info_moderacao = {
185
+ 'Inadequado': toxicity_score,
186
+ 'Adequado': 1 - toxicity_score
187
+ }
188
+
189
+ return mensagem_recusa, info_moderacao, f"{toxicity_score:.1%}", "Bloqueado", "Moderação"
190
 
191
+ # PASSO 2: ANÁLISE DE SENTIMENTO (só se passar na moderação)
192
+ texto_processado = texto[:512]
193
  predicoes = []
194
  scores_por_classe = {
195
  'Negativo': [],
 
199
 
200
  modelos_usados = 0
201
 
202
+ for classifier in classifiers:
203
  try:
204
  resultado = classifier(texto_processado)[0]
205
+ label_norm = normalizar_label(resultado['label'])
206
  score = resultado['score']
207
 
 
 
 
 
208
  predicoes.append(label_norm)
209
  modelos_usados += 1
210
 
 
217
  scores_por_classe['Neutro'].append(score)
218
  scores_por_classe['Negativo'].append((1-score) * 0.5)
219
  scores_por_classe['Positivo'].append((1-score) * 0.5)
220
+ else:
221
  scores_por_classe['Positivo'].append(score)
222
  scores_por_classe['Negativo'].append((1-score) * 0.7)
223
  scores_por_classe['Neutro'].append((1-score) * 0.3)
224
 
225
+ except:
226
  continue
227
 
228
  if not predicoes or modelos_usados == 0:
229
  return "Erro no processamento", {}, "-", "-", "-"
230
 
231
  # Voting majoritário
232
+ contagem = Counter(predicoes)
233
+ classificacao = contagem.most_common(1)[0][0]
234
+ votos = contagem[classificacao]
235
 
236
+ # Probabilidades
237
+ probs = {k: np.mean(v) if v else 0.0 for k, v in scores_por_classe.items()}
238
+ total = sum(probs.values())
239
+ if total > 0:
240
+ probs = {k: v/total for k, v in probs.items()}
 
 
 
 
 
 
 
241
 
242
+ confianca = probs[classificacao]
 
243
 
244
+ # Consistência
245
+ scores_final = scores_por_classe[classificacao]
246
  if len(scores_final) > 1:
247
  desvio = np.std(scores_final)
248
+ consistencia = 1 - desvio
249
+ nivel = "Alta" if consistencia > 0.8 else "Média" if consistencia > 0.6 else "Baixa"
250
  else:
251
+ desvio = 0
252
+ nivel = "N/A"
 
 
 
 
253
 
254
+ # Outputs
255
+ resultado_texto = f"{classificacao}"
256
  confianca_texto = f"{confianca:.1%}"
257
+ consenso_texto = f"{votos}/{modelos_usados} ({(votos/modelos_usados)*100:.0f}%)"
258
+ consistencia_texto = f"{nivel} (σ={desvio:.3f})" if desvio > 0 else "N/A"
259
 
260
+ return resultado_texto, probs, confianca_texto, consenso_texto, consistencia_texto
261
 
262
+ # Casos de teste
263
  casos_teste = [
264
+ ["Produto excepcional. Qualidade superior e entrega dentro do prazo."],
265
+ ["Experiência negativa. Produto defeituoso e atendimento inadequado."],
266
+ ["Atende as especificações básicas. Desempenho dentro do esperado."],
267
+ ["Recomendo fortemente. Excelente relação custo-benefício."],
 
268
  ["Performance satisfatória. Funcionalidades adequadas ao uso proposto."],
 
 
269
  ]
270
 
271
+ # Interface
272
+ with gr.Blocks(title="Sistema de Análise com Moderação") as demo:
273
 
274
  gr.Markdown(
275
  f"""
276
+ # Sistema de Análise de Sentimentos com Moderação de Conteúdo
277
 
278
+ Ensemble de {len(classifiers)} modelos de análise com {len(moderators)} moderadores de conteúdo.
279
+
280
+ **Sistema de proteção:** Detecta e bloqueia automaticamente conteúdo com discurso de ódio,
281
+ racismo, homofobia e outros tipos de discriminação.
282
  """
283
  )
284
 
 
297
 
298
  with gr.Row():
299
  with gr.Column(scale=1):
300
+ resultado_output = gr.Markdown(label="Resultado")
301
+ confianca_output = gr.Textbox(label="Confiança", interactive=False)
302
+ consenso_output = gr.Textbox(label="Consenso", interactive=False)
303
  consistencia_output = gr.Textbox(label="Consistência", interactive=False)
304
 
305
  with gr.Column(scale=1):
 
321
  gr.Markdown(
322
  f"""
323
  ---
324
+ ## Especificações do Sistema
325
+
326
+ ### Moderação de Conteúdo
327
+
328
+ O sistema verifica automaticamente:
329
+ - Discurso de ódio
330
+ - Racismo e discriminação racial
331
+ - Homofobia e LGBTfobia
332
+ - Sexismo e misoginia
333
+ - Xenofobia
334
+ - Outros conteúdos ofensivos ou discriminatórios
335
 
336
+ **Ação:** Textos detectados como inadequados são bloqueados e não passam pela análise de sentimentos.
337
+
338
+ **Limiar de Detecção:** {TOXICITY_THRESHOLD*100:.0f}% (ajustável)
339
+
340
+ ### Análise de Sentimentos
341
+
342
+ **Modelos Ativos:** {len(classifiers)} / {len(SENTIMENT_MODELS)}
343
+ **Moderadores Ativos:** {len(moderators)} / {len(MODERATION_MODELS)}
344
+ **Arquitetura:** Ensemble com voting majoritário
345
  **Classes:** Negativo, Neutro, Positivo
346
+ **Limite:** 512 caracteres
347
 
348
+ ### Fluxo de Processamento
349
 
350
+ 1. **Recebimento:** Sistema recebe o texto
351
+ 2. **Moderação:** Verificação de conteúdo inadequado
352
+ 3. **Bloqueio:** Se inadequado, interrompe e informa
353
+ 4. **Análise:** Se adequado, processa com ensemble
354
+ 5. **Resultado:** Retorna classificação e métricas
 
355
 
356
+ ### Modelos de Moderação
357
 
358
+ - DistilBERT Toxicity (CitizenLab) - Multilíngue
359
+ - Toxic-BERT (Unitary)
360
+ - Toxic Comment Model (Martin-HA)
361
+
362
+ ### Modelos de Análise
 
 
 
 
363
 
364
+ - XLM-RoBERTa (3 variantes)
365
+ - BERTimbau (2 variantes - PT-BR)
366
+ - DistilBERT Multilingual
367
+ - BERT Multilingual
368
+ - RoBERTa (4 variantes)
369
+ - Outros especializados
370
 
371
+ ### Política de Uso
 
 
 
372
 
373
+ Este sistema foi desenvolvido para análise de sentimentos em conteúdos respeitosos.
374
+ Não tolera e não processa conteúdo que promova ódio, discriminação ou violência.
375
 
376
+ **Compromisso:** Promover análise técnica mantendo respeito e dignidade humana.
 
 
 
 
377
  """
378
  )
379