Skip to content
Merged
Show file tree
Hide file tree
Changes from 54 commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
32392af
Add AdamW deprecation warning
manuelciosici Dec 10, 2021
c637f37
Add --optim to Trainer
manuelciosici Dec 10, 2021
b4d0b6d
Update src/transformers/optimization.py
manuelciosici Dec 13, 2021
bcc2408
Update src/transformers/optimization.py
manuelciosici Dec 13, 2021
460eff4
Update src/transformers/optimization.py
manuelciosici Dec 13, 2021
6dc78a6
Update src/transformers/optimization.py
manuelciosici Dec 13, 2021
68dd581
Update src/transformers/training_args.py
manuelciosici Dec 13, 2021
9560350
Update src/transformers/training_args.py
manuelciosici Dec 13, 2021
01f1c7b
Update src/transformers/training_args.py
stas00 Dec 27, 2021
0c79a5f
Merge remote-tracking branch 'origin/master' into deprecate_adamw
stas00 Dec 27, 2021
7ec094f
fix style
stas00 Dec 27, 2021
1c9cccf
fix
stas00 Dec 29, 2021
9807d35
Regroup adamws together
manuelciosici Dec 30, 2021
7a063ab
Change --adafactor to --optim adafactor
manuelciosici Dec 30, 2021
d599a38
Use Enum for optimizer values
manuelciosici Dec 30, 2021
1f9210c
fixup! Change --adafactor to --optim adafactor
manuelciosici Dec 30, 2021
a80b39e
fixup! Change --adafactor to --optim adafactor
manuelciosici Dec 30, 2021
fdf40b2
fixup! Change --adafactor to --optim adafactor
manuelciosici Dec 30, 2021
d5dc69a
Merge branch 'master' into deprecate_adamw
manuelciosici Dec 30, 2021
0acba0c
fixup! Use Enum for optimizer values
manuelciosici Dec 30, 2021
2b7d9dd
Improved documentation for --adafactor
manuelciosici Dec 31, 2021
7c3139a
Add mention of no_deprecation_warning
manuelciosici Dec 31, 2021
234f7d1
Rename OptimizerOptions to OptimizerNames
manuelciosici Dec 31, 2021
1786d42
Use choices for --optim
manuelciosici Dec 31, 2021
210ed37
Move optimizer selection code to a function and add a unit test
manuelciosici Dec 31, 2021
7e62da9
Change optimizer names
manuelciosici Dec 31, 2021
0e7f955
Rename method
manuelciosici Jan 1, 2022
12a9e37
Rename method
manuelciosici Jan 1, 2022
c5853b0
Remove TODO comment
manuelciosici Jan 1, 2022
d59aa52
Rename variable
manuelciosici Jan 1, 2022
e7ffd71
Rename variable
manuelciosici Jan 1, 2022
b64fc03
Rename function
manuelciosici Jan 1, 2022
c5b5443
Rename variable
manuelciosici Jan 1, 2022
91aff78
Parameterize the tests for supported optimizers
manuelciosici Jan 1, 2022
f3505db
Refactor
manuelciosici Jan 1, 2022
91c35f2
Attempt to make tests pass on CircleCI
manuelciosici Jan 1, 2022
bcd8a0d
Add a test with apex
manuelciosici Jan 2, 2022
f8cb39c
rework to add apex to parameterized; add actual train test
stas00 Jan 2, 2022
98f0f2f
fix import when torch is not available
stas00 Jan 2, 2022
eba41bd
fix optim_test_params when torch is not available
stas00 Jan 2, 2022
aaee305
fix optim_test_params when torch is not available
stas00 Jan 2, 2022
071198c
re-org
stas00 Jan 2, 2022
182dac8
small re-org
stas00 Jan 2, 2022
2b46361
fix test_fused_adam_no_apex
stas00 Jan 2, 2022
470a1d7
Update src/transformers/training_args.py
manuelciosici Jan 12, 2022
cb85474
Update src/transformers/training_args.py
manuelciosici Jan 12, 2022
b2675f8
Update src/transformers/training_args.py
manuelciosici Jan 12, 2022
1e8acec
Remove .value from OptimizerNames
manuelciosici Jan 12, 2022
b32a194
Rename optimizer strings s|--adam_|--adamw_|
manuelciosici Jan 12, 2022
b839e80
Also rename Enum options
manuelciosici Jan 12, 2022
e73249c
small fix
stas00 Jan 12, 2022
7ac8dc0
Fix instantiation of OptimizerNames. Remove redundant test
manuelciosici Jan 12, 2022
a2363cd
Use ExplicitEnum instead of Enum
manuelciosici Jan 12, 2022
ea02877
Add unit test with string optimizer
manuelciosici Jan 12, 2022
ec92011
Change optimizer default to string value
manuelciosici Jan 13, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/transformers/optimization.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"""PyTorch optimization for BERT model."""

