PEFT
Safetensors
English
File size: 9,168 Bytes
5e845b4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
# -*- coding: utf-8 -*-
"""Training Llama 3.1.ipynb

Automatically generated by Colab.

Original file is located at
    https://colab.research.google.com/drive/19LthnXISqvXgzE-1S2crf-PtTv3OaRmo

# **TRAINING DEL MODELO**

**Instalación de dependencias**
"""

# Commented out IPython magic to ensure Python compatibility.
# %%capture
# !pip install "unsloth[colab-new] @ git+https://github.com/unslothai/unsloth.git"
# !pip install --no-deps "xformers<0.0.27" "trl<0.9.0" peft accelerate bitsandbytes
# !pip install datasets # Se instalan

from unsloth import FastLanguageModel # Normalmente se utiliza transformers, pero esta es una librería que permite finetunear rápidamente modelos de lenguaje
import torch
max_seq_length = 2048 # Se puede elegir cualquier largo. Esta librería permite autoscaling (escala automáticamente si el dataset cuenta con un máximo mayor)
dtype = None
load_in_4bit = True # Cuantificación 4bit para reducir el uso de memoria

model, tokenizer = FastLanguageModel.from_pretrained(
    model_name = "unsloth/Meta-Llama-3.1-8B-Instruct", # Modelo Llama 3.1 pre-entrenado para la respuesta a instrucciones
    max_seq_length = max_seq_length,
    dtype = dtype,
    load_in_4bit = load_in_4bit,
)

"""**Definición de los Lora Adapters**"""

model = FastLanguageModel.get_peft_model(
    model,
    r = 16,  # Configura el número de parámetros de rango para LoRA. Se recomienda usar valores como 8, 16, 32, 64, 128.
    target_modules = ["q_proj", "k_proj", "v_proj", "o_proj",
                      "gate_proj", "up_proj", "down_proj",],
    lora_alpha = 16,  # Establece el valor de alpha para LoRA. Es un hiperparámetro que controla la intensidad de la adaptación de LoRA.
    lora_dropout = 0,  # Configura la tasa de abandono para LoRA. Se puede usar cualquier valor, pero 0 es la configuración más optimizada.
    bias = "none",     # Determina el tipo de sesgo para LoRA. La opción "none" es la más optimizada y elimina el sesgo en el modelo.
    # [NUEVO] La opción "unsloth" reduce el uso de VRAM en un 30% y permite tamaños de lote hasta 2 veces mayores.
    use_gradient_checkpointing = "unsloth",  # Usa True o "unsloth" para habilitar el registro de puntos de control de gradientes, lo que es útil para contextos muy largos.
    random_state = 3407,  # Establece la semilla para la generación de números aleatorios, asegurando reproducibilidad en el entrenamiento.
    use_rslora = False,  # Indica si se utiliza LoRA con rango estabilizado, que puede mejorar la estabilidad del entrenamiento.
    loftq_config = None, # Configura LoftQ si se requiere. LoftQ es una técnica adicional que puede ser utilizada en el modelo.
)

"""**Preparación del dataset**"""

alpaca_prompt = """Below is an instruction that describes a task, with an input that gives more context. Write a response that appropriately completes the request.

### Instruction:
Below you have a sentence in quotation marks. Provide the syntactic category of each word in the context of the sentence.

### Sentence:
"{}"

### Response:
{}"""

EOS_TOKEN = tokenizer.eos_token # Must add EOS_TOKEN
def formatting_prompts_func(examples):
    length= len(examples["sentence"])
    sentences = examples["sentence"]
    tags = examples["sentence_tagged"]
    texts = []
    for sentence,tag in zip(sentences,tags):
        # Must add EOS_TOKEN, otherwise your generation will go on forever!
        text = alpaca_prompt.format(sentence,tag) + EOS_TOKEN
        texts.append(text)
    return { "text" : texts, }
pass

from datasets import load_dataset
dataset = load_dataset("manupinasco/syntax_analysis")
dataset_train = dataset["train"].map(formatting_prompts_func, batched = True,)
dataset_test = dataset["test"]

"""**Prueba pre-entrenamiento**"""

FastLanguageModel.for_inference(model) # Enable native 2x faster inference
for sentence in dataset_test["sentence"][:5]:
  inputs = tokenizer(
  [
      alpaca_prompt.format(
          "Below you have a sentence in quotation marks. Provide the syntactic category of each word in the context of the sentence.", # instruction
          f' "{sentence}" ', # input
          "", # output - leave this blank for generation!
      )
  ], return_tensors = "pt").to("cuda")

  outputs = model.generate(**inputs, max_new_tokens = 64, use_cache = True)
  response = str(tokenizer.batch_decode(outputs))
  response=response.split("Response:")[1].replace("']", "").replace("\\n", "").replace("<|eot_id|>", "").lstrip()
  print("INPUT: "+sentence+"/// RESPONSE: "+response)

"""**Testeo pre-entrenamiento**"""

