Repository navigation
Expand file tree
/
Copy pathtrain_roberta.py
More file actions
298 lines (260 loc) · 11.7 KB
/
Copy pathtrain_roberta.py
File metadata and controls
298 lines (260 loc) · 11.7 KB
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
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
import os
import logging
import math
from dataclasses import dataclass, field
from typing import Optional
from datasets import load_dataset
from transformers import (
AutoTokenizer,
DataCollatorForLanguageModeling,
HfArgumentParser,
Trainer,
TrainingArguments,
TrainerCallback,
set_seed,
)
import torch
import wandb
from transformers.trainer_utils import get_last_checkpoint
from sparse_roberta import get_custom_model # Import the custom model
from alpha_scheduler import AlphaScheduler # Import the alpha scheduler
import evaluate
# Set up logging
logging.basicConfig(
format="%(asctime)s - %(levelname)s - %(name)s - %(message)s",
datefmt="%m/%d/%Y %H:%M:%S",
level=logging.INFO
)
logger = logging.getLogger(__name__)
@dataclass
class ModelArguments:
model_name_or_path: Optional[str] = field(
default="roberta-base",
metadata={"help": "The model checkpoint for weights initialization."}
)
use_triton_entmax: bool = field(
default=False,
metadata={"help": "Flag to use Triton's entmax implementation."}
)
train_from_scratch: bool = field(
default=False,
metadata={"help": "Flag to train from scratch."}
)
@dataclass
class DataTrainingArguments:
dataset_name: Optional[str] = field(
default="allenai/c4", metadata={"help": "The name of the dataset to use (via the datasets library)."}
)
dataset_subset: Optional[str] = field(
default="en", metadata={"help": "The subset of the dataset to use (e.g., 'en' for English)."}
)
mlm_probability: float = field(
default=0.15, metadata={"help": "Ratio of tokens to mask for masked language modeling loss"}
)
streaming: bool = field(default=True, metadata={"help": "Enable streaming mode for large datasets."})
max_eval_samples: Optional[int] = field(
default=50000, metadata={"help": "Maximum number of examples to use for evaluation to save memory."}
)
@dataclass
class CustomTrainingArguments(TrainingArguments):
max_steps: int = field(
default=100000,
metadata={"help": "Set the maximum number of training steps."}
)
resume_training: bool = field(default=False, metadata={"help": "Flag to resume training from the last checkpoint."})
report_to: str = field(
default="wandb", # Ensure WandB is used for logging
metadata={"help": "The integrations to report the logs to."}
)
@dataclass
class AlphaSchedulerArguments:
initial_alpha: float = field(default=1.0000001, metadata={"help": "Starting alpha value"})
final_alpha: float = field(default=2.0, metadata={"help": "Final alpha value"})
max_alpha_steps: int = field(default=10000, metadata={"help": "Total steps for alpha annealing"})
strategy: str = field(
default="linear",
metadata={"help": "Strategy for alpha scheduling", "choices": ["linear", "exponential", "cosine", "polynomial", "stepwise", "sigmoid"]}
)
power: int = field(default=2, metadata={"help": "Power for polynomial annealing"})
step_size: int = field(default=1000, metadata={"help": "Step size for stepwise annealing"})
increment: float = field(default=0.1, metadata={"help": "Increment for stepwise annealing"})
k: float = field(default=0.1, metadata={"help": "Steepness factor for sigmoid annealing"})
def main():
# Argument parsing
parser = HfArgumentParser((ModelArguments, DataTrainingArguments, CustomTrainingArguments, AlphaSchedulerArguments))
model_args, data_args, training_args, alpha_args = parser.parse_args_into_dataclasses()
# Print arguments
logger.info(f"Model arguments: {model_args}")
logger.info(f"Data arguments: {data_args}")
logger.info(f"Training arguments: {training_args}")
logger.info(f"Alpha scheduler arguments: {alpha_args}")
# Initialize WandB for logging
wandb.init(project="sparse_pretraining", config={**vars(model_args), **vars(data_args), **vars(training_args), **vars(alpha_args)})
logger.info("Initialized WandB for logging")
# Set seed for reproducibility
set_seed(training_args.seed)
# Load dataset splits
train_dataset = load_dataset(
data_args.dataset_name,
data_args.dataset_subset,
split='train',
streaming=data_args.streaming,
)
validation_dataset = load_dataset(
#"cerebras/SlimPajama-627B",
"allenai/c4",
"en",
split='validation',
streaming=data_args.streaming,
)
# Limit evaluation samples if max_eval_samples is set
if data_args.max_eval_samples is not None:
validation_dataset = validation_dataset.take(data_args.max_eval_samples)
# Load tokenizer and model
tokenizer = AutoTokenizer.from_pretrained(model_args.model_name_or_path)
model = get_custom_model(
model_args.model_name_or_path,
initial_alpha=alpha_args.initial_alpha,
use_triton_entmax=model_args.use_triton_entmax,
from_scratch=model_args.train_from_scratch,
)
# Define the tokenization function with filtering for empty texts
def tokenize_function(examples):
# Remove any empty or None texts before tokenizing
examples["text"] = [text for text in examples["text"] if text is not None and text.strip() != ""]
# return tokenizer(examples["text"], truncation=True, padding=False, return_special_tokens_mask=True)
return tokenizer(
examples["text"],
truncation=True, # Truncate sequences exceeding `max_length`
padding="max_length", # Pad sequences to `max_length`
max_length=512, # Ensure consistent sequence length
return_attention_mask=True
)
# Apply tokenization with filter for empty samples
train_dataset = train_dataset.map(tokenize_function, batched=True, remove_columns=["text"])
validation_dataset = validation_dataset.map(tokenize_function, batched=True, remove_columns=["text"])
# Initialize alpha scheduler
alpha_scheduler = AlphaScheduler(
initial_alpha=alpha_args.initial_alpha,
final_alpha=alpha_args.final_alpha,
max_steps=alpha_args.max_alpha_steps,
strategy=alpha_args.strategy,
power=alpha_args.power,
step_size=alpha_args.step_size,
increment=alpha_args.increment,
k=alpha_args.k,
)
# Define data collator
data_collator = DataCollatorForLanguageModeling(
tokenizer=tokenizer,
mlm_probability=data_args.mlm_probability,
# pad_to_multiple_of=8
)
# Define metric for evaluation
metric = evaluate.load("accuracy")
def compute_metrics(eval_preds):
logits, labels = eval_preds
predictions = logits.argmax(dim=-1)
labels = labels.reshape(-1)
predictions = predictions.reshape(-1)
mask = labels != -100
return metric.compute(predictions=predictions[mask], references=labels[mask])
# Custom callback to calculate and log perplexity
class PerplexityLoggingCallback(TrainerCallback):
def on_evaluate(self, args, state, control, metrics, **kwargs):
if "eval_loss" in metrics:
perplexity = math.exp(metrics["eval_loss"])
metrics["perplexity"] = perplexity
wandb.log({"perplexity": perplexity, "global_step": state.global_step})
# Custom callback for logging token count
class TokenCountingCallback(TrainerCallback):
def __init__(self):
super().__init__()
self.total_tokens = 0
def on_train_batch_end(self, args, state, control, **kwargs):
# Retrieve the trainer and batch from kwargs
batch = kwargs.get("inputs", None)
if batch and "attention_mask" in batch:
# Use the attention mask to count non-padding tokens
non_padding_tokens = batch["attention_mask"].sum().item()
self.total_tokens += non_padding_tokens
elif batch and "input_ids" in batch:
# Use the input IDs to count non-padding tokens
non_padding_tokens = (batch["input_ids"] != tokenizer.pad_token_id).sum().item()
self.total_tokens += non_padding_tokens
# Log the total tokens so far to WandB
wandb.log(
{
"total_tokens": self.total_tokens,
"global_step": state.global_step,
}
)
def on_train_end(self, args, state, control, **kwargs):
# Log the final token count
logger.info(f"Total tokens trained on: {self.total_tokens}")
wandb.log({"total_tokens": self.total_tokens})
class LossLoggingCallback(TrainerCallback):
def on_log(self, args, state, control, logs=None, **kwargs):
if logs is not None and "loss" in logs:
# Log the loss to WandB
wandb.log({"training_loss": logs["loss"], "global_step": state.global_step})
# Initialize Trainer with WandB callback for logging and validation dataset
trainer = Trainer(
model=model,
args=training_args,
train_dataset=train_dataset,
eval_dataset=validation_dataset,
data_collator=data_collator,
compute_metrics=compute_metrics,
callbacks=[
PerplexityLoggingCallback(),
TokenCountingCallback(),
LossLoggingCallback(),
],
)
# Define a custom callback for logging sparsity and alpha
class SparsityLoggingCallback(TrainerCallback):
def __init__(self, model, alpha_scheduler):
super().__init__()
self.model = model
self.alpha_scheduler = alpha_scheduler
self.n_tokens = 0
def on_step_end(self, args, state, control, **kwargs):
# Gather and log sparsity per head
sparsity_per_head_item = {}
for layer_index, layer in enumerate(trainer.model.roberta.encoder.layer):
if layer.attention.self.sparsity_per_head is not None:
sph = layer.attention.self.sparsity_per_head.mean(0) # average over all batches
for head in range(sph.size(0)): # iterate over all heads
sparsity_per_head_item[f"sparsity_l{layer_index:02d}_h{head:02d}"] = sph[head].item()
sparsity_per_head_item[f"sparsity_l{layer_index:02d}_avg"] = sph.mean().item()
# Update total token count
self.n_tokens += trainer.model.roberta.encoder.layer[0].attention.self.n_tokens.item()
# Update alpha and log it
old_alpha = self.alpha_scheduler.alpha
new_alpha = self.alpha_scheduler.step()
for layer in trainer.model.roberta.encoder.layer:
layer.attention.self.alpha = new_alpha
log_dict = {
"alpha": old_alpha,
"global_step": state.global_step,
"n_tokens_so_far": self.n_tokens,
}
wandb.log({**log_dict, **sparsity_per_head_item})
trainer.add_callback(SparsityLoggingCallback(model, alpha_scheduler))
# Detect the last checkpoint
checkpoint = None
if os.path.isdir(training_args.output_dir) and training_args.resume_training:
# Look for the last checkpoint if the directory exists
last_checkpoint = get_last_checkpoint(training_args.output_dir)
if last_checkpoint is not None:
logger.info(f"Resuming from checkpoint: {last_checkpoint}")
checkpoint = last_checkpoint
else:
logger.info(f"No previous checkpoint found in {training_args.output_dir}, starting fresh.")
# Train and save
trainer.train(resume_from_checkpoint=checkpoint)
trainer.save_model()
wandb.finish() # Finalize WandB session
if __name__ == "__main__":
main()