File size: 10,567 Bytes
4ca4e4c | 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 | import json
import argparse
import os
import numpy as np
from model_utils import get_model
from data_utils import get_train_dataset, get_tokenizer
from train_utils import train, save_model, make_dir, get_ident_name, get_data_ident_name, get_task_dir_name, get_lrs, add_lr
from test_utils import evaluation
from generate import force_args, task_choices
def count_parameters(model):
return sum(p.numel() for p in model.parameters() if p.requires_grad)
def parse_args():
parser = argparse.ArgumentParser()
parser.add_argument('--run_number', default=-1, type=int, help="The current run number. Will not save if the run has already been saved")
# Task parameters
parser.add_argument('--train_task', choices=task_choices,
required=True, help="Task to train the model")
parser.add_argument('--eval_task', choices=task_choices,
required=True, help="tasks to evaluate the model")
parser.add_argument('--num_vocab', default=26, type=int, help="vocabulary size in the strings. maximum is 26.")
parser.add_argument('--num_numbers', default=5, type=int, help="vocabulary (number) size in the strings. maximum is 9.")
parser.add_argument('--min_number', default=0, type=int, help="The smallest number token")
parser.add_argument('--ood_eval', default=False, type=bool, help="If true, perform out-of-distribution evaluation.")
parser.add_argument('--p', default=0.2, type=float, help="proportion, depends on task")
parser.add_argument('--eval_p', default=None, type=float, help="proportion, depends on task")
parser.add_argument('--mixed', default=False, type=bool, help="If true, use mixed distribution when generating data.")
# Model
parser.add_argument('--nope', default=False, type=bool, help="If true, use the no positional encoding version of the hybrid model")
parser.add_argument('--model', type=str, choices=['hybrid', 'TF', 'SSM'], default=None, help='The model architecture. Cannot specify layers.')
parser.add_argument('--num_layers', type=int, default=None, help="Number of layers in the model. Cannot specify layers.")
parser.add_argument('--layer1', type=str, choices=['TF', 'SSM'], default=None, help='The first layer of the trained model. Cannot specify a model.')
parser.add_argument('--layer2', type=str, choices=['TF', 'SSM'], default=None, help='The second layer of the trained model. Cannot specify a model.')
parser.add_argument('--layer3', type=str, choices=['TF', 'SSM'], default=None, help='The (optional) third layer of the trained model. Cannot specify a model.')
parser.add_argument('--hidden_size', default=8, type=int, help="Hidden size of the models")
parser.add_argument('--heads', default=1, type=int, help="Number of heads in the transformer models.")
parser.add_argument('--num_masked_heads', default=1, type=int, help='''Only when model = ''T_hard_alibi''.
Number of heads where we apply hard alibi. The remaining heads are set to nope.''')
parser.add_argument('--state_dim', default=1, type=int, help='''Only when model = ''mamba'' or ''hybrid''.
Sets the state dimension of the model.''')
# Optimization
parser.add_argument('--lr', default=1e-3, type=float, help="choice of learning rate")
parser.add_argument('--auto_lr', default=False, type=bool, help="If true, find the best lr with some training")
parser.add_argument('--force_do_lr', default=False, type=bool, help="If true, learn a new lr")
parser.add_argument('--epochs', default=4, type=int, help="number of epochs")
parser.add_argument('--num_examples', default=1000, type=int, help="number of samples for each epoch")
parser.add_argument('--num_eval_examples', default=100, type=int, help="number of evaluation examples per length")
parser.add_argument('--window', default=20, type=int, help="width of the sliding window attention")
parser.add_argument('--train_batch_size', default=8, type=int, help="training batch size")
parser.add_argument('--eval_batch_size', default=8, type=int, help="evaluation batch size")
parser.add_argument('--eval_num_batches', default=1, type=int, help='''number of batches to use for evaluation.
useful to have a mean + std over results.''')
parser.add_argument('--pack_examples', default=False, type=bool, help='If true, fill context with multiple examples, deliniated')
parser.add_argument('--min_train_length', default=97, type=int, help="minimum length of a training example")
parser.add_argument('--max_train_length', default=98, type=int, help="maximum length of a training example")
parser.add_argument('--min_eval_length', default=97, type=int, help="minimum length of an evaluation example")
parser.add_argument('--max_eval_length', default=98, type=int, help="maximum length of an evaluation example")
parser.add_argument('--gradient_accumulation_steps', default=1, type=int, help="number of gradient accumulation steps")
# Context length
parser.add_argument('--sequence_length', default=100, type=int, help="context length during training")
parser.add_argument('--eval_sequence_length', default=100, type=int, help="context length at evaluation time")
# Saving parameters
parser.add_argument('--save_model', default=False, type=bool, help="If true, save the model after training")
parser.add_argument('--save_results', default=False, type=bool, help="If true, save the results after training")
parser.add_argument('--run_anyways', default=False, type=bool, help="If true, run even if the results have already been saved")
# Visual parameters
parser.add_argument('--print', default=False, type=bool, help="If true, show helpful print statements")
parser.add_argument('--progress_bar', default=False, type=bool, help="If true, show the process of each epoch")
parser.add_argument('--num_log_steps', default=50, type=int, help="number of steps between each log when training")
parser.add_argument("--test_generate", default=False, type=bool, help="If true, test the synthetic tasks generation")
return parser.parse_args()
args = parse_args()
# Check the user is specifying either model or layers
do_layers = args.layer1 and args.layer2
do_model = args.model and args.num_layers
if do_model and (args.layer1 or args.layer2):
assert False, "Cannot specify both model and layers"
if do_layers and (args.model or args.num_layers):
assert False, "Cannot specify both model and layers"
if not do_layers and not do_model:
assert False, "Must specify either model or layers"
# Set the layers based on either the model or the specified layers
if do_model:
if args.model in ['TF', 'SSM']:
args.layers = [args.model] * args.num_layers
elif args.model == 'hybrid':
args.layers = ['SSM', 'TF'] * (args.num_layers // 2)
if args.num_layers % 2 == 1:
args.layers.append('SSM')
else:
if args.layer3 is not None:
args.layers = [args.layer1, args.layer2, args.layer3]
else:
args.layers = [args.layer1, args.layer2]
# Set the eval dataset to be the same as the train dataset if not specified
if args.eval_p is None:
args.eval_p = args.p
# Force task specific arguments
force_args(args)
if not args.auto_lr and args.save_results and args.run_number >= 0 and not args.run_anyways:
result_filename = 'results/' + get_task_dir_name(args) + '/%d.json' % args.run_number
if os.path.exists(result_filename):
exit(0)
args.data_name = get_data_ident_name(args)
if args.print:
print(args)
## Get train dataset & tokenizer
tokenizer = get_tokenizer(args)
train_dataset = get_train_dataset(args, tokenizer)
batch = next(iter(train_dataset))
if args.print:
print("v"*100)
print("EXAMPLE:", batch['input'][0])
# print("STRUNG:", tokenizer.to_string(batch['input_ids'][0]))
print("-"*100)
print("TOKENIZED:", batch['input_ids'][0][batch['mask'][0]==1])
print("^"*100)
if args.test_generate:
i = batch['input'][0].index("#0")
print(batch['input'][0][i])
print(batch['input'][0][i+1])
print(batch['output'][0][-1])
exit(0)
## Find the best LR
if args.auto_lr:
lrs = get_lrs(args)
key = get_data_ident_name(args) + "_" + get_ident_name(args)
if args.print:
print(key)
if key not in lrs.keys() or (args.force_do_lr and args.run_number == 0):
losses = []
for itr in range(2):
for lr in np.geomspace(1e-4, 1e-0, num=9):
model = get_model(args, tokenizer)
args.lr = lr
if args.print:
print("Testing LR:", lr)
# _, final_loss = train(args, model, tokenizer, train_dataset)
_, final_loss = train(args, model, tokenizer, train_dataset, one_epoch=True)
if args.print:
print("Final loss:", final_loss)
losses.append((final_loss, lr))
losses.sort()
best_lr = losses[0][1]
add_lr(args, best_lr)
args.lr = best_lr
else:
args.lr = lrs[key]
if args.save_results and args.run_number >= 0 and not args.run_anyways:
result_filename = 'results/' + get_task_dir_name(args) + '/%d.json' % args.run_number
if os.path.exists(result_filename):
exit(0)
## Get model
model = get_model(args, tokenizer)
if args.print:
print()
print("v"*100)
print(model)
print(f"Number of parameters of the model: {count_parameters(model)}")
print("^"*100)
print()
## train the model
accs, final_loss = train(args, model, tokenizer, train_dataset)
## save model
if args.save_model:
save_model(args, model)
## evaluation of the model
if args.print:
print("###EVALUATION")
model.eval()
str_acc_mean_list, str_acc_std_list, char_accuracy_list = evaluation(args, model, tokenizer)
if args.print:
print(args)
print("DONE")
print("String")
print(str_acc_mean_list)
print("Char")
print(char_accuracy_list)
if args.save_results and args.run_number >= 0:
# assert False, "Decide what we want to actually save"
results = {
"train_accs": accs,
"final_acc": char_accuracy_list,
"final_loss": final_loss,
"params": count_parameters(model),
"args": vars(args)
}
make_dir(args)
save_path = 'results/' + get_task_dir_name(args)
with open(save_path + '/%d.json' % args.run_number, 'w') as f:
json.dump(results, f)
|