deepspeedai / deepspeedai/DeepSpeed

[BUG] the input variables may be changed to scalars when use activation checkpoint

Open
#6,969 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug training
Dominant language
Python
Stars
43.1k
Forks
5k
Avg merge
4d 15h
Merged PRs (30d)
112

Description

Describe the bug
i'm using deepspeed activation checkpoint to train the hunyuanvideo. HunyuanVideo model is composed of a series of mmdit blocks. and every block has same inputs, and every block will return hiddenstates ,encoder_hidden_states which will be the input of next block. the other input variables won't be modified and will be passed to next block directly. but weirdly, when i use deepspeed checkpoint function to wrap every block. some of the inputs will be modified after block forward. if i change to torch.utils.checkpoint, the error disappeared

To Reproduce
here is a simple script:

import torch
import torch.nn as nn
from typing import Optional, Tuple

from deepspeed.runtime.activation_checkpointing import checkpointing
from diffusers.models import AutoencoderKLHunyuanVideo, HunyuanVideoTransformer3DModel

from diffusers.models.transformers.transformer_hunyuan_video import HunyuanVideoSingleTransformerBlock,HunyuanVideoTransformerBlock
from torch.distributed.algorithms._checkpoint.checkpoint_wrapper import (
    CheckpointImpl,
    apply_activation_checkpointing,
    checkpoint_wrapper,
)
from functools import partial
from accelerate import Accelerator
from peft import LoraConfig
def hack_transformer(transformer):
    with torch.no_grad():
        in_chans = 32
        embed_dim = 3072
        patch_size = (1,2,2)
        new_conv_in = nn.Conv3d(in_chans, embed_dim, kernel_size=patch_size, stride=patch_size)
        torch.nn.init.kaiming_normal_(new_conv_in.weight)  
        new_conv_in.weight.data = new_conv_in.weight.data * 0.  
        new_conv_in.weight.data[:, :16] = transformer.x_embedder.proj.weight.data  
        new_conv_in.bias.data[:] = transformer.x_embedder.proj.bias.data 
        transformer.x_embedder.proj = new_conv_in
        transformer.config.in_channels = in_chans
    return transformer

def apply_selective_checkpointing(model, block_types, p, use_deepspeed_ac):
    '''
    block_types: a list of nn.Module types to be checkpointed
    p: the fraction of the all blocks to be checkpointed
    '''
    block_idx = 0
    cut_off = 1 / 2
    # when passing p as a fraction number (e.g. 1/3), it will be interpreted
    # as a string in argv, thus we need eval("1/3") here for fractions.
    p = eval(p) if isinstance(p, str) else p

    def selective_checkpointing(submodule):
        nonlocal block_idx
        nonlocal cut_off

        if isinstance(submodule, block_types):
            block_idx += 1
            if block_idx * p >= cut_off:
                cut_off += 1

                return True
        return False
    
    def count_total_blocks(model, block_types):
        total_blocks = 0

        def count_blocks(module):
            nonlocal total_blocks
            if isinstance(module, block_types):
                total_blocks += 1

        model.apply(count_blocks)
        return total_blocks
    
    if use_deepspeed_ac:
        from deepspeed.runtime.activation_checkpointing import checkpointing
        total_block_num = count_total_blocks(model, block_types)
        num_checkpoints = round(p * total_block_num)
        checkpointing.configure(
            mpu_=None,
            deepspeed_config=None,
            partition_activations=False,
            contiguous_checkpointing=False,
            num_checkpoints=num_checkpoints, 
            checkpoint_in_cpu=True, 
            synchronize=False,
            profile=True,
        )
        checkpoint_fn = checkpointing.checkpoint
        checkpointing_wrapper = partial(checkpoint_wrapper, checkpoint_fn=checkpoint_fn)
    else:
        checkpointing_wrapper = partial(checkpoint_wrapper,checkpoint_impl=CheckpointImpl.NO_REENTRANT)

    apply_activation_checkpointing(
        model,
        checkpoint_wrapper_fn=checkpointing_wrapper,
        check_fn=selective_checkpointing,
    )


accelerator = Accelerator(
        mixed_precision='fp16',
    )
accelerator.state.deepspeed_plugin.deepspeed_config['train_micro_batch_size_per_gpu'] = 1

