jvomiranda commited on
Commit
b964596
·
1 Parent(s): 01242ac

Add all relevant files

Browse files
app.py ADDED
@@ -0,0 +1,292 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+
3
+ from models.bow import BowModel
4
+ from models.bertimbau import BertimbauModel
5
+
6
+
7
+ # ======================================================
8
+ # Carrega modelos
9
+ # ======================================================
10
+
11
+ bow_model = BowModel()
12
+ bert_model = BertimbauModel()
13
+
14
+
15
+ # ======================================================
16
+ # Inferência
17
+ # ======================================================
18
+
19
+ def predict(text):
20
+
21
+ text = text.strip()
22
+
23
+ if len(text) == 0:
24
+ return (
25
+ "",
26
+ {},
27
+ "",
28
+ "",
29
+ {},
30
+ ""
31
+ )
32
+
33
+ # -------------------------
34
+ # BoW
35
+ # -------------------------
36
+
37
+ bow = bow_model.predict(text)
38
+
39
+ bow_prediction = (
40
+ "😊 Positivo"
41
+ if bow["prediction"] == 1
42
+ else "😞 Negativo"
43
+ )
44
+
45
+ bow_probs = {
46
+ "Positivo": bow["probabilities"][1],
47
+ "Negativo": bow["probabilities"][0]
48
+ }
49
+
50
+ if len(bow["representation"]) == 0:
51
+
52
+ bow_representation = (
53
+ "Nenhuma palavra do texto pertence ao vocabulário do modelo."
54
+ )
55
+
56
+ else:
57
+
58
+ bow_representation = "\n".join(
59
+
60
+ f"{word:<20} {count}"
61
+
62
+ for word, count in bow["representation"].items()
63
+
64
+ )
65
+
66
+ # -------------------------
67
+ # BERT
68
+ # -------------------------
69
+
70
+ bert = bert_model.predict(text)
71
+
72
+ bert_prediction = (
73
+ "😊 Positivo"
74
+ if bert["prediction"] == 1
75
+ else "😞 Negativo"
76
+ )
77
+
78
+ bert_probs = {
79
+ "Positivo": bert["probabilities"][1],
80
+ "Negativo": bert["probabilities"][0]
81
+ }
82
+
83
+ bert_repr = "\n".join(
84
+ f"{item['token']:<15} "
85
+ f"[{', '.join(f'{v:.3f}' for v in item['vector'])}, ...]"
86
+ for item in bert["representation"]
87
+ )
88
+
89
+ return (
90
+
91
+ bow_prediction,
92
+ bow_probs,
93
+ bow_representation,
94
+
95
+ bert_prediction,
96
+ bert_probs,
97
+ bert_repr
98
+
99
+ )
100
+
101
+
102
+ # ======================================================
103
+ # Interface
104
+ # ======================================================
105
+
106
+ with gr.Blocks(
107
+ title="Sentiment Analysis Playground"
108
+ ) as demo:
109
+
110
+ gr.Markdown(
111
+ """
112
+ # Sentiment Analysis Playground
113
+
114
+ Compare como diferentes modelos processam um mesmo texto.
115
+
116
+ Atualmente a demonstração possui:
117
+
118
+ - 🟦 Bag of Words + Naive Bayes
119
+ - 🟩 BERTimbau Fine-Tuned
120
+
121
+ Digite qualquer texto em português e compare como cada modelo o representa internamente antes de classificá-lo.
122
+ """
123
+ )
124
+
125
+ textbox = gr.Textbox(
126
+
127
+ label="Texto",
128
+
129
+ placeholder="Digite um texto...",
130
+
131
+ lines=4
132
+
133
+ )
134
+
135
+ button = gr.Button(
136
+
137
+ "Classificar",
138
+
139
+ variant="primary"
140
+
141
+ )
142
+
143
+ with gr.Row():
144
+
145
+ # =====================================================
146
+ # BoW
147
+ # =====================================================
148
+
149
+ with gr.Column():
150
+
151
+ gr.Markdown("## 🟦 Bag of Words")
152
+
153
+ bow_prediction = gr.Textbox(
154
+
155
+ label="Classe",
156
+
157
+ interactive=False
158
+
159
+ )
160
+
161
+ bow_probs = gr.Label(
162
+
163
+ label="Probabilidades",
164
+
165
+ num_top_classes=2
166
+
167
+ )
168
+
169
+ with gr.Accordion(
170
+
171
+ "Como o modelo representa o texto",
172
+
173
+ open=False
174
+
175
+ ):
176
+
177
+ bow_repr = gr.Textbox(
178
+
179
+ label="Representação BoW",
180
+
181
+ lines=14,
182
+
183
+ interactive=False
184
+
185
+ )
186
+
187
+ gr.Markdown("""
188
+
189
+ ### Como esse modelo funciona
190
+
191
+ - Conta palavras
192
+ - Ignora contexto
193
+ - Ignora ordem das palavras
194
+ - Baseado em frequência de termos
195
+
196
+ """)
197
+
198
+ # =====================================================
199
+ # BERT
200
+ # =====================================================
201
+
202
+ with gr.Column():
203
+
204
+ gr.Markdown("## 🟩 BERTimbau")
205
+
206
+ bert_prediction = gr.Textbox(
207
+
208
+ label="Classe",
209
+
210
+ interactive=False
211
+
212
+ )
213
+
214
+ bert_probs = gr.Label(
215
+
216
+ label="Probabilidades",
217
+
218
+ num_top_classes=2
219
+
220
+ )
221
+
222
+ with gr.Accordion(
223
+
224
+ "Como o modelo representa o texto",
225
+
226
+ open=False
227
+
228
+ ):
229
+
230
+ bert_repr = gr.Textbox(
231
+
232
+ label="Tokens WordPiece",
233
+
234
+ lines=14,
235
+
236
+ interactive=False
237
+
238
+ )
239
+
240
+ gr.Markdown("""
241
+
242
+ ### Como esse modelo funciona
243
+
244
+ - Usa Transformer
245
+ - Considera contexto
246
+ - Considera ordem das palavras
247
+ - Usa mecanismo de atenção
248
+
249
+ """)
250
+
251
+ button.click(
252
+
253
+ predict,
254
+
255
+ textbox,
256
+
257
+ [
258
+
259
+ bow_prediction,
260
+ bow_probs,
261
+ bow_repr,
262
+
263
+ bert_prediction,
264
+ bert_probs,
265
+ bert_repr
266
+
267
+ ]
268
+
269
+ )
270
+
271
+ textbox.submit(
272
+
273
+ predict,
274
+
275
+ textbox,
276
+
277
+ [
278
+
279
+ bow_prediction,
280
+ bow_probs,
281
+ bow_repr,
282
+
283
+ bert_prediction,
284
+ bert_probs,
285
+ bert_repr
286
+
287
+ ]
288
+
289
+ )
290
+
291
+
292
+ demo.launch()
models/bertimbau.py ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from transformers import (
2
+ AutoTokenizer,
3
+ AutoModelForSequenceClassification
4
+ )
5
+
6
+ import torch
7
+
8
+
9
+ MODEL_NAME = "jvomiranda/BERTimbau-Sent-Analysis"
10
+
11
+
12
+ class BertimbauModel:
13
+
14
+ def __init__(self):
15
+
16
+ self.device = torch.device(
17
+ "cuda"
18
+ if torch.cuda.is_available()
19
+ else "cpu"
20
+ )
21
+
22
+ self.tokenizer = AutoTokenizer.from_pretrained(
23
+ MODEL_NAME
24
+ )
25
+
26
+ self.model = (
27
+ AutoModelForSequenceClassification
28
+ .from_pretrained(MODEL_NAME)
29
+ .to(self.device)
30
+ )
31
+
32
+ self.model.eval()
33
+
34
+ @torch.no_grad()
35
+ def predict(self, text: str):
36
+
37
+ encoding = self.tokenizer(
38
+ text,
39
+ return_tensors="pt",
40
+ truncation=True,
41
+ max_length=128
42
+ )
43
+
44
+ encoding = {
45
+ key: value.to(self.device)
46
+ for key, value in encoding.items()
47
+ }
48
+
49
+ outputs = self.model(**encoding, output_hidden_states=True)
50
+
51
+ probabilities = torch.softmax(
52
+ outputs.logits,
53
+ dim=1
54
+ )[0]
55
+
56
+ prediction = torch.argmax(
57
+ probabilities
58
+ ).item()
59
+
60
+ tokens = self.tokenizer.convert_ids_to_tokens(
61
+ encoding["input_ids"][0]
62
+ )
63
+
64
+ token_ids = (
65
+ encoding["input_ids"][0]
66
+ .cpu()
67
+ .tolist()
68
+ )
69
+
70
+ embeddings = outputs.hidden_states[-1][0]
71
+
72
+ representation = []
73
+
74
+ for token, vector in zip(tokens, embeddings):
75
+
76
+ representation.append({
77
+ "token": token,
78
+ "vector": [
79
+ round(float(x), 2)
80
+ for x in vector[:5]
81
+ ]
82
+ })
83
+
84
+ return {
85
+
86
+ "prediction": prediction,
87
+
88
+ "probabilities": {
89
+
90
+ 0: float(probabilities[0]),
91
+
92
+ 1: float(probabilities[1])
93
+
94
+ },
95
+
96
+ "tokens": tokens,
97
+
98
+ "token_ids": token_ids,
99
+
100
+ "representation": representation
101
+
102
+ }
models/bow.py ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pathlib import Path
2
+
3
+ import joblib
4
+
5
+
6
+ ROOT = Path(__file__).resolve().parents[1]
7
+
8
+ MODEL_PATH = (
9
+ ROOT /
10
+ "train" /
11
+ "saved_models" /
12
+ "bow_pipeline.joblib"
13
+ )
14
+
15
+
16
+ class BowModel:
17
+
18
+ def __init__(self):
19
+ self.pipeline = joblib.load(MODEL_PATH)
20
+
21
+ def predict(self, text):
22
+
23
+ prediction = self.pipeline.predict([text])[0]
24
+
25
+ probabilities = self.pipeline.predict_proba([text])[0]
26
+
27
+ vectorizer = self.pipeline.named_steps["vectorizer"]
28
+
29
+ X = vectorizer.transform([text])
30
+
31
+ vector = X.toarray()[0]
32
+
33
+ vocab = vectorizer.get_feature_names_out()
34
+
35
+ representation = {}
36
+
37
+ for palavra, contagem in zip(vocab, vector):
38
+ if contagem > 0:
39
+ representation[palavra] = int(contagem)
40
+
41
+ return {
42
+ "prediction": prediction,
43
+ "probabilities": {
44
+ classe: float(prob)
45
+ for classe, prob in zip(
46
+ self.pipeline.classes_,
47
+ probabilities
48
+ )
49
+ },
50
+ "representation": representation
51
+ }
requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ datasets
2
+ joblib
3
+ numpy
4
+ pandas
5
+ scikit-learn
6
+ torch
7
+ transformers
8
+ gradio
train/saved_models/bow_pipeline.joblib ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:62c5c4def00e3e3dc56d8b6f7273a92ac7fbdb3cb967bb7e9aca187f108a3258
3
+ size 9240493