forked from boyuanzheng010/rockfish-tutorial
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathclassification.py
More file actions
246 lines (192 loc) · 9.11 KB
/
classification.py
File metadata and controls
246 lines (192 loc) · 9.11 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
import torch
from torch.utils.data import DataLoader
from transformers import AutoTokenizer
from torch.utils.data import DataLoader
from datasets import load_dataset
import evaluate as evaluate
from transformers import get_scheduler
from transformers import AutoModelForSequenceClassification
import argparse
import subprocess
def print_gpu_memory():
""" Print the amount of GPU memory used """
# check if gpu is available
if torch.cuda.is_available():
print("torch.cuda.memory_allocated: %fGB" % (torch.cuda.memory_allocated(0) / 1024 / 1024 / 1024))
print("torch.cuda.memory_reserved: %fGB" % (torch.cuda.memory_reserved(0) / 1024 / 1024 / 1024))
print("torch.cuda.max_memory_reserved: %fGB" % (torch.cuda.max_memory_reserved(0) / 1024 / 1024 / 1024))
p = subprocess.check_output('nvidia-smi')
print(p.decode("utf-8"))
class BoolQADataset(torch.utils.data.Dataset):
""" Dataset for the IMDB dataset """
def __init__(self, passages, questions, answers, tokenizer, max_len):
self.passages = passages
self.questions = questions
self.answers = answers
self.tokenizer = tokenizer
self.max_len = max_len
def __len__(self):
return len(self.answers)
def __getitem__(self, index):
passage = str(self.passages[index])
question = self.questions[index]
answer = self.answers[index]
input = question + " [SEP] " + passage
# print(f"Input: {input} - Output: {answer}")
encoded_review = self.tokenizer.encode_plus(
input,
add_special_tokens=True,
max_length=self.max_len,
return_token_type_ids=False,
return_attention_mask=True,
return_tensors="pt",
padding="max_length",
truncation=True
)
return {
'input_ids': encoded_review['input_ids'][0],
'attention_mask': encoded_review['attention_mask'][0],
'labels': torch.tensor(answer, dtype=torch.long)
}
def update_metrics(metrics, predictions, labels):
""" Update a list of metrics with new predictions and labels
:param list<datasets.Metric> metrics: list of metrics
:param torch.Tensor predictions: tensor of predictions of shape (1, batch_size)
:param torch.Tensor labels: tensor of labels of shape (1, batch_size)
:return None
"""
for metric in metrics:
metric.add_batch(predictions=predictions, references=labels)
def evaluate_model(model, dataloader, device):
""" Evaluate a PyTorch Model
:param torch.nn.Module model: the model to be evaluated
:param torch.utils.data.DataLoader test_dataloader: DataLoader containing testing examples
:param torch.device device: the device that we'll be training on
:return dictionary<string, float>: dictionary of metrics names mapped to their values
"""
# load metrics
dev_accuracy = evaluate.load('accuracy')
# turn model into evaluation mode
model.eval()
for batch in dataloader:
input_ids = batch['input_ids'].to(device)
attention_mask = batch['attention_mask'].to(device)
output = model(input_ids=input_ids, attention_mask=attention_mask)
predictions = output.logits
predictions = torch.argmax(predictions, dim=1)
dev_accuracy.add_batch(predictions=predictions, references=batch['labels'])
# compute and return metrics
return dev_accuracy.compute()
def train(mymodel, num_epochs, train_dataloader, validation_dataloader, device, lr):
""" Train a PyTorch Module
:param torch.nn.Module mymodel: the model to be trained
:param int num_epochs: number of epochs to train for
:param torch.utils.data.DataLoader train_dataloader: DataLoader containing training examples
:param torch.utils.data.DataLoader validation_dataloader: DataLoader containing validation examples
:param torch.device device: the device that we'll be training on
:return None
"""
# here, we use the AdamW optimzer. Use torch.optim.Adam.
# instantiate it on the untrained model parameters with a learning rate of 5e-5
print(" >>>>>>>> Initializing optimizer")
optimizer = torch.optim.AdamW(mymodel.parameters(), lr=lr)
# now, we set up the learning rate scheduler
lr_scheduler = get_scheduler(
"linear",
optimizer=optimizer,
num_warmup_steps=50,
num_training_steps=len(train_dataloader) * num_epochs
)
loss = torch.nn.CrossEntropyLoss()
for epoch in range(num_epochs):
# put the model in training mode (important that this is done each epoch,
# since we put the model into eval mode during validation)
mymodel.train()
# load metrics
train_accuracy = evaluate.load('accuracy')
print(f"Epoch {epoch + 1} training:")
for i, batch in enumerate(train_dataloader):
input_ids = batch['input_ids'].to(device)
attention_mask = batch['attention_mask'].to(device)
output = mymodel(input_ids=input_ids, attention_mask=attention_mask)
predictions = output.logits
output = loss(predictions, batch['labels'].to(device))
output.backward()
# update the optimizer
optimizer.step()
lr_scheduler.step()
# now that we have applied the gradients, clear the gradients for the next iteration
optimizer.zero_grad()
predictions = torch.argmax(predictions, dim=1)
# update metrics
train_accuracy.add_batch(predictions=predictions, references=batch['labels'])
# print evaluation metrics
print(f" ===> Epoch {epoch + 1}")
print(f" - Average training metrics: accuracy={train_accuracy.compute()}")
# normally, validation would be more useful when training for many epochs
val_accuracy = evaluate_model(mymodel, validation_dataloader, device)
print(f" - Average validation metrics: accuracy={val_accuracy}")
def run_experiment(model_name, num_epochs, lr, batch_size, device):
# download dataset
print("Loading the dataset ...")
dataset = load_dataset("boolq")
dataset = dataset.shuffle() # shuffle the data
print("Slicing the data...")
# since the dataset does not come with any validation data,
# split the training data into "train" and "dev"
dataset_train_subset = dataset['train'][:8000]
dataset_dev_subset = dataset['validation']
dataset_test_subset = dataset['train'][8000:] # use the subset of the test set for making the experimentation faster
max_len = 128 # any input longer than this is cut
print("Loading the tokenizer...")
mytokenizer = AutoTokenizer.from_pretrained(model_name)
print("Loding the data into DS...")
train_dataset = BoolQADataset(
passages=list(dataset_train_subset['passage']),
questions=list(dataset_train_subset['question']),
answers=list(dataset_train_subset['answer']),
tokenizer=mytokenizer,
max_len=max_len
)
validation_dataset = BoolQADataset(
passages=list(dataset_dev_subset['passage']),
questions=list(dataset_dev_subset['question']),
answers=list(dataset_dev_subset['answer']),
tokenizer=mytokenizer,
max_len=max_len
)
test_dataset = BoolQADataset(
passages=list(dataset_test_subset['passage']),
questions=list(dataset_test_subset['question']),
answers=list(dataset_test_subset['answer']),
tokenizer=mytokenizer,
max_len=max_len
)
print(" >>>>>>>> Initializing the data loaders ... ")
train_dataloader = DataLoader(train_dataset, batch_size=batch_size)
validation_dataloader = DataLoader(validation_dataset, batch_size=batch_size)
test_dataloader = DataLoader(test_dataset, batch_size=batch_size)
# from Hugging Face (transformers), read their documentation to do this.
print("Loding the model ..." )
pretrained_model = AutoModelForSequenceClassification.from_pretrained(model_name, num_labels=2)
print("Moving model to device ..." + str(device))
pretrained_model.to(device)
print(" >>>>>>>> Starting training ... ")
train(pretrained_model, num_epochs, train_dataloader, validation_dataloader, device, lr=lr)
print_gpu_memory()
val_accuracy = evaluate_model(pretrained_model, validation_dataloader, device)
print(f" - Average DEV metrics: accuracy={val_accuracy}")
test_accuracy = evaluate_model(pretrained_model, test_dataloader, device)
print(f" - Average TEST metrics: accuracy={test_accuracy}")
# create the __main__ function
if __name__ == "__main__":
# get use arguments for num_epochs, lr, batch_size, device
parser = argparse.ArgumentParser()
parser.add_argument("--num_epochs", type=int, default=1)
parser.add_argument("--lr", type=float, default=5e-5)
parser.add_argument("--batch_size", type=int, default=32)
parser.add_argument("--device", type=str, default="cuda")
parser.add_argument("--model", type=str, default="distilbert-base-uncased")
args = parser.parse_args()
# run the experiment
run_experiment(args.model, args.num_epochs, args.lr, args.batch_size, args.device)