model = HunyuanVideoTransformer3DModel()
model = hack_transformer(model)
target_modules = [
            'timestep_embedder.linear_1',
            'timestep_embedder.linear_2',
            'text_embedder.linear_1',
            'text_embedder.linear_2',
            'guidance_embedder.linear_1',
            'guidance_embedder.linear_1',
    #        'norm1',
    #        'context_embedder',
            'norm_out.linear',
            'norm1_context.linear'
            'proj_out',
            'norm1.linear'
            'attn.to_q',
            'attn.to_k',
            'attn.to_v',
            "attn.to_out.0",
            "attn.add_k_proj",
            "attn.add_q_proj",
            "attn.add_v_proj",
            'attn.to_add_out',
            'ff.net.0.proj',
            'ff.net.2',
            'ff_context.net.0.proj',
            "ff_context.net.2",
            'norm.linear',
            'proj_mlp',
        ]
        # now we will add new LoRA weights the transformer layers
rank=256
transformer_lora_config = LoraConfig(
    r=rank,
    lora_alpha=rank,
    init_lora_weights="gaussian",
    target_modules=target_modules,
)
model.add_adapter(transformer_lora_config)

    
for param_name, param in model.named_parameters():
    if "lora"  in param_name or "dora_scale" in param_name:
        param.requires_grad = True
    if 'x_embedder' in param_name:
        param.requires_grad = True
        print('train x_embedder')
params_to_opt = list(filter(lambda p: p.requires_grad, model.parameters()))

optimizer = torch.optim.AdamW(
    params_to_opt,
    lr=1e-3
)

apply_selective_checkpointing(model, (HunyuanVideoSingleTransformerBlock,HunyuanVideoTransformerBlock), "1", True)
model.train()
model,optimizer = accelerator.prepare(model,optimizer)


for i in range(10):
    hidden_states = torch.rand(1, 32,17,128, 72).to(device=accelerator.device,dtype=torch.float16)
    encoder_hidden_states = torch.rand(1, 256, 4096).to(device=accelerator.device,dtype=torch.float16)
    encoder_attention_mask = torch.rand(1, 256).to(device=accelerator.device,dtype=torch.float16)
    pooled_projections = torch.rand(1, 768).to(device=accelerator.device,dtype=torch.float16)
    temb = torch.tensor([777]).to(device=accelerator.device)
    guidance = torch.tensor([0.7], device=accelerator.device)
    output=model(hidden_states=hidden_states,
                    timestep=temb,
                    encoder_hidden_states=encoder_hidden_states,
                    encoder_attention_mask=encoder_attention_mask,
                    pooled_projections=pooled_projections,
                    guidance=guidance
                    )



Expected behavior
the hunyuanvideo model got large intermediate activatetions which could cause oom even on h20. i want to use deepspeed activations checkpoint to reduce the vram cost

ds_report output

--------------------------------------------------
DeepSpeed C++/CUDA extension op report
--------------------------------------------------
NOTE: Ops not installed will be just-in-time (JIT) compiled at
      runtime if needed. Op compatibility means that your system
      meet the required dependencies to JIT install the op.
--------------------------------------------------
JIT compiled ops requires ninja
ninja .................. [OKAY]
--------------------------------------------------
op name ................ installed .. compatible
--------------------------------------------------
 [WARNING]  async_io requires the dev libaio .so object and headers but these were not found.
 [WARNING]  async_io: please install the libaio-dev package with apt
 [WARNING]  If libaio is already installed (perhaps from source), try setting the CFLAGS and LDFLAGS environment variables to where it can be found.
async_io ............... [NO] ....... [NO]
fused_adam ............. [NO] ....... [OKAY]
cpu_adam ............... [NO] ....... [OKAY]
cpu_adagrad ............ [NO] ....... [OKAY]
cpu_lion ............... [NO] ....... [OKAY]
 [WARNING]  Please specify the CUTLASS repo directory as environment variable $CUTLASS_PATH
evoformer_attn ......... [NO] ....... [NO]
 [WARNING]  FP Quantizer is using an untested triton version (3.1.0), only 2.3.(0, 1) and 3.0.0 are known to be compatible with these kernels
