-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtrain_evaluate_main.py
210 lines (158 loc) · 7.93 KB
/
train_evaluate_main.py
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
"""
Train test and evaluate T5 model on Webis-Argument-Framing-19 dataset. Use -h option or see README.md for help.
"""
from preprocessing import get_train_data,get_data
from model import get_model, get_tokenizer
import defaults
import tensorflow as tf
from tensorflow.keras.losses import SparseCategoricalCrossentropy
from tensorflow.keras.optimizers import Adam
from tqdm import tqdm
from rouge import Rouge
from os.path import isdir, join
from os import makedirs
import pickle
from sklearn.metrics import f1_score
from collections import defaultdict
import argparse
# make trained_models dir if not existing.
makedirs(defaults.models_path,exist_ok=True)
def train_and_save(modelname,pretrained_name,X,X_val,epochs=5,batch_size=6,learning_rate=1e-4):
"""
Train and save model under 'modelname', starting from 'pretrained_name' checkpoint.
The model and its configuration will be saved in a folder called 'modelname'.
Args:
modelname: name of the newly trained model.
pretrained_name: name of the checkpoint to start from.
X: train dataset.
X_val: validation dataset.
epochs: number of epochs to train.
batch_size: batchsize for training and validation
learning_rate: learning rate used by ADAM optimizer.
Returns:
trained model and training history as a dictionary.
"""
train_size = len(X['input_ids'])
val_size = len(X_val['input_ids'])
print("Load Model.")
model = get_model(pretrained_name)
model.compile(optimizer=Adam(learning_rate=learning_rate),loss=SparseCategoricalCrossentropy)
print(f"Starting training on {train_size} samples. Validating on {val_size} samples.")
history = model.fit(X,validation_data=X_val,epochs=epochs,batch_size=batch_size,validation_split=0.2)
model.save_pretrained(modelname)
print("finished training and saved model.")
return model, history
def build_int_mapping(args):
"""
get dict with {id : frame} from list of Arguments.
"""
# set default value to something not present.
d = defaultdict(lambda : 100000)
for a in args:
d[a.frame] = a.frame_id
return d
def evaluate(model, args_test,history , batch_size=4):
"""
Compute and print test metrics 'acc' 'rouge 1' and 'f1-macro'on test args.
Args:
model: Model to evaluate of type identFrameT5.
args_test: list of Arguments to evaluate on.
batch_size: batch size to use for evaluating.
"""
rouge = Rouge()
print(f"Evaluate on {len(args_test)} samples.")
X_test, y_test = get_train_data(args_test, return_text_labels=True)
tokenizer = get_tokenizer()
predictions = []
correct = 0
input_ids = X_test['input_ids']
attention_masks = X_test['attention_mask']
input_ids_batch = []
attention_mask_batch = []
total = len(input_ids)
no_predicted = 0
for x_input_ids, x_attention_masks, y in tqdm(zip(input_ids, attention_masks, y_test), total=total,
desc='predicting test set'):
# if batch is full or last batch (maybe < batchsize) is reached
if total - no_predicted - len(input_ids_batch) == 1:
input_ids_batch.append(x_input_ids)
attention_mask_batch.append(x_attention_masks)
if len(input_ids_batch) == batch_size or total - no_predicted - len(input_ids_batch) == 0:
# predict_batch
input_ids_batch = tf.stack(input_ids_batch)
attention_mask_batch = tf.stack(attention_mask_batch)
prediction = model.generate(input_ids=input_ids_batch, attention_mask=attention_mask_batch)
for out_ids in prediction:
tokens = tokenizer.convert_ids_to_tokens(out_ids, skip_special_tokens=True)
tokens = [t for t in tokens if t not in ['<pad>', '</s>']]
strs = tokenizer.convert_tokens_to_string(tokens)
predictions.append(strs)
no_predicted += batch_size
input_ids_batch = [x_input_ids]
attention_mask_batch = [x_attention_masks]
else:
# build batch
input_ids_batch.append(x_input_ids)
attention_mask_batch.append(x_attention_masks)
# compute accuracy
correct = 0
for i, (pred, label) in enumerate(zip(predictions, y_test)):
if pred == label:
correct += 1
if pred == "":
# commented out for cleaner output. Should be
#print("WARNING:Empty prediction!")
predictions[i] = "EMPTY"
# compute rouge1 f1 mean
r1_f1 = rouge.get_scores(predictions, y_test, avg=True)['rouge-1']['f']
# compute f1 classification score
class_int_mapping = build_int_mapping(args_test)
pred_int = [class_int_mapping[p] for p in predictions]
label_int = [class_int_mapping[l] for l in y_test]
f1 = f1_score(label_int,pred_int,average='macro')
print('rouge-1 f-score: ',r1_f1)
print('f1:', f1)
print('acc= ', correct / len(y_test))
if __name__ == "__main__":
# argparse. Better to put this in main! Import problems this way ...
parser = argparse.ArgumentParser(description='train and evaluate a T5 on the Webis-Framing-19 dataset.')
parser.add_argument('--model-savename', help="Name the new, trained model will be saved as.", type=str,default=defaults.model_savename)
parser.add_argument('--pretrained-name', help="Name of the pretrained model to use. Use 't5-small' for smaller checkpoint. Checkpoint in 'trained_models' folder can also be used.",type=str,default=defaults.pretrained_name)
parser.add_argument('--epochs', help="Train for that many epochs", type=int, default=defaults.epochs)
parser.add_argument('--batchsize', help="Use batchsize for training and evaluation. 6 works for t5-base on colab. t5-small can use 24.", type=int, default=defaults.batchsize)
parser.add_argument('--val-split', help="portion of the data to be used for val set.", type=float, default=defaults.val_split)
parser.add_argument('--test-split', help="portion of the data to be used for test set.", type=float, default=defaults.val_split)
parser.add_argument('--data-file', help="absolute or relative path to Webis-Framing-19 dataset csv file.", type=str, default=defaults.data_csv_file)
parser.add_argument('--debug', help="Use small dataset and model for testing the code.", action='store_true')
args = parser.parse_args()
PRETRAINED_NAME = args.pretrained_name
MODEL_FILENAME = join(defaults.models_path,args.model_savename)
EPOCHS = args.epochs
BATCHSIZE = args.batchsize
DEBUG=args.debug
VAL_SPLIT = args.val_split
TEST_SPLIT=args.test_split
DATA_CSV_FILENAME = "data/Webis-argument-framing.csv"
print(MODEL_FILENAME)
if isdir(MODEL_FILENAME):
yn = input(f"WARNING! Model \"{MODEL_FILENAME}\" already exists. Training again will overwrite old checkpoint."
f"\nYou can define a new name in default.py or call with option --model-savename \n\tContinue?[y/N]")
if yn != 'y':
print('Exiting, user abort.')
exit(0)
print("Preprocessing data for training.")
X_train,X_val, args_test = get_data(DATA_CSV_FILENAME, train_min_class_count=5, val_split=VAL_SPLIT, test_split=TEST_SPLIT)
if DEBUG:
# use small model and data for testing.
X_train['input_ids'] = X_train['input_ids'][0:100]
X_train['attention_mask'] = X_train['attention_mask'][0:100]
X_val['input_ids'] = X_val['input_ids'][0:20]
X_val['attention_mask'] = X_val['attention_mask'][0:20]
args_test=args_test[0:20]
EPOCHS = 2
PRETRAINED_NAME = 't5-small'
print("Finished preprocessing")
trained_model, history = train_and_save(MODEL_FILENAME,PRETRAINED_NAME, X_train, X_val, batch_size=BATCHSIZE, epochs=EPOCHS)
with open(join(MODEL_FILENAME,'history.pkl'),'wb') as f:
pickle.dump(history.history,f,-1)
evaluate(trained_model, args_test, history, batch_size=BATCHSIZE)