File size: 11,488 Bytes
ccb026f 870c41d ccb026f 870c41d 5992817 ccb026f 870c41d 5992817 870c41d 5992817 870c41d 5992817 870c41d 5992817 870c41d 5992817 870c41d 5992817 870c41d 5992817 870c41d 5992817 870c41d 5992817 870c41d 5992817 870c41d 5992817 870c41d 5992817 870c41d 5992817 870c41d 5992817 870c41d 5aa7333 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 |
import gradio as gr
from transformers import AutoModelForQuestionAnswering, AutoTokenizer, pipeline
import PyPDF2
import torch
import re
from typing import List, Dict, Tuple
import nltk
from nltk.tokenize import sent_tokenize
import fitz # PyMuPDF
import logging
from tqdm import tqdm
import os
# Configurar logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class NLTKDownloader:
@staticmethod
def download_nltk_resources():
"""
Download recursos NLTK necessários e configura o diretório de dados
"""
try:
# Configurar diretório de dados NLTK
nltk_data_dir = os.path.join(os.path.expanduser("~"), "nltk_data")
if not os.path.exists(nltk_data_dir):
os.makedirs(nltk_data_dir)
nltk.data.path.append(nltk_data_dir)
# Lista de recursos necessários
resources = ['punkt']
for resource in resources:
try:
nltk.data.find(f'tokenizers/{resource}')
logger.info(f"Recurso NLTK '{resource}' já está instalado")
except LookupError:
logger.info(f"Baixando recurso NLTK '{resource}'...")
nltk.download(resource, download_dir=nltk_data_dir, quiet=True)
return True
except Exception as e:
logger.error(f"Erro ao baixar recursos NLTK: {e}")
return False
class PDFQuestionAnswering:
def __init__(self):
# Inicializar recursos NLTK
if not NLTKDownloader.download_nltk_resources():
logger.warning("Alguns recursos NLTK podem não estar disponíveis")
# Usar modelo multilíngue mais avançado
self.model_name = "deepset/roberta-base-squad2"
try:
self.tokenizer = AutoTokenizer.from_pretrained(self.model_name)
self.model = AutoModelForQuestionAnswering.from_pretrained(self.model_name)
self.nlp = pipeline('question-answering',
model=self.model,
tokenizer=self.tokenizer,
device=0 if torch.cuda.is_available() else -1)
logger.info(f"Modelo {self.model_name} carregado com sucesso")
except Exception as e:
logger.error(f"Erro ao carregar o modelo: {e}")
raise
def split_text_simple(self, text: str, max_length: int = 512) -> List[str]:
"""
Método alternativo de divisão de texto caso o NLTK não esteja disponível
"""
words = text.split()
chunks = []
current_chunk = []
current_length = 0
for word in words:
if current_length + len(word) + 1 <= max_length:
current_chunk.append(word)
current_length += len(word) + 1
else:
chunks.append(' '.join(current_chunk))
current_chunk = [word]
current_length = len(word) + 1
if current_chunk:
chunks.append(' '.join(current_chunk))
return chunks
def split_into_chunks(self, text: str, max_length: int = 512) -> List[str]:
"""
Divide o texto em chunks menores, com fallback para método simples
"""
try:
return [chunk for chunk in self.split_text_simple(text, max_length)]
except Exception as e:
logger.warning(f"Erro ao dividir texto com NLTK: {e}. Usando método simples.")
return self.split_text_simple(text, max_length)
def extract_text_from_pdf(self, pdf_file: str) -> Tuple[str, Dict[int, str]]:
"""
Extrai texto do PDF com fallback para PyPDF2
"""
try:
# Tentar primeiro com PyMuPDF
doc = fitz.open(pdf_file)
full_text = ""
page_text = {}
for page_num in range(len(doc)):
page = doc[page_num]
text = page.get_text("text")
page_text[page_num] = text
full_text += text + "\n"
doc.close()
return full_text, page_text
except Exception as e:
logger.warning(f"Erro com PyMuPDF: {e}. Tentando PyPDF2...")
try:
# Fallback para PyPDF2
with open(pdf_file, "rb") as file:
reader = PyPDF2.PdfReader(file)
full_text = ""
page_text = {}
for i, page in enumerate(reader.pages):
text = page.extract_text()
page_text[i] = text
full_text += text + "\n"
return full_text, page_text
except Exception as e2:
logger.error(f"Erro ao extrair texto do PDF: {e2}")
raise
def preprocess_text(self, text: str) -> str:
"""
Pré-processa o texto removendo caracteres especiais e formatação indesejada
"""
try:
# Remover quebras de linha extras
text = re.sub(r'\n+', ' ', text)
# Remover espaços múltiplos
text = re.sub(r'\s+', ' ', text)
# Remover caracteres especiais mas manter acentos
text = re.sub(r'[^\w\s.,!?-áéíóúâêîôûãõçà]', '', text)
return text.strip()
except Exception as e:
logger.warning(f"Erro no pré-processamento: {e}")
return text # Retorna texto original em caso de erro
def get_best_answer(self, question: str, chunks: List[str]) -> Dict:
"""
Obtém a melhor resposta considerando todos os chunks de texto
"""
try:
if not chunks:
return {
'answer': "Não foi possível processar o texto do documento.",
'score': 0,
'context': ""
}
answers = []
for chunk in chunks:
if not chunk.strip():
continue
try:
result = self.nlp(question=question, context=chunk)
answers.append(result)
except Exception as e:
logger.warning(f"Erro ao processar chunk: {e}")
continue
if not answers:
return {
'answer': "Não foi possível encontrar uma resposta no documento.",
'score': 0,
'context': ""
}
# Ordenar por score
best_answer = max(answers, key=lambda x: x['score'])
return {
'answer': best_answer['answer'],
'score': best_answer['score'],
'context': best_answer['context']
}
except Exception as e:
logger.error(f"Erro ao processar resposta: {e}")
return {
'answer': "Ocorreu um erro ao processar sua pergunta.",
'score': 0,
'context': ""
}
def answer_question(self, pdf_file: gr.File, question: str) -> Dict:
"""
Processa o PDF e responde à pergunta
"""
try:
if not pdf_file or not question:
return {
'answer': "Por favor, forneça um arquivo PDF e uma pergunta.",
'score': 0,
'confidence': "0%",
'context': ""
}
# Extrair texto do PDF
full_text, page_text = self.extract_text_from_pdf(pdf_file.name)
if not full_text.strip():
return {
'answer': "Não foi possível extrair texto do PDF fornecido.",
'score': 0,
'confidence': "0%",
'context': ""
}
# Pré-processar texto
processed_text = self.preprocess_text(full_text)
# Dividir em chunks
chunks = self.split_into_chunks(processed_text)
# Obter melhor resposta
result = self.get_best_answer(question, chunks)
# Adicionar informações extras
result['confidence'] = f"{result['score']*100:.2f}%"
return result
except Exception as e:
logger.error(f"Erro ao processar pergunta: {e}")
return {
'answer': "Ocorreu um erro ao processar sua pergunta.",
'score': 0,
'confidence': "0%",
'context': ""
}
def create_interface():
qa_system = PDFQuestionAnswering()
# Interface mais elaborada com Gradio
with gr.Blocks(title="Sistema Avançado de QA sobre PDFs") as iface:
gr.Markdown("""
# Sistema de Perguntas e Respostas sobre PDFs
Este sistema utiliza um modelo de linguagem avançado para responder perguntas sobre documentos PDF.
Carregue um PDF e faça suas perguntas!
""")
with gr.Row():
with gr.Column():
pdf_input = gr.File(
label="Carregar PDF",
file_types=[".pdf"]
)
question_input = gr.Textbox(
label="Sua Pergunta",
placeholder="Digite sua pergunta aqui..."
)
submit_btn = gr.Button("Obter Resposta", variant="primary")
with gr.Column():
answer_output = gr.Textbox(label="Resposta")
confidence_output = gr.Textbox(label="Confiança da Resposta")
context_output = gr.Textbox(
label="Contexto da Resposta",
lines=5
)
def process_question(pdf, question):
result = qa_system.answer_question(pdf, question)
return (
result['answer'],
result['confidence'],
result['context']
)
submit_btn.click(
fn=process_question,
inputs=[pdf_input, question_input],
outputs=[answer_output, confidence_output, context_output]
)
gr.Markdown("""
### Dicas de Uso
- Faça perguntas específicas e diretas
- O sistema funciona melhor com PDFs bem formatados
- A confiança indica o quanto o sistema está seguro da resposta
""")
return iface
if __name__ == "__main__":
# Criar e iniciar a interface
demo = create_interface()
try:
# Tentar lançar com configurações padrão
demo.launch(
share=False,
debug=True,
server_name="0.0.0.0", # Permite acesso de qualquer IP
server_port=7860 # Porta padrão do Gradio
)
except Exception as e:
logger.error(f"Erro ao iniciar interface: {e}")
# Tentar fallback com menos opções
demo.launch() |