total = len(dataset_train["sentence"])
correct = 0
i=0
for sentence in dataset_test["sentence"]:
  inputs = tokenizer(
  [
      alpaca_prompt.format(
          "Below you have a sentence in quotation marks. Provide the syntactic category of each word in the context of the sentence.", # instruction
          f' "{sentence}" ', # input
          "", # output - leave this blank for generation!
      )
  ], return_tensors = "pt").to("cuda")

  outputs = model.generate(**inputs, max_new_tokens = 64, use_cache = True)
  response = str(tokenizer.batch_decode(outputs))
  response=response.split("Response:")[1].replace("']", "").replace("\\n", "").replace("<|eot_id|>", "").lstrip()
  if response.lower()==dataset_test["sentence_tagged"][i].lower():
    correct+=1
  print("RESPONSE: "+response)
  print("CORRECT_RESPONSE: "+dataset_test["sentence_tagged"][i])
  print("CORRECT RESPONSES SO FAR: "+correct)
  print("NUMBER OF SENTENCE: "+i)
  i+=1

print("CORRECT "+correct+" OUT OF "+total+". PERCENTAGE "+(correct/total)*100)

"""**Entrenamiento del modelo**



*   *Epoch*: cantidad de veces que recorre el dataset completo
*   *Batch*: cantidad de subgrupos en los que divide al dataset.
*   Entrenamiento común: cada vez que se recorre un batch, se updatean los weights.
*   Entrenamiento por **gradient accumulation**: para casos donde se cuente con poca memoria. Sirve para ir acumulando el gradiente de las distintas partes del batch de forma tal de no computar el gradiente recién al finalizarlo.

*   En el caso de gradient accumulation, el batch size es = batch size per device x gradient accumulation steps.

*   *Batch size*: partes en las que realmente dividí al conjunto de datos.

*   *Batch size per device*: partes en las que dividí al conjunto de datos para, al terminar de recorrer cada una de estas partes, hacer un update de los weights.

*   *Gradient accumulation steps*: veces que, por cada batch size per device, acumulé las gradientes previo al update de los weights.










"""

from trl import SFTTrainer
from transformers import TrainingArguments
from unsloth import is_bfloat16_supported

trainer = SFTTrainer(
    model = model,
    tokenizer = tokenizer,
    train_dataset = dataset_train,
    dataset_text_field = "text",
    max_seq_length = max_seq_length,
    dataset_num_proc = 2,
    packing = False, # Puede hacer el entrenamiento 5x más rápido para oraciones breves.
    args = TrainingArguments(
        per_device_train_batch_size = 2,
        gradient_accumulation_steps = 4,
        warmup_steps = 5,
        # num_train_epochs = 1, # Si se setea a 1 hace una corrida completa por todo el dataset.
        max_steps = 60,
        learning_rate = 2e-4,
        fp16 = not is_bfloat16_supported(),
        bf16 = is_bfloat16_supported(),
        logging_steps = 1,
        optim = "adamw_8bit",
        weight_decay = 0.01,
        lr_scheduler_type = "linear",
        seed = 3407,
        output_dir = "outputs",
    ),
)

trainer_stats = trainer.train()

"""# **TESTEO DEL MODELO**

**Prueba post-entrenamiento**
"""

FastLanguageModel.for_inference(model) # Enable native 2x faster inference
for sentence in dataset_test["sentence"][:5]:
  inputs = tokenizer(
  [
      alpaca_prompt.format(
          f' "{sentence}" ', # input
          "", # output - leave this blank for generation!
      )
  ], return_tensors = "pt").to("cuda")

  outputs = model.generate(**inputs, max_new_tokens = 64, use_cache = True)
  response = str(tokenizer.batch_decode(outputs))
  response=response.split("Response:")[1].replace("']", "").replace("\\n", "").replace("<|eot_id|>", "").lstrip()
  print("INPUT: "+sentence+"/// RESPONSE: "+response)

"""**Testeo post-entrenamiento**"""

total = len(dataset_train["sentence"])
correct = 0
i=0
for sentence in dataset_test["sentence"]:
  inputs = tokenizer(
  [
      alpaca_prompt.format(
          f' "{sentence}" ', # input
          "", # output - leave this blank for generation!
      )
  ], return_tensors = "pt").to("cuda")

  outputs = model.generate(**inputs, max_new_tokens = 64, use_cache = True)
  response = str(tokenizer.batch_decode(outputs))
  response=response.split("Response:")[1].replace("']", "").replace("\\n", "").replace("<|eot_id|>", "").lstrip()
  if response.lower()==dataset_test["sentence_tagged"][i].lower():
    correct+=1
  print("RESPONSE: "+response)
  print("CORRECT_RESPONSE: "+dataset_test["sentence_tagged"][i])
  print("CORRECT RESPONSES SO FAR: "+correct)
  print("NUMBER OF SENTENCE: "+i)
  i+=1

print("CORRECT "+correct+" OUT OF "+total+". PERCENTAGE "+(correct/total)*100)