[Integration] add swanlab logger (#10594)
* feat: add swanlabcallback * fix run * fix lint * fix requirements dev * fix url
This commit is contained in:
@@ -43,6 +43,10 @@ def is_wandb_available():
|
|||||||
return importlib.util.find_spec("wandb") is not None
|
return importlib.util.find_spec("wandb") is not None
|
||||||
|
|
||||||
|
|
||||||
|
def is_swanlab_available():
|
||||||
|
return importlib.util.find_spec("swanlab") is not None
|
||||||
|
|
||||||
|
|
||||||
def is_ray_available():
|
def is_ray_available():
|
||||||
return importlib.util.find_spec("ray.air") is not None
|
return importlib.util.find_spec("ray.air") is not None
|
||||||
|
|
||||||
@@ -55,6 +59,8 @@ def get_available_reporting_integrations():
|
|||||||
integrations.append("wandb")
|
integrations.append("wandb")
|
||||||
if is_tensorboardX_available():
|
if is_tensorboardX_available():
|
||||||
integrations.append("tensorboard")
|
integrations.append("tensorboard")
|
||||||
|
if is_swanlab_available():
|
||||||
|
integrations.append("swanlab")
|
||||||
|
|
||||||
return integrations
|
return integrations
|
||||||
|
|
||||||
@@ -395,6 +401,85 @@ class WandbCallback(TrainerCallback):
|
|||||||
self._wandb.log_artifact(artifact, aliases=[f"checkpoint-{state.global_step}"])
|
self._wandb.log_artifact(artifact, aliases=[f"checkpoint-{state.global_step}"])
|
||||||
|
|
||||||
|
|
||||||
|
class SwanLabCallback(TrainerCallback):
|
||||||
|
"""
|
||||||
|
A [`TrainerCallback`] that logs metrics, media to [Swanlab](https://swanlab.cn/).
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
has_swanlab = is_swanlab_available()
|
||||||
|
if not has_swanlab:
|
||||||
|
raise RuntimeError("SwanlabCallback requires swanlab to be installed. Run `pip install swanlab`.")
|
||||||
|
if has_swanlab:
|
||||||
|
import swanlab
|
||||||
|
|
||||||
|
self._swanlab = swanlab
|
||||||
|
|
||||||
|
self._initialized = False
|
||||||
|
|
||||||
|
def setup(self, args, state, model, **kwargs):
|
||||||
|
"""
|
||||||
|
Setup the optional Swanlab integration.
|
||||||
|
|
||||||
|
One can subclass and override this method to customize the setup if needed.
|
||||||
|
variables:
|
||||||
|
Environment:
|
||||||
|
- **SWANLAB_MODE** (`str`, *optional*, defaults to `"cloud"`):
|
||||||
|
Whether to use swanlab cloud, local or disabled. Set `SWANLAB_MODE="local"` to use local. Set `SWANLAB_MODE="disabled"` to disable.
|
||||||
|
- **SWANLAB_PROJECT** (`str`, *optional*, defaults to `"PaddleNLP"`):
|
||||||
|
Set this to a custom string to store results in a different project.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self._swanlab is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
self._initialized = True
|
||||||
|
|
||||||
|
if state.is_world_process_zero:
|
||||||
|
logger.info('Automatic Swanlab logging enabled, to disable set os.environ["SWANLAB_MODE"] = "disabled"')
|
||||||
|
|
||||||
|
combined_dict = {**args.to_dict()}
|
||||||
|
|
||||||
|
if hasattr(model, "config") and model.config is not None:
|
||||||
|
model_config = model.config.to_dict()
|
||||||
|
combined_dict = {**model_config, **combined_dict}
|
||||||
|
|
||||||
|
trial_name = state.trial_name
|
||||||
|
init_args = {}
|
||||||
|
if trial_name is not None:
|
||||||
|
init_args["name"] = trial_name
|
||||||
|
init_args["group"] = args.run_name
|
||||||
|
else:
|
||||||
|
if not (args.run_name is None or args.run_name == args.output_dir):
|
||||||
|
init_args["name"] = args.run_name
|
||||||
|
init_args["dir"] = args.logging_dir
|
||||||
|
if self._swanlab.get_run() is None:
|
||||||
|
self._swanlab.init(
|
||||||
|
project=os.getenv("SWANLAB_PROJECT", "PaddleNLP"),
|
||||||
|
**init_args,
|
||||||
|
)
|
||||||
|
self._swanlab.config.update(combined_dict, allow_val_change=True)
|
||||||
|
|
||||||
|
def on_train_begin(self, args, state, control, model=None, **kwargs):
|
||||||
|
if self._swanlab is None:
|
||||||
|
return
|
||||||
|
if not self._initialized:
|
||||||
|
self.setup(args, state, model, **kwargs)
|
||||||
|
|
||||||
|
def on_train_end(self, args, state, control, model=None, tokenizer=None, **kwargs):
|
||||||
|
if self._swanlab is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
def on_log(self, args, state, control, model=None, logs=None, **kwargs):
|
||||||
|
if self._swanlab is None:
|
||||||
|
return
|
||||||
|
if not self._initialized:
|
||||||
|
self.setup(args, state, model)
|
||||||
|
if state.is_world_process_zero:
|
||||||
|
logs = rewrite_logs(logs)
|
||||||
|
self._swanlab.log({**logs, "train/global_step": state.global_step}, step=state.global_step)
|
||||||
|
|
||||||
|
|
||||||
class AutoNLPCallback(TrainerCallback):
|
class AutoNLPCallback(TrainerCallback):
|
||||||
"""
|
"""
|
||||||
A [`TrainerCallback`] that sends the logs to [`Ray Tune`] for [`AutoNLP`]
|
A [`TrainerCallback`] that sends the logs to [`Ray Tune`] for [`AutoNLP`]
|
||||||
@@ -423,6 +508,7 @@ INTEGRATION_TO_CALLBACK = {
|
|||||||
"autonlp": AutoNLPCallback,
|
"autonlp": AutoNLPCallback,
|
||||||
"wandb": WandbCallback,
|
"wandb": WandbCallback,
|
||||||
"tensorboard": TensorBoardCallback,
|
"tensorboard": TensorBoardCallback,
|
||||||
|
"swanlab": SwanLabCallback,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -376,7 +376,7 @@ class TrainingArguments:
|
|||||||
instance of `Dataset`.
|
instance of `Dataset`.
|
||||||
report_to (`str` or `List[str]`, *optional*, defaults to `"visualdl"`):
|
report_to (`str` or `List[str]`, *optional*, defaults to `"visualdl"`):
|
||||||
The list of integrations to report the results and logs to.
|
The list of integrations to report the results and logs to.
|
||||||
Supported platforms are `"visualdl"`/`"wandb"`/`"tensorboard"`.
|
Supported platforms are `"visualdl"`/`"wandb"`/`"tensorboard"`/`"swanlab"`.
|
||||||
`"none"` for no integrations.
|
`"none"` for no integrations.
|
||||||
ddp_find_unused_parameters (`bool`, *optional*):
|
ddp_find_unused_parameters (`bool`, *optional*):
|
||||||
When using distributed training, the value of the flag `find_unused_parameters` passed to
|
When using distributed training, the value of the flag `find_unused_parameters` passed to
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ rouge
|
|||||||
tiktoken
|
tiktoken
|
||||||
visualdl
|
visualdl
|
||||||
wandb
|
wandb
|
||||||
|
swanlab
|
||||||
tensorboard
|
tensorboard
|
||||||
tensorboardX
|
tensorboardX
|
||||||
modelscope
|
modelscope
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ from visualdl import LogReader
|
|||||||
|
|
||||||
from paddlenlp.trainer import TrainerControl, TrainerState, TrainingArguments
|
from paddlenlp.trainer import TrainerControl, TrainerState, TrainingArguments
|
||||||
from paddlenlp.trainer.integrations import (
|
from paddlenlp.trainer.integrations import (
|
||||||
|
SwanLabCallback,
|
||||||
TensorBoardCallback,
|
TensorBoardCallback,
|
||||||
VisualDLCallback,
|
VisualDLCallback,
|
||||||
WandbCallback,
|
WandbCallback,
|
||||||
@@ -66,6 +67,36 @@ class TestWandbCallback(unittest.TestCase):
|
|||||||
shutil.rmtree(output_dir)
|
shutil.rmtree(output_dir)
|
||||||
|
|
||||||
|
|
||||||
|
class TestSwanlabCallback(unittest.TestCase):
|
||||||
|
def test_swanlabcallback(self):
|
||||||
|
output_dir = tempfile.mkdtemp()
|
||||||
|
args = TrainingArguments(
|
||||||
|
output_dir=output_dir,
|
||||||
|
max_steps=200,
|
||||||
|
logging_steps=20,
|
||||||
|
run_name="test_swanlabcallback",
|
||||||
|
logging_dir=output_dir,
|
||||||
|
)
|
||||||
|
state = TrainerState(trial_name="PaddleNLP")
|
||||||
|
control = TrainerControl()
|
||||||
|
config = RegressionModelConfig(a=1, b=1)
|
||||||
|
model = RegressionPretrainedModel(config)
|
||||||
|
os.environ["SWANLAB_MODE"] = "disabled"
|
||||||
|
swanlabcallback = SwanLabCallback()
|
||||||
|
self.assertFalse(swanlabcallback._initialized)
|
||||||
|
swanlabcallback.on_train_begin(args, state, control)
|
||||||
|
self.assertTrue(swanlabcallback._initialized)
|
||||||
|
for global_step in range(args.max_steps):
|
||||||
|
state.global_step = global_step
|
||||||
|
if global_step % args.logging_steps == 0:
|
||||||
|
log = {"loss": 100 - 0.4 * global_step, "learning_rate": 0.1, "global_step": global_step}
|
||||||
|
swanlabcallback.on_log(args, state, control, logs=log)
|
||||||
|
swanlabcallback.on_train_end(args, state, control, model=model)
|
||||||
|
swanlabcallback._swanlab.finish()
|
||||||
|
os.environ.pop("SWANLAB_MODE", None)
|
||||||
|
shutil.rmtree(output_dir)
|
||||||
|
|
||||||
|
|
||||||
class TestTensorboardCallback(unittest.TestCase):
|
class TestTensorboardCallback(unittest.TestCase):
|
||||||
def test_tbcallback(self):
|
def test_tbcallback(self):
|
||||||
output_dir = tempfile.mkdtemp()
|
output_dir = tempfile.mkdtemp()
|
||||||
|
|||||||
Reference in New Issue
Block a user