AiiTServices / app.py
HenriqueBraz's picture
Update app.py
90b048e verified
import streamlit as st
from transformers import pipeline
import torch
from PIL import Image
import io
import librosa
import numpy as np
import logging
import tempfile
import os
from streamlit.runtime.uploaded_file_manager import UploadedFile
from diffusers import StableDiffusionPipeline
# Configurar página
st.set_page_config(
page_title="Aplicação de IA Multi-Modal",
page_icon="🤖",
layout="wide"
)
# Configurar logging
logging.basicConfig(
filename='app_errors.log',
level=logging.ERROR,
format='%(asctime)s - %(levelname)s - %(message)s'
)
# Cache para evitar recarregar modelos a cada execução
@st.cache_resource(show_spinner=False)
def load_models():
"""Carrega todos os modelos com cache para melhor performance"""
device = torch.device("mps" if torch.backends.mps.is_available() else "cuda" if torch.cuda.is_available() else "cpu")
logging.info(f"Usando dispositivo: {device}")
models = {}
try:
# Modelos de texto
models['sentiment_analysis'] = pipeline(
"sentiment-analysis",
model="cardiffnlp/twitter-roberta-base-sentiment-latest",
device=device
)
models['text_classification'] = pipeline(
"text-classification",
model="distilbert-base-uncased-finetuned-sst-2-english",
device=device
)
models['summarization'] = pipeline(
"summarization",
model="facebook/bart-large-cnn",
device=device
)
models['question_answering'] = pipeline(
"question-answering",
model="deepset/roberta-base-squad2",
device=device
)
models['translation'] = pipeline(
"translation",
model="Helsinki-NLP/opus-mt-tc-big-en-pt",
device=device
)
models['text_generation'] = pipeline(
"text-generation",
model="gpt2",
device=device
)
models['ner'] = pipeline(
"ner",
model="dbmdz/bert-large-cased-finetuned-conll03-english",
device=device,
aggregation_strategy="simple"
)
# Modelos de imagem
models['image_classification'] = pipeline(
"image-classification",
model="google/vit-base-patch16-224",
device=device
)
models['object_detection'] = pipeline(
"object-detection",
model="facebook/detr-resnet-50",
device=device
)
models['image_segmentation'] = pipeline(
"image-segmentation",
model="facebook/detr-resnet-50-panoptic",
device=device
)
models['facial_recognition'] = pipeline(
"image-classification",
model="mo-thecreator/vit-Facial-Expression-Recognition",
device=device
)
# Modelos de áudio
models['speech_to_text'] = pipeline(
"automatic-speech-recognition",
model="openai/whisper-base",
device=device
)
models['audio_classification'] = pipeline(
"audio-classification",
model="superb/hubert-base-superb-er",
device=device
)
# Modelos generativos
# --- Stable Diffusion com segurança dinâmica ---
sd_dtype = torch.float16 if torch.cuda.is_available() else torch.float32
# Detecta se estamos em ambiente Hugging Face Spaces (produção pública)
is_hf_spaces = "SPACE_ID" in os.environ or "HF_HOME" in os.environ
if is_hf_spaces:
# ✅ Ativa safety checker em produção
safety_model_id = "runwayml/stable-diffusion-v1-5"
safety_checker = StableDiffusionSafetyChecker.from_pretrained(safety_model_id)
feature_extractor = AutoFeatureExtractor.from_pretrained(safety_model_id)
models['text_to_image'] = StableDiffusionPipeline.from_pretrained(
"runwayml/stable-diffusion-v1-5",
torch_dtype=sd_dtype,
use_safetensors=True,
safety_checker=safety_checker,
feature_extractor=feature_extractor
)
else:
# ⚡ Modo local: desativa safety checker por performance
models['text_to_image'] = StableDiffusionPipeline.from_pretrained(
"runwayml/stable-diffusion-v1-5",
torch_dtype=sd_dtype,
use_safetensors=True,
safety_checker=None
)
try:
models['text_to_image'] = models['text_to_image'].to(device)
models['text_to_image'].enable_attention_slicing()
except Exception:
logging.info("Otimizações de SD não aplicadas para este dispositivo.")
except Exception as e:
st.error(f"Erro crítico ao carregar modelos: {str(e)}")
logging.exception("Erro ao carregar modelos")
return {}
return models
def validate_audio_file(file: UploadedFile) -> bool:
"""Valida o arquivo de áudio"""
valid_extensions = ['.wav', '.mp3', '.flac', '.m4a']
if not any(file.name.lower().endswith(ext) for ext in valid_extensions):
return False
return True
def validate_image_file(file: UploadedFile) -> bool:
"""Valida o arquivo de imagem"""
valid_extensions = ['.jpg', '.jpeg', '.png', '.bmp']
if not any(file.name.lower().endswith(ext) for ext in valid_extensions):
return False
try:
Image.open(file).verify()
# reposiciona o cursor para permitir leituras subsequentes
try:
file.seek(0)
except Exception:
pass
return True
except Exception:
return False
def process_audio_file(audio_file):
"""Processa arquivo de áudio para o formato correto"""
try:
# Criar arquivo temporário para processamento
with tempfile.NamedTemporaryFile(delete=False, suffix=os.path.splitext(audio_file.name)[1]) as tmp_file:
tmp_file.write(audio_file.read())
tmp_file_path = tmp_file.name
# Carregar áudio com librosa
audio_array, sample_rate = librosa.load(tmp_file_path, sr=16000)
os.unlink(tmp_file_path)
return audio_array
except Exception as e:
st.error(f"Erro ao processar áudio: {str(e)}")
logging.error(f"Erro no processamento de áudio: {e}")
return None
def process_image_file(image_file):
"""Processa arquivo de imagem"""
try:
image = Image.open(image_file)
if image.mode != 'RGB':
image = image.convert('RGB')
return image
except Exception as e:
st.error(f"Erro ao processar imagem: {str(e)}")
logging.error(f"Erro no processamento de imagem: {e}")
return None
def extract_images_from_sd_result(result):
"""Normaliza o retorno do StableDiffusionPipeline em uma lista de PIL Images."""
images = []
if hasattr(result, 'images'):
images = result.images
elif isinstance(result, list):
images = result
else:
images = [result]
normalized_images = []
for img in images:
if isinstance(img, Image.Image):
normalized_images.append(img)
elif isinstance(img, np.ndarray):
try:
normalized_images.append(Image.fromarray(img))
except Exception:
continue
return normalized_images
def display_results(result, model_key):
"""Exibe resultados formatados de acordo com o tipo de modelo"""
if model_key == 'summarization':
st.subheader("📝 Resumo Gerado")
st.info(result[0]['summary_text'])
elif model_key == 'translation':
st.subheader("🌍 Tradução")
st.success(result[0]['translation_text'])
elif model_key in ['sentiment_analysis', 'text_classification']:
st.subheader("📊 Resultados")
for res in result:
label = res['label']
score = res['score']
st.progress(float(score), text=f"{label} ({score:.2%})")
elif model_key == 'ner':
st.subheader("🔍 Entidades Reconhecidas")
for entity in result:
st.write(f"- **{entity['word']}**: {entity['entity_group']} (confiança: {entity['score']:.2%})")
elif model_key == 'text_generation':
st.subheader("🧠 Texto Gerado")
st.write(result[0]['generated_text'])
elif model_key == 'image_classification':
st.subheader("🏷️ Classificação")
for res in result[:5]:
st.write(f"- **{res['label']}**: {res['score']:.2%}")
elif model_key == 'object_detection':
st.subheader("📦 Objetos Detectados")
for obj in result:
st.write(f"- {obj['label']} (confiança: {obj['score']:.2%})")
elif model_key == 'image_segmentation':
st.subheader("🧩 Segmentação")
st.image(result[0]['mask'], caption="Máscara de segmentação", use_container_width=True)
elif model_key == 'facial_recognition':
st.subheader("😊 Reconhecimento Facial")
top_result = result[0]
st.write(f"**Emoção predominante**: {top_result['label']} (confiança: {top_result['score']:.2%})")
elif model_key == 'speech_to_text':
st.subheader("🔈 Transcrição")
st.success(result['text'])
elif model_key == 'audio_classification':
st.subheader("🎧 Classificação de Áudio")
top_emotion = result[0]
st.write(f"**Emoção detectada**: {top_emotion['label']} (confiança: {top_emotion['score']:.2%})")
elif model_key == 'text_to_image':
st.subheader("🎨 Imagem Gerada")
images = extract_images_from_sd_result(result)
if not images:
st.error("Nenhuma imagem foi gerada.")
return
if len(images) == 1:
st.image(images[0], caption="Imagem gerada a partir do texto")
else:
st.image(images, caption=[f"Imagem {i+1}" for i in range(len(images))])
def main():
st.title("🤖 Aplicação de IA Multi-Modal Avançada")
st.markdown("---")
# Carregar modelos
with st.spinner("Carregando modelos de IA... (Isso pode levar alguns minutos na primeira execução)"):
models = load_models()
if not models:
st.error("Falha crítica ao carregar os modelos. Verifique os logs para mais detalhes.")
return
# Sidebar para seleção de modelo
st.sidebar.title("⚙️ Configurações")
model_categories = {
"📝 Processamento de Texto": [
("Análise de Sentimento", "sentiment_analysis"),
("Classificação de Texto", "text_classification"),
("Resumo de Texto", "summarization"),
("Perguntas e Respostas", "question_answering"),
("Tradução (EN→PT)", "translation"),
("Reconhecimento de Entidades", "ner"),
("Geração de Texto", "text_generation")
],
"🖼️ Processamento de Imagem": [
("Classificação de Imagem", "image_classification"),
("Detecção de Objetos", "object_detection"),
("Segmentação de Imagem", "image_segmentation"),
("Reconhecimento Facial", "facial_recognition")
],
"🎵 Processamento de Áudio": [
("Transcrição de Áudio", "speech_to_text"),
("Classificação de Emoções", "audio_classification")
],
"✨ Modelos Generativos": [
("Texto para Imagem", "text_to_image")
]
}
selected_category = st.sidebar.selectbox(
"Categoria",
list(model_categories.keys()),
index=0
)
selected_model = st.sidebar.selectbox(
"Modelo",
[name for name, key in model_categories[selected_category]],
format_func=lambda x: x,
index=0
)
# Obter chave do modelo selecionado
model_key = next(key for name, key in model_categories[selected_category] if name == selected_model)
# Interface principal
st.header(f"{selected_model}")
# Accordion para informações do modelo
with st.expander("ℹ️ Sobre este modelo"):
model_info = {
'sentiment_analysis': "Analisa o sentimento expresso em um texto (positivo/negativo/neutro)",
'text_classification': "Classifica textos em categorias pré-definidas",
'summarization': "Gera um resumo conciso de um texto longo",
'question_answering': "Responde perguntas baseadas em um contexto fornecido",
'translation': "Traduz texto de inglês para português",
'ner': "Identifica e classifica entidades nomeadas (pessoas, lugares, organizações)",
'text_generation': "Gera texto criativo continuando a partir de um prompt",
'image_classification': "Identifica objetos e cenas em imagens",
'object_detection': "Detecta e localiza múltiplos objetos em uma imagem",
'image_segmentation': "Segmenta diferentes elementos em uma imagem",
'facial_recognition': "Reconhece características faciais e emoções",
'speech_to_text': "Transcreve fala em texto",
'audio_classification': "Classifica emoções em arquivos de áudio",
'text_to_image': "Gera imagens a partir de descrições textuais"
}
st.info(model_info.get(model_key, "Informações detalhadas sobre este modelo."))
# Processamento baseado no tipo de modelo
try:
if model_key in ['sentiment_analysis', 'text_classification', 'summarization',
'translation', 'text_generation', 'ner']:
handle_text_models(models, model_key, selected_model)
elif model_key == 'question_answering':
handle_qa_model(models, model_key)
elif model_key in ['image_classification', 'object_detection',
'image_segmentation', 'facial_recognition']:
handle_image_models(models, model_key, selected_model)
elif model_key in ['speech_to_text', 'audio_classification']:
handle_audio_models(models, model_key)
elif model_key == 'text_to_image':
handle_generative_models(models, model_key)
except Exception as e:
st.error(f"Erro inesperado durante a execução: {str(e)}")
logging.exception("Erro durante a execução do modelo")
def handle_text_models(models, model_key, model_name):
"""Manipula modelos de texto"""
input_text = st.text_area(
f"Digite o texto para {model_name.lower()}:",
height=200,
placeholder="Cole ou digite seu texto aqui...",
key=f"text_input_{model_key}"
)
# Parâmetros adicionais para alguns modelos
advanced_params = {}
if model_key == 'summarization':
with st.expander("⚙️ Parâmetros Avançados"):
advanced_params['max_length'] = st.slider("Comprimento máximo", 50, 300, 150)
advanced_params['min_length'] = st.slider("Comprimento mínimo", 10, 100, 30)
if model_key == 'text_generation':
with st.expander("⚙️ Parâmetros Avançados"):
advanced_params['max_length'] = st.slider("Comprimento do texto", 50, 500, 100)
advanced_params['temperature'] = st.slider("Criatividade", 0.1, 1.0, 0.7)
advanced_params['num_return_sequences'] = st.slider("Número de resultados", 1, 5, 1)
if st.button(f"🚀 Executar {model_name}", type="primary", key=f"btn_{model_key}"):
if input_text.strip():
with st.spinner("Processando..."):
try:
if model_key == 'ner':
result = models[model_key](input_text)
else:
result = models[model_key](input_text, **advanced_params)
display_results(result, model_key)
except Exception as e:
st.error(f"Erro ao processar texto: {str(e)}")
logging.error(f"Erro no modelo {model_key}: {e}")
else:
st.warning("⚠️ Por favor, insira um texto válido.")
def handle_qa_model(models, model_key):
"""Manipula modelo de Q&A"""
col1, col2 = st.columns(2)
with col1:
context = st.text_area(
"Contexto:",
height=200,
placeholder="Cole o texto que contém a informação...",
key="qa_context"
)
with col2:
question = st.text_area(
"Pergunta:",
height=150,
placeholder="Faça sua pergunta sobre o contexto...",
key="qa_question"
)
with st.expander("⚙️ Parâmetros Avançados"):
confidence_threshold = st.slider("Limite de confiança", 0.0, 1.0, 0.5, 0.01)
if st.button("🚀 Executar Pergunta e Resposta", type="primary", key="btn_qa"):
if context.strip() and question.strip():
with st.spinner("Buscando resposta..."):
try:
result = models[model_key](question=question, context=context)
if result['score'] < confidence_threshold:
st.warning(f"⚠️ Confiança baixa na resposta ({result['score']:.2%})")
st.success("🔍 Resposta encontrada:")
st.markdown(f"**Resposta:** {result['answer']}")
st.markdown(f"**Confiança:** {result['score']:.2%}")
except Exception as e:
st.error(f"Erro ao processar Q&A: {str(e)}")
logging.error(f"Erro no modelo Q&A: {e}")
else:
st.warning("⚠️ Por favor, forneça tanto o contexto quanto a pergunta.")
def handle_image_models(models, model_key, model_name):
"""Manipula modelos de imagem"""
uploaded_file = st.file_uploader(
"Carregue uma imagem",
type=["jpg", "png", "jpeg", "bmp"],
help="Formatos suportados: JPG, PNG, JPEG, BMP",
key=f"img_upload_{model_key}"
)
if uploaded_file is not None:
if not validate_image_file(uploaded_file):
st.error("⚠️ Formato de arquivo inválido ou arquivo corrompido.")
return
col1, col2 = st.columns(2)
with col1:
st.subheader("🖼️ Imagem Original")
image = process_image_file(uploaded_file)
if image:
st.image(image, use_container_width=True)
with col2:
st.subheader("📊 Resultados")
if st.button(f"🚀 Executar {model_name}", type="primary", key=f"btn_img_{model_key}"):
if image:
with st.spinner("Analisando imagem..."):
try:
result = models[model_key](image)
display_results(result, model_key)
except Exception as e:
st.error(f"Erro ao processar imagem: {str(e)}")
logging.error(f"Erro no modelo {model_key}: {e}")
def handle_audio_models(models, model_key):
"""Manipula modelos de áudio"""
model_name = "Transcrição de Áudio" if model_key == 'speech_to_text' else "Classificação de Áudio"
uploaded_file = st.file_uploader(
f"Carregue um arquivo de áudio para {model_name}",
type=["wav", "mp3", "flac", "m4a"],
help="Formatos suportados: WAV, MP3, FLAC, M4A",
key=f"audio_upload_{model_key}"
)
if uploaded_file is not None:
if not validate_audio_file(uploaded_file):
st.error("⚠️ Formato de arquivo inválido ou não suportado.")
return
st.audio(uploaded_file)
if st.button(f"🚀 Executar {model_name}", type="primary", key=f"btn_audio_{model_key}"):
with st.spinner("Processando áudio..."):
try:
audio_array = process_audio_file(uploaded_file)
if audio_array is not None:
result = models[model_key]({"array": audio_array, "sampling_rate": 16000})
display_results(result, model_key)
else:
st.error("Não foi possível processar o arquivo de áudio.")
except Exception as e:
st.error(f"Erro ao processar áudio: {str(e)}")
logging.error(f"Erro no modelo {model_key}: {e}")
def handle_generative_models(models, model_key):
"""Manipula modelos generativos"""
prompt = st.text_area(
"Descrição da imagem:",
height=150,
placeholder="Descreva a imagem que deseja gerar...",
key="text_to_image_prompt"
)
with st.expander("⚙️ Parâmetros Avançados"):
cols = st.columns(2)
with cols[0]:
width = st.slider("Largura", 256, 1024, 512, 64)
with cols[1]:
height = st.slider("Altura", 256, 1024, 512, 64)
num_images = st.slider("Número de imagens", 1, 4, 1)
guidance_scale = st.slider("Escala de orientação", 1.0, 20.0, 7.5)
if st.button("🚀 Gerar Imagem", type="primary", key="btn_text_to_image"):
if prompt.strip():
with st.spinner("Criando imagem..."):
try:
result = models[model_key](
prompt,
height=height,
width=width,
num_images_per_prompt=num_images,
guidance_scale=guidance_scale
)
display_results(result, model_key)
except Exception as e:
st.error(f"Erro ao gerar imagem: {str(e)}")
logging.error(f"Erro no modelo text-to-image: {e}")
else:
st.warning("⚠️ Por favor, insira uma descrição para a imagem.")
if __name__ == "__main__":
main()