import math
import warnings
from typing import Callable, Iterable, Optional, Tuple, Union

import torch
Expand Down Expand Up @@ -287,6 +288,8 @@ class AdamW(Optimizer):
Decoupled weight decay to apply.
correct_bias (`bool`, *optional*, defaults to `True`):
Whether or not to correct bias in Adam (for instance, in Bert TF repository they use `False`).
no_deprecation_warning (`bool`, *optional*, defaults to `False`):
A flag used to disable the deprecation warning (set to `True` to disable the warning).
"""

def __init__(
Expand All @@ -297,7 +300,14 @@ def __init__(
eps: float = 1e-6,
weight_decay: float = 0.0,
correct_bias: bool = True,
no_deprecation_warning: bool = False,
):
if not no_deprecation_warning:
warnings.warn(
"This implementation of AdamW is deprecated and will be removed in a future version. Use the"
"PyTorch implementation torch.optim.AdamW instead, or set `no_deprecation_warning=True` to disable this warning",
FutureWarning,
)
require_version("torch>=1.5.0") # add_ with alpha
if lr < 0.0:
raise ValueError(f"Invalid learning rate: {lr} - should be >= 0.0")
Expand Down
58 changes: 45 additions & 13 deletions src/transformers/trainer.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@
from .modelcard import TrainingSummary
from .modeling_utils import PreTrainedModel, unwrap_model
from .models.auto.modeling_auto import MODEL_FOR_QUESTION_ANSWERING_MAPPING_NAMES
from .optimization import Adafactor, AdamW, get_scheduler
from .optimization import Adafactor, get_scheduler
from .tokenization_utils_base import PreTrainedTokenizerBase
from .trainer_callback import (
CallbackHandler,
Expand Down Expand Up @@ -128,7 +128,7 @@
set_seed,
speed_metrics,
)
from .training_args import ParallelMode, TrainingArguments
from .training_args import OptimizerNames, ParallelMode, TrainingArguments
from .utils import logging


Expand Down Expand Up @@ -819,17 +819,9 @@ def create_optimizer(self):
"weight_decay": 0.0,
},
]
optimizer_cls = Adafactor if self.args.adafactor else AdamW
if self.args.adafactor:
optimizer_cls = Adafactor
optimizer_kwargs = {"scale_parameter": False, "relative_step": False}
else:
optimizer_cls = AdamW
optimizer_kwargs = {
"betas": (self.args.adam_beta1, self.args.adam_beta2),
"eps": self.args.adam_epsilon,
}
optimizer_kwargs["lr"] = self.args.learning_rate

optimizer_cls, optimizer_kwargs = Trainer.get_optimizer_cls_and_kwargs(self.args)

if self.sharded_ddp == ShardedDDPOption.SIMPLE:
self.optimizer = OSS(
params=optimizer_grouped_parameters,
Expand All @@ -844,6 +836,46 @@ def create_optimizer(self):

return self.optimizer

@staticmethod
def get_optimizer_cls_and_kwargs(args: TrainingArguments) -> Tuple[Any, Any]:
"""
Returns the optimizer class and optimizer parameters based on the training arguments.

Args:
args (`transformers.training_args.TrainingArguments`):
The training arguments for the training session.

