Skip to content

[tests] model-level device_map clarifications #11681

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 18 commits into
base: main
Choose a base branch
from

Conversation

sayakpaul
Copy link
Member

What does this PR do?

Our device_map (model-level) documentation could be improved a bit. We could also test accepted values for device_map. This PR does it.

The PR relies on #11680 to get merged.

@Birch-san is it better?

@HuggingFaceDocBuilderDev

The docs for this PR live here. All of your documentation changes will be reflected on that endpoint. The docs are available until 30 days after the last update.

@GrigoryEvko
Copy link

GrigoryEvko commented Jun 9, 2025

Hi guys, I tinkered a bit with device_map code with Opus, replaced diffusers internal usage of device_map in favor of accelerate functions. I will test now on multi gpu machines, please share your thoughts!
3635b41

Next step would be making diffusers modeling_utils on par with transformers modeling_utils to allow Torch TP usage, Deepspeed and FSDP/FSDP2 usage, etc.

@sayakpaul
Copy link
Member Author

I tinkered a bit with device_map code with Opus, replaced diffusers internal usage of device_map in favor of accelerate functions.

We already make use of accelerate fun tions to facilitate device mapping. So, please open a PR as that is easier for the team to navigate through.

Next step would be making diffusers modeling_utils on par with transformers modeling_utils to allow Torch TP usage, Deepspeed and FSDP/FSDP2 usage, etc.

This is in the works already by @a-r-r-o-w. Again, it is much better to converse about it through PRs and issues.

This PR is exactly what the description of the PR says. So, let’s switch to other threads for discussing related things further.

@GrigoryEvko
Copy link

We already make use of accelerate fun tions to facilitate device mapping. So, please open a PR as that is easier for the team to navigate through.

Sure, I will clean up, test and then submit the PR. I just noticed the team started to work on the issue and decided to comment here.

Copy link
Member

@a-r-r-o-w a-r-r-o-w left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Would try to make sure that the case/failures encountered by Birch are resolved and work as expected


@parameterized.expand([0, "cuda", torch.device("cuda"), torch.device("cuda:0")])
@require_torch_gpu
def test_passing_non_dict_device_map_works(self, device_map):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we test some more cases like: {"": torch.device("meta"), "decoder": torch.device("cuda")}? For example, if this was a VAE, the intention here is to not load the encoder weights, but directly load the decoder weights to device.

Additionally, we should probably run device map tests for all models IMO (can be taken up in future PR)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree this would be fantastic, but we can probably tackle that in a separate PR, and leave the scope of this one to tests/docs/bugfixes/assertions.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additionally, we should probably run device map tests for all models IMO (can be taken up in future PR)

We have a bunch of device_map related tests already in https://github.com/huggingface/diffusers/blob/main/tests/models/test_modeling_common.py.

I can shift the current ones being added through this PR to test_modeling_common.py in a separate PR.

Can we test some more cases like: {"": torch.device("meta"), "decoder": torch.device("cuda")}? For example, if this was a VAE, the intention here is to not load the encoder weights, but directly load the decoder weights to device.

Feel free to add that in a separate PR.

@@ -1084,6 +1084,25 @@ def test_load_sharded_checkpoint_device_map_from_hub_local_subfolder(self):
assert loaded_model
assert new_output.sample.shape == (4, 4, 16, 16)

def test_wrong_device_map_raises_error(self):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Testing more of the failure code paths (if there are any; I am not fully aware of the relevant parts of the codebase) could be nice

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We check if the device_map is respected in https://github.com/huggingface/diffusers/blob/main/tests/models/test_modeling_common.py. Then we're testing for invalid device_map values for non-dict entries. I added another one in eb913e2.

Many errors are already handled in accelerate.

So, collectively, we should now be good I think.

@a-r-r-o-w
Copy link
Member

@GrigoryEvko re: FSDP/FSDP2; This is already supported via accelerate, and the intented way for usage is to enable it via the accelerate config and modify some parts of the codebase based on accelerate documentation. Could you elaborate what it would mean to support this natively?

re: TP; TP does not really help much with e2e speed with current diffusion model sizes IME. I will be prioritizing supporting CP natively instead in the coming weeks. For TP, it's something we could consider once CP is supported.

re: DeepSpeed; This is also something that should be enabled via accelerate in your training codebase

@Birch-san
Copy link
Contributor

Birch-san commented Jun 9, 2025

does device_map=device work (where the device variable is a torch.device)? the docs say it should work, but when I tried this, I got a "device cuda is invalid" error.

likewise, does device_map={'': device} work? the docs say it should work, but when I tried this, I got a "device cuda is invalid" error.

from diffusers.models.unets.unet_2d_condition import UNet2DConditionModel
import torch

device = torch.device('cuda')
unet: UNet2DConditionModel = UNet2DConditionModel.from_pretrained(
    'NovelAI/nai-anime-v1-full',
    torch_dtype=torch.float16,
    use_safetensors=True,
    subfolder='unet',
    device_map={'': device},
).eval()

I'm using diffusers==0.32.2 and accelerate==1.4.0 if it matters.

A map that specifies where each submodule should go. It doesn't need to be defined for each
parameter/buffer name; once a given module name is inside, every submodule of it will be sent to the
same device. Defaults to `None`, meaning that the model will be loaded on CPU.

Examples:
Copy link
Contributor

@Birch-san Birch-san Jun 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it looks like this is just documenting the scalar cases. the bit that I need docs for is the dictionary convention. {'': device.type} as the simpest valid input is extremely hard to guess. there really needs to be an explanation of what the key of the dictionary means.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cc: @SunMarc @stevhliu

How is this documented in transformers?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also can you fix the typing for the DiffusionPipeline from_pretrained for device_map since for this specific function, we only allow balanced value ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 407b67f.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Birch-san I clarified the docs to include the case of {"": torch.device("cuda")} and have added tests for it, too. For other possible and valid dict inputs to device_map, I would have to defer you to https://huggingface.co/docs/accelerate/en/concept_guides/big_model_inference#the-devicemap as you can notice it's hard to specify that beforehand without doing a bit of investigation.

So, I would suggest loading your model with "auto" device_map, first. And then printing (model.hf_device_map) to get a much better handle. This way, you will have a reasonable starting point which you could then use to tweak things around a bit.

@sayakpaul
Copy link
Member Author

@Birch-san I ran the following with this PR branch and it worked:

from diffusers.models.unets.unet_2d_condition import UNet2DConditionModel
import torch

for device_map in [torch.device('cuda'), {'': torch.device('cuda')}]:
    unet: UNet2DConditionModel = UNet2DConditionModel.from_pretrained(
        'NovelAI/nai-anime-v1-full',
        torch_dtype=torch.float16,
        use_safetensors=True,
        subfolder='unet',
        device_map=device_map,
    ).eval()

The test_passing_non_dict_device_map_works() tests should ensure device_maps that have non-dict entries, including the torch.device type are working.

Will add dict types in the tests as well.

@sayakpaul sayakpaul requested a review from a-r-r-o-w June 9, 2025 14:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
6 participants