fp_quantizer ........... [NO] ....... [NO]
fused_lamb ............. [NO] ....... [OKAY]
fused_lion ............. [NO] ....... [OKAY]
/media/sda/zjy/software/miniconda3/envs/stable-fast/compiler_compat/ld: /usr/local/cuda/lib64/libcufile.so: undefined reference to `std::basic_ifstream<char, std::char_traits<char> >::close()@GLIBCXX_3.4'
/media/sda/zjy/software/miniconda3/envs/stable-fast/compiler_compat/ld: /usr/local/cuda/lib64/libcufile.so: undefined reference to `__cxa_guard_release@CXXABI_1.3'
/media/sda/zjy/software/miniconda3/envs/stable-fast/compiler_compat/ld: /usr/local/cuda/lib64/libcufile.so: undefined reference to `__cxa_throw@CXXABI_1.3'
/media/sda/zjy/software/miniconda3/envs/stable-fast/compiler_compat/ld: /usr/local/cuda/lib64/libcufile.so: undefined reference to `std::_Rb_tree_decrement(std::_Rb_tree_node_base*)@GLIBCXX_3.4'
/media/sda/zjy/software/miniconda3/envs/stable-fast/compiler_compat/ld: /usr/local/cuda/lib64/libcufile.so: undefined reference to `std::basic_filebuf<char, std::char_traits<char> >::~basic_filebuf()@GLIBCXX_3.4'
collect2: error: ld returned 1 exit status
gds .................... [NO] ....... [NO]
transformer_inference .. [NO] ....... [OKAY]
inference_core_ops ..... [NO] ....... [OKAY]
cutlass_ops ............ [NO] ....... [OKAY]
quantizer .............. [NO] ....... [OKAY]
ragged_device_ops ...... [NO] ....... [OKAY]
ragged_ops ............. [NO] ....... [OKAY]
random_ltd ............. [NO] ....... [OKAY]
 [WARNING]  sparse_attn requires a torch version >= 1.5 and < 2.0 but detected 2.5
 [WARNING]  using untested triton version (3.1.0), only 1.0.0 is known to be compatible
sparse_attn ............ [NO] ....... [NO]
spatial_inference ...... [NO] ....... [OKAY]
transformer ............ [NO] ....... [OKAY]
stochastic_transformer . [NO] ....... [OKAY]
--------------------------------------------------
DeepSpeed general environment info:
torch install path ............... ['/media/sda/zjy/software/miniconda3/envs/stable-fast/lib/python3.10/site-packages/torch']
torch version .................... 2.5.1+cu118
deepspeed install path ........... ['/media/sda/zjy/software/miniconda3/envs/stable-fast/lib/python3.10/site-packages/deepspeed']
deepspeed info ................... 0.16.3, unknown, unknown
torch cuda version ............... 11.8
torch hip version ................ None
nvcc version ..................... 11.8
deepspeed wheel compiled w. ...... torch 2.5, cuda 11.8
shared memory (/dev/shm) size .... 503.76 GB

Screenshots

Image

i print the temb before the every block forward. and you can see the temb is a 1d array before the first block forward. and after the first block forward it became a scalar. the block doesn't has any in-place operations. if i change the checkpoint function to torch.utils.checkpoint, the error gone.

System info (please complete the following information):

  • OS: [e.g. Ubuntu 18.04]
  • GPU count and types [e.g. two machines with x8 A100s each]
  • Interconnects (if applicable) [e.g., two machines connected with 100 Gbps IB]
  • Python version
  • Any other relevant info about your setup

Launcher context
Are you launching your experiment with the deepspeed launcher, MPI, or something else?

Docker context
Are you using a specific docker image that you can share?

Additional context
Add any other context about the problem here.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the supplied reproduction and the deepspeed.runtime.activation_checkpointing.checkpointing entry point, then compare its checkpoint behavior with torch.utils.checkpoint. Trace how the temb argument is handled across successive HunyuanVideo block forwards. Done means non-mutated input variables retain their original tensor values under DeepSpeed activation checkpointing and the reproduction is covered by a regression test.

Written by the indexing model from the issue text.

Assessment

Tech stack
python, pytorch
Domain
distributed-systems, machine-learning
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.