matching multiple choice between new and old apis (#4918)

* matching multiple choice between new and old apis

* update according to reviewer's comments

Co-authored-by: liqun <liqun@OrtTrainingDev4.af05slrtruoetgaxwwjv5nsq5e.px.internal.cloudapp.net>
This commit is contained in:
liqunfu 2020-08-26 12:36:10 -07:00 committed by GitHub
parent 0d3bbfdd0f
commit b3783a9f85
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 90 additions and 36 deletions

View file

@ -33,6 +33,12 @@ import torch
logger = logging.getLogger(__name__)
def verify_old_and_new_api_are_equal(results_per_api):
new_api_results = results_per_api[True]
old_api_results = results_per_api[False]
for key in new_api_results.keys():
assert_allclose(new_api_results[key], old_api_results[key])
@dataclass
class ModelArguments:
"""
@ -75,12 +81,16 @@ class ORTGlueTest(unittest.TestCase):
expected_acc_and_f1 = 0.9013897443794272
expected_loss = 0.35917433314755853
results_per_api = dict()
for use_new_api in [True, False]:
results = self.run_glue(model_name="roberta-base", task_name="MRPC", fp16=False, use_new_api=use_new_api)
assert_allclose(results['acc'], expected_acc, rtol=self.rtol)
assert_allclose(results['f1'], expected_f1, rtol=self.rtol)
assert_allclose(results['acc_and_f1'], expected_acc_and_f1, rtol=self.rtol)
assert_allclose(results['loss'], expected_loss, rtol=self.rtol)
results_per_api[use_new_api] = results
verify_old_and_new_api_are_equal(results_per_api)
def test_roberta_fp16_with_mrpc(self):
expected_acc = 0.8946078431372549
@ -88,12 +98,16 @@ class ORTGlueTest(unittest.TestCase):
expected_acc_and_f1 = 0.90965068163868
expected_loss = 0.3052181116506165
results_per_api = dict()
for use_new_api in [True, False]:
results = self.run_glue(model_name="roberta-base", task_name="MRPC", fp16=True, use_new_api=use_new_api)
assert_allclose(results['acc'], expected_acc, rtol=self.rtol)
assert_allclose(results['f1'], expected_f1, rtol=self.rtol)
assert_allclose(results['acc_and_f1'], expected_acc_and_f1, rtol=self.rtol)
assert_allclose(results['loss'], expected_loss, rtol=self.rtol)
results_per_api[use_new_api] = results
verify_old_and_new_api_are_equal(results_per_api)
def test_bert_with_mrpc(self):
if self.local_rank == -1:
@ -109,8 +123,12 @@ class ORTGlueTest(unittest.TestCase):
if self.local_rank == -1:
# not parallel case, we can run both new and old api tests
results_per_api = dict()
for use_new_api in [True, False]:
results = self.run_glue(model_name="bert-base-cased", task_name="MRPC", fp16=False, use_new_api=use_new_api)
results_per_api[use_new_api] = results
verify_old_and_new_api_are_equal(results_per_api)
else:
# with parallel training, TrainingArguments can only be created once (due to its cached _setup_devices)
# thus we can only choose one test case to run.
@ -128,12 +146,16 @@ class ORTGlueTest(unittest.TestCase):
expected_acc_and_f1 = 0.8751007252215954
expected_loss = 0.412924896998732
results_per_api = dict()
for use_new_api in [True, False]:
results = self.run_glue(model_name="bert-base-cased", task_name="MRPC", fp16=True, use_new_api=use_new_api)
assert_allclose(results['acc'], expected_acc, rtol=self.rtol)
assert_allclose(results['f1'], expected_f1, rtol=self.rtol)
assert_allclose(results['acc_and_f1'], expected_acc_and_f1, rtol=self.rtol)
assert_allclose(results['loss'], expected_loss, rtol=self.rtol)
results_per_api[use_new_api] = results
verify_old_and_new_api_are_equal(results_per_api)
def model_to_desc(self, model_name, model):
if model_name.startswith('bert') or model_name.startswith('xlnet'):
@ -146,12 +168,12 @@ class ORTGlueTest(unittest.TestCase):
'outputs': [('loss', [], True),
('logits', ['batch', 2])]}
model_desc = ModelDescription([
IODescription('input_ids', ['batch', 'max_seq_len_in_batch'], torch.int64, num_classes=model.config.vocab_size),
IODescription('attention_mask', ['batch', 'max_seq_len_in_batch'], torch.int64, num_classes=2),
IODescription('token_type_ids', ['batch', 'max_seq_len_in_batch'], torch.int64, num_classes=2),
IODescription('labels', ['batch',], torch.int64, num_classes=2)], [
IODescription('loss', [], torch.float32),
IODescription('logits', ['batch', 2], torch.float32)])
IODescription('input_ids', ['batch', 'max_seq_len_in_batch']),
IODescription('attention_mask', ['batch', 'max_seq_len_in_batch']),
IODescription('token_type_ids', ['batch', 'max_seq_len_in_batch']),
IODescription('labels', ['batch',])], [
IODescription('loss', []),
IODescription('logits', ['batch', 2])])
elif model_name.startswith('roberta'):
new_model_desc = {
'inputs': [
@ -161,11 +183,11 @@ class ORTGlueTest(unittest.TestCase):
'outputs': [('loss', [], True),
('logits', ['batch', 2])]}
model_desc = ModelDescription([
IODescription('input_ids', ['batch', 'max_seq_len_in_batch'], torch.int64, num_classes=model.config.vocab_size),
IODescription('attention_mask', ['batch', 'max_seq_len_in_batch'], torch.int64, num_classes=2),
IODescription('labels', ['batch',], torch.int64, num_classes=2)], [
IODescription('loss', [], torch.float32),
IODescription('logits', ['batch', 2], torch.float32)])
IODescription('input_ids', ['batch', 'max_seq_len_in_batch']),
IODescription('attention_mask', ['batch', 'max_seq_len_in_batch']),
IODescription('labels', ['batch',])], [
IODescription('loss', []),
IODescription('logits', ['batch', 2])])
else:
raise RuntimeError("unsupported base model name {}.".format(model_name))

View file

@ -29,6 +29,7 @@ from orttraining_transformer_trainer import ORTTransformerTrainer
import torch
from utils_multiple_choice import MultipleChoiceDataset, Split, SwagProcessor
from orttraining_run_glue import verify_old_and_new_api_are_equal
logger = logging.getLogger(__name__)
@ -78,7 +79,7 @@ class ORTMultipleChoiceTest(unittest.TestCase):
def setUp(self):
# configurations not to be changed accoss tests
self.max_seq_length = 80
self.train_batch_size = 2
self.train_batch_size = 16
self.eval_batch_size = 2
self.learning_rate = 2e-5
self.num_train_epochs = 1.0
@ -91,22 +92,34 @@ class ORTMultipleChoiceTest(unittest.TestCase):
self.logging_steps = 10
def test_bert_with_swag(self):
expected_acc = 0.7752174347695692
expected_loss = 0.5848011111033019
expected_acc = 0.7640207937618715
expected_loss = 0.6234657892213054
results_per_api = dict()
for use_new_api in [False, True]:
results = self.run_multiple_choice(model_name="bert-base-cased", task_name="swag", fp16=False, use_new_api=use_new_api)
# assert_allclose(results['acc'], expected_acc)
# assert_allclose(results['loss'], expected_loss)
results_per_api[use_new_api] = results
results = self.run_multiple_choice(model_name="bert-base-cased", task_name="swag", fp16=False)
assert_allclose(results['acc'], expected_acc)
assert_allclose(results['loss'], expected_loss)
verify_old_and_new_api_are_equal(results_per_api)
def test_bert_fp16_with_swag(self):
expected_acc = 0.7765170448865341
expected_loss = 0.5851960482903881
# larger batch can be handled with mixed precision
self.train_batch_size = 32
results = self.run_multiple_choice(model_name="bert-base-cased", task_name="swag", fp16=True)
assert_allclose(results['acc'], expected_acc)
assert_allclose(results['loss'], expected_loss)
expected_acc = 0.7482255323402979
expected_loss = 0.6665529619455844
def run_multiple_choice(self, model_name, task_name, fp16):
results_per_api = dict()
for use_new_api in [False, True]:
results = self.run_multiple_choice(model_name="bert-base-cased", task_name="swag", fp16=True, use_new_api=use_new_api)
assert_allclose(results['acc'], expected_acc)
assert_allclose(results['loss'], expected_loss)
results_per_api[use_new_api] = results
verify_old_and_new_api_are_equal(results_per_api)
def run_multiple_choice(self, model_name, task_name, fp16, use_new_api):
model_args = ModelArguments(model_name_or_path=model_name, cache_dir=self.cache_dir)
data_args = DataTrainingArguments(task_name=task_name, data_dir=self.data_dir,
max_seq_length=self.max_seq_length)
@ -150,6 +163,7 @@ class ORTMultipleChoiceTest(unittest.TestCase):
finetuning_task=data_args.task_name,
cache_dir=model_args.cache_dir,
)
tokenizer = AutoTokenizer.from_pretrained(
model_args.tokenizer_name if model_args.tokenizer_name else model_args.model_name_or_path,
cache_dir=model_args.cache_dir,
@ -196,29 +210,45 @@ class ORTMultipleChoiceTest(unittest.TestCase):
if model_name.startswith('bert'):
model_desc = ModelDescription([
IODescription('input_ids', [self.train_batch_size, num_labels, data_args.max_seq_length], torch.int64, num_classes=model.config.vocab_size),
IODescription('attention_mask', [self.train_batch_size, num_labels, data_args.max_seq_length], torch.int64, num_classes=2),
IODescription('token_type_ids', [self.train_batch_size, num_labels, data_args.max_seq_length], torch.int64, num_classes=2),
IODescription('labels', [self.train_batch_size, num_labels], torch.int64, num_classes=num_labels)], [
IODescription('loss', [], torch.float32),
IODescription('reshaped_logits', [self.train_batch_size, num_labels], torch.float32)])
IODescription('input_ids', ['batch', num_labels, 'max_seq_len_in_batch']),
IODescription('attention_mask', ['batch', num_labels, 'max_seq_len_in_batch']),
IODescription('token_type_ids', ['batch', num_labels, 'max_seq_len_in_batch']),
IODescription('labels', ['batch', num_labels])], [
IODescription('loss', []),
IODescription('reshaped_logits', ['batch', num_labels])])
new_model_desc = {
'inputs': [
('input_ids', ['batch', num_labels, 'max_seq_len_in_batch'],),
('attention_mask', ['batch', num_labels, 'max_seq_len_in_batch'],),
('token_type_ids', ['batch', num_labels, 'max_seq_len_in_batch'],),
('labels', ['batch', num_labels],)],
'outputs': [('loss', [], True),
('reshaped_logits', ['batch', num_labels])]}
else:
model_desc = ModelDescription([
IODescription('input_ids', ['batch', num_labels, 'max_seq_len_in_batch'], torch.int64, num_classes=model.config.vocab_size),
IODescription('attention_mask', ['batch', num_labels, 'max_seq_len_in_batch'], torch.int64, num_classes=2),
IODescription('labels', ['batch', num_labels], torch.int64, num_classes=num_labels)], [
IODescription('loss', [], torch.float32),
IODescription('reshaped_logits', ['batch', num_labels], torch.float32)])
IODescription('input_ids', ['batch', num_labels, 'max_seq_len_in_batch']),
IODescription('attention_mask', ['batch', num_labels, 'max_seq_len_in_batch']),
IODescription('labels', ['batch', num_labels])], [
IODescription('loss', []),
IODescription('reshaped_logits', ['batch', num_labels])])
new_model_desc = {
'inputs': [
('input_ids', ['batch', num_labels, 'max_seq_len_in_batch'],),
('attention_mask', ['batch', num_labels, 'max_seq_len_in_batch'],),
('labels', ['batch', num_labels],)],
'outputs': [('loss', [], True),
('reshaped_logits', ['batch', num_labels])]}
# Initialize the ORTTrainer within ORTTransformerTrainer
trainer = ORTTransformerTrainer(
model=model,
model_desc=model_desc,
new_model_desc=None,
new_model_desc=new_model_desc,
args=training_args,
train_dataset=train_dataset,
eval_dataset=eval_dataset,
compute_metrics=compute_metrics,
use_new_api=use_new_api
)
# Training

View file

@ -178,7 +178,9 @@ class ORTTransformerTrainer:
loss_scaler = amp.DynamicLossScaler() if self.args.fp16 else None
device = self.args.device.type
device = f'{device}:{self.args.device.index}' if self.args.device.index else f'{device}:0'
options = orttrainer.ORTTrainerOptions({'device': {'id': device},
options = orttrainer.ORTTrainerOptions({'batch' : {
'gradient_accumulation_steps' : self.args.gradient_accumulation_steps},
'device': {'id': device},
'mixed_precision': {
'enabled': self.args.fp16,
'loss_scaler': loss_scaler},