File size: 9,448 Bytes
ea671ec
 
 
 
 
 
95c27da
ea671ec
0dcb9c1
ea671ec
 
 
 
0dcb9c1
ea671ec
534e7c2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98c9cb7
6c7d186
a1a8a38
faa980b
6c7d186
 
faa980b
6c7d186
 
faa980b
6c7d186
 
faa980b
6c7d186
 
faa980b
6c7d186
 
a1a8a38
 
534e7c2
ea671ec
 
f85d964
f428771
ea671ec
 
 
b65fa80
c592fd8
d744032
ea671ec
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
aa8acbb
de47955
ea671ec
 
 
 
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
import gradio as gr
import transformers
from transformers import AutoModelForTokenClassification, AutoTokenizer
import torch

# model large
model_name = "pierreguillou/ner-bert-large-cased-pt-lenerbr"
model_large = AutoModelForTokenClassification.from_pretrained(model_name)
tokenizer_large = AutoTokenizer.from_pretrained(model_name)

# model base
model_name = "pierreguillou/ner-bert-base-cased-pt-lenerbr"
model_base = AutoModelForTokenClassification.from_pretrained(model_name)
tokenizer_base = AutoTokenizer.from_pretrained(model_name)

# css
background_colors_entity_word = {
    'JURISPRUDENCIA': "#ccfbf1",
    'LEGISLACAO': "#ede9fe",
    'LOCAL': "#ffedd5", 
    'ORGANIZACAO': "#fae8ff",
    'PESSOA': "#e0f2fe", 
    'TEMPO': "#fefde0",  
}

background_colors_entity_tag = {
    'JURISPRUDENCIA': "#14b8a6", 
    'LEGISLACAO': "#8b5cf6", 
    'LOCAL': "#f97316", 
    'ORGANIZACAO': "#d946ef", 
    'PESSOA': "#0ea5e9", 
    'TEMPO': "#e9c70e", 
}

css = {
'entity_word': 'color:#000000;background: #xxxxxx; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 2.5; border-radius: 0.35em;',
'entity_tag': 'color:#fff;background: #xxxxxx; font-size: 0.8em; font-weight: bold; line-height: 2.5; border-radius: 0.35em; text-transform: uppercase; vertical-align: middle; margin-left: 0.5em;'
}

list_EN = "<span style='"
list_EN += f"{css['entity_tag'].replace('#xxxxxx',background_colors_entity_tag['JURISPRUDENCIA'])};padding:0.5em;"
list_EN += "'>JURISPRUDENCIA</span>"
list_EN += "<span style='"
list_EN += f"{css['entity_tag'].replace('#xxxxxx',background_colors_entity_tag['LEGISLACAO'])};padding:0.5em;"
list_EN += "'>LEGISLACAO</span>"
list_EN += "<span style='"
list_EN += f"{css['entity_tag'].replace('#xxxxxx',background_colors_entity_tag['LOCAL'])};padding:0.5em;"
list_EN += "'>LOCAL</span>"
list_EN += "<span style='"
list_EN += f"{css['entity_tag'].replace('#xxxxxx',background_colors_entity_tag['ORGANIZACAO'])};padding:0.5em;"
list_EN += "'>ORGANIZACAO</span>"
list_EN += "<span style='"
list_EN += f"{css['entity_tag'].replace('#xxxxxx',background_colors_entity_tag['PESSOA'])};padding:0.5em;"
list_EN += "'>PESSOA</span>"
list_EN += "<span style='"
list_EN += f"{css['entity_tag'].replace('#xxxxxx',background_colors_entity_tag['TEMPO'])};padding:0.5em;"
list_EN += "'>TEMPO</span>"

# list_EN += | <span style='{css['entity_tag'].replace('#xxxxxx',background_colors_entity_tag['LEGISLACAO'])}">LEGISLACAO</span>"

# infos
title = "LeNER-Br | NER App"
description = "(24/12/2021) Este aplicativo NER permite comparar 2 modelos de BERT ajustados (finetuned) na tarefa NER com o dataset jurídico LeNER-Br (NER BERT base com f1 de 0.893 e NER BERT large com f1 de 0.908). O download desses 2 modelos pode demorar alguns segundos."
article = f"<div style='text-align: center; font-size: 90%;'><p>Lista das Entidades Nomeadas (EN): {list_EN}</p><p>Links para os modelos BERT NER: <a style='color:blue;' href='https://huggingface.co/pierreguillou/ner-bert-base-cased-pt-lenerbr' target='_blank'>base</a> e <a style='color:blue;' href='https://huggingface.co/pierreguillou/ner-bert-large-cased-pt-lenerbr' target='_blank'>large</a></p><p>Blog post: <a style='color:blue;' href='https://medium.com/@pierre_guillou/nlp-modelos-e-web-app-para-reconhecimento-de-entidade-nomeada-ner-no-dom%C3%ADnio-jur%C3%ADdico-b658db55edfb' target='_blank'>NLP | Modelos e Web App para Reconhecimento de Entidade Nomeada (NER) no domínio jurídico brasileiro</a> (29/12/2021)</p><p>LeNER-Br NER App by <a style='color:blue;' href='https://www.linkedin.com/in/pierreguillou/' target='_blank'>Pierre GUILLOU</a> | <a style='color:blue;' href='https://github.com/piegu/language-models#language-models' target='_blank'>Github Repo</a></p></div>"
allow_screenshot = False
allow_flagging = False
examples = [
["Ao Instituto Médico Legal da jurisdição do acidente ou da residência cumpre fornecer, no prazo de 90 dias, laudo à vítima (art. 5, § 5, Lei n. 6.194/74  de 19 de dezembro de 1974), função técnica que pode ser suprida por prova pericial realizada por ordem do juízo da causa, ou por prova técnica realizada no âmbito administrativo que se mostre coerente com os demais elementos de prova constante dos autos."],
["Acrescento que não há de se falar em violação do artigo 114, § 3º, da Constituição Federal, posto que referido dispositivo revela-se impertinente, tratando da possibilidade de ajuizamento de dissídio coletivo pelo Ministério Público do Trabalho nos casos de greve em atividade essencial."],
["Todavia, entendo que extrair da aludida norma o sentido expresso na redação acima implica desconstruir o significado do texto constitucional, o que é absolutamente vedado ao intérprete. Nesse sentido, cito Dimitri Dimoulis: ‘(...) ao intérprete não é dado escolher significados que não estejam abarcados pela moldura da norma. Interpretar não pode significar violentar a norma.’ (Positivismo Jurídico. São Paulo: Método, 2006, p. 220).59. Dessa forma, deve-se tomar o sentido etimológico como limite da atividade interpretativa, a qual não pode superado, a ponto de destruir a própria norma a ser interpretada. Ou, como diz Konrad Hesse, ‘o texto da norma é o limite insuperável da atividade interpretativa.’ (Elementos de Direito Constitucional da República Federal da Alemanha, Porto Alegre: Sergio Antonio Fabris, 2003, p. 71)."],
]

