camilaaeromoca commited on
Commit
3cd80ad
·
verified ·
1 Parent(s): af228ba

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +262 -4
app.py CHANGED
@@ -1,8 +1,266 @@
1
- pip install -q gradio
2
 
3
- import torch
4
- import gradio as gr
5
- from transformers import AutoModelForCausalLM
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  modelo_llm = AutoModelForCausalLM.from_pretrained("modelos/modelo_final")
7
 
8
  # Definindo uma classe chamada NumberTokenizer, que é usada para tokenizar os números
 
 
1
 
2
+ # Tamanho do vocabulário
3
+ vocab_size = 13
4
+ # Comprimento da sequência
5
+ sequence_length = 4
6
+ # Comprimento do resultado
7
+ result_length = 2
8
+ # Comprimento do contexto
9
+ context_length = sequence_length + result_length
10
+ # Parâmetros de configuração do modelo GPT-2
11
+ config = AutoConfig.from_pretrained("gpt2",
12
+ vocab_size = vocab_size,
13
+ n_ctx = context_length,
14
+ n_head = 4,
15
+ n_layer = 2)
16
+
17
+ # Carrega o modelo
18
+ modelo = AutoModelForCausalLM.from_config(config)
19
+ # Função para calcular o tamanho do modelo
20
+ def model_size(model):
21
+ return sum(t.numel() for t in modelo.parameters())
22
+ print(f'Tamanho do Modelo: {model_size(modelo)/1000**2:.1f}M parâmetros')
23
+ #Tamanho do Modelo: 15.0M parâmetros
24
+ #Este modelo tem 15 milhões de parâmetros em vez dos 111 milhões de parâmetros da configuração padrão "gpt2".
25
+
26
+ type(modelo)
27
+ transformers.models.gpt2.modeling_gpt2.GPT2LMHeadModel
28
+ # Salva o modelo em disco
29
+ modelo.save_pretrained("modelos/modelo_inicial")
30
+
31
+ # Definindo uma classe chamada NumberTokenizer, que é usada para tokenizar os números
32
+ class DSATokenizer:
33
+
34
+ # Método construtor da classe, que é executado quando um objeto dessa classe é criado
35
+ def __init__(self, numbers_qty = 10):
36
+
37
+ # Lista de tokens possíveis que o tokenizador pode encontrar
38
+ vocab = ['+', '=', '-1', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
39
+
40
+ # Definindo a quantidade de números que o tokenizador pode lidar
41
+ self.numbers_qty = numbers_qty
42
+
43
+ # Definindo o token de preenchimento (padding)
44
+ self.pad_token = '-1'
45
+
46
+ # Criando um dicionário que mapeia cada token para um índice único
47
+ self.encoder = {str(v):i for i,v in enumerate(vocab)}
48
+
49
+ # Criando um dicionário que mapeia cada índice único de volta ao token correspondente
50
+ self.decoder = {i:str(v) for i,v in enumerate(vocab)}
51
+
52
+ # Obtendo o índice do token de preenchimento no encoder
53
+ self.pad_token_id = self.encoder[self.pad_token]
54
+
55
+ # Método para decodificar uma lista de IDs de token de volta para uma string
56
+ def decode(self, token_ids):
57
+ return ' '.join(self.decoder[t] for t in token_ids)
58
+
59
+ # Método que é chamado quando o objeto da classe é invocado como uma função
60
+ def __call__(self, text):
61
+ # Dividindo o texto em tokens individuais e retornando uma lista dos IDs correspondentes
62
+ return [self.encoder[t] for t in text.split()]
63
+
64
+
65
+ # Cria o objeto do tokenizador
66
+ tokenizer = DSATokenizer(vocab_size)
67
+ # Decoder do tokenizador
68
+ tokenizer.decoder
69
+
70
+ # Testando o tokenizador
71
+ tokenizer("1 + 1 = 2")
72
+
73
+ # Definindo uma classe chamada CriaDataset, que herda da classe Dataset do PyTorch
74
+ class CriaDataset(Dataset):
75
+
76
+ # Método construtor da classe, que é executado quando um objeto dessa classe é criado
77
+ def __init__(self, split, length = 6):
78
+
79
+ # Verificando se a divisão do dataset (split) é 'treino' ou 'teste'
80
+ assert split in {'treino', 'teste'}
81
+ self.split = split
82
+ self.length = length
83
+
84
+ # Definindo o método len que retorna o tamanho do dataset.
85
+ # Nesse caso, o tamanho é fixo e igual a 1 milhão.
86
+ def __len__(self):
87
+ return 1000000
88
+
89
+ # Definindo o método getitem que é usado para obter um item específico do dataset
90
+ def __getitem__(self, idx):
91
+
92
+ # Criando uma lista com todos os números disponíveis que não são tokens de padding e são numéricos
93
+ available_numbers = [int(n) for n in tokenizer.decoder.values() if n != tokenizer.pad_token and str(n).isnumeric()]
94
+
95
+ # Selecionando aleatoriamente números da lista de números disponíveis para criar uma entrada (input)
96
+ inp = torch.tensor(np.random.choice(available_numbers, size = result_length))
97
+
98
+ # Calculando a soma dos números selecionados e criando um tensor
99
+ sol = torch.tensor([int(i) for i in str(inp.sum().item())])
100
+
101
+ # Preenchendo o tensor com zeros para que tenha o tamanho desejado
102
+ sol = torch.nn.functional.pad(sol, (1 if sol.size()[0] == 1 else 0,0), 'constant', 0)
103
+
104
+ # Concatenando a entrada e a solução em um tensor
105
+ cat = torch.cat((inp, sol), dim = 0)
106
+
107
+ # Criando os tensores de entrada e alvo para o treinamento do modelo
108
+ x = cat[:-1].clone()
109
+ y = cat[1:].clone()
110
+
111
+ # Definindo o primeiro elemento do tensor alvo como o token de padding
112
+ y[:1] = int(tokenizer.pad_token)
113
+
114
+ # Transformando os tensores x e y em strings
115
+ x = str(x[0].item()) + ' + ' + str(x[1].item()) + ' = ' + str(x[2].item())
116
+ y = '-1 ' + str(y[0].item()) + ' -1 ' + str(y[1].item()) + ' ' + str(y[2].item())
117
+
118
+ # Tokenizando as strings de entrada e alvo
119
+ tokenized_input = tokenizer(x)
120
+ tokenized_output = tokenizer(y)
121
+
122
+ # Retornando os tensores de entrada e alvo como itens do dataset
123
+ return torch.tensor(tokenized_input), torch.tensor(tokenized_output)
124
+
125
+ dataset_treino = CriaDataset('treino', length = sequence_length)
126
+ dataset_teste = CriaDataset('teste', length = sequence_length)
127
+ x, y = dataset_treino[0]
128
+ x
129
+
130
+ y
131
+
132
+ print(tokenizer.decode(x.numpy()))
133
+ print(tokenizer.decode(y.numpy()))
134
+
135
+ num_epochs = 2
136
+ batch_size = 100
137
+ optimizer = torch.optim.Adam(modelo.parameters())
138
+ dados = torch.utils.data.DataLoader(dataset_treino, shuffle = True, batch_size = batch_size)
139
+
140
+ import accelerate
141
+ from accelerate import Accelerator
142
+ accelerator = Accelerator()
143
+
144
+ modelo, optimizer, dados = accelerator.prepare(modelo, optimizer, dados)
145
+
146
+ modelo.train()
147
+
148
+
149
+ # Iniciando o loop para as épocas de treinamento
150
+ for epoch in range(num_epochs):
151
+
152
+ # Iterando por cada batch (conjunto) de dados de entrada e alvos no dataset de treinamento
153
+ for source, targets in dados:
154
+
155
+ # Resetando os gradientes acumulados no otimizador
156
+ optimizer.zero_grad()
157
+
158
+ # Calculando a perda (loss) através da entropia cruzada entre as previsões do modelo e os alvos verdadeiros.
159
+ # Os tensores são "achatados" para que possam ser passados para a função de entropia cruzada.
160
+ # O índice do token de preenchimento (pad_token) é ignorado no cálculo da perda.
161
+ loss = F.cross_entropy(modelo(source).logits.flatten(end_dim = 1),
162
+ targets.flatten(end_dim = 1),
163
+ ignore_index = tokenizer.pad_token_id)
164
+
165
+ # Calculando os gradientes da perda em relação aos parâmetros do modelo
166
+ accelerator.backward(loss)
167
+
168
+ # Atualizando os parâmetros do modelo utilizando os gradientes calculados
169
+ optimizer.step()
170
+
171
+ # Recalculando a perda após a etapa de otimização.
172
+ loss = F.cross_entropy(modelo(source).logits.flatten(end_dim = 1),
173
+ targets.flatten(end_dim = 1),
174
+ ignore_index = tokenizer.pad_token_id)
175
+
176
+ # Imprimindo a época atual e a perda após cada época de treinamento
177
+ print(f'Epoch: {epoch+1}/{num_epochs} --- Erro: {loss.item()}')
178
+
179
+ # Definindo a função gera_solution com três parâmetros: input, solution_length e model
180
+ def faz_previsao(entrada, solution_length = 6, model = modelo):
181
+
182
+ # Colocando o modelo em modo de avaliação.
183
+ model.eval()
184
+
185
+ # Convertendo a entrada (string) em tensor utilizando o tokenizer.
186
+ # O tensor é uma estrutura de dados que o modelo de aprendizado de máquina pode processar.
187
+ entrada = torch.tensor(tokenizer(entrada))
188
+
189
+ # Enviando o tensor de entrada para o dispositivo de cálculo disponível (CPU ou GPU)
190
+ entrada = entrada.to(accelerator.device)
191
+
192
+ # Iniciando uma lista vazia para armazenar a solução
193
+ solution = []
194
+
195
+ # Loop que gera a solução de comprimento solution_length
196
+ for i in range(solution_length):
197
+
198
+ # Alimentando a entrada atual ao modelo e obtendo a saída
199
+ saida = model(entrada)
200
+
201
+ # Pegando o índice do maior valor no último conjunto de logits (log-odds) da saída,
202
+ # que é a previsão do modelo para o próximo token
203
+ predicted = saida.logits[-1].argmax()
204
+
205
+ # Concatenando a previsão atual com a entrada atual.
206
+ # Isso servirá como a nova entrada para a próxima iteração.
207
+ entrada = torch.cat((entrada, predicted.unsqueeze(0)), dim = 0)
208
+
209
+ # Adicionando a previsão atual à lista de soluções e convertendo o tensor em um número Python padrão
210
+ solution.append(predicted.cpu().item())
211
+
212
+ # Decodificando a lista de soluções para obter a string de saída e retornando-a
213
+ return tokenizer.decode(solution)
214
+
215
+ # Definindo a função avalia_modelo com dois parâmetros: num_samples e log
216
+ def avalia_modelo(num_samples = 1000, log = False):
217
+
218
+ # Iniciando um contador para as previsões corretas
219
+ correct = 0
220
+
221
+ # Loop que itera num_samples vezes
222
+ for i in range(num_samples):
223
+
224
+ # Obtendo a entrada e o alvo (resposta correta) do i-ésimo exemplo do conjunto de teste
225
+ entrada, target = dataset_teste[i]
226
+
227
+ # Convertendo os tensores de entrada e alvo em arrays numpy para processamento posterior
228
+ entrada = entrada.cpu().numpy()
229
+ target = target.cpu().numpy()
230
+
231
+ # Decodificando a entrada e o alvo utilizando o tokenizer
232
+ entrada = tokenizer.decode(entrada[:sequence_length])
233
+ target = tokenizer.decode(target[sequence_length-1:])
234
+
235
+ # Gerando a previsão utilizando a função faz_previsao
236
+ predicted = faz_previsao(entrada, solution_length = result_length, model = modelo)
237
+
238
+ # Se a previsão for igual ao alvo, incrementa o contador de previsões corretas
239
+ if target == predicted:
240
+ correct += 1
241
+ # Se log for True, imprime detalhes do exemplo e a previsão correta
242
+ if log:
243
+ print(f'Acerto do Modelo: Input: {entrada} Target: {target} Previsão: {predicted}')
244
+ else:
245
+ # Se log for True, imprime detalhes do exemplo e a previsão errada
246
+ if log:
247
+ print(f'Erro do Modelo: Input: {entrada} Target: {target} Previsão: {predicted}')
248
+
249
+ # Ao final do loop, calcula a acurácia (número de previsões corretas dividido pelo número total de exemplos)
250
+ print(f'Acurácia: {correct/num_samples}')
251
+
252
+ # Executa a função
253
+ avalia_modelo(num_samples = 10, log = True)
254
+
255
+ # Executa a função
256
+ avalia_modelo(num_samples = 1000, log = False)
257
+
258
+ type(modelo)
259
+
260
+ modelo.save_pretrained("modelos/modelo_final")
261
+
262
+
263
+
264
  modelo_llm = AutoModelForCausalLM.from_pretrained("modelos/modelo_final")
265
 
266
  # Definindo uma classe chamada NumberTokenizer, que é usada para tokenizar os números