From 29ad1cc46968c8b9861c10acaaff50d6708ecfcc Mon Sep 17 00:00:00 2001 From: sahil Date: Tue, 2 May 2023 10:24:28 +0200 Subject: [PATCH 1/4] flan t5 code train on boolq data --- rationales/data_helper.py | 151 +++++++++++++++++++ rationales/main.py | 296 ++++++++++++++++++++++++++++++++++++++ rationales/run.sh | 50 +++++++ rationales/utils.py | 33 +++++ 4 files changed, 530 insertions(+) create mode 100644 rationales/data_helper.py create mode 100644 rationales/main.py create mode 100644 rationales/run.sh create mode 100644 rationales/utils.py diff --git a/rationales/data_helper.py b/rationales/data_helper.py new file mode 100644 index 00000000..46eeb26a --- /dev/null +++ b/rationales/data_helper.py @@ -0,0 +1,151 @@ +import json +import os +from tqdm import tqdm +from dataclasses import dataclass +from typing import List, Optional +import random + +import torch +from torch.utils.data import Dataset, TensorDataset +from sklearn.model_selection import train_test_split +import pandas as pd + +@dataclass(frozen=True) +class InputExample: + + prompt: str + explanation: str + + +class TrainingDataset(Dataset): + features: List[InputExample] + + def __init__(self, features): + self.features = features + + def __len__(self): + return len(self.features) + + def __getitem__(self, i) -> InputExample: + return self.features[i] + + +def load_raw_dataset(split, args): + #data_path = os.path.join('./data', args.dataset, '{}.jsonl'.format(split)) + data = pd.read_csv("cache/boolq/GPT-3.5_rationales_BoolQ_val_400.csv") + train, test = train_test_split(data, test_size=0.3, random_state=42, shuffle=True) + + dataset = [] + + + for example_id, line in tqdm(enumerate(train), desc='processing {}'.format(split)): + example = line + + dataset.append( + InputExample( + prompt=example["prompt"], + explanation=example["completion"], + + ) + ) + + for example in dataset[:2]: + print("*** Example ***") + print(example) + + return TrainingDataset(dataset) + + +def get_label_tensor(raw_label, tokenizer, args): + label_ids = tokenizer.encode(raw_label, add_special_tokens=False) + label_ids = label_ids[:args.max_dec_length] + label_ids += [-100] * (args.max_dec_length - len(label_ids)) + return label_ids + + +def format_input(question, choices=None): + input_seq = "Question: {}".format(question.strip()) + # input_seq += " Answer: {}.".format(choice.strip()) + if choices is not None: + input_seq += " Answer Choices:" + for choice_id, choice in enumerate(choices): + input_seq += " ({}) {}".format(chr(ord('a') + choice_id), choice) + input_seq += '.' + return input_seq + + +def format_explanation(explanation): + input_seq = ' Explanation: ' + explanation.strip() + return input_seq + + +class Data_Collator_for_Training(object): + def __init__(self, tokenizer, args, mask_inference=False, dropout_context=0): + self.tokenizer = tokenizer + self.mask_inference = mask_inference + self.dropout_context = dropout_context + self.args = args + + def __call__(self, examples): + + encoder_input_tensor = [] + encoder_attention_mask_tensor = [] + decoder_label_tensor = [] + label_tensor = [] + smoothing_tensor = [] + + for example_idx, example in enumerate(examples): + input_ids = [] + attention_mask = [] + + context = example.prompt + input_ids = self.tokenizer.encode(context.strip(), add_special_tokens=False) + explanation = example.explanation + added_ids = self.tokenizer.encode(explanation, + add_special_tokens=False) + encoder_input_tensor.append(input_ids) + + encoder_attention_mask_tensor.append([1]*len(input_ids)) + decoder_label_tensor.append(added_ids) + + return tuple(torch.tensor(t) for t in + [encoder_input_tensor, encoder_attention_mask_tensor, decoder_label_tensor]) + + +def get_tensor_dataset(split, tokenizer, args): + #data_path = os.path.join('./data', args.dataset, '{}.jsonl'.format(split)) + data = pd.read_csv("cache/boolq/GPT-3.5_rationales_BoolQ_val_400.csv") + train, test = train_test_split(data, test_size=0.3,random_state=42, shuffle=True) + dev,test = train_test_split(test, test_size=0.2, random_state=42, shuffle=True) + split_args = dev if split == 'dev' else test + encoder_input_tensor = [] + encoder_attention_mask_tensor = [] + decoder_label_tensor = [] + task_label_tensor = [] + for example_idx, example in tqdm(enumerate(split_args), desc='processing {}'.format(data_path)): + input_ids = [] + attention_mask = [] + + context = example.prompt + input_ids = self.tokenizer.encode(context.strip(), add_special_tokens=False) + explanation = example.completion + added_ids = self.tokenizer.encode(explanation, + add_special_tokens=False) + encoder_input_tensor.append(input_ids) + + encoder_attention_mask_tensor.append([1] * len(input_ids)) + decoder_label_tensor.append(added_ids) + encoder_input_tensor = torch.tensor(encoder_input_tensor, dtype=torch.long) + encoder_attention_mask_tensor = torch.tensor(encoder_attention_mask_tensor, dtype=torch.long) + decoder_label_tensor = torch.tensor(decoder_label_tensor, dtype=torch.long) + for f1, f2, f3 in zip(encoder_input_tensor[:2], encoder_attention_mask_tensor[:2], decoder_label_tensor[:2]): + print("*** Example ***") + if len(f1.shape) == 3: + f1 = f1[0] + for ids in f1: + print("encoder input: %s" % tokenizer.decode(ids)) + # print("encoder attention mask: %s" % f2) + for ids in f3: + print("decoder output: %s" % tokenizer.decode([tid for tid in ids if not tid == -100])) + + return TensorDataset(encoder_input_tensor, encoder_attention_mask_tensor, decoder_label_tensor) \ No newline at end of file diff --git a/rationales/main.py b/rationales/main.py new file mode 100644 index 00000000..882714c1 --- /dev/null +++ b/rationales/main.py @@ -0,0 +1,296 @@ +import json +import os +import argparse +from tqdm import tqdm, trange +import numpy as np +import math + +import torch +import torch.nn.functional as F +from torch.utils.data import DataLoader, RandomSampler, SequentialSampler + +from transformers import set_seed, AutoConfig, AutoTokenizer, AutoModelForSeq2SeqLM, get_linear_schedule_with_warmup, \ + get_constant_schedule_with_warmup +from transformers.optimization import Adafactor + +from data_helper import load_raw_dataset, Data_Collator_for_Training, get_tensor_dataset +from utils import get_logger + + +class Text2TextForMultiChoice(torch.nn.Module): + def __init__(self, model_name): + super().__init__() + + self.model = AutoModelForSeq2SeqLM.from_pretrained(model_name, cache_dir='../cache/') + + def forward(self, input_ids, attention_mask, target_ids, labels=None): + num_choices = input_ids.shape[1] + input_ids = input_ids.view(-1, input_ids.size(-1)) + attention_mask = attention_mask.view(-1, attention_mask.size(-1)) + target_ids = target_ids.view(-1, target_ids.size(-1)) + + outputs = self.model( + input_ids=input_ids, + attention_mask=attention_mask + ) + + log_probs = - F.cross_entropy(outputs.logits.view(-1, outputs.logits.size(-1)), target_ids.view(-1), + ignore_index=-100, reduction='none') + + return log_probs + + +def evaluate(dataset, model, args): + data_sampler = SequentialSampler(dataset) + dataloader = DataLoader(dataset, sampler=data_sampler, batch_size=args.eval_batch_size) + model.eval() + epoch_iterator = tqdm(dataloader, desc="Eval Iteration") + + accuracy = 0. + output_predictions = [] + output_scores = [] + for step, batch in enumerate(epoch_iterator): + + input_ids, attention_mask, target_ids, labels = tuple(t.to(args.device) for t in batch) + with torch.no_grad(): + outputs = model( + input_ids=input_ids, + attention_mask=attention_mask, + target_ids=target_ids, + ) + logits = outputs + + _, predictions = logits.max(dim=1) + probs = F.softmax(logits, dim=-1) + answer_probs = torch.gather(probs, 1, labels.unsqueeze(1)).squeeze(1) + + accuracy += predictions.eq(labels).sum().item() + output_predictions.extend(predictions.tolist()) + output_scores.extend(answer_probs.tolist()) + + if args.debug and step > 10: + break + + accuracy /= len(dataset) + return accuracy * 100., output_predictions, output_scores + + +def main(args, seed): + # ----------------------------------------------------- # + # prepare logger + log_path = os.path.join(args.save_dir, 'train_seed{}.log'.format(seed)) + logger = get_logger("model", log_path) + logger.info('args: {}'.format(args)) + + # ----------------------------------------------------- # + # model + tokenizer = AutoTokenizer.from_pretrained(args.model_name, cache_dir='../cache/') + model = Text2TextForMultiChoice(args.model_name) + model.to(args.device) + + # ----------------------------------------------------- # + # data + train_dataloader_dict = {} + + # all input + trainset = load_raw_dataset('train', args) + train_sampler1 = RandomSampler(trainset) + data_collator_for_training1 = Data_Collator_for_Training(tokenizer, args, mask_inference=False, + dropout_context=args.dropout_context) + train_dataloader1 = DataLoader(trainset, collate_fn=data_collator_for_training1, sampler=train_sampler1, + batch_size=args.train_batch_size) + train_dataloader_dict["all"] = train_dataloader1 + + + + devset = get_tensor_dataset(args.dev_split, tokenizer, args) + + # ----------------------------------------------------- # + # optimization + no_decay = ["bias", "LayerNorm.weight"] + optimizer_grouped_parameters = [ + { + "params": [p for n, p in model.named_parameters() if + p.requires_grad and not any(nd in n for nd in no_decay)], + "weight_decay": args.weight_decay, + }, + { + "params": [p for n, p in model.named_parameters() if p.requires_grad and any(nd in n for nd in no_decay)], + "weight_decay": 0.0 + }, + ] + optimizer = Adafactor( + optimizer_grouped_parameters, + lr=args.learning_rate, + weight_decay=0.0, + relative_step=False, + scale_parameter=False, + warmup_init=False + ) + + num_update_steps_per_epoch = len(train_dataloader_dict["all"]) + t_total = num_update_steps_per_epoch // args.grad_step * args.num_epoch + warmup_steps = int(t_total * args.warmup_ratio) + scheduler = get_constant_schedule_with_warmup(optimizer, + num_warmup_steps=warmup_steps) # , num_training_steps=t_total) + + # ----------------------------------------------------- # + # training loop + model_ckpt = os.path.join(args.save_dir, 'model_{}.ckpt'.format(seed)) + global_step = 0 + best_dev_acc = 0 + step_nogress = 0 + optimizer.zero_grad() + if args.debug: + args.num_epoch = 1 + for epoch in trange(int(args.num_epoch), desc="Epoch"): + train_loss = 0.0 + epoch_no_inference_loss = 0. + model.train() + train_dataloader_list = [train_dataloader_dict["all"]] + epoch_iterator = tqdm(zip(*train_dataloader_list), desc="Train Iteration at Epoch {}".format(epoch), + total=num_update_steps_per_epoch) + for step, batch_list in enumerate(epoch_iterator): + + input_ids, attention_mask, target_ids, labels, _ = tuple(t.to(args.device) for t in batch_list[0]) + + outputs = model( + input_ids=input_ids, + attention_mask=attention_mask, + ) + + loss = outputs[0] + + loss /= args.grad_step + loss.backward() + if (global_step + 1) % args.grad_step == 0: + torch.nn.utils.clip_grad_norm_(model.parameters(), args.max_grad_norm) + optimizer.step() + + scheduler.step() # Update learning rate schedule + optimizer.zero_grad() + + train_loss += outputs[0].item() + step_log = "Epoch {} loss {:.4f}".format(epoch, train_loss / (step + 1)) + global_step += 1 + epoch_iterator.set_description(step_log) + if args.debug and global_step > 10: + break + + epoch_log = 'Epoch: {:03d} Train loss: {:.4f}'.format(epoch, train_loss / (step + 1)) + if args.counter_factor > 0: + epoch_log += " smoothing {:.4f}".format(epoch_no_inference_loss / (step + 1)) + logger.info(epoch_log) + + dev_result, _, _ = evaluate(devset, model, args) + + log = 'Epoch: {:03d}, dev accuracy: {:.4f}' + if dev_result > best_dev_acc: + torch.save({'ckpt': model.state_dict(), 'args': args}, model_ckpt) + log += ' best' + best_dev_acc = dev_result + step_nogress = 0 + else: + step_nogress += 1 + logger.info(log.format(epoch, dev_result)) + if step_nogress > 1 and global_step > warmup_steps: + break + + return_result = {} + model.load_state_dict(torch.load(model_ckpt)['ckpt']) + testset = get_tensor_dataset('test', tokenizer, args) + eval_result, predictions, scores = evaluate(testset, model, args) + log = 'Epoch: {:03d}, test accuracy: {:.4f}' + logger.info(log.format(-1, eval_result)) + return_result["test_all"] = eval_result + + with open(os.path.join(args.save_dir, 'predictions_test_seed{}.txt'.format(seed)), 'w') as fw: + for p, s in zip(predictions, scores): + fw.write('{}\t{}\n'.format(p, s)) + + if not args.test_split == "none": + for split in args.test_split.split(','): + testset = get_tensor_dataset(split, tokenizer, args) + eval_result, predictions, scores = evaluate(testset, model, args) + log = 'Epoch: {:03d}, {} accuracy: {:.4f}' + logger.info(log.format(-1, split, eval_result)) + return_result[split] = eval_result + + with open(os.path.join(args.save_dir, 'predictions_{}_seed{}.txt'.format(split, seed)), 'w') as fw: + for p, s in zip(predictions, scores): + fw.write('{}\t{}\n'.format(p, s)) + + if not args.save_ckpt: + os.remove(model_ckpt) + return return_result + + +if __name__ == "__main__": + + parser = argparse.ArgumentParser(description='Run main.') + parser.add_argument('--dataset', '-d', type=str) + parser.add_argument('--save_dir', '-o', type=str) + parser.add_argument('--sample_size', type=int, default=0) + parser.add_argument("--save_ckpt", action='store_true') + parser.add_argument("--debug", action='store_true') + + # model + parser.add_argument('--model_name', '-m', type=str) + parser.add_argument('--max_enc_length', type=int, default=128) + parser.add_argument('--max_dec_length', type=int, default=128) + parser.add_argument("--no_explanation", action='store_true') + parser.add_argument("--label_smoothing_no_inference", default=0, type=float) + parser.add_argument("--label_smoothing_shuffle_inference", default=0, type=float) + parser.add_argument("--dropout_context", default=0, type=float) + parser.add_argument("--mask_prob", default=1.0, type=float) + parser.add_argument("--counter_factor", default=1.0, type=float) + parser.add_argument("--mask_ratio", default=0, type=float) + parser.add_argument("--replace_ratio", default=0, type=float) + parser.add_argument("--contrast_size", default=0, type=int) + + # training + parser.add_argument('--train_batch_size', type=int, default=32) + parser.add_argument('--grad_step', type=int, default=1) + parser.add_argument('--learning_rate', type=float, default=1e-5) + parser.add_argument("--warmup_ratio", type=float, default=0.06) + parser.add_argument('--weight_decay', type=float, default=0.0) + parser.add_argument("--max_grad_norm", default=1.0, type=float) + parser.add_argument('--num_epoch', type=float, default=1000) + parser.add_argument('--dev_split', type=str, default="dev") + + # inference + parser.add_argument('--test_split', type=str, default="none") + parser.add_argument("--inference", action='store_true') + parser.add_argument("--evaluate", action='store_true') + parser.add_argument('--eval_split', type=str, default='test') + parser.add_argument('--eval_batch_size', type=int, default=8) + parser.add_argument('--sample', action='store_true') + parser.add_argument('--num_beams', type=int, default=1) + parser.add_argument('--top_k', type=int, default=0) + parser.add_argument('--top_p', type=float, default=1.0) + parser.add_argument('--num_return_sequences', type=int, default=1) + parser.add_argument("--overwrite_output", action='store_true') + + # gpu and workers option + parser.add_argument('--gpu_device', type=str, default='0') + + args = parser.parse_args() + + args.device = torch.device('cuda:{}'.format(args.gpu_device)) + + eval_result_all_split = {} + for seed in range(41, 45): + set_seed(seed) + eval_result = main(args, seed) + for split in eval_result: + if split not in eval_result_all_split: + eval_result_all_split[split] = [] + eval_result_all_split[split].append(eval_result[split]) + output_result = {} + for split in eval_result_all_split: + output_result[split] = { + "accuracy_mean": np.mean(eval_result_all_split[split]), + "accuracy_std": np.std(eval_result_all_split[split]), + } + with open(os.path.join(args.save_dir, 'evaluation_results.json'), 'w') as fw: + json.dump(output_result, fw, indent=4) \ No newline at end of file diff --git a/rationales/run.sh b/rationales/run.sh new file mode 100644 index 00000000..fdcf3af4 --- /dev/null +++ b/rationales/run.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +project_dir='.' + +dataset="boolq" +dev_split="dev" +test_split="train,dev,test" +sample_size=0 +model_name='google/flan-t5-base' +max_enc_length=128 +max_dec_length=128 +train_batch_size=16 +eval_batch_size=32 +grad_step=1 +learning_rate=3e-4 +weight_decay=0 +num_epoch=10 + +dropout_context=0 +label_smoothing_no_inference=0.1 +mask_prob=0.5 +counter_factor=1.0 +mask_ratio=1.0 +replace_ratio=0.3 + +save_dir="${project_dir}/checkpoints/${dataset}_${sample_size}-shot/dropout-context${dropout_context}_label-smooth${label_smoothing_no_inference}_mask${mask_ratio}-or-replace${replace_ratio}-inference${mask_prob}_${model_name}_bs${train_batch_size}_gs${grad_step}_lr${learning_rate}_wd${weight_decay}_e${num_epoch}" +mkdir -p $save_dir + +python \ + main.py \ + --mask_ratio $mask_ratio \ + --replace_ratio $replace_ratio \ + --counter_factor $counter_factor \ + --mask_prob $mask_prob \ + --dropout_context $dropout_context \ + --label_smoothing_no_inference $label_smoothing_no_inference \ + --dataset $dataset \ + --sample_size $sample_size \ + --test_split $test_split \ + --dev_split $dev_split \ + --save_dir $save_dir \ + --model_name $model_name \ + --max_enc_length $max_enc_length \ + --max_dec_length $max_dec_length \ + --train_batch_size $train_batch_size \ + --eval_batch_size $eval_batch_size \ + --grad_step $grad_step \ + --learning_rate $learning_rate \ + --weight_decay $weight_decay \ + --num_epoch $num_epoch \ diff --git a/rationales/utils.py b/rationales/utils.py new file mode 100644 index 00000000..03c452aa --- /dev/null +++ b/rationales/utils.py @@ -0,0 +1,33 @@ +import logging +import torch +import torch.nn.functional as F + + +def get_logger(name, log_path=None): + logger = logging.getLogger(name) + logger.setLevel(logging.DEBUG) + formatter = logging.Formatter('%(asctime)s: %(message)s', datefmt='%Y/%m/%d %H:%M:%S') + + if log_path: + handler = logging.FileHandler(log_path, 'w') + handler.setLevel(logging.INFO) + handler.setFormatter(formatter) + logger.addHandler(handler) + + return logger + + +class LabelSmoothingLoss(torch.nn.Module): + def __init__(self, ): + super(LabelSmoothingLoss, self).__init__() + + def linear_combination(self, x, y, smoothing): + return smoothing * x + (1 - smoothing) * y + + def forward(self, preds, target, smoothing, nll=None): + n = preds.size(-1) + log_preds = F.log_softmax(preds, dim=-1) + loss = -log_preds.sum(dim=-1) / n + if nll is None: + nll = F.nll_loss(log_preds, target, reduction='none') + return self.linear_combination(loss, nll, smoothing).mean() \ No newline at end of file From 65d720221b3cefcd04c5f0e3f2b9d442771ad364 Mon Sep 17 00:00:00 2001 From: sahil Date: Wed, 10 May 2023 15:59:23 +0200 Subject: [PATCH 2/4] flan t5 code train updated --- rationales/data_helper.py | 56 +++++++++++++++++++++++++++------------ rationales/main.py | 16 ++++++----- 2 files changed, 49 insertions(+), 23 deletions(-) diff --git a/rationales/data_helper.py b/rationales/data_helper.py index 46eeb26a..d79f1f24 100644 --- a/rationales/data_helper.py +++ b/rationales/data_helper.py @@ -32,22 +32,23 @@ def __getitem__(self, i) -> InputExample: def load_raw_dataset(split, args): #data_path = os.path.join('./data', args.dataset, '{}.jsonl'.format(split)) - data = pd.read_csv("cache/boolq/GPT-3.5_rationales_BoolQ_val_400.csv") + #ROOT_DIR = os.path.dirname(os.path.abspath(__file__)) + #print + CONFIG_PATH = '../cache/boolq/GPT-3.5_rationales_BoolQ_val_400.csv' + data = pd.read_csv(CONFIG_PATH) train, test = train_test_split(data, test_size=0.3, random_state=42, shuffle=True) dataset = [] - - for example_id, line in tqdm(enumerate(train), desc='processing {}'.format(split)): + for example_id, line in tqdm(train.iterrows(), desc='processing {}'.format(split)): example = line - - dataset.append( + dataset.append( InputExample( prompt=example["prompt"], explanation=example["completion"], ) - ) + ) for example in dataset[:2]: print("*** Example ***") @@ -103,18 +104,27 @@ def __call__(self, examples): explanation = example.explanation added_ids = self.tokenizer.encode(explanation, add_special_tokens=False) + encoder_input_tensor.append(input_ids) - encoder_attention_mask_tensor.append([1]*len(input_ids)) + encoder_attention_mask_tensor.append([1]*len(input_ids[:self.args.max_enc_length]) + [0] * (self.args.max_enc_length - len(input_ids))) decoder_label_tensor.append(added_ids) - + choices_input_ids = [ids[:self.args.max_enc_length] for ids in encoder_input_tensor] + choices_input_ids = [ids + [self.tokenizer.pad_token_id] * (self.args.max_enc_length - len(ids)) for ids in + choices_input_ids] + label_tensor = [ids[:self.args.max_dec_length] for ids in decoder_label_tensor] + label_tensor = [ids + [self.tokenizer.pad_token_id] * (self.args.max_dec_length - len(ids)) + for ids in label_tensor] return tuple(torch.tensor(t) for t in - [encoder_input_tensor, encoder_attention_mask_tensor, decoder_label_tensor]) + [choices_input_ids, encoder_attention_mask_tensor, label_tensor]) def get_tensor_dataset(split, tokenizer, args): #data_path = os.path.join('./data', args.dataset, '{}.jsonl'.format(split)) - data = pd.read_csv("cache/boolq/GPT-3.5_rationales_BoolQ_val_400.csv") + #ROOT_DIR = os.path.dirname(os.path.abspath(__file__)) + + CONFIG_PATH = '../cache/boolq/GPT-3.5_rationales_BoolQ_val_400.csv' + data = pd.read_csv(CONFIG_PATH) train, test = train_test_split(data, test_size=0.3,random_state=42, shuffle=True) dev,test = train_test_split(test, test_size=0.2, random_state=42, shuffle=True) split_args = dev if split == 'dev' else test @@ -122,22 +132,32 @@ def get_tensor_dataset(split, tokenizer, args): encoder_attention_mask_tensor = [] decoder_label_tensor = [] task_label_tensor = [] - for example_idx, example in tqdm(enumerate(split_args), desc='processing {}'.format(data_path)): + for example_idx, example in tqdm(split_args.iterrows(), desc='processing {}'.format(CONFIG_PATH)): input_ids = [] attention_mask = [] context = example.prompt - input_ids = self.tokenizer.encode(context.strip(), add_special_tokens=False) + input_ids = tokenizer.encode(context.strip(), add_special_tokens=False) explanation = example.completion - added_ids = self.tokenizer.encode(explanation, + added_ids = tokenizer.encode(explanation, add_special_tokens=False) encoder_input_tensor.append(input_ids) - encoder_attention_mask_tensor.append([1] * len(input_ids)) + encoder_attention_mask_tensor.append([1] * len(input_ids[:args.max_enc_length]) + [0] * (args.max_enc_length - len(input_ids))) decoder_label_tensor.append(added_ids) - encoder_input_tensor = torch.tensor(encoder_input_tensor, dtype=torch.long) + print(f"input tensor {len(encoder_input_tensor)}") + print(f"one item{len(input_ids)}") + choices_input_ids = [ids[:args.max_enc_length] for ids in encoder_input_tensor] + choices_input_ids = [ids + [tokenizer.pad_token_id] * (args.max_enc_length - len(ids)) for ids in + choices_input_ids] + label_tensor = [ids[:args.max_dec_length] for ids in decoder_label_tensor] + label_tensor = [ids + [tokenizer.pad_token_id] * (args.max_dec_length - len(ids)) + for ids in label_tensor] + encoder_input_tensor = torch.tensor(choices_input_ids, dtype=torch.long) encoder_attention_mask_tensor = torch.tensor(encoder_attention_mask_tensor, dtype=torch.long) - decoder_label_tensor = torch.tensor(decoder_label_tensor, dtype=torch.long) + decoder_label_tensor = torch.tensor(label_tensor, dtype=torch.long) + print(decoder_label_tensor.shape) + for f1, f2, f3 in zip(encoder_input_tensor[:2], encoder_attention_mask_tensor[:2], decoder_label_tensor[:2]): print("*** Example ***") if len(f1.shape) == 3: @@ -145,7 +165,9 @@ def get_tensor_dataset(split, tokenizer, args): for ids in f1: print("encoder input: %s" % tokenizer.decode(ids)) # print("encoder attention mask: %s" % f2) + print(f3) for ids in f3: - print("decoder output: %s" % tokenizer.decode([tid for tid in ids if not tid == -100])) + if not ids == -100: + print("decoder output: %s" % tokenizer.decode(ids)) return TensorDataset(encoder_input_tensor, encoder_attention_mask_tensor, decoder_label_tensor) \ No newline at end of file diff --git a/rationales/main.py b/rationales/main.py index 882714c1..be5c9e13 100644 --- a/rationales/main.py +++ b/rationales/main.py @@ -31,7 +31,8 @@ def forward(self, input_ids, attention_mask, target_ids, labels=None): outputs = self.model( input_ids=input_ids, - attention_mask=attention_mask + attention_mask=attention_mask, + labels=target_ids, ) log_probs = - F.cross_entropy(outputs.logits.view(-1, outputs.logits.size(-1)), target_ids.view(-1), @@ -51,7 +52,7 @@ def evaluate(dataset, model, args): output_scores = [] for step, batch in enumerate(epoch_iterator): - input_ids, attention_mask, target_ids, labels = tuple(t.to(args.device) for t in batch) + input_ids, attention_mask, target_ids = tuple(t.to(args.device) for t in batch) with torch.no_grad(): outputs = model( input_ids=input_ids, @@ -59,12 +60,13 @@ def evaluate(dataset, model, args): target_ids=target_ids, ) logits = outputs + print(logits) _, predictions = logits.max(dim=1) probs = F.softmax(logits, dim=-1) - answer_probs = torch.gather(probs, 1, labels.unsqueeze(1)).squeeze(1) + answer_probs = torch.gather(probs, 1, target_ids.unsqueeze(1)).squeeze(1) - accuracy += predictions.eq(labels).sum().item() + accuracy += predictions.eq(target_ids).sum().item() output_predictions.extend(predictions.tolist()) output_scores.extend(answer_probs.tolist()) @@ -86,6 +88,7 @@ def main(args, seed): # model tokenizer = AutoTokenizer.from_pretrained(args.model_name, cache_dir='../cache/') model = Text2TextForMultiChoice(args.model_name) + print(args.device) model.to(args.device) # ----------------------------------------------------- # @@ -152,11 +155,12 @@ def main(args, seed): total=num_update_steps_per_epoch) for step, batch_list in enumerate(epoch_iterator): - input_ids, attention_mask, target_ids, labels, _ = tuple(t.to(args.device) for t in batch_list[0]) + input_ids, attention_mask, target_ids = tuple(t.to(args.device) for t in batch_list[0]) outputs = model( input_ids=input_ids, attention_mask=attention_mask, + target_ids=target_ids ) loss = outputs[0] @@ -272,7 +276,7 @@ def main(args, seed): parser.add_argument("--overwrite_output", action='store_true') # gpu and workers option - parser.add_argument('--gpu_device', type=str, default='0') + parser.add_argument('--gpu_device', type=str, default='1') args = parser.parse_args() From bac80d70ecf51be1ba864b6ae19ae730e3d7410d Mon Sep 17 00:00:00 2001 From: sahil Date: Sun, 14 May 2023 22:49:17 +0200 Subject: [PATCH 3/4] flan t5 code using hugging face trainer --- rationales/data_helper.py | 2 +- rationales/t5 _trainer.py | 107 +++++++++++++++++++++++++++++++++++++ rationales/t5_inference.py | 20 +++++++ 3 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 rationales/t5 _trainer.py create mode 100644 rationales/t5_inference.py diff --git a/rationales/data_helper.py b/rationales/data_helper.py index d79f1f24..24712eed 100644 --- a/rationales/data_helper.py +++ b/rationales/data_helper.py @@ -30,7 +30,7 @@ def __getitem__(self, i) -> InputExample: return self.features[i] -def load_raw_dataset(split, args): +def load_raw_dataset(split): #data_path = os.path.join('./data', args.dataset, '{}.jsonl'.format(split)) #ROOT_DIR = os.path.dirname(os.path.abspath(__file__)) #print diff --git a/rationales/t5 _trainer.py b/rationales/t5 _trainer.py new file mode 100644 index 00000000..7e04b36f --- /dev/null +++ b/rationales/t5 _trainer.py @@ -0,0 +1,107 @@ + + +from datasets import concatenate_datasets +from transformers import AutoTokenizer, AutoModelForSeq2SeqLM +from data_helper import load_raw_dataset, Data_Collator_for_Training, get_tensor_dataset + +from datasets import load_dataset +from transformers import Seq2SeqTrainer, Seq2SeqTrainingArguments +from transformers import DataCollatorForSeq2Seq + +from transformers import AutoModelForSeq2SeqLM + +# huggingface hub model id +model_id="google/flan-t5-base" + +# load model from the hub +model_id="google/flan-t5-base" + +# Load tokenizer of FLAN-t5-base +tokenizer = AutoTokenizer.from_pretrained(model_id) +dataset = load_dataset("csv", data_files="../cache/boolq/GPT-3.5_rationales_BoolQ_val_400.csv") +dataset=dataset["train"].train_test_split() +# The maximum total input sequence length after tokenization. +# Sequences longer than this will be truncated, sequences shorter will be padded. +tokenized_inputs = concatenate_datasets([dataset["train"], dataset["test"]]).map(lambda x: tokenizer(x["prompt"], truncation=True), batched=True ) +max_source_length = max([len(x) for x in tokenized_inputs["input_ids"]]) +print(f"Max source length: {max_source_length}") + +# The maximum total sequence length for target text after tokenization. +# Sequences longer than this will be truncated, sequences shorter will be padded." +tokenized_targets = concatenate_datasets([dataset["train"], dataset["test"]]).map(lambda x: tokenizer(x["completion"], truncation=True), batched=True) +max_target_length = max([len(x) for x in tokenized_targets["input_ids"]]) +print(f"Max target length: {max_target_length}") + +def preprocess_function(sample,padding="max_length"): + # add prefix to the input for t5 + + + # tokenize inputs + model_inputs = tokenizer(sample["prompt"], max_length=max_source_length, padding=padding, truncation=True) + + # Tokenize targets with the `text_target` keyword argument + labels = tokenizer(sample["completion"], max_length=max_target_length, padding=padding, truncation=True) + + # If we are padding here, replace all tokenizer.pad_token_id in the labels by -100 when we want to ignore + # padding in the loss. + if padding == "max_length": + labels["input_ids"] = [ + [(l if l != tokenizer.pad_token_id else -100) for l in label] for label in labels["input_ids"] + ] + + model_inputs["labels"] = labels["input_ids"] + return model_inputs + +tokenized_dataset = dataset.map(preprocess_function, batched=True) +print(f"Keys of tokenized dataset: {list(tokenized_dataset['train'].features)}") + + + +model = AutoModelForSeq2SeqLM.from_pretrained(model_id) + + + + +# we want to ignore tokenizer pad token in the loss +label_pad_token_id = -100 +# Data collator +data_collator = DataCollatorForSeq2Seq( + tokenizer, + model=model, + label_pad_token_id=label_pad_token_id, + pad_to_multiple_of=8 +) + + +# Hugging Face repository id + +# Define training args +training_args = Seq2SeqTrainingArguments( + output_dir="/hd2/sahil/t5", + per_device_train_batch_size=4, + per_device_eval_batch_size=4, + predict_with_generate=True, + fp16=False, # Overflows with fp16 + learning_rate=5e-5, + num_train_epochs=5, + # logging & evaluation strategies + logging_dir=f"/hd2/sahil/t5/logs", + logging_strategy="steps", + logging_steps=500, + evaluation_strategy="epoch", + save_strategy="epoch", + save_total_limit=2, + load_best_model_at_end=True +) + +# Create Trainer instance +trainer = Seq2SeqTrainer( + model=model, + args=training_args, + data_collator=data_collator, + train_dataset=tokenized_dataset["train"], + eval_dataset=tokenized_dataset["test"], +) + + +trainer.train() \ No newline at end of file diff --git a/rationales/t5_inference.py b/rationales/t5_inference.py new file mode 100644 index 00000000..6cb207be --- /dev/null +++ b/rationales/t5_inference.py @@ -0,0 +1,20 @@ +from datasets import concatenate_datasets +from transformers import AutoTokenizer, AutoModelForSeq2SeqLM +from data_helper import load_raw_dataset, Data_Collator_for_Training, get_tensor_dataset + +from datasets import load_dataset +from transformers import Seq2SeqTrainer, Seq2SeqTrainingArguments +from transformers import DataCollatorForSeq2Seq +from random import randrange +from transformers import AutoModelForSeq2SeqLM +model_id="google/flan-t5-base" +tokenizer = AutoTokenizer.from_pretrained(model_id) +dataset = load_dataset("csv", data_files="../cache/boolq/GPT-3.5_rationales_BoolQ_val_400.csv") +dataset=dataset["train"].train_test_split() +model = AutoModelForSeq2SeqLM.from_pretrained("/hd2/sahil/t5/checkpoint-95") + +sample = dataset['test'][randrange(len(dataset["test"]))] +print(sample) +inputs = tokenizer(sample['prompt'], return_tensors="pt") +outputs = model.generate(**inputs) +print(tokenizer.batch_decode(outputs, skip_special_tokens=True)) \ No newline at end of file From d23396fdc4cd1bc5bad5e9f4742c32defcdfdb1a Mon Sep 17 00:00:00 2001 From: sahil Date: Mon, 15 May 2023 23:53:33 +0200 Subject: [PATCH 4/4] upload readme file --- rationales/README.md | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 rationales/README.md diff --git a/rationales/README.md b/rationales/README.md new file mode 100644 index 00000000..4fbf99c1 --- /dev/null +++ b/rationales/README.md @@ -0,0 +1,38 @@ +## Running with conda / virtualenv + +Create the environment and install dependencies. + +```shell +conda create -n ttm python=3.9 +conda activate ttm +``` + +Create a directory to save the model and write log +eg + +```shell +mkdir t5 +``` + +Change the directory path on line 80 and 88 in t5_trainer.py + +```shell +output_dir="/hd2/sahil/t5", +logging_dir=f"/hd2/sahil/t5/logs", +``` + +Run python command to train the model +```python +python t5_trainer.py +``` + +For inference,load the latest checkpoint from t5 in line 14 of t5_inference.py + +``` +model = AutoModelForSeq2SeqLM.from_pretrained("/hd2/sahil/t5/checkpoint-95") +``` + +run inference of one example to check the output +```shell +python t5_infernce.py +``` \ No newline at end of file