def ner(input_text):

  num = 0
  for tokenizer,model in zip([tokenizer_large,tokenizer_base],[model_large,model_base]):

    # tokenization
    inputs = tokenizer(input_text, max_length=512, truncation=True, return_tensors="pt")
    tokens = inputs.tokens()

    # get predictions
    outputs = model(**inputs).logits
    predictions = torch.argmax(outputs, dim=2)
    preds = [model_base.config.id2label[prediction] for prediction in predictions[0].numpy()]

    # variables
    groups_pred = dict()
    group_indices = list()
    group_label = ''
    pred_prec = ''
    group_start =  ''
    count = 0

    # group the NEs
    for i,en in enumerate(preds):

      if en == 'O': 

        if len(group_indices) > 0:
          groups_pred[count] = {'indices':group_indices,'en':group_label}
          group_indices = list()
          group_label = ''
          count += 1

      if en.startswith('B'):

        if len(group_indices) > 0:
          groups_pred[count] = {'indices':group_indices,'en':group_label}
          group_indices = list()
          group_label = '' 
          count += 1 

        group_indices.append(i)
        group_label = en.replace('B-','')
        pred_prec = en

      elif en.startswith('I'):
        
        if len(group_indices) > 0:
          if en.replace('I-','') == group_label:
            group_indices.append(i)
          else:
            groups_pred[count] = {'indices':group_indices,'en':group_label}
            group_indices = [i]
            group_label = en.replace('I-','')
            count += 1 
        else:
          group_indices = [i]
          group_label = en.replace('I-','')

      if i == len(preds) - 1 and len(group_indices) > 0:
        groups_pred[count] = {'indices':group_indices,'en':group_label}
        group_indices = list()
        group_label = ''
        count += 1

    # there is at least one NE
    len_groups_pred = len(groups_pred)
    inputs = inputs['input_ids'][0].numpy()#[1:-1]
    if len_groups_pred > 0:
      for pred_num in range(len_groups_pred):
        en = groups_pred[pred_num]['en']
        indices = groups_pred[pred_num]['indices']
        if pred_num == 0:
          if indices[0] > 0:
            output = tokenizer.decode(inputs[:indices[0]]) + f'<span style="{css["entity_word"].replace("#xxxxxx",background_colors_entity_word[en])}">' + tokenizer.decode(inputs[indices[0]:indices[-1]+1]) + f'<span style="{css["entity_tag"].replace("#xxxxxx",background_colors_entity_tag[en])}">' + en + '</span></span> '
          else:
            output = f'<span style="{css["entity_word"].replace("#xxxxxx",background_colors_entity_word[en])}">' + tokenizer.decode(inputs[indices[0]:indices[-1]+1]) + f'<span style="{css["entity_tag"].replace("#xxxxxx",background_colors_entity_tag[en])}">' + en + '</span></span> '
        else:
          output += tokenizer.decode(inputs[indices_prev[-1]+1:indices[0]]) + f'<span style="{css["entity_word"].replace("#xxxxxx",background_colors_entity_word[en])}">' + tokenizer.decode(inputs[indices[0]:indices[-1]+1]) + f'<span style="{css["entity_tag"].replace("#xxxxxx",background_colors_entity_tag[en])}">' + en + '</span></span> '
        indices_prev = indices
      output += tokenizer.decode(inputs[indices_prev[-1]+1:])
    else:
      output = input_text

    # output
    output = output.replace('[CLS]','').replace(' [SEP]','').replace('##','')
    output = "<div style='max-width:100%; max-height:360px; overflow:auto'>" + output + "</div>"

    if num == 0: 
      output_large = output
      num += 1
    else: output_base = output

  return output_large, output_base

# interface gradio
iface = gr.Interface(
    title=title,
    description=description,
    article=article,
    allow_screenshot=allow_screenshot,
    allow_flagging=allow_flagging,
    fn=ner,
    inputs=gr.inputs.Textbox(placeholder="Digite uma frase aqui ou clique em um exemplo:", lines=5),
    outputs=[gr.outputs.HTML(label="NER com BERT large"),gr.outputs.HTML(label="NER com BERT base")],
    examples=examples
)

iface.launch()