"""
optimizer_kwargs = {"lr": args.learning_rate}
adam_kwargs = {
"betas": (args.adam_beta1, args.adam_beta2),
"eps": args.adam_epsilon,
}
if args.optim == OptimizerNames.ADAFACTOR:
optimizer_cls = Adafactor
optimizer_kwargs.update({"scale_parameter": False, "relative_step": False})
elif args.optim == OptimizerNames.ADAMW_HF:
from .optimization import AdamW

optimizer_cls = AdamW
optimizer_kwargs.update(adam_kwargs)
elif args.optim == OptimizerNames.ADAMW_TORCH:
from torch.optim import AdamW

optimizer_cls = AdamW
optimizer_kwargs.update(adam_kwargs)
elif args.optim == OptimizerNames.ADAMW_APEX_FUSED:
try:
from apex.optimizers import FusedAdam

optimizer_cls = FusedAdam
optimizer_kwargs.update(adam_kwargs)
except ImportError:
raise ValueError("Trainer tried to instantiate apex FusedAdam but apex is not installed!")
else:
raise ValueError(f"Trainer cannot instantiate unsupported optimizer: {args.optim}")
return optimizer_cls, optimizer_kwargs

def create_scheduler(self, num_training_steps: int, optimizer: torch.optim.Optimizer = None):
"""
Setup the scheduler. The optimizer of the trainer must have been set up either before this method is called or
Expand Down
29 changes: 28 additions & 1 deletion src/transformers/training_args.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

