Files
2026-07-13 13:18:33 +08:00

777 lines
32 KiB
Python

# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0
# DeepSpeed Team
import deepspeed
from types import SimpleNamespace
from deepspeed.ops.op_builder import CPUAdamBuilder
from deepspeed.checkpoint.utils import clone_tensors_for_torch_save, get_model_ckpt_name_for_rank
from deepspeed.accelerator import get_accelerator
from deepspeed.runtime.zero import ZeroParamStatus
from deepspeed.runtime.zero.config import DeepSpeedZeroConfig
from deepspeed.utils.torch import required_torch_version
from unit.common import DistributedTest, DistributedFixture
from unit.simple_model import *
from unit.checkpoint.common import *
import pytest
class TestZeROCheckpoint(DistributedTest):
world_size = 2
@pytest.mark.parametrize('zero_stage', [3])
def test_pipeline_checkpoint_loading(self, tmpdir, zero_stage):
config_dict = {
"train_batch_size": 2,
"optimizer": {
"type": 'Adam'
},
"zero_optimization": {
"stage": zero_stage,
"pipeline_loading_checkpoint": True,
}
}
if get_accelerator().is_bf16_supported():
config_dict["bf16"] = {"enabled": True}
elif get_accelerator().is_fp16_supported():
config_dict["fp16"] = {"enabled": True, "initial_scale_power": 8}
hidden_dim = 10
with deepspeed.zero.Init(config_dict_or_path=config_dict):
models = [SimpleModel(hidden_dim, empty_grad=False) for _ in range(2)]
checkpoint_correctness_verification(config_dict, models, hidden_dim, tmpdir, load_module_only=True)
@pytest.mark.parametrize('zero_stage, use_cpu_offload, adam_optimizer', [(0, False, 'Adam'), (1, False, 'Adam'),
(2, False, 'Adam'),
(2, True, 'deepspeed_adam'),
(3, False, 'Adam'),
(3, True, 'deepspeed_adam')])
def test_load_optimizer_state(self, tmpdir, zero_stage, use_cpu_offload, adam_optimizer):
if use_cpu_offload and not deepspeed.ops.__compatible_ops__[CPUAdamBuilder.NAME]:
pytest.skip("cpu-adam is not compatible")
config_dict = {
"train_batch_size": 2,
"steps_per_print": 1,
"optimizer": {
"type": 'Adam',
"params": {
"lr": 0.00015,
"betas": [0.8, 0.999],
"eps": 1e-8,
"weight_decay": 3e-7
}
},
"wall_clock_breakdown": True,
"zero_optimization": {
"stage": zero_stage,
"cpu_offload": use_cpu_offload
}
}
if get_accelerator().is_bf16_supported():
config_dict["bf16"] = {"enabled": True}
elif get_accelerator().is_fp16_supported():
config_dict["fp16"] = {"enabled": True, "initial_scale_power": 8}
hidden_dim = 10
if zero_stage == 3:
with deepspeed.zero.Init(config_dict_or_path=config_dict):
models = [SimpleModel(hidden_dim, empty_grad=False) for _ in range(2)]
else:
models = [SimpleModel(hidden_dim, empty_grad=False) for _ in range(2)]
checkpoint_correctness_verification(config_dict, models, hidden_dim, tmpdir, load_optimizer_states=True)
@pytest.mark.parametrize('zero_stage, use_cpu_offload, adam_optimizer', [(1, False, "Adam"), (2, False, "Adam"),
(2, True, 'deepspeed_adam'),
(3, False, 'Adam'),
(3, True, 'deepspeed_adam')])
def test_not_load_optimizer_state(self, tmpdir, zero_stage, use_cpu_offload, adam_optimizer):
if use_cpu_offload and not deepspeed.ops.__compatible_ops__[CPUAdamBuilder.NAME]:
pytest.skip("cpu-adam is not compatible")
config_dict = {
"train_batch_size": 2,
"steps_per_print": 1,
"optimizer": {
"type": 'Adam',
"params": {
"lr": 0.00015,
"betas": [0.8, 0.999],
"eps": 1e-8,
"weight_decay": 3e-7
}
},
"zero_optimization": {
"stage": zero_stage,
"cpu_offload": use_cpu_offload
}
}
if get_accelerator().is_bf16_supported():
config_dict["bf16"] = {"enabled": True}
elif get_accelerator().is_fp16_supported():
config_dict["fp16"] = {"enabled": True, "initial_scale_power": 8}
hidden_dim = 10
if zero_stage == 3:
global DeepSpeedZeroOptimizer_Stage3
from deepspeed.runtime.zero.stage3 import DeepSpeedZeroOptimizer_Stage3
with deepspeed.zero.Init(config_dict_or_path=config_dict):
models = [SimpleModel(hidden_dim, empty_grad=False) for _ in range(2)]
else:
models = [SimpleModel(hidden_dim, empty_grad=False) for _ in range(2)]
checkpoint_correctness_verification(config_dict, models, hidden_dim, tmpdir, load_optimizer_states=False)
@pytest.mark.parametrize('zero_stage', [1, 2])
def test_hybrid_optimizer_state(self, tmpdir, zero_stage):
config_dict = {
"train_micro_batch_size_per_gpu": 2,
"gradient_accumulation_steps": 2,
"steps_per_print": 1,
"zero_optimization": {
"stage": zero_stage
},
"zero_allow_untested_optimizer": True,
}
if get_accelerator().is_bf16_supported():
config_dict["bf16"] = {"enabled": True}
elif get_accelerator().is_fp16_supported():
config_dict["fp16"] = {"enabled": True, "initial_scale_power": 8}
hidden_dim = 10
models = [SimpleModel(hidden_dim=hidden_dim) for _ in range(2)]
optimizers = [HybridStateOptimizer(model.parameters()) for model in models]
checkpoint_correctness_verification(config_dict,
models=models,
base_optimizers=optimizers,
hidden_dim=hidden_dim,
tmpdir=tmpdir,
load_optimizer_states=True)
@pytest.mark.parametrize('zero_stage', [0, 1, 2, 3])
def test_load_module_only(self, tmpdir, zero_stage):
if zero_stage == 0 and get_accelerator().device_name() == "cpu":
pytest.skip("CPU Accelerator does not support this test")
config_dict = {
"train_batch_size": 2,
"optimizer": {
"type": 'Adam'
},
"zero_optimization": {
"stage": zero_stage,
}
}
if get_accelerator().is_bf16_supported():
config_dict["bf16"] = {"enabled": True}
elif get_accelerator().is_fp16_supported():
config_dict["fp16"] = {"enabled": True, "initial_scale_power": 8}
hidden_dim = 10
if zero_stage == 3:
with deepspeed.zero.Init(config_dict_or_path=config_dict):
models = [SimpleModel(hidden_dim, empty_grad=False) for _ in range(2)]
else:
models = [SimpleModel(hidden_dim, empty_grad=False) for _ in range(2)]
checkpoint_correctness_verification(config_dict, models, hidden_dim, tmpdir, load_module_only=True)
class ws4_model_checkpoint(DistributedFixture):
world_size = 4
def run(self, class_tmpdir, elastic_save, load_optim):
config_dict = {
"train_batch_size": 4,
"optimizer": {
"type": 'Adam'
},
"zero_optimization": {
"stage": 2,
"elastic_checkpoint": elastic_save
}
}
if get_accelerator().is_bf16_supported():
config_dict["bf16"] = {"enabled": True}
elif get_accelerator().is_fp16_supported():
config_dict["fp16"] = {"enabled": True, "initial_scale_power": 8}
hidden_dim = 10
model = SimpleModel(hidden_dim)
model, _, _, _ = deepspeed.initialize(config=config_dict, model=model, model_parameters=model.parameters())
data_loader = random_dataloader(model=model, total_samples=8, hidden_dim=hidden_dim, device=model.device)
for n, batch in enumerate(data_loader):
loss = model(batch[0], batch[1])
model.backward(loss)
model.step()
if load_optim:
torch.save(model.optimizer.optimizer.state_dict(), os.path.join(class_tmpdir, 'opt-state-dict'))
model.save_checkpoint(class_tmpdir)
class ws4_model_checkpoint_zeropp(DistributedFixture):
world_size = 4
def run(self, class_tmpdir):
config_dict = {
"train_batch_size": 4,
"optimizer": {
"type": 'Adam'
},
"zero_optimization": {
"stage": 3,
"zero_hpz_partition_size": 2,
}
}
hidden_dim = 10
model = SimpleModel(hidden_dim)
for param in model.parameters():
param.data = torch.ones_like(param.data, device=param.data.device, requires_grad=False)
# save model and zero checkpoint
torch.save(model.state_dict(), os.path.join(class_tmpdir, "model.pt"))
ds_model = create_deepspeed_model(config_dict=config_dict, model=model, base_optimizer=None)
ds_model.save_checkpoint(class_tmpdir)
@pytest.mark.parametrize("elastic_save", [True, False])
@pytest.mark.parametrize("elastic_load", [True, False])
@pytest.mark.parametrize("load_optim", [True, False])
class TestZeROElasticCheckpoint(DistributedTest):
world_size = 2
def test_elastic_checkpoint_fixed_dp(self, tmpdir, elastic_save, elastic_load, load_optim):
config_dict = {
"train_batch_size": 2,
"optimizer": {
"type": 'Adam'
},
"zero_optimization": {
"stage": 2,
"elastic_checkpoint": elastic_save
}
}
if get_accelerator().is_bf16_supported():
config_dict["bf16"] = {"enabled": True}
elif get_accelerator().is_fp16_supported():
config_dict["fp16"] = {"enabled": True, "initial_scale_power": 8}
hidden_dim = 10
# torch 1.2.* stores raw tensor id numbers in checkpoint state which leads to
# false positive mismatches in checkpoint state comparisons.
# Newer torch versions store tensor ids as 0, 1, 2, ...
expected_mismatch_keys = [] if required_torch_version(min_version=1.4) else ['params']
models = [SimpleModel(hidden_dim) for _ in range(2)]
model, _, _, _ = deepspeed.initialize(config=config_dict,
model=models[0],
model_parameters=models[0].parameters())
run_steps = 8
data_loader = random_dataloader(model=model,
total_samples=run_steps,
hidden_dim=hidden_dim,
device=model.device)
for n, batch in enumerate(data_loader):
loss = model(batch[0], batch[1])
model.backward(loss)
model.step()
if load_optim:
opt_state_dict_file = f'opt-state-dict_rank{dist.get_rank()}'
torch.save(model.optimizer.optimizer.state_dict(), os.path.join(tmpdir, opt_state_dict_file))
model.save_checkpoint(tmpdir)
config_dict["zero_optimization"]["elastic_checkpoint"] = elastic_load
model, _, _, _ = deepspeed.initialize(config=config_dict,
model=models[1],
model_parameters=models[1].parameters())
model.load_checkpoint(tmpdir, load_optimizer_states=load_optim)
if load_optim:
saved_sd = torch.load(os.path.join(tmpdir, opt_state_dict_file), weights_only=False)
curr_sd = model.optimizer.optimizer.state_dict()
compare_opt_state_dicts(curr_sd, saved_sd, expected_mismatch_keys)
data_loader = random_dataloader(model=model, total_samples=8, hidden_dim=hidden_dim, device=model.device)
for n, batch in enumerate(data_loader):
loss = model(batch[0], batch[1])
model.backward(loss)
model.step()
def test_elastic_checkpoint_change_dp(self, ws4_model_checkpoint, class_tmpdir, elastic_save, elastic_load,
load_optim):
config_dict = {
"train_batch_size": 4,
"optimizer": {
"type": 'Adam'
},
"zero_optimization": {
"stage": 2,
"elastic_checkpoint": elastic_load
}
}
if get_accelerator().is_bf16_supported():
config_dict["bf16"] = {"enabled": True}
elif get_accelerator().is_fp16_supported():
config_dict["fp16"] = {"enabled": True, "initial_scale_power": 8}
hidden_dim = 10
model = SimpleModel(hidden_dim)
# Load checkpoint with dp world size = 2
model, _, _, _ = deepspeed.initialize(config=config_dict, model=model, model_parameters=model.parameters())
if load_optim:
with pytest.raises(deepspeed.runtime.zero.utils.ZeRORuntimeException):
model.load_checkpoint(class_tmpdir, load_optimizer_states=load_optim)
else:
model.load_checkpoint(class_tmpdir, load_optimizer_states=load_optim)
class TestZeROSaveLoadEdgeCase(DistributedTest):
world_size = 2
@pytest.mark.parametrize('zero_stage', [0, 1, 2, 3])
def test_immediate_save_load(self, tmpdir, zero_stage):
config_dict = {
"train_batch_size": 4,
"optimizer": {
"type": 'Adam'
},
"zero_optimization": {
"stage": zero_stage,
}
}
if get_accelerator().is_bf16_supported():
config_dict["bf16"] = {"enabled": True}
elif get_accelerator().is_fp16_supported():
config_dict["fp16"] = {"enabled": True, "initial_scale_power": 8}
hidden_dim = 10
model = SimpleModel(hidden_dim)
ds_model = create_deepspeed_model(config_dict=config_dict, model=model, base_optimizer=None)
ds_model.save_checkpoint(tmpdir)
ds_model.load_checkpoint(tmpdir,
load_optimizer_states=False,
load_lr_scheduler_states=False,
load_module_only=False)
@pytest.mark.parametrize('zero_stage', [0, 1, 2, 3])
def test_load_immediate_save(self, tmpdir, zero_stage):
if zero_stage == 0 and get_accelerator().device_name() == "cpu":
pytest.skip("CPU Accelerator does not support this test")
config_dict = {
"train_batch_size": 4,
"optimizer": {
"type": 'Adam'
},
"zero_optimization": {
"stage": zero_stage,
}
}
if get_accelerator().is_bf16_supported():
config_dict["bf16"] = {"enabled": True}
elif get_accelerator().is_fp16_supported():
config_dict["fp16"] = {"enabled": True, "initial_scale_power": 8}
hidden_dim = 10
model = SimpleModel(hidden_dim)
# 1. pretrain a model and save it
ds_model = create_deepspeed_model(config_dict=config_dict, model=model, base_optimizer=None)
data_loader = random_dataloader(model=ds_model, total_samples=1, hidden_dim=hidden_dim, device=ds_model.device)
for _, batch in enumerate(data_loader):
loss = ds_model(batch[0], batch[1])
ds_model.backward(loss)
ds_model.step()
ds_model.empty_partition_cache()
ds_model.save_checkpoint(tmpdir)
# 2. load and immediately save a model with a fresh ds engine
ds_model = create_deepspeed_model(config_dict=config_dict, model=model, base_optimizer=None)
ds_model.load_checkpoint(tmpdir,
load_optimizer_states=False,
load_lr_scheduler_states=False,
load_module_only=False)
ds_model.save_checkpoint(tmpdir)
@pytest.mark.parametrize('zero_stage', [0, 1, 2, 3])
def test_save_before_accum_grad_is_done(self, tmpdir, zero_stage):
config_dict = {
"optimizer": {
"type": 'Adam'
},
"zero_optimization": {
"stage": zero_stage,
"stage3_gather_fp16_weights_on_model_save": True,
},
"gradient_accumulation_steps": 2,
"train_micro_batch_size_per_gpu": 1,
"train_batch_size": 4,
}
if get_accelerator().is_bf16_supported():
config_dict["bf16"] = {"enabled": True}
elif get_accelerator().is_fp16_supported():
config_dict["fp16"] = {"enabled": True, "initial_scale_power": 8}
hidden_dim = 10
model = SimpleModel(hidden_dim)
# This test reproduces a bug where one tries to retrieve a 16bit model before grad_accum
# cycle was completed.
# So we config grad_accum=2 and step only once and save_16bit_model
ds_model = create_deepspeed_model(config_dict=config_dict, model=model, base_optimizer=None)
data_loader = random_dataloader(model=ds_model, total_samples=2, hidden_dim=hidden_dim, device=ds_model.device)
batch = next(iter(data_loader))
loss = ds_model(batch[0], batch[1])
ds_model.backward(loss)
ds_model.step()
ds_model.empty_partition_cache()
# we stepped only once, and now save 16bit model before gradient_accumulation_steps=2 is complete
ds_model.save_16bit_model(tmpdir, "model.pt")
# let's test just as well that we can save the checkpoint too
ds_model.save_checkpoint(tmpdir)
class TestZeROCheckpointFrozenWeights(DistributedTest):
world_size = 2
@pytest.mark.parametrize('zero_stage', [1, 2, 3])
def test_load_optimizer_state(self, tmpdir, zero_stage):
config_dict = {
"train_batch_size": 2,
"steps_per_print": 1,
"optimizer": {
"type": 'Adam',
"params": {
"lr": 0.00015,
"betas": [0.8, 0.999],
"eps": 1e-8,
"weight_decay": 3e-7
}
},
"wall_clock_breakdown": True,
"zero_optimization": {
"stage": zero_stage
}
}
if get_accelerator().is_bf16_supported():
config_dict["bf16"] = {"enabled": True}
elif get_accelerator().is_fp16_supported():
config_dict["fp16"] = {"enabled": True, "initial_scale_power": 8}
hidden_dim = 10
with deepspeed.zero.Init(enabled=zero_stage == 3, config_dict_or_path=config_dict):
models = [SimpleFrozenModel(hidden_dim, empty_grad=False) for _ in range(2)]
checkpoint_correctness_verification(config_dict, models, hidden_dim, tmpdir, load_optimizer_states=True)
@pytest.mark.parametrize('zero_stage', [1, 2, 3])
def test_not_load_optimizer_state(self, tmpdir, zero_stage):
config_dict = {
"train_batch_size": 2,
"steps_per_print": 1,
"optimizer": {
"type": 'Adam',
"params": {
"lr": 0.00015,
"betas": [0.8, 0.999],
"eps": 1e-8,
"weight_decay": 3e-7
}
},
"zero_optimization": {
"stage": zero_stage
}
}
if get_accelerator().is_bf16_supported():
config_dict["bf16"] = {"enabled": True}
elif get_accelerator().is_fp16_supported():
config_dict["fp16"] = {"enabled": True}
hidden_dim = 10
with deepspeed.zero.Init(enabled=zero_stage == 3, config_dict_or_path=config_dict):
models = [SimpleFrozenModel(hidden_dim, empty_grad=False) for _ in range(2)]
checkpoint_correctness_verification(config_dict, models, hidden_dim, tmpdir, load_optimizer_states=False)
@pytest.mark.parametrize('zero_stage', [1, 2, 3])
def test_load_module_only(self, tmpdir, zero_stage):
config_dict = {
"train_batch_size": 2,
"optimizer": {
"type": 'Adam'
},
"zero_optimization": {
"stage": zero_stage,
}
}
if get_accelerator().is_bf16_supported():
config_dict["bf16"] = {"enabled": True}
elif get_accelerator().is_fp16_supported():
config_dict["fp16"] = {"enabled": True, "initial_scale_power": 8}
hidden_dim = 10
with deepspeed.zero.Init(enabled=zero_stage == 3, config_dict_or_path=config_dict):
models = [SimpleFrozenModel(hidden_dim, empty_grad=False) for _ in range(2)]
checkpoint_correctness_verification(config_dict, models, hidden_dim, tmpdir, load_module_only=True)
@pytest.mark.parametrize('zero_stage', [1, 2])
def test_save_exclude_frozen_weights(self, tmpdir, zero_stage):
world_size = 1
config_dict = {
"train_micro_batch_size_per_gpu": 1,
"optimizer": {
"type": 'Adam'
},
"zero_optimization": {
"stage": zero_stage,
}
}
if get_accelerator().is_bf16_supported():
config_dict["bf16"] = {"enabled": True}
elif get_accelerator().is_fp16_supported():
config_dict["fp16"] = {"enabled": True, "initial_scale_power": 8}
hidden_dim = 10
model = SimpleFrozenModel(hidden_dim, empty_grad=False)
ds_engine, _, _, _ = deepspeed.initialize(model=model, model_parameters=model.parameters(), config=config_dict)
# Validate backwards-compatibility of including frozen parameters in checkpoint
all_ckpt_folder = os.path.join(tmpdir, 'all_params')
ds_engine.save_checkpoint(all_ckpt_folder)
all_params_ckpt_file = get_model_ckpt_name_for_rank(os.path.join(all_ckpt_folder, 'global_step0'), '00')
loaded_all_param_model = torch.load(all_params_ckpt_file, weights_only=False)['module']
all_param_names = set([n for n, p in model.named_parameters()])
assert set(loaded_all_param_model.keys()) == all_param_names
# Validate exclusion of frozen parameters
trainable_ckpt_folder = os.path.join(tmpdir, 'no_frozen_params')
ds_engine.save_checkpoint(trainable_ckpt_folder, exclude_frozen_parameters=True)
trainable_ckpt_file = get_model_ckpt_name_for_rank(os.path.join(trainable_ckpt_folder, 'global_step0'), '00')
# Excluding frozen parameters should reduce checkpoint size
assert os.path.getsize(all_params_ckpt_file) > os.path.getsize(trainable_ckpt_file)
loaded_trainable_param_model = torch.load(trainable_ckpt_file, weights_only=False)['module']
frozen_param_names = set([n for n, p in model.named_parameters() if not p.requires_grad])
loaded_trainable_param_names = set(loaded_trainable_param_model.keys())
overlap_names = set.intersection(loaded_trainable_param_names, frozen_param_names)
assert len(overlap_names) == 0
trainable_param_names = set([n for n, p in model.named_parameters() if p.requires_grad])
assert loaded_trainable_param_names == trainable_param_names
@pytest.mark.parametrize('zero_stage', [1, 2])
def test_save_exclude_custom_frozen_weights(self, tmpdir, zero_stage):
world_size = 1
config_dict = {
"train_micro_batch_size_per_gpu": 1,
"optimizer": {
"type": 'Adam'
},
"zero_optimization": {
"stage": zero_stage,
}
}
if get_accelerator().is_bf16_supported():
config_dict["bf16"] = {"enabled": True}
elif get_accelerator().is_fp16_supported():
config_dict["fp16"] = {"enabled": True, "initial_scale_power": 8}
hidden_dim = 10
model = SimpleFrozenModel(hidden_dim, empty_grad=False)
ds_engine, _, _, _ = deepspeed.initialize(model=model, model_parameters=model.parameters(), config=config_dict)
# Validate custom state_dict model
state_dict_bk = model.state_dict
model.state_dict = model.custom_state_dict
custom_state_dict_ckpt_folder = os.path.join(tmpdir, 'custom_state_dict')
ds_engine.save_checkpoint(custom_state_dict_ckpt_folder, exclude_frozen_parameters=True)
custom_state_dict_ckpt_file = get_model_ckpt_name_for_rank(
os.path.join(custom_state_dict_ckpt_folder, 'global_step0'), '00')
loaded_custom_state_dict_param_model = torch.load(custom_state_dict_ckpt_file, weights_only=False)['module']
loaded_custom_state_dict_param_names = set(loaded_custom_state_dict_param_model.keys())
custom_state_dict_param_names = set([k for k, v in model.state_dict().items()])
trainable_param_names = set([n for n, p in model.named_parameters() if p.requires_grad])
overlap_names = set.intersection(custom_state_dict_param_names, trainable_param_names)
assert loaded_custom_state_dict_param_names == overlap_names
model.state_dict = state_dict_bk
class TestSaveTensorClone(DistributedTest):
world_size = 1
@pytest.mark.parametrize('zero_stage', [1, 2])
@pytest.mark.parametrize('use_cpu_device', [True, False])
def test_save_tensor_clone(self, tmpdir, zero_stage, use_cpu_device):
config_dict = {
"optimizer": {
"type": "AdamW",
},
"zero_optimization": {
"stage": zero_stage
},
"train_batch_size": 1,
"train_micro_batch_size_per_gpu": 1
}
hidden_dim = 1024
model = SimpleModel(hidden_dim, nlayers=4).half()
ref_model_state_dict = model.state_dict()
ds_engine, _, _, _ = deepspeed.initialize(model=model, config_params=config_dict)
clone_device = torch.device('cpu') if use_cpu_device else get_accelerator().current_device()
clone_state_dict = clone_tensors_for_torch_save(ds_engine.module.state_dict())
compare_state_dicts(ref_model_state_dict, clone_state_dict)
ref_ckpt_file = os.path.join(tmpdir, 'ref_ckpt.pt')
torch.save(ref_model_state_dict, ref_ckpt_file)
clone_ckpt_file = os.path.join(tmpdir, 'clone_ckpt.pt')
torch.save(clone_state_dict, clone_ckpt_file)
compare_state_dicts(torch.load(ref_ckpt_file, weights_only=False),
torch.load(clone_ckpt_file, weights_only=False))
def test_elastic_checkpoint_is_deprecated_for_zero3(monkeypatch):
warning_messages = []
def mock_logger_warning(message, *args, **kwargs):
warning_messages.append(message)
monkeypatch.setattr("deepspeed.utils.logger.warning", mock_logger_warning)
DeepSpeedZeroConfig(stage=3, elastic_checkpoint=True)
assert any("elastic checkpointing is deprecated" in str(message).lower() for message in warning_messages)
class TestZeRONonDistributed(DistributedTest):
world_size = 1
# This test calls deepspeed.initialize(), so use the harness' file-store
# initialization instead of env:// TCP rendezvous ports under xdist.
init_distributed = True
@pytest.mark.parametrize('zero_stage', [1, 2, 3])
def test_chmod_exception_handling(self, monkeypatch, zero_stage):
config_dict = {
"optimizer": {
"type": "AdamW"
},
"train_batch_size": 1,
"zero_optimization": {
"stage": zero_stage
}
}
args = SimpleNamespace(local_rank=0)
net = SimpleModel(hidden_dim=4)
engine, _, _, _ = deepspeed.initialize(args=args,
config=config_dict,
model=net,
model_parameters=net.parameters())
log_called = False
def mock_logger_info(message, *args, **kwargs):
nonlocal log_called
log_called = True
monkeypatch.setattr("deepspeed.utils.logger.info", mock_logger_info)
"""
This is presented for use-cases like Azure Storage File Share (where permissions are not allowed)
We use a fake file for this test (file not existing would present a similar issue as not being able to chmod)
"""
fake_recovery_script_dst = os.path.join("tmp", "zero_to_fp32.py")
engine._change_recovery_script_permissions(fake_recovery_script_dst)
assert log_called, "Expected deepspeed.utils.logger.info to be called."
class TestZeROPPLoadCheckpoint(DistributedTest):
world_size = 4
def test_load_zeropp_model(self, ws4_model_checkpoint_zeropp, class_tmpdir):
config_dict = {
"train_batch_size": 4,
"optimizer": {
"type": 'Adam'
},
"zero_optimization": {
"stage": 3,
"zero_hpz_partition_size": 2,
"stage3_param_persistence_threshold": 1
}
}
# Init model and load saved model
hidden_dim = 10
with deepspeed.zero.Init(config_dict_or_path=config_dict):
model = SimpleModel(hidden_dim)
ds_model = create_deepspeed_model(config_dict=config_dict, model=model, base_optimizer=None)
with deepspeed.zero.GatheredParameters(ds_model.module.parameters(), modifier_rank=0):
if dist.get_rank() == 0:
state_dict = torch.load(os.path.join(class_tmpdir, "model.pt"))
ds_model.module.load_state_dict(state_dict)
# Check the parameters after gather
params_to_gather = [p for p in ds_model.module.parameters() if p.ds_status == ZeroParamStatus.NOT_AVAILABLE]
if len(params_to_gather) > 0:
handle = params_to_gather[0].all_gather_coalesced(params_to_gather)
handle.wait()
for ds_param in params_to_gather:
for v in ds_param.data.cpu().flatten().numpy():
assert v == 1.0
def test_load_zeropp_checkpoint(self, ws4_model_checkpoint_zeropp, class_tmpdir):
config_dict = {
"train_batch_size": 4,
"optimizer": {
"type": 'Adam'
},
"zero_optimization": {
"stage": 3,
"zero_hpz_partition_size": 2,
"stage3_param_persistence_threshold": 1
}
}
# Init model and load zero checkpoint
hidden_dim = 10
model = SimpleModel(hidden_dim)
ds_model = create_deepspeed_model(config_dict=config_dict, model=model, base_optimizer=None)
ds_model.load_checkpoint(class_tmpdir,
load_optimizer_states=True,
load_lr_scheduler_states=False,
load_module_only=False)
# Check the parameters after gather
params_to_gather = [p for p in ds_model.module.parameters() if p.ds_status == ZeroParamStatus.NOT_AVAILABLE]
if len(params_to_gather) > 0:
handle = params_to_gather[0].all_gather_coalesced(params_to_gather)
handle.wait()
for ds_param in params_to_gather:
for v in ds_param.data.cpu().flatten().numpy():
assert v == 1.0