thlinhares commited on
Commit
0bad57a
·
verified ·
1 Parent(s): d33e303

Update analyzers/ner_analyzer.py

Browse files
Files changed (1) hide show
  1. analyzers/ner_analyzer.py +73 -56
analyzers/ner_analyzer.py CHANGED
@@ -1,45 +1,50 @@
1
- from transformers import AutoModelForTokenClassification, AutoTokenizer
2
  import torch
3
  from typing import List, Tuple
4
- import logging
5
  import spacy
6
- from .base_analyzer import BaseAnalyzer
7
 
8
  logger = logging.getLogger(__name__)
9
 
10
- class NERAnalyzer(BaseAnalyzer):
11
  def __init__(self):
12
- self.model_name = "dominguesm/ner-legal-bert-base-cased-ptbr"
 
13
  logger.info(f"Carregando o modelo NER: {self.model_name}")
14
- self.model = AutoModelForTokenClassification.from_pretrained(self.model_name)
15
- self.tokenizer = AutoTokenizer.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
- logger.debug("Iniciando extração de entidades com NER")
23
  inputs = self.tokenizer(text, max_length=512, truncation=True, return_tensors="pt")
24
- tokens = inputs.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.info(f"tokens: {entities}")
37
  return entities
38
 
39
- def extract_representatives(self, entities: List[Tuple[str, str]]) -> List[str]:
 
 
 
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
- return representatives
 
58
 
59
- def extract_participation_percentage(self, text: str, representatives: List[str]) -> dict:
60
  """
61
- Identifica os percentuais de participação no texto e associa ao representante mais próximo.
62
  """
63
- # Utilizando o spaCy para análise de dependências e encontrar a relação entre porcentagens e nomes
64
- doc = self.nlp(text)
65
- participation_data = {}
66
-
67
- # Iterar sobre o texto para buscar percentuais e associá-los ao nome mais próximo
68
- for token in doc:
69
- # Verificar se o token é um percentual (%)
70
- if token.text.endswith('%'):
71
- percentage = token.text.replace('%', '').strip()
72
- try:
73
- percentage = float(percentage.replace(',', '.')) # Garantir que o valor seja numérico
74
- except ValueError:
75
- continue
76
-
77
- # Verificar os nomes próximos ao percentual
78
- left_context = [w.text for w in token.lefts]
79
- right_context = [w.text for w in token.rights]
80
- context = left_context + right_context
81
 
82
- # Procurar por um representante no contexto
83
- for rep in representatives:
84
- # Se o nome do representante estiver nas palavras ao redor do percentual, associamos
85
- if any(rep.lower() in word.lower() for word in context):
86
- participation_data[rep] = percentage
87
- break
 
 
 
 
88
 
89
- logger.debug(f"Dados de participação: {participation_data}")
90
- return participation_data
91
 
92
- def analyze(self, text: str) -> List[str]:
93
- # Extraímos as entidades (nomes) do texto
94
  entities = self.extract_entities(text)
95
- # Extraímos os representantes a partir das entidades
96
- representatives = self.extract_representatives(entities)
97
- # Extraímos os percentuais de participação e associamos aos representantes
98
- participation_data = self.extract_participation_percentage(text, representatives)
99
 
100
- # Associa os representantes aos seus percentuais de participação, se disponível
101
- representatives_with_percentage = []
102
- for rep in representatives:
103
- # Tentando associar o nome do representante ao percentual de participação
104
- if rep in participation_data:
105
- representatives_with_percentage.append(f"{rep} ({participation_data[rep]}%)")
106
- else:
107
- representatives_with_percentage.append(rep)
108
 
109
- return representatives_with_percentage
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"