Spaces:
Sleeping
Sleeping
Update analyzers/ner_analyzer.py
Browse files- analyzers/ner_analyzer.py +73 -56
analyzers/ner_analyzer.py
CHANGED
@@ -1,45 +1,50 @@
|
|
1 |
-
from transformers import
|
2 |
import torch
|
3 |
from typing import List, Tuple
|
4 |
-
import logging
|
5 |
import spacy
|
6 |
-
|
7 |
|
8 |
logger = logging.getLogger(__name__)
|
9 |
|
10 |
-
class NERAnalyzer
|
11 |
def __init__(self):
|
12 |
-
|
|
|
13 |
logger.info(f"Carregando o modelo NER: {self.model_name}")
|
14 |
-
self.model =
|
15 |
-
self.tokenizer =
|
16 |
logger.info("Modelo NER e tokenizador carregados com sucesso")
|
17 |
|
18 |
# Carregar modelo spaCy para processamento de dependências e identificação de entidades
|
19 |
self.nlp = spacy.load("pt_core_news_lg")
|
20 |
|
21 |
def extract_entities(self, text: str) -> List[Tuple[str, str]]:
|
22 |
-
|
23 |
inputs = self.tokenizer(text, max_length=512, truncation=True, return_tensors="pt")
|
24 |
-
tokens =
|
25 |
|
26 |
with torch.no_grad():
|
27 |
outputs = self.model(**inputs).logits
|
28 |
predictions = torch.argmax(outputs, dim=2)
|
29 |
|
|
|
30 |
entities = []
|
31 |
for token, prediction in zip(tokens, predictions[0].numpy()):
|
32 |
entity_label = self.model.config.id2label[prediction]
|
33 |
-
if entity_label != "O":
|
34 |
entities.append((token, entity_label))
|
35 |
|
36 |
-
logger.
|
37 |
return entities
|
38 |
|
39 |
-
def
|
|
|
|
|
|
|
40 |
representatives = []
|
41 |
current_person = ""
|
42 |
|
|
|
43 |
for token, label in entities:
|
44 |
if label in ["B-PESSOA", "I-PESSOA"]:
|
45 |
if token.startswith('##'):
|
@@ -53,60 +58,72 @@ class NERAnalyzer(BaseAnalyzer):
|
|
53 |
|
54 |
if current_person:
|
55 |
representatives.append(current_person)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
56 |
|
57 |
-
|
|
|
58 |
|
59 |
-
def
|
60 |
"""
|
61 |
-
|
62 |
"""
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
right_context = [w.text for w in token.rights]
|
80 |
-
context = left_context + right_context
|
81 |
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
|
|
|
|
|
|
|
|
88 |
|
89 |
-
logger.debug(f"Dados de participação: {
|
90 |
-
return
|
91 |
|
92 |
-
def analyze(self, text: str) -> List[str]:
|
93 |
-
#
|
94 |
entities = self.extract_entities(text)
|
95 |
-
#
|
96 |
-
|
97 |
-
#
|
98 |
-
participation_data = self.
|
99 |
|
100 |
-
#
|
101 |
-
|
102 |
-
for
|
103 |
-
|
104 |
-
|
105 |
-
representatives_with_percentage.append(f"{rep} ({participation_data[rep]}%)")
|
106 |
-
else:
|
107 |
-
representatives_with_percentage.append(rep)
|
108 |
|
109 |
-
return
|
110 |
|
111 |
def format_output(self, representatives: List[str]) -> str:
|
112 |
output = "ANÁLISE DO CONTRATO SOCIAL (NER)\n\n"
|
|
|
1 |
+
from transformers import DistilBertTokenizer, DistilBertForTokenClassification
|
2 |
import torch
|
3 |
from typing import List, Tuple
|
|
|
4 |
import spacy
|
5 |
+
import logging
|
6 |
|
7 |
logger = logging.getLogger(__name__)
|
8 |
|
9 |
+
class NERAnalyzer:
|
10 |
def __init__(self):
|
11 |
+
# Usando DistilBERT para token classification
|
12 |
+
self.model_name = "dbmdz/bert-large-cased-finetuned-conll03-english"
|
13 |
logger.info(f"Carregando o modelo NER: {self.model_name}")
|
14 |
+
self.model = DistilBertForTokenClassification.from_pretrained(self.model_name)
|
15 |
+
self.tokenizer = DistilBertTokenizer.from_pretrained(self.model_name)
|
16 |
logger.info("Modelo NER e tokenizador carregados com sucesso")
|
17 |
|
18 |
# Carregar modelo spaCy para processamento de dependências e identificação de entidades
|
19 |
self.nlp = spacy.load("pt_core_news_lg")
|
20 |
|
21 |
def extract_entities(self, text: str) -> List[Tuple[str, str]]:
|
22 |
+
# Tokeniza o texto e prepara para a análise de entidades
|
23 |
inputs = self.tokenizer(text, max_length=512, truncation=True, return_tensors="pt")
|
24 |
+
tokens = self.tokenizer.convert_ids_to_tokens(inputs['input_ids'][0])
|
25 |
|
26 |
with torch.no_grad():
|
27 |
outputs = self.model(**inputs).logits
|
28 |
predictions = torch.argmax(outputs, dim=2)
|
29 |
|
30 |
+
# Mapeia as predições para labels de entidades
|
31 |
entities = []
|
32 |
for token, prediction in zip(tokens, predictions[0].numpy()):
|
33 |
entity_label = self.model.config.id2label[prediction]
|
34 |
+
if entity_label != "O": # "O" significa que não é uma entidade
|
35 |
entities.append((token, entity_label))
|
36 |
|
37 |
+
logger.debug(f"Tokens identificados: {entities}")
|
38 |
return entities
|
39 |
|
40 |
+
def extract_representatives_and_quotas(self, entities: List[Tuple[str, str]], text: str) -> List[dict]:
|
41 |
+
"""
|
42 |
+
Extrai os representantes legais e as quotas a partir do texto usando o modelo NER.
|
43 |
+
"""
|
44 |
representatives = []
|
45 |
current_person = ""
|
46 |
|
47 |
+
# Encontrar os sócios a partir das entidades
|
48 |
for token, label in entities:
|
49 |
if label in ["B-PESSOA", "I-PESSOA"]:
|
50 |
if token.startswith('##'):
|
|
|
58 |
|
59 |
if current_person:
|
60 |
representatives.append(current_person)
|
61 |
+
|
62 |
+
# Agora, vamos analisar o texto para encontrar as quotas associadas aos sócios
|
63 |
+
doc = self.nlp(text)
|
64 |
+
quota_values = self.extract_quotas(doc)
|
65 |
+
|
66 |
+
# Associa os representantes com suas quotas
|
67 |
+
representative_data = []
|
68 |
+
for rep in representatives:
|
69 |
+
if rep in quota_values:
|
70 |
+
representative_data.append({"representante": rep, "quotas": quota_values[rep]})
|
71 |
+
else:
|
72 |
+
representative_data.append({"representante": rep, "quotas": 0})
|
73 |
|
74 |
+
logger.debug(f"Representantes e quotas extraídos: {representative_data}")
|
75 |
+
return representative_data
|
76 |
|
77 |
+
def extract_quotas(self, doc) -> dict:
|
78 |
"""
|
79 |
+
Extrai as quotas dos sócios a partir do texto processado pelo spaCy.
|
80 |
"""
|
81 |
+
quota_values = {}
|
82 |
+
# Buscando por padrões relacionados a quotas utilizando as dependências sintáticas
|
83 |
+
for ent in doc.ents:
|
84 |
+
if ent.label_ == "MONEY" and 'quota' in ent.sent.text.lower():
|
85 |
+
# Encontrar o sócio associado à quota
|
86 |
+
for token in ent.sent:
|
87 |
+
if token.dep_ == "nsubj" and token.pos_ == "PROPN":
|
88 |
+
# A entidade que está associada à quota é o sujeito da frase
|
89 |
+
name = token.text
|
90 |
+
if name not in quota_values:
|
91 |
+
quota_values[name] = 0
|
92 |
+
# Adicionar a quota à pessoa associada
|
93 |
+
quota_values[name] += float(ent.text.replace("R$", "").replace(",", ".").strip())
|
94 |
+
|
95 |
+
logger.debug(f"Valores de quotas extraídos com o spaCy: {quota_values}")
|
96 |
+
return quota_values
|
|
|
|
|
97 |
|
98 |
+
def calculate_participation(self, total_quotas: int, total_capital: float, representative_data: List[dict]) -> List[dict]:
|
99 |
+
"""
|
100 |
+
Calcula a participação de cada sócio com base nas quotas e no capital total.
|
101 |
+
"""
|
102 |
+
quota_value = total_capital / total_quotas # Valor de cada quota
|
103 |
+
for data in representative_data:
|
104 |
+
quotas = data["quotas"]
|
105 |
+
percentage = (quotas / total_quotas) * 100
|
106 |
+
data["percentual"] = round(percentage, 2)
|
107 |
+
data["valor"] = quotas * quota_value
|
108 |
|
109 |
+
logger.debug(f"Dados de participação calculados: {representative_data}")
|
110 |
+
return representative_data
|
111 |
|
112 |
+
def analyze(self, text: str, total_quotas: int, total_capital: float) -> List[str]:
|
113 |
+
# Passo 1: Extrair as entidades (nomes dos sócios) do texto
|
114 |
entities = self.extract_entities(text)
|
115 |
+
# Passo 2: Extrair representantes e associá-los com quotas
|
116 |
+
representative_data = self.extract_representatives_and_quotas(entities, text)
|
117 |
+
# Passo 3: Calcular a participação de cada representante com base nas quotas
|
118 |
+
participation_data = self.calculate_participation(total_quotas, total_capital, representative_data)
|
119 |
|
120 |
+
# Formatar a saída final com representantes e seus percentuais
|
121 |
+
formatted_output = []
|
122 |
+
for data in participation_data:
|
123 |
+
rep = data["representante"]
|
124 |
+
formatted_output.append(f"{rep} - {data['percentual']}% (R${data['valor']})")
|
|
|
|
|
|
|
125 |
|
126 |
+
return formatted_output
|
127 |
|
128 |
def format_output(self, representatives: List[str]) -> str:
|
129 |
output = "ANÁLISE DO CONTRATO SOCIAL (NER)\n\n"
|