from .debug_utils import DebugOption
from .file_utils import (
ExplicitEnum,
cached_property,
get_full_repo_name,
is_sagemaker_dp_enabled,
Expand Down Expand Up @@ -69,6 +70,17 @@ def default_logdir() -> str:
return os.path.join("runs", current_time + "_" + socket.gethostname())


class OptimizerNames(ExplicitEnum):
"""
Stores the acceptable string identifiers for optimizers.
"""

ADAMW_HF = "adamw_hf"
ADAMW_TORCH = "adamw_torch"
ADAMW_APEX_FUSED = "adamw_apex_fused"
ADAFACTOR = "adafactor"


@dataclass
class TrainingArguments:
"""
Expand Down Expand Up @@ -327,8 +339,10 @@ class TrainingArguments:
- `"tpu_metrics_debug"`: print debug metrics on TPU

The options should be separated by whitespaces.
optim (`str` or [`training_args.OptimizerNames`], *optional*, defaults to `"adamw_hf"`):
The optimizer to use: adamw_hf, adamw_torch, adamw_apex_fused, or adafactor.
adafactor (`bool`, *optional*, defaults to `False`):
Whether or not to use the [`Adafactor`] optimizer instead of [`AdamW`].
This argument is deprecated. Use `--optim adafactor` instead.
group_by_length (`bool`, *optional*, defaults to `False`):
Whether or not to group together samples of roughly the same length in the training dataset (to minimize
padding applied and be more efficient). Only useful if applying dynamic padding.
Expand Down Expand Up @@ -641,6 +655,10 @@ class TrainingArguments:
label_smoothing_factor: float = field(
default=0.0, metadata={"help": "The label smoothing epsilon to apply (zero means no label smoothing)."}
)
optim: OptimizerNames = field(
default=OptimizerNames.ADAMW_HF.value,
Comment thread
stas00 marked this conversation as resolved.
Outdated
metadata={"help": "The optimizer to use."},
)
adafactor: bool = field(default=False, metadata={"help": "Whether or not to replace AdamW by Adafactor."})
group_by_length: bool = field(
default=False,
Expand Down Expand Up @@ -809,6 +827,15 @@ def __post_init__(self):
)
if not (self.sharded_ddp == "" or not self.sharded_ddp):
raise ValueError("sharded_ddp is not supported with bf16")

self.optim = OptimizerNames(self.optim)
if self.adafactor:
warnings.warn(
Comment thread
stas00 marked this conversation as resolved.
"`--adafactor` is deprecated and will be removed in version 5 of 🤗 Transformers. Use `--optim adafactor` instead",
FutureWarning,
)
Comment thread
stas00 marked this conversation as resolved.
self.optim = OptimizerNames.ADAFACTOR

Comment thread
stas00 marked this conversation as resolved.
if (
is_torch_available()
and self.device.type != "cuda"
Expand Down
101 changes: 100 additions & 1 deletion tests/test_trainer.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@
import tempfile
import unittest
from pathlib import Path
from unittest.mock import Mock, patch

import numpy as np

from huggingface_hub import Repository, delete_repo, login
from parameterized import parameterized
from requests.exceptions import HTTPError
from transformers import (
AutoTokenizer,
Expand All @@ -36,7 +38,7 @@
is_torch_available,
logging,
)
from transformers.file_utils import WEIGHTS_NAME
from transformers.file_utils import WEIGHTS_NAME, is_apex_available
from transformers.testing_utils import (
ENDPOINT_STAGING,
PASS,
Expand All @@ -61,6 +63,7 @@
slow,
)
from transformers.trainer_utils import PREFIX_CHECKPOINT_DIR
from transformers.training_args import OptimizerNames
from transformers.utils.hp_naming import TrialShortNamer


Expand All @@ -69,6 +72,7 @@
from torch import nn
from torch.utils.data import IterableDataset

import transformers.optimization
from transformers import (
AutoModelForSequenceClassification,
EarlyStoppingCallback,
Expand Down Expand Up @@ -1690,3 +1694,98 @@ def hp_name(trial):
trainer.hyperparameter_search(
direction="minimize", hp_space=hp_space, hp_name=hp_name, backend="sigopt", n_trials=4
)


optim_test_params = []
if is_torch_available():
default_adam_kwargs = {
"betas": (TrainingArguments.adam_beta1, TrainingArguments.adam_beta2),
"eps": TrainingArguments.adam_epsilon,
"lr": TrainingArguments.learning_rate,
}

optim_test_params = [
(
OptimizerNames.ADAMW_HF,
transformers.optimization.AdamW,
default_adam_kwargs,
),
(
OptimizerNames.ADAMW_HF.value,
transformers.optimization.AdamW,
default_adam_kwargs,
),
(
OptimizerNames.ADAMW_TORCH,
torch.optim.AdamW,
default_adam_kwargs,
),
(
OptimizerNames.ADAFACTOR,
transformers.optimization.Adafactor,
{
"scale_parameter": False,
"relative_step": False,
"lr": TrainingArguments.learning_rate,
},
),
]
if is_apex_available():
import apex

optim_test_params.append(
(
OptimizerNames.ADAMW_APEX_FUSED,
apex.optimizers.FusedAdam,
default_adam_kwargs,
)
)


@require_torch
class TrainerOptimizerChoiceTest(unittest.TestCase):
def check_optim_and_kwargs(self, optim: OptimizerNames, mandatory_kwargs, expected_cls):
args = TrainingArguments(optim=optim, output_dir="None")
actual_cls, optim_kwargs = Trainer.get_optimizer_cls_and_kwargs(args)
self.assertEqual(expected_cls, actual_cls)
self.assertIsNotNone(optim_kwargs)

for p, v in mandatory_kwargs.items():
self.assertTrue(p in optim_kwargs)
actual_v = optim_kwargs[p]
self.assertTrue(actual_v == v, f"Failed check for {p}. Expected {v}, but got {actual_v}.")

@parameterized.expand(optim_test_params, skip_on_empty=True)
def test_optim_supported(self, name: str, expected_cls, mandatory_kwargs):
# exercises all the valid --optim options
self.check_optim_and_kwargs(name, mandatory_kwargs, expected_cls)

trainer = get_regression_trainer(optim=name)
trainer.train()

def test_fused_adam(self):
Comment thread
stas00 marked this conversation as resolved.
# Pretend that apex is installed and mock apex.optimizers.FusedAdam exists.
# Trainer.get_optimizer_cls_and_kwargs does not use FusedAdam, but only has to return a
# class called, so mocking apex.optimizers.FusedAdam should be fine for testing and allow
# the test to run without requiring an apex installation.
mock = Mock()
modules = {
"apex": mock,
"apex.optimizers": mock.optimizers,
"apex.optimizers.FusedAdam": mock.optimizers.FusedAdam,
}
with patch.dict("sys.modules", modules):
self.check_optim_and_kwargs(
OptimizerNames.ADAMW_APEX_FUSED,
default_adam_kwargs,
mock.optimizers.FusedAdam,
)

def test_fused_adam_no_apex(self):
args = TrainingArguments(optim=OptimizerNames.ADAMW_APEX_FUSED, output_dir="None")

# Pretend that apex does not exist, even if installed. By setting apex to None, importing
# apex will fail even if apex is installed.
with patch.dict("sys.modules", {"apex.optimizers": None}):
with self.assertRaises(ValueError):
Trainer.get_optimizer_cls_and_kwargs(args)