camilaaeromoca commited on
Commit
71892ba
·
verified ·
1 Parent(s): b032c3a

Create modelo.py

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