[Integration] add swanlab logger (#10594)

* feat: add swanlabcallback

* fix run

* fix lint

* fix requirements dev

* fix url
This commit is contained in:
Ze-Yi LIN
2025-05-27 11:25:16 +08:00
committed by GitHub
parent c418bba09c
commit 062debf2aa
4 changed files with 119 additions and 1 deletions

View File

@@ -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,
} }

View File

@@ -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

View File

@@ -18,6 +18,7 @@ rouge
tiktoken tiktoken
visualdl visualdl
wandb wandb
swanlab
tensorboard tensorboard
tensorboardX tensorboardX
modelscope modelscope

View File

@@ -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()