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